Traffic
Business outcomes
Outcomes are what your systems see after a conversation: paid, refunded, cancelled, escalated. Duet Machines ties each to its conversation and shows which issues are associated with losing them, never claiming cause.
#Declare, then send
Each agent declares the outcome names its events may carry in its Traffic, under Outcomes, or with PUT /api/agents/{uid}/outcomes/{name}: good or bad for the business, whether it carries an amount (and in which currency; a workspace uses one), and whether events may name a user instead of a conversation. Duet Machines refuses an event whose name, amount, currency or reference does not fit its declaration, so nothing is guessed.
curl -X POST https://platform.duetmachines.com/api/ingest/outcomes \
-H "Authorization: Bearer $DUET_API_KEY" -H "Content-Type: application/json" \
-d '{"agent": "<agent uid>", "id": "evt_1001", "name": "refunded", "occurred_at": "2026-10-12T15:04:05Z",
"conversation": "conv_8f2a", "value": 49.99, "currency": "USD"}'id is yours and unique per agent, so a retry replaces the event instead of counting it twice. conversation is the conversation's external_id. With a declared user window, send user_id instead: the event goes to that user's latest conversation that started within the window before it occurred, decided once when it arrives. POST /api/ingest/outcomes:batch takes up to 100. The SDKs have dm.outcome(name, ...). Metadata text is redacted like conversations; amounts are numbers. Outcomes are deleted with their conversation, by retention or a user's deletion.
#Webhooks from any sender
Tools that cannot run your code (Stripe, Shopify, Zendesk, a CRM) post their own JSON to a webhook URL from the agent's Traffic, under Outcomes. You declare where the event id, time (ISO or Unix seconds), conversation or user id and amount (with a scale, 0.01 for cents) sit in it, and optionally a field that must equal a value (type = charge.refunded). Payloads the condition leaves out are answered and counted as ignored; payloads whose paths do not fit are refused with the path and counted. The URL is the credential: it is shown once and deleting the webhook revokes it.
OpenTelemetry has no outcome mapping: outcomes arrive from billing and support systems days after a trace ends.
#Zendesk, HubSpot, Salesforce and Intercom
An owner or admin connects each system once per workspace in Settings, Connections (OAuth, read-only where the provider allows it; Salesforce has no read-only scope, so connect a read-only integration user). Each agent then maps each connected system in its Traffic, under Outcomes: which field of a Zendesk ticket, HubSpot deal, Salesforce opportunity or Intercom conversation holds the conversation's external_id (or the user id, for outcomes with a user window), and rules such as status = solved → resolved or dealstage = closedwon → won with its amount.
Duet Machines reads changed records every hour and stores one event per record and state, so reading a record again never counts it twice. A record a rule matches but whose join field is empty, names no conversation, lacks its amount or is refused is counted as unmatched with the reason and listed, never guessed onto a conversation. When a provider revokes access the connection says to reconnect, and nothing syncs until someone does.
#Issues and outcomes
For every issue judged in production in the last 30 days, Duet Machines compares each outcome among its conversations with the agent's judged conversations without it on the same versions in the same window: both rates, their difference with a 95% interval and p (Fisher exact for small counts), and for money the amount per conversation with minus without, with Welch's interval, times the conversations with the issue. That is the money associated with the issue, shown on the issue, ranked in Review (Sort: Money lost) and on Home. Conversations with an issue can differ from the others in more than the issue, so this is an association; an experiment or a proven fix decides cause.
Health shows each outcome's rate per release against the release before it, with the test it uses for failures. Experiments can pre-register an outcome as the primary metric. Exports carry them, and the migration report calibrates graders against them.