Email

Send email from your own connected mailbox, continue threads, and control which replies an agent answers.
View as Markdown

Jelliu agents send and answer email through a mailbox your workspace connects: Gmail, Outlook or Zoho Mail. Mail leaves from that mailbox, is signed by that provider with your domain, and appears in its Sent folder. Every send goes through the same do-not-contact, opt-out and daily-cap checks as the rest of the platform.

Email is always sent from your own mailbox. Jelliu does not send your customer email from a platform address. Until a mailbox is connected and selected, POST /api/email/send fails with 409.

How it works

  1. Connect a mailbox as an integration. See Integrations.
  2. Select it as the sender. Jelliu checks with the provider that the connection works before saving it.
  3. Send. Proactive email goes out through the API, campaigns or an agent.
  4. Receive replies. Turn on reply ingestion for the mailbox so the agent can answer. A reply is answered only if the conversation is one your workspace started.

Endpoints

MethodPathAPI key scope
POST/api/email/sendwrite
GET/api/email/settingsread
PUT/api/email/settings/sendingfull
PATCH/api/email/settingsfull
GET/api/composio/triggers/email-replies/supportfull
POST/api/composio/triggers/email-repliesfull

Sending identity

Read the settings

curl -sS "https://api.jelliu.co/api/email/settings" \
-H "Authorization: Bearer $JELLIU_API_KEY"
{
"data": {
"fromEmail": "ventas@clinicanorte.example.com",
"senderName": "Clínica Norte",
"inboundEmail": null,
"replyToEmail": "contacto@clinicanorte.example.com",
"sendingProvider": "gmail",
"sendingConnectionId": "5e0c7a1b-2d3f-4e5a-9b8c-7d6e5f4a3b21",
"sendingIssue": null,
"canSend": true,
"replyToSupported": false,
"availableMailboxes": [
{
"provider": "gmail",
"connectionId": "5e0c7a1b-2d3f-4e5a-9b8c-7d6e5f4a3b21",
"address": "ventas@clinicanorte.example.com",
"status": "active",
"usable": true,
"issue": null
}
]
}
}
FieldDescription
canSendtrue only when a connected mailbox is selected and resolves. Check this before sending.
fromEmailThe address mail will actually leave from, resolved the same way a send resolves it.
senderNameDisplay name used with the address.
sendingProvidergmail, outlook, zoho_mail, or null when no mailbox is selected.
sendingConnectionIdThe integration connection used to send.
sendingIssueWhy the selected mailbox cannot be used right now, or null.
replyToEmailYour workspace contact address.
inboundEmailThe workspace’s inbound address for replies, when that feature is enabled; otherwise null.
replyToSupportedfalse whenever a mailbox is selected: the providers’ send tools do not accept a Reply-To header, so replies arrive in the mailbox itself.
availableMailboxesConnected mailboxes you can select. usable is true only for an active connection with a known address and no recorded error (and, for Zoho Mail, its account ID).

Select the sending mailbox

provider
stringRequired

gmail, outlook or zoho_mail. null is rejected: there is no platform sender to go back to.

connectionId
string (uuid)

Which connection to use when several of the same provider are connected. Defaults to the most recently connected one.

curl -sS -X PUT "https://api.jelliu.co/api/email/settings/sending" \
-H "Authorization: Bearer $JELLIU_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "provider": "gmail", "connectionId": "5e0c7a1b-2d3f-4e5a-9b8c-7d6e5f4a3b21" }'

Before saving, Jelliu makes a read-only call to the provider to confirm the connection still works and to learn the address it controls. The response is the full settings object with the address that will really be used. A connection that is missing, not active, has a recorded error, has no known address, or (for Zoho Mail) has no account ID is refused with 409 and a message naming what to fix, for example:

{
"error": {
"code": "EMAIL_ERROR",
"message": "No hay una conexion de outlook en este workspace. Conectala primero en Integraciones."
}
}

Connecting a mailbox in Integrations gives agents tools to use it, but does not by itself change where email is sent from. Selecting the sender with this endpoint (or in the dashboard under Settings → Account → Email) is a separate step.

Set the contact address

PATCH /api/email/settings with { "replyToEmail": "contacto@clinicanorte.example.com" } (a valid email, up to 254 characters) sets the workspace contact address and returns the settings object.

Sending email

POST /api/email/send sends one email to one contact.

contactId
string (uuid)Required

The contact to write to. It must belong to your workspace and have an email address.

subject
stringRequired

