Skip to content
couryo

Developer guideAutomations

Contacts and events

Contacts with attributes and your app's events through the Couryo API (POST /v1/contacts and POST /v1/events), idempotent, that start and end sequences. No charge per contact.

View as Markdown

A contact is a person who gets your sequences, with attributes to personalize (name, plan, timezone). An event is something that happened in your app (user.signed_up, order.paid): it puts the contact in the sequences that start with that name and ends the ones waiting for it as conversion.

No charge per contact. Having 100 or 100,000 contacts costs the same: only the emails sent count in the plan quota.

Send an event#

cURL
curl https://api.couryo.com/v1/events \
  -H "Authorization: Bearer $COURYO_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: signup-ana-1042" \
  -d '{
    "contact": { "email": "ana@example.com", "attributes": { "name": "Ana", "plan": "free" } },
    "name": "user.signed_up",
    "properties": { "source": "site" }
  }'
Response 201
{
  "id": "cev_8k2m4q9w1x7z3abc",
  "contact_id": "con_3n5p7r9t1v2x4zab",
  "name": "user.signed_up",
  "enrolled": [{ "sequence_id": "seq_q8w2e4r6t8y0u1io", "enrollment_id": "enr_a1s3d5f7g9h2j4kl" }],
  "converted": [],
  "created_at": "2026-10-08T18:30:00Z"
}
  • The contact is created or updated by email (attributes are merged into the existing ones).
  • enrolled lists the active sequences this event started. A contact enters a sequence by event once; to put it back, use the manual entry.
  • converted lists the enrollments closed because this is their conversion event.
  • properties are available in the sequence emails as {{event.source}}.
  • Event name: letters, numbers, ., _, : and -, up to 100 characters.
  • Scope: a send (or admin) key. A ck_test_ key puts the contact in test mode: the sequence emails are captured and never delivered.

Idempotency#

Send Idempotency-Key with every event. The same key with the same body returns the same response, with Idempotent-Replayed: true, and creates no other event or enrollment. A different body with the same key returns 409 idempotency_conflict. Beyond the usual 24 hours, the event keeps the key: repeating it later still returns the same event.

Contacts#

Create or update (upsert)
curl https://api.couryo.com/v1/contacts \
  -H "Authorization: Bearer $COURYO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "email": "ana@example.com", "attributes": { "plan": "pro", "city": null } }'
  • Returns 201 when it creates and 200 when it updates.
  • Attributes are plain values (text, number, true or false). null removes the attribute.
  • The timezone attribute (for example America/New_York) is used by waits "until HH:mm in the contact's time zone".
  • subscribed is false when the address is on the suppression list for marketing (unsubscribe, bounce, complaint or manual). Unsubscribes always win: the contact gets no marketing sequences, and a running sequence ends right away.
Method and path Scope What it does
POST /v1/contacts send creates or updates by email
GET /v1/contacts read lists, newest first; q searches part of the email, limit and starting_after paginate
GET /v1/contacts/{id} read one contact, by id (con_...) or by email
PATCH /v1/contacts/{id} admin changes the email or the attributes
DELETE /v1/contacts/{id} admin deletes the contact and its events; it leaves the sequences (suppression stays)
POST /v1/events send records an event of the contact

Through MCP#

The track_event (scope send, with idempotency_key) and upsert_contact (scope send) tools do the same through the MCP server, so your AI agent can record sign-ups and purchases.