Integrating · Published 2026-09-28 · Updated 2026-09-29 · AgentiSend
Migrating from Amazon SES
How an Amazon SES SendEmail call maps onto this API, and what is refused on day one.
AgentiSend does not send through Amazon SES. This page is the field map for a move off that API.
The request shape is the same
Amazon SES SendEmail takes FromEmailAddress, Destination and Content (with Subject, HTML and text). Ours is POST /emails with from, to, subject, html and text. The full shape is on the POST /emails page.
Headers
headers takes a map of name to value. From, To, Subject, Message-ID and Date are refused in headers.
Error names resolve
Amazon SES error names are not aliases here. Branch on our code. Every refusal carries message, fix and docs_url. The catalogue is at /docs/errors.
Payload map
Fetched from the SendEmail reference on 2026-09-28 (https://docs.aws.amazon.com/ses/latest/APIReference-V2/API_SendEmail.html). That page does not document an idempotency key.
| Amazon SES | AgentiSend |
|---|---|
FromEmailAddress | from |
Destination | to |
Content subject | subject |
Content HTML | html |
Content text | text |
| Account pause | PUT /v2/email/account/sending with SendingEnabled false. Here that job is POST /limits/kill-all. |
Webhook events
The SendEmail and account-sending pages fetched on 2026-09-28 do not name webhook event types. Amazon SES publishes delivery events to a destination you configure. Each of our types is listed so the gap is visible.
| Our event | Their event |
|---|---|
email.queued | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
email.sent | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
email.delivered | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
email.delivery_delayed | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
email.bounced | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
email.complained | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
email.opened | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
email.clicked | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
email.failed | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
email.scheduled | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
email.canceled | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
email.suppressed | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
domain.created | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
domain.verified | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
domain.failed | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
domain.updated | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
domain.deleted | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
suppression.added | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
suppression.removed | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
limit.warning | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
limit.exceeded | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
agent.approval_requested | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
agent.approved | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
agent.rejected | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
agent.killed | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
contact.created | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
contact.updated | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
contact.deleted | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
segment.joined | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
segment.left | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
segment.created | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
segment.updated | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
segment.deleted | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
broadcast.scheduled | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
broadcast.sent | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
broadcast.updated | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
automation.triggered | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
email.received | Inbound mail is not offered here. Do not build on email.received. |
topic.created | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
topic.updated | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
topic.deleted | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
contact.subscribed | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
contact.unsubscribed | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
trust.warning | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
trust.throttled | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
trust.paused | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
trust.appealed | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
trust.restored | Not named on the pages fetched on 2026-09-28. This row does not guess a name. |
What will refuse on day one
- Inbound mail is not offered. A migration that depends on receiving mail at your domain does not have that path here.
- Keeping customer data in the EU is not offered. Data is processed in the United States.
- There is no business associate agreement for health data.
- A key is created with a send budget. A key that sends past the budget is refused with
agent_budget_exceededrather than invoiced.
The compatibility client, when the import line is all you want to change
That client mirrors one provider's SDK (agentisend/compat-resend). It does not mirror this provider. Call POST /emails with from, to, subject, html and text.
Contacts
POST /contacts accepts first_name, last_name, unsubscribed and properties. unsubscribed: true records that the person asked not to receive marketing mail. GET, PATCH and DELETE /contacts/{id} take the contact id or the email address. The Amazon SES pages fetched on 2026-09-28 cover pricing, SendEmail and the account-sending call. They do not document a contact-import field map, so this guide does not invent one. Add each person with POST /contacts. An API key cannot lift an unsubscribe.
The bounce return path
A domain that already sends from somewhere else often has an MX record on send. Creating it here without a return-path label then uses bounce instead of refusing the domain. To choose the label, pass custom_return_path on POST /domains.
Two differences, both deliberate
Keys have budgets, and the budget is enforced. Set the budget to your real volume before you cut over.
Inbound mail and an EU data region are not part of the move. If the old account uses either, that part stays on the old provider.
What you gain in the move
- Per-key budgets and one kill switch for the account, on Budgets and the kill switch.
- An approval queue for a send the loop guard flags, or one an agent asks about with request_approval, on Approvals.
- Signed webhooks, a replay call, and a dead-letter list, on Webhooks.
Order of operations
- Add and verify your domains here while the old provider still sends.
- Create keys with budgets, one per sender, and point webhooks at the new signing scheme.
- Move a low-volume stream first.
- Move the rest, and keep the old keys alive until you have a week of clean data.