1 to 998 characters. Line breaks are replaced with spaces.

message
stringRequired

Plain-text body, 1 to 50,000 characters. Leading and trailing whitespace is trimmed.

inReplyTo
string

Continue an existing thread instead of starting a new one: the provider’s thread ID (Gmail) or message ID (Outlook, Zoho Mail), up to 998 characters.

curl -sS -X POST "https://api.jelliu.co/api/email/send" \
-H "Authorization: Bearer $JELLIU_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"contactId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"subject": "Tu cotización de Clínica Norte",
"message": "Hola Ana,\n\nTe comparto la cotización que pediste. Si tienes preguntas, responde a este correo.\n\nClínica Norte"
}'
200 OK
{
"data": {
"sent": true,
"to": "ana@example.com"
}
}

The message is also recorded in the contact’s email conversation, so when the contact replies the agent knows what was sent. Read it through Conversations.

Checks before every send

In order, stopping at the first failure:

  1. The contact exists in your workspace and has an email address.
  2. A connected mailbox is selected and usable.
  3. Compliance. The contact is checked against the suppression list, do-not-call status, blocked prefixes and the daily and total attempt limits of your compliance configuration.
  4. Opt-out. Contacts with status dnc, do_not_call or an opt-out date are refused.
  5. Daily cap. One unit of the workspace’s daily email cap is consumed. It is refunded if the send fails.

Allowed contact hours do not apply to email. They come from telemarketing calling-time rules, which say nothing about when an email is delivered. WhatsApp and calls still respect them.

Threads

Pass inReplyTo to answer inside a conversation the contact is already reading. The mailbox’s reply action is then used (Gmail replies to the thread; Outlook and Zoho Mail reply to the message), so the email lands in the same thread instead of arriving as a new message.

  • When replying in a thread, the provider’s thread determines the subject; your subject is still required and validated.
  • When inReplyTo is omitted, a new email is sent.
  • inReplyTo changes only how the email is delivered. Every check above still runs.

Agent replies to inbound mail always use the thread or message ID of the email they answer.

Attachments and formatting

The API sends plain-text bodies only. There is no attachment field on POST /api/email/send.

Retries and duplicates

A failed send is not retried when the result is uncertain, because retrying could deliver the same email twice. If you receive a 5xx or a network error, check the contact’s conversation before sending again.

Receiving replies

Replies arrive in the connected mailbox. For an agent to answer them, turn on reply ingestion for that mailbox.

Check support

curl -sS "https://api.jelliu.co/api/composio/triggers/email-replies/support" \
-H "Authorization: Bearer $JELLIU_API_KEY"
{
"data": [
{ "toolkit": "gmail", "connected": true, "triggerSlug": "GMAIL_NEW_GMAIL_MESSAGE", "support": "supported", "reason": null },
{ "toolkit": "zoho_mail", "connected": true, "triggerSlug": null, "support": "unsupported", "reason": "Este buzón no publica eventos de correo nuevo" }
]
}
MailboxCan receive replies
GmailYes
OutlookYes. The full message body is fetched before the agent answers.
Zoho MailNo. The provider publishes no new-mail events.

Turn on replies

toolkit
stringRequired

The connected mailbox: gmail or outlook.

agentId
string (uuid)Required

The agent that answers.

createContact
booleanDefaults to false

Create a contact for a sender your workspace has never seen and let the agent answer people who write to the mailbox on their own. Off by default.

curl -sS -X POST "https://api.jelliu.co/api/composio/triggers/email-replies" \
-H "Authorization: Bearer $JELLIU_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "toolkit": "gmail", "agentId": "7c1d9f30-5e2a-4a7e-9c8f-91a0b1c2d3e4" }'
201 Created
{
"data": {
"id": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
"toolkit": "gmail",
"triggerSlug": "GMAIL_NEW_GMAIL_MESSAGE",
"status": "active",
"action": "email",
"agentId": "7c1d9f30-5e2a-4a7e-9c8f-91a0b1c2d3e4"
}
}

The result is an ordinary trigger. Pause, resume or delete it through the trigger endpoints described in Integrations.

Activating an email campaign sends email but does not, by itself, make the agent answer replies. Reply ingestion is a switch for the whole mailbox, not for a campaign.

The reply gate

A mailbox receives newsletters, vendor mail and cold lists, and knowing a sender’s address is not a reason to answer them. For every incoming email, Jelliu decides whether the agent may reply:

