Ray
PricingDocsSign inStart free
Browse the docs

Getting started

Ray documentationAuthenticationUsing Ray with AI coding agentsMCP serverTypeScript SDK

Guides

Sending notificationsIdempotencyTemplatesScheduling and test sendsStatus and feedsClick trackingDelivery webhooks

Channels

Email with Amazon SESEmail with SMTPPush notifications with Firebase Cloud MessagingSlack notificationsDiscord notificationsTelegram notificationsSMS with TwilioSMS with sendsms.mnHTTPS webhook channel

Reference

Rate limits and quotasErrors

Getting started

Ray documentationAuthenticationUsing Ray with AI coding agentsMCP serverTypeScript SDK

Guides

Sending notificationsIdempotencyTemplatesScheduling and test sendsStatus and feedsClick trackingDelivery webhooks

Channels

Email with Amazon SESEmail with SMTPPush notifications with Firebase Cloud MessagingSlack notificationsDiscord notificationsTelegram notificationsSMS with TwilioSMS with sendsms.mnHTTPS webhook channel

Reference

Rate limits and quotasErrors

Channels

Email with SMTP

Send email through any SMTP relay with Ray, such as Google Workspace, SendGrid, Mailgun or your own server. Setup, config fields, recipient, per-send from and reply-to, custom headers and errors.

View as Markdownllms.txtAPI reference

The smtp_email channel sends email through any SMTP server you have credentials for: a transactional provider's SMTP endpoint, Google Workspace, or your own relay. It uses the same email_html templates and recipient shape as Amazon SES, so a template works over either. API calls go to https://ray-api.gege.mn with Authorization: Bearer $RAY_API_KEY.

Channel type smtp_email
Template kind email_html (shared with SES)
Recipient { "email", "name"?, "cc"?, "bcc"?, "from"?, "replyTo"?, "headers"?, "attachments"? }
Pacing ratePerMinute, default 840, max 1600
Suppression Not populated (SMTP has no bounce callback)
Click tracking Yes (Starter plan and above)

What you need from your provider

Collect five values: host, port, TLS mode, username and password, plus a from address the server allows you to send as.

Provider Host Port Username Password
SendGrid smtp.sendgrid.net 587 apikey (literally) A SendGrid API key with Mail Send permission
Mailgun smtp.mailgun.org (or smtp.eu.mailgun.org) 587 The domain's SMTP login That login's SMTP password
Google Workspace or Gmail smtp.gmail.com 587 Your full address An app password (requires 2-Step Verification)
Amazon SES SMTP interface email-smtp.<region>.amazonaws.com 587 SES SMTP username SES SMTP password (created under SES, SMTP settings)
Your own relay Your host 587 or 465 As configured As configured

Generic steps:

  1. In your provider's dashboard, verify the domain or address you will send from (SPF and DKIM records).
  2. Create SMTP credentials (often an API key or a dedicated SMTP user).
  3. Note your provider's sending limit to set ratePerMinute.

Channel config

Create the config in the dashboard: Channels, New channel, SMTP relay. Credentials are stored encrypted.

Field Required Notes
Name yes Internal label.
SMTP host (host) yes Hostname or IP, max 255 characters.
Port (port) no Default 587.
Use implicit TLS (secure) no Default off. Turn on for port 465 (TLS from connect). Off means the connection must upgrade with STARTTLS (ports 587 and 25); a server that doesn't offer STARTTLS fails the send. Mail is never sent unencrypted.
Username (username) yes Max 255 characters.
Password (password) yes Max 1024 characters. Leave blank when editing to keep the stored password.
From address (fromAddress) yes A valid email address the server lets you send as. The default; a send can override it with from.
From name (fromName) no Display name. No line breaks. The default; a send can override it.
Rate per minute (ratePerMinute) no Default 840. Max 1600. Match your provider's limit.

Host restrictions: Ray refuses to connect to loopback (127.0.0.0/8, ::1), 0.0.0.0/8 and link-local addresses (169.254.0.0/16, fe80::/10, including cloud metadata endpoints), checked against the resolved address at send time. Private network ranges are allowed.

Find the config's id with GET /channels; its kind is smtp_email.

Template content

Same as SES:

Field Type Notes
subject string, 1 to 998 Params inserted as-is; a line break in the rendered subject fails the send with 400.
bodyHtml string Params are HTML-escaped.
bodyText string Params inserted as-is.

Recipient

