Event log¶
Sending events covers the write side of this endpoint
family: POST /api/v1/agents/{agent_id}/events, and how a posted payload
turns into a matched and call_ids response. This page covers the read
side: listing and inspecting every event an agent has ever received. Come
here to answer "I posted an event, why didn't it call anyone?"
Authentication¶
Both endpoints on this page take an admin session token (see Authentication), not an API key.
Any admin role can read this
Unlike most of the endpoints documented elsewhere on this site, the event log has no elevated role requirement. Owner, Admin, Editor, and Viewer can all call both endpoints below, any active admin account works regardless of role.
List an agent's events¶
Returns every event ever posted to the agent, newest first (ordered by
id descending), with no pagination and no query filters. There's no
limit parameter. If an agent has accumulated a large event history,
handle the narrowing on your own client rather than expecting the server
to page results.
[
{
"id": 123,
"agent_id": 1,
"event_type": "abandoned_cart",
"payload": {
"customer": { "name": "Jamie", "phone": "+15557654321" },
"cart_value": 89,
"items": [1, 2, 3]
},
"processing_status": "triggered",
"processing_error": null,
"created_at": "2026-01-15T10:30:00Z"
}
]
Get a single event¶
Returns 404 if event_id doesn't exist, or belongs to a different agent
than the one in the URL.
Fields¶
| Field | Type | Description |
|---|---|---|
id |
integer | The event's id, the same value returned as event_id in the ingestion response. |
agent_id |
integer | The agent this event was posted to. |
event_type |
string | The event_type the caller sent, or "" if omitted on ingestion (it's a required field on the request, so this is only ever exactly what was posted). |
payload |
object | The exact JSON body the caller posted under payload. |
processing_status |
string | One of received, no_match, triggered. See below. |
processing_error |
string or null | Set when a matching Trigger couldn't be completed, most commonly an unresolvable phone number. See below. |
created_at |
datetime | When AiFlow recorded the event, before trigger evaluation ran. |
Interpreting processing_status¶
Trigger evaluation happens synchronously, inside the same request that
ingests the event, before the 201 response is returned. In practice,
that means you'll only ever see two of the three possible values by the
time you read an event back:
| Value | Meaning |
|---|---|
received |
The event row's initial default, before trigger evaluation runs. Because evaluation completes within the same request and transaction that creates the event, this state isn't durably observable in normal operation: an event you can read at all has already been evaluated. |
no_match |
Trigger evaluation ran, and nothing fired: no call was queued, no WhatsApp or Telegram message was sent, and no agent_task ran. |
triggered |
At least one enabled Trigger fired: a call, a WhatsApp or Telegram send, or an agent_task run. This is the same signal as matched: true in the ingestion response. If you already captured that response when you posted the event, this field just confirms it after the fact. |
no_match is a broader bucket than "nothing about this event looked
right." Each of these distinct situations also produces no_match, and
processing_status alone can't tell them apart:
- No enabled Trigger on the agent has an
event_type_filterthat matches this event'sevent_type(or has none at all). - A candidate Trigger's
conditionsdidn't match againstpayload. - A Trigger's conditions did match, but the resolved phone number was
already within that Trigger's
cooldown_secondswindow. This case is silent: noprocessing_erroris recorded, because suppressing a repeat fire isn't a failure, it's the cooldown working as configured. To distinguish "nothing matched" from "matched but cooled down," check the Trigger's own configuration (see Triggers) against the recipient's recent call and message history yourself. - A Trigger's conditions matched but no valid phone number could be
resolved. Unlike cooldown suppression, this case is recorded, in
processing_error, covered next.
Interpreting processing_error¶
processing_error is populated when a Trigger matched but its recipient
could not be worked out. That happens two ways: a call, whatsapp, or
both Trigger whose phone_field_path didn't resolve to a usable phone
number in the posted payload (a missing key, or a value that isn't a
valid phone number), or a telegram Trigger with no chat to send to,
meaning its telegram_chat_id_field_path found nothing, no
telegram_chat_id is set on it, and the deployment has no
TELEGRAM_CHAT_ID either. The phone case looks like this:
{
"processing_error": "Trigger 4 ('Abandoned cart follow-up') matched but no valid phone number was found at 'customer.phone'."
}
Two things worth knowing before you rely on this field for debugging:
- It's a single string field, not a list. If more than one candidate
Trigger fails phone resolution for the same event, only the last one
encountered during evaluation is kept, earlier failures are silently
overwritten. Don't treat an event with
processing_errorset as proof that exactly one Trigger had a problem. - It's never set for an
agent_taskTrigger (there's no phone to resolve) and never set for cooldown suppression (see above). Its absence doesn't mean every candidate Trigger evaluated cleanly, only that none of them hit this specific phone-resolution failure.
Cross-referencing calls¶
An event record carries no call id directly (that's what the ingestion
response's call_ids is for, captured at send time). To find a call after
the fact from the event side, use Calls: a Call record's
triggered_by_event_id points back to the event that queued it, so
filtering GET /api/v1/agents/{agent_id}/calls for that value gets you
from an event to whatever call or calls it produced.