invoice.credited for invoice 0823 lands on Marina Souza’s memory at 2:06 pm, next to the action the billing agent recorded.
How a system event becomes memory
- The ERP posts its own JSON to
POST /v1/ingest/webhook/{source_id}. - Niadra authenticates the request with the method declared for that source and stores the raw payload before answering.
- The versioned mapping of the source produces the event:
canonical_type, the object, thesystem_idof the customer, the fields andoccurred_at. - The object gets its timeline and a derived state, with
as_ofand a reference to the source record. Open items tied to it may close. - The live layer has it in under a second; the recompiled packs in under ten.
Steps
1. Create a source for the system
Each system is a source, with its own purpose and audience. Create it in the Console or through the control API. Note thesource_id: it is the last segment of the webhook address.
2. Choose how the system authenticates
Authentication is mandatory and declared per source in the mapping. Without it, anyone who learned the address could inject a fake history, an action that closes a promise or a paid invoice.
The secret is written straight into the vault of your cell and never shown again. A request that fails authentication gets 401 and is counted in the coverage of the source, so a misconfigured system shows up before anyone misses its events. The verification some systems send by
GET before the first delivery (Meta’s hub.challenge) is answered only by a mapping with url_token, and only with the token in the query, since that GET carries no signature; under any other mode the answer is 404. With mtls, the system sends to the region’s mTLS address, https://mtls.<region>.api.niadra.com/v1/ingest/webhook/<source_id>, which asks for the client certificate in the handshake (TLS 1.3, without chain verification, since the cell pins one fingerprint per source) and forwards it to the cell; the cell takes the first certificate, the client’s own, and compares its fingerprint in constant time with the mapping’s. A missing or other certificate gets 401 and counts as a refusal. Only that route receives the certificate: every other route of the edge drops any certificate header a client sends, and no header naming a fingerprint is read.
3. Write the mapping
A mapping turns the payload your system already sends into events, with JMESPath expressions. This is the ERP payload:ERP payload
Mapping
match is true produces one event, and idempotency_key points to the id the system gives the event. The signature arrives in hex or base64, with an optional sha256= prefix, and the timestamp in the header named by timestamp_header. secret_ref points to the secret you wrote with PUT /v1/secrets/webhook/{id}, in the form spaces/<space>/webhook/<id>, always under your own space: a reference to another space’s secret is refused in the document and on arrival. anchor_terms on a rule with a fixed canonical_type names the words the customer uses for that event (vistoria, inspection): a question such as “after the inspection” is then read against the customer’s latest event of that type, adding to the space’s anchors. Only mapped types get in. That filter at the edge protects the memory from the volume of an ERP. The raw payload of an unmapped type is kept for 7 days, as it arrived, and never enters the memory.
4. Propose the mapping as a versioned change
Configuration is never written in place. A mapping change is a diff in the control API, with the whole newdocument of the type (the list of mappings of the space, one per source) and a reason, approved by a person; the cell receives it through a signed snapshot. GET /v1/config/types lists the types and the roles that may change each one, and GET /v1/config/mappings returns the current document to start from. Every version stays in the history and can be rolled back.
5. Point the system at the address
Configure the webhook in your ERP with the address from step 1 and the secret from step 2. From then on, every request answers with the same shape as a batch:accepted, duplicates and errors per event, with 200 when everything went in and 207 when something was rejected:
Response
6. Check what arrived
Read the object to see its derived state and timeline. The credit shows the ERP event and the billing agent’s action as one record: the action wasdeclared at 2:06 pm and became confirmed when invoice.credited arrived.
Free text inside a system
A ticket description or an e-mail body is not a structured event. Through the SDK or the batch, send it as amessage on its own channel (ticket, email), with the ticket or thread id in conversation_id, and it goes through the normal extraction, like a conversation. Through the webhook, write a rule with "kind": "message" and the text expression in text:
Free-text rule
conversation_id expression, the rule files the text under the object it names, such as obj:ticket:helpdesk:4471, so every text about one ticket forms one conversation; without an object, each text becomes a conversation of its own. The session closes by inactivity and goes through extraction like any conversation. Mapped system events are not billed.
Systems without webhooks
For systems that only export files, turn the export into JSONL with one batch item per line and send it toPOST /v1/ingest/files; each line is validated like an item of POST /v1/batch. To seed identity from a CRM export, send it as CSV. For systems your team already integrates in code, send system events through the SDK or POST /v1/batch with kind: "system_event" and canonical_type.
Next steps
Systems, objects and actions
objects, derived state and how actions close open items.
Internal agents
the billing agent that recorded the credit.
Receive a system webhook
the endpoint reference.
Triggers and webhooks
the other direction, from Niadra to your systems.

