Send an outbound message via the API
You’ll use this when you want to create an outbound or programmatic conversation in Atender from another system.
This endpoint is not the Custom Channel ingress flow. For custom in-app chat, marketplace inboxes, or two-way external channels where customer messages enter Atender and agent replies are delivered back to your system, use a custom-channel integration instead. That flow uses POST /api/v1/channels, POST /api/v1/channels/{id}/messages, Idempotency-Key, attachment support, and reply delivery through the webhook configured for the channel.
Before you start
- A tenant API key with the
conversations:writescope. Generate one in Settings → API & MCP if you don’t have one yet. - Decide on an externalReference scheme for your conversations. This is a string you supply that uniquely identifies the conversation in your system — it makes retries safe.
Request
POST https://YOUR-ATENDER-HOST/api/v1/conversations/outbound
Headers:
Authorization: Bearer sa_live_xxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json
Body:
{
"externalReference": "your-system-123",
"contact": {
"email": "customer@example.com",
"name": "Optional Name",
"phone": "+15551234567"
},
"subject": "Order #4521 — refund request",
"channel": "email",
"outboundContext": {
"originalMessage": "The customer's original message body",
"sentAt": "2026-05-11T09:00:00Z",
"metadata": { "source": "shopify" }
},
"tags": ["refund", "vip"],
"customFields": { "order_id": "4521" }
}
Required fields
externalReference— string (max 255) — Unique per tenant. Reusing the same reference returns 409 — useful for safely retrying network failures.contact.email— string — Valid email. Atender uses this to match (or create) a contact.subject— string — Conversation subject.
Optional fields
contact.name— string — Contact’s full name. If omitted, the email’s local part is used.contact.phone— string — Contact’s phone number.channel— enum — One ofemail(default),messenger,sms,phone,voice,whatsapp,webchat,amazon. Tag the conversation with the channel it really came from in your system.outboundContext.originalMessage— string — The first message body as plain text.outboundContext.originalHtml— string — The first message body as HTML, if you have it.outboundContext.sentAt— string — ISO timestamp of when the message was originally sent.outboundContext.metadata— object — Free-form metadata for your own use.tags— string[] — Tag slugs to attach to the conversation. Slugs that don’t exist will be ignored.customFields— object — Custom-field values keyed by field slug.
Response
On success, you get a 201 Created. Every v1 response wraps its payload in a
data object — read body.data, not body:
{
"data": {
"id": "...",
"externalReference": "your-system-123",
"replyToAddress": "reply+abc123@reply.your-atender-host",
"status": "waiting",
"createdAt": "2026-05-11T09:00:00.000Z"
}
}
status here is the outbound status of the conversation — waiting until the
contact replies. The conversation itself is created Active, which is where you will
find it in the inbox.
Verify it worked
- Open the inbox in Atender. The new conversation appears in Active, assigned to the team that owns the channel (or the default team if no routing rules match).
- Reply to the conversation in Atender. If you’ve also subscribed to webhook events, your server will receive a
message.sentevent with the reply payload — that’s how you ship the agent’s reply back to wherever the customer is.
Errors to handle
400— Validation error — Check required fields. The response body haserrors[]pointing to the offending fields.401— Bad or missing API key — Re-check theAuthorizationheader.403— Wrong scope — Your API key needsconversations:write.409—externalReferencealready used — A conversation already exists for that reference. Use a different reference, or fetch the existing conversation by reference.5xx— Server error — Retry with exponential backoff. Because you supplied anexternalReference, a retry that succeeds after a 5xx will not create a duplicate.
Patterns
- Use this endpoint for outbound or programmatic conversations. If you are building a custom in-app chat, marketplace inbox, or another two-way external channel, use the custom-channel flow instead so inbound messages, attachments, idempotent retries, and Atender replies are handled through the channel and its configured webhook.
- Always supply an
externalReference. It is what makes retries safe. If your network blip causes you to receive a 5xx but the conversation was actually created, your retry will get a 409 and you can move on. - Use the
channelfield honestly. If the conversation came from Instagram, setchannel: "messenger". The channel badge in the inbox is set from this field, and downstream filters and analytics depend on it. - Store the returned
idalongside yourexternalReferenceso you can correlate later. You’ll need it to fetch the conversation, push replies, or correlate webhook events.
Limits
- The
externalReferencemust be unique per tenant. Conflicts return 409. - Validation enforces a max length of 255 on
externalReferenceand a valid email oncontact.email.