RuleAnswered
The first message of the oldest email conversation with that address was sent by your side (an agent, a person, or the API).Yes
The contact belongs to a campaign that is active and whose channel is email.Yes
Reply ingestion was turned on with createContact: true.Yes
Anything else.No

Then, even when the gate allows it, the agent does not answer if:

  • the email is automated: an Auto-Submitted header other than no; Precedence: bulk, list or junk; auto-reply headers; mailing-list headers such as List-Id or List-Unsubscribe; a sender such as mailer-daemon, postmaster or no-reply; or an out-of-office or delivery-failure subject. The contact is left untouched and their next real email is answered;
  • the email is an opt-out (see below);
  • the contact has already opted out;
  • the daily email cap is reached, or the monthly AI message allowance is used up.

Unanswered emails are not an error. When the provider delivers the same email as two events, it is claimed by its message ID and processed once.

Opt-out

An incoming email is an opt-out when:

  • its first non-empty line is one of the opt-out keywords used on every text channel: stop, stopall, stop all, cancel, unsubscribe, quit, end, baja, cancelar, darme de baja, dar de baja, no molestar; or
  • its first 500 characters contain a phrase such as unsubscribe, darme de baja, dar de baja, no me envíen más, remove me from or stop emailing.

Jelliu then marks every contact in your workspace with that email address (compared case-insensitively) as dnc, records the date and the phrase, and sends nothing back. Later sends to those contacts fail with 403: Contact has opted out — cannot send email.

Automated email is checked before opt-out, so an unsubscribe footer quoted inside a bounce never suppresses a contact.

Bounces

Delivery failures and out-of-office replies are recognized as automated mail and are not answered. They do not change the contact’s status: Jelliu does not currently suppress an address because a message to it bounced.

Email campaigns

A campaign with channel set to email writes first to every contact, using the campaign’s emailSubject (up to 300 characters) and emailBody (up to 20,000 characters). Campaign sends are deliberately paced, about one every 700 milliseconds per worker, and stop at 80% of the daily cap so replies always have room. See Campaigns.

Errors

Send and settings errors use code EMAIL_ERROR with the HTTP status that describes the cause. Messages are shown verbatim; some are in Spanish.

StatusCodeMessage or cause
400VALIDATION_FAILEDThe body failed validation. See details.
400EMAIL_ERRORContact has no email address
403EMAIL_ERRORContact has opted out — cannot send email
409EMAIL_ERRORNo mailbox selected: Este workspace todavía no tiene un buzón propio para enviar correo. Conecta Gmail, Outlook o Zoho Mail en Integraciones y elígelo en Ajustes → Cuenta → Email → «Sale desde». El correo sale siempre desde tu buzón, nunca desde una dirección de la plataforma.
409EMAIL_ERRORThe selected mailbox stopped working, for example El buzón de envío conectado (gmail) no está disponible: la conexión ya no existe. Vuelve a conectar la aplicación en Integraciones, o cambia el remitente a la dirección de la plataforma en Ajustes → Email. Reconnect the mailbox in Integrations.
409EMAIL_ERRORPUT /api/email/settings/sending with provider: null, or with a connection that is not usable.
422VALIDATION_FAILEDTurning on replies for a mailbox that cannot receive them, such as Zoho Mail.
423EMAIL_ERRORBlocked by compliance: Phone number is on the opt-out suppression list, Contact is on the Do Not Call list, a blocked prefix, or an attempt limit.
429EMAIL_ERRORDaily email send cap reached for this tenant (1000/day). Try again tomorrow or contact support to raise the limit.
429RATE_LIMIT_EXCEEDEDPer-minute rate limit. Honor Retry-After.
403FORBIDDENThe key’s scope does not cover the route.

Limits

LimitValue
Requests to /api/email/*20 per minute per workspace, and fails closed. See Rate limits.
POST /api/email/send and settings changesAlso count against the shared 10 per minute budget for configuration mutations.
Daily email sends1,000 per workspace per UTC day by default; 25,000 on plans with unlimited monthly AI messages. Includes API sends, campaign sends and agent replies.
Reply reserveCampaign sends stop at 80% of the daily cap, keeping at least 25 messages for replies.
Subject998 characters (300 in campaigns).
Body50,000 characters (20,000 in campaigns).
AI repliesCount against the plan’s monthly AI message allowance shared by all text channels. See Billing and usage.

Your mailbox provider’s own sending limits also apply to every email sent from it.

Scopes at a glance

OperationMinimum key scope
Read email settingsread
Send emailwrite
Select the sending mailbox, change the contact address, turn on repliesfull