Events API
Events are how systems sense into Fibric. Every occurrence, whether a commerce webhook, a sensor reading, a cron tick, or an operator's own output, is normalized into one canonical EventEnvelope before anything reasons over it. This page documents ingesting an envelope, listing stored events, and retrieving one by id.
Conventions shared by every endpoint on this page, including authentication, pagination, the Idempotency-Key header, and the error envelope, are defined in the API overview. Error codes are catalogued in Errors.
The event object
The stored event exposes the kernel envelope's fields verbatim, plus the API metadata the server adds at ingest. The API id (prefix ev_) is the public form of the kernel envelope's event_id. The envelope shape is defined in packages/kernel/src/envelope.ts and explained in The event envelope.
Unique identifier for the event, prefixed ev_. Assigned by the server at ingest.
Always event.
The reseller the tenant belongs to. null means Fibric-direct, no reseller. Present on every envelope; stamped from your API key, never from the request body.
The tenant that owns this event. Stamped from your API key. See Tenancy & isolation.
Optional workspace partition within the tenant. null when the tenant does not use workspaces.
Where the event came from: a connector instance (magento), a hardware gateway (bacnet-gw-7), a schedule (cron), or an operator's own output (operator:order-risk).
Dotted noun.verb type, for example order.created or hvac.zone.fault. Operators subscribe to event types.
Threads related envelopes together across the sense, reason, and act loop. Supplied by the caller or generated at ingest.
The event body, arbitrary JSON from the source system. Payloads must carry real values from a real source; the platform tags anything else so a placeholder can never read as a governed metric.
The operator that emitted this envelope, when the source is an operator. null for external events.
The reasoning session the envelope belongs to, when one exists. null for external events.
RFC 3339 timestamp of when the API accepted the event. Server-assigned.
Execution plans proposed off this event, if any. Follow each id into the Actions & plans API.
{
"id": "ev_3a91c7",
"object": "event",
"reseller_id": null,
"tenant_id": "t_8f2ac901",
"workspace_id": null,
"source": "magento",
"event_type": "order.updated",
"correlation_id": "co_51d2e8",
"payload": {
"order": "SO-10884",
"status": "open",
"ship_by": "2026-07-06",
"carrier_scanned": false
},
"agent_id": null,
"session_id": null,
"received_at": "2026-07-02T14:58:01Z",
"plan_ids": ["pl_7c1a"]
}
reseller_id and tenant_id are derived from the API key on every request. You may omit them from the body. If you include them and they do not match the key, the request is rejected with 403 tenant_mismatch. There is no body field that reaches another tenant.
Ingest an event
events:writeAccepts one envelope. The server assigns id and received_at, stamps reseller_id and tenant_id from the key, generates correlation_id if you did not supply one, and routes the event to any operator subscribed to its event_type. Ingestion never acts by itself: an event can only cause an operator to propose a plan, and the executor disposes that plan under policy.
Send an Idempotency-Key header on every ingest. Retrying with the same key returns the original stored envelope instead of ingesting a duplicate. This is what makes a webhook retry storm, or a 657-message flood, absorb into one event rather than multiply.
The system the event came from, for example magento, kustomer, bacnet-gw-7, or cron.
Dotted noun.verb event type, for example order.updated. Validated against a lowercase dotted-segment pattern; anything else fails with 400 invalid_parameter.
The event body. Defaults to an empty object. Maximum size 256 KB; larger payloads are rejected with 400 invalid_request.
Supply your own value to thread this event with related envelopes. Generated by the server when omitted.
Workspace partition within the tenant. Defaults to null.
Set when an operator emits the event through the API on its own behalf. Defaults to null.
Reasoning session identifier, when one exists. Defaults to null.
curl -X POST https://api.fibric.io/v1/events \
-H "Authorization: Bearer $FIBRIC_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: magento:SO-10884:v7" \
-d '{
"source": "magento",
"event_type": "order.updated",
"payload": {
"order": "SO-10884",
"status": "open",
"ship_by": "2026-07-06",
"carrier_scanned": false
}
}'
{
"id": "ev_3a91c7",
"object": "event",
"reseller_id": null,
"tenant_id": "t_8f2ac901",
"workspace_id": null,
"source": "magento",
"event_type": "order.updated",
"correlation_id": "co_51d2e8",
"payload": { "order": "SO-10884", "status": "open", "ship_by": "2026-07-06", "carrier_scanned": false },
"agent_id": null,
"session_id": null,
"received_at": "2026-07-02T14:58:01Z",
"plan_ids": []
}
Replaying an Idempotency-Key the server has already seen, with an identical body, returns 200 and the original stored envelope. Replaying the same key with a different body returns 409 idempotency_conflict; see Errors for the semantics.
Error cases:
| Status | Code | When |
|---|---|---|
400 | missing_parameter | source or event_type is absent. |
400 | invalid_parameter | event_type does not match the dotted noun.verb pattern, or payload is not an object. |
403 | tenant_mismatch | The body carried a tenant_id or reseller_id that does not match the key. |
409 | idempotency_conflict | The Idempotency-Key was already used with a different body. |
429 | rate_limited | The event-ingest budget is exhausted. Honor Retry-After; see Rate limits & quotas. |
List events
events:readReturns events for the authenticated tenant, newest first, cursor-paginated. Filters combine with AND. The entity filter matches events through the plans they triggered: an event is indexed by the entity_key of every action proposed off it, which lets you reconstruct everything that ever touched one order, one room, or one asset.
Only events from this source, for example magento or operator:order-risk.
Only events of this type, for example order.updated.
Only events whose proposed actions carried this entity_key, for example order:SO-10884. Events that triggered no plan do not match.
Only events received at or after this RFC 3339 timestamp, for example 2026-07-01T00:00:00Z.
Page size, 1–100. Defaults to 20.
Pagination cursor from a previous response's next_cursor.
curl "https://api.fibric.io/v1/events?source=magento&entity=order:SO-10884&since=2026-07-01T00:00:00Z" \
-H "Authorization: Bearer $FIBRIC_KEY"
{
"object": "list",
"data": [
{
"id": "ev_3a91c7",
"object": "event",
"source": "magento",
"event_type": "order.updated",
"correlation_id": "co_51d2e8",
"received_at": "2026-07-02T14:58:01Z",
"plan_ids": ["pl_7c1a"]
},
{
"id": "ev_3a90b2",
"object": "event",
"source": "magento",
"event_type": "order.created",
"correlation_id": "co_51d2e8",
"received_at": "2026-07-01T09:12:44Z",
"plan_ids": []
}
],
"has_more": true,
"next_cursor": "cur_eyJpZCI6ImV2XzNhOTBiMiJ9"
}
List responses omit payload and the tenancy fields for brevity; retrieve a single event for the full envelope.
Error cases:
| Status | Code | When |
|---|---|---|
400 | invalid_parameter | since is not RFC 3339, or limit is outside 1–100. |
400 | invalid_cursor | The cursor is malformed or was issued for a different query. |
Retrieve an event
events:readReturns the full stored envelope, including payload and the tenancy fields, plus the ids of any plans proposed off it. An id that belongs to another tenant reads as 404 not_found; existence is never disclosed across the wall.
The event id, for example ev_3a91c7.
curl https://api.fibric.io/v1/events/ev_3a91c7 \
-H "Authorization: Bearer $FIBRIC_KEY"
{
"id": "ev_3a91c7",
"object": "event",
"reseller_id": null,
"tenant_id": "t_8f2ac901",
"workspace_id": null,
"source": "magento",
"event_type": "order.updated",
"correlation_id": "co_51d2e8",
"payload": {
"order": "SO-10884",
"status": "open",
"ship_by": "2026-07-06",
"carrier_scanned": false
},
"agent_id": null,
"session_id": null,
"received_at": "2026-07-02T14:58:01Z",
"plan_ids": ["pl_7c1a"]
}
Error cases:
| Status | Code | When |
|---|---|---|
404 | not_found | No event with this id exists for the authenticated tenant. |
Retention and immutability
Stored events are immutable. There is no update or delete endpoint: an envelope, once accepted, is part of the audit trail that receipts reference. During early access, events are retained for the life of the tenant; retention windows are finalized with each team during onboarding.