Skip to main content
POST
Emitir un evento del CRM
Records a CRM event against a contact. The event lands on that contact’s open ticket; if they have none, Keebai opens one. This is the inbound direction — your ERP, POS, or backoffice calling Keebai. It is not related to /v1/webhooks (plural), which manages the outbound subscriptions Keebai delivers to you.
The event type must already exist in your account, and its key is what you send as type. Create it in the portal under CRM → Event types; the same screen shows this endpoint, generates an API key for it, and renders the exact body for that type.

Endpoint

Required scope

crm:events:write

Headers

Body

Send exactly one contact identifier. Resolution runs in order — ticket_id, then chat_user_id, then customer_id, then phone — and stops at the first one present.
Never send company or project — not in the body, not as a header. The token is the only thing that decides which tenant a write lands in, and it is not overridable.Sending either returns 400. There is no x-company header on the public API; if you find one in an old snippet, it belongs to an internal service call and does not apply here.They are absent from the response too: the event you get back carries no tenant identifiers.

Example request

Response

202 Accepted

202, not 201: the event is recorded synchronously, but the work it triggers downstream — projecting it onto the conversation timeline, pushing it to the portal in realtime, firing crm.ticket.updated — completes after the response. duplicated is true when the idempotency_key had already been used. The original event comes back untouched and nothing is written a second time.

400 Bad Request

A malformed body: missing type, a phone outside 6–20 characters, an occurred_at that is not ISO 8601, a malformed ObjectId, or a property the endpoint does not accept — including company and project, which come from the token.

401 Unauthorized

Missing, invalid, revoked, or expired token.

403 Forbidden

The token does not have the crm:events:write scope.

422 Unprocessable Entity

The request was well-formed but the event was not recorded. Branch on error.code:

429 Too Many Requests

You hit the ingest rate limit. See below.

Rate limits

This route carries its own limits, well above the platform defaults, because an ingest exhausts a quota sized for interactive use in an afternoon: Still per project, and still shared across every token in that project.

Operational notes

  • Always send an idempotency_key. Network retries are cheap and duplicated timeline entries are not. Use whatever identifier the event already has in your system — an order id, a transaction id, a row id.
  • data writes through. The values you send are not only stored on the event: each field the type declares is written onto the ticket or onto the contact, depending on its scope, and overwrites whatever was there. See CRM events.
  • Keys outside the declared fields are kept but not written. They stay in the event’s payload and reach nothing else. Only fields associated with the type reach the ticket or the contact.
  • A 422 is not retryable except for CONTACT_LOOKUP_FAILED. Everything else needs a fix on your side or in the CRM configuration. Dead-letter the payload and alert rather than looping.
  • occurred_at is when it happened, not when you sent it. Backfilling a batch with the real timestamps puts the events in the right order on the timeline.

Authorizations

Authorization
string
header
required

Personal Access Token con prefijo kbai_pk_. Generar desde el portal con permiso developer.manage_tokens.

Body

application/json
type
string
required

Key del tipo de evento configurado en el portal, por ejemplo custom.pedido_confirmado.

phone
string

Teléfono del contacto en formato canónico (sólo dígitos, con código de país y sin +). Alternativa a chat_user_id / customer_id / ticket_id.

chat_user_id
string

ObjectId del contacto (chat user)

customer_id
string

ObjectId del cliente del CRM

ticket_id
string

ObjectId del ticket. Si viene, el evento se adjunta a ese ticket y no se resuelve el contacto.

data
object

Valores de los campos asociados al tipo de evento, indexados por la key del campo. Los requeridos deben venir y cada valor debe respetar el tipo del campo.

summary
string

Texto corto del evento. Por defecto, el nombre del tipo.

occurred_at
string<date-time>

Momento en que ocurrió, ISO 8601. Por defecto, ahora.

idempotency_key
string

Identificador único del evento en tu sistema. Reenviar el mismo valor no duplica el evento.

Response

event
object
required
duplicated
boolean

true cuando el idempotency_key ya existía: se devuelve el evento original y no se registra uno nuevo.