API reference

Events

Record what your users do in your product

An event is something a contact did in your product: signed_up, trial_started, order_paid. Events can:

  • start a flow whose trigger is this event name, such as an onboarding sequence on signed_up (Flows);
  • filter a segment, such as "performed order_paid in the last 30 days" (Segments).

Step-by-step example: Onboard users with events.

Record an event

POST /v1/events

Records an event for a contact, identified by email address, created if unknown. Allowed with a sending access key.

email
string required
The contact's address.
name
string required
1 to 100 letters, digits, _, ., : or -, such as trial_started. Case-sensitive.
properties
object
Details of the event, such as {"plan": "pro", "amount": 49}. At most 50 keys and 10 KB.
occurredAt
string
ISO 8601 date-time. Defaults to now.
contact
object
Contact fields to set: firstName, lastName and attributes (merged, same rules as contact attributes).
createContact
boolean
Create the contact when the address is unknown. Default true; with false, an unknown address answers 404.
curl https://api.kulamail.com/v1/events \
  -H "Authorization: Bearer ml_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "ada@example.com",
    "name": "trial_started",
    "properties": { "plan": "pro", "trial_days": 14 },
    "contact": {
      "firstName": "Ada",
      "attributes": { "plan": "pro_trial" }
    }
  }'
201 Created
{
  "id": "e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b",
  "contactId": "0d9e8f7a-6b5c-4d3e-2f1a-0b9c8d7e6f5a",
  "name": "trial_started",
  "properties": { "plan": "pro", "trial_days": 14 },
  "occurredAt": "2026-10-17T09:30:00Z"
}
StatusCodeWhen
400validation_error, bad_requestInvalid name, too many properties, invalid address or attributes
404not_foundUnknown address and createContact is false

Good to know

  • Events don't subscribe anyone. A contact created by an event belongs to no list, so it receives no campaign. Flows triggered by the event still email it.
  • Personalize with attributes, not properties. Flow emails read {{ contact.* }}, not event properties. Send values an email needs (the plan, the trial end date) in contact.attributes.
  • A flow only starts for active contacts. An event from an unsubscribed, bounced or complained contact is recorded but starts nothing.
  • The same event twice starts the flow twice only if the flow lets contacts enter every time. By default, a contact goes through a flow once. Entry rules.
  • Stripe or another tool? No native integration: forward their webhooks to this route.