Skip to main content
Events are timestamped records of things contacts do — in your store, your app, or anywhere else. Each event lands on the contact’s timeline, becomes available to segments, and can trigger automations. All requests require authentication.

Track an event

POST /api/v1/events Records an event and attaches it to a contact, creating that contact if they don’t exist yet. Returns 201 Created.

Request

eventName
string
required
Human-readable event name, e.g. Add to Cart. Any non-empty string is accepted — see How event names become types.
uniqueIdentifier
string
required
Idempotency key for this occurrence — a cart ID, checkout ID, or order ID. See Idempotency; getting this wrong is the most common integration bug.
contact
object
required
Who the event belongs to. Must contain either contactUUID, or at least one of email / phone.
eventData
object
required
Flat key-value object describing the event. Must not be empty, and must not contain nested objects.
description
string
Optional human-readable description, used when registering the event schema.

The contact object

Field names are snake_case. Unrecognized fields are rejected with 400, so emailAddress or firstName will fail — use email and first_name.
When this endpoint creates a new contact, email_opt_in defaults to NON_SUBSCRIBED. A contact created this way will not receive marketing email until they opt in. Send email_opt_in explicitly if you have consent.
Example request

Response

201 Created
The event row is written immediately. Schema registration and automation delivery happen asynchronously a moment later.

Idempotency

uniqueIdentifier is what stops a retried HTTP call from emailing your customer twice. Uniqueness is scoped to the combination of your organization, the event type, and the identifier. So: use a stable ID per real-world occurrence (order_55501), and a new ID when you want the flow to fire again.
Two things still happen on a resubmit: the contact is upserted (so profile fields update), and the event schema is re-registered. Only the automation trigger is suppressed.

How event names become types

You send a human-readable eventName. Retainful normalizes it and prefixes it with your integration’s key to produce the internal event type you’ll see in the dashboard: Normalization uppercases the name and replaces every run of non-alphanumeric characters with a single underscore. The REST-API. prefix comes from the integration the API key belongs to. There is no fixed list of event names. Any non-empty string works — Quiz Completed and Subscription Renewed are as valid as the commerce names above.
Because names are normalized, Add to Cart, add-to-cart, and ADD TO CART all collapse to the same event type and share the same idempotency namespace. Pick one spelling and keep it stable — automations and segments are bound to the normalized type, so renaming an event orphans them.
Keep event names under about 50 characters. The internal type is stored in a 64-character column and includes the integration prefix; longer names fail on write.

eventData rules

  • Must not be empty.
  • No nested objects. A nested object is rejected with 400 and a message naming the offending field. Flatten it: shipping_city rather than shipping: { city }.
  • Values may be strings, numbers, or booleans. Types are inferred and preserved for use in automation filters, so send "amount": 19.99 if you want to compare it numerically.
  • Useful keys: product_id, product_name, quantity, price, currency, order_id, checkout_id, total, amount.
  • Every key becomes available in automation trigger filters and email personalization.

Register an event schema

POST /api/v1/events/register Registers an event type and its shape without attaching it to a contact. Use it so the event’s fields appear in the automation builder’s filter and personalization pickers before any real event has fired — letting marketing build the flow while engineering ships the integration.
eventName
string
required
The event name to register.
eventData
object
required
An example payload. Retainful infers the field names and types from it.
description
string
Optional description of the event.
This endpoint registers a schema only. It does not store an event, does not appear on any contact’s timeline, and does not trigger automations. It accepts neither contact nor uniqueIdentifier — sending either returns 400. Use POST /api/v1/events for everything tied to a real shopper.

Designing good events

  • Name by fact, not intent: Subscription Renewed (what happened), not Send Renewal Email (what you want done). Marketing decides the response in the automation builder.
  • Keep names stable. Renaming orphans the automations and segments built on the old type.
  • Include the fields you’ll filter on. If a flow should only fire for high-value renewals, send amount.
  • Send one event per real occurrence, with a uniqueIdentifier that matches that occurrence’s own ID.

Event-triggered automations end to end

  1. POST /api/v1/events/register with your event shape.
  2. In the dashboard, create an automation triggered by your event, with filters like plan is annual.
  3. POST /api/v1/events from production whenever it happens, with a fresh uniqueIdentifier each time.
  4. Watch contacts flow through in the automation’s analytics.

Errors

See Errors for the response shape.