ZuloOne
English
Sign in
Menu

📧 Outgoing mail

Updated

A queue of messages and a list of SMTP servers. A script only puts the message in the queue; a background sender delivers it separately — a business transaction that rolls back sends no mail.

← Developer wiki

The SMTP server list#

The Administrator → Mail → SMTP servers page (/smtpservers). One row is one named account: Host, Port, Security (Auto / None / StartTls / SslOnConnect), User, Password (or an OAuth access token), From address and From name, Enabled / Default.

The password is encrypted with ASP.NET Data Protection (ZuloOne.Smtp.Password) and is never returned by the API. If the protection keys have changed, the page will say the password has to be entered again.

The Test button sends a real "ZuloOne SMTP test" message through the chosen account — save the row first, and give it a recipient.

The table: MetaSmtpServers. The name is unique.

Gmail#

  1. In the Google account, switch 2FA on and create an app password.
  2. Host smtp.gmail.com, port 587, security StartTls (or 465 + SslOnConnect).
  3. The user is the full address ([email protected]), the password is the app password with no spaces (Google draws it as xxxx xxxx xxxx xxxx).
  4. Accept invalid certificate is not needed for Gmail.

"The operation has timed out" is not a login refusal: Core could not reach the host (CRL/OCSP out of Docker, a firewall on 587). Certificate revocation checks do not block the connection.

Choosing a server from a script#

await context.GetService<IEmailService>().SendAsync(
    new EmailMessage
    {
        AddressTo = "[email protected], [email protected]",
        Subject = "Order posted",
        Body = "…",
    },
    new EmailOptions { SmtpServerName = "office365" });

Several recipients in one AddressTo: separated by a comma, a semicolon or a + b. The name (AddressToName) only makes sense with a single address.

Or EmailOptions.SmtpServerId — the metaId of the SMTP server row.

An attachment from disk: message.AttachFile(filePath, fileName) — the bytes are read by the platform on the machine the script is running on. A UNC share is read by the office agent, not by the Core in the cloud. The agent then passes the already-read bytes to Core through IEmailService.SendAsync (JSON-RPC) — the file path does not travel to Core. Scheduled reports are the example.

The order of selection when sending:

  1. the id given, otherwise the name;
  2. if id and name are both empty — the enabled server flagged Default;
  3. otherwise the Email:* configuration section (Email:Enabled + Email:Host).

The config stays as a fallback channel (a stand with no UI). The server list does not replace it.

The queue#

The Administrator → Mail → Mail queue page (/emailqueue). Send again only returns the message to the queue — it does not poke SMTP itself. The "a sender is configured" check is true when there is an enabled server in the server list or Email:Host is set.

The server-list API: GET/POST /api/email/servers, PUT/DELETE /api/email/servers/{id}, POST /api/email/servers/{id}/test with the body { "to": "…" }.

Where next#

Was this page helpful?