Elocuenti Events API
The events API from Elocuenti — 1 operation(s) for events.
The events API from Elocuenti — 1 operation(s) for events.
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
find_apisBrowse and filter every API in the catalog.get_api_artifactsOne API's artifacts, grouped by type.get_openapiThe primary OpenAPI for this API.find_similar_apisAPIs that look like this one.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.curl "https://apis.io/api/v1/apis/elocuenti-com:elocuenti-com-events-api"
curl "https://apis.io/api/v1/apis?limit=25"
Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: Elocuenti Events API
version: 1.0.0
description: 'The public REST API of Elocuenti: contacts, conversations, funnels, WhatsApp templates, knowledge base, automations, events, AI calls, webhooks and charges (Colombia).
Authenticate every request with `Authorization: Bearer `. A key that starts with `eloc_live_` acts in production; one that starts with `eloc_sand_` is a sandbox key: it never messages or calls your customers, and the **Sandbox** line of each operation says what it does instead. Create keys in Settings → API Keys.
Guides and reference: https://www.elocuenti.com/api/docs'
servers:
- url: https://www.elocuenti.com
tags:
- name: Events
paths:
/api/v1/events:
post:
tags:
- Events
summary: Send events of your contacts
description: 'Records up to 100 events at once: what your customers do in your own systems (a purchase, a sign-up, a form, a visit), so it stays in each contact''s history. Nothing is sent to your customers and the events are not reported to advertising platforms. See them in the dashboard, in Analytics → Explorer → Live Events (filter Custom).
Each event names its contact with at least one of `contact_id`, `external_id`, `phone` or `email` (all the same contact if you send several), a name in `event`, your own `event_id` and, if you want, `properties` and `occurred_at`. The contact must already exist (`POST /api/v1/contacts`; one created a moment ago can receive events at once).
Each event is judged on its own: the request is accepted (`202`) when its shape is right, and the answer says how many were recorded, which were already known (`duplicates`) and which were rejected, by position, with the field and the reason: fix only those and send them again. The same `event` with the same `event_id` is never recorded twice, so repeat a request whenever you are not sure it arrived (an `event_id` belongs to its `event` name). This operation does not use `Idempotency-Key`.
`properties` holds up to 30 keys and 4 KB; `occurred_at` can be up to 30 days in the past and 24 hours ahead. A key records up to 10,000 new events in each 24-hour period, counted from its first event.
**Errors:** `422` when the request itself is wrong (no events, more than 100, a field that is not listed): nothing is recorded. `413 payload_too_large`. `429 daily_limit_exceeded` past the day''s limit: nothing in that request is recorded. `503 service_unavailable`: nothing was recorded, try again. `503 outcome_unknown`: send the same request again; what was already recorded comes back in `duplicates`.
**Permission:** `events:write` · **Sandbox:** checked, nothing is saved or sent (`simulated: true`) · **Max body:** 512 KB'
operationId: send_events
security:
- ApiKey: []
x-access:
permission: events:write
sandbox: simulate
maxBody: 512 KB
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/EventsBody'
examples:
purchase:
summary: A purchase, by phone
value:
events:
- phone: '+573001234567'
event: purchase
event_id: order-1042
properties:
value: 189900
currency: COP
order_number: '1042'
occurred_at: '2026-10-27T15:04:05Z'
mixed:
summary: Several contacts and kinds of events
value:
events:
- external_id: crm-10042
event: lead
event_id: form-88
properties:
source: landing
plan: pro
- email: ana@ejemplo.com
event: purchase
event_id: order-1043
properties:
value: 59900
currency: COP
required: true
responses:
'202':
description: The request was accepted. Look at `rejected` for the events that were not recorded. With a sandbox key, `simulated` is `true` and nothing was recorded.
headers:
X-Request-Id:
$ref: '#/components/headers/X-Request-Id'
X-RateLimit-Limit:
$ref: '#/components/headers/X-RateLimit-Limit'
X-RateLimit-Remaining:
$ref: '#/components/headers/X-RateLimit-Remaining'
X-RateLimit-Reset:
$ref: '#/components/headers/X-RateLimit-Reset'
content:
application/json:
schema:
$ref: '#/components/schemas/EventsResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'413':
$ref: '#/components/responses/PayloadTooLarge'
'422':
$ref: '#/components/responses/UnprocessableEntity'
'429':
$ref: '#/components/responses/TooManyRequests'
'500':
$ref: '#/components/responses/InternalError'
'503':
$ref: '#/components/responses/ServiceUnavailable'
components:
schemas:
EventItem:
type: object
properties:
contact_id:
type: string
format: uuid
pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$
description: The identifier of the contact, as returned by the contacts operations.
examples:
- 00000000-0000-4000-8000-0000000000d1
title: Contact Id
external_id:
type: string
minLength: 1
maxLength: 255
description: The identifier the contact has in your own system, the same one you sent when you created the contact (`external_id`).
examples:
- crm-10042
title: External Id
phone:
type: string
pattern: ^\+[1-9]\d{6,14}$
description: 'The contact''s phone number in international format (E.164): a plus sign, the country code and the number, with no spaces or dashes. Old regional formats (for example a Mexican mobile written with the extra 1 after +52) are matched to the same contact as the current one.'
examples:
- '+573001234567'
title: Phone
email:
type: string
maxLength: 254
format: email
description: 'The contact''s email address. If two contacts share it, the event is refused as `ambiguous`: name the contact with `phone`, `external_id` or `contact_id` instead.'
examples:
- ana@ejemplo.com
title: Email
event:
type: string
pattern: ^[a-z][a-z0-9_.]{0,63}$
description: 'The name of the event: lowercase letters, digits, `_` and `.`, from a letter, up to 64 characters. Use the same name every time the same thing happens. `purchase` and `lead` are the usual ones. A new name needs no setup: it exists from the first time you send it.'
examples:
- purchase
title: Event
event_id:
type: string
pattern: ^[A-Za-z0-9_.:-]{1,128}$
description: 'Your own identifier for this occurrence (an order number, a form id): letters, digits and `_ . : -`, from 1 to 128 characters. Sending the same `event` with the same `event_id` again never records it twice, so you can retry safely. The same `event_id` under another `event` name is another event.'
examples:
- order-1042
title: Event Id
properties:
$ref: '#/components/schemas/EventProperties'
occurred_at:
type: string
format: date-time
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
description: When it happened (ISO 8601, with a time zone). Up to 30 days in the past and 24 hours ahead. An event outside that window is rejected, unless it was already recorded (then it comes back as a duplicate). If you leave it out, it is the moment we receive the event.
examples:
- '2026-10-27T15:04:05Z'
title: Occurred At
required:
- event
- event_id
additionalProperties: false
description: One event of one contact. Name the contact with at least one of `contact_id`, `external_id`, `phone` or `email`; if you send several, they must all be the same contact.
title: EventItem
EventsBody:
type: object
properties:
events:
minItems: 1
maxItems: 100
type: array
items:
$ref: '#/components/schemas/EventItem'
description: 'The events to record, from 1 to 100. Each one is checked on its own: the ones that pass are recorded and the answer lists, by position, the ones that did not.'
title: Events
required:
- events
additionalProperties: false
description: The events to record.
title: EventsBody
EventsResult:
type: object
properties:
accepted:
type: integer
description: How many events are new and were recorded. With a sandbox key, how many would have been.
examples:
- 2
title: Accepted
duplicates:
type: array
items:
type: integer
description: The positions of the events whose `event` and `event_id` were already recorded. They are not recorded again and you do not need to fix anything.
examples:
- - 1
title: Duplicates
rejected:
type: array
items:
$ref: '#/components/schemas/EventRejection'
description: The events that did not pass, one entry each. Fix those and send them again; the rest are already done.
title: Rejected
required:
- accepted
- duplicates
- rejected
description: What happened to each event of the request.
examples:
- accepted: 1
duplicates:
- 1
rejected:
- index: 2
details:
- field: events.2.phone
issue: not_found
title: EventsResult
EventRejection:
type: object
properties:
index:
type: integer
description: The position of the event in your request, starting at 0.
examples:
- 2
title: Index
details:
type: array
items:
type: object
properties:
field:
type: string
description: The field that failed, as a path that starts with its position.
examples:
- events.2.phone
title: Field
issue:
type: string
description: 'What is wrong: an issue any field can have (`required`, `invalid_type`, `invalid_format`, `too_small`, `too_big`, `invalid_value`, `unrecognized_field`) or one specific to events: `identity_required` (no contact named), `not_found` (no contact with that value), `ambiguous` (two contacts share it, like an email: name the contact with `phone`, `external_id` or `contact_id`) or `identity_conflict` (the values name different contacts). It never repeats the value you sent.'
examples:
- not_found
title: Issue
required:
- field
- issue
description: One entry per field that failed.
title: Details
required:
- index
- details
description: An event that was not recorded, and why.
title: EventRejection
Error:
type: object
properties:
error:
type: string
description: What happened and what to do about it, in English or Spanish (it follows the `Accept-Language` header). Meant for a person; do not parse it.
examples:
- The API key has expired. Create a new key in Settings → API Keys and use it instead.
title: Error
code:
type: string
enum:
- missing_api_key
- invalid_api_key
- api_key_expired
- api_key_inactive
- forbidden
- legacy_key_not_allowed
- creator_cannot_attend_conversations
- api_key_owner_inactive
- sandbox_not_supported
- not_found
- media_expired
- media_not_ready
- conflict
- template_name_taken
- template_being_created
- template_content_rejected
- template_limit_reached
- whatsapp_not_connected
- whatsapp_connection_unavailable
- whatsapp_rate_limited
- webhook_limit_reached
- rotation_in_progress
- webhook_url_taken
- webhook_content_not_allowed
- webhook_inactive
- contact_cannot_be_called
- call_outside_window
- calls_not_available
- phone_line_unavailable
- call_capacity_reached
- call_in_progress
- call_not_placed
- request_in_progress
- idempotency_key_reused
- invalid_json
- invalid_request
- payload_too_large
- rate_limit_exceeded
- daily_limit_exceeded
- temporary_conflict
- outcome_unknown
- service_unavailable
- internal_error
description: A stable identifier of the error. Branch on this, not on `error`. Every code is explained in the error guide.
examples:
- api_key_expired
title: Code
request_id:
type: string
description: The identifier of this request. Give it to support if you need help.
examples:
- 3f9a1c07d2b4
title: Request Id
details:
description: 'On `invalid_request`: one entry per field that failed. A `404` or `409` that blames one field of your request (for example, an id that does not exist) names it here too.'
type: array
items:
$ref: '#/components/schemas/FieldIssue'
title: Details
doc_url:
type: string
description: The section of the error guide that explains this code.
examples:
- https://www.elocuenti.com/api/docs/errors#api_key_expired
title: Doc Url
required:
- error
- code
- request_id
- doc_url
description: The body of an error answer.
title: Error
EventsResponse:
type: object
properties:
data:
$ref: '#/components/schemas/EventsResult'
request_id:
type: string
description: The identifier of this request. Give it to support if you need help.
examples:
- 3f9a1c07d2b4
title: Request Id
simulated:
description: 'Present, and `true`, only when you used a sandbox key: the request was checked and answered as if it had worked, but nothing was saved.'
type: boolean
const: true
title: Simulated
required:
- data
- request_id
title: EventsResponse
FieldIssue:
type: object
properties:
field:
type: string
description: 'The field that failed: its name, or a dotted path for a nested one (`items.0.name`).'
examples:
- name
title: Field
issue:
type: string
description: 'What is wrong with it: `required`, `invalid_type`, `invalid_format`, `too_small`, `too_big`, `invalid_value` or `unrecognized_field`. An operation that refers to another resource by id can also say `not_found` (that id does not exist in your business) or a reason specific to it, which its page explains. It never repeats the value you sent.'
examples:
- required
title: Issue
required:
- field
- issue
description: One field that did not pass validation.
title: FieldIssue
EventProperties:
type: object
propertyNames:
type: string
pattern: ^[a-z][a-z0-9_]{0,63}$
additionalProperties:
anyOf:
- type: string
maxLength: 500
- type: number
- type: boolean
- type: 'null'
description: What happened, as a flat object of up to 30 keys. Keys are lowercase letters, digits and underscores, from a letter, up to 64 characters. Each value is a text (up to 500 characters), a number, `true`/`false` or `null`; nested objects and lists are refused. All of it, as JSON, fits in 4096 bytes.
examples:
- value: 189900
currency: COP
order_number: '1042'
title: EventProperties
headers:
X-RateLimit-Reset:
description: 'The moment that window ends, as a Unix timestamp (seconds since 1970-01-01 UTC). It is not the seconds to wait: for that, use `Retry-After`.'
schema:
type: integer
Retry-After:
description: 'When the answer asks you to wait (a rate limit, a request still being processed, a temporary failure): how many seconds to wait before sending the request again.'
schema:
type: integer
X-RateLimit-Limit:
description: How many requests the key may make in the window that applies to this answer.
schema:
type: integer
X-RateLimit-Remaining:
description: How many requests are left in that window.
schema:
type: integer
X-Request-Id:
description: The identifier of this request, also in the body as `request_id`. Send your own `X-Request-Id` (up to 128 letters, digits, `.`, `_`, `:` or `-`) and it comes back unchanged, so you can match your logs with ours.
schema:
type: string
responses:
TooManyRequests:
description: 'Too many requests.
- `whatsapp_rate_limited`: WhatsApp is limiting how fast the business can create or delete templates. Wait a few minutes and try again with the same Idempotency-Key. WhatsApp allows a limited number of new templates per hour.
- `call_capacity_reached`: All the business''s phone lines for AI calls are busy right now. Nothing was dialed. Wait the seconds in the `Retry-After` header and send the request again; some lines are always kept free for your team''s own calls.
- `rate_limit_exceeded`: Too many requests: the limit was reached. Wait the seconds in the `Retry-After` header and try again. Your key''s limits are in `GET /api/v1/me`.
- `daily_limit_exceeded`: This API key reached the number of changes it can make in a day for this operation. Nothing was changed. Send only what changed since your last sync, or wait until tomorrow; the day''s limit is in the operation''s page.'
headers:
Retry-After:
$ref: '#/components/headers/Retry-After'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
InternalError:
description: 'Something went wrong on our side.
- `internal_error`: An unexpected error happened on our side. Do not blindly repeat a write: check whether it was applied, or repeat it with the same Idempotency-Key. If it keeps failing, write to us with the `request_id`.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
BadRequest:
description: 'The request could not be read.
- `invalid_json`: The request body is not valid JSON. Send a JSON object with the `Content-Type: application/json` header.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
PayloadTooLarge:
description: 'The request body is too large.
- `payload_too_large`: The request body is bigger than this operation accepts. Send less data in a single request; each operation''s page states its maximum size.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
Forbidden:
description: 'The API key is valid but cannot do this.
- `forbidden`: The API key does not have the permission this operation needs. Create a new key with the permission the operation documents: the permissions of a key are set when it is created.
- `legacy_key_not_allowed`: This operation is not available to the original business key (the one that starts with `eloq_` or `elocuenti_`). Create an API key in Settings → API Keys (it starts with `eloc_live_` or `eloc_sand_`) and use it.
- `creator_cannot_attend_conversations`: The team that attends conversations is not shown to this key, because the person who created it does not attend conversations. Read the team with a key created by an owner or an administrator.
- `sandbox_not_supported`: This operation is not available with a sandbox key. Use a production key (`eloc_live_`), or check the operation''s page to see how it behaves in sandbox.
- `webhook_content_not_allowed`: Receiving the text of the messages needs a key with the `conversations:read` permission that was created by an owner or an administrator. The text of every message of the business would reach your address, so it is limited to who can read all the conversations. Create the subscription (or change it) with a key made by an owner or an administrator that holds `conversations:read`, or leave `include_message_content` off and read each message with the API.
- `calls_not_available`: The business''s plan or billing status does not allow outbound calls. Nothing was dialed. Check the plan and the billing status in the dashboard (for example, a trial does not include voice calls).'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
ServiceUnavailable:
description: 'The service could not complete the request.
- `temporary_conflict`: The request collided with another change made at the same moment and was not applied. Send it again right away: nothing was changed, so repeating it is safe.
- `outcome_unknown`: We could not confirm whether the operation was completed. If the operation accepts an Idempotency-Key, repeat it with the same key: if it was already applied, you will get the same result. If it does not accept one, check the state before repeating it.
- `service_unavailable`: The service cannot answer right now. Try again in a few seconds. If it persists, write to us at contacto@elocuenti.com with the `request_id` of the answer.'
headers:
Retry-After:
$ref: '#/components/headers/Retry-After'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
Unauthorized:
description: 'The API key was not accepted.
- `missing_api_key`: The request has no API key. Send it in the Authorization header: `Authorization: Bearer <your key>`.
- `invalid_api_key`: The API key is not recognized. Check that it is complete and was copied correctly. If it still fails, create a new one in Settings → API Keys.
- `api_key_expired`: The API key has expired. Create a new key in Settings → API Keys and use it instead.
- `api_key_inactive`: The API key was revoked. Create a new key in Settings → API Keys and use it instead.
- `api_key_owner_inactive`: The person who created this key is no longer active in the business. Ask an owner or administrator to transfer it to another person in Settings → API Keys, or create a new one.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
UnprocessableEntity:
description: 'The request did not pass validation.
- `template_content_rejected`: WhatsApp did not accept the content of the template, so it was not created. Look at `details`: it says which part (`header`, `body`, `footer`, `buttons`) and why. Fix it and send the request again with a new Idempotency-Key. If `details` only says `template`, WhatsApp gave no more information: review the text and the category against WhatsApp''s template rules, and write to us with the `request_id` if you cannot find the cause.
- `call_not_placed`: The phone network did not accept the call, so it was not placed. Check that the contact''s number is a real number that can receive calls and that the country is one the business can call. Repeating the same request will be refused again; write to us with the `request_id` if the number is valid.
- `idempotency_key_reused`: This Idempotency-Key was already used with a different request. Use a new Idempotency-Key for each different operation. Reuse a key only to repeat the exact same request.
- `invalid_request`: The request does not pass validation. The `details` list says which fields and why. Fix each field listed in `details` and send the request again.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
securitySchemes:
ApiKey:
type: http
scheme: bearer
bearerFormat: API key