action.recorded. A trigger is a rule of yours over what the memory knows, with parameters, a window and deduplication per subject, such as “a company promise one day past due”. In both cases Niadra notifies by webhook, and your agent or your system acts. A trigger never calls a system of record, never opens a ticket, never messages the end customer and never branches.
Outgoing event types
Endpoints, subscriptions and trigger rules are versioned configuration in the control API, changed through diffs a person approves. Every endpoint belongs to a source, and an event about the memory only reaches an endpoint whose source may read the item it is about, under the same policy as the context: purpose, audience and the verification level the source can reach. With the starter policy, the outcome of a health conversation never reaches the billing CRM, and
trait.added for overdue_invoices only reaches sources with the billing purpose. A trigger rule is only accepted, and only fires, if its condition and payload are readable by the endpoint’s source: a diff that would leave a rule silent is refused when it is sent and again when it is approved. trait.present(overdue_invoices) never reaches a marketing system. Conditions and warnings count only the items that arrived through confirmed handles: an episode that came through an unconfirmed link completes no recurrence and fires no recontact, and the webhook never carries its id. A confirmation, a merge or a split re-checks the profile’s rules right away, and a split retracts the firings whose evidence left with the handle.
An endpoint with format ocsf or hec does not receive these events: it receives the space’s stream of receipts for your SIEM, signed and with the same retries. See Receipts and audit.
In the Console, the Alerts screen builds the rule from the catalog, with no JSON: you pick the condition, the parameters and the endpoint, test it and send it for approval with a reason. A rule in use is adjusted there too, with the same ID and the next version, and turned on or off by a diff. The control plane checks the diff when it arrives and again at approval, against the documents of that moment, and refuses:
- a rule whose endpoint cannot receive the warning: it is a SIEM stream, does not subscribe to
trigger.fired, belongs to an analysis or export source, is a customer agent receiving an identity warning, or shares no purpose or audience with the pattern of the warning; - a
trait.presentrule that names a pattern that is off or does not exist; - a
context_use.repetition_raterule with parameters the sweep cannot read, or with asource_idthat is not in the space; - an endpoint that subscribes to an event type Niadra does not send: the accepted list is exactly the table above;
- a new endpoint or mapping for a source that was cut;
- a document with more than 50 warning rules.
endpoint_disabled, the mapping’s webhooks are refused, and the warning rules aimed at them stop). The documents keep them, as history.
The outcome of every conversation
For your CRM to record how each conversation ended, subscribe toconversation.outcome, open_item.closed and action.recorded. None of them carries the text of the conversation.
conversation.outcome goes out when the memory of a conversation or task is ready (by: extraction, with the open items it left open in open_item_ids) and when a correction changes the outcome (by: feedback, with a higher version). It carries the labels of your closed lists, such as intent, normalized category and outcome, never the summary or the resolution. conversation_id is the conversation id in your system, and it is null when it looks like a phone number, an e-mail or a WhatsApp id.
open_item.closed says which open item closed (open_item_id, owner, object, expected_operation, due_at and, in opened_in, the session and the source that opened it) and what closed it, in closed_by: kind action, with action_id and operation, when an agent’s action did; system_event, with event_id, when an event of the system of record did; conversation, with session_id, when a later conversation resolved it; or feedback. links.open opens the object’s timeline or, without an object, the profile’s memory. open_item.reopened undoes a closing when the session of the closing action had a turn flagged as injection and the action was quarantined (reason: closing_action_quarantined, with the action_id).
The trigger catalog
New conditions come through the product catalog, never through free text. A rule fires at most once per subject per window (24 hours by default), and the same evidence never fires the same rule twice; the ceiling is 100 firings per minute per space, and a space holds at most 50 trigger rules, on or off. Conditions about a profile are evaluated right after its memory changes; deadlines and conditions about a source, every 5 minutes. A late firing, more than 2 hours after the condition became true (or what
late_after_minutes says), is marked late or suppressed, as the rule says.
Conditions about a source
source.silent, source.gap and context_use.repetition_rate are about a source, not a customer. The subject of the notice is the source ("kind": "source", with its id in pseudonym), and the 5-minute sweep evaluates all three.
source.silentwatches the sources that sent events in the last 30 days. Each silence is its own occurrence: a source that comes back and falls silent again warns again.context_use.repetition_ratereads the daily measurement of Context use. It fires when, over the lastwindow_daysdays (7 by default, up to 90), repeated questions exceed theaboveshare (0.2 by default) of the source’s questions, with at leastmin_questionsquestions (30 by default) for the number to count. Withoutsource_id, each source is watched on its own. The control group of an experiment is left out, since it never received the context. A period above the threshold warns once, counted from the day it began; if the rate drops and rises again, a new notice goes out. The notice carries indata.detailthe questions (questions), the repeated ones (repeated), the rate (rate), the threshold (above), the window (window_days) and the day the period began (above_since), andlinks.openpoints to/v1/context-use?source_id=.... Parameters the sweep cannot read, or asource_idthat does not exist in the space, are refused when the diff is sent and when it is approved.
trigger.fired to the CRM endpoint, and your CRM agent reschedules the visit.
What a delivery looks like
Deliveries follow the open Standard Webhooks format.links.open is the API route that opens what the notice points at: the profile, the memory, an object’s timeline, an episode or a source’s measurement. It only works with a credential of your space that has the scope for it.
Three headers come with every delivery:
webhook-id: unique per message; the same on every retry. Use it to deduplicate.webhook-timestamp: Unix seconds. Reject anything more than 5 minutes away from your clock.webhook-signature:v1,followed by the base64 HMAC-SHA256 of{webhook-id}.{webhook-timestamp}.{body}. During a secret rotation, two signatures separated by a space are valid at once.
Verify the signature
The secret is written once, through the Console orPUT /v1/secrets/webhook/{id}, and starts with whsec_; the key is the base64 part after the prefix. Verify on the raw body, before parsing it.
Delivery guarantees
- At least once. A message may arrive more than once; deduplicate by
webhook-id. - No ordering. Messages may arrive out of order; every event carries
as_ofand a version so you can order them. - Retries with exponential backoff, from 30 seconds up to 4 hours between them, for up to 24 hours. After that the delivery goes to the dead-letter queue, visible in the Console, and is deleted 7 days after it was created.
- An endpoint failing for 24 hours straight, without a single success, stops receiving: new deliveries to it go straight to the dead-letter queue with the error
endpoint_failing_24h. A manual resend that succeeds opens it again. - Controlled egress. Every outgoing call goes out over HTTPS only and never follows a redirect. The address is resolved and checked on each attempt, and the connection goes to the checked address. Private, loopback, link-local (the cloud metadata address included), CGNAT, reserved and multicast addresses are refused.
The notifications feed
Those who prefer to pull instead of receiving read the same events throughGET /v1/notifications: the webhook bodies, oldest first, by cursor, ids and pointers only. Each item has the fields of a delivery (id, type, created_at, tenant, subject, data and links); the id is the event’s, and a webhook delivery of the same event has an id of its own, so a reader of both deduplicates by the event id. Without a cursor, the feed starts from the beginning; next_cursor is where the next poll starts, and a poll with nothing new answers the same cursor. Events stay 7 days. An event aimed at one endpoint, or about a memory item only some sources may read, reaches its webhooks only, so the feed carries what any source of the space may see, and never a conversation’s text. Any source key reads the feed, whatever its scopes. The feed is the space’s notifications feature, off by default and turned on in the features document by an approved diff of the security role; in a space without it, the route answers 404.
Answering a delivery
Answer with any 2xx quickly and do the work asynchronously. A delivery ispending, delivered, failing while it retries, or dead. These routes take a Console person with the integration role (firings also analysis) or a key with the admin scope. To inspect deliveries use Webhook deliveries; to send a dead delivery again, with the same webhook-id, use Redeliver. To check an endpoint or a SIEM stream before you rely on it, Send a test delivery sends once, signed like the rest, a delivery of type webhook.test: the usual envelope with data: {"test": true}, or an OCSF event marked as a test for the SIEM. It shows in the deliveries list. Firings, with the rule, the version and the evidence, are listed at Trigger firings.
Next steps
Patterns
the signals
trait.present can watch.Webhooks from your systems
the other direction, events coming in.
Context use
the repetition rate behind
context_use.repetition_rate.Receipts and audit
receipts delivered to your SIEM through the same mechanism.

