Record a custom event for a contact
POST
https://emails.routeon.io/api/v1/events
Appends a behavioural event ("contact X did Y with properties Z") to the contact's event log. The contact is resolved by the exact email as stored (no case normalisation); an unknown or erased contact is a 404. API-key surface only — portal sessions are refused with 403. The event is stored synchronously and always answers 201.
Request body
{
"name": "order.completed",
"email": "user@example.com",
"properties": [],
"occurred_at": "2024-01-01T00:00:00Z"
}
Responses
201
Event recorded.
404
Resource not found within tenant scope
422
Body/query/params failed validation. meta.reason=VALIDATION_ERROR, meta.validation_error[] carries {field, message} per failure ('' field = show the message as-is). A request that parsed but broke a schema rule answers 422. An id in the path, or in the query of an operation that does not take `cursor` and `per_page`, that is not a UUID is rejected with 400 VALIDATION_ERROR before the request is handled. Every query parameter of a list that takes `cursor` and `per_page` (including id filters) is checked before any data is read and answers 422 with that parameter as the field: an invalid, empty or repeated value, or a text value containing the NUL character; unknown parameters are ignored. Sender resolution reports through meta.details.code: invalid_from_address (the `from` string is not `local@domain` or `Name <local@domain>`), from_domain_mismatch (its domain is not a verified domain of the tenant), sender_not_configured (the local part of `from` matches no sender profile of the tenant, or, without `from`, no route and no default profile resolves for this send) and sender_not_found (the referenced profile id is unknown to the tenant). A send with neither `domain_id` nor `from` reports through meta.validation_error (field `domain_id`), not meta.details.code; so do `send_at` failures (field `send_at`, message `send_at_too_far` or `send_at_invalid`). content_moderation_rejected: POST /transactional/send and /transactional/send-batch verify the template's active version before accepting the send, and a rejected verdict fails the request. The same applies to ready-made `subject`/`html`/`text` content. The check cannot be switched off. content_too_large: ready-made content is within the size limits but too big for the moderation check (`details.code`). `html` plus `text` over 4 MB is a plain VALIDATION_ERROR with meta.validation_error field `html`. invalid_variable: a variable name is reserved (`unsubscribe_url`, `this`, `constructor`, `prototype`, or any name starting with `@` or `__`), or `custom` is not an object of strings; the offending name is in `validation_error`. A send that carries both `template_id` and content, or neither, or content with an empty `subject` or with neither `html` nor `text`, is 422 `VALIDATION_ERROR`. new_account_daily_limit_exceeded: POST /campaigns/{id}/send for a new account whose audience exceeds its reduced daily cap (meta.details.limit, needed); it can never fit, so a scheduled launch goes back to draft. throttle_per_hour_too_high: POST /campaigns and PATCH /campaigns/{id} with `throttle_per_hour` above the platform ceiling (meta.validation_error field `throttle_per_hour`, meta.details.max carries the ceiling). PATCH /domains/{id}/warmup accepts only `{state}`; any other property, including `today_cap_override`, or an empty body is 422.
Example response · 201
{
"status": "success",
"http_code": 0,
"payload": {
"id": "00000000-0000-0000-0000-000000000000",
"contact_id": "00000000-0000-0000-0000-000000000000"
},
"meta": []
}
Example response · 404
{
"status": "error",
"http_code": 0,
"payload": [],
"meta": {
"reason": "VALIDATION_ERROR",
"validation_error": [
{
"field": "string",
"message": "string"
}
],
"details": []
}
}
Example response · 422
{
"status": "error",
"http_code": 0,
"payload": [],
"meta": {
"reason": "VALIDATION_ERROR",
"validation_error": [
{
"field": "string",
"message": "string"
}
],
"details": []
}
}