Skip to navigation

Send an email

View as Markdown

Sends an email to an existing contact from the workspace’s own connected mailbox (Gmail, Outlook or Zoho Mail), then records it in the contact’s email conversation. There is no platform sender. A workspace that has not connected a mailbox and selected it under Settings → Account → Email gets 409.

Replying in a thread. Pass inReplyTo with the provider’s thread id (Gmail) or message id (Outlook, Zoho) to answer inside a thread the customer is already reading. The mailbox’s reply action is used, so the provider threads the message and subject is not applied. Without inReplyTo a new email is sent. inReplyTo changes how the email goes out, never whether: every check below still runs.

Checks, in order: the contact exists in the workspace and has an email address, a usable mailbox is configured, compliance rules pass (do-not-contact list, suppression list, blocked prefixes; quiet hours are not applied to email), the contact has not opted out, and the daily email cap has room. The subject is trimmed and line breaks in it are replaced with spaces.

After a successful send the email is written into the contact’s latest email conversation, or a new one if none exists, without reopening a closed thread. That way the agent handling the reply knows what was sent. Recording is best-effort and never fails the request.

Side effects. Sends a real email from the customer’s mailbox through its provider connection. Consumes one unit of the daily email send cap: 1,000 per day by default, 25,000 on plans with no monthly limit, counted in your workspace’s timezone since 2026-09-15 (it was the UTC day before), so the allowance resets at local midnight. The unit is refunded on any delivery failure. A retryable transport error is retried once, only when the provider guarantees nothing was delivered. Does not draw on the AI-message allowance. Writes a message to the thread and an audit entry.

Idempotency. Not idempotent. A retry after a timeout can send the email twice.

Webhook events. audit.log_recorded for subscribers that selected it explicitly. See Webhooks.

Access

  • Required scope: write.
  • Rate limit: Outbound — 20 requests/min per workspace, fail-closed, spent only by this route among /api/email routes (shared with placing calls, transfers and WhatsApp), and the configuration-mutations limiter — 10 requests/min per workspace, shared with other configuration mutations, and the general API limit (120 to 600 requests/min by plan). See Rate limits.
  • Plan: Available on every plan. Requires a connected Gmail, Outlook or Zoho Mail mailbox selected as the sending mailbox.

Authentication

AuthorizationBearer

Workspace API key: jl_ followed by 64 lowercase hex characters, created by the workspace owner in the dashboard (Settings → API Keys) and sent as Authorization: Bearer jl_.... The plaintext is shown once, at creation; Jelliu stores only a SHA-256 hash. A workspace can hold up to 25 active keys.

ScopeGET / HEADPOST / PUT / PATCH / DELETEAdmin-only routes
readYesNoNo
writeYesYesNo
fullYesYesYes

Operations restricted to admins or owners reject keys without the full scope with 403, and say so in their description. No key, whatever its scope, can mint or revoke API keys or rotate a webhook secret — that requires a signed-in owner session. A revoked key stops authenticating within about 10 seconds. See Authentication.

Request

This endpoint expects an object.
contactIdstringRequiredformat: "uuid"

Contact to email. Must belong to the workspace and have an email address; otherwise 400 Contact has no email address.

subjectstringRequired1-998 characters

Subject line. Trimmed; CR/LF characters are replaced by spaces. Not applied when inReplyTo threads the reply.

messagestringRequired1-50000 characters

Plain-text body, trimmed before sending.

inReplyTostringOptional1-998 characters

Provider thread id (Gmail) or message id (Outlook, Zoho Mail) to reply within. Opaque, not a UUID.

Response

Email sent.
dataobject

Errors

400
Bad Request Error
401
Unauthorized Error
403
Forbidden Error
409
Conflict Error
423
Locked Error
429
Too Many Requests Error
500
Internal Server Error