Skip to main content
An event is something a contact did, with a name such as order.completed. Give it a goalType and it becomes a conversion (a goal), which can also carry money. The browser version of the same call is POST /public/events.

Recording a purchase

The response is 201 { id, contactId, deduplicated: false, status: "recorded" }. Sending the same externalId again answers 200 with status: "duplicate".

Rules

  • event must be lowercase letters, digits, ., _, / or -, starting with a letter or digit, up to 100 characters.
  • Identify the contact with email or contactId, and send one of the two.
  • goalType is one of purchase, subscription, lead, signup, booking, trial or other. purchase and subscription require a value. A value is only accepted together with a goalType, and a negative value records a refund.
  • currency is a 3-letter ISO code.
  • occurredAt is an ISO date-time and defaults to now.
If the email or ID matches no contact, the event is still stored with contactId: null. That is not an error, and no contact is created. An event with no contact starts no journey and fires no webhook. Create the contact first if you need it.

What an event does

When the event matches a contact:
  • It starts every active journey whose API Event trigger listens for that event name, and every website-event journey that listens for it. See Journeys.
  • It fires the EVENT_RECORDED webhook.
  • It updates the contact’s last-activity time.
  • It feeds goal-based segment rules, such as lifetime value, purchase count and last purchase.

Batch import

POST /v1/events/import takes { "events": [ … ] } with 1 to 50 events. The whole body is checked against the event schema first, so one malformed row fails the entire call with 400 Invalid input. After that, each row is recorded on its own and reported by index. A row that is missing email/contactId, or that breaks a goal rule, gets an error without affecting the others. The call answers 400 only when every row failed.