> ## Documentation Index
> Fetch the complete documentation index at: https://docs.senderz.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Event ingest

> Send custom events into Senderz to create profiles and trigger flows.

Send your own events into Senderz — from a backend, a POS, a subscription system, anything. An event resolves or creates the contact and enrols any `custom_event` flow scoped to that event name.

## Send an event

```http theme={null}
POST /api/v1/events/ingest
```

Authenticated with the [`X-Sender-Tenant` + `X-API-Key`](/en/api-reference/authentication) headers.

### Body

<ParamField body="event" type="string" required>
  The event name (1–128 chars), e.g. `subscription_renewed`. Flows enrol on a
  matching `custom_event` name.
</ParamField>

<ParamField body="contactId" type="string">
  Existing contact UUID. If omitted, the contact is resolved by email/phone or
  created.
</ParamField>

<ParamField body="email" type="string">
  Contact email (used to resolve or create).
</ParamField>

<ParamField body="phone" type="string">
  Contact phone (used to resolve or create), max 32 chars.
</ParamField>

<ParamField body="firstName" type="string">
  Optional, set on a newly-created contact. Max 255.
</ParamField>

<ParamField body="lastName" type="string">
  Optional, set on a newly-created contact. Max 255.
</ParamField>

<ParamField body="properties" type="object">
  Arbitrary event properties. Stored on the event and available as flat merge
  tags in flow templates. Capped at \~8 KB.
</ParamField>

<Info>
  A contact is created only if you supply an `email` or `phone` and none
  matches. A brand-new contact imports with source `api` and fires
  `contact_created` flows.
</Info>

### Response

<ResponseField name="status" type="string">
  `ok` when processed, `no_contact` when no contact could be resolved or
  created.
</ResponseField>

<ResponseField name="enrolled" type="integer">
  Number of flows the event enrolled the contact into.
</ResponseField>

<ResponseField name="contactId" type="string | null">
  The resolved/created contact UUID.
</ResponseField>

<ResponseField name="created" type="boolean">
  `true` if this event created the contact.
</ResponseField>

### Example

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.senderz.app/api/v1/events/ingest \
    -H "X-Sender-Tenant: your-workspace-slug" \
    -H "X-API-Key: your-api-key" \
    -H "Content-Type: application/json" \
    -d '{
      "event": "subscription_renewed",
      "email": "dana@example.com",
      "firstName": "Dana",
      "properties": { "plan": "pro", "amount": 249.90 }
    }'
  ```

  ```json Response theme={null}
  {
    "success": true,
    "data": {
      "status": "ok",
      "enrolled": 1,
      "contactId": "b2f1c0de-1234-4a56-9abc-0123456789ab",
      "created": false
    }
  }
  ```
</CodeGroup>

## Using event properties in flows

Properties become flat merge tags in flow email/SMS templates — `{{plan}}`,
`{{amount}}`, and so on. Identity and compliance tags (`{{firstName}}`,
`{{unsubscribeUrl}}`) are reserved and can't be shadowed. See [Flows](/en/messaging/flows).

<Note>
  Events are deduplicated per contact/event/minute, so a burst of identical
  events won't create duplicate timeline rows or re-enrol repeatedly within
  the same minute.
</Note>