{
  "email": "[email protected]",
  "name": "Ada Lovelace",
  "cc": [{ "email": "[email protected]" }],
  "bcc": [{ "email": "[email protected]" }],
  "from": { "name": "Ada at Example" },
  "replyTo": [{ "email": "[email protected]" }],
  "headers": { "List-Unsubscribe": "<https://example.com/unsubscribe/123>" },
  "attachments": [
    { "filename": "logo.png", "content": "iVBORw0KGgo=", "contentType": "image/png", "disposition": "inline", "contentId": "logo" }
  ]
}
Field Type Notes
email string Required. Checked against the config's suppression list.
name string, 1 to 255 Optional. No line breaks.
cc, bcc array, max 50 each [{ "email", "name"? }].
from object { "email"?, "name"? }. Overrides the config's from address and name for this send; a field you leave out keeps the config's value. The address must be on the same domain as the config's fromAddress (another domain, including a subdomain, is a 400). It is also used as the envelope sender, so the server must let you send as it.
replyTo array, 1 to 10 [{ "email", "name"? }]. Sets Reply-To.
headers object, max 15 Extra headers, { "Name": "value" }. Same rules as SES: printable ASCII, no line breaks, and Ray-managed headers such as From, Reply-To and Subject, plus provider controls such as X-SMTPAPI, X-PM-* or X-Mailgun-*, are rejected.
attachments array, max 20 filename, content (base64, max 14,000,000 characters), contentType?, disposition (attachment default, or inline), contentId? for <img src="cid:...">. Total at most 34,000,000 base64 characters.

Your SMTP provider may enforce lower limits on recipients or message size.

Send an email

curl -X POST https://ray-api.gege.mn/send \
  -H "Authorization: Bearer $RAY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: welcome-user_123" \
  -d '{
    "channelConfigId": "5d2e8f1a-6b7c-4d8e-9f0a-2b3c4d5e6f7a",
    "templateId": "1d2e3f4a-5b6c-4d7e-8f9a-0b1c2d3e4f5a",
    "params": { "name": "Ada", "loginUrl": "https://app.example.com/login" },
    "recipient": { "email": "[email protected]", "name": "Ada Lovelace" },
    "trackClicks": true
  }'
const res = await fetch("https://ray-api.gege.mn/send", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.RAY_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": "welcome-user_123",
  },
  body: JSON.stringify({
    channelConfigId: "5d2e8f1a-6b7c-4d8e-9f0a-2b3c4d5e6f7a",
    content: {
      subject: "Welcome, {{name}}",
      bodyHtml: '<p>Hi {{name}}, <a href="{{loginUrl}}">sign in</a> to get started.</p>',
      bodyText: "Hi {{name}}, sign in at {{loginUrl}} to get started.",
    },
    params: { name: "Ada", loginUrl: "https://app.example.com/login" },
    recipient: { email: "[email protected]", name: "Ada Lovelace" },
  }),
});
if (res.status !== 202) throw new Error(`Ray ${res.status}: ${await res.text()}`);

The delivery row's providerMessageId is the Message-ID of the sent email. delivered means the SMTP server accepted the message.

Bounces

SMTP has no standard delivery callback, so Ray does not learn about bounces or complaints on this channel and never adds suppressions for it. Handle bounces with your provider's own tools. If you send through Amazon SES, the SES channel handles bounces and complaints automatically.

Errors

Errors appear on the delivery row as providerError: { "name", "message" }, with the server's reply in message.

Result SMTP errors
failed_terminal (not retried) 5xx replies (unknown mailbox, message refused, relay denied), authentication failure (EAUTH), a host that resolves to a blocked address, SenderNotAllowedError (a scheduled send's from.email is no longer on the config's domain after a config edit)
failed_retryable (retried) 4xx replies (greylisting, mailbox busy, temporary rate limit), connection, DNS, socket errors and timeouts

Common fixes:

  • EAUTH or 535 Authentication failed: wrong username or password; for Gmail use an app password; for SendGrid the username is apikey.
  • 550 or 553 sender rejected: the from address isn't verified or allowed on this account.
  • Repeated connection timeouts: wrong port or TLS mode. Use 587 with implicit TLS off, or 465 with it on.

Request errors from POST /send are listed in Errors.

PreviousEmail with Amazon SESNextPush notifications with Firebase Cloud Messaging

On this page

  • What you need from your provider
  • Channel config
  • Template content
  • Recipient
  • Send an email
  • Bounces
  • Errors
Ray

Notification delivery for teams that already have providers.

Product

PricingDocsAPI referenceMCP serverTypeScript SDKAgent skillsllms.txt

Legal

TermsPrivacyRefunds

Contact

[email protected]

© 2026 L’Atelier Gege LLC