POST /v1/decisions/triage_reply
| Call | POST /v1/decisions/triage_reply |
| Price | $0.003 per call, billed only when an answer is returned |
| Scope | decisions:write |
| Operation id | triageReply |
What it does
Section titled “What it does”What does this reply mean? Returns the reply state and the next_action.
Call it when a reply arrives and your agent must decide what to do. Give the thread (up to 40 messages, 8 KB each, oldest first). Returns the state (one of 13), next_action, p (probability of state) and answer: the state when p clears your threshold, otherwise needs_review.
Price: $0.003 per call (x-verdict-price-usd), billed only when an answer is returned: errors (including decision_unavailable, 503 with Retry-After) and refused calls are free. Send X-Max-Cost-USD to cap spend and Idempotency-Key (required) to retry safely; a repeated key within 24 hours returns the stored response and bills nothing. Every response carries the common envelope: id, p (always present, calibrated), answer, reason_code, reason, evidence, calibration, cost and balance_usd. Report what happened next with POST /v1/outcomes.
Scope: decisions:write.
Example request
Section titled “Example request”Copy it as curl. Set VERDICT_API_KEY first; uuidgen makes a fresh Idempotency-Key.
curl -X POST 'https://api.askverdict.io/v1/decisions/triage_reply' \ -H "Authorization: Bearer $VERDICT_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H 'Content-Type: application/json' \ -d '{ "thread": [ { "role": "sender", "text": "Hi Dana, saw your Form D last week. Worth a 15-minute call on trade ops?", "sent_at": "2026-11-05T13:00:00Z" }, { "role": "prospect", "text": "Possibly. Can you send pricing first? Next week is better for a call.", "sent_at": "2026-11-06T09:12:00Z" } ], "context": { "decision_id": "dec_01JC3Z4Y8R9K2M5N7P1Q3S5T7V" }}'Example response
Section titled “Example response”200: The triage_reply decision, in the common envelope.
{ "id": "dec_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "version": "2026-11-01", "question_set_version": "2026-11-01", "model": "jev", "calibration": { "basis": "org", "n_outcomes": 412, "threshold": 0.6 }, "latency_ms": 180, "created_at": "2026-11-07T14:03:00Z", "decision": "triage_reply", "p": 0.71, "answer": "question", "reason_code": "explicit_question", "reason": "The prospect asks for pricing before agreeing to a call.", "evidence": [ { "source": "request", "url": null, "observed_at": "2026-11-06", "pointer": "/thread/1" } ], "state": "question", "next_action": "answer_question", "cost": { "decision_usd": 0.003, "vendor_usd": 0, "total_usd": 0.003 }, "balance_usd": 18.307}Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
idempotency-key |
header | string (uuid) | yes | A UUID you generate per logical request. Required on every POST. Repeating it within 24 hours returns the stored response and bills nothing; reusing it with a different body returns 409 idempotency_conflict. |
x-max-cost-usd |
header | string | no | Maximum you are willing to pay for this call, in US dollars (up to 6 decimals). If the call would cost more, it is refused with 402 max_cost_exceeded before spending. |
x-verdict-version |
header | string | no | API version to use, a date. Defaults to the version current when the key was created. The current version is 2026-11-01. |
Request body
Section titled “Request body”Schema TriageReplyRequest. The triage_reply input.
| Field | Type | Required | Description |
|---|---|---|---|
thread |
array of ThreadMessage | yes | The thread, oldest first, 1 to 40 messages; the last is the reply to triage. |
thread[].role |
enum: “sender”, “prospect”, “system” | yes | Who wrote the message. |
thread[].text |
string | yes | Plain-text body of the message. At most 8192 bytes of UTF-8. |
thread[].sent_at |
string (date-time) | yes | When the message was sent. |
context |
object | no | Optional context. |
context.decision_id |
string | no | The decision behind the original send. |
context.notes |
string | no | Short notes on what was offered. At most 2000 bytes of UTF-8. |
icp_id |
string | no | Optional ICP profile, for calibration by segment. |
Response fields (200)
Section titled “Response fields (200)”Schema TriageReplyResponse.
| Field | Type | Required | Description |
|---|---|---|---|
id |
string | yes | The decision id. Report outcomes against it. |
decision |
enum: “triage_reply” | yes | The decision name. |
version |
enum: “2026-11-01” | yes | API version that served the decision. |
question_set_version |
string (date) | yes | Version (a date) of the question set that answered (spec 6.1). |
model |
enum: “jev” | yes | Decision model that answered: always jev (ADR-0008). |
p |
number | yes | Calibrated probability (spec 6.4). Always present. |
answer |
enum: “interested”, “meeting_requested”, “question”, “objection_price”, “objection_timing”, “objection_fit”, “not_decision_maker”, “referral”, “out_of_office”, “unsubscribe”, “bounce”, “wrong_person”, “other”, “needs_review” | yes | The state when p is at or above your threshold, otherwise needs_review. |
reason_code |
enum: “explicit_positive_intent”, “explicit_meeting_request”, “explicit_question”, “explicit_objection”, “explicit_opt_out”, “redirect_to_other_person”, “auto_reply_detected”, “delivery_failure_detected”, “ambiguous_reply”, “thread_too_short” | yes | Why, from the fixed triage_reply reason-code enum. |
reason |
string | yes | Short templated explanation of reason_code; never free text from a frontier model. |
evidence |
array of Evidence | yes | Cited sources, with URLs and dates. |
evidence[].source |
enum: “sec_edgar”, “gleif”, “companies_house”, “wikidata”, “request” | yes | The index source cited, or request when the evidence is part of your own input. |
evidence[].url |
string or null (uri) | yes | Link to the cited record at the source; null when source is request. |
evidence[].observed_at |
string (date) | yes | Date the source reported the fact. |
evidence[].label |
string | no | Short description of what the evidence shows. |
evidence[].pointer |
string | no | JSON Pointer into your request body when source is request, for example /thread/2. |
calibration |
Calibration | yes | How p was calibrated (spec 6.4). |
calibration.basis |
enum: “org”, “segment”, “global” | yes | org (your own outcomes), segment (your ICP’s segment) or global: how much to trust p. |
calibration.n_outcomes |
integer | yes | Outcomes behind the calibration map used. |
calibration.threshold |
number | yes | The threshold answer applied to p. |
cost |
Cost | yes | What this call was billed, itemized. Misses and errors bill nothing. |
cost.decision_usd |
number | yes | Price of the decision. US dollars, up to 6 decimals. |
cost.vendor_usd |
number | yes | Vendor pass-through charges (always 0 in v1). US dollars, up to 6 decimals. |
cost.total_usd |
number | yes | Total billed for this call. US dollars, up to 6 decimals. |
balance_usd |
number | yes | Balance after this call. US dollars, up to 6 decimals. |
latency_ms |
integer | yes | Server-side latency of the call, in milliseconds. |
created_at |
string (date-time) | yes | When the decision was made. Decisions are immutable. |
state |
enum: “interested”, “meeting_requested”, “question”, “objection_price”, “objection_timing”, “objection_fit”, “not_decision_maker”, “referral”, “out_of_office”, “unsubscribe”, “bounce”, “wrong_person”, “other” | yes | The most likely state of the thread. |
next_action |
enum: “reply”, “book_meeting”, “answer_question”, “handle_objection”, “forward_to_referral”, “find_other_contact”, “retry_later”, “unsubscribe”, “stop”, “escalate_to_human”, “none” | yes | What the agent should do next. |
Errors
Section titled “Errors”Errors are RFC 9457 problem details and are never billed. Branch on type. See Errors.
| Status | Meaning |
|---|---|
400 |
Invalid request: malformed JSON, an unknown field, a failed constraint or a size limit (invalid_request). |
401 |
Missing, malformed, revoked or expired API key, or a bad webhook signature (unauthorized). |
402 |
Payment required: the balance cannot cover the call (insufficient_balance), or the call would exceed X-Max-Cost-USD (max_cost_exceeded). Nothing was spent. |
403 |
The key lacks the scope this operation needs (forbidden_scope). |
404 |
The resource does not exist in this organization (not_found). |
409 |
The Idempotency-Key was reused with a different request, or the first request is still running (idempotency_conflict). |
429 |
Rate limited (rate_limited). Wait Retry-After seconds. |
500 |
Unexpected server error (internal). Not billed; retry with the same Idempotency-Key. |
503 |
Temporarily unavailable: the decision model failed, timed out or is rate limited, or decisions are switched off (decision_unavailable), or a vendor the call depends on is unavailable (vendor_unavailable, phase 2). Not billed. Wait Retry-After seconds and retry with the same Idempotency-Key. |
Try it
Section titled “Try it”Open the interactive reference to send this request from the browser. The full document is at /openapi.json.