Skip to content

Attribute payments to agents

A payment record that cannot say which agent made it, for whom, and on what instruction is little use to a bank. Attribution is the one job the SDK cannot do for you, because only your code knows who the agent is.

Wrap the code that runs an agent task in withAgentContext. Every event emitted inside it, including by the rail adapters, picks up the context without you passing it around.

import { HalvorClient, withAgentContext } from '@halvorai/ingest';
const halvor = new HalvorClient();
await withAgentContext(
{
agent: { id: 'agt_research_07', name: 'Market research agent', framework: 'openai-agents' },
owner: { id: user.id, type: 'person', country: 'US' },
instruction: { id: task.id, text: task.prompt, channel: 'chat' },
},
() => agent.run(task),
);

Context follows the call through await, timers and callbacks, and concurrent tasks keep separate contexts. A nested context replaces fields of the outer one.

In Python, use agent_context(...) as a with block. See the Python SDK.

FieldGuidance
agent.idA stable id you choose, such as agt_research_07. The same agent must always use the same id.
owner.idThe person or business the agent acts for, as your system knows them. Not an email address if you can avoid it.
owner.typeperson or business.
owner.countryOptional ISO country code.
instruction.idYour id for the task or request.
instruction.textThe text the agent received. It is hashed on your machine. Only the hash leaves.
instruction.summaryOptional. A short redacted description a bank may read, up to 500 characters.
instruction.tool_call_idThe tool call that triggered the payment, when there is one.

The hash lets a bank later confirm that an instruction you disclose is the one the agent actually received. HalvorAI never sees the text.

The dashboard reports attribution coverage: events that have an agent, an owner and an instruction, divided by all events. A sponsor bank often asks for this number. Events with no agent are stored under the agent unattributed and count against it. They are never dropped.

To raise coverage:

  • Make sure payments happen inside withAgentContext.
  • For Stripe, tag the object when you create it so webhooks can be attributed later. See the Stripe guide.
  • For MCP tools, observe the client so the tool call id is recorded. See the MCP guide.

If one agent hires another, set instruction.channel to agent on the second agent’s events and link them with related.previous_event_id. The schema has no nested owners in this version.

Do not send identity documents or KYC data in owner. kyc_ref is a pointer into your own system. Do not put a person’s payment card details anywhere. See data handling.