Skip to content

Outcomes and the feedback loop

An outcome says what happened after a decision. Reporting outcomes is free, unlimited and idempotent. It is the one thing that makes Verdict better for you specifically.

POST /v1/outcomes. Free. Scope outcomes:write.

Request
curl -X POST 'https://api.askverdict.io/v1/outcomes' \
-H "Authorization: Bearer $VERDICT_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H 'Content-Type: application/json' \
-d '{
"decision_id": "dec_01JC3Z4Y8R9K2M5N7P1Q3S5T7V",
"outcome": "meeting_booked",
"observed_at": "2026-11-09T16:30:00Z",
"meta": {
"channel": "email"
}
}'
Response 200
{
"id": "out_01JC3Z4Y8R9K2M5N7P1Q3S5T7V",
"decision_id": "dec_01JC3Z4Y8R9K2M5N7P1Q3S5T7V",
"outcome": "meeting_booked",
"observed_at": "2026-11-09T16:30:00Z",
"meta": {
"channel": "email"
},
"created_at": "2026-11-09T16:30:02Z"
}

The call is idempotent on (decision_id, outcome, observed_at): the same triple twice records one outcome. Use custom:<slug> for outcomes of your own.

outcome Meaning
bounced The message bounced.
delivered The message was delivered.
replied The prospect replied.
interested The prospect showed interest.
not_interested The prospect declined.
meeting_booked A meeting was booked.
meeting_held A booked meeting took place.
approved A human approved the agent’s proposed action.
skipped A human skipped (rejected) the agent’s proposed action.
reopened A closed thread or deal reopened.
converted The prospect became a customer.
  1. Every decision stores p_raw from the model, the calibrated p, the threshold applied and a hash of its structured features. Never your text.
  2. Your outcomes are joined to those decisions.
  3. Once your org has 50 outcomes for a decision and question-set version, Verdict fits a calibration map for your org (isotonic from 200 outcomes, Platt scaling below) and a threshold that maximizes F1 on the positive outcome. Below 50, it uses the map for your ICP’s segment (industry group and size band), built from all orgs that opt in. Below that, the global map.
  4. calibration on every response says which map answered: org, segment or global, and how many outcomes it rests on.

Maps are refit nightly and when your org crosses an outcome threshold.

Only outcomes and feature hashes enter the segment and global maps. No name, email, domain, roster or thread text is ever an input to a pooled map. No customer’s records train anything another customer sees. You can opt out of contributing; your own calibration keeps working.

Point your sending and booking tools at Verdict and outcomes arrive on their own. Each receiver verifies the vendor’s signature and maps the vendor’s event to an outcome on the decision you tagged.

Tool Receiver
Instantly POST /v1/webhooks/instantly/{receiver_token}
Smartlead POST /v1/webhooks/smartlead/{receiver_token}
Cal.com POST /v1/webhooks/calcom/{receiver_token}
Calendly POST /v1/webhooks/calendly/{receiver_token}

The receiver_token in the path identifies your org. Treat the full URL as a secret. To tag a send, put the decision id in the vendor’s custom field verdict_decision_id (the SDK helper withDecision(decisionId) does it) or in the email header X-Verdict-Decision; Closing the loop has the whole convention. Verdict never holds your vendor credentials.

If a human or a rule approves each action before it happens, report approved or skipped. The SDKs ship approve(decisionId) and skip(decisionId) helpers for exactly this.