Skip to main content
Internal agents work inside your systems without talking to the customer: billing reviews disputes and posts credits, orders reschedules deliveries in the ERP, tickets triages the help desk. They act on the same customers your service agents talk to, and without a shared memory each side gets it wrong in its own way. The internal agent acts without knowing what was said in service; the service agent promises what another agent already did. This guide wires a billing agent to Niadra with the same calls a service agent uses: it reads the context of the invoice it works on, posts the credit in the ERP with its own credentials, and records the action. A minute later the voice agent answers knowing it.

The flow at 2:05, 2:06 and 2:07 pm

  1. 2:05 pm, app. Marina disputes the August bill in your app. The message is tied to invoice:erp:0823 through object_refs and is in the live layer in under a second.
  2. 2:06 pm, billing agent. It reads context(object="invoice:erp:0823", view="task:billing", verification="no_customer"), receives the dispute through live, the technician visit that failed at 2:02 pm and the March credit from the history. It posts the $40 credit in the ERP and records action("credit", closes=...).
  3. 2:06 pm, ERP. The ERP emits invoice.credited. The action and the event become one record, and the action goes from declared to confirmed.
  4. 2:07 pm, call. The voice pack already says: “Done by another agent: $40 credit on the August bill, Billing, 2:06 pm, confirmed by the system”.
No language model sits between the recorded action and the voice context. Actions and system events reach the live layer in under a second and the recompiled pack in under ten.
Niadra never executes an action in a system of record. Your agent acts, with its own credentials. Niadra keeps what it needs to remember and what it did.

Before you start: the source

An internal agent is a source like any other, with its own key, audience class internal_agent and a purpose such as billing. Access to the ERP is not access to the memory: the billing agent reads invoices and disputes, not technical open items or health data. Two settings matter here:
  • Scope act on the key, and trusted_action_ops on the source (here ["credit"]): the closed list of operations it may record, and the ones whose declared actions close open items before the system confirms them. The track scope alone never records an action.
  • Verification no_customer, the level for tasks with no customer present. It sits outside the V0 to V4 scale and is only accepted from internal_agent sources. A pack built for no_customer never goes to the customer.
Both are set on the source through the control API or the Console.

Steps

1. Open a task centered on the invoice

A task plays the role a conversation plays for service agents: it pins the pack, scopes the SDK cache, groups the events for billing and closes with task.ended. Without it, the server closes the task after 10 minutes of inactivity. The task:billing view is defined by your space as a template plus a policy. It favors objects of the task type, the open items tied to them and what was said about them in conversations; it leaves out what the billing purpose does not allow.
context(object=...) resolves the invoice to its owner and returns the pack centered on it: the timeline of the object plus the customer context the task needs. The same laws hold as for a conversation: precompiled, pinned by task, with ETag and a receipt of the read.

2. Act in the ERP, then record the action

Post the credit through your ERP integration. Then record the action with closes, the open item it fulfils. Name it by id, or by the object and a canonical operation, which is what you usually know: the dispute on invoice 0823.
An action is immutable, like every event. A correction is a new action with corrects_action_id pointing to the old one.

3. Let the system of record confirm it

The action stays declared until an event from the system of record confirms it. Send the ERP events through the generic webhook or as system events. When invoice.credited arrives for the same object and operation within the window, the two become a single record and the action is confirmed, so the credit is never counted twice. A declared action only closes an open item if its operation is in the trusted_action_ops of the source; a confirmed one always closes it.

4. Understand the retroactive close

The action can arrive before the open item exists. The dispute written in the app at 2:05 pm only becomes an open item when the app session closes and is extracted, say at 2:35 pm, and the billing agent acted at 2:06 pm. When the open item is created, Niadra looks for actions and events already applied to the same object and operation within the window. If one exists, the open item is born resolved, pointing to the action that closed it.

5. Let other agents know

Every agent that reads this customer next sees the action in “Done by another agent”, with its source and time. If your CRM needs to show the outcome, subscribe to the action.recorded and open_item.closed webhooks and write it there with your own integration. See Triggers and webhooks.

Delta for recurring work

Internal agents often revisit the same customers. Ask with delta=True (delta: true) and the answer carries only what changed since this source last read the customer: “what changed since the last time YOU looked” costs tens of tokens.

Next steps

Systems, objects and actions

objects, derived state and actions in depth.

Webhooks from your systems

how invoice.credited gets in.

Voice agents

the call that knows about the credit.

Context use

how the action counts as use of the context.