Selzy supports an event tracking API that allows retrieval of user behavioral events from your backend systems.Event data can later be used for organizing mailing lists, creating contact segments, and building omnichannel automation scenarios.
At the moment, only server events are supported, but we’re working on adding other event types.
Make sure events are configured in your Selzy account, otherwise tracking won’t work.
Server events tracking
Send one or more user behavioral events in a single request. Events are tied to an email address and a name, with any additional properties you want to attach.
Method: POST
Authentication
Every request must include your API key in the Authorization header using the Bearer scheme. You can get this API key in your Selzy account settings. Do not make it publically available.
Authorization: Bearer YOUR_API_KEY
Requests without a valid key return the “401 Unauthorized” error.
Headers
| Header | Value | Required |
Content-Type |
application/json |
Yes |
Authorization |
Bearer YOUR_API_KEY |
Yes |
Body
The request body is in JSON format.
{
"events": [ ]
}
The events parameter is a required array that accepts a list of 1 to 25 event objects.
EventObject
| Field | Type | Required | Description |
event.name |
string | Yes | Event identifier. Must match the Event name field, set when creating an event configuration in Event manager. Limits |
event.email |
string | Yes | User's email address. Must be a valid address using ASCII characters in the local part. Punycode domains are supported. Internationalized addresses (RFC 6530–6532) are not supported. |
event.created |
string | No | When the event occurred, in YYYY-MM-DD HH:MM:SS format. Defaults to the time the request is received if omitted. |
| (custom fields) | any | No | Attach any additional properties. Limits |
Example request
curl --request POST \
--url http://https://event.selzy.com/v1/events/ \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"events": [
{
"name": "abandoned_cart",
"email": "user@example.com",
"cart_value": 149.99,
"item_count": 3,
"currency": "USD",
"theObj": {
"arr": [1, "2", "qwerty"],
"foo": "bar"
}
}
]
}'
Responses
Incoming requests are validated in two stages:
- Stage 1 — authentication, headers, and JSON structure. A failure here rejects the entire batch.
- Stage 2 — each EventObject within the batch. Invalid events are dropped; the rest go through
200 OK — All events accepted
{
"success": true
}
Every event in the batch was valid and has been registered.
400 Bad Request — One or more events invalid
{
"code": "invalid_params",
"error": "Error ID: 019CE0BE-B4DD-7DA0-8883-692A90D68E7F. {\"invalidEvents\": [...], \"inactiveEvents\": [...]}"
}
This is a partial success scenario. Valid events in the same batch are still registered. Only the flagged ones are dropped.
The error string embeds a JSON payload with two keys:
- invalidEvents — events that failed schema validation (bad email, name too long, etc.)
- inactiveEvents — events that exist in the system but are currently disabled or deleted
Log the Error ID — it's a UUID you can hand to support to trace the exact request.
401 Unauthorized
{
"code": "unauthorized",
"error": "Error ID: 911B6D1B-ED22-400F-8D9A-A879B8D1B447. Unauthorized."
}
Your API key is missing, malformed, or revoked. Double-check the Authorization header.
500 Internal Server Error
{
"code": "server_error",
"error": "Error ID: 019BE0C6-2625-73C1-AB4A-437B4FBD38C9. Sth went wrong."
}
Something failed on our end. Retry with exponential backoff. If it persists, file a bug with the Error ID.
Limits
| Constraint | Limit |
| Events per request | 25 |
| Properties per event object | 25 |
| event.name length | 1–40 chars |
| POST request body | 130 KB |
Stay within these limits. Requests exceeding the body size limit will be rejected before any events are processed.
What to watch out for
Partial failures are silent to the caller unless you inspect the 400 body. A 400 response doesn't mean your whole batch failed — it means at least one event was bad. Parse the invalidEvents and inactiveEvents arrays out of the error string to know exactly which events didn't land.
event.created is optional but you should always send it. If you omit it, the service timestamps events at ingestion time. That's fine for real-time tracking. If you're replaying historical events or dealing with any buffering in your pipeline, send the actual event timestamp explicitly.
Batch smartly. Grouping events into batches of up to 25 reduces overhead and is the preferred pattern for high-throughput pipelines. Don't send single-event requests if you can avoid it.
Quick-start example
Here's a minimal integration that sends a signup event and handles both success and partial failure:
curl --request POST \
--url http://eventmanager:8096/v1/events/ \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"events": [
{
"name": "signup",
"email": "newuser@example.com",
"created": "2026-05-12 10:00:00"
}
]
}'
Expected response on success:
{
"success": true
}
Partial failure response (for example, an event is disabled in Event Manager):
{
"code": "invalid_params",
"error_id": "019E1FF8-1D95-79AE-B786-FC583BA6E0AD",
"details": {
"invalidEvents": [],
"inactiveEvents": [
{
"name": "signup",
"email": "newuser@example.com"
}
]
}
}
An event in inactiveEvents means it doesn't exist, is disabled, or has been deleted in Event Manager. It can also mean the event name contains a leading or trailing space. Check your event settings in your Selzy account.