Questions · Updated 2026-09-27
How do I send an email when a custom event happens?
Send an email when a custom event happens by creating an automation with POST /automations whose trigger is type custom_event with an event_name and whose step is kind email, enabling it with POST /automations/{id}/enable, then posting the event with POST /events and the contact_id of the recipient.
Send an email when a custom event happens in three calls. POST /automations drafts the automation — its summary is "Draft an automation: a trigger plus ordered steps (email, webhook, A/B split)." — with a trigger whose type is custom_event and an event_name, and a step whose kind is email. POST /automations/{id}/enable turns it on. Then your code calls POST /events with that name and the contact_id of the person, and every enabled automation with that trigger fires.
The automation
The trigger is {"type": "custom_event", "event_name": "order.shipped"}; the schema refuses a custom-event trigger without event_name with "custom_event triggers need an event_name." The email step has kind email, from_address on a domain that has finished verification, subject, and content. content takes the shape templates use; the API's own sentence is that "content must be {"mode": "markdown", "source": "..."} (mode markdown or html), or {"mode": "block", "blocks": [...]}." The subject and body can carry placeholders such as {{first_name}}, filled from the contact's properties at send time; the address itself is available as email. The other step kinds are webhook, with a public url that receives a JSON POST, and ab_split, with variant, ratio and split.a and split.b step lists. Steps run in the order you list them.
curl -sS -X POST https://api.agentisend.com/automations \
-H "Authorization: Bearer $AGENTISEND_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"name":"yourdomain.com","steps":[],"trigger":{}}'201
{
"created_at": "2026-09-04T09:14:00Z",
"current_version": 1,
"id": "9c8f8f0e-3d1a-4d3f-9a1e-2b7c1a0f5e42",
"name": "yourdomain.com",
"status": "string",
"steps": [],
"trigger": {},
"updated_at": "2026-09-04T09:14:00Z"
}POST /automations/{id}/enable — "Snapshot trigger+steps as an immutable version and start firing." An enabled automation cannot be edited; PATCH /automations/{id} answers "Disable the automation before editing; enabling snapshots a new immutable version." POST /automations/{id}/disable is "Stop firing. Versions stay for audit." Automations covers the other trigger types.
Firing the event
POST /events — "Ingest a custom event. Matching enabled automations fire synchronously (202 once enqueued)." The body is name, contact_id and data; the reply is fired, the number of automations that ran. It is a management call, so use a full_access key.
curl -sS -X POST https://api.agentisend.com/events \
-H "Authorization: Bearer $AGENTISEND_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"name":"yourdomain.com"}'201
{
"fired": 1
}The recipient is the contact, created or updated with POST /contacts — "Create or update a contact by email. Properties are typed from their value and auto-created — nothing needs pre-declaring." A contact is someone who gave you their address for this. The email step is skipped when the contact's status is not active; the run records skipped: contact is unsubscribed. The mail goes out with one-click unsubscribe headers and runs every send check, so the plan inclusion, a suppression or an unverified domain refuses it the way it refuses POST /emails.
The first POST /events with a new name registers it. GET /events is "Every custom event this account has declared, with its typed schema and when it was last seen." PATCH /events/{id} — "Declare or re-declare an event’s fields. Adding a field to a strict event starts refusing payloads that omit it — that is the point." Its schema maps each field to string, number, boolean, date or enum, and strict true refuses fields the schema does not declare. A typo in an event name is then a refusal that names the field, not a silent event nobody listened for.
Did it run
GET /automations/{id}/runs — "Every time this automation fired, newest first — the answer to "did it run?", which the engine used to throw away." Each run has status (running, succeeded or failed), event_name, contact_id and version_number. GET /automations/{id}/runs/{run_id} adds steps[], each with status succeeded, skipped or failed, output — message_id and to for an email step — and error, the refusal sentence when a send was not accepted. The message_id is then readable at GET /emails/{id}. Every firing also emits automation.triggered on webhooks.