# Verdict > Verdict answers the questions an AI agent asks in the middle of its work (is this firm in my ICP, does this event matter now, who should I write to, what does this reply mean) as a calibrated probability with a reason code and cited evidence. One API key. Priced per decision. Misses, errors and refused calls are never billed. - Base URL: `https://api.askverdict.io/v1`. JSON only. API version `2026-11-01` (pin it with `X-Verdict-Version`). - Auth: `Authorization: Bearer vd_live_...` (test keys start `vd_test_`). Get a key with `POST /v1/signup`, then `POST /v1/signup/verify`. Starter credit: $5, no card. - Send `Idempotency-Key: ` on every POST. A repeat within 24 hours returns the stored response and bills nothing. - Cap spend per call with `X-Max-Cost-USD`. A call that would cost more is refused with 402 before any spend. - Errors are RFC 9457 problem details. Branch on `type`. Every error has a `request_id`. - Every decision returns `p` (calibrated), `answer` (branch on this), `reason_code`, `evidence`, `cost` and `balance_usd`. Report what happened next with `POST /v1/outcomes` (free). - Everything at once: https://askverdict.io/llms-full.txt. OpenAPI 3.1: https://docs.askverdict.io/openapi.json. # Guides ## Verdict docs Source: https://docs.askverdict.io/index.md ### For agents Read [`llms.txt`](https://docs.askverdict.io/llms.txt) first. It lists every call with its price, one line each. [`llms-full.txt`](https://docs.askverdict.io/llms-full.txt) holds every guide, every endpoint and every schema in one plain-Markdown file. The OpenAPI 3.1 document is at [`/openapi.json`](https://docs.askverdict.io/openapi.json). No login is needed for any of them. The shortest useful path: 1. `POST /v1/signup` with an email, then `POST /v1/signup/verify` with the token from the email. You get a key and $5 of credit. 2. `POST /v1/icps` to describe who you sell to. Free. 3. `POST /v1/decisions/qualify_firm` with a domain and the ICP id. $0.005 per answer. 4. Branch on `answer`. Report what happened with `POST /v1/outcomes`. Free. ### The decisions | Decision | Price per call | Question | | --- | --- | --- | | [`qualify_firm`](https://docs.askverdict.io/reference/qualify-firm/) | $0.005 | Is this firm in my ICP? | | [`score_signal`](https://docs.askverdict.io/reference/score-signal/) | $0.005 | Does this event matter, and is now the time? | | [`pick_person`](https://docs.askverdict.io/reference/pick-person/) | $0.005 | Who at this firm should I write to? | | [`triage_reply`](https://docs.askverdict.io/reference/triage-reply/) | $0.003 | What does this reply mean? | | [`right_person`](https://docs.askverdict.io/reference/right-person/) | $0.003 | Is this the right person to contact? | | [`should_send`](https://docs.askverdict.io/reference/should-send/) | $0.003 | Should this message go out now? | Everything else is free: index reads, firm resolution, outcomes, watches setup and account calls. See [Pricing](https://docs.askverdict.io/pricing/). ### Where to go next - [Quickstart](https://docs.askverdict.io/quickstart/): the fifteen-minute path, curl first, then the SDK. - [Concepts](https://docs.askverdict.io/concepts/): org, project, key, ICP, firm, event, decision, outcome, watch. - [Decisions](https://docs.askverdict.io/decisions/): when to call each one, what it costs, what comes back. - [Errors](https://docs.askverdict.io/errors/): every error `type`, its status and what to do. - [Eval results](https://docs.askverdict.io/evals/): precision and recall per decision version. - [Status](https://docs.askverdict.io/status/): live health of the API and decisions. ## Quickstart Source: https://docs.askverdict.io/quickstart.md This page is the fifteen-minute path. Every step is one HTTP call. No browser and no card are needed. Total cost: one `qualify_firm` answer, $0.005, paid from the $5 starter credit. You need `curl`, `uuidgen` and `jq`. All requests go to `https://api.askverdict.io/v1`. ### 1. Sign up Send your email. Verdict emails a verification link. Free. ```bash curl -X POST 'https://api.askverdict.io/v1/signup' \ -H "Idempotency-Key: $(uuidgen)" \ -H 'Content-Type: application/json' \ -d '{"email": "you@company.com", "org_name": "Your Company"}' ``` The response is `202` with `status` and `expires_at`. Disposable email domains are refused. ### 2. Verify and receive your key The email contains a token. Exchange it for your organization, your first project and your first API key. Free. ```bash curl -X POST 'https://api.askverdict.io/v1/signup/verify' \ -H "Idempotency-Key: $(uuidgen)" \ -H 'Content-Type: application/json' \ -d '{"token": "TOKEN_FROM_THE_EMAIL"}' ``` The response carries `secret`, shown once and never again, and `starter_credit_usd` of $5. Store the secret now: ```bash export VERDICT_API_KEY='vd_live_...' ``` The key has the default scopes `decisions:write`, `index:read` and `outcomes:write`. That is enough for everything below. ### 3. Describe who you sell to An ICP profile is plain text plus optional filters. Every decision is made against one. Free. ```bash ICP_ID=$(curl -s -X POST 'https://api.askverdict.io/v1/icps' \ -H "Authorization: Bearer $VERDICT_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H 'Content-Type: application/json' \ -d '{"name": "RIAs under $1B", "description": "US registered investment advisers with under $1B in assets and fewer than 50 staff. We sell trade reconciliation software to their operations lead.", "filters": {"firm_types": ["investment_adviser"], "size_bands": ["micro", "small"], "countries": ["US"]}}' \ | jq -r .id) echo "$ICP_ID" ``` The response is the `Icp` with its `id` (`icp_...`). ### 4. Make the first decision Ask whether a firm is in your ICP. Give a domain and the ICP id. $0.005, billed only when an answer comes back. ```bash curl -X POST 'https://api.askverdict.io/v1/decisions/qualify_firm' \ -H "Authorization: Bearer $VERDICT_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H 'X-Max-Cost-USD: 0.01' \ -H 'Content-Type: application/json' \ --data @- <`, then `verdict decide qualify_firm --domain northfieldcapital.com --icp `. ### What to read next - [Concepts](https://docs.askverdict.io/concepts/) for the vocabulary. - [Decisions](https://docs.askverdict.io/decisions/) for the other five calls. - [Outcomes and the feedback loop](https://docs.askverdict.io/outcomes/) to make `p` learn from your results. - [Errors](https://docs.askverdict.io/errors/) for what each `type` means and whether to retry. ## Concepts Source: https://docs.askverdict.io/concepts.md Nine nouns cover the whole API. Each has an id with a fixed prefix, so an agent can tell them apart on sight. | Concept | Id prefix | What it is | Created with | | ------------ | --------- | -------------------------------------------------------------------------------------------------------- | --------------------------- | | Organization | `org_` | The billing and data boundary. Nothing is ever read across orgs. | `POST /v1/signup/verify` | | Project | `prj_` | A namespace inside an org, for example `outbound` or `recruiting`. Keys, ICPs and watches belong to one. | first one at sign-up | | API key | `key_` | `vd_live_...` or `vd_test_...`. Scoped, shown once, revocable, rotatable. | `POST /v1/keys` | | ICP profile | `icp_` | Who you sell to and what you sell, as text plus optional filters. Every decision is made against one. | `POST /v1/icps` | | Firm | `frm_` | A company in Verdict's index, with its identifiers (domain, LEI, CIK, CRD, Companies House number). | resolved for you | | Event | `evt_` | Something that happened to a firm, from an open-licensed source. | the index | | Decision | `dec_` | One answer to one named question: `p`, `answer`, `reason_code`, `evidence`, `cost`. Immutable. | `POST /v1/decisions/` | | Outcome | `out_` | What happened after a decision: a bounce, a reply, a booking, an approval. | `POST /v1/outcomes` | | Watch | `wch_` | A standing request: push me scored events for this ICP above this probability. | `POST /v1/watches` | ### Organization and project An organization owns the balance, the members and the data. Every row Verdict stores for you carries your org id and is isolated by row-level security in the database. A project is a label inside the org. Usage is reported per project, so one org can run several agents and see each one's spend. ### API key Keys look like `vd_live_` plus 43 base62 characters. Test keys start `vd_test_`; they hit the same API and the same models but draw on a separate, non-cash test balance. A key is shown once when created and never again. Verdict stores only a peppered hash. Each key carries scopes. The default is `decisions:write index:read outcomes:write`. | Scope | Grants | | --- | --- | | `decisions:write` | Call the priced decisions. | | `index:read` | Read firms, events and sources. | | `outcomes:write` | Report outcomes. | | `watches:manage` | Create, list and delete watches. | | `vendors:call` | Call vendor pass-through (phase 2). | | `account:read` | Read the account, balance, usage, ledger, limits and audit log. | | `account:manage` | Top up and change decision thresholds on ICP profiles. | | `keys:manage` | Create, list, rotate and revoke API keys. | Each key can also have a rate limit, an IP allowlist, a daily spend cap and an expiry. `POST /v1/keys/{id}/rotate` issues a new secret and keeps the old one valid for a grace window you choose. ### ICP profile An ICP is your description of who you sell to, in plain text, plus optional structured filters (`firm_types`, `size_bands`, `countries`). Verdict summarizes it once when you create it and reuses that summary on every call, so long descriptions do not cost more per decision. Each ICP also holds your thresholds per decision (`thresholds`); see [thresholds](#decision). ### Firm A firm is a canonical company in Verdict's index. You rarely create one. Pass any reference (a domain, a name with a country hint, an LEI, a CIK, a CRD or a Companies House number) and Verdict resolves it. `GET /v1/firms/resolve` does the same lookup for free when you only need the id and identifiers. ### Event An event is one thing that happened to a firm: a filing, a registration, an officer change, a fund raise. Every event has the same shape whatever its source, with typed `attributes` per type. Events come only from open-licensed sources; see [Data sources](https://docs.askverdict.io/sources/). | Event `type` | Meaning | | --- | --- | | `filing` | A regulatory filing; the sub-type is in `attributes.filing_type`. | | `registration` | A new registration with a registry or regulator. | | `officer_change` | An officer or director was appointed or left. | | `ownership_change` | A change in significant ownership or control. | | `lei_change` | A change to the firm's LEI record or its parent relationships. | | `address_change` | A change of registered or headquarters address. | | `fund_raise` | A capital raise (for example a Form D offering). | | `insider_trade` | An insider transaction (for example a Form 4). | | `patent_filing` | A patent or trademark filing (phase-2 source). | | `litigation` | A court case involving the firm (phase-2 source). | | `news_mention` | A news mention with citation (phase-2 source). | | `repo_activity` | Organization-level open-source repository activity (phase-2 source). | | `hiring_aggregate` | An aggregate hiring indicator (phase-2 source). | ### Decision A decision is the priced unit. You name a question (`qualify_firm`, `score_signal`, `pick_person`, `triage_reply`, `right_person`, `should_send`), Verdict does the lookups underneath, asks its decision model a fixed set of typed questions and returns one envelope: - `p`: a calibrated probability, always present. - `answer`: `p` applied to your threshold for that decision. Branch on this one field. - `reason_code`: from a fixed list per decision. `reason` is a short template sentence. - `evidence`: sources with URLs and dates. - `calibration`: which map produced `p` and how many outcomes it rests on. - `cost` and `balance_usd`: what you paid and what is left. | `calibration.basis` | Meaning | | --- | --- | | `org` | The organization's own map (50 or more outcomes for this decision and question-set version). | | `segment` | The map for the ICP's segment (industry group and size band), pooled from opted-in orgs. | | `global` | The global map, used when neither the org nor the segment has 50 outcomes. | Thresholds are yours to move. Read them on the ICP; change them with `PATCH /v1/icps/{id}/thresholds` (needs `account:manage`) to trade precision for recall. Decisions are immutable: `GET /v1/decisions/{id}` returns the same body forever. ### Outcome An outcome says what happened after a decision. Report it with the decision id. Outcomes are free, unlimited and idempotent. They are how `calibration.basis` moves from `global` to `segment` to `org`, and how your thresholds tune themselves. See [Outcomes and the feedback loop](https://docs.askverdict.io/outcomes/). ### Watch A watch turns the index into a push feed. Give an ICP, an HTTPS URL, a minimum probability and the event types you care about. Verdict scores each new event for your ICP and posts the ones above `min_p`, signed. Each delivered event bills one `score_signal`. See [Watches and webhooks](https://docs.askverdict.io/watches/). ## Decisions Source: https://docs.askverdict.io/decisions.md A decision is one named question answered with a calibrated probability. All six are `POST /v1/decisions/`, take JSON, need the `decisions:write` scope and return the same envelope. Each is billed only when it returns an answer. | Decision | Price per call | Question | | --- | --- | --- | | [`qualify_firm`](https://docs.askverdict.io/reference/qualify-firm/) | $0.005 | Is this firm in my ICP? | | [`score_signal`](https://docs.askverdict.io/reference/score-signal/) | $0.005 | Does this event matter, and is now the time? | | [`pick_person`](https://docs.askverdict.io/reference/pick-person/) | $0.005 | Who at this firm should I write to? | | [`triage_reply`](https://docs.askverdict.io/reference/triage-reply/) | $0.003 | What does this reply mean? | | [`right_person`](https://docs.askverdict.io/reference/right-person/) | $0.003 | Is this the right person to contact? | | [`should_send`](https://docs.askverdict.io/reference/should-send/) | $0.003 | Should this message go out now? | ### Which one to call | Your agent is about to... | Call | Branch on | | ------------------------------------------- | ------------------------------------------ | ---------------------------------------------- | | spend effort on a company | [`qualify_firm`](https://docs.askverdict.io/decisions/qualify_firm/) | `answer`: `fit` or `not_fit` | | act on something that happened to a company | [`score_signal`](https://docs.askverdict.io/decisions/score_signal/) | `answer`, then `act_now` and `window_days` | | choose who at a firm to contact | [`pick_person`](https://docs.askverdict.io/decisions/pick_person/) | `people[0]`, `answer`: `pick` or `no_pick` | | handle a reply | [`triage_reply`](https://docs.askverdict.io/decisions/triage_reply/) | `answer`: the reply `state`, or `needs_review` | | check one contact before using them | [`right_person`](https://docs.askverdict.io/decisions/right_person/) | `answer`: `right_person` or `not_right_person` | | send a prepared message | [`should_send`](https://docs.askverdict.io/decisions/should_send/) | `answer`: `send` or `hold`, then `hold_reason` | ### The common envelope Every decision returns these fields, plus a few specific to the decision. | Field | Meaning | | ------------------------------------------------ | ------------------------------------------------------------------------ | | `id` | The decision id (`dec_...`). Report outcomes against it. | | `decision` | The decision name. | | `version` | The API version that answered. | | `question_set_version` | The question set that answered. Match it to the [eval results](https://docs.askverdict.io/evals/). | | `p` | Calibrated probability. Always present. | | `answer` | `p` against your threshold. Branch on this. | | `reason_code` | Fixed enum per decision (listed on each decision page). | | `reason` | A short template sentence. Never free text from a large model. | | `evidence` | Sources with URL and date. `source` is an index slug or `request`. | | `calibration` | `basis` (`org`, `segment` or `global`), `n_outcomes`, `threshold`. | | `cost` | `decision_usd`, `vendor_usd`, `total_usd`. | | `balance_usd` | Balance after this call. | | `latency_ms` | Server time for the call. | ### Rules that hold for every decision - **Idempotency.** Send `Idempotency-Key` (a UUID). A repeat within 24 hours returns the stored response and bills nothing. Reusing a key with a different body returns 409 `idempotency_conflict`. - **Spend caps.** Send `X-Max-Cost-USD`. A call that would cost more is refused with 402 `max_cost_exceeded` before any spend. - **Misses are free.** No answer, an error, or a refused call bills nothing. - **Decision model down.** You get 503 `decision_unavailable` with `Retry-After`. Retry with the same `Idempotency-Key`. Nothing is billed. - **Limits.** Request bodies up to 256 KB, rosters up to 50 people, threads up to 40 messages of 8 KB each. - **Fetch later.** `GET /v1/decisions/{id}` returns any past decision, free. ## qualify_firm Source: https://docs.askverdict.io/decisions/qualify_firm.md **Is this firm in my ICP?** [`POST /v1/decisions/qualify_firm`](https://docs.askverdict.io/reference/qualify-firm/). $0.005 per answer. Scope `decisions:write`. ### When to call Call it when your agent meets a company and must decide whether to spend effort on it: before enrichment, before research, before writing. One call replaces a lookup plus a model prompt. Do not call it twice for the same firm and ICP in a short window; the answer will not change until the index or your thresholds do. ### Inputs Give one firm reference and an ICP id. A domain is the easiest reference. A name works best with a country hint. Identifiers (`lei`, `cik`, `crd`, `companies_house`, `firm_id`) resolve exactly. | Field | Type | Required | Description | | --- | --- | --- | --- | | `firm` | FirmReference | yes | A firm reference: exactly one of `firm_id`, `domain`, `name` (with an optional `hint`), `lei`, `cik`, `crd` or `companies_house`. | | `firm.firm_id` | string | no | A firm id from Verdict's index. | | `firm.domain` | string | no | The firm's web domain. | | `firm.name` | string | no | The firm's legal or trading name. | | `firm.hint` | object | no | Optional country, city or domain to disambiguate the name. | | `firm.hint.country` | string | no | ISO 3166-1 alpha-2 country of the firm's headquarters. | | `firm.hint.city` | string | no | City of the firm's headquarters. | | `firm.hint.domain` | string | no | A domain the firm is known to use. | | `firm.lei` | string | no | The firm's Legal Entity Identifier. | | `firm.cik` | string | no | The firm's SEC Central Index Key. | | `firm.crd` | string | no | The firm's CRD number. | | `firm.companies_house` | string | no | The firm's UK Companies House number. | | `icp_id` | string | yes | The ICP profile to decide against. | ### Outputs The common envelope (see [Decisions](https://docs.askverdict.io/decisions/)) plus: - `fit`: the same value as `p`. - `answer`: `fit` when `p` clears your threshold, else `not_fit`. - `firm`: the resolved firm: `id`, `name`, `domain`, `identifiers`, `type`, `size_band` and `hq`. #### Reason codes | `reason_code` | Meaning | | --- | --- | | `icp_type_and_size_match` | The firm's type and size band both match the ICP. | | `icp_type_match_size_mismatch` | The firm's type matches the ICP but its size band is outside the ICP's range. | | `icp_size_match_type_mismatch` | The firm's size band matches the ICP but its type is not one the ICP targets. | | `icp_type_and_size_mismatch` | Neither the firm's type nor its size band matches the ICP. | | `icp_geography_mismatch` | The firm's headquarters are outside the ICP's geography. | | `icp_exclusion_filter_match` | The firm matches one of the ICP's structured exclusion filters. | | `firm_inactive` | Public registries show the firm as dissolved or inactive. | | `insufficient_firm_data` | Too few public facts are indexed for the firm to judge fit with confidence. | ### Cost $0.005 when an answer is returned. A firm that cannot be resolved returns 404 `not_found` and bills nothing. Errors bill nothing. ### Example ```bash title="Request" curl -X POST 'https://api.askverdict.io/v1/decisions/qualify_firm' \ -H "Authorization: Bearer $VERDICT_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H 'Content-Type: application/json' \ -d '{ "firm": { "domain": "northfieldcapital.com" }, "icp_id": "icp_01JC3Z4Y8R9K2M5N7P1Q3S5T7V" }' ``` ```json title="Response 200" { "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": "qualify_firm", "p": 0.82, "answer": "fit", "reason_code": "icp_type_and_size_match", "reason": "Registered investment adviser, $400M AUM band, 12 employees; matches ICP type and size.", "evidence": [ { "source": "sec_edgar", "url": "https://adviserinfo.sec.gov/firm/summary/298765", "observed_at": "2026-10-28", "label": "Form ADV Part 1A" } ], "fit": 0.82, "firm": { "id": "frm_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "name": "Northfield Capital Advisors LLC", "domain": "northfieldcapital.com", "identifiers": { "crd": "298765", "lei": "549300ABCDEF12345678" }, "type": "investment_adviser", "size_band": "small", "hq": { "country": "US", "region": "MA", "city": "Boston" }, "status": "active" }, "cost": { "decision_usd": 0.005, "vendor_usd": 0, "total_usd": 0.005 }, "balance_usd": 18.32 } ``` ### Then Report what happened with [`POST /v1/outcomes`](https://docs.askverdict.io/reference/create-outcome/): `approved` or `skipped` from your own review, then `replied` or `meeting_booked` later. See the [eval results](https://docs.askverdict.io/evals/) for this decision's precision and recall. ## score_signal Source: https://docs.askverdict.io/decisions/score_signal.md **Does this event matter for my offer, and is now the time?** [`POST /v1/decisions/score_signal`](https://docs.askverdict.io/reference/score-signal/). $0.005 per answer. Scope `decisions:write`. ### When to call Call it when your agent sees something happen to a company (a filing, a raise, a new officer) and must decide whether to act on it. Use an `event_id` from [`GET /v1/events`](https://docs.askverdict.io/reference/list-events/) or a watch delivery. If the event is not in Verdict's index, send it inline. To score many events at once, call [`GET /v1/events`](https://docs.askverdict.io/reference/list-events/) with `icp_id`: each returned event is scored and billed as one `score_signal`. To have events pushed to you, create a [watch](https://docs.askverdict.io/watches/). ### Inputs Either an indexed event or an inline event, plus the ICP. | Field | Type | Required | Description | | --- | --- | --- | --- | | `event_id` | string | no | An event from the index. | | `icp_id` | string | no | The ICP profile to decide against. | | `event` | InlineEvent | no | An event the caller supplies, for events Verdict's index does not hold. | | `event.type` | enum: "filing", "registration", "officer_change", "ownership_change", "lei_change", "address_change", "fund_raise", "insider_trade", "patent_filing", "litigation", "news_mention", "repo_activity", "hiring_aggregate" | yes | Event type. | | `event.firm` | FirmReference | yes | A firm reference: exactly one of `firm_id`, `domain`, `name` (with an optional `hint`), `lei`, `cik`, `crd` or `companies_house`. | | `event.firm.firm_id` | string | no | A firm id from Verdict's index. | | `event.firm.domain` | string | no | The firm's web domain. | | `event.firm.name` | string | no | The firm's legal or trading name. | | `event.firm.hint` | object | no | Optional country, city or domain to disambiguate the name. | | `event.firm.lei` | string | no | The firm's Legal Entity Identifier. | | `event.firm.cik` | string | no | The firm's SEC Central Index Key. | | `event.firm.crd` | string | no | The firm's CRD number. | | `event.firm.companies_house` | string | no | The firm's UK Companies House number. | | `event.occurred_at` | string (date-time) | yes | When the event happened. | | `event.title` | string | no | One-line summary of the event. | | `event.description` | string | no | What happened, in plain text. At most 8192 bytes of UTF-8. | | `event.source_url` | string (uri) | no | Where the event was reported. | ### Outputs The common envelope plus: - `relevant`: probability the event matters for your offer. Same value as `p`. - `act_now`: probability that now is the time to act. - `window_days`: how many days the window stays open. - `answer`: `relevant` or `not_relevant`. - `event_id` and `firm`. #### Reason codes | `reason_code` | Meaning | | --- | --- | | `event_relevant_and_timely` | The event type matters for the ICP's offer and is recent enough to act on now. | | `event_relevant_but_stale` | The event type matters for the ICP's offer but the window to act has passed. | | `event_type_not_relevant` | The event type does not bear on the ICP's offer. | | `event_routine` | The event is routine (a periodic or amended filing, a minor change) and carries little signal. | | `firm_not_in_icp` | The firm the event belongs to does not fit the ICP, so the event does not matter. | | `event_low_confidence` | The event's source confidence or firm keying is low. | | `event_superseded` | The event duplicates or is superseded by a later event for the same firm. | | `insufficient_event_data` | The event carries too few attributes to judge relevance with confidence. | ### Cost $0.005 when an answer is returned. Errors bill nothing. ### Example ```bash title="Request" curl -X POST 'https://api.askverdict.io/v1/decisions/score_signal' \ -H "Authorization: Bearer $VERDICT_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H 'Content-Type: application/json' \ -d '{ "event_id": "evt_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "icp_id": "icp_01JC3Z4Y8R9K2M5N7P1Q3S5T7V" }' ``` ```json title="Response 200" { "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": "score_signal", "p": 0.78, "answer": "relevant", "reason_code": "event_relevant_and_timely", "reason": "Form D offering filed 4 days ago; new capital is a buying trigger for this ICP.", "evidence": [ { "source": "sec_edgar", "url": "https://www.sec.gov/Archives/edgar/data/1601234/000160123426000004/xslFormDX01/primary_doc.xml", "observed_at": "2026-11-04", "label": "Form D" } ], "relevant": 0.78, "act_now": 0.64, "window_days": 21, "event_id": "evt_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "firm": { "id": "frm_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "name": "Northfield Capital Advisors LLC", "domain": "northfieldcapital.com", "identifiers": { "crd": "298765", "lei": "549300ABCDEF12345678" }, "type": "investment_adviser", "size_band": "small", "hq": { "country": "US", "region": "MA", "city": "Boston" }, "status": "active" }, "cost": { "decision_usd": 0.005, "vendor_usd": 0, "total_usd": 0.005 }, "balance_usd": 18.315 } ``` ## pick_person Source: https://docs.askverdict.io/decisions/pick_person.md **Who at this firm should I write to?** [`POST /v1/decisions/pick_person`](https://docs.askverdict.io/reference/pick-person/). $0.005 per call, for up to 50 people. Scope `decisions:write`. ### When to call Call it when you have a list of people at one firm and must choose whom to contact. Bring the roster from your own source. Verdict never uses LinkedIn data and does not need it: name, title, seniority and department are enough. ### Inputs | Field | Type | Required | Description | | --- | --- | --- | --- | | `roster` | array of RosterEntry | yes | The people to rank, 1 to 50. | | `roster[].ref` | string | no | Your own id for this person, echoed back in `people`. | | `roster[].name` | string | yes | Full name. | | `roster[].title` | string | yes | Job title. | | `roster[].seniority` | enum: "owner", "c_level", "vp", "director", "manager", "senior", "entry", "unknown" | no | Seniority, if known. | | `roster[].department` | string | no | Department or function, for example `Operations`. | | `roster[].location` | string | no | City or country. | | `roster[].tenure_months` | integer | no | Months in the current role. | | `firm` | FirmReference | no | A firm reference: exactly one of `firm_id`, `domain`, `name` (with an optional `hint`), `lei`, `cik`, `crd` or `companies_house`. | | `firm.firm_id` | string | no | A firm id from Verdict's index. | | `firm.domain` | string | no | The firm's web domain. | | `firm.name` | string | no | The firm's legal or trading name. | | `firm.hint` | object | no | Optional country, city or domain to disambiguate the name. | | `firm.hint.country` | string | no | ISO 3166-1 alpha-2 country of the firm's headquarters. | | `firm.hint.city` | string | no | City of the firm's headquarters. | | `firm.hint.domain` | string | no | A domain the firm is known to use. | | `firm.lei` | string | no | The firm's Legal Entity Identifier. | | `firm.cik` | string | no | The firm's SEC Central Index Key. | | `firm.crd` | string | no | The firm's CRD number. | | `firm.companies_house` | string | no | The firm's UK Companies House number. | | `icp_id` | string | yes | The ICP profile to decide against. | | `question` | string | no | Optional extra question, for example `who owns trading operations?`. | Give each person a `ref` of your own so you can map the ranking back. Add an optional `question` to steer the choice, for example `who owns trading operations?`. ### Outputs The common envelope plus `people`: the roster ranked, each entry with `ref`, `index`, `rank` and `p`. `p` on the envelope is the top person's. `answer` is `pick` when the top person clears your threshold, else `no_pick`: nobody on this roster is a good fit. #### Reason codes | `reason_code` | Meaning | | --- | --- | | `buyer_role_match` | The top-ranked person's title and department match the ICP's buyer role. | | `seniority_match` | The top-ranked person's seniority fits the decision authority the offer needs. | | `influencer_role_match` | The best match is an adjacent role likely to influence, not own, the purchase. | | `question_match` | The top-ranked person best matches the caller's own `question`. | | `too_senior_for_offer` | The strongest title matches are more senior than the offer usually needs. | | `too_junior_for_offer` | The strongest title matches are more junior than the offer usually needs. | | `titles_ambiguous` | Roster titles are too vague to rank with confidence. | | `no_strong_match` | No one in the roster is a strong match; the ranking is best effort. | ### Cost $0.005 per call, whatever the roster size up to 50. Errors bill nothing. Rosters over 50 are refused with 400 `invalid_request`. ### Example ```bash title="Request" curl -X POST 'https://api.askverdict.io/v1/decisions/pick_person' \ -H "Authorization: Bearer $VERDICT_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H 'Content-Type: application/json' \ -d '{ "roster": [ { "ref": "c-101", "name": "Dana Whitfield", "title": "Chief Operating Officer", "seniority": "c_level", "department": "Operations" }, { "ref": "c-102", "name": "Sam Ortega", "title": "Operations Associate", "seniority": "entry", "department": "Operations" }, { "ref": "c-103", "name": "Priya Raman", "title": "Chief Compliance Officer", "seniority": "c_level", "department": "Compliance" } ], "firm": { "domain": "northfieldcapital.com" }, "icp_id": "icp_01JC3Z4Y8R9K2M5N7P1Q3S5T7V" }' ``` ```json title="Response 200" { "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": "pick_person", "p": 0.74, "answer": "pick", "reason_code": "buyer_role_match", "reason": "Chief Operating Officer owns operations tooling at a 12-person adviser.", "evidence": [ { "source": "request", "url": null, "observed_at": "2026-11-07", "pointer": "/roster/0" } ], "people": [ { "index": 0, "ref": "c-101", "rank": 1, "p": 0.74 }, { "index": 2, "ref": "c-103", "rank": 2, "p": 0.41 }, { "index": 1, "ref": "c-102", "rank": 3, "p": 0.09 } ], "cost": { "decision_usd": 0.005, "vendor_usd": 0, "total_usd": 0.005 }, "balance_usd": 18.31 } ``` ## triage_reply Source: https://docs.askverdict.io/decisions/triage_reply.md **What does this reply mean?** [`POST /v1/decisions/triage_reply`](https://docs.askverdict.io/reference/triage-reply/). $0.003 per answer. Scope `decisions:write`. ### When to call Call it on every inbound reply before your agent answers, books, stops or escalates. Send the thread (up to 40 messages, 8 KB each), oldest first. Text in the thread passes an injection guard before it reaches the model, so a reply that says "ignore your instructions" is treated as text, not as an instruction. ### Inputs | 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. | `icp_id` is optional. Without it the call is calibrated on your org's map, or the global map, and thresholded at your `triage_reply` threshold, else 0.5. Pass `context.decision_id` to link the triage to the decision behind the original send. ### Outputs The common envelope plus: - `state`: the most likely state. - `next_action`: what to do next. - `answer`: the `state` when `p` clears your threshold, else `needs_review`. Send `needs_review` to a human. #### States | `state` | Meaning | | --- | --- | | `interested` | The prospect is interested. | | `meeting_requested` | The prospect asked for or accepted a meeting. | | `question` | The prospect asked a question. | | `objection_price` | The prospect objected on price. | | `objection_timing` | The prospect objected on timing (not now). | | `objection_fit` | The prospect objected on fit (not for us). | | `not_decision_maker` | The recipient says they are not the decision maker. | | `referral` | The recipient referred the sender to someone else. | | `out_of_office` | An out-of-office or vacation auto-reply. | | `unsubscribe` | The prospect asked not to be contacted again. | | `bounce` | The message was not delivered. | | `wrong_person` | The recipient is the wrong person (wrong role or wrong firm). | | `other` | None of the above. | #### Next actions | `next_action` | Meaning | | --- | --- | | `reply` | Write a reply that continues the conversation. | | `book_meeting` | Propose times or send a booking link. | | `answer_question` | Answer the prospect's question. | | `handle_objection` | Address the price, timing or fit objection. | | `forward_to_referral` | Contact the person the prospect referred you to. | | `find_other_contact` | Pick another person at the firm (for example with `pick_person`). | | `retry_later` | Wait and follow up after the stated return date or timing. | | `unsubscribe` | Add the address to the suppression list. | | `stop` | Stop the sequence for this contact (for example after a bounce). | | `escalate_to_human` | Hand the thread to a human; the agent should not act alone. | | `none` | No action is needed. | #### Reason codes | `reason_code` | Meaning | | --- | --- | | `explicit_positive_intent` | The reply states interest in the offer in so many words. | | `explicit_meeting_request` | The reply asks for or proposes a meeting time. | | `explicit_question` | The reply asks a question that needs an answer before it can progress. | | `explicit_objection` | The reply raises a price, timing or fit objection. | | `explicit_opt_out` | The reply asks not to be contacted again. | | `redirect_to_other_person` | The reply points to someone else or says the recipient is not the right person. | | `auto_reply_detected` | The reply is an automatic message (out of office, vacation, auto-acknowledgement). | | `delivery_failure_detected` | The reply is a delivery-failure notice (bounce). | | `ambiguous_reply` | The reply's intent cannot be read with confidence. | | `thread_too_short` | The thread carries too little context to classify with confidence. | ### Cost $0.003 when an answer is returned. Errors bill nothing. ### Example ```bash title="Request" 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" } }' ``` ```json title="Response 200" { "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 } ``` ## right_person Source: https://docs.askverdict.io/decisions/right_person.md **Is this contact the right one?** [`POST /v1/decisions/right_person`](https://docs.askverdict.io/reference/right-person/). $0.003 per answer. Scope `decisions:write`. ### When to call Call it when you already have one candidate contact and want a go or no-go before using them: a contact from a vendor, a referral, a name from a reply. For choosing among several people at one firm, use [`pick_person`](https://docs.askverdict.io/decisions/pick_person/), which costs the same for up to 50. ### Inputs | Field | Type | Required | Description | | --- | --- | --- | --- | | `candidate` | CandidateContact | yes | One candidate contact. LinkedIn-sourced data is not accepted. | | `candidate.name` | string | no | Full name. | | `candidate.title` | string | yes | Job title. | | `candidate.seniority` | enum: "owner", "c_level", "vp", "director", "manager", "senior", "entry", "unknown" | no | Seniority, if known. | | `candidate.department` | string | no | Department or function. | | `candidate.email` | string (email) | no | Work email, if known. | | `candidate.firm` | FirmReference | no | A firm reference: exactly one of `firm_id`, `domain`, `name` (with an optional `hint`), `lei`, `cik`, `crd` or `companies_house`. | | `candidate.firm.firm_id` | string | no | A firm id from Verdict's index. | | `candidate.firm.domain` | string | no | The firm's web domain. | | `candidate.firm.name` | string | no | The firm's legal or trading name. | | `candidate.firm.hint` | object | no | Optional country, city or domain to disambiguate the name. | | `candidate.firm.lei` | string | no | The firm's Legal Entity Identifier. | | `candidate.firm.cik` | string | no | The firm's SEC Central Index Key. | | `candidate.firm.crd` | string | no | The firm's CRD number. | | `candidate.firm.companies_house` | string | no | The firm's UK Companies House number. | | `candidate.title_observed_at` | string (date-time) | no | When you last saw this title, to judge staleness. | | `icp_id` | string | yes | The ICP profile to decide against. | Only `title` is required on the candidate. Add `title_observed_at` when you know how fresh the title is; stale titles lower `p`. ### Outputs The common envelope. `answer` is `right_person` or `not_right_person`. #### Reason codes | `reason_code` | Meaning | | --- | --- | | `title_and_seniority_match` | The candidate's title and seniority both match the ICP's buyer. | | `title_match_seniority_mismatch` | The candidate's title matches but their seniority does not. | | `seniority_match_title_mismatch` | The candidate's seniority matches but their title does not. | | `department_mismatch` | The candidate works in a department the ICP does not sell to. | | `firm_not_in_icp` | The candidate's firm does not fit the ICP. | | `candidate_data_stale` | The candidate's details look out of date (role or firm likely changed). | | `insufficient_candidate_data` | Too few candidate fields were supplied to judge with confidence. | ### Cost $0.003 when an answer is returned. Errors bill nothing. ### Example ```bash title="Request" curl -X POST 'https://api.askverdict.io/v1/decisions/right_person' \ -H "Authorization: Bearer $VERDICT_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H 'Content-Type: application/json' \ -d '{ "candidate": { "name": "Dana Whitfield", "title": "Chief Operating Officer", "seniority": "c_level", "department": "Operations", "firm": { "domain": "northfieldcapital.com" } }, "icp_id": "icp_01JC3Z4Y8R9K2M5N7P1Q3S5T7V" }' ``` ```json title="Response 200" { "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": "right_person", "p": 0.81, "answer": "right_person", "reason_code": "title_and_seniority_match", "reason": "COO at a small adviser matches the ICP's operations buyer.", "evidence": [ { "source": "request", "url": null, "observed_at": "2026-11-07", "pointer": "/candidate/title" } ], "cost": { "decision_usd": 0.003, "vendor_usd": 0, "total_usd": 0.003 }, "balance_usd": 18.304 } ``` ## should_send Source: https://docs.askverdict.io/decisions/should_send.md **Should this message go out now?** [`POST /v1/decisions/should_send`](https://docs.askverdict.io/reference/should-send/). $0.003 per answer. Scope `decisions:write`. ### When to call Call it as the last step before your agent sends a message, after the message is written. It weighs the message, the decisions that led to it and the inbox health numbers you supply. Verdict never sends anything; it only answers. ### Inputs | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | object | yes | The prepared message. | | `message.channel` | enum: "email", "other" | yes | Channel the message will go out on. | | `message.subject` | string | no | Subject line. | | `message.body` | string | yes | Plain-text body as it will be sent. At most 8192 bytes of UTF-8. | | `decision_ids` | array of string | yes | The decisions behind the message (for example `qualify_firm` and `pick_person`), 1 to 10. | | `inbox_health` | InboxHealth | yes | Inbox health you measure; Verdict never reads your inbox. | | `inbox_health.bounce_rate_7d` | number | no | Share of sends that bounced over the last 7 days. | | `inbox_health.spam_complaint_rate_7d` | number | no | Share of sends marked as spam over the last 7 days. | | `inbox_health.reply_rate_7d` | number | no | Share of sends replied to over the last 7 days. | | `inbox_health.sent_today` | integer | no | Messages sent from this inbox today. | | `inbox_health.daily_limit` | integer | no | Your daily send limit for this inbox. | | `inbox_health.domain_age_days` | integer | no | Age of the sending domain in days. | | `recipient` | object | no | Optional contact history of the recipient. | | `recipient.last_contacted_at` | string (date-time) | no | When you last wrote to this recipient. | | `recipient.touches_30d` | integer | no | Messages sent to this recipient in 30 days. | | `icp_id` | string | yes | The ICP profile to decide against. | `decision_ids` are the earlier decisions behind this message (for example the `qualify_firm` and `pick_person` ids). `inbox_health` comes from your sending tool: bounce rate, complaint rate, today's volume and limit, domain age. ### Outputs The common envelope plus: - `send`: probability the message should go out now. Same value as `p`. - `answer`: `send` or `hold`. - `hold_reason`: why to hold, when `answer` is `hold`. #### Hold reasons | `hold_reason` | Meaning | | --- | --- | | `upstream_decision_weak` | A decision behind the message is below its threshold; re-check the firm, person or signal. | | `upstream_decision_missing` | A decision id behind the message is unknown; make the decision first. | | `inbox_health_degraded` | The sending inbox is unhealthy; pause sending from it. | | `daily_volume_limit` | The inbox has hit its daily volume; send tomorrow. | | `recently_contacted` | The recipient was contacted too recently; wait. | | `message_issue` | Fix the message before sending. | | `recipient_opted_out` | The recipient opted out; never send. | | `low_confidence` | The overall send probability is below threshold without a single dominant cause. | #### Reason codes | `reason_code` | Meaning | | --- | --- | | `all_checks_passed` | The upstream decisions, inbox health and message checks all support sending now. | | `upstream_decision_below_threshold` | At least one decision behind the message is below its current threshold. | | `upstream_decision_missing` | At least one decision id behind the message is unknown to this organization. | | `inbox_health_degraded` | The supplied inbox health (bounce, spam-complaint or reply rates) is outside safe bounds. | | `daily_volume_limit_reached` | The sending inbox has reached the daily volume the caller supplied. | | `recipient_recently_contacted` | The recipient was contacted too recently according to the supplied history. | | `message_quality_issue` | The message has a defect such as an unfilled template variable or an empty body. | | `recipient_opted_out` | An earlier outcome shows the recipient opted out. | ### Cost $0.003 when an answer is returned. Errors bill nothing. ### Example ```bash title="Request" curl -X POST 'https://api.askverdict.io/v1/decisions/should_send' \ -H "Authorization: Bearer $VERDICT_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H 'Content-Type: application/json' \ -d '{ "message": { "channel": "email", "subject": "Trade ops after your raise", "body": "Hi Dana, congratulations on the new fund. We help 10-person advisers automate trade reconciliation. Open to a short call next week?" }, "decision_ids": [ "dec_01JC3Z4Y8R9K2M5N7P1Q3S5T7V" ], "inbox_health": { "bounce_rate_7d": 0.012, "spam_complaint_rate_7d": 0.0005, "sent_today": 38, "daily_limit": 50 }, "recipient": { "touches_30d": 1 }, "icp_id": "icp_01JC3Z4Y8R9K2M5N7P1Q3S5T7V" }' ``` ```json title="Response 200" { "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": "should_send", "p": 0.88, "answer": "send", "reason_code": "all_checks_passed", "reason": "Upstream decisions clear their thresholds and inbox health is within bounds.", "evidence": [ { "source": "request", "url": null, "observed_at": "2026-11-07", "pointer": "/inbox_health" } ], "send": 0.88, "hold_reason": null, "cost": { "decision_usd": 0.003, "vendor_usd": 0, "total_usd": 0.003 }, "balance_usd": 18.301 } ``` ## Outcomes and the feedback loop Source: https://docs.askverdict.io/outcomes.md 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. ### Report an outcome [`POST /v1/outcomes`](https://docs.askverdict.io/reference/create-outcome/). Free. Scope `outcomes:write`. ```bash title="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" } }' ``` ```json title="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:` 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. | ### What the loop does 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. ### What is pooled, and what is not 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. ### Outcomes without code 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}`](https://docs.askverdict.io/reference/receive-instantly-webhook/) | | Smartlead | [`POST /v1/webhooks/smartlead/{receiver_token}`](https://docs.askverdict.io/reference/receive-smartlead-webhook/) | | Cal.com | [`POST /v1/webhooks/calcom/{receiver_token}`](https://docs.askverdict.io/reference/receive-calcom-webhook/) | | Calendly | [`POST /v1/webhooks/calendly/{receiver_token}`](https://docs.askverdict.io/reference/receive-calendly-webhook/) | 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](https://docs.askverdict.io/closing-the-loop/) has the whole convention. Verdict never holds your vendor credentials. ### Your own approval step 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. ## Closing the loop Source: https://docs.askverdict.io/closing-the-loop.md Verdict gets better for you when it learns what happened after each decision. That only works if every outcome can be traced to the decision that caused it. This page is the tagging convention that makes the trace work: one id, carried by every send, reported back with one call. ### The decision id Every decision response has an `id`, for example `dec_01JC3Z4Y8R9K2M5N7P1Q3S5T7V` (`id`). It is `dec_` followed by a ULID, it is unique, and it never changes. Keep it next to whatever your agent does with the answer: the lead you add to a campaign, the email you send, the meeting link you share, the approval you ask a human for. You can read the decision again at any time with [`GET /v1/decisions/{id}`](https://docs.askverdict.io/reference/get-decision/), free. ### Tag the send Put the id where the tool you send with can carry it, so that tool's events come back tagged. | Where the send goes | Carry the id as | | ----------------------------------------------- | ------------------------------------------------------------ | | Your own SMTP server or an inbox API | the email header `X-Verdict-Decision: dec_...` | | Instantly, Smartlead or another sequencer | a custom field named `verdict_decision_id` on the lead | | Cal.com, Calendly or another booking link | a metadata or UTM field named `verdict_decision_id` | | A human approval step (a queue, a Slack button) | keep the id with the item and report `approved` or `skipped` | Use exactly these names: `X-Verdict-Decision` for the header and `verdict_decision_id` for custom fields and metadata. The inbound receivers ([`POST /v1/webhooks/instantly/{receiver_token}`](https://docs.askverdict.io/reference/receive-instantly-webhook/) and the others on [Outcomes](https://docs.askverdict.io/outcomes/)) look for them. One id per send: when a message rests on several decisions (for example `qualify_firm`, then `pick_person`, then `should_send`), tag it with the last one, the decision that let it go out. The header is only for your sends. Verdict's own endpoints do not read or return `X-Verdict-Decision`; the id of a decision is always the `id` in its response. ### The SDK helpers The SDKs build the tags for you. `withDecision(decisionId)` (TypeScript, `@verdict/sdk`) and `with_decision(decision_id)` (Python, `askverdict`) check that the id is a decision id and return the three shapes: ```ts import { Verdict, withDecision } from "@verdict/sdk"; const verdict = new Verdict(); // reads VERDICT_API_KEY const decision = await verdict.decisions.shouldSend(request); const tag = withDecision(decision.id); // tag.headers -> { "X-Verdict-Decision": "dec_..." } for your own SMTP or inbox API // tag.customFields -> { verdict_decision_id: "dec_..." } Instantly custom_variables, Smartlead custom_fields // tag.metadata -> { verdict_decision_id: "dec_..." } Cal.com metadata, Calendly fields ``` The names are also exported as constants: `DECISION_HEADER` (`X-Verdict-Decision`) and `DECISION_FIELD` (`verdict_decision_id`). `isDecisionId(value)` and `assertDecisionId(value)` validate an id you stored yourself. Python has the same names in snake case. For your own approval step, `verdict.approve(decisionId)` and `verdict.skip(decisionId)` report `approved` and `skipped`, and `verdict.outcomes.report({ decisionId, outcome, observedAt })` reports any other outcome (`observedAt` defaults to now). ### Report the outcome When something happens, report it with [`POST /v1/outcomes`](https://docs.askverdict.io/reference/create-outcome/). Free, unlimited, scope `outcomes:write`. ```bash title="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" } }' ``` ```json title="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" } ``` - `decision_id` is the id you tagged the send with. - `outcome` is a built-in outcome or `custom:` for your own. - `observed_at` is when it happened, not when you report it. The call is idempotent on the three together: the first report answers `201`, the same report again answers `200` with the outcome already stored and changes nothing. So a webhook retried by a vendor, or a job that reports twice, never double counts. An id from another organization is answered `404`, exactly like an id that does not exist. ### What happens next Once your organization has 50 labeled decisions for a decision type and question-set version, Verdict refits a calibration map for your org right away, and again at every further 50. A decision is labeled by its first outcome that says whether it was right (`approved`, `replied` or `meeting_booked` say yes; `skipped`, `not_interested` or `reopened` say no). Further outcomes on the same decision, and outcomes that say neither (such as `delivered` for most decisions, or `custom:` ones), do not add to the count. From then on `calibration` says `basis: org` and `p` is calibrated on your own results. Maps are also refit nightly. [Outcomes](https://docs.askverdict.io/outcomes/) explains the bases and what is pooled. Your thresholds are separate from calibration: set them per ICP profile with [`PATCH /v1/icps/{id}/thresholds`](https://docs.askverdict.io/reference/update-icp-thresholds/) to trade precision for recall. A threshold changes `answer` and nothing else. ## Watches and webhooks Source: https://docs.askverdict.io/watches.md A watch is a standing request: "send me scored events for this ICP above this probability". Verdict scores each new event in the index against your ICP and posts the ones that clear `min_p` to your URL. ### Cost Creating, listing and deleting watches is free. Each delivered event bills one `score_signal`, $0.005. Retries are never billed. Scope `watches:manage`. ### Create a watch [`POST /v1/watches`](https://docs.askverdict.io/reference/create-watch/). ```bash title="Request" curl -X POST 'https://api.askverdict.io/v1/watches' \ -H "Authorization: Bearer $VERDICT_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H 'Content-Type: application/json' \ -d '{ "icp_id": "icp_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "webhook_url": "https://agent.northwind-ops.io/hooks/verdict", "min_p": 0.7, "types": [ "filing", "fund_raise", "officer_change" ] }' ``` ```json title="Response 201" { "id": "wch_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "project_id": "prj_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "icp_id": "icp_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "webhook_url": "https://agent.northwind-ops.io/hooks/verdict", "min_p": 0.7, "types": [ "filing", "fund_raise", "officer_change" ], "status": "pending_verification", "created_at": "2026-11-07T14:10:00Z", "updated_at": "2026-11-07T14:10:00Z", "signing_secret": "whsec_ExampleExampleExampleExampleExample" } ``` The response includes `signing_secret` (`whsec_...`). It is shown once, like an API key. Store it to verify deliveries. | Field | Meaning | | -------------------------------------- | ------------------------------------------------------------------ | | `icp_id` | The ICP to score events against. | | `webhook_url` | Your HTTPS endpoint. Private and link-local addresses are refused. | | `min_p` | Deliver events whose `relevant` is at least this. | | `types` | Event types to watch. | ### The challenge Right after creation, Verdict sends a `watchChallenge` request to your URL. Answer 200 with `{"challenge": }` within 10 seconds. The watch becomes `active` only then. The challenge is signed like every delivery. ### Deliveries Each delivery is a POST of a scored event to your URL. See the [`scoredEvent` webhook](https://docs.askverdict.io/reference/scored-event-webhook/) for the full body. ```bash title="Request" curl -X POST 'https://your-app.example.com/verdict/webhook' \ -H 'Verdict-Signature: t=1794060000,v1=5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e' \ -H 'Content-Type: application/json' \ -d '{ "id": "dlv_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "type": "event.scored", "created_at": "2026-11-07T15:00:03Z", "watch_id": "wch_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "event": { "id": "evt_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "type": "filing", "firm_id": "frm_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "occurred_at": "2026-11-03T21:15:00Z", "observed_at": "2026-11-04T02:05:00Z", "source": "sec_edgar", "source_url": "https://www.sec.gov/Archives/edgar/data/1601234/000160123426000004/xslFormDX01/primary_doc.xml", "confidence": 0.97, "attributes": { "filing_type": "sec_form_d", "accession_number": "0001601234-26-000004", "title": "Notice of Exempt Offering of Securities" }, "score": { "decision_id": "dec_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "icp_id": "icp_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "relevant": 0.78, "act_now": 0.64, "window_days": 21, "answer": "relevant", "reason_code": "event_relevant_and_timely" } }, "score": { "decision_id": "dec_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "icp_id": "icp_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "relevant": 0.78, "act_now": 0.64, "window_days": 21, "answer": "relevant", "reason_code": "event_relevant_and_timely" } }' ``` ```http title="Response" HTTP/1.1 200 ``` Answer any 2xx within 10 seconds. Anything else is retried with exponential backoff for 24 hours. Dedupe on the delivery `id`. Check past deliveries and their status with [`GET /v1/watches/{id}/deliveries`](https://docs.askverdict.io/reference/list-watch-deliveries/). ### Verify the signature Every request carries `Verdict-Signature: t=,v1=`. The `v1` value is HMAC-SHA256 of `.` keyed with the watch's signing secret. 1. Read the raw body before parsing JSON. 2. Compute HMAC-SHA256 of `t + "." + body` with your secret. 3. Accept if any `v1` matches (several appear while a secret rotation overlaps) and `t` is within 5 minutes of your clock. ```ts import { createHmac, timingSafeEqual } from "node:crypto"; export function verify(header: string, rawBody: string, secret: string): boolean { const parts = header.split(","); const t = parts.find((p) => p.startsWith("t="))?.slice(2) ?? ""; if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false; const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest(); return parts .filter((p) => p.startsWith("v1=")) .some((p) => { const got = Buffer.from(p.slice(3), "hex"); return got.length === expected.length && timingSafeEqual(got, expected); }); } ``` ### Manage watches [`GET /v1/watches`](https://docs.askverdict.io/reference/list-watches/), [`GET /v1/watches/{id}`](https://docs.askverdict.io/reference/get-watch/) and [`DELETE /v1/watches/{id}`](https://docs.askverdict.io/reference/delete-watch/). All free. ## Errors Source: https://docs.askverdict.io/errors.md Errors are [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) problem details with media type `application/problem+json`. Errors are never billed. Branch on `type`, never on `title` or `detail`. Every error carries `request_id`, also sent as the `X-Request-Id` header. Quote it to support. ### Every error type | `type` | Status | Title | Meaning | Retry | What to do | | --- | --- | --- | --- | --- | --- | | `invalid_request` | 400 | Invalid request | The request is malformed, has unknown fields or breaks a size limit. | no | Fix the request. `errors[]` holds a JSON Pointer and a message per bad field. Do not retry unchanged. | | `unauthorized` | 401 | Unauthorized | The API key is missing, malformed, revoked or expired. | no | Check the `Authorization: Bearer vd_live_...` header. The key may be revoked or expired; create or rotate one. | | `forbidden_scope` | 403 | Forbidden | The key lacks the scope this endpoint needs, or the resource belongs to another org. | no | Use a key with the scope the endpoint lists, or add the scope with `PATCH /v1/keys/{id}`. | | `not_found` | 404 | Not found | The resource does not exist in this organization. | no | The id does not exist in your organization. Check the id; firms resolve with `GET /v1/firms/resolve`. | | `insufficient_balance` | 402 | Insufficient balance | The balance cannot cover the call; top up. | after top-up | Top up with `POST /v1/topup` (returns a Checkout URL), then retry with the same `Idempotency-Key`. | | `max_cost_exceeded` | 402 | Maximum cost exceeded | The call would cost more than `X-Max-Cost-USD`; nothing was spent. | no | The call costs more than your `X-Max-Cost-USD`. Raise the cap or skip the call. Nothing was spent. | | `rate_limited` | 429 | Rate limited | Too many requests; wait `Retry-After` seconds. | yes | Wait `Retry-After` seconds, then retry with the same `Idempotency-Key`. Watch `X-RateLimit-Remaining`. | | `idempotency_conflict` | 409 | Idempotency conflict | The `Idempotency-Key` was reused with a different request, or the first request is still running. | maybe | The key was reused with a different body: send a new UUID. If the first request is still running, retry shortly. | | `vendor_unavailable` | 503 | Vendor unavailable | A vendor the call depends on is unavailable; nothing was billed. | yes | Phase 2 only. Wait `Retry-After` seconds and retry with the same `Idempotency-Key`. | | `decision_unavailable` | 503 | Decision unavailable | The decision model is temporarily unavailable or decisions are switched off; retry after `Retry-After`. Nothing was billed. | yes | The decision model is down or decisions are switched off. Wait `Retry-After` seconds and retry with the same `Idempotency-Key`. Nothing was billed. | | `internal` | 500 | Internal error | An unexpected server error; nothing was billed. Retry with the same `Idempotency-Key`. | yes | Retry with the same `Idempotency-Key` and backoff. If it persists, send the `request_id` to support. | ### Extra fields | Field | Present on | | ----------------------------------- | --------------------------------------------------------------------------------------- | | `errors` | `invalid_request`: one entry per bad field, with a JSON Pointer `path` and a `message`. | | `required_usd` | 402 errors: what the call would cost. | | `balance_usd` | `insufficient_balance`: your current balance. | | `retry_after_seconds` | 429 and 503: seconds to wait, equal to `Retry-After`. | ### Example ```json { "type": "max_cost_exceeded", "title": "Maximum cost exceeded", "status": 402, "detail": "This call costs $0.005, above X-Max-Cost-USD 0.001. Nothing was spent.", "request_id": "req_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "required_usd": 0.005 } ``` ### Retrying safely Retry only the types marked "yes" above, and always with the same `Idempotency-Key`. A retried POST that already succeeded returns the stored response and bills nothing. Honour `Retry-After`. Add jitter. Stop after a few attempts. ### Rate limits Each key allows 600 requests per minute by default, with a burst of 100. Your org can raise it. Every response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset`. Over the limit you get 429 `rate_limited` with `Retry-After`. ## Versioning Source: https://docs.askverdict.io/versioning.md ### API versions Versions are dates. The current version is `2026-11-01`. - Pin a version per request with the `X-Verdict-Version` header. - Without the header, a key uses the version that was current when the key was created. - Every decision records the version that answered it in `version`. Additive changes (a new optional field, a new endpoint, a new enum value in a response) ship without a new version. Write clients that ignore unknown fields and unknown enum values. Breaking changes (a removed or renamed field, a changed meaning, a new required input) get a new date, and the old version keeps working for twelve months after the new one ships. ### Question-set versions Each decision runs a versioned question set: the typed questions asked of the decision model, their options and the mapping to `answer` and `reason_code`. Versions are dates. Every response records the one that answered in `question_set_version`. A new question set ships only with an eval run, and the build fails if precision or recall drops by more than two points. Results per decision and version are on [Eval results](https://docs.askverdict.io/evals/). ### The OpenAPI document The [OpenAPI 3.1 document](https://docs.askverdict.io/openapi.json) is generated from the code's schemas on every change, and these docs are generated from it. A guide that references a field that no longer exists fails the docs build. ## Use Verdict as tools in an agent Source: https://docs.askverdict.io/agent-tools.md `@verdict/sdk/tools` turns Verdict into ready-made tools for an agent framework. You get eleven tools: the six decisions, four ICP calls and outcome reporting. Each has a description written for the model (what it does, when to call it, its price, what comes back) and a self-contained JSON Schema of its arguments. The tools are generated from the same OpenAPI document as the API reference, so they cannot drift from the API. Every call goes through the SDK client. The client sends your key, a fresh `Idempotency-Key` per call (reused across its own retries, so a retried decision is never billed twice) and your spend cap. It retries 429 and 5xx with backoff, and waits out the `Retry-After` of a 503 `decision_unavailable` or `vendor_unavailable`. The model never sees or sets any of this. ```sh npm i @verdict/sdk ``` ### The tools | Tool | Call | Price | | ----------------------- | -------------------------- | --------------------- | | `qualify_firm` | [`POST /v1/decisions/qualify_firm`](https://docs.askverdict.io/reference/qualify-firm/) | $0.005 | | `score_signal` | [`POST /v1/decisions/score_signal`](https://docs.askverdict.io/reference/score-signal/) | $0.005 | | `pick_person` | [`POST /v1/decisions/pick_person`](https://docs.askverdict.io/reference/pick-person/) | $0.005 | | `triage_reply` | [`POST /v1/decisions/triage_reply`](https://docs.askverdict.io/reference/triage-reply/) | $0.003 | | `right_person` | [`POST /v1/decisions/right_person`](https://docs.askverdict.io/reference/right-person/) | $0.003 | | `should_send` | [`POST /v1/decisions/should_send`](https://docs.askverdict.io/reference/should-send/) | $0.003 | | `create_icp` | [`POST /v1/icps`](https://docs.askverdict.io/reference/create-icp/) | free | | `get_icp` | [`GET /v1/icps/{id}`](https://docs.askverdict.io/reference/get-icp/) | free | | `update_icp` | [`PATCH /v1/icps/{id}`](https://docs.askverdict.io/reference/update-icp/) | free | | `update_icp_thresholds` | [`PATCH /v1/icps/{id}/thresholds`](https://docs.askverdict.io/reference/update-icp-thresholds/) | free | | `create_outcome` | [`POST /v1/outcomes`](https://docs.askverdict.io/reference/create-outcome/) | free | A decision is billed only when it returns an answer. Errors, including a 503 that outlives the retries, are free. `verdictTools(client, options)` binds the tools to a client: - `include` / `exclude`: pick tools by name, for example `{ include: ["qualify_firm", "create_outcome"] }`. An unknown name throws. - `icpId`: your default ICP profile. Every tool that takes `icp_id` then lists it as optional, and the tool fills it in whenever the model leaves it out. An `icp_id` the model does pass is used as given. Set it when your agent works for one ICP, so the model never has to find or invent a profile id. - `maxCostUsd`: a spend cap per priced call, sent as `X-Max-Cost-USD`. A call that would cost more is refused with 402 `max_cost_exceeded` and nothing is billed. The client's own `maxCostUsd` applies when this is not set. - `idempotencyKey`: how each call's `Idempotency-Key` is chosen. The default, `"per-call"`, is a fresh UUID for every tool call (the client reuses it across its own retries). Set `"from-call-id"` when your agent runs on a durable runtime that can replay a step after it finished, such as a workflow engine or a crash-and-resume loop. The key is then derived from the framework's tool call id (the AI SDK's `toolCallId`, Anthropic's `tool_use` id, OpenAI's `call_id`), so a replayed call gets the stored response instead of a second billed decision. You can also pass your own function of `{ callId, toolName }`. - `requestOptions`: other per-call client options (version pin, timeout, retries, headers). Arguments that go into the URL (the `id` of `get_icp`, `update_icp` and `update_icp_thresholds`) are checked before anything is sent. A missing, empty or malformed id fails as `invalid_arguments`, which the run helpers return to the model as an error result. ### Vercel AI SDK This example targets the AI SDK `ai` 7.x (`generateText`, `stopWhen` and `isStepCount`), the version `@verdict/sdk/tools` is type-checked against and the one its optional peer range names. `ai` 5 and 6 use the same tool shape (`description`, `inputSchema`, `execute`) and call the stop condition `stepCountIs`, which `ai` 7 still exports, but they are not tested. The AI SDK reads JSON Schemas only through its own `jsonSchema()` helper. Pass that helper to `toVercelAiTools`, so `@verdict/sdk` needs no dependency on `ai`: ```ts import { anthropic } from "@ai-sdk/anthropic"; import { Verdict } from "@verdict/sdk"; import { toVercelAiTools, verdictTools } from "@verdict/sdk/tools"; import { generateText, isStepCount, jsonSchema } from "ai"; const verdict = new Verdict({ maxCostUsd: 0.01 }); // reads VERDICT_API_KEY const tools = toVercelAiTools( verdictTools(verdict, { icpId: process.env.VERDICT_ICP_ID, idempotencyKey: "from-call-id" }), jsonSchema, ); const { text, steps } = await generateText({ model: anthropic("claude-opus-5-5"), tools, stopWhen: isStepCount(8), system: "You qualify inbound companies. Call qualify_firm for each one. " + "Act only when answer is fit. When a human approves the next step, call create_outcome with outcome approved.", prompt: "Should we work northfieldcapital.com and acmeholdings.com?", }); console.log(text); for (const step of steps) { for (const r of step.toolResults) console.log(r.toolName, r.output); } ``` `streamText` takes the same `tools` and `stopWhen`. When a call fails after the client's retries, `execute` throws the SDK's `VerdictError` and the AI SDK hands it to the model as a tool error, so the agent can move on or try later. ### Anthropic Messages API `toAnthropicTools` gives the `tools` list. `runAnthropicToolUse` runs one `tool_use` block and returns its `tool_result` block. A Verdict error (for example `insufficient_balance`, or a 503 that outlived the retries) comes back as `is_error: true` with a short JSON body (`error`, `status`, `detail`, `retry_after_seconds`) for the model to read. A missing API key still throws, because that is your bug to fix. ```ts import Anthropic from "@anthropic-ai/sdk"; import { Verdict } from "@verdict/sdk"; import { runAnthropicToolUse, toAnthropicTools, verdictTools } from "@verdict/sdk/tools"; const anthropic = new Anthropic(); const tools = verdictTools(new Verdict(), { icpId: process.env.VERDICT_ICP_ID }); const messages: Anthropic.MessageParam[] = [ { role: "user", content: "Is northfieldcapital.com a fit for us?" }, ]; for (;;) { const response = await anthropic.messages.create({ model: "claude-opus-5-5", max_tokens: 16000, tools: toAnthropicTools(tools), messages, }); messages.push({ role: "assistant", content: response.content }); if (response.stop_reason !== "tool_use") break; const calls = response.content.filter((b) => b.type === "tool_use"); const results = await Promise.all(calls.map((b) => runAnthropicToolUse(tools, b))); messages.push({ role: "user", content: results }); // every result in one message } ``` ### OpenAI `toOpenAiTools` returns Chat Completions function tools, or the flat Responses API shape with `{ api: "responses" }`. It sets `strict: true` only when a schema meets OpenAI's strict-mode rules: every property required, `additionalProperties: false` on every object, and only supported keywords. Today every Verdict tool has optional fields or string length limits, so `strict` is left off (Chat Completions) or `false` (Responses). `runOpenAiToolCall` runs one call and returns the message or input item that carries its result. Errors come back as for Anthropic. ```ts import OpenAI from "openai"; import { Verdict } from "@verdict/sdk"; import { runOpenAiToolCall, toOpenAiTools, verdictTools } from "@verdict/sdk/tools"; const openai = new OpenAI(); const tools = verdictTools(new Verdict(), { icpId: process.env.VERDICT_ICP_ID }); const input: OpenAI.Responses.ResponseInput = [ { role: "user", content: "Is northfieldcapital.com a fit for us?" }, ]; for (;;) { const response = await openai.responses.create({ model: process.env.OPENAI_MODEL ?? "gpt-5", // any tool-calling model tools: toOpenAiTools(tools, { api: "responses" }), input, }); input.push(...response.output); const calls = response.output.filter((item) => item.type === "function_call"); if (calls.length === 0) break; input.push(...(await Promise.all(calls.map((c) => runOpenAiToolCall(tools, c))))); } ``` With Chat Completions, pass `tools: toOpenAiTools(tools)` and answer each `message.tool_calls[i]` with `await runOpenAiToolCall(tools, call)`, which returns the `{ role: "tool", tool_call_id, content }` message. ### Any other framework `verdictTools(client)` returns plain objects: `{ name, description, inputSchema, execute(args) }`, plus `operationId` and `priceUsd`. `inputSchema` is a JSON Schema object with no `$ref` and an object at the top level. `execute` takes the parsed arguments (or their JSON string) and resolves to the API's JSON response. ### MCP If your agent speaks the Model Context Protocol (Claude Desktop, Claude Code, Cursor and others), you do not need this package: run the Verdict MCP server, `@verdict/mcp` (bin `verdict-mcp`). It exposes the decisions and the index as MCP tools with the same descriptions. ### Close the loop Decisions get better for you as you report what happened after them. Give your agent `create_outcome`, or report from your own code with `verdict.approve(id)`, `verdict.skip(id)` and `verdict.outcomes.report(...)`. See [Outcomes and the feedback loop](https://docs.askverdict.io/outcomes/). ## Pricing Source: https://docs.askverdict.io/pricing.md Verdict sells one thing: the decision. Everything else is free. ### Decisions | Decision | Price per call | Question | | --- | --- | --- | | [`qualify_firm`](https://docs.askverdict.io/reference/qualify-firm/) | $0.005 | Is this firm in my ICP? | | [`score_signal`](https://docs.askverdict.io/reference/score-signal/) | $0.005 | Does this event matter, and is now the time? | | [`pick_person`](https://docs.askverdict.io/reference/pick-person/) | $0.005 | Who at this firm should I write to? | | [`triage_reply`](https://docs.askverdict.io/reference/triage-reply/) | $0.003 | What does this reply mean? | | [`right_person`](https://docs.askverdict.io/reference/right-person/) | $0.003 | Is this the right person to contact? | | [`should_send`](https://docs.askverdict.io/reference/should-send/) | $0.003 | Should this message go out now? | Prices come from the `x-verdict-price-usd` field on each operation in the [OpenAPI document](https://docs.askverdict.io/openapi.json), the same value the billing ledger uses. ### Calls that bill per item | Call | Price | | --- | --- | | [`POST /v1/decisions/qualify_firm`](https://docs.askverdict.io/reference/qualify-firm/) | $0.005 per call, billed only when an answer is returned | | [`POST /v1/decisions/score_signal`](https://docs.askverdict.io/reference/score-signal/) | $0.005 per call, billed only when an answer is returned | | [`POST /v1/decisions/pick_person`](https://docs.askverdict.io/reference/pick-person/) | $0.005 per call, billed only when an answer is returned | | [`POST /v1/decisions/triage_reply`](https://docs.askverdict.io/reference/triage-reply/) | $0.003 per call, billed only when an answer is returned | | [`POST /v1/decisions/right_person`](https://docs.askverdict.io/reference/right-person/) | $0.003 per call, billed only when an answer is returned | | [`POST /v1/decisions/should_send`](https://docs.askverdict.io/reference/should-send/) | $0.003 per call, billed only when an answer is returned | | [`GET /v1/events`](https://docs.askverdict.io/reference/list-events/) | free unscored; with `icp_id` each scored event bills one `score_signal` ($0.005) | | [`POST /v1/watches`](https://docs.askverdict.io/reference/create-watch/) | free to create; each delivered event bills one `score_signal` ($0.005) | | [`POST (scoredEvent)`](https://docs.askverdict.io/reference/scored-event-webhook/) | bills one `score_signal` ($0.005) when delivered, never per retry | ### Free Index reads (`GET /v1/firms/resolve`, `GET /v1/firms/{id}`, unscored `GET /v1/events`, `GET /v1/sources`), outcomes, ICP profiles, watch management, keys, balance, usage and every other account call. ### Never billed - A call that returns no answer. - A call that fails, for any reason, including 503 `decision_unavailable`. - A call refused before spend: 402 `insufficient_balance` or `max_cost_exceeded`. - A repeated request with the same `Idempotency-Key` within 24 hours. - A watch delivery retry. ### Credit You get $5 of starter credit at sign-up, no card. Test keys (`vd_test_`) draw on a separate, non-cash test balance. A card is needed once you pass $5 of usage or 1,000 calls, whichever comes first. Top up with `POST /v1/topup`. It returns a Stripe Checkout URL. Needs `account:manage`. | Pack | Buy with | | --- | --- | | $20 | `POST /v1/topup` with `{"pack_usd": 20}` | | $50 | `POST /v1/topup` with `{"pack_usd": 50}` | | $200 | `POST /v1/topup` with `{"pack_usd": 200}` | Auto top-up is optional: set a threshold and a pack. Balance does not expire while the account is active. Accounts with no calls for 12 months are notified and then closed under the retention policy. ### Controlling spend - `X-Max-Cost-USD` on any call: refused with 402 before spend if the call would cost more. - A daily spend cap per key. - `GET /v1/usage` and `GET /v1/ledger` (scope `account:read`) for spend per project, per decision and per day. ### Vendor data Phase 2 adds vendor pass-through (for example email lookup through treg or your own vendor keys). Vendor calls are billed at the vendor's price, itemized as `cost.vendor_usd` with the vendor named, with no markup. Calls on your own vendor keys bill the decision price only. ## Security overview Source: https://docs.askverdict.io/security.md Verdict targets OWASP ASVS 4.0 level 2 for the API and console, with level 3 controls for keys, secrets and logging. This page is the customer view. Report a vulnerability by email to `security@askverdict.io` with the subject `SECURITY: `. Do not include live credentials. Acknowledgement within 2 business days. ### API keys - 32 bytes from a cryptographic random generator, base62, prefixed `vd_live_` or `vd_test_`. - Stored only as a SHA-256 hash with a server-side pepper. Shown once at creation. Never retrievable. - Never logged: a redaction layer masks anything shaped like a key in logs, errors and traces. - Scoped (see [Concepts](https://docs.askverdict.io/concepts/)), with optional rate limit, IP allowlist, daily spend cap and expiry per key. - Rotation issues a new key and keeps the old one valid for a grace window you choose (default 24 hours). ### Sign-up and console - Email verification before any key is issued. Disposable email domains are refused. Sign-ups are rate limited per IP and per domain. - Console sign-in by magic link (single use, 10-minute expiry) or GitHub. No passwords. - Optional TOTP, required for org owners once an org has more than one member. - Roles: owner, admin, developer, billing, read-only. Checked on the server for every action. ### Transport and edge - TLS 1.2 minimum, 1.3 preferred. HSTS preload. No plaintext listeners. - Cloudflare in front of every public hostname: managed WAF rules, bot management, an edge rate limit above the per-key limits. - The origin accepts traffic only through Cloudflare. ### Input handling - Every request body is validated against the published schema. Unknown fields are rejected. - Size caps: 256 KB bodies, 50 roster entries, 40 thread messages, 8 KB per message. - No SQL built from strings. No shell execution from request paths. - Verdict fetches no URL you send, except webhook deliveries to endpoints you registered and verified. ### Tenant isolation Every table that holds org data has Postgres row-level security, forced for the API's database role. Each transaction sets the org from the authenticated key. An automated test asserts every org-scoped table has a policy and that a cross-org read returns zero rows. ### Data you send - Rosters, threads and ICP text are processed for the request and not stored, except in a 30-day request log where emails, phone numbers and names are redacted first. - Decisions store feature hashes, probabilities and token counts, never your text. - Decisions and outcomes are kept for the life of the org plus 90 days. Org deletion completes within 30 days and is confirmed by email. - Verdict is a processor for the data you send and a controller for its index of public business data. See the [DPA](https://docs.askverdict.io/legal/dpa/) and [privacy policy](https://docs.askverdict.io/legal/privacy/). ### Model-input safety Every text field that reaches the decision model (thread messages, event descriptions, your ICP text) passes an injection guard first. It neutralizes instruction-shaped content: role assertions, "ignore previous", fake system text and non-evidence URLs. This keeps a hostile reply from steering your decision. ### Webhooks - Watch deliveries are signed: `Verdict-Signature: t=,v1=`. Reject skew over 5 minutes. Secrets rotate with overlap. See [Watches](https://docs.askverdict.io/watches/). - Registered URLs must be HTTPS and answer a challenge. Private and link-local IP ranges are refused. - Inbound receivers verify each vendor's signature before parsing and are idempotent on the vendor event id. ### Secrets All service secrets live in a secrets manager and are injected at runtime. Webhook secrets and phase-2 vendor keys are encrypted at rest with AES-256-GCM under per-row data keys wrapped by a master key, decrypted only in memory, never returned by any API, and every access is audit-logged. ### Logging and audit - Structured logs with request id, org, key id, route, status, latency and cost. Never bodies, keys or personal data. Kept 30 days. - An append-only audit log of key, scope, member, threshold, webhook, top-up, export and deletion events, readable with `GET /v1/audit` (scope `account:read`). - Alerts on error rate, latency, decision-model errors, ingestion failures, ledger drift and abnormal spend per key. ### Compliance No SOC 2 report yet; the controls above are mapped to the Trust Services Criteria for readiness. A penetration test is commissioned before public launch and yearly after. Confirmed data incidents are notified to affected customers within 72 hours. Legal documents are on [Legal](https://docs.askverdict.io/legal/terms/); they are drafts until reviewed by counsel. ## Data sources and licences Source: https://docs.askverdict.io/sources.md Verdict's index holds only open-licensed or public-domain data. Every source records a licence that permits caching and derived use. No LinkedIn data enters the index from any source. Vendor data (phase 2) is never stored; it passes through at the vendor's price under the vendor's terms. ### Sources The table below is the reference data behind [`GET /v1/sources`](https://docs.askverdict.io/reference/list-sources/). Live freshness (the last successful run) comes from the API. | Source | Name | Licence | Refresh cadence | Rate limit | Attribution | | --- | --- | --- | --- | --- | --- | | `sec_edgar` | SEC EDGAR | [public_domain](https://www.sec.gov/os/accessing-edgar-data) | Daily: submissions API and full-index (8-K, Form D, Form 4, S-1, 13F); Form ADV via the IAPD bulk file monthly and the daily feed. | Under 10 requests per second with a declared User-Agent (SEC fair access). | Source: U.S. Securities and Exchange Commission (EDGAR and IAPD). | | `gleif` | GLEIF | [cc0](https://creativecommons.org/publicdomain/zero/1.0/) | Golden copy plus daily deltas (LEI records and direct and ultimate parents). | none published | Source: Global Legal Entity Identifier Foundation (GLEIF), LEI data under CC0. | | `companies_house` | UK Companies House | [ogl_v3](https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/) | Streaming API for filings, officers and PSC changes; monthly bulk download for the base. | none published | Contains public sector information licensed under the Open Government Licence v3.0. | | `wikidata` | Wikidata | [cc0](https://creativecommons.org/publicdomain/zero/1.0/) | Weekly dump extract; SPARQL for deltas. | none published | Source: Wikidata, under CC0. | Phase 2 adds USPTO, GDELT, CourtListener, EU national registers, the Australian ABN bulk file, GitHub org-level activity and Indeed Hiring Lab aggregates, each with its licence recorded here before it ships. ### Check freshness from your agent [`GET /v1/sources`](https://docs.askverdict.io/reference/list-sources/). Free. Scope `index:read`. Use it to show attribution or to check how fresh a source is (`freshness_seconds`). ```bash title="Request" curl -X GET 'https://api.askverdict.io/v1/sources' \ -H "Authorization: Bearer $VERDICT_API_KEY" ``` ```json title="Response 200" { "data": [ { "id": "src_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "source": "gleif", "name": "GLEIF LEI golden copy", "phase": 1, "license": { "kind": "cc0", "name": "CC0 1.0 Universal", "url": "https://creativecommons.org/publicdomain/zero/1.0/", "attribution": null }, "refresh_cadence": "daily", "rate_limit": null, "last_successful_run": { "id": "run_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "status": "succeeded", "started_at": "2026-11-07T03:00:00Z", "finished_at": "2026-11-07T03:21:40Z", "events_upserted": 1874 }, "last_run": { "id": "run_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "status": "succeeded", "started_at": "2026-11-07T03:00:00Z", "finished_at": "2026-11-07T03:21:40Z", "events_upserted": 1874 }, "freshness_seconds": 7200 } ], "next_cursor": null, "has_more": false } ``` #### Response fields | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The source id. | | `source` | enum: "sec_edgar", "gleif", "companies_house", "wikidata" | yes | Stable source slug, as cited in `evidence[].source`. | | `name` | string | yes | Human name. | | `phase` | integer | yes | Release phase that ships the source. | | `license` | object | yes | The source's license. | | `license.kind` | enum: "public_domain", "cc0", "ogl_v3", "cc_by_4_0", "cc_by_3_0_au" | yes | License kind. | | `license.name` | string | yes | License name. | | `license.url` | string (uri) | yes | License text. | | `license.attribution` | string or null | yes | Attribution text to show when you display the data, if required. | | `refresh_cadence` | string | yes | How often Verdict refreshes it, for example `daily`. | | `rate_limit` | string or null | yes | The source's fair-access limit Verdict observes. | | `last_successful_run` | object or null | yes | Latest successful ingestion, or `null` if none yet. | | `last_successful_run.id` | string | yes | The run id. | | `last_successful_run.status` | enum: "running", "succeeded", "partial", "failed" | yes | Run status. | | `last_successful_run.started_at` | string (date-time) | yes | When the run started. | | `last_successful_run.finished_at` | string or null (date-time) | yes | When the run finished. | | `last_successful_run.events_upserted` | integer | yes | Events inserted or updated. | | `last_run` | object or null | yes | Latest ingestion run of any status (it may still be running or have failed), or `null` if none yet. | | `last_run.id` | string | yes | The run id. | | `last_run.status` | enum: "running", "succeeded", "partial", "failed" | yes | Run status. | | `last_run.started_at` | string (date-time) | yes | When the run started. | | `last_run.finished_at` | string or null (date-time) | yes | When the run finished. | | `last_run.events_upserted` | integer | yes | Events inserted or updated. | | `freshness_seconds` | integer or null | yes | Seconds since the latest successful run finished: how fresh this source's data is. `null` if it has never succeeded. | ### Source details Generated from each adapter's `SOURCE.md` in the index package. Sources without one are not ingested yet. #### GLEIF (`gleif`) | | | | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Publisher | Global Legal Entity Identifier Foundation: LEI golden copy and delta files | | Licence | CC0 1.0 Universal; and | | Attribution | "Source: Global Legal Entity Identifier Foundation (GLEIF), LEI data under CC0." (not required; shown at `GET /v1/sources`) | | Rate limit | None published for the golden copy files. Verdict fetches four files a day at most (two publish manifests, two zips) and holds the client to 4 requests per second, `User-Agent: Verdict/0.1 (+https://askverdict.io)`. | | Cadence | `gleif_golden` 01:00 UTC on Sundays (full golden copy); `gleif_delta` 03:00 UTC daily (`LastDay` delta); `canary_gleif` 05:35 UTC (live only with `CANARY_ENABLED=true`). | | Format | LEI-CDF 3.1 XML (`lei2`) and RR-CDF XML (`rr`), one XML file per zip, from `https://goldencopy.gleif.org/api/v2/golden-copies/publishes//latest`. XML, not CSV or JSON: the records nest and XML is the canonical CDF format (ADR-0020). | | Firm keys | LEI; the Companies House number when the registration authority is `RA000585`. | #### What is ingested - LEI records: firm facts (legal name, other names, headquarters country, region and city, entity status, fund category) and `lei_change` events: `issued`, `renewed` (keyed by the next renewal date, once renewed), `lapsed`, `retired`. Duplicate and annulled registrations are skipped. - Relationship records: direct parents (`IS_DIRECTLY_CONSOLIDATED_BY`) and ultimate parents (`IS_ULTIMATELY_CONSOLIDATED_BY`) only, as an `ownership_change` (`control_changed`) and a `lei_change` (`parent_changed`) on the child, both with `parent_lei`. Fund management, sub-fund, feeder and branch relationships are skipped. The zips are streamed into the blob store and parsed as streams, record by record; the 3.5-million-record golden copy never sits in memory. #### Fixtures Real records from the GLEIF delta files published 2026-11-08, downloaded that day; see `packages/index/test/fixtures/gleif/FIXTURES.md`. #### SEC EDGAR (`sec_edgar`) | | | | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Publisher | U.S. Securities and Exchange Commission: EDGAR (filings) and IAPD (Form ADV) | | Licence | Public domain: works of the U.S. government (17 U.S.C. 105); | | Attribution | "Source: U.S. Securities and Exchange Commission (EDGAR and IAPD)." (not required; shown at `GET /v1/sources`) | | Rate limit | SEC fair access: under 10 requests per second, declared User-Agent. Verdict sends `User-Agent: Verdict/0.1 ()` and `Accept-Encoding: gzip, deflate`, and a token bucket holds every SEC client to 8 requests per second, with retries on 429 and 5xx that honour `Retry-After`. | | Cadence | `sec_daily` 06:00 UTC (previous day's daily index); `iapd_daily` 07:00 UTC; `iapd_monthly` 05:00 UTC on the 1st; `sec_full_index` and `sec_submissions` on demand; `canary_sec_edgar` 05:30 UTC (live only with `CANARY_ENABLED=true`). | | Firm keys | CIK (every EDGAR filing), CRD (Form ADV, 13F cover page), LEI and web domain (Form ADV Item 1, submissions API when listed). | #### What is ingested | Job | Endpoint | Produces | | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `sec_daily` | `https://www.sec.gov/Archives/edgar/daily-index//QTR/form..idx`, then one document per filing | `filing` events (`sec_8k`, `sec_form_d`, `sec_form_4`, `sec_s1`, `sec_13f`), `officer_change` (8-K 5.02), `ownership_change` (8-K 5.01), `fund_raise` (Form D), `insider_trade` (Form 4) | | `sec_full_index` | `https://www.sec.gov/Archives/edgar/full-index//QTR/form.idx` (backfill) | as `sec_daily` | | `sec_submissions` | `https://data.sec.gov/submissions/CIK##########.json` | firm facts (name, former names, HQ, type, website, LEI) and the events of the company's recent filings, 8-K items included | | `iapd_daily`, `iapd_monthly` | `https://reports.adviserinfo.sec.gov/reports/CompilationReports/IA_FIRM_SEC_Feed_.xml.gz` (from the manifest) | adviser facts (type, size band from employees, HQ, domain, LEI), `filing` (`sec_adv`), `registration` | Documents per form: 8-K and S-1 from `.hdr.sgml`; Form D and 13F from `primary_doc.xml`; Form 4 from `.txt` (ADR-0020). Forms and amendments: `8-K`, `8-K/A`, `D`, `D/A`, `4`, `4/A`, `S-1`, `S-1/A`, `13F-HR`, `13F-HR/A`. Personal data is minimised (spec 7.3): a Form 4 reporting person's name and address are not read, only the relationship to the issuer; Form D related persons are not read; IAPD social-profile URLs are dropped (LinkedIn never enters the index). #### Fixtures Real files downloaded on 2026-11-08 (UTC) with the declared User-Agent, one request at a time, trimmed to the smallest slice that exercises each parser path; see `packages/index/test/fixtures/sec_edgar/FIXTURES.md` for each file's origin URL and what was removed. ### Attribution Show the `attribution` string when you display data from a source that requires it. Evidence in every decision names its source slug in `evidence[].source` and links the original record. ## Changelog Source: https://docs.askverdict.io/changelog.md Newest first. Additive changes ship without a new API version; breaking changes get a new date with twelve months of overlap (see [Versioning](https://docs.askverdict.io/versioning/)). ### 2026-11-01 First API version. - Six decisions: `qualify_firm`, `score_signal`, `pick_person`, `triage_reply`, `right_person`, `should_send`. - Index: firm resolution, firms, events (scored with `icp_id`) and sources. - Watches with signed deliveries. - Outcomes, and receivers for Instantly, Smartlead, Cal.com and Calendly. - Account: sign-up without a browser, keys with scopes, balance, top-up, usage, ledger, limits and the audit log. - `decision_unavailable` is HTTP 503 with `Retry-After`, retryable with the same `Idempotency-Key`. - Docs, `llms.txt` and `llms-full.txt` published. ## Status Source: https://docs.askverdict.io/status.md Live status is at [status.askverdict.io](https://status.askverdict.io). Incidents are posted there first. Components: - **API**: every endpoint. - **Decisions**: degraded when the decision model is unavailable. During an outage decisions return 503 `decision_unavailable` with `Retry-After` and bill nothing. Index reads, outcomes and account calls keep working. - **MCP**: the hosted MCP server. - **Index freshness**: one component per source. Your agent can check the same thing with `GET /v1/sources`. - **Console**. From an agent, the cheapest health check is `GET /v1/me` with your key. It is free. ## Eval results Source: https://docs.askverdict.io/evals.md Precision, recall and F1 for each decision and question-set version, read from the result files committed in `packages/engine/evals/results/`. Each row says which labeled set it was measured on and where that set comes from. :::caution[Synthetic sets only] Every labeled set published here is synthetic. These numbers show the pipeline is consistent and that changes do not regress it. They do not show real-world accuracy. The design partner's labeled sets are not included yet. ::: ### Results | Decision | Question set | Status | Labeled set | Provenance | Rows | Precision | Recall | F1 | Change vs baseline (precision / recall, points) | Gate | | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | [`qualify_firm`](https://docs.askverdict.io/evals/qualify_firm/2026-11-01/) | 2026-11-01 | current | `synthetic-qualify_firm` | Synthetic | 240 | 1.000 | 0.900 | 0.947 | 0.0 / 0.0 | passed | | [`score_signal`](https://docs.askverdict.io/evals/score_signal/2026-11-01/) | 2026-11-01 | current | `synthetic-score_signal` | Synthetic | 240 | 0.762 | 0.762 | 0.762 | 0.0 / 0.0 | passed | | [`pick_person`](https://docs.askverdict.io/evals/pick_person/2026-11-01/) | 2026-11-01 | current | `synthetic-pick_person` | Synthetic | 240 | 0.903 | 0.949 | 0.926 | 0.0 / 0.0 | passed | | [`triage_reply`](https://docs.askverdict.io/evals/triage_reply/2026-11-01/) | 2026-11-01 | current | `synthetic-triage_reply` | Synthetic | 240 | 0.915 | 0.995 | 0.954 | 0.0 / 0.0 | passed | | [`right_person`](https://docs.askverdict.io/evals/right_person/2026-11-01/) | 2026-11-01 | current | `synthetic-right_person` | Synthetic | 240 | 0.768 | 0.843 | 0.804 | 0.0 / 0.0 | passed | | [`should_send`](https://docs.askverdict.io/evals/should_send/2026-11-01/) | 2026-11-01 | current | `synthetic-should_send` | Synthetic | 240 | 1.000 | 0.905 | 0.950 | 0.0 / 0.0 | passed | ### How it is measured Precision and recall are measured at the default threshold: calibrated `p` from a global calibration map, cross-fitted over two folds so no row is scored by a map fitted on itself, with the threshold chosen for F1 on the other fold. F1 is computed from those precision and recall values. The raw columns apply the uncalibrated model probability at 0.5, so the gap shows what calibration changes. During the eval the model answers are the answers recorded in each labeled row, served by the fixture adapter (model version `jev-fixture`). No call is made to Jev, the production decision model. ### The gate CI runs the eval on every pull request. The build fails if precision or recall at the default threshold drops more than two points (0.02) below the committed baseline (`packages/engine/evals/baseline.json`). F1 and the raw columns are reported, not gated. A change to a question set ships only with an eval run. ### What does not run yet - The design partner's labeled sets are not included yet. When they are added, their results are published here per decision and question-set version, separately from the synthetic ones. - No public labeled set is included yet. - The live model is not evaluated. Because the answers are recorded, not produced by Jev, these numbers do not yet show how Jev itself scores on these rows, and they do not yet prove the raw Jev versus Verdict comparison on real data. - Every v1 decision has at least one labeled set, so none is listed as not yet published. ## qualify_firm 2026-11-01 Source: https://docs.askverdict.io/evals/qualify_firm/2026-11-01.md Eval results for [`qualify_firm`](https://docs.askverdict.io/decisions/qualify_firm/) with question set `2026-11-01`. Every response records the question set that answered it in `question_set_version`, so you can match a decision you received to these numbers. ### Labeled set - `synthetic-qualify_firm` (synthetic): Generated by `packages/engine/evals/scripts/generate-synthetic.ts` with seed 20261101: 240 rows of invented firms against one invented ICP, each with a label and model answers drawn around that label with noise. No row comes from a real firm, person or reply, so the numbers measure whether the pipeline (input builder, injection guard, calibration, threshold) stays consistent from one change to the next. They are not a measure of real-world accuracy. The design partner's labeled sets are not yet included for this decision. ### Results | Metric | This run | Baseline | Change (points) | | --- | --- | --- | --- | | Precision (calibrated, default threshold) | 1.000 | 1.000 | 0.0 | | Recall (calibrated, default threshold) | 0.900 | 0.900 | 0.0 | | F1 | 0.947 | 0.947 | 0.0 | | Default threshold | 0.210 | | | | Raw precision (model `p` at 0.5) | 0.556 | | | | Raw recall (model `p` at 0.5) | 1.000 | | | | Raw F1 | 0.714 | | | | Labeled rows | 240 | | | | Rows the injection guard fired on | 0 | | | Gate: **passed**. CI runs the eval on every pull request. The build fails if precision or recall at the default threshold drops more than two points (0.02) below the committed baseline (`packages/engine/evals/baseline.json`). F1 and the raw columns are reported, not gated. A change to a question set ships only with an eval run. ### How this was run Precision and recall are measured at the default threshold: calibrated `p` from a global calibration map, cross-fitted over two folds so no row is scored by a map fitted on itself, with the threshold chosen for F1 on the other fold. F1 is computed from those precision and recall values. The raw columns apply the uncalibrated model probability at 0.5, so the gap shows what calibration changes. During the eval the model answers are the answers recorded in each labeled row, served by the fixture adapter (model version `jev-fixture`). No call is made to Jev, the production decision model. | Field | Value | | --- | --- | | Adapter | `fixture` | | Model version | `jev-fixture` | | Run date | 2026-11-08 | | Commit | `4ddf9174f1c8` | | Clean tree | yes | ## score_signal 2026-11-01 Source: https://docs.askverdict.io/evals/score_signal/2026-11-01.md Eval results for [`score_signal`](https://docs.askverdict.io/decisions/score_signal/) with question set `2026-11-01`. Every response records the question set that answered it in `question_set_version`, so you can match a decision you received to these numbers. ### Labeled set - `synthetic-score_signal` (synthetic): Generated by `packages/engine/evals/scripts/generate-synthetic.ts` with seed 20261101: 240 rows of invented firm events against one invented ICP, each with a label and model answers drawn around that label with noise. No row comes from a real firm, person or reply, so the numbers measure whether the pipeline (input builder, injection guard, calibration, threshold) stays consistent from one change to the next. They are not a measure of real-world accuracy. The design partner's labeled sets are not yet included for this decision. ### Results | Metric | This run | Baseline | Change (points) | | --- | --- | --- | --- | | Precision (calibrated, default threshold) | 0.762 | 0.762 | 0.0 | | Recall (calibrated, default threshold) | 0.762 | 0.762 | 0.0 | | F1 | 0.762 | 0.762 | 0.0 | | Default threshold | 0.225 | | | | Raw precision (model `p` at 0.5) | 0.750 | | | | Raw recall (model `p` at 0.5) | 0.857 | | | | Raw F1 | 0.800 | | | | Labeled rows | 240 | | | | Rows the injection guard fired on | 0 | | | Gate: **passed**. CI runs the eval on every pull request. The build fails if precision or recall at the default threshold drops more than two points (0.02) below the committed baseline (`packages/engine/evals/baseline.json`). F1 and the raw columns are reported, not gated. A change to a question set ships only with an eval run. ### How this was run Precision and recall are measured at the default threshold: calibrated `p` from a global calibration map, cross-fitted over two folds so no row is scored by a map fitted on itself, with the threshold chosen for F1 on the other fold. F1 is computed from those precision and recall values. The raw columns apply the uncalibrated model probability at 0.5, so the gap shows what calibration changes. During the eval the model answers are the answers recorded in each labeled row, served by the fixture adapter (model version `jev-fixture`). No call is made to Jev, the production decision model. | Field | Value | | --- | --- | | Adapter | `fixture` | | Model version | `jev-fixture` | | Run date | 2026-11-08 | | Commit | `4ddf9174f1c8` | | Clean tree | yes | ## pick_person 2026-11-01 Source: https://docs.askverdict.io/evals/pick_person/2026-11-01.md Eval results for [`pick_person`](https://docs.askverdict.io/decisions/pick_person/) with question set `2026-11-01`. Every response records the question set that answered it in `question_set_version`, so you can match a decision you received to these numbers. ### Labeled set - `synthetic-pick_person` (synthetic): Generated by `packages/engine/evals/scripts/generate-synthetic.ts` with seed 20261101: 240 rows of invented rosters with generic titles, each with a label and model answers drawn around that label with noise. No row comes from a real firm, person or reply, so the numbers measure whether the pipeline (input builder, injection guard, calibration, threshold) stays consistent from one change to the next. They are not a measure of real-world accuracy. The design partner's labeled sets are not yet included for this decision. ### Results | Metric | This run | Baseline | Change (points) | | --- | --- | --- | --- | | Precision (calibrated, default threshold) | 0.903 | 0.903 | 0.0 | | Recall (calibrated, default threshold) | 0.949 | 0.949 | 0.0 | | F1 | 0.926 | 0.926 | 0.0 | | Default threshold | 0.421 | | | | Raw precision (model `p` at 0.5) | 0.922 | | | | Raw recall (model `p` at 0.5) | 0.954 | | | | Raw F1 | 0.938 | | | | Labeled rows | 240 | | | | Rows the injection guard fired on | 0 | | | Gate: **passed**. CI runs the eval on every pull request. The build fails if precision or recall at the default threshold drops more than two points (0.02) below the committed baseline (`packages/engine/evals/baseline.json`). F1 and the raw columns are reported, not gated. A change to a question set ships only with an eval run. ### How this was run Precision and recall are measured at the default threshold: calibrated `p` from a global calibration map, cross-fitted over two folds so no row is scored by a map fitted on itself, with the threshold chosen for F1 on the other fold. F1 is computed from those precision and recall values. The raw columns apply the uncalibrated model probability at 0.5, so the gap shows what calibration changes. During the eval the model answers are the answers recorded in each labeled row, served by the fixture adapter (model version `jev-fixture`). No call is made to Jev, the production decision model. | Field | Value | | --- | --- | | Adapter | `fixture` | | Model version | `jev-fixture` | | Run date | 2026-11-08 | | Commit | `4ddf9174f1c8` | | Clean tree | yes | ## triage_reply 2026-11-01 Source: https://docs.askverdict.io/evals/triage_reply/2026-11-01.md Eval results for [`triage_reply`](https://docs.askverdict.io/decisions/triage_reply/) with question set `2026-11-01`. Every response records the question set that answered it in `question_set_version`, so you can match a decision you received to these numbers. ### Labeled set - `synthetic-triage_reply` (synthetic): Generated by `packages/engine/evals/scripts/generate-synthetic.ts` with seed 20261101: 240 rows of short template reply threads, each with a label and model answers drawn around that label with noise. No row comes from a real firm, person or reply, so the numbers measure whether the pipeline (input builder, injection guard, calibration, threshold) stays consistent from one change to the next. They are not a measure of real-world accuracy. The design partner's labeled sets are not yet included for this decision. ### Results | Metric | This run | Baseline | Change (points) | | --- | --- | --- | --- | | Precision (calibrated, default threshold) | 0.915 | 0.915 | 0.0 | | Recall (calibrated, default threshold) | 0.995 | 0.995 | 0.0 | | F1 | 0.954 | 0.954 | 0.0 | | Default threshold | 0.602 | | | | Raw precision (model `p` at 0.5) | 0.900 | | | | Raw recall (model `p` at 0.5) | 1.000 | | | | Raw F1 | 0.947 | | | | Labeled rows | 240 | | | | Rows the injection guard fired on | 0 | | | Gate: **passed**. CI runs the eval on every pull request. The build fails if precision or recall at the default threshold drops more than two points (0.02) below the committed baseline (`packages/engine/evals/baseline.json`). F1 and the raw columns are reported, not gated. A change to a question set ships only with an eval run. ### How this was run Precision and recall are measured at the default threshold: calibrated `p` from a global calibration map, cross-fitted over two folds so no row is scored by a map fitted on itself, with the threshold chosen for F1 on the other fold. F1 is computed from those precision and recall values. The raw columns apply the uncalibrated model probability at 0.5, so the gap shows what calibration changes. During the eval the model answers are the answers recorded in each labeled row, served by the fixture adapter (model version `jev-fixture`). No call is made to Jev, the production decision model. | Field | Value | | --- | --- | | Adapter | `fixture` | | Model version | `jev-fixture` | | Run date | 2026-11-08 | | Commit | `4ddf9174f1c8` | | Clean tree | yes | ## right_person 2026-11-01 Source: https://docs.askverdict.io/evals/right_person/2026-11-01.md Eval results for [`right_person`](https://docs.askverdict.io/decisions/right_person/) with question set `2026-11-01`. Every response records the question set that answered it in `question_set_version`, so you can match a decision you received to these numbers. ### Labeled set - `synthetic-right_person` (synthetic): Generated by `packages/engine/evals/scripts/generate-synthetic.ts` with seed 20261101: 240 rows of invented candidate contacts with generic titles, each with a label and model answers drawn around that label with noise. No row comes from a real firm, person or reply, so the numbers measure whether the pipeline (input builder, injection guard, calibration, threshold) stays consistent from one change to the next. They are not a measure of real-world accuracy. The design partner's labeled sets are not yet included for this decision. ### Results | Metric | This run | Baseline | Change (points) | | --- | --- | --- | --- | | Precision (calibrated, default threshold) | 0.768 | 0.768 | 0.0 | | Recall (calibrated, default threshold) | 0.843 | 0.843 | 0.0 | | F1 | 0.804 | 0.804 | 0.0 | | Default threshold | 0.363 | | | | Raw precision (model `p` at 0.5) | 0.821 | | | | Raw recall (model `p` at 0.5) | 0.902 | | | | Raw F1 | 0.860 | | | | Labeled rows | 240 | | | | Rows the injection guard fired on | 0 | | | Gate: **passed**. CI runs the eval on every pull request. The build fails if precision or recall at the default threshold drops more than two points (0.02) below the committed baseline (`packages/engine/evals/baseline.json`). F1 and the raw columns are reported, not gated. A change to a question set ships only with an eval run. ### How this was run Precision and recall are measured at the default threshold: calibrated `p` from a global calibration map, cross-fitted over two folds so no row is scored by a map fitted on itself, with the threshold chosen for F1 on the other fold. F1 is computed from those precision and recall values. The raw columns apply the uncalibrated model probability at 0.5, so the gap shows what calibration changes. During the eval the model answers are the answers recorded in each labeled row, served by the fixture adapter (model version `jev-fixture`). No call is made to Jev, the production decision model. | Field | Value | | --- | --- | | Adapter | `fixture` | | Model version | `jev-fixture` | | Run date | 2026-11-08 | | Commit | `4ddf9174f1c8` | | Clean tree | yes | ## should_send 2026-11-01 Source: https://docs.askverdict.io/evals/should_send/2026-11-01.md Eval results for [`should_send`](https://docs.askverdict.io/decisions/should_send/) with question set `2026-11-01`. Every response records the question set that answered it in `question_set_version`, so you can match a decision you received to these numbers. ### Labeled set - `synthetic-should_send` (synthetic): Generated by `packages/engine/evals/scripts/generate-synthetic.ts` with seed 20261101: 240 rows of template messages with invented inbox health, each with a label and model answers drawn around that label with noise. No row comes from a real firm, person or reply, so the numbers measure whether the pipeline (input builder, injection guard, calibration, threshold) stays consistent from one change to the next. They are not a measure of real-world accuracy. The design partner's labeled sets are not yet included for this decision. ### Results | Metric | This run | Baseline | Change (points) | | --- | --- | --- | --- | | Precision (calibrated, default threshold) | 1.000 | 1.000 | 0.0 | | Recall (calibrated, default threshold) | 0.905 | 0.905 | 0.0 | | F1 | 0.950 | 0.950 | 0.0 | | Default threshold | 0.330 | | | | Raw precision (model `p` at 0.5) | 0.784 | | | | Raw recall (model `p` at 0.5) | 0.952 | | | | Raw F1 | 0.860 | | | | Labeled rows | 240 | | | | Rows the injection guard fired on | 0 | | | Gate: **passed**. CI runs the eval on every pull request. The build fails if precision or recall at the default threshold drops more than two points (0.02) below the committed baseline (`packages/engine/evals/baseline.json`). F1 and the raw columns are reported, not gated. A change to a question set ships only with an eval run. ### How this was run Precision and recall are measured at the default threshold: calibrated `p` from a global calibration map, cross-fitted over two folds so no row is scored by a map fitted on itself, with the threshold chosen for F1 on the other fold. F1 is computed from those precision and recall values. The raw columns apply the uncalibrated model probability at 0.5, so the gap shows what calibration changes. During the eval the model answers are the answers recorded in each labeled row, served by the fixture adapter (model version `jev-fixture`). No call is made to Jev, the production decision model. | Field | Value | | --- | --- | | Adapter | `fixture` | | Model version | `jev-fixture` | | Run date | 2026-11-08 | | Commit | `4ddf9174f1c8` | | Clean tree | yes | ## Terms of service Source: https://docs.askverdict.io/legal/terms.md :::caution[DRAFT placeholder] This is a DRAFT placeholder created in WP11 because no draft existed in `docs/legal/`. It is not legal text and has not been reviewed by counsel. It lists the structure spec 9.10 requires and the product facts each section must reflect. Counsel writes the text before public launch. ::: ### Structure required Each heading below is a section counsel must draft. The bullet points are product facts from the build spec that the section must be consistent with. They are not terms. #### 1. Parties and acceptance - Counsel to draft. #### 2. The service - Verdict is a headless decision API for AI agents, reached at `https://api.askverdict.io`, plus an MCP server, SDKs, a CLI and a minimal console. - Verdict never sends email, SMS or LinkedIn messages on a customer's behalf. #### 3. Accounts, organizations and API keys - Sign-up needs a verified email. Keys are shown once and can be revoked and rotated. - The customer is responsible for the keys it issues. #### 4. Credits, prices and payment - Prices are per decision, listed on the pricing page. Index reads, outcomes and account calls are free. - $5 starter credit at sign-up, no card. Packs of $20, $50 and $200 via Stripe Checkout. Optional auto top-up. - Calls that return no answer, fail or are refused are not billed. - Balance does not expire while the account is active; accounts dormant for 12 months are notified and closed per the retention policy. #### 5. Acceptable use - Incorporates the acceptable use policy. #### 6. Customer data and privacy - Incorporates the privacy policy and the data processing addendum. #### 7. Vendor pass-through (phase 2) - Vendor calls are governed by the vendor's terms, which the response links. #### 8. Availability and support - Counsel to draft. Status is published at `https://status.askverdict.io`. #### 9. Suspension and termination - Counsel to draft. Org deletion completes within 30 days and is confirmed by email. #### 10. Warranties, liability and indemnities - Counsel to draft. #### 11. Changes to the service and the API - API versions are dated; breaking changes get a new version with twelve months of overlap. #### 12. Governing law and disputes - Counsel to draft. ## Privacy policy Source: https://docs.askverdict.io/legal/privacy.md :::caution[DRAFT placeholder] This is a DRAFT placeholder created in WP11 because no draft existed in `docs/legal/`. It is not legal text and has not been reviewed by counsel. It lists the structure spec 9.10 requires and the product facts each section must reflect. Counsel writes the text before public launch. ::: ### Structure required #### 1. Who we are and how to contact us - Counsel to draft. Security reports go to `security@askverdict.io`. #### 2. Our roles - Verdict is a **processor** for customer-supplied data: rosters, threads and ICP text. - Verdict is a **controller** for its own index of public business data and for account data. #### 3. Data we collect - Account data: email, organization, members, billing records (cards are handled by Stripe, never by Verdict). - Customer-supplied data sent in API requests. - Public business data in the index, from open-licensed sources only. Officer names from registers such as Companies House are public-record data, handled per the source's rules. - No LinkedIn data from any source. #### 4. How we use it - Counsel to draft. Outcomes and feature hashes may be pooled across opted-in orgs for calibration; no record, name, email, domain or text is ever pooled. #### 5. Retention - Decisions and outcomes: life of the org plus 90 days. - Request logs: 30 days, with emails, phone numbers and names redacted before storage. - Customer-supplied text: never stored past the request, except redacted in the 30-day log. - Raw public source files: kept indefinitely (public data). - Org deletion: within 30 days, confirmed by email. #### 6. Subprocessors - See the subprocessors page. #### 7. Your rights and how to exercise them - A documented request path exists (`docs/security/DSAR.md`, to be written in WP16). Counsel to draft the public text. #### 8. International transfers - Verdict runs in the US only. Counsel to draft. #### 9. Changes to this policy - Counsel to draft. ## Data processing addendum Source: https://docs.askverdict.io/legal/dpa.md :::caution[DRAFT placeholder] This is a DRAFT placeholder created in WP11 because no draft existed in `docs/legal/`. It is not legal text and has not been reviewed by counsel. It lists the structure spec 9.10 requires and the product facts each section must reflect. Counsel writes the text before public launch. ::: ### Structure required #### 1. Definitions and scope - Counsel to draft. #### 2. Roles - The customer is the controller of customer-supplied data (rosters, threads, ICP text); Verdict is its processor. #### 3. Details of processing - Subject matter: answering named decisions for the customer's agent. - Duration: for the request; redacted request logs for 30 days; decisions and outcomes (which hold feature hashes, not text) for the life of the org plus 90 days. - Categories of data and data subjects: counsel to draft from the API schemas. #### 4. Processor obligations - Counsel to draft. #### 5. Subprocessors - The list is on the subprocessors page. Counsel to draft the notice and objection mechanism. #### 6. Security measures - Technical and organizational measures are described on the security overview page (encryption in transit, row-level tenant isolation, hashed keys, redacted logs, envelope-encrypted secrets, audit logging). #### 7. Personal data breach notification - Customers are notified within 72 hours of a confirmed data incident. #### 8. Assistance with data subject requests - Counsel to draft. #### 9. Deletion and return - Org deletion completes within 30 days and is confirmed by email. Vault entries are deleted on removal and on org deletion. #### 10. Audits - Counsel to draft. SOC 2 readiness, not certification, in v1. Annual penetration test. #### 11. International transfers - Counsel to draft. US only. ## Acceptable use policy Source: https://docs.askverdict.io/legal/acceptable-use.md :::caution[DRAFT placeholder] This is a DRAFT placeholder created in WP11 because no draft existed in `docs/legal/`. It is not legal text and has not been reviewed by counsel. It lists the structure spec 9.10 requires and the product facts each section must reflect. Counsel writes the text before public launch. ::: ### Structure required #### 1. Scope - Counsel to draft. #### 2. Prohibited data - Any LinkedIn-sourced data, from any source, in any request. #### 3. Prohibited industries and use cases - Healthcare, government, defense, banking and insurance use cases are out of scope. #### 4. Prohibited claims - No use of Verdict to make eligibility claims, or "safe to email" or deliverability claims, from vendor data where the vendor's terms forbid it. #### 5. Prohibited conduct - Counsel to draft (for example abuse of the API, attempts to read another organization's data, circumventing rate limits or spend caps). #### 6. Sending - Verdict never sends messages. The customer is responsible for its own sending and for the laws that govern it. #### 7. Enforcement - Counsel to draft. ## Subprocessors Source: https://docs.askverdict.io/legal/subprocessors.md :::caution[DRAFT placeholder] This is a DRAFT placeholder created in WP11 because no draft existed in `docs/legal/`. It is not legal text and has not been reviewed by counsel. It lists the structure spec 9.10 requires and the product facts each section must reflect. Counsel writes the text before public launch. ::: ### Structure required The list below is taken from spec 9.10 and the vendor table in spec 5. Purposes are factual. Locations, transfer mechanisms and the change-notice process are for counsel to confirm. | Subprocessor | Purpose | Data | Location | | --- | --- | --- | --- | | Railway | API, worker and console hosting; Postgres database | All customer and account data at rest | To be confirmed | | Cloudflare | DNS, TLS, WAF and bot management; R2 storage of public source files; docs hosting | Request metadata in transit; public data at rest | To be confirmed | | Upstash | Redis: rate-limit counters, idempotency cache (24 hours), hot firm cache | Cached responses, request metadata | To be confirmed | | TypeSafe | Decision model (Jev) | Compact decision inputs (redacted, guarded) | To be confirmed | | Stripe | Payments for packs and auto top-up | Billing contact and payment data | To be confirmed | | Resend | Transactional email only (verification, receipts, alerts) | Email address, message content | To be confirmed | | Doppler | Secrets management | Service secrets, no customer data | To be confirmed | | Sentry | Error tracking | Error metadata, redacted | To be confirmed | | Log vendor (Axiom or Grafana Cloud, to be chosen in WP12) | Logs, traces and metrics | Structured logs without bodies, keys or personal data | To be confirmed | | treg (phase 2) | Vendor data pass-through | Vendor requests the customer makes | To be confirmed | # API reference ## POST /v1/decisions/qualify_firm Tag: Decisions. Source: https://docs.askverdict.io/reference/qualify-firm.md | | | | --- | --- | | Call | `POST /v1/decisions/qualify_firm` | | Price | $0.005 per call, billed only when an answer is returned | | Scope | `decisions:write` | | Operation id | `qualifyFirm` | ### What it does Is this firm in my ICP? Returns `fit` as `p` and the resolved firm. Call it when your agent meets a company and must decide whether to spend effort on it. Give one firm reference (a domain is easiest) and an ICP profile id. Verdict resolves the firm in its index, reads its public facts (type, size band, HQ) and returns `fit` as `p`, `answer` (`fit` or `not_fit`), a `reason_code`, the resolved `firm` and cited `evidence`. **Price: $0.005 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 Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X POST 'https://api.askverdict.io/v1/decisions/qualify_firm' \ -H "Authorization: Bearer $VERDICT_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H 'Content-Type: application/json' \ -d '{ "firm": { "domain": "northfieldcapital.com" }, "icp_id": "icp_01JC3Z4Y8R9K2M5N7P1Q3S5T7V" }' ``` ### Example response `200`: The `qualify_firm` decision, in the common envelope. ```json title="200 response" { "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": "qualify_firm", "p": 0.82, "answer": "fit", "reason_code": "icp_type_and_size_match", "reason": "Registered investment adviser, $400M AUM band, 12 employees; matches ICP type and size.", "evidence": [ { "source": "sec_edgar", "url": "https://adviserinfo.sec.gov/firm/summary/298765", "observed_at": "2026-10-28", "label": "Form ADV Part 1A" } ], "fit": 0.82, "firm": { "id": "frm_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "name": "Northfield Capital Advisors LLC", "domain": "northfieldcapital.com", "identifiers": { "crd": "298765", "lei": "549300ABCDEF12345678" }, "type": "investment_adviser", "size_band": "small", "hq": { "country": "US", "region": "MA", "city": "Boston" }, "status": "active" }, "cost": { "decision_usd": 0.005, "vendor_usd": 0, "total_usd": 0.005 }, "balance_usd": 18.32 } ``` ### 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 Schema `QualifyFirmRequest`. The `qualify_firm` input. | Field | Type | Required | Description | | --- | --- | --- | --- | | `firm` | FirmReference | yes | A firm reference: exactly one of `firm_id`, `domain`, `name` (with an optional `hint`), `lei`, `cik`, `crd` or `companies_house`. | | `firm.firm_id` | string | no | A firm id from Verdict's index. | | `firm.domain` | string | no | The firm's web domain. | | `firm.name` | string | no | The firm's legal or trading name. | | `firm.hint` | object | no | Optional country, city or domain to disambiguate the name. | | `firm.hint.country` | string | no | ISO 3166-1 alpha-2 country of the firm's headquarters. | | `firm.hint.city` | string | no | City of the firm's headquarters. | | `firm.hint.domain` | string | no | A domain the firm is known to use. | | `firm.lei` | string | no | The firm's Legal Entity Identifier. | | `firm.cik` | string | no | The firm's SEC Central Index Key. | | `firm.crd` | string | no | The firm's CRD number. | | `firm.companies_house` | string | no | The firm's UK Companies House number. | | `icp_id` | string | yes | The ICP profile to decide against. | ### Response fields (200) Schema `QualifyFirmResponse`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The decision id. Report outcomes against it. | | `decision` | enum: "qualify_firm" | 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: "fit", "not_fit" | yes | `fit` or `not_fit`: `p` applied to your threshold. | | `reason_code` | enum: "icp_type_and_size_match", "icp_type_match_size_mismatch", "icp_size_match_type_mismatch", "icp_type_and_size_mismatch", "icp_geography_mismatch", "icp_exclusion_filter_match", "firm_inactive", "insufficient_firm_data" | yes | Why, from the fixed `qualify_firm` 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. | | `fit` | number | yes | Probability the firm fits the ICP; equal to `p`. | | `firm` | FirmSummary | yes | A firm's id, names, identifiers and public facts. | | `firm.id` | string | yes | The firm id. | | `firm.name` | string | yes | Canonical firm name. | | `firm.domain` | string or null | yes | Primary web domain, if known. | | `firm.identifiers` | object | yes | Registry identifiers, by kind. | | `firm.identifiers.lei` | string | no | Legal Entity Identifier. | | `firm.identifiers.cik` | string | no | SEC Central Index Key. | | `firm.identifiers.crd` | string | no | CRD number. | | `firm.identifiers.companies_house` | string | no | UK Companies House number. | | `firm.identifiers.wikidata` | string | no | Wikidata item id. | | `firm.type` | enum: "operating_company", "investment_adviser", "broker_dealer", "investment_fund", "nonprofit", "public_body", "other", "unknown" | yes | Coarse firm type. | | `firm.size_band` | enum: "micro", "small", "medium", "large", "enterprise", "unknown" | yes | Employee-count size band. | | `firm.hq` | object | yes | Headquarters location. | | `firm.hq.country` | string or null | yes | ISO 3166-1 alpha-2 country. | | `firm.hq.region` | string or null | yes | State, county or region, as the registry gives it. | | `firm.hq.city` | string or null | yes | City. | | `firm.status` | enum: "active", "inactive", "dissolved", "unknown", "merged" | yes | Registry status. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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 Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## POST /v1/decisions/score_signal Tag: Decisions. Source: https://docs.askverdict.io/reference/score-signal.md | | | | --- | --- | | Call | `POST /v1/decisions/score_signal` | | Price | $0.005 per call, billed only when an answer is returned | | Scope | `decisions:write` | | Operation id | `scoreSignal` | ### What it does Does this event matter for my offer, and is now the time? Returns `relevant`, `act_now` and `window_days`. Call it when an event (a filing, a raise, an officer change) arrives and you must decide whether it is worth acting on now. Give an `event_id` from `GET /v1/events` or an inline `event`, and an ICP profile id. Returns `relevant` as `p`, `act_now`, `window_days`, `answer` (`relevant` or `not_relevant`) and a `reason_code`. **Price: $0.005 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 Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X POST 'https://api.askverdict.io/v1/decisions/score_signal' \ -H "Authorization: Bearer $VERDICT_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H 'Content-Type: application/json' \ -d '{ "event_id": "evt_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "icp_id": "icp_01JC3Z4Y8R9K2M5N7P1Q3S5T7V" }' ``` ### Example response `200`: The `score_signal` decision, in the common envelope. ```json title="200 response" { "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": "score_signal", "p": 0.78, "answer": "relevant", "reason_code": "event_relevant_and_timely", "reason": "Form D offering filed 4 days ago; new capital is a buying trigger for this ICP.", "evidence": [ { "source": "sec_edgar", "url": "https://www.sec.gov/Archives/edgar/data/1601234/000160123426000004/xslFormDX01/primary_doc.xml", "observed_at": "2026-11-04", "label": "Form D" } ], "relevant": 0.78, "act_now": 0.64, "window_days": 21, "event_id": "evt_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "firm": { "id": "frm_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "name": "Northfield Capital Advisors LLC", "domain": "northfieldcapital.com", "identifiers": { "crd": "298765", "lei": "549300ABCDEF12345678" }, "type": "investment_adviser", "size_band": "small", "hq": { "country": "US", "region": "MA", "city": "Boston" }, "status": "active" }, "cost": { "decision_usd": 0.005, "vendor_usd": 0, "total_usd": 0.005 }, "balance_usd": 18.315 } ``` ### 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 Schema `ScoreSignalRequest`. The `score_signal` input. | Field | Type | Required | Description | | --- | --- | --- | --- | | `event_id` | string | no | An event from the index. | | `icp_id` | string | no | The ICP profile to decide against. | | `event` | InlineEvent | no | An event the caller supplies, for events Verdict's index does not hold. | | `event.type` | enum: "filing", "registration", "officer_change", "ownership_change", "lei_change", "address_change", "fund_raise", "insider_trade", "patent_filing", "litigation", "news_mention", "repo_activity", "hiring_aggregate" | yes | Event type. | | `event.firm` | FirmReference | yes | A firm reference: exactly one of `firm_id`, `domain`, `name` (with an optional `hint`), `lei`, `cik`, `crd` or `companies_house`. | | `event.firm.firm_id` | string | no | A firm id from Verdict's index. | | `event.firm.domain` | string | no | The firm's web domain. | | `event.firm.name` | string | no | The firm's legal or trading name. | | `event.firm.hint` | object | no | Optional country, city or domain to disambiguate the name. | | `event.firm.lei` | string | no | The firm's Legal Entity Identifier. | | `event.firm.cik` | string | no | The firm's SEC Central Index Key. | | `event.firm.crd` | string | no | The firm's CRD number. | | `event.firm.companies_house` | string | no | The firm's UK Companies House number. | | `event.occurred_at` | string (date-time) | yes | When the event happened. | | `event.title` | string | no | One-line summary of the event. | | `event.description` | string | no | What happened, in plain text. At most 8192 bytes of UTF-8. | | `event.source_url` | string (uri) | no | Where the event was reported. | ### Response fields (200) Schema `ScoreSignalResponse`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The decision id. Report outcomes against it. | | `decision` | enum: "score_signal" | 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: "relevant", "not_relevant" | yes | `relevant` or `not_relevant`: `p` applied to your threshold. | | `reason_code` | enum: "event_relevant_and_timely", "event_relevant_but_stale", "event_type_not_relevant", "event_routine", "firm_not_in_icp", "event_low_confidence", "event_superseded", "insufficient_event_data" | yes | Why, from the fixed `score_signal` 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. | | `relevant` | number | yes | Probability the event matters for the ICP's offer; equal to `p`. | | `act_now` | number | yes | Probability that now is the time to act on it. | | `window_days` | integer or null | yes | Days left to act, or `null` when there is no clear window. | | `event_id` | string | yes | The scored event (an inline event is stored and given an id). | | `firm` | FirmSummary | yes | A firm's id, names, identifiers and public facts. | | `firm.id` | string | yes | The firm id. | | `firm.name` | string | yes | Canonical firm name. | | `firm.domain` | string or null | yes | Primary web domain, if known. | | `firm.identifiers` | object | yes | Registry identifiers, by kind. | | `firm.identifiers.lei` | string | no | Legal Entity Identifier. | | `firm.identifiers.cik` | string | no | SEC Central Index Key. | | `firm.identifiers.crd` | string | no | CRD number. | | `firm.identifiers.companies_house` | string | no | UK Companies House number. | | `firm.identifiers.wikidata` | string | no | Wikidata item id. | | `firm.type` | enum: "operating_company", "investment_adviser", "broker_dealer", "investment_fund", "nonprofit", "public_body", "other", "unknown" | yes | Coarse firm type. | | `firm.size_band` | enum: "micro", "small", "medium", "large", "enterprise", "unknown" | yes | Employee-count size band. | | `firm.hq` | object | yes | Headquarters location. | | `firm.hq.country` | string or null | yes | ISO 3166-1 alpha-2 country. | | `firm.hq.region` | string or null | yes | State, county or region, as the registry gives it. | | `firm.hq.city` | string or null | yes | City. | | `firm.status` | enum: "active", "inactive", "dissolved", "unknown", "merged" | yes | Registry status. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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 Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## POST /v1/decisions/pick_person Tag: Decisions. Source: https://docs.askverdict.io/reference/pick-person.md | | | | --- | --- | | Call | `POST /v1/decisions/pick_person` | | Price | $0.005 per call, billed only when an answer is returned | | Scope | `decisions:write` | | Operation id | `pickPerson` | ### What it does Who in this roster should I write to? Returns the roster ranked, each person with `p`. Call it when you have several people at a firm and must choose whom to write to. Give a `roster` of up to 50 people (name, title, seniority, department; no LinkedIn data), the ICP and an optional `question`. Returns the whole roster ranked in `people`, each with `p`; `p` and `answer` (`pick` or `no_pick`) refer to the top person. One price per call for up to 50 people. **Price: $0.005 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 Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X POST 'https://api.askverdict.io/v1/decisions/pick_person' \ -H "Authorization: Bearer $VERDICT_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H 'Content-Type: application/json' \ -d '{ "roster": [ { "ref": "c-101", "name": "Dana Whitfield", "title": "Chief Operating Officer", "seniority": "c_level", "department": "Operations" }, { "ref": "c-102", "name": "Sam Ortega", "title": "Operations Associate", "seniority": "entry", "department": "Operations" }, { "ref": "c-103", "name": "Priya Raman", "title": "Chief Compliance Officer", "seniority": "c_level", "department": "Compliance" } ], "firm": { "domain": "northfieldcapital.com" }, "icp_id": "icp_01JC3Z4Y8R9K2M5N7P1Q3S5T7V" }' ``` ### Example response `200`: The `pick_person` decision, in the common envelope. ```json title="200 response" { "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": "pick_person", "p": 0.74, "answer": "pick", "reason_code": "buyer_role_match", "reason": "Chief Operating Officer owns operations tooling at a 12-person adviser.", "evidence": [ { "source": "request", "url": null, "observed_at": "2026-11-07", "pointer": "/roster/0" } ], "people": [ { "index": 0, "ref": "c-101", "rank": 1, "p": 0.74 }, { "index": 2, "ref": "c-103", "rank": 2, "p": 0.41 }, { "index": 1, "ref": "c-102", "rank": 3, "p": 0.09 } ], "cost": { "decision_usd": 0.005, "vendor_usd": 0, "total_usd": 0.005 }, "balance_usd": 18.31 } ``` ### 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 Schema `PickPersonRequest`. The `pick_person` input. | Field | Type | Required | Description | | --- | --- | --- | --- | | `roster` | array of RosterEntry | yes | The people to rank, 1 to 50. | | `roster[].ref` | string | no | Your own id for this person, echoed back in `people`. | | `roster[].name` | string | yes | Full name. | | `roster[].title` | string | yes | Job title. | | `roster[].seniority` | enum: "owner", "c_level", "vp", "director", "manager", "senior", "entry", "unknown" | no | Seniority, if known. | | `roster[].department` | string | no | Department or function, for example `Operations`. | | `roster[].location` | string | no | City or country. | | `roster[].tenure_months` | integer | no | Months in the current role. | | `firm` | FirmReference | no | A firm reference: exactly one of `firm_id`, `domain`, `name` (with an optional `hint`), `lei`, `cik`, `crd` or `companies_house`. | | `firm.firm_id` | string | no | A firm id from Verdict's index. | | `firm.domain` | string | no | The firm's web domain. | | `firm.name` | string | no | The firm's legal or trading name. | | `firm.hint` | object | no | Optional country, city or domain to disambiguate the name. | | `firm.hint.country` | string | no | ISO 3166-1 alpha-2 country of the firm's headquarters. | | `firm.hint.city` | string | no | City of the firm's headquarters. | | `firm.hint.domain` | string | no | A domain the firm is known to use. | | `firm.lei` | string | no | The firm's Legal Entity Identifier. | | `firm.cik` | string | no | The firm's SEC Central Index Key. | | `firm.crd` | string | no | The firm's CRD number. | | `firm.companies_house` | string | no | The firm's UK Companies House number. | | `icp_id` | string | yes | The ICP profile to decide against. | | `question` | string | no | Optional extra question, for example `who owns trading operations?`. | ### Response fields (200) Schema `PickPersonResponse`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The decision id. Report outcomes against it. | | `decision` | enum: "pick_person" | 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: "pick", "no_pick" | yes | `pick` or `no_pick`: the top person's `p` applied to your threshold. | | `reason_code` | enum: "buyer_role_match", "seniority_match", "influencer_role_match", "question_match", "too_senior_for_offer", "too_junior_for_offer", "titles_ambiguous", "no_strong_match" | yes | Why, from the fixed `pick_person` 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. | | `people` | array of object | yes | The whole roster ranked best first, each with `p`. | | `people[].index` | integer | yes | Zero-based position of the person in your `roster`. | | `people[].ref` | string or null | yes | Your `ref` for the person, or `null` if you gave none. | | `people[].rank` | integer | yes | Rank, 1 is best. | | `people[].p` | number | yes | Calibrated probability that this is the right person to write to. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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 Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## POST /v1/decisions/triage_reply Tag: Decisions. Source: https://docs.askverdict.io/reference/triage-reply.md | | | | --- | --- | | 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 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 Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" 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 `200`: The `triage_reply` decision, in the common envelope. ```json title="200 response" { "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 | 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 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) 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 Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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 Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## POST /v1/decisions/right_person Tag: Decisions. Source: https://docs.askverdict.io/reference/right-person.md | | | | --- | --- | | Call | `POST /v1/decisions/right_person` | | Price | $0.003 per call, billed only when an answer is returned | | Scope | `decisions:write` | | Operation id | `rightPerson` | ### What it does Is this candidate contact the right one for my ICP? Returns `p`. Call it to check one candidate contact before you write to them. Give the candidate (title, optional seniority, department, firm) and the ICP. Returns `p` and `answer` (`right_person` or `not_right_person`). **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 Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X POST 'https://api.askverdict.io/v1/decisions/right_person' \ -H "Authorization: Bearer $VERDICT_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H 'Content-Type: application/json' \ -d '{ "candidate": { "name": "Dana Whitfield", "title": "Chief Operating Officer", "seniority": "c_level", "department": "Operations", "firm": { "domain": "northfieldcapital.com" } }, "icp_id": "icp_01JC3Z4Y8R9K2M5N7P1Q3S5T7V" }' ``` ### Example response `200`: The `right_person` decision, in the common envelope. ```json title="200 response" { "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": "right_person", "p": 0.81, "answer": "right_person", "reason_code": "title_and_seniority_match", "reason": "COO at a small adviser matches the ICP's operations buyer.", "evidence": [ { "source": "request", "url": null, "observed_at": "2026-11-07", "pointer": "/candidate/title" } ], "cost": { "decision_usd": 0.003, "vendor_usd": 0, "total_usd": 0.003 }, "balance_usd": 18.304 } ``` ### 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 Schema `RightPersonRequest`. The `right_person` input. | Field | Type | Required | Description | | --- | --- | --- | --- | | `candidate` | CandidateContact | yes | One candidate contact. LinkedIn-sourced data is not accepted. | | `candidate.name` | string | no | Full name. | | `candidate.title` | string | yes | Job title. | | `candidate.seniority` | enum: "owner", "c_level", "vp", "director", "manager", "senior", "entry", "unknown" | no | Seniority, if known. | | `candidate.department` | string | no | Department or function. | | `candidate.email` | string (email) | no | Work email, if known. | | `candidate.firm` | FirmReference | no | A firm reference: exactly one of `firm_id`, `domain`, `name` (with an optional `hint`), `lei`, `cik`, `crd` or `companies_house`. | | `candidate.firm.firm_id` | string | no | A firm id from Verdict's index. | | `candidate.firm.domain` | string | no | The firm's web domain. | | `candidate.firm.name` | string | no | The firm's legal or trading name. | | `candidate.firm.hint` | object | no | Optional country, city or domain to disambiguate the name. | | `candidate.firm.lei` | string | no | The firm's Legal Entity Identifier. | | `candidate.firm.cik` | string | no | The firm's SEC Central Index Key. | | `candidate.firm.crd` | string | no | The firm's CRD number. | | `candidate.firm.companies_house` | string | no | The firm's UK Companies House number. | | `candidate.title_observed_at` | string (date-time) | no | When you last saw this title, to judge staleness. | | `icp_id` | string | yes | The ICP profile to decide against. | ### Response fields (200) Schema `RightPersonResponse`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The decision id. Report outcomes against it. | | `decision` | enum: "right_person" | 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: "right_person", "not_right_person" | yes | `right_person` or `not_right_person`: `p` applied to your threshold. | | `reason_code` | enum: "title_and_seniority_match", "title_match_seniority_mismatch", "seniority_match_title_mismatch", "department_mismatch", "firm_not_in_icp", "candidate_data_stale", "insufficient_candidate_data" | yes | Why, from the fixed `right_person` 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. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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 Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## POST /v1/decisions/should_send Tag: Decisions. Source: https://docs.askverdict.io/reference/should-send.md | | | | --- | --- | | Call | `POST /v1/decisions/should_send` | | Price | $0.003 per call, billed only when an answer is returned | | Scope | `decisions:write` | | Operation id | `shouldSend` | ### What it does Should this prepared message go out now? Returns `send` as `p` and a `hold_reason`. Call it as the last gate before a prepared message goes out. Give the message, the decision ids behind it, the inbox health you measure and the ICP. Returns `send` as `p`, `answer` (`send` or `hold`) and a `hold_reason` when holding. Verdict never sends anything. **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 Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X POST 'https://api.askverdict.io/v1/decisions/should_send' \ -H "Authorization: Bearer $VERDICT_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H 'Content-Type: application/json' \ -d '{ "message": { "channel": "email", "subject": "Trade ops after your raise", "body": "Hi Dana, congratulations on the new fund. We help 10-person advisers automate trade reconciliation. Open to a short call next week?" }, "decision_ids": [ "dec_01JC3Z4Y8R9K2M5N7P1Q3S5T7V" ], "inbox_health": { "bounce_rate_7d": 0.012, "spam_complaint_rate_7d": 0.0005, "sent_today": 38, "daily_limit": 50 }, "recipient": { "touches_30d": 1 }, "icp_id": "icp_01JC3Z4Y8R9K2M5N7P1Q3S5T7V" }' ``` ### Example response `200`: The `should_send` decision, in the common envelope. ```json title="200 response" { "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": "should_send", "p": 0.88, "answer": "send", "reason_code": "all_checks_passed", "reason": "Upstream decisions clear their thresholds and inbox health is within bounds.", "evidence": [ { "source": "request", "url": null, "observed_at": "2026-11-07", "pointer": "/inbox_health" } ], "send": 0.88, "hold_reason": null, "cost": { "decision_usd": 0.003, "vendor_usd": 0, "total_usd": 0.003 }, "balance_usd": 18.301 } ``` ### 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 Schema `ShouldSendRequest`. The `should_send` input. | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | object | yes | The prepared message. | | `message.channel` | enum: "email", "other" | yes | Channel the message will go out on. | | `message.subject` | string | no | Subject line. | | `message.body` | string | yes | Plain-text body as it will be sent. At most 8192 bytes of UTF-8. | | `decision_ids` | array of string | yes | The decisions behind the message (for example `qualify_firm` and `pick_person`), 1 to 10. | | `inbox_health` | InboxHealth | yes | Inbox health you measure; Verdict never reads your inbox. | | `inbox_health.bounce_rate_7d` | number | no | Share of sends that bounced over the last 7 days. | | `inbox_health.spam_complaint_rate_7d` | number | no | Share of sends marked as spam over the last 7 days. | | `inbox_health.reply_rate_7d` | number | no | Share of sends replied to over the last 7 days. | | `inbox_health.sent_today` | integer | no | Messages sent from this inbox today. | | `inbox_health.daily_limit` | integer | no | Your daily send limit for this inbox. | | `inbox_health.domain_age_days` | integer | no | Age of the sending domain in days. | | `recipient` | object | no | Optional contact history of the recipient. | | `recipient.last_contacted_at` | string (date-time) | no | When you last wrote to this recipient. | | `recipient.touches_30d` | integer | no | Messages sent to this recipient in 30 days. | | `icp_id` | string | yes | The ICP profile to decide against. | ### Response fields (200) Schema `ShouldSendResponse`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The decision id. Report outcomes against it. | | `decision` | enum: "should_send" | 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: "send", "hold" | yes | `send` or `hold`: `p` applied to your threshold. | | `reason_code` | enum: "all_checks_passed", "upstream_decision_below_threshold", "upstream_decision_missing", "inbox_health_degraded", "daily_volume_limit_reached", "recipient_recently_contacted", "message_quality_issue", "recipient_opted_out" | yes | Why, from the fixed `should_send` 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. | | `send` | number | yes | Probability that sending now is right; equal to `p`. | | `hold_reason` | enum: "upstream_decision_weak", "upstream_decision_missing", "inbox_health_degraded", "daily_volume_limit", "recently_contacted", "message_issue", "recipient_opted_out", "low_confidence", null | yes | Why to hold, when `answer` is `hold`; `null` when `answer` is `send`. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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 Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## GET /v1/decisions/{id} Tag: Decisions. Source: https://docs.askverdict.io/reference/get-decision.md | | | | --- | --- | | Call | `GET /v1/decisions/{id}` | | Price | free | | Scope | `decisions:write` | | Operation id | `getDecision` | ### What it does Fetch a decision your organization made, exactly as it was returned (decisions are immutable). Use it to audit an answer or to recover a response you lost. Free. Scope: `decisions:write`. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X GET 'https://api.askverdict.io/v1/decisions/dec_01JC3Z4Y8R9K2M5N7P1Q3S5T7V' \ -H "Authorization: Bearer $VERDICT_API_KEY" ``` ### Example response `200`: The stored decision. ```json title="200 response" { "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": "qualify_firm", "p": 0.82, "answer": "fit", "reason_code": "icp_type_and_size_match", "reason": "Registered investment adviser, $400M AUM band, 12 employees; matches ICP type and size.", "evidence": [ { "source": "sec_edgar", "url": "https://adviserinfo.sec.gov/firm/summary/298765", "observed_at": "2026-10-28", "label": "Form ADV Part 1A" } ], "fit": 0.82, "firm": { "id": "frm_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "name": "Northfield Capital Advisors LLC", "domain": "northfieldcapital.com", "identifiers": { "crd": "298765", "lei": "549300ABCDEF12345678" }, "type": "investment_adviser", "size_band": "small", "hq": { "country": "US", "region": "MA", "city": "Boston" }, "status": "active" }, "cost": { "decision_usd": 0.005, "vendor_usd": 0, "total_usd": 0.005 }, "balance_usd": 18.32 } ``` ### Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `id` | path | string | yes | The decision id. | | `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`. | ### Response fields (200) Schema `Decision`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | no | The decision id. Report outcomes against it. | | `decision` | enum: "qualify_firm" | no | The decision name. | | `version` | enum: "2026-11-01" | no | API version that served the decision. | | `question_set_version` | string (date) | no | Version (a date) of the question set that answered (spec 6.1). | | `model` | enum: "jev" | no | Decision model that answered: always `jev` (ADR-0008). | | `p` | number | no | Calibrated probability (spec 6.4). Always present. | | `answer` | enum: "fit", "not_fit" | no | `fit` or `not_fit`: `p` applied to your threshold. | | `reason_code` | enum: "icp_type_and_size_match", "icp_type_match_size_mismatch", "icp_size_match_type_mismatch", "icp_type_and_size_mismatch", "icp_geography_mismatch", "icp_exclusion_filter_match", "firm_inactive", "insufficient_firm_data" | no | Why, from the fixed `qualify_firm` reason-code enum. | | `reason` | string | no | Short templated explanation of `reason_code`; never free text from a frontier model. | | `evidence` | array of Evidence | no | 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 | no | 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 | no | 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 | no | Balance after this call. US dollars, up to 6 decimals. | | `latency_ms` | integer | no | Server-side latency of the call, in milliseconds. | | `created_at` | string (date-time) | no | When the decision was made. Decisions are immutable. | | `fit` | number | no | Probability the firm fits the ICP; equal to `p`. | | `firm` | FirmSummary | no | A firm's id, names, identifiers and public facts. | | `firm.id` | string | yes | The firm id. | | `firm.name` | string | yes | Canonical firm name. | | `firm.domain` | string or null | yes | Primary web domain, if known. | | `firm.identifiers` | object | yes | Registry identifiers, by kind. | | `firm.identifiers.lei` | string | no | Legal Entity Identifier. | | `firm.identifiers.cik` | string | no | SEC Central Index Key. | | `firm.identifiers.crd` | string | no | CRD number. | | `firm.identifiers.companies_house` | string | no | UK Companies House number. | | `firm.identifiers.wikidata` | string | no | Wikidata item id. | | `firm.type` | enum: "operating_company", "investment_adviser", "broker_dealer", "investment_fund", "nonprofit", "public_body", "other", "unknown" | yes | Coarse firm type. | | `firm.size_band` | enum: "micro", "small", "medium", "large", "enterprise", "unknown" | yes | Employee-count size band. | | `firm.hq` | object | yes | Headquarters location. | | `firm.hq.country` | string or null | yes | ISO 3166-1 alpha-2 country. | | `firm.hq.region` | string or null | yes | State, county or region, as the registry gives it. | | `firm.hq.city` | string or null | yes | City. | | `firm.status` | enum: "active", "inactive", "dissolved", "unknown", "merged" | yes | Registry status. | | `relevant` | number | no | Probability the event matters for the ICP's offer; equal to `p`. | | `act_now` | number | no | Probability that now is the time to act on it. | | `window_days` | integer or null | no | Days left to act, or `null` when there is no clear window. | | `event_id` | string | no | The scored event (an inline event is stored and given an id). | | `people` | array of object | no | The whole roster ranked best first, each with `p`. | | `people[].index` | integer | yes | Zero-based position of the person in your `roster`. | | `people[].ref` | string or null | yes | Your `ref` for the person, or `null` if you gave none. | | `people[].rank` | integer | yes | Rank, 1 is best. | | `people[].p` | number | yes | Calibrated probability that this is the right person to write to. | | `state` | enum: "interested", "meeting_requested", "question", "objection_price", "objection_timing", "objection_fit", "not_decision_maker", "referral", "out_of_office", "unsubscribe", "bounce", "wrong_person", "other" | no | 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" | no | What the agent should do next. | | `send` | number | no | Probability that sending now is right; equal to `p`. | | `hold_reason` | enum: "upstream_decision_weak", "upstream_decision_missing", "inbox_health_degraded", "daily_volume_limit", "recently_contacted", "message_issue", "recipient_opted_out", "low_confidence", null | no | Why to hold, when `answer` is `hold`; `null` when `answer` is `send`. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `403` | The key lacks the scope this operation needs (`forbidden_scope`). | | `404` | The resource does not exist in this organization (`not_found`). | | `429` | Rate limited (`rate_limited`). Wait `Retry-After` seconds. | | `500` | Unexpected server error (`internal`). Not billed; retry with the same `Idempotency-Key`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## GET /v1/firms/resolve Tag: Index. Source: https://docs.askverdict.io/reference/resolve-firm.md | | | | --- | --- | | Call | `GET /v1/firms/resolve` | | Price | free | | Scope | `index:read` | | Operation id | `resolveFirm` | ### What it does Turn a domain, name (with `country`), LEI, CIK, CRD or Companies House number into Verdict's firm id and identifier crosswalk. Give exactly one reference parameter. Call it to dedupe companies or to get a `firm_id` before other calls; `qualify_firm` resolves on its own, so you do not need it first. Free. Unresolved references return a provisional firm (`matched_on: provisional`). Scope: `index:read`. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X GET 'https://api.askverdict.io/v1/firms/resolve?domain=northfieldcapital.com&country=US&lei=549300ABCDEF12345678&cik=1601234&crd=298765&companies_house=09876543' \ -H "Authorization: Bearer $VERDICT_API_KEY" ``` ### Example response `200`: The resolved firm. ```json title="200 response" { "firm": { "id": "frm_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "name": "Northfield Capital Advisors LLC", "domain": "northfieldcapital.com", "identifiers": { "crd": "298765", "lei": "549300ABCDEF12345678" }, "type": "investment_adviser", "size_band": "small", "hq": { "country": "US", "region": "MA", "city": "Boston" }, "status": "active", "provisional": false, "crosswalk": [ { "kind": "crd", "value": "298765", "source": "sec_edgar", "observed_at": "2026-10-28" }, { "kind": "lei", "value": "549300ABCDEF12345678", "source": "gleif", "observed_at": "2026-11-02" }, { "kind": "domain", "value": "northfieldcapital.com", "source": "sec_edgar", "observed_at": "2026-10-28" }, { "kind": "name_variant", "value": "Northfield Capital", "source": "wikidata", "observed_at": "2026-10-30" } ], "created_at": "2026-09-14T08:12:00Z", "updated_at": "2026-11-02T06:40:00Z" }, "matched_on": "domain" } ``` ### Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `domain` | query | string | no | Resolve by web domain. | | `name` | query | string | no | Resolve by name; add `country` to disambiguate. | | `country` | query | string | no | Country hint for `name`. | | `lei` | query | string | no | Resolve by LEI. | | `cik` | query | string | no | Resolve by SEC CIK. | | `crd` | query | string | no | Resolve by CRD number. | | `companies_house` | query | string | no | Resolve by Companies House number. | | `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`. | ### Response fields (200) Schema `ResolveFirmResponse`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `firm` | Firm | yes | A canonical firm with its public facts and identifier crosswalk. | | `firm.id` | string | yes | The firm id. | | `firm.name` | string | yes | Canonical firm name. | | `firm.domain` | string or null | yes | Primary web domain, if known. | | `firm.identifiers` | object | yes | Registry identifiers, by kind. | | `firm.identifiers.lei` | string | no | Legal Entity Identifier. | | `firm.identifiers.cik` | string | no | SEC Central Index Key. | | `firm.identifiers.crd` | string | no | CRD number. | | `firm.identifiers.companies_house` | string | no | UK Companies House number. | | `firm.identifiers.wikidata` | string | no | Wikidata item id. | | `firm.type` | enum: "operating_company", "investment_adviser", "broker_dealer", "investment_fund", "nonprofit", "public_body", "other", "unknown" | yes | Coarse firm type. | | `firm.size_band` | enum: "micro", "small", "medium", "large", "enterprise", "unknown" | yes | Employee-count size band. | | `firm.hq` | object | yes | Headquarters location. | | `firm.hq.country` | string or null | yes | ISO 3166-1 alpha-2 country. | | `firm.hq.region` | string or null | yes | State, county or region, as the registry gives it. | | `firm.hq.city` | string or null | yes | City. | | `firm.status` | enum: "active", "inactive", "dissolved", "unknown", "merged" | yes | Registry status. | | `firm.provisional` | boolean | yes | True when the firm was created from an unresolved reference and awaits nightly reconciliation. | | `firm.crosswalk` | array of object | yes | Every known identifier, including name variants and secondary domains, with its source. | | `firm.crosswalk[].kind` | enum: "domain", "lei", "cik", "crd", "companies_house", "wikidata", "name_variant" | yes | Identifier kind. | | `firm.crosswalk[].value` | string | yes | Identifier value. | | `firm.crosswalk[].source` | enum: "sec_edgar", "gleif", "companies_house", "wikidata", "request" | yes | Source that reported the identifier: an index source, or `request` when it came from a caller's reference (provisional firms). | | `firm.crosswalk[].observed_at` | string (date) | yes | Date the source last reported the identifier. | | `firm.created_at` | string (date-time) | yes | When the firm entered the index. | | `firm.updated_at` | string (date-time) | yes | When the firm's record last changed. | | `matched_on` | enum: "identifier", "domain", "name_and_country", "match_decision", "provisional" | yes | How the reference was matched. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `403` | The key lacks the scope this operation needs (`forbidden_scope`). | | `429` | Rate limited (`rate_limited`). Wait `Retry-After` seconds. | | `500` | Unexpected server error (`internal`). Not billed; retry with the same `Idempotency-Key`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## GET /v1/firms/{id} Tag: Index. Source: https://docs.askverdict.io/reference/get-firm.md | | | | --- | --- | | Call | `GET /v1/firms/{id}` | | Price | free | | Scope | `index:read` | | Operation id | `getFirm` | ### What it does Public facts (type, size band, HQ, status) and every known identifier of a firm, each with its source and date. Free. Scope: `index:read`. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X GET 'https://api.askverdict.io/v1/firms/frm_01JC3Z4Y8R9K2M5N7P1Q3S5T7V' \ -H "Authorization: Bearer $VERDICT_API_KEY" ``` ### Example response `200`: The firm. ```json title="200 response" { "id": "frm_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "name": "Northfield Capital Advisors LLC", "domain": "northfieldcapital.com", "identifiers": { "crd": "298765", "lei": "549300ABCDEF12345678" }, "type": "investment_adviser", "size_band": "small", "hq": { "country": "US", "region": "MA", "city": "Boston" }, "status": "active", "provisional": false, "crosswalk": [ { "kind": "crd", "value": "298765", "source": "sec_edgar", "observed_at": "2026-10-28" }, { "kind": "lei", "value": "549300ABCDEF12345678", "source": "gleif", "observed_at": "2026-11-02" }, { "kind": "domain", "value": "northfieldcapital.com", "source": "sec_edgar", "observed_at": "2026-10-28" }, { "kind": "name_variant", "value": "Northfield Capital", "source": "wikidata", "observed_at": "2026-10-30" } ], "created_at": "2026-09-14T08:12:00Z", "updated_at": "2026-11-02T06:40:00Z" } ``` ### Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `id` | path | string | yes | The firm id. | | `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`. | ### Response fields (200) Schema `Firm`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The firm id. | | `name` | string | yes | Canonical firm name. | | `domain` | string or null | yes | Primary web domain, if known. | | `identifiers` | object | yes | Registry identifiers, by kind. | | `identifiers.lei` | string | no | Legal Entity Identifier. | | `identifiers.cik` | string | no | SEC Central Index Key. | | `identifiers.crd` | string | no | CRD number. | | `identifiers.companies_house` | string | no | UK Companies House number. | | `identifiers.wikidata` | string | no | Wikidata item id. | | `type` | enum: "operating_company", "investment_adviser", "broker_dealer", "investment_fund", "nonprofit", "public_body", "other", "unknown" | yes | Coarse firm type. | | `size_band` | enum: "micro", "small", "medium", "large", "enterprise", "unknown" | yes | Employee-count size band. | | `hq` | object | yes | Headquarters location. | | `hq.country` | string or null | yes | ISO 3166-1 alpha-2 country. | | `hq.region` | string or null | yes | State, county or region, as the registry gives it. | | `hq.city` | string or null | yes | City. | | `status` | enum: "active", "inactive", "dissolved", "unknown", "merged" | yes | Registry status. | | `provisional` | boolean | yes | True when the firm was created from an unresolved reference and awaits nightly reconciliation. | | `crosswalk` | array of object | yes | Every known identifier, including name variants and secondary domains, with its source. | | `crosswalk[].kind` | enum: "domain", "lei", "cik", "crd", "companies_house", "wikidata", "name_variant" | yes | Identifier kind. | | `crosswalk[].value` | string | yes | Identifier value. | | `crosswalk[].source` | enum: "sec_edgar", "gleif", "companies_house", "wikidata", "request" | yes | Source that reported the identifier: an index source, or `request` when it came from a caller's reference (provisional firms). | | `crosswalk[].observed_at` | string (date) | yes | Date the source last reported the identifier. | | `created_at` | string (date-time) | yes | When the firm entered the index. | | `updated_at` | string (date-time) | yes | When the firm's record last changed. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `403` | The key lacks the scope this operation needs (`forbidden_scope`). | | `404` | The resource does not exist in this organization (`not_found`). | | `429` | Rate limited (`rate_limited`). Wait `Retry-After` seconds. | | `500` | Unexpected server error (`internal`). Not billed; retry with the same `Idempotency-Key`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## GET /v1/events Tag: Index. Source: https://docs.askverdict.io/reference/list-events.md | | | | --- | --- | | Call | `GET /v1/events` | | Price | free unscored; with `icp_id` each scored event bills one `score_signal` ($0.005) | | Scope | `index:read` | | Operation id | `listEvents` | ### What it does Events from the index for one firm (`firm_id`) or for an ICP (`icp_id`), newest first, optionally filtered by `since` and `types`. Without `icp_id` the call is free and `score` is `null`. With `icp_id` every returned event is scored (`score` set) and **bills one `score_signal` ($0.005) per scored event** (needs `decisions:write` too); a page with `icp_id` holds at most 50 events. A score made for the same ICP and event within the last hour is reused and not billed again. `min_p` filters the returned events on `relevant`; events below it were still scored and billed. If the balance (or the key's daily cap) runs out, or the decision model becomes unavailable, part-way through a page, the page stops there: you get the events scored and billed so far, `has_more: true` and a `next_cursor` that resumes at the first unscored event. Only when the first event cannot be scored is the error returned (402 `insufficient_balance`, or 503 `decision_unavailable` with `Retry-After`). Cursor-paginated. Scope: `index:read`. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X GET 'https://api.askverdict.io/v1/events?firm_id=frm_01JC3Z4Y8R9K2M5N7P1Q3S5T7V&icp_id=icp_01JC3Z4Y8R9K2M5N7P1Q3S5T7V&types=filing%2Cfund_raise&min_p=0.6' \ -H "Authorization: Bearer $VERDICT_API_KEY" ``` ### Example response `200`: A page of events. ```json title="200 response" { "data": [ { "id": "evt_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "type": "filing", "firm_id": "frm_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "occurred_at": "2026-11-03T21:15:00Z", "observed_at": "2026-11-04T02:05:00Z", "source": "sec_edgar", "source_url": "https://www.sec.gov/Archives/edgar/data/1601234/000160123426000004/xslFormDX01/primary_doc.xml", "confidence": 0.97, "attributes": { "filing_type": "sec_form_d", "accession_number": "0001601234-26-000004", "title": "Notice of Exempt Offering of Securities" }, "score": { "decision_id": "dec_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "icp_id": "icp_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "relevant": 0.78, "act_now": 0.64, "window_days": 21, "answer": "relevant", "reason_code": "event_relevant_and_timely" } } ], "next_cursor": null, "has_more": false } ``` ### Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `firm_id` | query | string | no | Events of this firm. | | `icp_id` | query | string | no | Events for this ICP, each scored (one `score_signal` billed per scored event). | | `since` | query | string (date-time) | no | Only events observed at or after this time. | | `types` | query | string | no | Comma-separated event types to include, for example `filing,fund_raise`. | | `min_p` | query | number or null | no | With `icp_id`: only events whose `relevant` is at least this. | | `cursor` | query | string | no | Opaque cursor from the `next_cursor` of the previous page. Omit it for the first page. | | `limit` | query | integer | no | Page size, 1 to 200. Default 50. | | `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`. | ### Response fields (200) | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | array of Event | yes | Events, newest first. | | `data[].id` | string | no | The event id. | | `data[].firm_id` | string | no | The firm the event happened to. | | `data[].occurred_at` | string (date-time) | no | When the event happened, per the source. | | `data[].observed_at` | string (date-time) | no | When Verdict ingested the event. | | `data[].source` | enum: "sec_edgar", "gleif", "companies_house", "wikidata" | no | The index source. | | `data[].source_url` | string (uri) | no | Link to the event at the source. | | `data[].confidence` | number | no | Confidence in the event's normalization and firm keying. | | `data[].score` | EventScore | no | The event's score for the requested ICP, or `null` when read without `icp_id`. | | `data[].score.decision_id` | string | yes | The `score_signal` decision that produced this score (billed once). | | `data[].score.icp_id` | string | yes | The ICP profile the event was scored against. | | `data[].score.relevant` | number | yes | Calibrated probability that the event matters for the ICP's offer. | | `data[].score.act_now` | number | yes | Calibrated probability that now is the time to act. | | `data[].score.window_days` | integer or null | yes | Days left to act on the event, or `null` when there is no clear window. | | `data[].score.answer` | enum: "relevant", "not_relevant" | yes | `relevant` applied to the threshold. | | `data[].score.reason_code` | enum: "event_relevant_and_timely", "event_relevant_but_stale", "event_type_not_relevant", "event_routine", "firm_not_in_icp", "event_low_confidence", "event_superseded", "insufficient_event_data" | yes | Why, as a `score_signal` reason code. | | `data[].type` | enum: "filing" | no | Event type `filing`. | | `data[].attributes` | object | no | Attributes of a `filing` event. | | `data[].attributes.filing_type` | enum: "sec_8k", "sec_form_d", "sec_form_4", "sec_s1", "sec_13f", "sec_adv" | yes | The filing sub-type. | | `data[].attributes.accession_number` | string | no | SEC accession number. | | `data[].attributes.items` | array of string | no | 8-K item numbers reported, for `sec_8k`. | | `data[].attributes.title` | string | no | Filing title or form description. | | `data[].attributes.form` | string | no | The form type exactly as the source lists it, for example `8-K/A` or `13F-HR`. | | `data[].attributes.file_number` | string | no | SEC file number, for example `333-299845`. | | `next_cursor` | string or null | yes | Pass as `cursor` to fetch the next page. `null` when this is the last page. | | `has_more` | boolean | yes | Whether another page follows. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `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 Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## GET /v1/sources Tag: Index. Source: https://docs.askverdict.io/reference/list-sources.md | | | | --- | --- | | Call | `GET /v1/sources` | | Price | free | | Scope | `index:read` | | Operation id | `listSources` | ### What it does The sources behind Verdict's index: license and attribution, refresh cadence, and the last successful run. Call it to check data freshness or the attribution to show. Free. Scope: `index:read`. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X GET 'https://api.askverdict.io/v1/sources' \ -H "Authorization: Bearer $VERDICT_API_KEY" ``` ### Example response `200`: A page of sources. ```json title="200 response" { "data": [ { "id": "src_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "source": "gleif", "name": "GLEIF LEI golden copy", "phase": 1, "license": { "kind": "cc0", "name": "CC0 1.0 Universal", "url": "https://creativecommons.org/publicdomain/zero/1.0/", "attribution": null }, "refresh_cadence": "daily", "rate_limit": null, "last_successful_run": { "id": "run_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "status": "succeeded", "started_at": "2026-11-07T03:00:00Z", "finished_at": "2026-11-07T03:21:40Z", "events_upserted": 1874 }, "last_run": { "id": "run_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "status": "succeeded", "started_at": "2026-11-07T03:00:00Z", "finished_at": "2026-11-07T03:21:40Z", "events_upserted": 1874 }, "freshness_seconds": 7200 } ], "next_cursor": null, "has_more": false } ``` ### Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `cursor` | query | string | no | Opaque cursor from the `next_cursor` of the previous page. Omit it for the first page. | | `limit` | query | integer | no | Page size, 1 to 200. Default 50. | | `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`. | ### Response fields (200) | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | array of Source | yes | Sources. | | `data[].id` | string | yes | The source id. | | `data[].source` | enum: "sec_edgar", "gleif", "companies_house", "wikidata" | yes | Stable source slug, as cited in `evidence[].source`. | | `data[].name` | string | yes | Human name. | | `data[].phase` | integer | yes | Release phase that ships the source. | | `data[].license` | object | yes | The source's license. | | `data[].license.kind` | enum: "public_domain", "cc0", "ogl_v3", "cc_by_4_0", "cc_by_3_0_au" | yes | License kind. | | `data[].license.name` | string | yes | License name. | | `data[].license.url` | string (uri) | yes | License text. | | `data[].license.attribution` | string or null | yes | Attribution text to show when you display the data, if required. | | `data[].refresh_cadence` | string | yes | How often Verdict refreshes it, for example `daily`. | | `data[].rate_limit` | string or null | yes | The source's fair-access limit Verdict observes. | | `data[].last_successful_run` | object or null | yes | Latest successful ingestion, or `null` if none yet. | | `data[].last_successful_run.id` | string | yes | The run id. | | `data[].last_successful_run.status` | enum: "running", "succeeded", "partial", "failed" | yes | Run status. | | `data[].last_successful_run.started_at` | string (date-time) | yes | When the run started. | | `data[].last_successful_run.finished_at` | string or null (date-time) | yes | When the run finished. | | `data[].last_successful_run.events_upserted` | integer | yes | Events inserted or updated. | | `data[].last_run` | object or null | yes | Latest ingestion run of any status (it may still be running or have failed), or `null` if none yet. | | `data[].last_run.id` | string | yes | The run id. | | `data[].last_run.status` | enum: "running", "succeeded", "partial", "failed" | yes | Run status. | | `data[].last_run.started_at` | string (date-time) | yes | When the run started. | | `data[].last_run.finished_at` | string or null (date-time) | yes | When the run finished. | | `data[].last_run.events_upserted` | integer | yes | Events inserted or updated. | | `data[].freshness_seconds` | integer or null | yes | Seconds since the latest successful run finished: how fresh this source's data is. `null` if it has never succeeded. | | `next_cursor` | string or null | yes | Pass as `cursor` to fetch the next page. `null` when this is the last page. | | `has_more` | boolean | yes | Whether another page follows. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `403` | The key lacks the scope this operation needs (`forbidden_scope`). | | `429` | Rate limited (`rate_limited`). Wait `Retry-After` seconds. | | `500` | Unexpected server error (`internal`). Not billed; retry with the same `Idempotency-Key`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## GET /v1/watches Tag: Watches. Source: https://docs.askverdict.io/reference/list-watches.md | | | | --- | --- | | Call | `GET /v1/watches` | | Price | free | | Scope | `watches:manage` | | Operation id | `listWatches` | ### What it does Your project's watches with their ICP, `min_p`, event types and status, newest first. Use it to find a watch to inspect or delete. Free. Scope: `watches:manage`. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X GET 'https://api.askverdict.io/v1/watches' \ -H "Authorization: Bearer $VERDICT_API_KEY" ``` ### Example response `200`: A page of watches. ```json title="200 response" { "data": [ { "id": "wch_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "project_id": "prj_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "icp_id": "icp_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "webhook_url": "https://agent.northwind-ops.io/hooks/verdict", "min_p": 0.7, "types": [ "filing", "fund_raise", "officer_change" ], "status": "pending_verification", "created_at": "2026-11-07T14:10:00Z", "updated_at": "2026-11-07T14:10:00Z" } ], "next_cursor": null, "has_more": false } ``` ### Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `cursor` | query | string | no | Opaque cursor from the `next_cursor` of the previous page. Omit it for the first page. | | `limit` | query | integer | no | Page size, 1 to 200. Default 50. | | `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`. | ### Response fields (200) | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | array of Watch | yes | Watches. | | `data[].id` | string | yes | The watch id. | | `data[].project_id` | string | yes | Project the watch belongs to. | | `data[].icp_id` | string | yes | ICP profile events are scored for. | | `data[].webhook_url` | string (uri) | yes | Delivery endpoint. | | `data[].min_p` | number | yes | Minimum `relevant` delivered. | | `data[].types` | array of enum: "filing", "registration", "officer_change", "ownership_change", "lei_change", "address_change", "fund_raise", "insider_trade", "patent_filing", "litigation", "news_mention", "repo_activity", "hiring_aggregate" | yes | Event types watched. | | `data[].status` | enum: "pending_verification", "active", "paused", "deleted" | yes | Lifecycle status. | | `data[].created_at` | string (date-time) | yes | When the watch was created. | | `data[].updated_at` | string (date-time) | yes | When the watch last changed. | | `next_cursor` | string or null | yes | Pass as `cursor` to fetch the next page. `null` when this is the last page. | | `has_more` | boolean | yes | Whether another page follows. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `403` | The key lacks the scope this operation needs (`forbidden_scope`). | | `429` | Rate limited (`rate_limited`). Wait `Retry-After` seconds. | | `500` | Unexpected server error (`internal`). Not billed; retry with the same `Idempotency-Key`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## POST /v1/watches Tag: Watches. Source: https://docs.askverdict.io/reference/create-watch.md | | | | --- | --- | | Call | `POST /v1/watches` | | Price | free to create; each delivered event bills one `score_signal` ($0.005) | | Scope | `watches:manage` | | Operation id | `createWatch` | ### What it does Ask Verdict to push scored events for an ICP to your HTTPS endpoint as they land: each event whose `relevant` is at least `min_p` is delivered, signed with `Verdict-Signature` (see the `scoredEvent` webhook). The URL must answer the `watchChallenge` webhook before deliveries start. Creating a watch is free; **each delivered event bills one `score_signal` ($0.005)**, never per retry. The response includes the signing secret once. Scope: `watches:manage`. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X POST 'https://api.askverdict.io/v1/watches' \ -H "Authorization: Bearer $VERDICT_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H 'Content-Type: application/json' \ -d '{ "icp_id": "icp_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "webhook_url": "https://agent.northwind-ops.io/hooks/verdict", "min_p": 0.7, "types": [ "filing", "fund_raise", "officer_change" ] }' ``` ### Example response `201`: The watch, pending verification, with its signing secret. ```json title="201 response" { "id": "wch_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "project_id": "prj_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "icp_id": "icp_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "webhook_url": "https://agent.northwind-ops.io/hooks/verdict", "min_p": 0.7, "types": [ "filing", "fund_raise", "officer_change" ], "status": "pending_verification", "created_at": "2026-11-07T14:10:00Z", "updated_at": "2026-11-07T14:10:00Z", "signing_secret": "whsec_ExampleExampleExampleExampleExample" } ``` ### 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-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 Schema `CreateWatchRequest`. The watch to create. | Field | Type | Required | Description | | --- | --- | --- | --- | | `icp_id` | string | yes | Score events for this ICP profile. | | `webhook_url` | string (uri) | yes | HTTPS endpoint that receives deliveries. Private and link-local addresses are refused; the URL must answer a challenge at registration. | | `min_p` | number | yes | Deliver only events whose `relevant` is at least this. | | `types` | array of enum: "filing", "registration", "officer_change", "ownership_change", "lei_change", "address_change", "fund_raise", "insider_trade", "patent_filing", "litigation", "news_mention", "repo_activity", "hiring_aggregate" | yes | Event types to watch. | ### Response fields (201) Schema `CreatedWatch`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The watch id. | | `project_id` | string | yes | Project the watch belongs to. | | `icp_id` | string | yes | ICP profile events are scored for. | | `webhook_url` | string (uri) | yes | Delivery endpoint. | | `min_p` | number | yes | Minimum `relevant` delivered. | | `types` | array of enum: "filing", "registration", "officer_change", "ownership_change", "lei_change", "address_change", "fund_raise", "insider_trade", "patent_filing", "litigation", "news_mention", "repo_activity", "hiring_aggregate" | yes | Event types watched. | | `status` | enum: "pending_verification", "active", "paused", "deleted" | yes | Lifecycle status. | | `created_at` | string (date-time) | yes | When the watch was created. | | `updated_at` | string (date-time) | yes | When the watch last changed. | | `signing_secret` | string | yes | Secret for verifying `Verdict-Signature` on deliveries. Shown once; store it now. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `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`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## GET /v1/watches/{id} Tag: Watches. Source: https://docs.askverdict.io/reference/get-watch.md | | | | --- | --- | | Call | `GET /v1/watches/{id}` | | Price | free | | Scope | `watches:manage` | | Operation id | `getWatch` | ### What it does One watch with its ICP, `min_p`, event types and status. Call it after creating a watch to see whether the URL passed the challenge (`active`) or is still `pending_verification`. Free. Scope: `watches:manage`. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X GET 'https://api.askverdict.io/v1/watches/wch_01JC3Z4Y8R9K2M5N7P1Q3S5T7V' \ -H "Authorization: Bearer $VERDICT_API_KEY" ``` ### Example response `200`: The watch. ```json title="200 response" { "id": "wch_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "project_id": "prj_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "icp_id": "icp_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "webhook_url": "https://agent.northwind-ops.io/hooks/verdict", "min_p": 0.7, "types": [ "filing", "fund_raise", "officer_change" ], "status": "pending_verification", "created_at": "2026-11-07T14:10:00Z", "updated_at": "2026-11-07T14:10:00Z" } ``` ### Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `id` | path | string | yes | The watch id. | | `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`. | ### Response fields (200) Schema `Watch`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The watch id. | | `project_id` | string | yes | Project the watch belongs to. | | `icp_id` | string | yes | ICP profile events are scored for. | | `webhook_url` | string (uri) | yes | Delivery endpoint. | | `min_p` | number | yes | Minimum `relevant` delivered. | | `types` | array of enum: "filing", "registration", "officer_change", "ownership_change", "lei_change", "address_change", "fund_raise", "insider_trade", "patent_filing", "litigation", "news_mention", "repo_activity", "hiring_aggregate" | yes | Event types watched. | | `status` | enum: "pending_verification", "active", "paused", "deleted" | yes | Lifecycle status. | | `created_at` | string (date-time) | yes | When the watch was created. | | `updated_at` | string (date-time) | yes | When the watch last changed. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `403` | The key lacks the scope this operation needs (`forbidden_scope`). | | `404` | The resource does not exist in this organization (`not_found`). | | `429` | Rate limited (`rate_limited`). Wait `Retry-After` seconds. | | `500` | Unexpected server error (`internal`). Not billed; retry with the same `Idempotency-Key`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## DELETE /v1/watches/{id} Tag: Watches. Source: https://docs.askverdict.io/reference/delete-watch.md | | | | --- | --- | | Call | `DELETE /v1/watches/{id}` | | Price | free | | Scope | `watches:manage` | | Operation id | `deleteWatch` | ### What it does Stop a watch. Pending deliveries are cancelled and never billed; delivery history stays readable. Free. Scope: `watches:manage`. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X DELETE 'https://api.askverdict.io/v1/watches/wch_01JC3Z4Y8R9K2M5N7P1Q3S5T7V' \ -H "Authorization: Bearer $VERDICT_API_KEY" ``` ### Example response `200`: The watch, now `deleted`. ```json title="200 response" { "id": "wch_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "project_id": "prj_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "icp_id": "icp_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "webhook_url": "https://agent.northwind-ops.io/hooks/verdict", "min_p": 0.7, "types": [ "filing", "fund_raise", "officer_change" ], "status": "deleted", "created_at": "2026-11-07T14:10:00Z", "updated_at": "2026-11-07T14:10:00Z" } ``` ### Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `id` | path | string | yes | The watch id. | | `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`. | ### Response fields (200) Schema `Watch`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The watch id. | | `project_id` | string | yes | Project the watch belongs to. | | `icp_id` | string | yes | ICP profile events are scored for. | | `webhook_url` | string (uri) | yes | Delivery endpoint. | | `min_p` | number | yes | Minimum `relevant` delivered. | | `types` | array of enum: "filing", "registration", "officer_change", "ownership_change", "lei_change", "address_change", "fund_raise", "insider_trade", "patent_filing", "litigation", "news_mention", "repo_activity", "hiring_aggregate" | yes | Event types watched. | | `status` | enum: "pending_verification", "active", "paused", "deleted" | yes | Lifecycle status. | | `created_at` | string (date-time) | yes | When the watch was created. | | `updated_at` | string (date-time) | yes | When the watch last changed. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `403` | The key lacks the scope this operation needs (`forbidden_scope`). | | `404` | The resource does not exist in this organization (`not_found`). | | `429` | Rate limited (`rate_limited`). Wait `Retry-After` seconds. | | `500` | Unexpected server error (`internal`). Not billed; retry with the same `Idempotency-Key`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## GET /v1/watches/{id}/deliveries Tag: Watches. Source: https://docs.askverdict.io/reference/list-watch-deliveries.md | | | | --- | --- | | Call | `GET /v1/watches/{id}/deliveries` | | Price | free | | Scope | `watches:manage` | | Operation id | `listWatchDeliveries` | ### What it does Delivery attempts for one watch, newest first, with status, attempts and the last response code. Use it to debug your endpoint or to replay missed events by id. Free. Scope: `watches:manage`. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X GET 'https://api.askverdict.io/v1/watches/wch_01JC3Z4Y8R9K2M5N7P1Q3S5T7V/deliveries' \ -H "Authorization: Bearer $VERDICT_API_KEY" ``` ### Example response `200`: A page of deliveries. ```json title="200 response" { "data": [ { "id": "dlv_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "watch_id": "wch_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "event_id": "evt_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "decision_id": "dec_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "status": "delivered", "attempts": 1, "last_attempt_at": "2026-11-07T15:00:04Z", "last_response_status": 200, "next_attempt_at": null, "delivered_at": "2026-11-07T15:00:04Z", "created_at": "2026-11-07T15:00:03Z" } ], "next_cursor": null, "has_more": false } ``` ### Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `id` | path | string | yes | The watch id. | | `cursor` | query | string | no | Opaque cursor from the `next_cursor` of the previous page. Omit it for the first page. | | `limit` | query | integer | no | Page size, 1 to 200. Default 50. | | `status` | query | enum: "pending", "retrying", "delivered", "failed" | no | Only deliveries with this status. | | `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`. | ### Response fields (200) | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | array of Delivery | yes | Deliveries, newest first. | | `data[].id` | string | yes | The delivery id; also the `id` of the delivered payload. | | `data[].watch_id` | string | yes | The watch. | | `data[].event_id` | string | yes | The delivered event. | | `data[].decision_id` | string or null | yes | The `score_signal` decision behind it. | | `data[].status` | enum: "pending", "retrying", "delivered", "failed" | yes | Delivery status. | | `data[].attempts` | integer | yes | Attempts so far. | | `data[].last_attempt_at` | string or null (date-time) | yes | Time of the last attempt. | | `data[].last_response_status` | integer or null | yes | HTTP status of the last attempt, if a response arrived. | | `data[].next_attempt_at` | string or null (date-time) | yes | Time of the next retry, while `retrying`. | | `data[].delivered_at` | string or null (date-time) | yes | When a 2xx was received. | | `data[].created_at` | string (date-time) | yes | When the delivery was queued. | | `next_cursor` | string or null | yes | Pass as `cursor` to fetch the next page. `null` when this is the last page. | | `has_more` | boolean | yes | Whether another page follows. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `403` | The key lacks the scope this operation needs (`forbidden_scope`). | | `404` | The resource does not exist in this organization (`not_found`). | | `429` | Rate limited (`rate_limited`). Wait `Retry-After` seconds. | | `500` | Unexpected server error (`internal`). Not billed; retry with the same `Idempotency-Key`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## POST (scoredEvent) Tag: Watches. Source: https://docs.askverdict.io/reference/scored-event-webhook.md | | | | --- | --- | | Call | `POST (scoredEvent)` | | Price | bills one `score_signal` ($0.005) when delivered, never per retry | | Scope | none (Verdict calls you; verify `Verdict-Signature`) | | Operation id | `scoredEventWebhook` | ### What it does Verdict POSTs this to a watch's `webhook_url` for each event whose `relevant` is at least `min_p`. Verify `Verdict-Signature` against the raw body before parsing, dedupe on `id`, and answer 2xx within 10 seconds; anything else is retried with exponential backoff for 24 hours. Billed once (one `score_signal`) when delivered, never per attempt. ### Example request Verdict sends this request to your endpoint. The curl below replays it against your own server. ```bash title="Request" curl -X POST 'https://your-app.example.com/verdict/webhook' \ -H 'Verdict-Signature: t=1794060000,v1=5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e' \ -H 'Content-Type: application/json' \ -d '{ "id": "dlv_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "type": "event.scored", "created_at": "2026-11-07T15:00:03Z", "watch_id": "wch_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "event": { "id": "evt_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "type": "filing", "firm_id": "frm_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "occurred_at": "2026-11-03T21:15:00Z", "observed_at": "2026-11-04T02:05:00Z", "source": "sec_edgar", "source_url": "https://www.sec.gov/Archives/edgar/data/1601234/000160123426000004/xslFormDX01/primary_doc.xml", "confidence": 0.97, "attributes": { "filing_type": "sec_form_d", "accession_number": "0001601234-26-000004", "title": "Notice of Exempt Offering of Securities" }, "score": { "decision_id": "dec_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "icp_id": "icp_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "relevant": 0.78, "act_now": 0.64, "window_days": 21, "answer": "relevant", "reason_code": "event_relevant_and_timely" } }, "score": { "decision_id": "dec_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "icp_id": "icp_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "relevant": 0.78, "act_now": 0.64, "window_days": 21, "answer": "relevant", "reason_code": "event_relevant_and_timely" } }' ``` ### Example response `200`: Any 2xx acknowledges the delivery. ```http title="200 response" HTTP/1.1 200 ``` ### Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `verdict-signature` | header | string | yes | `t=,v1=." with the watch's signing secret>`. Several `v1` values appear while a secret rotation overlaps. Reject the delivery if no `v1` matches or `t` is more than 5 minutes from your clock. | ### Request body Schema `ScoredEventDelivery`. The scored event. | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | Delivery id; dedupe on it, retries reuse it. | | `type` | enum: "event.scored" | yes | Payload type. | | `created_at` | string (date-time) | yes | When the delivery was created. | | `watch_id` | string | yes | The watch. | | `event` | Event | yes | Something that happened to a firm, normalized to one schema; `attributes` are typed per `type` (one of `filing`, `registration`, `officer_change`, `ownership_change`, `lei_change`, `address_change`, `fund_raise`, `insider_trade`, `patent_filing`, `litigation`, `news_mention`, `repo_activity`, `hiring_aggregate`). | | `event.id` | string | no | The event id. | | `event.firm_id` | string | no | The firm the event happened to. | | `event.occurred_at` | string (date-time) | no | When the event happened, per the source. | | `event.observed_at` | string (date-time) | no | When Verdict ingested the event. | | `event.source` | enum: "sec_edgar", "gleif", "companies_house", "wikidata" | no | The index source. | | `event.source_url` | string (uri) | no | Link to the event at the source. | | `event.confidence` | number | no | Confidence in the event's normalization and firm keying. | | `event.score` | EventScore | no | The event's score for the requested ICP, or `null` when read without `icp_id`. | | `event.score.decision_id` | string | yes | The `score_signal` decision that produced this score (billed once). | | `event.score.icp_id` | string | yes | The ICP profile the event was scored against. | | `event.score.relevant` | number | yes | Calibrated probability that the event matters for the ICP's offer. | | `event.score.act_now` | number | yes | Calibrated probability that now is the time to act. | | `event.score.window_days` | integer or null | yes | Days left to act on the event, or `null` when there is no clear window. | | `event.score.answer` | enum: "relevant", "not_relevant" | yes | `relevant` applied to the threshold. | | `event.score.reason_code` | enum: "event_relevant_and_timely", "event_relevant_but_stale", "event_type_not_relevant", "event_routine", "firm_not_in_icp", "event_low_confidence", "event_superseded", "insufficient_event_data" | yes | Why, as a `score_signal` reason code. | | `event.type` | enum: "filing" | no | Event type `filing`. | | `event.attributes` | object | no | Attributes of a `filing` event. | | `event.attributes.filing_type` | enum: "sec_8k", "sec_form_d", "sec_form_4", "sec_s1", "sec_13f", "sec_adv" | yes | The filing sub-type. | | `event.attributes.accession_number` | string | no | SEC accession number. | | `event.attributes.items` | array of string | no | 8-K item numbers reported, for `sec_8k`. | | `event.attributes.title` | string | no | Filing title or form description. | | `event.attributes.form` | string | no | The form type exactly as the source lists it, for example `8-K/A` or `13F-HR`. | | `event.attributes.file_number` | string | no | SEC file number, for example `333-299845`. | | `score` | EventScore and any | yes | | | `score.decision_id` | string | yes | The `score_signal` decision that produced this score (billed once). | | `score.icp_id` | string | yes | The ICP profile the event was scored against. | | `score.relevant` | number | yes | Calibrated probability that the event matters for the ICP's offer. | | `score.act_now` | number | yes | Calibrated probability that now is the time to act. | | `score.window_days` | integer or null | yes | Days left to act on the event, or `null` when there is no clear window. | | `score.answer` | enum: "relevant", "not_relevant" | yes | `relevant` applied to the threshold. | | `score.reason_code` | enum: "event_relevant_and_timely", "event_relevant_but_stale", "event_type_not_relevant", "event_routine", "firm_not_in_icp", "event_low_confidence", "event_superseded", "insufficient_event_data" | yes | Why, as a `score_signal` reason code. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## POST (watchChallenge) Tag: Watches. Source: https://docs.askverdict.io/reference/watch-challenge-webhook.md | | | | --- | --- | | Call | `POST (watchChallenge)` | | Price | free | | Scope | none (Verdict calls you; verify `Verdict-Signature`) | | Operation id | `watchChallengeWebhook` | ### What it does Sent once when a watch is created. Respond 200 with `{"challenge": }` within 10 seconds; the watch becomes `active` only then. Signed with `Verdict-Signature` like deliveries. ### Example request Verdict sends this request to your endpoint. The curl below replays it against your own server. ```bash title="Request" curl -X POST 'https://your-app.example.com/verdict/webhook' \ -H 'Verdict-Signature: t=1794060000,v1=5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e' \ -H 'Content-Type: application/json' \ -d '{ "type": "webhook.challenge", "watch_id": "wch_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "challenge": "cccccccccccccccccccccccccccccccccccccccc" }' ``` ### Example response `200`: The challenge, echoed. ```json title="200 response" { "challenge": "cccccccccccccccccccccccccccccccccccccccc" } ``` ### Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `verdict-signature` | header | string | yes | `t=,v1=." with the watch's signing secret>`. Several `v1` values appear while a secret rotation overlaps. Reject the delivery if no `v1` matches or `t` is more than 5 minutes from your clock. | ### Request body Schema `WebhookChallenge`. The challenge. | Field | Type | Required | Description | | --- | --- | --- | --- | | `type` | enum: "webhook.challenge" | yes | Payload type. | | `watch_id` | string | yes | The watch being registered. | | `challenge` | string | yes | Random string; respond 200 with `{"challenge": }` within 10 seconds. | ### Response fields (200) | Field | Type | Required | Description | | --- | --- | --- | --- | | `challenge` | string | yes | The `challenge` value, echoed. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## POST /v1/outcomes Tag: Outcomes. Source: https://docs.askverdict.io/reference/create-outcome.md | | | | --- | --- | | Call | `POST /v1/outcomes` | | Price | free | | Scope | `outcomes:write` | | Operation id | `createOutcome` | ### What it does Tell Verdict what happened after a decision (bounced, replied, meeting_booked, approved, skipped, or `custom:`). Outcomes calibrate `p` and move your thresholds. Free and unlimited. Idempotent on `(decision_id, outcome, observed_at)`: a repeat returns 200 with the stored outcome. The SDK reports `approved` and `skipped` for you and tags sends with the `X-Verdict-Decision` email header (or a vendor custom field) so the vendor receivers can report the rest. Scope: `outcomes:write`. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="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" } }' ``` ### Example response `200`: The same outcome was already recorded; nothing changed. ```json title="200 response" { "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" } ``` `201`: The outcome was recorded. ```json title="201 response" { "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" } ``` ### 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-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 Schema `CreateOutcomeRequest`. The outcome. | Field | Type | Required | Description | | --- | --- | --- | --- | | `decision_id` | string | yes | The decision the outcome belongs to. | | `outcome` | enum: "bounced", "delivered", "replied", "interested", "not_interested", "meeting_booked", "meeting_held", "approved", "skipped", "reopened", "converted" or string | yes | The outcome: a built-in type or `custom:`. | | `observed_at` | string (date-time) | yes | When the outcome happened. | | `meta` | object | no | Optional metadata: up to 20 keys of at most 64 characters, scalar values. No personal data. | ### Response fields (200) Schema `Outcome`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The outcome id. | | `decision_id` | string | yes | The decision it belongs to. | | `outcome` | enum: "bounced", "delivered", "replied", "interested", "not_interested", "meeting_booked", "meeting_held", "approved", "skipped", "reopened", "converted" or string | yes | The outcome: a built-in type or `custom:`. | | `observed_at` | string (date-time) | yes | When the outcome happened. | | `meta` | object or null | yes | Optional metadata: up to 20 keys of at most 64 characters, scalar values. No personal data. | | `created_at` | string (date-time) | yes | When Verdict recorded it. | ### Response fields (201) Schema `Outcome`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The outcome id. | | `decision_id` | string | yes | The decision it belongs to. | | `outcome` | enum: "bounced", "delivered", "replied", "interested", "not_interested", "meeting_booked", "meeting_held", "approved", "skipped", "reopened", "converted" or string | yes | The outcome: a built-in type or `custom:`. | | `observed_at` | string (date-time) | yes | When the outcome happened. | | `meta` | object or null | yes | Optional metadata: up to 20 keys of at most 64 characters, scalar values. No personal data. | | `created_at` | string (date-time) | yes | When Verdict recorded it. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `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`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## POST /v1/webhooks/instantly/{receiver_token} Tag: Inbound webhooks. Source: https://docs.askverdict.io/reference/receive-instantly-webhook.md | | | | --- | --- | | Call | `POST /v1/webhooks/instantly/{receiver_token}` | | Price | free | | Scope | none (public endpoint) | | Operation id | `receiveInstantlyWebhook` | ### What it does Point Instantly's webhook at this URL (copy it from the console) and Verdict turns its events into outcomes on the decision you tagged when sending. Not called by agents. The body is Instantly's own payload, passed through unchanged. Verification: Instantly does not sign webhooks: the per-org `receiver_token` in the path authenticates the call, plus Instantly's published source IPs where available. Mapping: `email_sent` to `delivered`, `email_bounced` to `bounced`, `reply_received` to `replied`, `lead_interested` to `interested`, `lead_not_interested` to `not_interested`, `lead_meeting_booked` to `meeting_booked`. Idempotent on the vendor's event id. Free. Verdict never holds your Instantly credentials. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X POST 'https://api.askverdict.io/v1/webhooks/instantly/%3Creceiver_token%3E' \ -H 'Content-Type: application/json' \ -d '{ "event_type": "reply_received", "timestamp": "2026-11-06T09:12:00Z", "campaign_id": "cmp_example", "lead_email": "dana@northfieldcapital.com", "verdict_decision_id": "dec_01JC3Z4Y8R9K2M5N7P1Q3S5T7V" }' ``` ### Example response `200`: The payload was verified and processed. ```json title="200 response" { "received": true, "duplicate": false, "outcome_ids": [ "out_01JC3Z4Y8R9K2M5N7P1Q3S5T7V" ] } ``` ### Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `receiver_token` | path | string | yes | Per-org receiver token issued in the console; identifies your organization. Treat the full URL as a secret. | ### Request body Schema `VendorWebhookBody`. Instantly's webhook payload. Type: `object`. ### Response fields (200) Schema `WebhookReceipt`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `received` | enum: true | yes | The payload was verified and accepted. | | `duplicate` | boolean | yes | True when this vendor event id was already processed. | | `outcome_ids` | array of string | yes | Outcomes created from the payload; empty when it maps to none. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `404` | The resource does not exist in this organization (`not_found`). | | `429` | Rate limited (`rate_limited`). Wait `Retry-After` seconds. | | `500` | Unexpected server error (`internal`). Not billed; retry with the same `Idempotency-Key`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## POST /v1/webhooks/smartlead/{receiver_token} Tag: Inbound webhooks. Source: https://docs.askverdict.io/reference/receive-smartlead-webhook.md | | | | --- | --- | | Call | `POST /v1/webhooks/smartlead/{receiver_token}` | | Price | free | | Scope | none (public endpoint) | | Operation id | `receiveSmartleadWebhook` | ### What it does Point Smartlead's webhook at this URL (copy it from the console) and Verdict turns its events into outcomes on the decision you tagged when sending. Not called by agents. The body is Smartlead's own payload, passed through unchanged. Verification: Smartlead's webhook secret is checked when configured; otherwise the per-org `receiver_token` in the path authenticates the call. Mapping: `EMAIL_SENT` to `delivered`, `EMAIL_BOUNCE` to `bounced`, `EMAIL_REPLY` to `replied`, lead category changes to `interested` or `not_interested`. Idempotent on the vendor's event id. Free. Verdict never holds your Smartlead credentials. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X POST 'https://api.askverdict.io/v1/webhooks/smartlead/%3Creceiver_token%3E' \ -H 'Content-Type: application/json' \ -d '{ "event_type": "EMAIL_REPLY", "event_timestamp": "2026-11-06T09:12:00Z", "campaign_id": 4821, "custom_fields": { "verdict_decision_id": "dec_01JC3Z4Y8R9K2M5N7P1Q3S5T7V" } }' ``` ### Example response `200`: The payload was verified and processed. ```json title="200 response" { "received": true, "duplicate": false, "outcome_ids": [ "out_01JC3Z4Y8R9K2M5N7P1Q3S5T7V" ] } ``` ### Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `receiver_token` | path | string | yes | Per-org receiver token issued in the console; identifies your organization. Treat the full URL as a secret. | ### Request body Schema `VendorWebhookBody`. Smartlead's webhook payload. Type: `object`. ### Response fields (200) Schema `WebhookReceipt`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `received` | enum: true | yes | The payload was verified and accepted. | | `duplicate` | boolean | yes | True when this vendor event id was already processed. | | `outcome_ids` | array of string | yes | Outcomes created from the payload; empty when it maps to none. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `404` | The resource does not exist in this organization (`not_found`). | | `429` | Rate limited (`rate_limited`). Wait `Retry-After` seconds. | | `500` | Unexpected server error (`internal`). Not billed; retry with the same `Idempotency-Key`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## POST /v1/webhooks/calcom/{receiver_token} Tag: Inbound webhooks. Source: https://docs.askverdict.io/reference/receive-calcom-webhook.md | | | | --- | --- | | Call | `POST /v1/webhooks/calcom/{receiver_token}` | | Price | free | | Scope | none (public endpoint) | | Operation id | `receiveCalcomWebhook` | ### What it does Point Cal.com's webhook at this URL (copy it from the console) and Verdict turns its events into outcomes on the decision you tagged when sending. Not called by agents. The body is Cal.com's own payload, passed through unchanged. Verification: `X-Cal-Signature-256` (HMAC-SHA256 of the raw body with the webhook secret you set in Cal.com and in the console) is verified before parsing. Mapping: `BOOKING_CREATED` to `meeting_booked`, `MEETING_ENDED` to `meeting_held`, `BOOKING_CANCELLED` is recorded but creates no outcome. Idempotent on the vendor's event id. Free. Verdict never holds your Cal.com credentials. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X POST 'https://api.askverdict.io/v1/webhooks/calcom/%3Creceiver_token%3E' \ -H 'Content-Type: application/json' \ -d '{ "triggerEvent": "BOOKING_CREATED", "createdAt": "2026-11-07T10:00:00Z", "payload": { "metadata": { "verdict_decision_id": "dec_01JC3Z4Y8R9K2M5N7P1Q3S5T7V" } } }' ``` ### Example response `200`: The payload was verified and processed. ```json title="200 response" { "received": true, "duplicate": false, "outcome_ids": [ "out_01JC3Z4Y8R9K2M5N7P1Q3S5T7V" ] } ``` ### Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `receiver_token` | path | string | yes | Per-org receiver token issued in the console; identifies your organization. Treat the full URL as a secret. | ### Request body Schema `VendorWebhookBody`. Cal.com's webhook payload. Type: `object`. ### Response fields (200) Schema `WebhookReceipt`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `received` | enum: true | yes | The payload was verified and accepted. | | `duplicate` | boolean | yes | True when this vendor event id was already processed. | | `outcome_ids` | array of string | yes | Outcomes created from the payload; empty when it maps to none. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `404` | The resource does not exist in this organization (`not_found`). | | `429` | Rate limited (`rate_limited`). Wait `Retry-After` seconds. | | `500` | Unexpected server error (`internal`). Not billed; retry with the same `Idempotency-Key`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## POST /v1/webhooks/calendly/{receiver_token} Tag: Inbound webhooks. Source: https://docs.askverdict.io/reference/receive-calendly-webhook.md | | | | --- | --- | | Call | `POST /v1/webhooks/calendly/{receiver_token}` | | Price | free | | Scope | none (public endpoint) | | Operation id | `receiveCalendlyWebhook` | ### What it does Point Calendly's webhook at this URL (copy it from the console) and Verdict turns its events into outcomes on the decision you tagged when sending. Not called by agents. The body is Calendly's own payload, passed through unchanged. Verification: `Calendly-Webhook-Signature` (`t=...,v1=...` with the subscription's signing key) is verified before parsing. Mapping: `invitee.created` to `meeting_booked`; `invitee.canceled` is recorded but creates no outcome. Idempotent on the vendor's event id. Free. Verdict never holds your Calendly credentials. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X POST 'https://api.askverdict.io/v1/webhooks/calendly/%3Creceiver_token%3E' \ -H 'Content-Type: application/json' \ -d '{ "event": "invitee.created", "created_at": "2026-11-07T10:00:00Z", "payload": { "tracking": { "utm_content": "dec_01JC3Z4Y8R9K2M5N7P1Q3S5T7V" } } }' ``` ### Example response `200`: The payload was verified and processed. ```json title="200 response" { "received": true, "duplicate": false, "outcome_ids": [ "out_01JC3Z4Y8R9K2M5N7P1Q3S5T7V" ] } ``` ### Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `receiver_token` | path | string | yes | Per-org receiver token issued in the console; identifies your organization. Treat the full URL as a secret. | ### Request body Schema `VendorWebhookBody`. Calendly's webhook payload. Type: `object`. ### Response fields (200) Schema `WebhookReceipt`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `received` | enum: true | yes | The payload was verified and accepted. | | `duplicate` | boolean | yes | True when this vendor event id was already processed. | | `outcome_ids` | array of string | yes | Outcomes created from the payload; empty when it maps to none. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `404` | The resource does not exist in this organization (`not_found`). | | `429` | Rate limited (`rate_limited`). Wait `Retry-After` seconds. | | `500` | Unexpected server error (`internal`). Not billed; retry with the same `Idempotency-Key`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## POST /v1/webhooks/stripe Tag: Inbound webhooks. Source: https://docs.askverdict.io/reference/receive-stripe-webhook.md | | | | --- | --- | | Call | `POST /v1/webhooks/stripe` | | Price | free | | Scope | none (public endpoint) | | Operation id | `receiveStripeWebhook` | ### What it does Stripe calls this endpoint; agents never do. Operator setup: in the Stripe dashboard (test mode for staging, live mode for production) add a webhook endpoint with this URL, subscribe it to `checkout.session.completed`, `checkout.session.async_payment_succeeded`, `checkout.session.async_payment_failed` and `checkout.session.expired`, and store its signing secret (`whsec_...`) in Doppler as `STRIPE_WEBHOOK_SECRET`. Verdict verifies `Stripe-Signature` over the raw body before parsing anything (401 `unauthorized` on failure, nothing stored), reads the organization from the event's `metadata.org_id` and checks it against the pending pack named by `metadata.pack_id` (400 `invalid_request` when no organization resolves, nothing stored), records the event id, and credits the pack once when payment is confirmed. Idempotent on the Stripe event id: a replay returns 200 with `duplicate: true` and credits nothing; the same id with a different payload is refused with 400. Other event types are recorded and ignored. Free. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X POST 'https://api.askverdict.io/v1/webhooks/stripe' \ -H 'Stripe-Signature: t=1794060000,v1=5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e' \ -H 'Content-Type: application/json' \ -d '{ "id": "evt_1QExampleCheckoutCompleted", "object": "event", "type": "checkout.session.completed", "livemode": false, "data": { "object": { "id": "cs_test_example", "object": "checkout.session", "mode": "payment", "payment_status": "paid", "amount_total": 5000, "currency": "usd", "client_reference_id": "org_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "metadata": { "org_id": "org_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "pack_id": "pck_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "kind": "pack" } } } }' ``` ### Example response `200`: The event was verified and recorded. ```json title="200 response" { "received": true, "duplicate": false } ``` ### Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `stripe-signature` | header | string | yes | Stripe's signature over the raw body (`t=,v1=`), computed with the endpoint's signing secret. A missing or wrong signature, or a timestamp more than 5 minutes off, is refused with 401 `unauthorized` and nothing is stored. | ### Request body Schema `StripeWebhookBody`. The Stripe event, unchanged. Type: `object`. ### Response fields (200) Schema `StripeWebhookReceipt`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `received` | enum: true | yes | The event was verified and recorded. | | `duplicate` | boolean | yes | True when this Stripe event id was already recorded; nothing changed. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `404` | The resource does not exist in this organization (`not_found`). | | `429` | Rate limited (`rate_limited`). Wait `Retry-After` seconds. | | `500` | Unexpected server error (`internal`). Not billed; retry with the same `Idempotency-Key`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## GET /v1/icps Tag: ICP profiles. Source: https://docs.askverdict.io/reference/list-icps.md | | | | --- | --- | | Call | `GET /v1/icps` | | Price | free | | Scope | `decisions:write` | | Operation id | `listIcps` | ### What it does Your project's ICP profiles, newest first, each with its segment, summary status and thresholds. Call it to find the `icp_id` to pass to decisions. Free. Scope: `decisions:write`. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X GET 'https://api.askverdict.io/v1/icps' \ -H "Authorization: Bearer $VERDICT_API_KEY" ``` ### Example response `200`: A page of profiles. ```json title="200 response" { "data": [ { "id": "icp_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "project_id": "prj_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "name": "RIAs under $1B", "description": "US registered investment advisers with under $1B in assets and fewer than 50 staff. We sell trade reconciliation software to their operations lead.", "filters": { "firm_types": [ "investment_adviser" ], "size_bands": [ "micro", "small" ], "countries": [ "US" ] }, "segment": { "industry_group": "asset_management", "size_band": "small" }, "summary_status": "ready", "thresholds": { "qualify_firm": { "value": 0.6, "source": "calibrated" }, "score_signal": { "value": 0.55, "source": "calibrated" }, "pick_person": { "value": 0.5, "source": "calibrated" }, "triage_reply": { "value": 0.65, "source": "calibrated" }, "right_person": { "value": 0.7, "source": "custom" }, "should_send": { "value": 0.6, "source": "calibrated" } }, "created_at": "2026-11-07T14:00:00Z", "updated_at": "2026-11-07T14:20:00Z" } ], "next_cursor": null, "has_more": false } ``` ### Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `cursor` | query | string | no | Opaque cursor from the `next_cursor` of the previous page. Omit it for the first page. | | `limit` | query | integer | no | Page size, 1 to 200. Default 50. | | `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`. | ### Response fields (200) | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | array of Icp | yes | ICP profiles. | | `data[].id` | string | yes | The ICP profile id. | | `data[].project_id` | string | yes | Project the profile belongs to. | | `data[].name` | string | yes | Name. | | `data[].description` | string | yes | Plain-text description. | | `data[].filters` | IcpFilters | yes | Optional structured filters that sharpen the plain-text profile. | | `data[].filters.firm_types` | array of enum: "operating_company", "investment_adviser", "broker_dealer", "investment_fund", "nonprofit", "public_body", "other", "unknown" | no | Firm types to target. | | `data[].filters.size_bands` | array of enum: "micro", "small", "medium", "large", "enterprise", "unknown" | no | Size bands to target. | | `data[].filters.countries` | array of string | no | Headquarters countries to target. | | `data[].filters.exclude_countries` | array of string | no | Headquarters countries to exclude. | | `data[].filters.exclude_domains` | array of string | no | Firms to exclude by domain (for example existing customers). | | `data[].filters.event_types` | array of enum: "filing", "registration", "officer_change", "ownership_change", "lei_change", "address_change", "fund_raise", "insider_trade", "patent_filing", "litigation", "news_mention", "repo_activity", "hiring_aggregate" | no | Event types that signal buying intent for this ICP. | | `data[].segment` | object | yes | Calibration segment of the profile (spec 6.4). | | `data[].segment.industry_group` | string or null | yes | Industry group assigned at summarization. | | `data[].segment.size_band` | enum: "micro", "small", "medium", "large", "enterprise", "unknown", null | yes | Size band assigned at summarization. | | `data[].summary_status` | enum: "pending", "ready", "failed" | yes | Status of the one-time summarization. | | `data[].thresholds` | Thresholds | yes | Decision thresholds, one per decision. | | `data[].thresholds.qualify_firm` | object | yes | Threshold for `qualify_firm`. | | `data[].thresholds.score_signal` | object | yes | Threshold for `score_signal`. | | `data[].thresholds.pick_person` | object | yes | Threshold for `pick_person`. | | `data[].thresholds.triage_reply` | object | yes | Threshold for `triage_reply`. | | `data[].thresholds.right_person` | object | yes | Threshold for `right_person`. | | `data[].thresholds.should_send` | object | yes | Threshold for `should_send`. | | `data[].created_at` | string (date-time) | yes | When the profile was created. | | `data[].updated_at` | string (date-time) | yes | When the profile last changed. | | `next_cursor` | string or null | yes | Pass as `cursor` to fetch the next page. `null` when this is the last page. | | `has_more` | boolean | yes | Whether another page follows. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `403` | The key lacks the scope this operation needs (`forbidden_scope`). | | `429` | Rate limited (`rate_limited`). Wait `Retry-After` seconds. | | `500` | Unexpected server error (`internal`). Not billed; retry with the same `Idempotency-Key`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## POST /v1/icps Tag: ICP profiles. Source: https://docs.askverdict.io/reference/create-icp.md | | | | --- | --- | | Call | `POST /v1/icps` | | Price | free | | Scope | `decisions:write` | | Operation id | `createIcp` | ### What it does Describe who you sell to and what you sell, in plain text plus optional filters. Every decision is made against an ICP profile, so create one first. Verdict summarizes it once (`summary_status`), assigns a calibration segment and sets calibrated default thresholds. Free. Scope: `decisions:write`. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X POST 'https://api.askverdict.io/v1/icps' \ -H "Authorization: Bearer $VERDICT_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H 'Content-Type: application/json' \ -d '{ "name": "RIAs under $1B", "description": "US registered investment advisers with under $1B in assets and fewer than 50 staff. We sell trade reconciliation software to their operations lead.", "filters": { "firm_types": [ "investment_adviser" ], "size_bands": [ "micro", "small" ], "countries": [ "US" ] } }' ``` ### Example response `201`: The new profile. ```json title="201 response" { "id": "icp_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "project_id": "prj_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "name": "RIAs under $1B", "description": "US registered investment advisers with under $1B in assets and fewer than 50 staff. We sell trade reconciliation software to their operations lead.", "filters": { "firm_types": [ "investment_adviser" ], "size_bands": [ "micro", "small" ], "countries": [ "US" ] }, "segment": { "industry_group": "asset_management", "size_band": "small" }, "summary_status": "pending", "thresholds": { "qualify_firm": { "value": 0.6, "source": "calibrated" }, "score_signal": { "value": 0.55, "source": "calibrated" }, "pick_person": { "value": 0.5, "source": "calibrated" }, "triage_reply": { "value": 0.65, "source": "calibrated" }, "right_person": { "value": 0.7, "source": "custom" }, "should_send": { "value": 0.6, "source": "calibrated" } }, "created_at": "2026-11-07T14:00:00Z", "updated_at": "2026-11-07T14:20:00Z" } ``` ### 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-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 Schema `CreateIcpRequest`. The profile. | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | yes | Short name, for example `RIAs under $1B`. | | `description` | string | yes | Plain-text description of who you sell to and what you sell. At most 8192 bytes of UTF-8. | | `filters` | IcpFilters | no | Optional structured filters that sharpen the plain-text profile. | | `filters.firm_types` | array of enum: "operating_company", "investment_adviser", "broker_dealer", "investment_fund", "nonprofit", "public_body", "other", "unknown" | no | Firm types to target. | | `filters.size_bands` | array of enum: "micro", "small", "medium", "large", "enterprise", "unknown" | no | Size bands to target. | | `filters.countries` | array of string | no | Headquarters countries to target. | | `filters.exclude_countries` | array of string | no | Headquarters countries to exclude. | | `filters.exclude_domains` | array of string | no | Firms to exclude by domain (for example existing customers). | | `filters.event_types` | array of enum: "filing", "registration", "officer_change", "ownership_change", "lei_change", "address_change", "fund_raise", "insider_trade", "patent_filing", "litigation", "news_mention", "repo_activity", "hiring_aggregate" | no | Event types that signal buying intent for this ICP. | | `project_id` | string | no | Project to create the profile in; defaults to the key's project. | ### Response fields (201) Schema `Icp`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The ICP profile id. | | `project_id` | string | yes | Project the profile belongs to. | | `name` | string | yes | Name. | | `description` | string | yes | Plain-text description. | | `filters` | IcpFilters | yes | Optional structured filters that sharpen the plain-text profile. | | `filters.firm_types` | array of enum: "operating_company", "investment_adviser", "broker_dealer", "investment_fund", "nonprofit", "public_body", "other", "unknown" | no | Firm types to target. | | `filters.size_bands` | array of enum: "micro", "small", "medium", "large", "enterprise", "unknown" | no | Size bands to target. | | `filters.countries` | array of string | no | Headquarters countries to target. | | `filters.exclude_countries` | array of string | no | Headquarters countries to exclude. | | `filters.exclude_domains` | array of string | no | Firms to exclude by domain (for example existing customers). | | `filters.event_types` | array of enum: "filing", "registration", "officer_change", "ownership_change", "lei_change", "address_change", "fund_raise", "insider_trade", "patent_filing", "litigation", "news_mention", "repo_activity", "hiring_aggregate" | no | Event types that signal buying intent for this ICP. | | `segment` | object | yes | Calibration segment of the profile (spec 6.4). | | `segment.industry_group` | string or null | yes | Industry group assigned at summarization. | | `segment.size_band` | enum: "micro", "small", "medium", "large", "enterprise", "unknown", null | yes | Size band assigned at summarization. | | `summary_status` | enum: "pending", "ready", "failed" | yes | Status of the one-time summarization. | | `thresholds` | Thresholds | yes | Decision thresholds, one per decision. | | `thresholds.qualify_firm` | object | yes | Threshold for `qualify_firm`. | | `thresholds.qualify_firm.value` | number | yes | The threshold `answer` applies to `p`. | | `thresholds.qualify_firm.source` | enum: "calibrated", "custom" | yes | Calibrated default or your own value. | | `thresholds.score_signal` | object | yes | Threshold for `score_signal`. | | `thresholds.score_signal.value` | number | yes | The threshold `answer` applies to `p`. | | `thresholds.score_signal.source` | enum: "calibrated", "custom" | yes | Calibrated default or your own value. | | `thresholds.pick_person` | object | yes | Threshold for `pick_person`. | | `thresholds.pick_person.value` | number | yes | The threshold `answer` applies to `p`. | | `thresholds.pick_person.source` | enum: "calibrated", "custom" | yes | Calibrated default or your own value. | | `thresholds.triage_reply` | object | yes | Threshold for `triage_reply`. | | `thresholds.triage_reply.value` | number | yes | The threshold `answer` applies to `p`. | | `thresholds.triage_reply.source` | enum: "calibrated", "custom" | yes | Calibrated default or your own value. | | `thresholds.right_person` | object | yes | Threshold for `right_person`. | | `thresholds.right_person.value` | number | yes | The threshold `answer` applies to `p`. | | `thresholds.right_person.source` | enum: "calibrated", "custom" | yes | Calibrated default or your own value. | | `thresholds.should_send` | object | yes | Threshold for `should_send`. | | `thresholds.should_send.value` | number | yes | The threshold `answer` applies to `p`. | | `thresholds.should_send.source` | enum: "calibrated", "custom" | yes | Calibrated default or your own value. | | `created_at` | string (date-time) | yes | When the profile was created. | | `updated_at` | string (date-time) | yes | When the profile last changed. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `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`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## GET /v1/icps/{id} Tag: ICP profiles. Source: https://docs.askverdict.io/reference/get-icp.md | | | | --- | --- | | Call | `GET /v1/icps/{id}` | | Price | free | | Scope | `decisions:write` | | Operation id | `getIcp` | ### What it does One ICP profile with its segment, summary status and the current threshold of every decision. Call it to see what `answer` will apply. Free. Scope: `decisions:write`. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X GET 'https://api.askverdict.io/v1/icps/icp_01JC3Z4Y8R9K2M5N7P1Q3S5T7V' \ -H "Authorization: Bearer $VERDICT_API_KEY" ``` ### Example response `200`: The profile. ```json title="200 response" { "id": "icp_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "project_id": "prj_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "name": "RIAs under $1B", "description": "US registered investment advisers with under $1B in assets and fewer than 50 staff. We sell trade reconciliation software to their operations lead.", "filters": { "firm_types": [ "investment_adviser" ], "size_bands": [ "micro", "small" ], "countries": [ "US" ] }, "segment": { "industry_group": "asset_management", "size_band": "small" }, "summary_status": "ready", "thresholds": { "qualify_firm": { "value": 0.6, "source": "calibrated" }, "score_signal": { "value": 0.55, "source": "calibrated" }, "pick_person": { "value": 0.5, "source": "calibrated" }, "triage_reply": { "value": 0.65, "source": "calibrated" }, "right_person": { "value": 0.7, "source": "custom" }, "should_send": { "value": 0.6, "source": "calibrated" } }, "created_at": "2026-11-07T14:00:00Z", "updated_at": "2026-11-07T14:20:00Z" } ``` ### Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `id` | path | string | yes | The ICP profile id. | | `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`. | ### Response fields (200) Schema `Icp`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The ICP profile id. | | `project_id` | string | yes | Project the profile belongs to. | | `name` | string | yes | Name. | | `description` | string | yes | Plain-text description. | | `filters` | IcpFilters | yes | Optional structured filters that sharpen the plain-text profile. | | `filters.firm_types` | array of enum: "operating_company", "investment_adviser", "broker_dealer", "investment_fund", "nonprofit", "public_body", "other", "unknown" | no | Firm types to target. | | `filters.size_bands` | array of enum: "micro", "small", "medium", "large", "enterprise", "unknown" | no | Size bands to target. | | `filters.countries` | array of string | no | Headquarters countries to target. | | `filters.exclude_countries` | array of string | no | Headquarters countries to exclude. | | `filters.exclude_domains` | array of string | no | Firms to exclude by domain (for example existing customers). | | `filters.event_types` | array of enum: "filing", "registration", "officer_change", "ownership_change", "lei_change", "address_change", "fund_raise", "insider_trade", "patent_filing", "litigation", "news_mention", "repo_activity", "hiring_aggregate" | no | Event types that signal buying intent for this ICP. | | `segment` | object | yes | Calibration segment of the profile (spec 6.4). | | `segment.industry_group` | string or null | yes | Industry group assigned at summarization. | | `segment.size_band` | enum: "micro", "small", "medium", "large", "enterprise", "unknown", null | yes | Size band assigned at summarization. | | `summary_status` | enum: "pending", "ready", "failed" | yes | Status of the one-time summarization. | | `thresholds` | Thresholds | yes | Decision thresholds, one per decision. | | `thresholds.qualify_firm` | object | yes | Threshold for `qualify_firm`. | | `thresholds.qualify_firm.value` | number | yes | The threshold `answer` applies to `p`. | | `thresholds.qualify_firm.source` | enum: "calibrated", "custom" | yes | Calibrated default or your own value. | | `thresholds.score_signal` | object | yes | Threshold for `score_signal`. | | `thresholds.score_signal.value` | number | yes | The threshold `answer` applies to `p`. | | `thresholds.score_signal.source` | enum: "calibrated", "custom" | yes | Calibrated default or your own value. | | `thresholds.pick_person` | object | yes | Threshold for `pick_person`. | | `thresholds.pick_person.value` | number | yes | The threshold `answer` applies to `p`. | | `thresholds.pick_person.source` | enum: "calibrated", "custom" | yes | Calibrated default or your own value. | | `thresholds.triage_reply` | object | yes | Threshold for `triage_reply`. | | `thresholds.triage_reply.value` | number | yes | The threshold `answer` applies to `p`. | | `thresholds.triage_reply.source` | enum: "calibrated", "custom" | yes | Calibrated default or your own value. | | `thresholds.right_person` | object | yes | Threshold for `right_person`. | | `thresholds.right_person.value` | number | yes | The threshold `answer` applies to `p`. | | `thresholds.right_person.source` | enum: "calibrated", "custom" | yes | Calibrated default or your own value. | | `thresholds.should_send` | object | yes | Threshold for `should_send`. | | `thresholds.should_send.value` | number | yes | The threshold `answer` applies to `p`. | | `thresholds.should_send.source` | enum: "calibrated", "custom" | yes | Calibrated default or your own value. | | `created_at` | string (date-time) | yes | When the profile was created. | | `updated_at` | string (date-time) | yes | When the profile last changed. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `403` | The key lacks the scope this operation needs (`forbidden_scope`). | | `404` | The resource does not exist in this organization (`not_found`). | | `429` | Rate limited (`rate_limited`). Wait `Retry-After` seconds. | | `500` | Unexpected server error (`internal`). Not billed; retry with the same `Idempotency-Key`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## PATCH /v1/icps/{id} Tag: ICP profiles. Source: https://docs.askverdict.io/reference/update-icp.md | | | | --- | --- | | Call | `PATCH /v1/icps/{id}` | | Price | free | | Scope | `decisions:write` | | Operation id | `updateIcp` | ### What it does Change a profile's name, description or filters. Changing the description re-runs summarization. Past decisions are unchanged. Free. Scope: `decisions:write`. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X PATCH 'https://api.askverdict.io/v1/icps/icp_01JC3Z4Y8R9K2M5N7P1Q3S5T7V' \ -H "Authorization: Bearer $VERDICT_API_KEY" \ -H 'Content-Type: application/json' \ -d '{ "name": "RIAs under $2B" }' ``` ### Example response `200`: The updated profile. ```json title="200 response" { "id": "icp_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "project_id": "prj_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "name": "RIAs under $2B", "description": "US registered investment advisers with under $1B in assets and fewer than 50 staff. We sell trade reconciliation software to their operations lead.", "filters": { "firm_types": [ "investment_adviser" ], "size_bands": [ "micro", "small" ], "countries": [ "US" ] }, "segment": { "industry_group": "asset_management", "size_band": "small" }, "summary_status": "ready", "thresholds": { "qualify_firm": { "value": 0.6, "source": "calibrated" }, "score_signal": { "value": 0.55, "source": "calibrated" }, "pick_person": { "value": 0.5, "source": "calibrated" }, "triage_reply": { "value": 0.65, "source": "calibrated" }, "right_person": { "value": 0.7, "source": "custom" }, "should_send": { "value": 0.6, "source": "calibrated" } }, "created_at": "2026-11-07T14:00:00Z", "updated_at": "2026-11-07T14:20:00Z" } ``` ### Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `id` | path | string | yes | The ICP profile id. | | `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 Schema `UpdateIcpRequest`. Fields to change. | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | no | New name. | | `description` | string | no | New plain-text description; re-runs summarization. At most 8192 bytes of UTF-8. | | `filters` | IcpFilters | no | Optional structured filters that sharpen the plain-text profile. | | `filters.firm_types` | array of enum: "operating_company", "investment_adviser", "broker_dealer", "investment_fund", "nonprofit", "public_body", "other", "unknown" | no | Firm types to target. | | `filters.size_bands` | array of enum: "micro", "small", "medium", "large", "enterprise", "unknown" | no | Size bands to target. | | `filters.countries` | array of string | no | Headquarters countries to target. | | `filters.exclude_countries` | array of string | no | Headquarters countries to exclude. | | `filters.exclude_domains` | array of string | no | Firms to exclude by domain (for example existing customers). | | `filters.event_types` | array of enum: "filing", "registration", "officer_change", "ownership_change", "lei_change", "address_change", "fund_raise", "insider_trade", "patent_filing", "litigation", "news_mention", "repo_activity", "hiring_aggregate" | no | Event types that signal buying intent for this ICP. | ### Response fields (200) Schema `Icp`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The ICP profile id. | | `project_id` | string | yes | Project the profile belongs to. | | `name` | string | yes | Name. | | `description` | string | yes | Plain-text description. | | `filters` | IcpFilters | yes | Optional structured filters that sharpen the plain-text profile. | | `filters.firm_types` | array of enum: "operating_company", "investment_adviser", "broker_dealer", "investment_fund", "nonprofit", "public_body", "other", "unknown" | no | Firm types to target. | | `filters.size_bands` | array of enum: "micro", "small", "medium", "large", "enterprise", "unknown" | no | Size bands to target. | | `filters.countries` | array of string | no | Headquarters countries to target. | | `filters.exclude_countries` | array of string | no | Headquarters countries to exclude. | | `filters.exclude_domains` | array of string | no | Firms to exclude by domain (for example existing customers). | | `filters.event_types` | array of enum: "filing", "registration", "officer_change", "ownership_change", "lei_change", "address_change", "fund_raise", "insider_trade", "patent_filing", "litigation", "news_mention", "repo_activity", "hiring_aggregate" | no | Event types that signal buying intent for this ICP. | | `segment` | object | yes | Calibration segment of the profile (spec 6.4). | | `segment.industry_group` | string or null | yes | Industry group assigned at summarization. | | `segment.size_band` | enum: "micro", "small", "medium", "large", "enterprise", "unknown", null | yes | Size band assigned at summarization. | | `summary_status` | enum: "pending", "ready", "failed" | yes | Status of the one-time summarization. | | `thresholds` | Thresholds | yes | Decision thresholds, one per decision. | | `thresholds.qualify_firm` | object | yes | Threshold for `qualify_firm`. | | `thresholds.qualify_firm.value` | number | yes | The threshold `answer` applies to `p`. | | `thresholds.qualify_firm.source` | enum: "calibrated", "custom" | yes | Calibrated default or your own value. | | `thresholds.score_signal` | object | yes | Threshold for `score_signal`. | | `thresholds.score_signal.value` | number | yes | The threshold `answer` applies to `p`. | | `thresholds.score_signal.source` | enum: "calibrated", "custom" | yes | Calibrated default or your own value. | | `thresholds.pick_person` | object | yes | Threshold for `pick_person`. | | `thresholds.pick_person.value` | number | yes | The threshold `answer` applies to `p`. | | `thresholds.pick_person.source` | enum: "calibrated", "custom" | yes | Calibrated default or your own value. | | `thresholds.triage_reply` | object | yes | Threshold for `triage_reply`. | | `thresholds.triage_reply.value` | number | yes | The threshold `answer` applies to `p`. | | `thresholds.triage_reply.source` | enum: "calibrated", "custom" | yes | Calibrated default or your own value. | | `thresholds.right_person` | object | yes | Threshold for `right_person`. | | `thresholds.right_person.value` | number | yes | The threshold `answer` applies to `p`. | | `thresholds.right_person.source` | enum: "calibrated", "custom" | yes | Calibrated default or your own value. | | `thresholds.should_send` | object | yes | Threshold for `should_send`. | | `thresholds.should_send.value` | number | yes | The threshold `answer` applies to `p`. | | `thresholds.should_send.source` | enum: "calibrated", "custom" | yes | Calibrated default or your own value. | | `created_at` | string (date-time) | yes | When the profile was created. | | `updated_at` | string (date-time) | yes | When the profile last changed. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `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`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## PATCH /v1/icps/{id}/thresholds Tag: ICP profiles. Source: https://docs.askverdict.io/reference/update-icp-thresholds.md | | | | --- | --- | | Call | `PATCH /v1/icps/{id}/thresholds` | | Price | free | | Scope | `account:manage` | | Operation id | `updateIcpThresholds` | ### What it does Trade precision for recall: set the threshold `answer` applies to `p` for one or more decisions on this profile, or `null` to return to the calibrated default. Changes `answer` on future decisions and nothing else; `p` is unaffected. Audited. Free. Scope: `account:manage`. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X PATCH 'https://api.askverdict.io/v1/icps/icp_01JC3Z4Y8R9K2M5N7P1Q3S5T7V/thresholds' \ -H "Authorization: Bearer $VERDICT_API_KEY" \ -H 'Content-Type: application/json' \ -d '{ "thresholds": { "right_person": 0.7, "qualify_firm": null } }' ``` ### Example response `200`: The profile's thresholds after the change. ```json title="200 response" { "qualify_firm": { "value": 0.6, "source": "calibrated" }, "score_signal": { "value": 0.55, "source": "calibrated" }, "pick_person": { "value": 0.5, "source": "calibrated" }, "triage_reply": { "value": 0.65, "source": "calibrated" }, "right_person": { "value": 0.7, "source": "custom" }, "should_send": { "value": 0.6, "source": "calibrated" } } ``` ### Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `id` | path | string | yes | The ICP profile id. | | `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 Schema `UpdateThresholdsRequest`. Thresholds to change. | Field | Type | Required | Description | | --- | --- | --- | --- | | `thresholds` | object | yes | Thresholds to change, by decision name. | | `thresholds.qualify_firm` | number or null | no | New threshold for `qualify_firm`, or `null` to return to the calibrated default. | | `thresholds.score_signal` | number or null | no | New threshold for `score_signal`, or `null` to return to the calibrated default. | | `thresholds.pick_person` | number or null | no | New threshold for `pick_person`, or `null` to return to the calibrated default. | | `thresholds.triage_reply` | number or null | no | New threshold for `triage_reply`, or `null` to return to the calibrated default. | | `thresholds.right_person` | number or null | no | New threshold for `right_person`, or `null` to return to the calibrated default. | | `thresholds.should_send` | number or null | no | New threshold for `should_send`, or `null` to return to the calibrated default. | ### Response fields (200) Schema `Thresholds`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `qualify_firm` | object | yes | Threshold for `qualify_firm`. | | `qualify_firm.value` | number | yes | The threshold `answer` applies to `p`. | | `qualify_firm.source` | enum: "calibrated", "custom" | yes | Calibrated default or your own value. | | `score_signal` | object | yes | Threshold for `score_signal`. | | `score_signal.value` | number | yes | The threshold `answer` applies to `p`. | | `score_signal.source` | enum: "calibrated", "custom" | yes | Calibrated default or your own value. | | `pick_person` | object | yes | Threshold for `pick_person`. | | `pick_person.value` | number | yes | The threshold `answer` applies to `p`. | | `pick_person.source` | enum: "calibrated", "custom" | yes | Calibrated default or your own value. | | `triage_reply` | object | yes | Threshold for `triage_reply`. | | `triage_reply.value` | number | yes | The threshold `answer` applies to `p`. | | `triage_reply.source` | enum: "calibrated", "custom" | yes | Calibrated default or your own value. | | `right_person` | object | yes | Threshold for `right_person`. | | `right_person.value` | number | yes | The threshold `answer` applies to `p`. | | `right_person.source` | enum: "calibrated", "custom" | yes | Calibrated default or your own value. | | `should_send` | object | yes | Threshold for `should_send`. | | `should_send.value` | number | yes | The threshold `answer` applies to `p`. | | `should_send.source` | enum: "calibrated", "custom" | yes | Calibrated default or your own value. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `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`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## POST /v1/signup Tag: Account. Source: https://docs.askverdict.io/reference/signup.md | | | | --- | --- | | Call | `POST /v1/signup` | | Price | free | | Scope | none (public endpoint) | | Operation id | `signup` | ### What it does Start sign-up with a work email; no browser and no card needed. Verdict emails a verification link; complete sign-up with `POST /v1/signup/verify` using the token from the link, which issues your first key (shown once) and $5 of starter credits. If the verification email cannot be sent, the answer is 503 `vendor_unavailable` with `Retry-After` and nothing is created: retry the same request after that many seconds. Free. No key needed. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X POST 'https://api.askverdict.io/v1/signup' \ -H "Idempotency-Key: $(uuidgen)" \ -H 'Content-Type: application/json' \ -d '{ "email": "dana@northwind-ops.io", "org_name": "Northwind Ops" }' ``` ### Example response `202`: Verification email sent. ```json title="202 response" { "status": "verification_sent", "email": "dana@northwind-ops.io", "expires_at": "2026-11-07T14:10:00Z" } ``` ### 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`. | ### Request body Schema `SignupRequest`. The email to verify. | Field | Type | Required | Description | | --- | --- | --- | --- | | `email` | string (email) | yes | Work email. A verification link is sent to it; disposable domains are refused. | | `org_name` | string | no | Organization name; defaults to the email's domain. | ### Response fields (202) Schema `SignupResponse`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `status` | enum: "verification_sent" | yes | A verification email was sent. | | `email` | string (email) | yes | The address it was sent to. | | `expires_at` | string (date-time) | yes | When the verification link expires. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/errors/). | Status | Meaning | | --- | --- | | `400` | Invalid request: malformed JSON, an unknown field, a failed constraint or a size limit (`invalid_request`). | | `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 Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## POST /v1/signup/verify Tag: Account. Source: https://docs.askverdict.io/reference/verify-signup.md | | | | --- | --- | | Call | `POST /v1/signup/verify` | | Price | free | | Scope | none (public endpoint) | | Operation id | `verifySignup` | ### What it does Exchange the token from the verification email for your organization, default project and first API key. The key secret is returned once; store it. Starter credits ($5) are granted. The token is single use. Free. No key needed. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X POST 'https://api.askverdict.io/v1/signup/verify' \ -H "Idempotency-Key: $(uuidgen)" \ -H 'Content-Type: application/json' \ -d '{ "token": "Ex4mpleVerificationTokenFromTheEmailLink0001" }' ``` ### Example response `201`: The new organization and key. ```json title="201 response" { "org_id": "org_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "project_id": "prj_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "key": { "id": "key_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "project_id": "prj_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "name": "default", "environment": "live", "prefix": "vd_live_Ab12Cd", "scopes": [ "decisions:write", "index:read", "outcomes:write" ], "status": "active", "rate_limit_per_minute": 600, "daily_spend_cap_usd": 25, "ip_allowlist": [], "expires_at": null, "last_used_at": null, "created_at": "2026-11-07T14:00:00Z", "secret": "vd_live_" }, "starter_credit_usd": 5, "balance_usd": 5 } ``` ### 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`. | ### Request body Schema `VerifySignupRequest`. The verification token. | Field | Type | Required | Description | | --- | --- | --- | --- | | `token` | string | yes | The token from the verification link. | ### Response fields (201) Schema `VerifySignupResponse`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `org_id` | string | yes | The new organization. | | `project_id` | string | yes | Its default project. | | `key` | CreatedApiKey | yes | A new API key with its secret (shown once). | | `key.id` | string | yes | The key id. | | `key.project_id` | string | yes | Project the key belongs to. | | `key.name` | string | yes | Name. | | `key.environment` | enum: "live", "test" | yes | `live` or `test`. | | `key.prefix` | string | yes | The key's first characters, to recognize it. | | `key.scopes` | array of enum: "decisions:write", "index:read", "outcomes:write", "watches:manage", "vendors:call", "account:read", "account:manage", "keys:manage" | yes | Scopes granted. | | `key.status` | enum: "active", "rotating", "revoked", "expired" | yes | Lifecycle status. | | `key.rate_limit_per_minute` | integer | yes | Requests per minute allowed. | | `key.daily_spend_cap_usd` | number or null | yes | Daily spend cap, or `null` for none. US dollars, up to 6 decimals. | | `key.ip_allowlist` | array of string | yes | CIDR blocks the key may be used from; empty means anywhere. | | `key.expires_at` | string or null (date-time) | yes | Expiry, or `null` for none. | | `key.last_used_at` | string or null (date-time) | yes | Last successful use, or `null`. | | `key.created_at` | string (date-time) | yes | When the key was created. | | `key.secret` | string | yes | The API key: `vd_live_` or `vd_test_` plus a base62 body. Shown once; store it now. | | `starter_credit_usd` | number | yes | Starter credits granted. US dollars, up to 6 decimals. | | `balance_usd` | number | yes | Balance after the grant. US dollars, up to 6 decimals. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/errors/). | Status | Meaning | | --- | --- | | `400` | Invalid request: malformed JSON, an unknown field, a failed constraint or a size limit (`invalid_request`). | | `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`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## GET /v1/me Tag: Account. Source: https://docs.askverdict.io/reference/get-me.md | | | | --- | --- | | Call | `GET /v1/me` | | Price | free | | Scope | any valid API key | | Operation id | `getMe` | ### What it does The organization, project and key behind the calling key, and its default API version. Call it to check a key works. Free. Any valid key. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X GET 'https://api.askverdict.io/v1/me' \ -H "Authorization: Bearer $VERDICT_API_KEY" ``` ### Example response `200`: The calling identity. ```json title="200 response" { "org": { "id": "org_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "name": "Northwind Ops", "created_at": "2026-11-01T09:00:00Z" }, "project": { "id": "prj_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "name": "outbound-ria" }, "key": { "id": "key_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "project_id": "prj_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "name": "outbound-agent prod", "environment": "live", "prefix": "vd_live_Ab12Cd", "scopes": [ "decisions:write", "index:read", "outcomes:write" ], "status": "active", "rate_limit_per_minute": 600, "daily_spend_cap_usd": 25, "ip_allowlist": [], "expires_at": null, "last_used_at": "2026-11-07T14:03:00Z", "created_at": "2026-11-01T09:00:00Z" }, "api_version": "2026-11-01" } ``` ### Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `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`. | ### Response fields (200) Schema `MeResponse`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `org` | object | yes | The calling key's organization. | | `org.id` | string | yes | Organization id. | | `org.name` | string | yes | Organization name. | | `org.created_at` | string (date-time) | yes | When the organization was created. | | `project` | object | yes | The calling key's project. | | `project.id` | string | yes | Project id. | | `project.name` | string | yes | Project name. | | `key` | ApiKey | yes | An API key's metadata. | | `key.id` | string | yes | The key id. | | `key.project_id` | string | yes | Project the key belongs to. | | `key.name` | string | yes | Name. | | `key.environment` | enum: "live", "test" | yes | `live` or `test`. | | `key.prefix` | string | yes | The key's first characters, to recognize it. | | `key.scopes` | array of enum: "decisions:write", "index:read", "outcomes:write", "watches:manage", "vendors:call", "account:read", "account:manage", "keys:manage" | yes | Scopes granted. | | `key.status` | enum: "active", "rotating", "revoked", "expired" | yes | Lifecycle status. | | `key.rate_limit_per_minute` | integer | yes | Requests per minute allowed. | | `key.daily_spend_cap_usd` | number or null | yes | Daily spend cap, or `null` for none. US dollars, up to 6 decimals. | | `key.ip_allowlist` | array of string | yes | CIDR blocks the key may be used from; empty means anywhere. | | `key.expires_at` | string or null (date-time) | yes | Expiry, or `null` for none. | | `key.last_used_at` | string or null (date-time) | yes | Last successful use, or `null`. | | `key.created_at` | string (date-time) | yes | When the key was created. | | `api_version` | enum: "2026-11-01" | yes | API version this key uses by default. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `403` | The key lacks the scope this operation needs (`forbidden_scope`). | | `429` | Rate limited (`rate_limited`). Wait `Retry-After` seconds. | | `500` | Unexpected server error (`internal`). Not billed; retry with the same `Idempotency-Key`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## GET /v1/keys Tag: Account. Source: https://docs.askverdict.io/reference/list-keys.md | | | | --- | --- | | Call | `GET /v1/keys` | | Price | free | | Scope | `keys:manage` | | Operation id | `listKeys` | ### What it does Your organization's keys with scopes, limits, status and last use. Secrets are never returned. Free. Scope: `keys:manage`. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X GET 'https://api.askverdict.io/v1/keys' \ -H "Authorization: Bearer $VERDICT_API_KEY" ``` ### Example response `200`: A page of keys. ```json title="200 response" { "data": [ { "id": "key_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "project_id": "prj_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "name": "outbound-agent prod", "environment": "live", "prefix": "vd_live_Ab12Cd", "scopes": [ "decisions:write", "index:read", "outcomes:write" ], "status": "active", "rate_limit_per_minute": 600, "daily_spend_cap_usd": 25, "ip_allowlist": [], "expires_at": null, "last_used_at": "2026-11-07T14:03:00Z", "created_at": "2026-11-01T09:00:00Z" } ], "next_cursor": null, "has_more": false } ``` ### Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `cursor` | query | string | no | Opaque cursor from the `next_cursor` of the previous page. Omit it for the first page. | | `limit` | query | integer | no | Page size, 1 to 200. Default 50. | | `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`. | ### Response fields (200) | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | array of ApiKey | yes | Keys. | | `data[].id` | string | yes | The key id. | | `data[].project_id` | string | yes | Project the key belongs to. | | `data[].name` | string | yes | Name. | | `data[].environment` | enum: "live", "test" | yes | `live` or `test`. | | `data[].prefix` | string | yes | The key's first characters, to recognize it. | | `data[].scopes` | array of enum: "decisions:write", "index:read", "outcomes:write", "watches:manage", "vendors:call", "account:read", "account:manage", "keys:manage" | yes | Scopes granted. | | `data[].status` | enum: "active", "rotating", "revoked", "expired" | yes | Lifecycle status. | | `data[].rate_limit_per_minute` | integer | yes | Requests per minute allowed. | | `data[].daily_spend_cap_usd` | number or null | yes | Daily spend cap, or `null` for none. US dollars, up to 6 decimals. | | `data[].ip_allowlist` | array of string | yes | CIDR blocks the key may be used from; empty means anywhere. | | `data[].expires_at` | string or null (date-time) | yes | Expiry, or `null` for none. | | `data[].last_used_at` | string or null (date-time) | yes | Last successful use, or `null`. | | `data[].created_at` | string (date-time) | yes | When the key was created. | | `next_cursor` | string or null | yes | Pass as `cursor` to fetch the next page. `null` when this is the last page. | | `has_more` | boolean | yes | Whether another page follows. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `403` | The key lacks the scope this operation needs (`forbidden_scope`). | | `429` | Rate limited (`rate_limited`). Wait `Retry-After` seconds. | | `500` | Unexpected server error (`internal`). Not billed; retry with the same `Idempotency-Key`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## POST /v1/keys Tag: Account. Source: https://docs.askverdict.io/reference/create-key.md | | | | --- | --- | | Call | `POST /v1/keys` | | Price | free | | Scope | `keys:manage` | | Operation id | `createKey` | ### What it does Create a scoped key with optional rate limit, daily spend cap, IP allowlist and expiry. The secret is in the response once and never again. Audited. Free. Scope: `keys:manage`. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X POST 'https://api.askverdict.io/v1/keys' \ -H "Authorization: Bearer $VERDICT_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H 'Content-Type: application/json' \ -d '{ "name": "outbound-agent prod", "scopes": [ "decisions:write", "index:read", "outcomes:write" ], "daily_spend_cap_usd": 25 }' ``` ### Example response `201`: The key, with its secret shown once. ```json title="201 response" { "id": "key_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "project_id": "prj_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "name": "outbound-agent prod", "environment": "live", "prefix": "vd_live_Ab12Cd", "scopes": [ "decisions:write", "index:read", "outcomes:write" ], "status": "active", "rate_limit_per_minute": 600, "daily_spend_cap_usd": 25, "ip_allowlist": [], "expires_at": null, "last_used_at": null, "created_at": "2026-11-07T14:00:00Z", "secret": "vd_live_" } ``` ### 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-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 Schema `CreateKeyRequest`. The key to create. | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | yes | Name, for example `outbound-agent prod`. | | `project_id` | string | no | Project; defaults to the calling key's project. | | `environment` | enum: "live", "test" | no | `live` (default) or `test`. | | `scopes` | array of enum: "decisions:write", "index:read", "outcomes:write", "watches:manage", "vendors:call", "account:read", "account:manage", "keys:manage" | no | Scopes; default `decisions:write`, `index:read`, `outcomes:write`. | | `expires_at` | string (date-time) | no | Expiry; default none. | | `rate_limit_per_minute` | integer | no | Requests per minute; default 600. | | `daily_spend_cap_usd` | number | no | Daily spend cap; default none. US dollars, up to 6 decimals. | | `ip_allowlist` | array of string (ipv4-cidr) or string (ipv6-cidr) | no | CIDR blocks the key may be used from; default anywhere. | ### Response fields (201) Schema `CreatedApiKey`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The key id. | | `project_id` | string | yes | Project the key belongs to. | | `name` | string | yes | Name. | | `environment` | enum: "live", "test" | yes | `live` or `test`. | | `prefix` | string | yes | The key's first characters, to recognize it. | | `scopes` | array of enum: "decisions:write", "index:read", "outcomes:write", "watches:manage", "vendors:call", "account:read", "account:manage", "keys:manage" | yes | Scopes granted. | | `status` | enum: "active", "rotating", "revoked", "expired" | yes | Lifecycle status. | | `rate_limit_per_minute` | integer | yes | Requests per minute allowed. | | `daily_spend_cap_usd` | number or null | yes | Daily spend cap, or `null` for none. US dollars, up to 6 decimals. | | `ip_allowlist` | array of string | yes | CIDR blocks the key may be used from; empty means anywhere. | | `expires_at` | string or null (date-time) | yes | Expiry, or `null` for none. | | `last_used_at` | string or null (date-time) | yes | Last successful use, or `null`. | | `created_at` | string (date-time) | yes | When the key was created. | | `secret` | string | yes | The API key: `vd_live_` or `vd_test_` plus a base62 body. Shown once; store it now. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `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`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## PATCH /v1/keys/{id} Tag: Account. Source: https://docs.askverdict.io/reference/update-key.md | | | | --- | --- | | Call | `PATCH /v1/keys/{id}` | | Price | free | | Scope | `keys:manage` | | Operation id | `updateKey` | ### What it does Change a key's name, scopes or limits (rate limit, daily spend cap, IP allowlist, expiry) without rotating it; the secret is unchanged. A key can only grant scopes it holds itself. Takes effect on the next request. Audited (`key_scopes_changed` for scopes, `key_updated` for the rest). Free. Scope: `keys:manage`. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X PATCH 'https://api.askverdict.io/v1/keys/key_01JC3Z4Y8R9K2M5N7P1Q3S5T7V' \ -H "Authorization: Bearer $VERDICT_API_KEY" \ -H 'Content-Type: application/json' \ -d '{ "scopes": [ "decisions:write", "index:read" ], "rate_limit_per_minute": 120 }' ``` ### Example response `200`: The key, updated. ```json title="200 response" { "id": "key_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "project_id": "prj_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "name": "outbound-agent prod", "environment": "live", "prefix": "vd_live_Ab12Cd", "scopes": [ "decisions:write", "index:read" ], "status": "active", "rate_limit_per_minute": 120, "daily_spend_cap_usd": 25, "ip_allowlist": [], "expires_at": null, "last_used_at": "2026-11-07T14:03:00Z", "created_at": "2026-11-01T09:00:00Z" } ``` ### Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `id` | path | string | yes | The key id. | | `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 Schema `UpdateKeyRequest`. Fields to change. | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | no | New name. | | `scopes` | array of enum: "decisions:write", "index:read", "outcomes:write", "watches:manage", "vendors:call", "account:read", "account:manage", "keys:manage" | no | Replacement scopes. A key can only grant scopes the calling key holds itself. Audited as `key_scopes_changed`. | | `rate_limit_per_minute` | integer | no | Requests per minute. | | `daily_spend_cap_usd` | number or null | no | Daily spend cap; `null` removes it. US dollars, up to 6 decimals. | | `ip_allowlist` | array of string (ipv4-cidr) or string (ipv6-cidr) | no | CIDR blocks the key may be used from; empty means anywhere. | | `expires_at` | string or null (date-time) | no | Expiry; `null` removes it. | ### Response fields (200) Schema `ApiKey`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The key id. | | `project_id` | string | yes | Project the key belongs to. | | `name` | string | yes | Name. | | `environment` | enum: "live", "test" | yes | `live` or `test`. | | `prefix` | string | yes | The key's first characters, to recognize it. | | `scopes` | array of enum: "decisions:write", "index:read", "outcomes:write", "watches:manage", "vendors:call", "account:read", "account:manage", "keys:manage" | yes | Scopes granted. | | `status` | enum: "active", "rotating", "revoked", "expired" | yes | Lifecycle status. | | `rate_limit_per_minute` | integer | yes | Requests per minute allowed. | | `daily_spend_cap_usd` | number or null | yes | Daily spend cap, or `null` for none. US dollars, up to 6 decimals. | | `ip_allowlist` | array of string | yes | CIDR blocks the key may be used from; empty means anywhere. | | `expires_at` | string or null (date-time) | yes | Expiry, or `null` for none. | | `last_used_at` | string or null (date-time) | yes | Last successful use, or `null`. | | `created_at` | string (date-time) | yes | When the key was created. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `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`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## DELETE /v1/keys/{id} Tag: Account. Source: https://docs.askverdict.io/reference/revoke-key.md | | | | --- | --- | | Call | `DELETE /v1/keys/{id}` | | Price | free | | Scope | `keys:manage` | | Operation id | `revokeKey` | ### What it does Revoke a key immediately; calls with it fail within a second. Use it when a key may have leaked (then create or rotate a replacement). Cannot be undone. Audited. Free. Scope: `keys:manage`. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X DELETE 'https://api.askverdict.io/v1/keys/key_01JC3Z4Y8R9K2M5N7P1Q3S5T7V' \ -H "Authorization: Bearer $VERDICT_API_KEY" ``` ### Example response `200`: The key, now `revoked`. ```json title="200 response" { "id": "key_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "project_id": "prj_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "name": "outbound-agent prod", "environment": "live", "prefix": "vd_live_Ab12Cd", "scopes": [ "decisions:write", "index:read", "outcomes:write" ], "status": "revoked", "rate_limit_per_minute": 600, "daily_spend_cap_usd": 25, "ip_allowlist": [], "expires_at": null, "last_used_at": "2026-11-07T14:03:00Z", "created_at": "2026-11-01T09:00:00Z" } ``` ### Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `id` | path | string | yes | The key id. | | `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`. | ### Response fields (200) Schema `ApiKey`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The key id. | | `project_id` | string | yes | Project the key belongs to. | | `name` | string | yes | Name. | | `environment` | enum: "live", "test" | yes | `live` or `test`. | | `prefix` | string | yes | The key's first characters, to recognize it. | | `scopes` | array of enum: "decisions:write", "index:read", "outcomes:write", "watches:manage", "vendors:call", "account:read", "account:manage", "keys:manage" | yes | Scopes granted. | | `status` | enum: "active", "rotating", "revoked", "expired" | yes | Lifecycle status. | | `rate_limit_per_minute` | integer | yes | Requests per minute allowed. | | `daily_spend_cap_usd` | number or null | yes | Daily spend cap, or `null` for none. US dollars, up to 6 decimals. | | `ip_allowlist` | array of string | yes | CIDR blocks the key may be used from; empty means anywhere. | | `expires_at` | string or null (date-time) | yes | Expiry, or `null` for none. | | `last_used_at` | string or null (date-time) | yes | Last successful use, or `null`. | | `created_at` | string (date-time) | yes | When the key was created. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `403` | The key lacks the scope this operation needs (`forbidden_scope`). | | `404` | The resource does not exist in this organization (`not_found`). | | `429` | Rate limited (`rate_limited`). Wait `Retry-After` seconds. | | `500` | Unexpected server error (`internal`). Not billed; retry with the same `Idempotency-Key`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## POST /v1/keys/{id}/rotate Tag: Account. Source: https://docs.askverdict.io/reference/rotate-key.md | | | | --- | --- | | Call | `POST /v1/keys/{id}/rotate` | | Price | free | | Scope | `keys:manage` | | Operation id | `rotateKey` | ### What it does Create a replacement key with the same scopes and limits; the old key keeps working for `grace_period_hours` (default 24) and then stops. The new secret is shown once. Audited. Free. Scope: `keys:manage`. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X POST 'https://api.askverdict.io/v1/keys/key_01JC3Z4Y8R9K2M5N7P1Q3S5T7V/rotate' \ -H "Authorization: Bearer $VERDICT_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H 'Content-Type: application/json' \ -d '{ "grace_period_hours": 24 }' ``` ### Example response `201`: The replacement key and the old key's grace window. ```json title="201 response" { "key": { "id": "key_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "project_id": "prj_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "name": "outbound-agent prod", "environment": "live", "prefix": "vd_live_Ab12Cd", "scopes": [ "decisions:write", "index:read", "outcomes:write" ], "status": "active", "rate_limit_per_minute": 600, "daily_spend_cap_usd": 25, "ip_allowlist": [], "expires_at": null, "last_used_at": null, "created_at": "2026-11-07T14:00:00Z", "secret": "vd_live_" }, "previous_key": { "id": "key_01JC3Z4Y8R9K2M5N7P1Q3S5T7W", "valid_until": "2026-11-08T14:00:00Z" } } ``` ### Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `id` | path | string | yes | The key id. | | `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-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 Schema `RotateKeyRequest`. Rotation options. | Field | Type | Required | Description | | --- | --- | --- | --- | | `grace_period_hours` | integer | no | Hours the old key stays valid; default 24. | ### Response fields (201) Schema `RotateKeyResponse`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `key` | CreatedApiKey | yes | A new API key with its secret (shown once). | | `key.id` | string | yes | The key id. | | `key.project_id` | string | yes | Project the key belongs to. | | `key.name` | string | yes | Name. | | `key.environment` | enum: "live", "test" | yes | `live` or `test`. | | `key.prefix` | string | yes | The key's first characters, to recognize it. | | `key.scopes` | array of enum: "decisions:write", "index:read", "outcomes:write", "watches:manage", "vendors:call", "account:read", "account:manage", "keys:manage" | yes | Scopes granted. | | `key.status` | enum: "active", "rotating", "revoked", "expired" | yes | Lifecycle status. | | `key.rate_limit_per_minute` | integer | yes | Requests per minute allowed. | | `key.daily_spend_cap_usd` | number or null | yes | Daily spend cap, or `null` for none. US dollars, up to 6 decimals. | | `key.ip_allowlist` | array of string | yes | CIDR blocks the key may be used from; empty means anywhere. | | `key.expires_at` | string or null (date-time) | yes | Expiry, or `null` for none. | | `key.last_used_at` | string or null (date-time) | yes | Last successful use, or `null`. | | `key.created_at` | string (date-time) | yes | When the key was created. | | `key.secret` | string | yes | The API key: `vd_live_` or `vd_test_` plus a base62 body. Shown once; store it now. | | `previous_key` | object | yes | The old key and the end of its grace window. | | `previous_key.id` | string | yes | The rotated key. | | `previous_key.valid_until` | string (date-time) | yes | When the old key stops working. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `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`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## GET /v1/limits Tag: Account. Source: https://docs.askverdict.io/reference/get-limits.md | | | | --- | --- | | Call | `GET /v1/limits` | | Price | free | | Scope | `account:read` | | Operation id | `getLimits` | ### What it does The rate limit, spend cap and request size limits that apply to the calling key, and the starter allowance left. Call it before batching. Free. Scope: `account:read`. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X GET 'https://api.askverdict.io/v1/limits' \ -H "Authorization: Bearer $VERDICT_API_KEY" ``` ### Example response `200`: The limits. ```json title="200 response" { "rate_limit": { "requests_per_minute": 600, "burst": 100 }, "daily_spend_cap_usd": 25, "spent_today_usd": 1.42, "request_limits": { "max_body_bytes": 262144, "max_roster_entries": 50, "max_thread_messages": 40, "max_message_bytes": 8192, "max_page_limit": 200 }, "starter": { "card_required": false, "free_usd_remaining": 3.58, "free_calls_remaining": 716 } } ``` ### Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `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`. | ### Response fields (200) Schema `LimitsResponse`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `rate_limit` | object | yes | Rate limit of the calling key. | | `rate_limit.requests_per_minute` | integer | yes | Requests per minute for this key. | | `rate_limit.burst` | integer | yes | Burst allowance. | | `daily_spend_cap_usd` | number or null | yes | Daily spend cap of the key, or `null`. US dollars, up to 6 decimals. | | `spent_today_usd` | number | yes | Spent today (UTC) by the key. US dollars, up to 6 decimals. | | `request_limits` | object | yes | Request size limits (spec 9.4). | | `request_limits.max_body_bytes` | integer | yes | Maximum request body size. | | `request_limits.max_roster_entries` | integer | yes | Maximum `pick_person` roster size. | | `request_limits.max_thread_messages` | integer | yes | Maximum `triage_reply` thread length. | | `request_limits.max_message_bytes` | integer | yes | Maximum bytes per message. | | `request_limits.max_page_limit` | integer | yes | Maximum `limit` on list endpoints. | | `starter` | object | yes | Starter allowance (spec 9.1): a card is required past $5 or 1,000 calls. | | `starter.card_required` | boolean | yes | Whether a card is now required to keep calling. | | `starter.free_usd_remaining` | number | yes | Starter credit left before a card is required. US dollars, up to 6 decimals. | | `starter.free_calls_remaining` | integer | yes | Calls left before a card is required. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `403` | The key lacks the scope this operation needs (`forbidden_scope`). | | `429` | Rate limited (`rate_limited`). Wait `Retry-After` seconds. | | `500` | Unexpected server error (`internal`). Not billed; retry with the same `Idempotency-Key`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## GET /v1/audit Tag: Account. Source: https://docs.askverdict.io/reference/list-audit.md | | | | --- | --- | | Call | `GET /v1/audit` | | Price | free | | Scope | `account:read` | | Operation id | `listAudit` | ### What it does The organization's append-only audit log (key changes, member changes, threshold changes, webhook endpoints, top-ups, exports, deletion requests), newest first. Free. Scope: `account:read`. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X GET 'https://api.askverdict.io/v1/audit' \ -H "Authorization: Bearer $VERDICT_API_KEY" ``` ### Example response `200`: A page of audit entries. ```json title="200 response" { "data": [ { "id": "aud_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "action": "key_rotated", "actor": { "kind": "user", "id": "usr_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "role": "owner" }, "target": { "type": "key", "id": "key_01JC3Z4Y8R9K2M5N7P1Q3S5T7V" }, "ip": "203.0.113.7", "metadata": { "grace_period_hours": 24 }, "created_at": "2026-11-07T14:30:00Z" } ], "next_cursor": null, "has_more": false } ``` ### Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `cursor` | query | string | no | Opaque cursor from the `next_cursor` of the previous page. Omit it for the first page. | | `limit` | query | integer | no | Page size, 1 to 200. Default 50. | | `action` | query | enum (21 values) | no | Only this action. | | `from` | query | string (date-time) | no | Only entries at or after this time. | | `to` | query | string (date-time) | no | Only entries before this time. | | `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`. | ### Response fields (200) | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | array of AuditEntry | yes | Audit entries, newest first. | | `data[].id` | string | yes | The audit entry id. | | `data[].action` | enum (21 values) | yes | What happened. | | `data[].actor` | object | yes | Who did it. | | `data[].actor.kind` | enum: "user", "key", "system" | yes | Kind of actor. | | `data[].actor.id` | string or string or null | yes | The `usr_` or `key_` id, or `null` for `system`. | | `data[].actor.role` | enum: "owner", "admin", "developer", "billing", "read_only", null | yes | The user's role at the time, for `user` actors. | | `data[].target` | object or null | yes | What it was done to, if a single resource. | | `data[].target.type` | string | yes | Kind of resource, for example `key` or `icp`. | | `data[].target.id` | string | yes | The resource id. | | `data[].ip` | string or null | yes | Client IP address, if the action came over the network. | | `data[].metadata` | object | yes | Action details (for example old and new scopes). Never secrets. | | `data[].created_at` | string (date-time) | yes | When it happened. | | `next_cursor` | string or null | yes | Pass as `cursor` to fetch the next page. `null` when this is the last page. | | `has_more` | boolean | yes | Whether another page follows. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `403` | The key lacks the scope this operation needs (`forbidden_scope`). | | `429` | Rate limited (`rate_limited`). Wait `Retry-After` seconds. | | `500` | Unexpected server error (`internal`). Not billed; retry with the same `Idempotency-Key`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## GET /v1/balance Tag: Billing. Source: https://docs.askverdict.io/reference/get-balance.md | | | | --- | --- | | Call | `GET /v1/balance` | | Price | free | | Scope | `account:read` | | Operation id | `getBalance` | ### What it does Cash and test balances in US dollars. Billed calls also return `balance_usd`, so you rarely need this. Free. Scope: `account:read`. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X GET 'https://api.askverdict.io/v1/balance' \ -H "Authorization: Bearer $VERDICT_API_KEY" ``` ### Example response `200`: The balances. ```json title="200 response" { "balance_usd": 18.32, "test_balance_usd": 5, "auto_topup": { "enabled": false, "threshold_usd": null, "pack_usd": null }, "as_of": "2026-11-07T14:03:01Z" } ``` ### Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `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`. | ### Response fields (200) Schema `BalanceResponse`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `balance_usd` | number | yes | Cash balance (live keys). US dollars, up to 6 decimals. | | `test_balance_usd` | number | yes | Non-cash test balance (test keys). US dollars, up to 6 decimals. | | `auto_topup` | object | yes | Auto top-up settings (optional, phase 2). | | `auto_topup.enabled` | boolean | yes | Whether auto top-up is on. | | `auto_topup.threshold_usd` | number or null | yes | Balance that triggers a top-up. US dollars, up to 6 decimals. | | `auto_topup.pack_usd` | enum: 20 or enum: 50 or enum: 200 or null | yes | Pack bought on trigger. | | `as_of` | string (date-time) | yes | When the balance was read. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `403` | The key lacks the scope this operation needs (`forbidden_scope`). | | `429` | Rate limited (`rate_limited`). Wait `Retry-After` seconds. | | `500` | Unexpected server error (`internal`). Not billed; retry with the same `Idempotency-Key`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## POST /v1/topup Tag: Billing. Source: https://docs.askverdict.io/reference/create-topup.md | | | | --- | --- | | Call | `POST /v1/topup` | | Price | free | | Scope | `account:manage` | | Operation id | `createTopup` | ### What it does Start buying a $20, $50 or $200 pack. Returns a Stripe Checkout URL for a human to pay; the balance is credited when Stripe confirms. Balance does not expire while the account is active. The call itself is free. Scope: `account:manage`. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X POST 'https://api.askverdict.io/v1/topup' \ -H "Authorization: Bearer $VERDICT_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H 'Content-Type: application/json' \ -d '{ "pack_usd": 50 }' ``` ### Example response `201`: The Checkout session. ```json title="201 response" { "id": "pck_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "pack_usd": 50, "status": "pending", "checkout_url": "https://checkout.stripe.com/c/pay/cs_test_example", "expires_at": "2026-11-08T14:03:00Z" } ``` ### 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-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 Schema `TopupRequest`. The pack. | Field | Type | Required | Description | | --- | --- | --- | --- | | `pack_usd` | enum: 20 or enum: 50 or enum: 200 | yes | Pack to buy: 20, 50 or 200 US dollars. | ### Response fields (201) Schema `TopupResponse`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The pack purchase id. | | `pack_usd` | enum: 20 or enum: 50 or enum: 200 | yes | Pack size. | | `status` | enum: "pending", "completed", "expired", "failed" | yes | Purchase status. | | `checkout_url` | string (uri) | yes | Stripe Checkout URL; open it to pay. The balance is credited when Stripe confirms payment. | | `expires_at` | string (date-time) | yes | When the Checkout session expires. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `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`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## GET /v1/usage Tag: Billing. Source: https://docs.askverdict.io/reference/get-usage.md | | | | --- | --- | | Call | `GET /v1/usage` | | Price | free | | Scope | `account:read` | | Operation id | `getUsage` | ### What it does Calls, billed calls and charges between two UTC days, grouped by day, project, key or decision. Totals equal the ledger. Free. Scope: `account:read`. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X GET 'https://api.askverdict.io/v1/usage?from=2026-11-01&to=2026-11-01' \ -H "Authorization: Bearer $VERDICT_API_KEY" ``` ### Example response `200`: Usage over the window. ```json title="200 response" { "from": "2026-11-01", "to": "2026-11-07", "group_by": "decision", "rows": [ { "group": "qualify_firm", "calls": 1200, "billed_calls": 1180, "decision_usd": 5.9, "vendor_usd": 0, "total_usd": 5.9 }, { "group": "triage_reply", "calls": 310, "billed_calls": 310, "decision_usd": 0.93, "vendor_usd": 0, "total_usd": 0.93 } ], "totals": { "calls": 1510, "billed_calls": 1490, "decision_usd": 6.83, "vendor_usd": 0, "total_usd": 6.83 } } ``` ### Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `from` | query | string (date) | yes | First UTC day included. | | `to` | query | string (date) | yes | Last UTC day included. | | `group_by` | query | enum: "day", "project", "key", "decision" | no | Grouping; default `day`. | | `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`. | ### Response fields (200) Schema `UsageResponse`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `from` | string (date) | yes | First day included. | | `to` | string (date) | yes | Last day included. | | `group_by` | enum: "day", "project", "key", "decision" | yes | Grouping used. | | `rows` | array of object | yes | One row per group. | | `rows[].calls` | integer | yes | Calls made. | | `rows[].billed_calls` | integer | yes | Calls billed (misses and errors are free). | | `rows[].decision_usd` | number | yes | Decision charges. US dollars, up to 6 decimals. | | `rows[].vendor_usd` | number | yes | Vendor pass-through charges. US dollars, up to 6 decimals. | | `rows[].total_usd` | number | yes | Total charges. US dollars, up to 6 decimals. | | `rows[].group` | string | yes | Group key: a date, project id, key id or decision name. | | `totals` | object | yes | Totals over the window; equal to the ledger sums. | | `totals.calls` | integer | yes | Calls made. | | `totals.billed_calls` | integer | yes | Calls billed (misses and errors are free). | | `totals.decision_usd` | number | yes | Decision charges. US dollars, up to 6 decimals. | | `totals.vendor_usd` | number | yes | Vendor pass-through charges. US dollars, up to 6 decimals. | | `totals.total_usd` | number | yes | Total charges. US dollars, up to 6 decimals. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `403` | The key lacks the scope this operation needs (`forbidden_scope`). | | `429` | Rate limited (`rate_limited`). Wait `Retry-After` seconds. | | `500` | Unexpected server error (`internal`). Not billed; retry with the same `Idempotency-Key`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## GET /v1/ledger Tag: Billing. Source: https://docs.askverdict.io/reference/list-ledger.md | | | | --- | --- | | Call | `GET /v1/ledger` | | Price | free | | Scope | `account:read` | | Operation id | `listLedger` | ### What it does Every credit and debit on your balances, newest first, each linked to its decision or pack. Use it to reconcile spend; filter by `kind` or time. Free. Scope: `account:read`. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X GET 'https://api.askverdict.io/v1/ledger' \ -H "Authorization: Bearer $VERDICT_API_KEY" ``` ### Example response `200`: A page of ledger entries. ```json title="200 response" { "data": [ { "id": "led_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "kind": "decision_debit", "environment": "live", "amount_usd": -0.005, "balance_after_usd": 18.32, "decision_id": "dec_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "pack_id": null, "description": "qualify_firm", "created_at": "2026-11-07T14:03:00Z" } ], "next_cursor": null, "has_more": false } ``` ### Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `cursor` | query | string | no | Opaque cursor from the `next_cursor` of the previous page. Omit it for the first page. | | `limit` | query | integer | no | Page size, 1 to 200. Default 50. | | `kind` | query | enum: "starter_credit", "pack_credit", "auto_topup_credit", "promotional_credit", "decision_debit", "vendor_debit", "refund", "adjustment" | no | Only this kind. | | `from` | query | string (date-time) | no | Only entries at or after this time. | | `to` | query | string (date-time) | no | Only entries before this time. | | `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`. | ### Response fields (200) | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | array of LedgerEntry | yes | Ledger entries, newest first. | | `data[].id` | string | yes | The ledger entry id. | | `data[].kind` | enum: "starter_credit", "pack_credit", "auto_topup_credit", "promotional_credit", "decision_debit", "vendor_debit", "refund", "adjustment" | yes | Entry kind. | | `data[].environment` | enum: "live", "test" | yes | `live` (cash) or `test` balance. | | `data[].amount_usd` | number | yes | Signed amount: credits positive, debits negative. US dollars, up to 6 decimals. | | `data[].balance_after_usd` | number | yes | Balance after the entry. US dollars, up to 6 decimals. | | `data[].decision_id` | string or null | yes | Decision billed, for debits. | | `data[].pack_id` | string or null | yes | Pack credited, for pack credits. | | `data[].description` | string | yes | Human description. | | `data[].created_at` | string (date-time) | yes | When the entry was written. | | `next_cursor` | string or null | yes | Pass as `cursor` to fetch the next page. `null` when this is the last page. | | `has_more` | boolean | yes | Whether another page follows. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `403` | The key lacks the scope this operation needs (`forbidden_scope`). | | `429` | Rate limited (`rate_limited`). Wait `Retry-After` seconds. | | `500` | Unexpected server error (`internal`). Not billed; retry with the same `Idempotency-Key`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## POST /v1/console/magic-link Tag: Console. Source: https://docs.askverdict.io/reference/request-magic-link.md | | | | --- | --- | | Call | `POST /v1/console/magic-link` | | Price | free | | Scope | none (public endpoint) | | Operation id | `requestMagicLink` | ### What it does Send a single-use sign-in link to an existing user's email. The link works for 10 minutes, once, and only in the browser (user agent) that asked for it. Always answers 202 so it does not reveal which emails have accounts. Rate limited per IP and per email. No key or session needed. Passwords are not supported. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X POST 'https://api.askverdict.io/v1/console/magic-link' \ -H "Idempotency-Key: $(uuidgen)" \ -H 'Content-Type: application/json' \ -d '{ "email": "dana@northwind-ops.io" }' ``` ### Example response `202`: A link was sent if the account exists. ```json title="202 response" { "status": "sent", "expires_at": "2026-11-07T14:10:00Z" } ``` ### 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`. | ### Request body Schema `MagicLinkRequest`. Who is signing in. | Field | Type | Required | Description | | --- | --- | --- | --- | | `email` | string (email) | yes | The email of an existing Verdict user. | ### Response fields (202) Schema `MagicLinkResponse`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `status` | enum: "sent" | yes | Always `sent`, whether or not the address has an account, so the endpoint does not reveal which emails are registered. | | `expires_at` | string (date-time) | yes | When a link sent now expires. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `429` | Rate limited (`rate_limited`). Wait `Retry-After` seconds. | | `500` | Unexpected server error (`internal`). Not billed; retry with the same `Idempotency-Key`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## POST /v1/console/magic-link/consume Tag: Console. Source: https://docs.askverdict.io/reference/consume-magic-link.md | | | | --- | --- | | Call | `POST /v1/console/magic-link/consume` | | Price | free | | Scope | none (public endpoint) | | Operation id | `consumeMagicLink` | ### What it does Exchange the token from a sign-in or invitation link for a console session, set as the httpOnly `verdict_session` cookie. The token is single use, expires 10 minutes after it was sent and must come from the same user agent that requested it; otherwise 401. An invitation link also adds the user to the inviting organization (creating the user if needed). No key or session needed. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X POST 'https://api.askverdict.io/v1/console/magic-link/consume' \ -H "Idempotency-Key: $(uuidgen)" \ -H 'Content-Type: application/json' \ -d '{ "token": "Ex4mpleMagicLinkTokenFromTheEmailLink00000001" }' ``` ### Example response `200`: Signed in; the cookie is set. ```json title="200 response" { "user": { "id": "usr_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "email": "dana@northwind-ops.io", "totp_enabled": false }, "orgs": [ { "id": "org_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "name": "Northwind Ops", "slug": "northwind-ops", "role": "owner", "created_at": "2026-11-01T09:00:00Z" } ], "mfa_verified": false, "idle_expires_at": "2026-11-14T14:00:00Z" } ``` ### 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`. | ### Request body Schema `ConsumeMagicLinkRequest`. The link token. | Field | Type | Required | Description | | --- | --- | --- | --- | | `token` | string | yes | The token from the sign-in or invitation link. | ### Response fields (200) Schema `ConsoleSession`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `user` | ConsoleUser | yes | A console user. | | `user.id` | string | yes | User id. | | `user.email` | string (email) | yes | The user's verified email. | | `user.totp_enabled` | boolean | yes | Whether TOTP is turned on. | | `orgs` | array of ConsoleOrg | yes | Organizations the user belongs to. | | `orgs[].id` | string | yes | Organization id. | | `orgs[].name` | string | yes | Organization name. | | `orgs[].slug` | string | yes | URL-safe organization slug. | | `orgs[].role` | enum: "owner", "admin", "developer", "billing", "read_only" | yes | The user's role in this organization. | | `orgs[].created_at` | string (date-time) | yes | When the organization was created. | | `mfa_verified` | boolean | yes | Whether this session has passed TOTP. When the user has TOTP on and this is `false`, org actions answer 401 until `POST /v1/console/totp/verify` succeeds. | | `idle_expires_at` | string (date-time) | yes | When the session expires if it is not used again (7 days after last use). | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `429` | Rate limited (`rate_limited`). Wait `Retry-After` seconds. | | `500` | Unexpected server error (`internal`). Not billed; retry with the same `Idempotency-Key`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## POST /v1/console/oauth/github/start Tag: Console. Source: https://docs.askverdict.io/reference/start-github-oauth.md | | | | --- | --- | | Call | `POST /v1/console/oauth/github/start` | | Price | free | | Scope | none (public endpoint) | | Operation id | `startGithubOauth` | ### What it does Begin console sign-in with GitHub. Returns GitHub's authorization URL with a single-use `state` (valid 10 minutes) and sets a short-lived httpOnly cookie that binds the state to this browser. GitHub redirects back to the console, which posts `code` and `state` to `POST /v1/console/oauth/github/callback`. No key or session needed. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X POST 'https://api.askverdict.io/v1/console/oauth/github/start' \ -H "Idempotency-Key: $(uuidgen)" ``` ### Example response `200`: Send the browser to `authorize_url`. ```json title="200 response" { "authorize_url": "https://github.com/login/oauth/authorize?client_id=Iv1.example&state=example", "expires_at": "2026-11-07T14:10:00Z" } ``` ### 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`. | ### Response fields (200) Schema `GithubOauthStartResponse`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `authorize_url` | string (uri) | yes | GitHub's authorization page; send the browser there. | | `expires_at` | string (date-time) | yes | When the OAuth `state` in the URL expires. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `429` | Rate limited (`rate_limited`). Wait `Retry-After` seconds. | | `500` | Unexpected server error (`internal`). Not billed; retry with the same `Idempotency-Key`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## POST /v1/console/oauth/github/callback Tag: Console. Source: https://docs.askverdict.io/reference/github-oauth-callback.md | | | | --- | --- | | Call | `POST /v1/console/oauth/github/callback` | | Price | free | | Scope | none (public endpoint) | | Operation id | `githubOauthCallback` | ### What it does Complete GitHub sign-in: Verdict checks `state` against the browser's state cookie, exchanges `code` with GitHub and signs in the existing user whose verified email matches the GitHub account's verified primary email (the GitHub identity is linked on first use). GitHub sign-in does not create accounts: sign up with `POST /v1/signup` first. Sets the `verdict_session` cookie. No key or session needed. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X POST 'https://api.askverdict.io/v1/console/oauth/github/callback' \ -H "Idempotency-Key: $(uuidgen)" \ -H 'Content-Type: application/json' \ -d '{ "code": "gh-code-example", "state": "sssssssssssssssssssssssssssssssssssssssssss" }' ``` ### Example response `200`: Signed in; the cookie is set. ```json title="200 response" { "user": { "id": "usr_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "email": "dana@northwind-ops.io", "totp_enabled": false }, "orgs": [ { "id": "org_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "name": "Northwind Ops", "slug": "northwind-ops", "role": "owner", "created_at": "2026-11-01T09:00:00Z" } ], "mfa_verified": false, "idle_expires_at": "2026-11-14T14:00:00Z" } ``` ### 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`. | ### Request body Schema `GithubOauthCallbackRequest`. What GitHub returned. | Field | Type | Required | Description | | --- | --- | --- | --- | | `code` | string | yes | The `code` GitHub returned. | | `state` | string | yes | The `state` GitHub returned. | ### Response fields (200) Schema `ConsoleSession`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `user` | ConsoleUser | yes | A console user. | | `user.id` | string | yes | User id. | | `user.email` | string (email) | yes | The user's verified email. | | `user.totp_enabled` | boolean | yes | Whether TOTP is turned on. | | `orgs` | array of ConsoleOrg | yes | Organizations the user belongs to. | | `orgs[].id` | string | yes | Organization id. | | `orgs[].name` | string | yes | Organization name. | | `orgs[].slug` | string | yes | URL-safe organization slug. | | `orgs[].role` | enum: "owner", "admin", "developer", "billing", "read_only" | yes | The user's role in this organization. | | `orgs[].created_at` | string (date-time) | yes | When the organization was created. | | `mfa_verified` | boolean | yes | Whether this session has passed TOTP. When the user has TOTP on and this is `false`, org actions answer 401 until `POST /v1/console/totp/verify` succeeds. | | `idle_expires_at` | string (date-time) | yes | When the session expires if it is not used again (7 days after last use). | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `429` | Rate limited (`rate_limited`). Wait `Retry-After` seconds. | | `500` | Unexpected server error (`internal`). Not billed; retry with the same `Idempotency-Key`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## GET /v1/console/session Tag: Console. Source: https://docs.askverdict.io/reference/get-console-session.md | | | | --- | --- | | Call | `GET /v1/console/session` | | Price | free | | Scope | console session cookie (not an API key) | | Operation id | `getConsoleSession` | ### What it does Who is signed in, the organizations they belong to with their role in each, whether TOTP is still owed for this session, and when the session expires if left idle (7 days after last use). Using the session extends it. Session cookie required. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X GET 'https://api.askverdict.io/v1/console/session' \ --cookie "verdict_session=$VERDICT_SESSION" ``` ### Example response `200`: The session. ```json title="200 response" { "user": { "id": "usr_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "email": "dana@northwind-ops.io", "totp_enabled": false }, "orgs": [ { "id": "org_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "name": "Northwind Ops", "slug": "northwind-ops", "role": "owner", "created_at": "2026-11-01T09:00:00Z" } ], "mfa_verified": false, "idle_expires_at": "2026-11-14T14:00:00Z" } ``` ### Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `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`. | ### Response fields (200) Schema `ConsoleSession`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `user` | ConsoleUser | yes | A console user. | | `user.id` | string | yes | User id. | | `user.email` | string (email) | yes | The user's verified email. | | `user.totp_enabled` | boolean | yes | Whether TOTP is turned on. | | `orgs` | array of ConsoleOrg | yes | Organizations the user belongs to. | | `orgs[].id` | string | yes | Organization id. | | `orgs[].name` | string | yes | Organization name. | | `orgs[].slug` | string | yes | URL-safe organization slug. | | `orgs[].role` | enum: "owner", "admin", "developer", "billing", "read_only" | yes | The user's role in this organization. | | `orgs[].created_at` | string (date-time) | yes | When the organization was created. | | `mfa_verified` | boolean | yes | Whether this session has passed TOTP. When the user has TOTP on and this is `false`, org actions answer 401 until `POST /v1/console/totp/verify` succeeds. | | `idle_expires_at` | string (date-time) | yes | When the session expires if it is not used again (7 days after last use). | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `429` | Rate limited (`rate_limited`). Wait `Retry-After` seconds. | | `500` | Unexpected server error (`internal`). Not billed; retry with the same `Idempotency-Key`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## DELETE /v1/console/session Tag: Console. Source: https://docs.askverdict.io/reference/sign-out.md | | | | --- | --- | | Call | `DELETE /v1/console/session` | | Price | free | | Scope | console session cookie (not an API key) | | Operation id | `signOut` | ### What it does Revoke the current console session immediately and clear the cookie. Other sessions of the same user are unaffected. Session cookie required. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X DELETE 'https://api.askverdict.io/v1/console/session' \ --cookie "verdict_session=$VERDICT_SESSION" ``` ### Example response `200`: Signed out. ```json title="200 response" { "status": "signed_out" } ``` ### Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `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`. | ### Response fields (200) Schema `SignOutResponse`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `status` | enum: "signed_out" | yes | The session was revoked. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `429` | Rate limited (`rate_limited`). Wait `Retry-After` seconds. | | `500` | Unexpected server error (`internal`). Not billed; retry with the same `Idempotency-Key`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## POST /v1/console/totp/enrol Tag: Console. Source: https://docs.askverdict.io/reference/enrol-totp.md | | | | --- | --- | | Call | `POST /v1/console/totp/enrol` | | Price | free | | Scope | console session cookie (not an API key) | | Operation id | `enrolTotp` | ### What it does Generate a new TOTP secret for the signed-in user (RFC 6238: SHA-1, 6 digits, 30 seconds), shown once. It takes effect after `POST /v1/console/totp/verify` with a current code. Calling it again replaces a pending secret; when TOTP is already on, disable it first. Session cookie required. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X POST 'https://api.askverdict.io/v1/console/totp/enrol' \ --cookie "verdict_session=$VERDICT_SESSION" \ -H "Idempotency-Key: $(uuidgen)" ``` ### Example response `201`: The pending secret. ```json title="201 response" { "secret": "JBSWY3DPEHPK3PXPJBSWY3DPEHPK3PXP", "otpauth_uri": "otpauth://totp/Verdict:dana%40northwind-ops.io?secret=JBSWY3DPEHPK3PXPJBSWY3DPEHPK3PXP&issuer=Verdict&algorithm=SHA1&digits=6&period=30" } ``` ### 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`. | ### Response fields (201) Schema `TotpEnrolResponse`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `secret` | string | yes | The base32 TOTP secret (shown once). Add it to an authenticator app, then confirm with a code. | | `otpauth_uri` | string | yes | The same secret as an `otpauth://` URI, to render as a QR code. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `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`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## POST /v1/console/totp/verify Tag: Console. Source: https://docs.askverdict.io/reference/verify-totp.md | | | | --- | --- | | Call | `POST /v1/console/totp/verify` | | Price | free | | Scope | console session cookie (not an API key) | | Operation id | `verifyTotp` | ### What it does Check a current TOTP code. With a pending enrolment it turns TOTP on (audited as `totp_enabled`); when TOTP is already on it completes sign-in for this session. Either way the session is marked as having passed TOTP. A wrong code answers 401; attempts are rate limited. Session cookie required. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X POST 'https://api.askverdict.io/v1/console/totp/verify' \ --cookie "verdict_session=$VERDICT_SESSION" \ -H "Idempotency-Key: $(uuidgen)" \ -H 'Content-Type: application/json' \ -d '{ "code": "123456" }' ``` ### Example response `200`: The code was accepted. ```json title="200 response" { "totp_enabled": true, "mfa_verified": true } ``` ### 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`. | ### Request body Schema `TotpCodeRequest`. The code. | Field | Type | Required | Description | | --- | --- | --- | --- | | `code` | string | yes | The six-digit code from the authenticator app. | ### Response fields (200) Schema `TotpStatusResponse`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `totp_enabled` | boolean | yes | Whether TOTP is now on for the user. | | `mfa_verified` | boolean | yes | Whether this session has now passed TOTP. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `404` | The resource does not exist in this organization (`not_found`). | | `429` | Rate limited (`rate_limited`). Wait `Retry-After` seconds. | | `500` | Unexpected server error (`internal`). Not billed; retry with the same `Idempotency-Key`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## POST /v1/console/totp/disable Tag: Console. Source: https://docs.askverdict.io/reference/disable-totp.md | | | | --- | --- | | Call | `POST /v1/console/totp/disable` | | Price | free | | Scope | console session cookie (not an API key) | | Operation id | `disableTotp` | ### What it does Turn TOTP off for the signed-in user, given a current code. Refused with 403 while the user owns an organization with more than one member, where TOTP is required (spec 9.1). Audited as `totp_disabled` in each of the user's organizations. Session cookie required. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X POST 'https://api.askverdict.io/v1/console/totp/disable' \ --cookie "verdict_session=$VERDICT_SESSION" \ -H "Idempotency-Key: $(uuidgen)" \ -H 'Content-Type: application/json' \ -d '{ "code": "123456" }' ``` ### Example response `200`: TOTP is off. ```json title="200 response" { "totp_enabled": false, "mfa_verified": false } ``` ### 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`. | ### Request body Schema `TotpCodeRequest`. A current code. | Field | Type | Required | Description | | --- | --- | --- | --- | | `code` | string | yes | The six-digit code from the authenticator app. | ### Response fields (200) Schema `TotpStatusResponse`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `totp_enabled` | boolean | yes | Whether TOTP is now on for the user. | | `mfa_verified` | boolean | yes | Whether this session has now passed TOTP. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `403` | The key lacks the scope this operation needs (`forbidden_scope`). | | `404` | The resource does not exist in this organization (`not_found`). | | `429` | Rate limited (`rate_limited`). Wait `Retry-After` seconds. | | `500` | Unexpected server error (`internal`). Not billed; retry with the same `Idempotency-Key`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## GET /v1/console/orgs Tag: Console. Source: https://docs.askverdict.io/reference/list-console-orgs.md | | | | --- | --- | | Call | `GET /v1/console/orgs` | | Price | free | | Scope | console session cookie (not an API key) | | Operation id | `listConsoleOrgs` | ### What it does The organizations the signed-in user is a member of, with their role in each. Use the `id` in the org-scoped console endpoints. Session cookie required. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X GET 'https://api.askverdict.io/v1/console/orgs' \ --cookie "verdict_session=$VERDICT_SESSION" ``` ### Example response `200`: The organizations. ```json title="200 response" { "data": [ { "id": "org_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "name": "Northwind Ops", "slug": "northwind-ops", "role": "owner", "created_at": "2026-11-01T09:00:00Z" } ] } ``` ### Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `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`. | ### Response fields (200) Schema `ConsoleOrgsResponse`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | array of ConsoleOrg | yes | Organizations, oldest membership first. | | `data[].id` | string | yes | Organization id. | | `data[].name` | string | yes | Organization name. | | `data[].slug` | string | yes | URL-safe organization slug. | | `data[].role` | enum: "owner", "admin", "developer", "billing", "read_only" | yes | The user's role in this organization. | | `data[].created_at` | string (date-time) | yes | When the organization was created. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `429` | Rate limited (`rate_limited`). Wait `Retry-After` seconds. | | `500` | Unexpected server error (`internal`). Not billed; retry with the same `Idempotency-Key`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## GET /v1/console/orgs/{org_id}/members Tag: Console. Source: https://docs.askverdict.io/reference/list-members.md | | | | --- | --- | | Call | `GET /v1/console/orgs/{org_id}/members` | | Price | free | | Scope | console session cookie (not an API key) | | Operation id | `listMembers` | ### What it does The organization's members with their roles and TOTP state, and the pending invitations. A user who is not a member gets 404. Roles: `owner`, `admin`, `developer`, `billing`, `read_only` (checked server-side). When the user has TOTP on, the session must have passed `POST /v1/console/totp/verify`; an owner of an organization with more than one member must have TOTP on (spec 9.1), otherwise 403. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X GET 'https://api.askverdict.io/v1/console/orgs/org_01JC3Z4Y8R9K2M5N7P1Q3S5T7V/members' \ --cookie "verdict_session=$VERDICT_SESSION" ``` ### Example response `200`: Members and invitations. ```json title="200 response" { "members": [ { "user_id": "usr_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "email": "dana@northwind-ops.io", "role": "owner", "totp_enabled": true, "joined_at": "2026-11-01T09:00:00Z" } ], "invites": [ { "id": "0b8f6c1e-3d2a-4f5b-9c7d-1e2f3a4b5c6d", "email": "sam@northwind-ops.io", "role": "developer", "invited_by": "usr_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "expires_at": "2026-11-14T14:00:00Z", "created_at": "2026-11-07T14:00:00Z" } ] } ``` ### Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `org_id` | path | string | yes | The organization. | | `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`. | ### Response fields (200) Schema `MembersResponse`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `members` | array of Member | yes | Current members, oldest first. | | `members[].user_id` | string | yes | The member's user id. | | `members[].email` | string (email) | yes | The member's email. | | `members[].role` | enum: "owner", "admin", "developer", "billing", "read_only" | yes | The member's role. | | `members[].totp_enabled` | boolean | yes | Whether the member has TOTP on. | | `members[].joined_at` | string (date-time) | yes | When the member joined. | | `invites` | array of Invite | yes | Pending invitations, newest first. | | `invites[].id` | string (uuid) | yes | The invitation id. | | `invites[].email` | string (email) | yes | Who was invited. | | `invites[].role` | enum: "owner", "admin", "developer", "billing", "read_only" | yes | The role the invitee gets on accepting. | | `invites[].invited_by` | string | yes | Who sent the invitation. | | `invites[].expires_at` | string (date-time) | yes | When the invitation expires. | | `invites[].created_at` | string (date-time) | yes | When it was sent. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `403` | The key lacks the scope this operation needs (`forbidden_scope`). | | `404` | The resource does not exist in this organization (`not_found`). | | `429` | Rate limited (`rate_limited`). Wait `Retry-After` seconds. | | `500` | Unexpected server error (`internal`). Not billed; retry with the same `Idempotency-Key`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## POST /v1/console/orgs/{org_id}/invites Tag: Console. Source: https://docs.askverdict.io/reference/create-invite.md | | | | --- | --- | | Call | `POST /v1/console/orgs/{org_id}/invites` | | Price | free | | Scope | console session cookie (not an API key) | | Operation id | `createInvite` | ### What it does Invite someone by email with a role. They get a magic link (valid 7 days, single use) that adds them to the organization when they sign in with it. Only an owner may invite an owner; inviting an existing member answers 409. Audited as `member_invited` (and `member_added` on acceptance). Roles: `owner`, `admin` (checked server-side). When the user has TOTP on, the session must have passed `POST /v1/console/totp/verify`; an owner of an organization with more than one member must have TOTP on (spec 9.1), otherwise 403. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X POST 'https://api.askverdict.io/v1/console/orgs/org_01JC3Z4Y8R9K2M5N7P1Q3S5T7V/invites' \ --cookie "verdict_session=$VERDICT_SESSION" \ -H "Idempotency-Key: $(uuidgen)" \ -H 'Content-Type: application/json' \ -d '{ "email": "sam@northwind-ops.io", "role": "developer" }' ``` ### Example response `201`: The invitation was sent. ```json title="201 response" { "id": "0b8f6c1e-3d2a-4f5b-9c7d-1e2f3a4b5c6d", "email": "sam@northwind-ops.io", "role": "developer", "invited_by": "usr_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "expires_at": "2026-11-14T14:00:00Z", "created_at": "2026-11-07T14:00:00Z" } ``` ### Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `org_id` | path | string | yes | The organization. | | `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`. | ### Request body Schema `CreateInviteRequest`. Who to invite. | Field | Type | Required | Description | | --- | --- | --- | --- | | `email` | string (email) | yes | Who to invite. | | `role` | enum: "owner", "admin", "developer", "billing", "read_only" | yes | Role on accepting. Only an owner may invite an owner. | ### Response fields (201) Schema `Invite`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string (uuid) | yes | The invitation id. | | `email` | string (email) | yes | Who was invited. | | `role` | enum: "owner", "admin", "developer", "billing", "read_only" | yes | The role the invitee gets on accepting. | | `invited_by` | string | yes | Who sent the invitation. | | `expires_at` | string (date-time) | yes | When the invitation expires. | | `created_at` | string (date-time) | yes | When it was sent. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `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`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## PATCH /v1/console/orgs/{org_id}/members/{user_id} Tag: Console. Source: https://docs.askverdict.io/reference/update-member.md | | | | --- | --- | | Call | `PATCH /v1/console/orgs/{org_id}/members/{user_id}` | | Price | free | | Scope | console session cookie (not an API key) | | Operation id | `updateMember` | ### What it does Change a member's role. Only an owner may grant `owner` or change an owner's role, and the last owner cannot be demoted (409). Audited as `member_role_changed`. Roles: `owner`, `admin` (checked server-side). When the user has TOTP on, the session must have passed `POST /v1/console/totp/verify`; an owner of an organization with more than one member must have TOTP on (spec 9.1), otherwise 403. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X PATCH 'https://api.askverdict.io/v1/console/orgs/org_01JC3Z4Y8R9K2M5N7P1Q3S5T7V/members/usr_01JC3Z4Y8R9K2M5N7P1Q3S5T7V' \ --cookie "verdict_session=$VERDICT_SESSION" \ -H 'Content-Type: application/json' \ -d '{ "role": "admin" }' ``` ### Example response `200`: The member, updated. ```json title="200 response" { "user_id": "usr_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "email": "dana@northwind-ops.io", "role": "admin", "totp_enabled": true, "joined_at": "2026-11-01T09:00:00Z" } ``` ### Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `org_id` | path | string | yes | The organization. | | `user_id` | path | string | yes | The member's user id. | | `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 Schema `UpdateMemberRequest`. The new role. | Field | Type | Required | Description | | --- | --- | --- | --- | | `role` | enum: "owner", "admin", "developer", "billing", "read_only" | yes | The new role. Only an owner may grant or remove `owner`. | ### Response fields (200) Schema `Member`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `user_id` | string | yes | The member's user id. | | `email` | string (email) | yes | The member's email. | | `role` | enum: "owner", "admin", "developer", "billing", "read_only" | yes | The member's role. | | `totp_enabled` | boolean | yes | Whether the member has TOTP on. | | `joined_at` | string (date-time) | yes | When the member joined. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `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`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## DELETE /v1/console/orgs/{org_id}/members/{user_id} Tag: Console. Source: https://docs.askverdict.io/reference/remove-member.md | | | | --- | --- | | Call | `DELETE /v1/console/orgs/{org_id}/members/{user_id}` | | Price | free | | Scope | console session cookie (not an API key) | | Operation id | `removeMember` | ### What it does Remove a member from the organization (any member may remove themselves). Only an owner may remove an owner, and the last owner cannot be removed (409). The user's sessions stay valid for their other organizations. Audited as `member_removed`. Roles: `owner`, `admin` (checked server-side). When the user has TOTP on, the session must have passed `POST /v1/console/totp/verify`; an owner of an organization with more than one member must have TOTP on (spec 9.1), otherwise 403. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X DELETE 'https://api.askverdict.io/v1/console/orgs/org_01JC3Z4Y8R9K2M5N7P1Q3S5T7V/members/usr_01JC3Z4Y8R9K2M5N7P1Q3S5T7V' \ --cookie "verdict_session=$VERDICT_SESSION" ``` ### Example response `200`: The member that was removed. ```json title="200 response" { "user_id": "usr_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "email": "dana@northwind-ops.io", "role": "owner", "totp_enabled": true, "joined_at": "2026-11-01T09:00:00Z" } ``` ### Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `org_id` | path | string | yes | The organization. | | `user_id` | path | string | yes | The member's user id. | | `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`. | ### Response fields (200) Schema `Member`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `user_id` | string | yes | The member's user id. | | `email` | string (email) | yes | The member's email. | | `role` | enum: "owner", "admin", "developer", "billing", "read_only" | yes | The member's role. | | `totp_enabled` | boolean | yes | Whether the member has TOTP on. | | `joined_at` | string (date-time) | yes | When the member joined. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `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`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). ## GET /v1/console/orgs/{org_id}/audit Tag: Console. Source: https://docs.askverdict.io/reference/list-console-audit.md | | | | --- | --- | | Call | `GET /v1/console/orgs/{org_id}/audit` | | Price | free | | Scope | console session cookie (not an API key) | | Operation id | `listConsoleAudit` | ### What it does The organization's append-only audit log, newest first, exactly as `GET /v1/audit` returns it to a key with `account:read`. In the console it is limited to owners and admins (ADR-0010). Roles: `owner`, `admin` (checked server-side). When the user has TOTP on, the session must have passed `POST /v1/console/totp/verify`; an owner of an organization with more than one member must have TOTP on (spec 9.1), otherwise 403. ### Example request Copy it as curl. Set `VERDICT_API_KEY` first; `uuidgen` makes a fresh `Idempotency-Key`. ```bash title="Request" curl -X GET 'https://api.askverdict.io/v1/console/orgs/org_01JC3Z4Y8R9K2M5N7P1Q3S5T7V/audit' \ --cookie "verdict_session=$VERDICT_SESSION" ``` ### Example response `200`: A page of audit entries. ```json title="200 response" { "data": [ { "id": "aud_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "action": "key_rotated", "actor": { "kind": "user", "id": "usr_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "role": "owner" }, "target": { "type": "key", "id": "key_01JC3Z4Y8R9K2M5N7P1Q3S5T7V" }, "ip": "203.0.113.7", "metadata": { "grace_period_hours": 24 }, "created_at": "2026-11-07T14:30:00Z" } ], "next_cursor": null, "has_more": false } ``` ### Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `org_id` | path | string | yes | The organization. | | `cursor` | query | string | no | Opaque cursor from the `next_cursor` of the previous page. Omit it for the first page. | | `limit` | query | integer | no | Page size, 1 to 200. Default 50. | | `action` | query | enum (21 values) | no | Only this action. | | `from` | query | string (date-time) | no | Only entries at or after this time. | | `to` | query | string (date-time) | no | Only entries before this time. | | `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`. | ### Response fields (200) | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | array of AuditEntry | yes | Audit entries, newest first. | | `data[].id` | string | yes | The audit entry id. | | `data[].action` | enum (21 values) | yes | What happened. | | `data[].actor` | object | yes | Who did it. | | `data[].actor.kind` | enum: "user", "key", "system" | yes | Kind of actor. | | `data[].actor.id` | string or string or null | yes | The `usr_` or `key_` id, or `null` for `system`. | | `data[].actor.role` | enum: "owner", "admin", "developer", "billing", "read_only", null | yes | The user's role at the time, for `user` actors. | | `data[].target` | object or null | yes | What it was done to, if a single resource. | | `data[].target.type` | string | yes | Kind of resource, for example `key` or `icp`. | | `data[].target.id` | string | yes | The resource id. | | `data[].ip` | string or null | yes | Client IP address, if the action came over the network. | | `data[].metadata` | object | yes | Action details (for example old and new scopes). Never secrets. | | `data[].created_at` | string (date-time) | yes | When it happened. | | `next_cursor` | string or null | yes | Pass as `cursor` to fetch the next page. `null` when this is the last page. | | `has_more` | boolean | yes | Whether another page follows. | ### Errors Errors are RFC 9457 problem details and are never billed. Branch on `type`. See [Errors](https://docs.askverdict.io/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`). | | `403` | The key lacks the scope this operation needs (`forbidden_scope`). | | `404` | The resource does not exist in this organization (`not_found`). | | `429` | Rate limited (`rate_limited`). Wait `Retry-After` seconds. | | `500` | Unexpected server error (`internal`). Not billed; retry with the same `Idempotency-Key`. | ### Try it Open the [interactive reference](https://docs.askverdict.io/api-reference/) to send this request from the browser. The full document is at [/openapi.json](https://docs.askverdict.io/openapi.json). # Schemas ### ApiKey An API key's metadata. | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The key id. | | `project_id` | string | yes | Project the key belongs to. | | `name` | string | yes | Name. | | `environment` | enum: "live", "test" | yes | `live` or `test`. | | `prefix` | string | yes | The key's first characters, to recognize it. | | `scopes` | array of enum: "decisions:write", "index:read", "outcomes:write", "watches:manage", "vendors:call", "account:read", "account:manage", "keys:manage" | yes | Scopes granted. | | `status` | enum: "active", "rotating", "revoked", "expired" | yes | Lifecycle status. | | `rate_limit_per_minute` | integer | yes | Requests per minute allowed. | | `daily_spend_cap_usd` | number or null | yes | Daily spend cap, or `null` for none. US dollars, up to 6 decimals. | | `ip_allowlist` | array of string | yes | CIDR blocks the key may be used from; empty means anywhere. | | `expires_at` | string or null (date-time) | yes | Expiry, or `null` for none. | | `last_used_at` | string or null (date-time) | yes | Last successful use, or `null`. | | `created_at` | string (date-time) | yes | When the key was created. | ### AuditEntry One append-only audit log entry. | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The audit entry id. | | `action` | enum (21 values) | yes | What happened. | | `actor` | object | yes | Who did it. | | `actor.kind` | enum: "user", "key", "system" | yes | Kind of actor. | | `actor.id` | string or string or null | yes | The `usr_` or `key_` id, or `null` for `system`. | | `actor.role` | enum: "owner", "admin", "developer", "billing", "read_only", null | yes | The user's role at the time, for `user` actors. | | `target` | object or null | yes | What it was done to, if a single resource. | | `target.type` | string | yes | Kind of resource, for example `key` or `icp`. | | `target.id` | string | yes | The resource id. | | `ip` | string or null | yes | Client IP address, if the action came over the network. | | `metadata` | object | yes | Action details (for example old and new scopes). Never secrets. | | `created_at` | string (date-time) | yes | When it happened. | ### BalanceResponse Current balances. | Field | Type | Required | Description | | --- | --- | --- | --- | | `balance_usd` | number | yes | Cash balance (live keys). US dollars, up to 6 decimals. | | `test_balance_usd` | number | yes | Non-cash test balance (test keys). US dollars, up to 6 decimals. | | `auto_topup` | object | yes | Auto top-up settings (optional, phase 2). | | `auto_topup.enabled` | boolean | yes | Whether auto top-up is on. | | `auto_topup.threshold_usd` | number or null | yes | Balance that triggers a top-up. US dollars, up to 6 decimals. | | `auto_topup.pack_usd` | enum: 20 or enum: 50 or enum: 200 or null | yes | Pack bought on trigger. | | `as_of` | string (date-time) | yes | When the balance was read. | ### Calibration How `p` was calibrated (spec 6.4). | Field | Type | Required | Description | | --- | --- | --- | --- | | `basis` | enum: "org", "segment", "global" | yes | `org` (your own outcomes), `segment` (your ICP's segment) or `global`: how much to trust `p`. | | `n_outcomes` | integer | yes | Outcomes behind the calibration map used. | | `threshold` | number | yes | The threshold `answer` applied to `p`. | ### CandidateContact One candidate contact. LinkedIn-sourced data is not accepted. | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | no | Full name. | | `title` | string | yes | Job title. | | `seniority` | enum: "owner", "c_level", "vp", "director", "manager", "senior", "entry", "unknown" | no | Seniority, if known. | | `department` | string | no | Department or function. | | `email` | string (email) | no | Work email, if known. | | `firm` | FirmReference | no | A firm reference: exactly one of `firm_id`, `domain`, `name` (with an optional `hint`), `lei`, `cik`, `crd` or `companies_house`. | | `firm.firm_id` | string | no | A firm id from Verdict's index. | | `firm.domain` | string | no | The firm's web domain. | | `firm.name` | string | no | The firm's legal or trading name. | | `firm.hint` | object | no | Optional country, city or domain to disambiguate the name. | | `firm.hint.country` | string | no | ISO 3166-1 alpha-2 country of the firm's headquarters. | | `firm.hint.city` | string | no | City of the firm's headquarters. | | `firm.hint.domain` | string | no | A domain the firm is known to use. | | `firm.lei` | string | no | The firm's Legal Entity Identifier. | | `firm.cik` | string | no | The firm's SEC Central Index Key. | | `firm.crd` | string | no | The firm's CRD number. | | `firm.companies_house` | string | no | The firm's UK Companies House number. | | `title_observed_at` | string (date-time) | no | When you last saw this title, to judge staleness. | ### ConsoleOrg An organization the user is a member of. | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | Organization id. | | `name` | string | yes | Organization name. | | `slug` | string | yes | URL-safe organization slug. | | `role` | enum: "owner", "admin", "developer", "billing", "read_only" | yes | The user's role in this organization. | | `created_at` | string (date-time) | yes | When the organization was created. | ### ConsoleOrgsResponse The organizations the user belongs to. | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | array of ConsoleOrg | yes | Organizations, oldest membership first. | | `data[].id` | string | yes | Organization id. | | `data[].name` | string | yes | Organization name. | | `data[].slug` | string | yes | URL-safe organization slug. | | `data[].role` | enum: "owner", "admin", "developer", "billing", "read_only" | yes | The user's role in this organization. | | `data[].created_at` | string (date-time) | yes | When the organization was created. | ### ConsoleSession The current console session. | Field | Type | Required | Description | | --- | --- | --- | --- | | `user` | ConsoleUser | yes | A console user. | | `user.id` | string | yes | User id. | | `user.email` | string (email) | yes | The user's verified email. | | `user.totp_enabled` | boolean | yes | Whether TOTP is turned on. | | `orgs` | array of ConsoleOrg | yes | Organizations the user belongs to. | | `orgs[].id` | string | yes | Organization id. | | `orgs[].name` | string | yes | Organization name. | | `orgs[].slug` | string | yes | URL-safe organization slug. | | `orgs[].role` | enum: "owner", "admin", "developer", "billing", "read_only" | yes | The user's role in this organization. | | `orgs[].created_at` | string (date-time) | yes | When the organization was created. | | `mfa_verified` | boolean | yes | Whether this session has passed TOTP. When the user has TOTP on and this is `false`, org actions answer 401 until `POST /v1/console/totp/verify` succeeds. | | `idle_expires_at` | string (date-time) | yes | When the session expires if it is not used again (7 days after last use). | ### ConsoleUser A console user. | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | User id. | | `email` | string (email) | yes | The user's verified email. | | `totp_enabled` | boolean | yes | Whether TOTP is turned on. | ### ConsumeMagicLinkRequest Exchange a magic link token for a session. | Field | Type | Required | Description | | --- | --- | --- | --- | | `token` | string | yes | The token from the sign-in or invitation link. | ### Cost What this call was billed, itemized. Misses and errors bill nothing. | Field | Type | Required | Description | | --- | --- | --- | --- | | `decision_usd` | number | yes | Price of the decision. US dollars, up to 6 decimals. | | `vendor_usd` | number | yes | Vendor pass-through charges (always 0 in v1). US dollars, up to 6 decimals. | | `total_usd` | number | yes | Total billed for this call. US dollars, up to 6 decimals. | ### CreatedApiKey A new API key with its secret (shown once). | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The key id. | | `project_id` | string | yes | Project the key belongs to. | | `name` | string | yes | Name. | | `environment` | enum: "live", "test" | yes | `live` or `test`. | | `prefix` | string | yes | The key's first characters, to recognize it. | | `scopes` | array of enum: "decisions:write", "index:read", "outcomes:write", "watches:manage", "vendors:call", "account:read", "account:manage", "keys:manage" | yes | Scopes granted. | | `status` | enum: "active", "rotating", "revoked", "expired" | yes | Lifecycle status. | | `rate_limit_per_minute` | integer | yes | Requests per minute allowed. | | `daily_spend_cap_usd` | number or null | yes | Daily spend cap, or `null` for none. US dollars, up to 6 decimals. | | `ip_allowlist` | array of string | yes | CIDR blocks the key may be used from; empty means anywhere. | | `expires_at` | string or null (date-time) | yes | Expiry, or `null` for none. | | `last_used_at` | string or null (date-time) | yes | Last successful use, or `null`. | | `created_at` | string (date-time) | yes | When the key was created. | | `secret` | string | yes | The API key: `vd_live_` or `vd_test_` plus a base62 body. Shown once; store it now. | ### CreatedWatch A new watch and its signing secret (shown once). | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The watch id. | | `project_id` | string | yes | Project the watch belongs to. | | `icp_id` | string | yes | ICP profile events are scored for. | | `webhook_url` | string (uri) | yes | Delivery endpoint. | | `min_p` | number | yes | Minimum `relevant` delivered. | | `types` | array of enum: "filing", "registration", "officer_change", "ownership_change", "lei_change", "address_change", "fund_raise", "insider_trade", "patent_filing", "litigation", "news_mention", "repo_activity", "hiring_aggregate" | yes | Event types watched. | | `status` | enum: "pending_verification", "active", "paused", "deleted" | yes | Lifecycle status. | | `created_at` | string (date-time) | yes | When the watch was created. | | `updated_at` | string (date-time) | yes | When the watch last changed. | | `signing_secret` | string | yes | Secret for verifying `Verdict-Signature` on deliveries. Shown once; store it now. | ### CreateIcpRequest A new ICP profile. | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | yes | Short name, for example `RIAs under $1B`. | | `description` | string | yes | Plain-text description of who you sell to and what you sell. At most 8192 bytes of UTF-8. | | `filters` | IcpFilters | no | Optional structured filters that sharpen the plain-text profile. | | `filters.firm_types` | array of enum: "operating_company", "investment_adviser", "broker_dealer", "investment_fund", "nonprofit", "public_body", "other", "unknown" | no | Firm types to target. | | `filters.size_bands` | array of enum: "micro", "small", "medium", "large", "enterprise", "unknown" | no | Size bands to target. | | `filters.countries` | array of string | no | Headquarters countries to target. | | `filters.exclude_countries` | array of string | no | Headquarters countries to exclude. | | `filters.exclude_domains` | array of string | no | Firms to exclude by domain (for example existing customers). | | `filters.event_types` | array of enum: "filing", "registration", "officer_change", "ownership_change", "lei_change", "address_change", "fund_raise", "insider_trade", "patent_filing", "litigation", "news_mention", "repo_activity", "hiring_aggregate" | no | Event types that signal buying intent for this ICP. | | `project_id` | string | no | Project to create the profile in; defaults to the key's project. | ### CreateInviteRequest Invite someone by email. | Field | Type | Required | Description | | --- | --- | --- | --- | | `email` | string (email) | yes | Who to invite. | | `role` | enum: "owner", "admin", "developer", "billing", "read_only" | yes | Role on accepting. Only an owner may invite an owner. | ### CreateKeyRequest A new API key. | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | yes | Name, for example `outbound-agent prod`. | | `project_id` | string | no | Project; defaults to the calling key's project. | | `environment` | enum: "live", "test" | no | `live` (default) or `test`. | | `scopes` | array of enum: "decisions:write", "index:read", "outcomes:write", "watches:manage", "vendors:call", "account:read", "account:manage", "keys:manage" | no | Scopes; default `decisions:write`, `index:read`, `outcomes:write`. | | `expires_at` | string (date-time) | no | Expiry; default none. | | `rate_limit_per_minute` | integer | no | Requests per minute; default 600. | | `daily_spend_cap_usd` | number | no | Daily spend cap; default none. US dollars, up to 6 decimals. | | `ip_allowlist` | array of string (ipv4-cidr) or string (ipv6-cidr) | no | CIDR blocks the key may be used from; default anywhere. | ### CreateOutcomeRequest An outcome for one decision. | Field | Type | Required | Description | | --- | --- | --- | --- | | `decision_id` | string | yes | The decision the outcome belongs to. | | `outcome` | enum: "bounced", "delivered", "replied", "interested", "not_interested", "meeting_booked", "meeting_held", "approved", "skipped", "reopened", "converted" or string | yes | The outcome: a built-in type or `custom:`. | | `observed_at` | string (date-time) | yes | When the outcome happened. | | `meta` | object | no | Optional metadata: up to 20 keys of at most 64 characters, scalar values. No personal data. | ### CreateWatchRequest A standing request for scored events, delivered by webhook. | Field | Type | Required | Description | | --- | --- | --- | --- | | `icp_id` | string | yes | Score events for this ICP profile. | | `webhook_url` | string (uri) | yes | HTTPS endpoint that receives deliveries. Private and link-local addresses are refused; the URL must answer a challenge at registration. | | `min_p` | number | yes | Deliver only events whose `relevant` is at least this. | | `types` | array of enum: "filing", "registration", "officer_change", "ownership_change", "lei_change", "address_change", "fund_raise", "insider_trade", "patent_filing", "litigation", "news_mention", "repo_activity", "hiring_aggregate" | yes | Event types to watch. | ### Decision A stored decision, exactly as it was returned; discriminated by `decision`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | no | The decision id. Report outcomes against it. | | `decision` | enum: "qualify_firm" | no | The decision name. | | `version` | enum: "2026-11-01" | no | API version that served the decision. | | `question_set_version` | string (date) | no | Version (a date) of the question set that answered (spec 6.1). | | `model` | enum: "jev" | no | Decision model that answered: always `jev` (ADR-0008). | | `p` | number | no | Calibrated probability (spec 6.4). Always present. | | `answer` | enum: "fit", "not_fit" | no | `fit` or `not_fit`: `p` applied to your threshold. | | `reason_code` | enum: "icp_type_and_size_match", "icp_type_match_size_mismatch", "icp_size_match_type_mismatch", "icp_type_and_size_mismatch", "icp_geography_mismatch", "icp_exclusion_filter_match", "firm_inactive", "insufficient_firm_data" | no | Why, from the fixed `qualify_firm` reason-code enum. | | `reason` | string | no | Short templated explanation of `reason_code`; never free text from a frontier model. | | `evidence` | array of Evidence | no | 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 | no | 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 | no | 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 | no | Balance after this call. US dollars, up to 6 decimals. | | `latency_ms` | integer | no | Server-side latency of the call, in milliseconds. | | `created_at` | string (date-time) | no | When the decision was made. Decisions are immutable. | | `fit` | number | no | Probability the firm fits the ICP; equal to `p`. | | `firm` | FirmSummary | no | A firm's id, names, identifiers and public facts. | | `firm.id` | string | yes | The firm id. | | `firm.name` | string | yes | Canonical firm name. | | `firm.domain` | string or null | yes | Primary web domain, if known. | | `firm.identifiers` | object | yes | Registry identifiers, by kind. | | `firm.identifiers.lei` | string | no | Legal Entity Identifier. | | `firm.identifiers.cik` | string | no | SEC Central Index Key. | | `firm.identifiers.crd` | string | no | CRD number. | | `firm.identifiers.companies_house` | string | no | UK Companies House number. | | `firm.identifiers.wikidata` | string | no | Wikidata item id. | | `firm.type` | enum: "operating_company", "investment_adviser", "broker_dealer", "investment_fund", "nonprofit", "public_body", "other", "unknown" | yes | Coarse firm type. | | `firm.size_band` | enum: "micro", "small", "medium", "large", "enterprise", "unknown" | yes | Employee-count size band. | | `firm.hq` | object | yes | Headquarters location. | | `firm.hq.country` | string or null | yes | ISO 3166-1 alpha-2 country. | | `firm.hq.region` | string or null | yes | State, county or region, as the registry gives it. | | `firm.hq.city` | string or null | yes | City. | | `firm.status` | enum: "active", "inactive", "dissolved", "unknown", "merged" | yes | Registry status. | | `relevant` | number | no | Probability the event matters for the ICP's offer; equal to `p`. | | `act_now` | number | no | Probability that now is the time to act on it. | | `window_days` | integer or null | no | Days left to act, or `null` when there is no clear window. | | `event_id` | string | no | The scored event (an inline event is stored and given an id). | | `people` | array of object | no | The whole roster ranked best first, each with `p`. | | `people[].index` | integer | yes | Zero-based position of the person in your `roster`. | | `people[].ref` | string or null | yes | Your `ref` for the person, or `null` if you gave none. | | `people[].rank` | integer | yes | Rank, 1 is best. | | `people[].p` | number | yes | Calibrated probability that this is the right person to write to. | | `state` | enum: "interested", "meeting_requested", "question", "objection_price", "objection_timing", "objection_fit", "not_decision_maker", "referral", "out_of_office", "unsubscribe", "bounce", "wrong_person", "other" | no | 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" | no | What the agent should do next. | | `send` | number | no | Probability that sending now is right; equal to `p`. | | `hold_reason` | enum: "upstream_decision_weak", "upstream_decision_missing", "inbox_health_degraded", "daily_volume_limit", "recently_contacted", "message_issue", "recipient_opted_out", "low_confidence", null | no | Why to hold, when `answer` is `hold`; `null` when `answer` is `send`. | ### Delivery One webhook delivery of one scored event. Billed once, when delivered. | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The delivery id; also the `id` of the delivered payload. | | `watch_id` | string | yes | The watch. | | `event_id` | string | yes | The delivered event. | | `decision_id` | string or null | yes | The `score_signal` decision behind it. | | `status` | enum: "pending", "retrying", "delivered", "failed" | yes | Delivery status. | | `attempts` | integer | yes | Attempts so far. | | `last_attempt_at` | string or null (date-time) | yes | Time of the last attempt. | | `last_response_status` | integer or null | yes | HTTP status of the last attempt, if a response arrived. | | `next_attempt_at` | string or null (date-time) | yes | Time of the next retry, while `retrying`. | | `delivered_at` | string or null (date-time) | yes | When a 2xx was received. | | `created_at` | string (date-time) | yes | When the delivery was queued. | ### Event Something that happened to a firm, normalized to one schema; `attributes` are typed per `type` (one of `filing`, `registration`, `officer_change`, `ownership_change`, `lei_change`, `address_change`, `fund_raise`, `insider_trade`, `patent_filing`, `litigation`, `news_mention`, `repo_activity`, `hiring_aggregate`). | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | no | The event id. | | `firm_id` | string | no | The firm the event happened to. | | `occurred_at` | string (date-time) | no | When the event happened, per the source. | | `observed_at` | string (date-time) | no | When Verdict ingested the event. | | `source` | enum: "sec_edgar", "gleif", "companies_house", "wikidata" | no | The index source. | | `source_url` | string (uri) | no | Link to the event at the source. | | `confidence` | number | no | Confidence in the event's normalization and firm keying. | | `score` | EventScore | no | The event's score for the requested ICP, or `null` when read without `icp_id`. | | `score.decision_id` | string | yes | The `score_signal` decision that produced this score (billed once). | | `score.icp_id` | string | yes | The ICP profile the event was scored against. | | `score.relevant` | number | yes | Calibrated probability that the event matters for the ICP's offer. | | `score.act_now` | number | yes | Calibrated probability that now is the time to act. | | `score.window_days` | integer or null | yes | Days left to act on the event, or `null` when there is no clear window. | | `score.answer` | enum: "relevant", "not_relevant" | yes | `relevant` applied to the threshold. | | `score.reason_code` | enum: "event_relevant_and_timely", "event_relevant_but_stale", "event_type_not_relevant", "event_routine", "firm_not_in_icp", "event_low_confidence", "event_superseded", "insufficient_event_data" | yes | Why, as a `score_signal` reason code. | | `type` | enum: "filing" | no | Event type `filing`. | | `attributes` | object | no | Attributes of a `filing` event. | | `attributes.filing_type` | enum: "sec_8k", "sec_form_d", "sec_form_4", "sec_s1", "sec_13f", "sec_adv" | yes | The filing sub-type. | | `attributes.accession_number` | string | no | SEC accession number. | | `attributes.items` | array of string | no | 8-K item numbers reported, for `sec_8k`. | | `attributes.title` | string | no | Filing title or form description. | | `attributes.form` | string | no | The form type exactly as the source lists it, for example `8-K/A` or `13F-HR`. | | `attributes.file_number` | string | no | SEC file number, for example `333-299845`. | ### EventScore The event's score for the requested ICP, or `null` when read without `icp_id`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `decision_id` | string | yes | The `score_signal` decision that produced this score (billed once). | | `icp_id` | string | yes | The ICP profile the event was scored against. | | `relevant` | number | yes | Calibrated probability that the event matters for the ICP's offer. | | `act_now` | number | yes | Calibrated probability that now is the time to act. | | `window_days` | integer or null | yes | Days left to act on the event, or `null` when there is no clear window. | | `answer` | enum: "relevant", "not_relevant" | yes | `relevant` applied to the threshold. | | `reason_code` | enum: "event_relevant_and_timely", "event_relevant_but_stale", "event_type_not_relevant", "event_routine", "firm_not_in_icp", "event_low_confidence", "event_superseded", "insufficient_event_data" | yes | Why, as a `score_signal` reason code. | ### Evidence A cited source behind a decision. | Field | Type | Required | Description | | --- | --- | --- | --- | | `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. | | `url` | string or null (uri) | yes | Link to the cited record at the source; `null` when `source` is `request`. | | `observed_at` | string (date) | yes | Date the source reported the fact. | | `label` | string | no | Short description of what the evidence shows. | | `pointer` | string | no | JSON Pointer into your request body when `source` is `request`, for example `/thread/2`. | ### Firm A canonical firm with its public facts and identifier crosswalk. | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The firm id. | | `name` | string | yes | Canonical firm name. | | `domain` | string or null | yes | Primary web domain, if known. | | `identifiers` | object | yes | Registry identifiers, by kind. | | `identifiers.lei` | string | no | Legal Entity Identifier. | | `identifiers.cik` | string | no | SEC Central Index Key. | | `identifiers.crd` | string | no | CRD number. | | `identifiers.companies_house` | string | no | UK Companies House number. | | `identifiers.wikidata` | string | no | Wikidata item id. | | `type` | enum: "operating_company", "investment_adviser", "broker_dealer", "investment_fund", "nonprofit", "public_body", "other", "unknown" | yes | Coarse firm type. | | `size_band` | enum: "micro", "small", "medium", "large", "enterprise", "unknown" | yes | Employee-count size band. | | `hq` | object | yes | Headquarters location. | | `hq.country` | string or null | yes | ISO 3166-1 alpha-2 country. | | `hq.region` | string or null | yes | State, county or region, as the registry gives it. | | `hq.city` | string or null | yes | City. | | `status` | enum: "active", "inactive", "dissolved", "unknown", "merged" | yes | Registry status. | | `provisional` | boolean | yes | True when the firm was created from an unresolved reference and awaits nightly reconciliation. | | `crosswalk` | array of object | yes | Every known identifier, including name variants and secondary domains, with its source. | | `crosswalk[].kind` | enum: "domain", "lei", "cik", "crd", "companies_house", "wikidata", "name_variant" | yes | Identifier kind. | | `crosswalk[].value` | string | yes | Identifier value. | | `crosswalk[].source` | enum: "sec_edgar", "gleif", "companies_house", "wikidata", "request" | yes | Source that reported the identifier: an index source, or `request` when it came from a caller's reference (provisional firms). | | `crosswalk[].observed_at` | string (date) | yes | Date the source last reported the identifier. | | `created_at` | string (date-time) | yes | When the firm entered the index. | | `updated_at` | string (date-time) | yes | When the firm's record last changed. | ### FirmReference A firm reference: exactly one of `firm_id`, `domain`, `name` (with an optional `hint`), `lei`, `cik`, `crd` or `companies_house`. | Field | Type | Required | Description | | --- | --- | --- | --- | | `firm_id` | string | no | A firm id from Verdict's index. | | `domain` | string | no | The firm's web domain. | | `name` | string | no | The firm's legal or trading name. | | `hint` | object | no | Optional country, city or domain to disambiguate the name. | | `hint.country` | string | no | ISO 3166-1 alpha-2 country of the firm's headquarters. | | `hint.city` | string | no | City of the firm's headquarters. | | `hint.domain` | string | no | A domain the firm is known to use. | | `lei` | string | no | The firm's Legal Entity Identifier. | | `cik` | string | no | The firm's SEC Central Index Key. | | `crd` | string | no | The firm's CRD number. | | `companies_house` | string | no | The firm's UK Companies House number. | ### FirmSummary A firm's id, names, identifiers and public facts. | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The firm id. | | `name` | string | yes | Canonical firm name. | | `domain` | string or null | yes | Primary web domain, if known. | | `identifiers` | object | yes | Registry identifiers, by kind. | | `identifiers.lei` | string | no | Legal Entity Identifier. | | `identifiers.cik` | string | no | SEC Central Index Key. | | `identifiers.crd` | string | no | CRD number. | | `identifiers.companies_house` | string | no | UK Companies House number. | | `identifiers.wikidata` | string | no | Wikidata item id. | | `type` | enum: "operating_company", "investment_adviser", "broker_dealer", "investment_fund", "nonprofit", "public_body", "other", "unknown" | yes | Coarse firm type. | | `size_band` | enum: "micro", "small", "medium", "large", "enterprise", "unknown" | yes | Employee-count size band. | | `hq` | object | yes | Headquarters location. | | `hq.country` | string or null | yes | ISO 3166-1 alpha-2 country. | | `hq.region` | string or null | yes | State, county or region, as the registry gives it. | | `hq.city` | string or null | yes | City. | | `status` | enum: "active", "inactive", "dissolved", "unknown", "merged" | yes | Registry status. | ### GithubOauthCallbackRequest The query parameters GitHub appended to the console callback URL. | Field | Type | Required | Description | | --- | --- | --- | --- | | `code` | string | yes | The `code` GitHub returned. | | `state` | string | yes | The `state` GitHub returned. | ### GithubOauthStartResponse Where to send the browser for GitHub. | Field | Type | Required | Description | | --- | --- | --- | --- | | `authorize_url` | string (uri) | yes | GitHub's authorization page; send the browser there. | | `expires_at` | string (date-time) | yes | When the OAuth `state` in the URL expires. | ### Icp An ICP profile: who you sell to and what you sell. | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The ICP profile id. | | `project_id` | string | yes | Project the profile belongs to. | | `name` | string | yes | Name. | | `description` | string | yes | Plain-text description. | | `filters` | IcpFilters | yes | Optional structured filters that sharpen the plain-text profile. | | `filters.firm_types` | array of enum: "operating_company", "investment_adviser", "broker_dealer", "investment_fund", "nonprofit", "public_body", "other", "unknown" | no | Firm types to target. | | `filters.size_bands` | array of enum: "micro", "small", "medium", "large", "enterprise", "unknown" | no | Size bands to target. | | `filters.countries` | array of string | no | Headquarters countries to target. | | `filters.exclude_countries` | array of string | no | Headquarters countries to exclude. | | `filters.exclude_domains` | array of string | no | Firms to exclude by domain (for example existing customers). | | `filters.event_types` | array of enum: "filing", "registration", "officer_change", "ownership_change", "lei_change", "address_change", "fund_raise", "insider_trade", "patent_filing", "litigation", "news_mention", "repo_activity", "hiring_aggregate" | no | Event types that signal buying intent for this ICP. | | `segment` | object | yes | Calibration segment of the profile (spec 6.4). | | `segment.industry_group` | string or null | yes | Industry group assigned at summarization. | | `segment.size_band` | enum: "micro", "small", "medium", "large", "enterprise", "unknown", null | yes | Size band assigned at summarization. | | `summary_status` | enum: "pending", "ready", "failed" | yes | Status of the one-time summarization. | | `thresholds` | Thresholds | yes | Decision thresholds, one per decision. | | `thresholds.qualify_firm` | object | yes | Threshold for `qualify_firm`. | | `thresholds.qualify_firm.value` | number | yes | The threshold `answer` applies to `p`. | | `thresholds.qualify_firm.source` | enum: "calibrated", "custom" | yes | Calibrated default or your own value. | | `thresholds.score_signal` | object | yes | Threshold for `score_signal`. | | `thresholds.score_signal.value` | number | yes | The threshold `answer` applies to `p`. | | `thresholds.score_signal.source` | enum: "calibrated", "custom" | yes | Calibrated default or your own value. | | `thresholds.pick_person` | object | yes | Threshold for `pick_person`. | | `thresholds.pick_person.value` | number | yes | The threshold `answer` applies to `p`. | | `thresholds.pick_person.source` | enum: "calibrated", "custom" | yes | Calibrated default or your own value. | | `thresholds.triage_reply` | object | yes | Threshold for `triage_reply`. | | `thresholds.triage_reply.value` | number | yes | The threshold `answer` applies to `p`. | | `thresholds.triage_reply.source` | enum: "calibrated", "custom" | yes | Calibrated default or your own value. | | `thresholds.right_person` | object | yes | Threshold for `right_person`. | | `thresholds.right_person.value` | number | yes | The threshold `answer` applies to `p`. | | `thresholds.right_person.source` | enum: "calibrated", "custom" | yes | Calibrated default or your own value. | | `thresholds.should_send` | object | yes | Threshold for `should_send`. | | `thresholds.should_send.value` | number | yes | The threshold `answer` applies to `p`. | | `thresholds.should_send.source` | enum: "calibrated", "custom" | yes | Calibrated default or your own value. | | `created_at` | string (date-time) | yes | When the profile was created. | | `updated_at` | string (date-time) | yes | When the profile last changed. | ### IcpFilters Optional structured filters that sharpen the plain-text profile. | Field | Type | Required | Description | | --- | --- | --- | --- | | `firm_types` | array of enum: "operating_company", "investment_adviser", "broker_dealer", "investment_fund", "nonprofit", "public_body", "other", "unknown" | no | Firm types to target. | | `size_bands` | array of enum: "micro", "small", "medium", "large", "enterprise", "unknown" | no | Size bands to target. | | `countries` | array of string | no | Headquarters countries to target. | | `exclude_countries` | array of string | no | Headquarters countries to exclude. | | `exclude_domains` | array of string | no | Firms to exclude by domain (for example existing customers). | | `event_types` | array of enum: "filing", "registration", "officer_change", "ownership_change", "lei_change", "address_change", "fund_raise", "insider_trade", "patent_filing", "litigation", "news_mention", "repo_activity", "hiring_aggregate" | no | Event types that signal buying intent for this ICP. | ### InboxHealth Inbox health you measure; Verdict never reads your inbox. | Field | Type | Required | Description | | --- | --- | --- | --- | | `bounce_rate_7d` | number | no | Share of sends that bounced over the last 7 days. | | `spam_complaint_rate_7d` | number | no | Share of sends marked as spam over the last 7 days. | | `reply_rate_7d` | number | no | Share of sends replied to over the last 7 days. | | `sent_today` | integer | no | Messages sent from this inbox today. | | `daily_limit` | integer | no | Your daily send limit for this inbox. | | `domain_age_days` | integer | no | Age of the sending domain in days. | ### InlineEvent An event the caller supplies, for events Verdict's index does not hold. | Field | Type | Required | Description | | --- | --- | --- | --- | | `type` | enum: "filing", "registration", "officer_change", "ownership_change", "lei_change", "address_change", "fund_raise", "insider_trade", "patent_filing", "litigation", "news_mention", "repo_activity", "hiring_aggregate" | yes | Event type. | | `firm` | FirmReference | yes | A firm reference: exactly one of `firm_id`, `domain`, `name` (with an optional `hint`), `lei`, `cik`, `crd` or `companies_house`. | | `firm.firm_id` | string | no | A firm id from Verdict's index. | | `firm.domain` | string | no | The firm's web domain. | | `firm.name` | string | no | The firm's legal or trading name. | | `firm.hint` | object | no | Optional country, city or domain to disambiguate the name. | | `firm.hint.country` | string | no | ISO 3166-1 alpha-2 country of the firm's headquarters. | | `firm.hint.city` | string | no | City of the firm's headquarters. | | `firm.hint.domain` | string | no | A domain the firm is known to use. | | `firm.lei` | string | no | The firm's Legal Entity Identifier. | | `firm.cik` | string | no | The firm's SEC Central Index Key. | | `firm.crd` | string | no | The firm's CRD number. | | `firm.companies_house` | string | no | The firm's UK Companies House number. | | `occurred_at` | string (date-time) | yes | When the event happened. | | `title` | string | no | One-line summary of the event. | | `description` | string | no | What happened, in plain text. At most 8192 bytes of UTF-8. | | `source_url` | string (uri) | no | Where the event was reported. | ### Invite A pending invitation to join the organization. | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string (uuid) | yes | The invitation id. | | `email` | string (email) | yes | Who was invited. | | `role` | enum: "owner", "admin", "developer", "billing", "read_only" | yes | The role the invitee gets on accepting. | | `invited_by` | string | yes | Who sent the invitation. | | `expires_at` | string (date-time) | yes | When the invitation expires. | | `created_at` | string (date-time) | yes | When it was sent. | ### LedgerEntry One ledger entry. | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The ledger entry id. | | `kind` | enum: "starter_credit", "pack_credit", "auto_topup_credit", "promotional_credit", "decision_debit", "vendor_debit", "refund", "adjustment" | yes | Entry kind. | | `environment` | enum: "live", "test" | yes | `live` (cash) or `test` balance. | | `amount_usd` | number | yes | Signed amount: credits positive, debits negative. US dollars, up to 6 decimals. | | `balance_after_usd` | number | yes | Balance after the entry. US dollars, up to 6 decimals. | | `decision_id` | string or null | yes | Decision billed, for debits. | | `pack_id` | string or null | yes | Pack credited, for pack credits. | | `description` | string | yes | Human description. | | `created_at` | string (date-time) | yes | When the entry was written. | ### LimitsResponse Limits that apply to the calling key. | Field | Type | Required | Description | | --- | --- | --- | --- | | `rate_limit` | object | yes | Rate limit of the calling key. | | `rate_limit.requests_per_minute` | integer | yes | Requests per minute for this key. | | `rate_limit.burst` | integer | yes | Burst allowance. | | `daily_spend_cap_usd` | number or null | yes | Daily spend cap of the key, or `null`. US dollars, up to 6 decimals. | | `spent_today_usd` | number | yes | Spent today (UTC) by the key. US dollars, up to 6 decimals. | | `request_limits` | object | yes | Request size limits (spec 9.4). | | `request_limits.max_body_bytes` | integer | yes | Maximum request body size. | | `request_limits.max_roster_entries` | integer | yes | Maximum `pick_person` roster size. | | `request_limits.max_thread_messages` | integer | yes | Maximum `triage_reply` thread length. | | `request_limits.max_message_bytes` | integer | yes | Maximum bytes per message. | | `request_limits.max_page_limit` | integer | yes | Maximum `limit` on list endpoints. | | `starter` | object | yes | Starter allowance (spec 9.1): a card is required past $5 or 1,000 calls. | | `starter.card_required` | boolean | yes | Whether a card is now required to keep calling. | | `starter.free_usd_remaining` | number | yes | Starter credit left before a card is required. US dollars, up to 6 decimals. | | `starter.free_calls_remaining` | integer | yes | Calls left before a card is required. | ### MagicLinkRequest Ask for a sign-in link by email. | Field | Type | Required | Description | | --- | --- | --- | --- | | `email` | string (email) | yes | The email of an existing Verdict user. | ### MagicLinkResponse A sign-in link was sent if the account exists. | Field | Type | Required | Description | | --- | --- | --- | --- | | `status` | enum: "sent" | yes | Always `sent`, whether or not the address has an account, so the endpoint does not reveal which emails are registered. | | `expires_at` | string (date-time) | yes | When a link sent now expires. | ### Member A member of the organization. | Field | Type | Required | Description | | --- | --- | --- | --- | | `user_id` | string | yes | The member's user id. | | `email` | string (email) | yes | The member's email. | | `role` | enum: "owner", "admin", "developer", "billing", "read_only" | yes | The member's role. | | `totp_enabled` | boolean | yes | Whether the member has TOTP on. | | `joined_at` | string (date-time) | yes | When the member joined. | ### MembersResponse Members and pending invitations. | Field | Type | Required | Description | | --- | --- | --- | --- | | `members` | array of Member | yes | Current members, oldest first. | | `members[].user_id` | string | yes | The member's user id. | | `members[].email` | string (email) | yes | The member's email. | | `members[].role` | enum: "owner", "admin", "developer", "billing", "read_only" | yes | The member's role. | | `members[].totp_enabled` | boolean | yes | Whether the member has TOTP on. | | `members[].joined_at` | string (date-time) | yes | When the member joined. | | `invites` | array of Invite | yes | Pending invitations, newest first. | | `invites[].id` | string (uuid) | yes | The invitation id. | | `invites[].email` | string (email) | yes | Who was invited. | | `invites[].role` | enum: "owner", "admin", "developer", "billing", "read_only" | yes | The role the invitee gets on accepting. | | `invites[].invited_by` | string | yes | Who sent the invitation. | | `invites[].expires_at` | string (date-time) | yes | When the invitation expires. | | `invites[].created_at` | string (date-time) | yes | When it was sent. | ### MeResponse Who the calling key is. | Field | Type | Required | Description | | --- | --- | --- | --- | | `org` | object | yes | The calling key's organization. | | `org.id` | string | yes | Organization id. | | `org.name` | string | yes | Organization name. | | `org.created_at` | string (date-time) | yes | When the organization was created. | | `project` | object | yes | The calling key's project. | | `project.id` | string | yes | Project id. | | `project.name` | string | yes | Project name. | | `key` | ApiKey | yes | An API key's metadata. | | `key.id` | string | yes | The key id. | | `key.project_id` | string | yes | Project the key belongs to. | | `key.name` | string | yes | Name. | | `key.environment` | enum: "live", "test" | yes | `live` or `test`. | | `key.prefix` | string | yes | The key's first characters, to recognize it. | | `key.scopes` | array of enum: "decisions:write", "index:read", "outcomes:write", "watches:manage", "vendors:call", "account:read", "account:manage", "keys:manage" | yes | Scopes granted. | | `key.status` | enum: "active", "rotating", "revoked", "expired" | yes | Lifecycle status. | | `key.rate_limit_per_minute` | integer | yes | Requests per minute allowed. | | `key.daily_spend_cap_usd` | number or null | yes | Daily spend cap, or `null` for none. US dollars, up to 6 decimals. | | `key.ip_allowlist` | array of string | yes | CIDR blocks the key may be used from; empty means anywhere. | | `key.expires_at` | string or null (date-time) | yes | Expiry, or `null` for none. | | `key.last_used_at` | string or null (date-time) | yes | Last successful use, or `null`. | | `key.created_at` | string (date-time) | yes | When the key was created. | | `api_version` | enum: "2026-11-01" | yes | API version this key uses by default. | ### Outcome A recorded outcome. | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The outcome id. | | `decision_id` | string | yes | The decision it belongs to. | | `outcome` | enum: "bounced", "delivered", "replied", "interested", "not_interested", "meeting_booked", "meeting_held", "approved", "skipped", "reopened", "converted" or string | yes | The outcome: a built-in type or `custom:`. | | `observed_at` | string (date-time) | yes | When the outcome happened. | | `meta` | object or null | yes | Optional metadata: up to 20 keys of at most 64 characters, scalar values. No personal data. | | `created_at` | string (date-time) | yes | When Verdict recorded it. | ### PickPersonRequest A roster of people at one firm, the ICP and an optional question. | Field | Type | Required | Description | | --- | --- | --- | --- | | `roster` | array of RosterEntry | yes | The people to rank, 1 to 50. | | `roster[].ref` | string | no | Your own id for this person, echoed back in `people`. | | `roster[].name` | string | yes | Full name. | | `roster[].title` | string | yes | Job title. | | `roster[].seniority` | enum: "owner", "c_level", "vp", "director", "manager", "senior", "entry", "unknown" | no | Seniority, if known. | | `roster[].department` | string | no | Department or function, for example `Operations`. | | `roster[].location` | string | no | City or country. | | `roster[].tenure_months` | integer | no | Months in the current role. | | `firm` | FirmReference | no | A firm reference: exactly one of `firm_id`, `domain`, `name` (with an optional `hint`), `lei`, `cik`, `crd` or `companies_house`. | | `firm.firm_id` | string | no | A firm id from Verdict's index. | | `firm.domain` | string | no | The firm's web domain. | | `firm.name` | string | no | The firm's legal or trading name. | | `firm.hint` | object | no | Optional country, city or domain to disambiguate the name. | | `firm.hint.country` | string | no | ISO 3166-1 alpha-2 country of the firm's headquarters. | | `firm.hint.city` | string | no | City of the firm's headquarters. | | `firm.hint.domain` | string | no | A domain the firm is known to use. | | `firm.lei` | string | no | The firm's Legal Entity Identifier. | | `firm.cik` | string | no | The firm's SEC Central Index Key. | | `firm.crd` | string | no | The firm's CRD number. | | `firm.companies_house` | string | no | The firm's UK Companies House number. | | `icp_id` | string | yes | The ICP profile to decide against. | | `question` | string | no | Optional extra question, for example `who owns trading operations?`. | ### PickPersonResponse The `pick_person` decision envelope. | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The decision id. Report outcomes against it. | | `decision` | enum: "pick_person" | 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: "pick", "no_pick" | yes | `pick` or `no_pick`: the top person's `p` applied to your threshold. | | `reason_code` | enum: "buyer_role_match", "seniority_match", "influencer_role_match", "question_match", "too_senior_for_offer", "too_junior_for_offer", "titles_ambiguous", "no_strong_match" | yes | Why, from the fixed `pick_person` 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. | | `people` | array of object | yes | The whole roster ranked best first, each with `p`. | | `people[].index` | integer | yes | Zero-based position of the person in your `roster`. | | `people[].ref` | string or null | yes | Your `ref` for the person, or `null` if you gave none. | | `people[].rank` | integer | yes | Rank, 1 is best. | | `people[].p` | number | yes | Calibrated probability that this is the right person to write to. | ### Problem An RFC 9457 problem-details error. Every error carries `type` and `request_id`. Errors are never billed. | Field | Type | Required | Description | | --- | --- | --- | --- | | `type` | enum: "invalid_request", "unauthorized", "forbidden_scope", "not_found", "insufficient_balance", "max_cost_exceeded", "rate_limited", "idempotency_conflict", "vendor_unavailable", "decision_unavailable", "internal" | yes | Stable error slug (a relative URI reference, RFC 9457). Branch on this, not on `title` or `detail`. `invalid_request` (400): The request is malformed, has unknown fields or breaks a size limit. `unauthorized` (401): The API key is missing, malformed, revoked or expired. `forbidden_scope` (403): The key lacks the scope this endpoint needs, or the resource belongs to another org. `not_found` (404): The resource does not exist in this organization. `insufficient_balance` (402): The balance cannot cover the call; top up. `max_cost_exceeded` (402): The call would cost more than `X-Max-Cost-USD`; nothing was spent. `rate_limited` (429): Too many requests; wait `Retry-After` seconds. `idempotency_conflict` (409): The `Idempotency-Key` was reused with a different request, or the first request is still running. `vendor_unavailable` (503): A vendor the call depends on is unavailable; nothing was billed. `decision_unavailable` (503): The decision model is temporarily unavailable or decisions are switched off; retry after `Retry-After`. Nothing was billed. `internal` (500): An unexpected server error; nothing was billed. Retry with the same `Idempotency-Key`. | | `title` | string | yes | Short human summary of the error type. | | `status` | integer | yes | The HTTP status code, repeated. | | `detail` | string | yes | Human explanation of this occurrence. | | `instance` | string | no | The request path that failed. | | `request_id` | string | yes | The request id; also sent as `X-Request-Id`. | | `errors` | array of object | no | Field-level issues, present on `invalid_request`. | | `errors[].path` | string | yes | JSON Pointer (RFC 6901) to the offending field, or the header or query parameter name. | | `errors[].message` | string | yes | What is wrong with the field. | | `required_usd` | number | no | What the call would cost, in US dollars; present on 402 errors. | | `balance_usd` | number | no | The current balance, in US dollars; present on `insufficient_balance`. | | `retry_after_seconds` | integer | no | Seconds to wait before retrying; present on `rate_limited` (429) and on 503s (`decision_unavailable`, `vendor_unavailable`), equal to the `Retry-After` header. | ### QualifyFirmRequest A firm reference and the ICP to qualify it against. | Field | Type | Required | Description | | --- | --- | --- | --- | | `firm` | FirmReference | yes | A firm reference: exactly one of `firm_id`, `domain`, `name` (with an optional `hint`), `lei`, `cik`, `crd` or `companies_house`. | | `firm.firm_id` | string | no | A firm id from Verdict's index. | | `firm.domain` | string | no | The firm's web domain. | | `firm.name` | string | no | The firm's legal or trading name. | | `firm.hint` | object | no | Optional country, city or domain to disambiguate the name. | | `firm.hint.country` | string | no | ISO 3166-1 alpha-2 country of the firm's headquarters. | | `firm.hint.city` | string | no | City of the firm's headquarters. | | `firm.hint.domain` | string | no | A domain the firm is known to use. | | `firm.lei` | string | no | The firm's Legal Entity Identifier. | | `firm.cik` | string | no | The firm's SEC Central Index Key. | | `firm.crd` | string | no | The firm's CRD number. | | `firm.companies_house` | string | no | The firm's UK Companies House number. | | `icp_id` | string | yes | The ICP profile to decide against. | ### QualifyFirmResponse The `qualify_firm` decision envelope. | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The decision id. Report outcomes against it. | | `decision` | enum: "qualify_firm" | 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: "fit", "not_fit" | yes | `fit` or `not_fit`: `p` applied to your threshold. | | `reason_code` | enum: "icp_type_and_size_match", "icp_type_match_size_mismatch", "icp_size_match_type_mismatch", "icp_type_and_size_mismatch", "icp_geography_mismatch", "icp_exclusion_filter_match", "firm_inactive", "insufficient_firm_data" | yes | Why, from the fixed `qualify_firm` 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. | | `fit` | number | yes | Probability the firm fits the ICP; equal to `p`. | | `firm` | FirmSummary | yes | A firm's id, names, identifiers and public facts. | | `firm.id` | string | yes | The firm id. | | `firm.name` | string | yes | Canonical firm name. | | `firm.domain` | string or null | yes | Primary web domain, if known. | | `firm.identifiers` | object | yes | Registry identifiers, by kind. | | `firm.identifiers.lei` | string | no | Legal Entity Identifier. | | `firm.identifiers.cik` | string | no | SEC Central Index Key. | | `firm.identifiers.crd` | string | no | CRD number. | | `firm.identifiers.companies_house` | string | no | UK Companies House number. | | `firm.identifiers.wikidata` | string | no | Wikidata item id. | | `firm.type` | enum: "operating_company", "investment_adviser", "broker_dealer", "investment_fund", "nonprofit", "public_body", "other", "unknown" | yes | Coarse firm type. | | `firm.size_band` | enum: "micro", "small", "medium", "large", "enterprise", "unknown" | yes | Employee-count size band. | | `firm.hq` | object | yes | Headquarters location. | | `firm.hq.country` | string or null | yes | ISO 3166-1 alpha-2 country. | | `firm.hq.region` | string or null | yes | State, county or region, as the registry gives it. | | `firm.hq.city` | string or null | yes | City. | | `firm.status` | enum: "active", "inactive", "dissolved", "unknown", "merged" | yes | Registry status. | ### ResolveFirmResponse The resolved firm and how it was matched. | Field | Type | Required | Description | | --- | --- | --- | --- | | `firm` | Firm | yes | A canonical firm with its public facts and identifier crosswalk. | | `firm.id` | string | yes | The firm id. | | `firm.name` | string | yes | Canonical firm name. | | `firm.domain` | string or null | yes | Primary web domain, if known. | | `firm.identifiers` | object | yes | Registry identifiers, by kind. | | `firm.identifiers.lei` | string | no | Legal Entity Identifier. | | `firm.identifiers.cik` | string | no | SEC Central Index Key. | | `firm.identifiers.crd` | string | no | CRD number. | | `firm.identifiers.companies_house` | string | no | UK Companies House number. | | `firm.identifiers.wikidata` | string | no | Wikidata item id. | | `firm.type` | enum: "operating_company", "investment_adviser", "broker_dealer", "investment_fund", "nonprofit", "public_body", "other", "unknown" | yes | Coarse firm type. | | `firm.size_band` | enum: "micro", "small", "medium", "large", "enterprise", "unknown" | yes | Employee-count size band. | | `firm.hq` | object | yes | Headquarters location. | | `firm.hq.country` | string or null | yes | ISO 3166-1 alpha-2 country. | | `firm.hq.region` | string or null | yes | State, county or region, as the registry gives it. | | `firm.hq.city` | string or null | yes | City. | | `firm.status` | enum: "active", "inactive", "dissolved", "unknown", "merged" | yes | Registry status. | | `firm.provisional` | boolean | yes | True when the firm was created from an unresolved reference and awaits nightly reconciliation. | | `firm.crosswalk` | array of object | yes | Every known identifier, including name variants and secondary domains, with its source. | | `firm.crosswalk[].kind` | enum: "domain", "lei", "cik", "crd", "companies_house", "wikidata", "name_variant" | yes | Identifier kind. | | `firm.crosswalk[].value` | string | yes | Identifier value. | | `firm.crosswalk[].source` | enum: "sec_edgar", "gleif", "companies_house", "wikidata", "request" | yes | Source that reported the identifier: an index source, or `request` when it came from a caller's reference (provisional firms). | | `firm.crosswalk[].observed_at` | string (date) | yes | Date the source last reported the identifier. | | `firm.created_at` | string (date-time) | yes | When the firm entered the index. | | `firm.updated_at` | string (date-time) | yes | When the firm's record last changed. | | `matched_on` | enum: "identifier", "domain", "name_and_country", "match_decision", "provisional" | yes | How the reference was matched. | ### RightPersonRequest A candidate contact and the ICP. | Field | Type | Required | Description | | --- | --- | --- | --- | | `candidate` | CandidateContact | yes | One candidate contact. LinkedIn-sourced data is not accepted. | | `candidate.name` | string | no | Full name. | | `candidate.title` | string | yes | Job title. | | `candidate.seniority` | enum: "owner", "c_level", "vp", "director", "manager", "senior", "entry", "unknown" | no | Seniority, if known. | | `candidate.department` | string | no | Department or function. | | `candidate.email` | string (email) | no | Work email, if known. | | `candidate.firm` | FirmReference | no | A firm reference: exactly one of `firm_id`, `domain`, `name` (with an optional `hint`), `lei`, `cik`, `crd` or `companies_house`. | | `candidate.firm.firm_id` | string | no | A firm id from Verdict's index. | | `candidate.firm.domain` | string | no | The firm's web domain. | | `candidate.firm.name` | string | no | The firm's legal or trading name. | | `candidate.firm.hint` | object | no | Optional country, city or domain to disambiguate the name. | | `candidate.firm.lei` | string | no | The firm's Legal Entity Identifier. | | `candidate.firm.cik` | string | no | The firm's SEC Central Index Key. | | `candidate.firm.crd` | string | no | The firm's CRD number. | | `candidate.firm.companies_house` | string | no | The firm's UK Companies House number. | | `candidate.title_observed_at` | string (date-time) | no | When you last saw this title, to judge staleness. | | `icp_id` | string | yes | The ICP profile to decide against. | ### RightPersonResponse The `right_person` decision envelope. | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The decision id. Report outcomes against it. | | `decision` | enum: "right_person" | 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: "right_person", "not_right_person" | yes | `right_person` or `not_right_person`: `p` applied to your threshold. | | `reason_code` | enum: "title_and_seniority_match", "title_match_seniority_mismatch", "seniority_match_title_mismatch", "department_mismatch", "firm_not_in_icp", "candidate_data_stale", "insufficient_candidate_data" | yes | Why, from the fixed `right_person` 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. | ### RosterEntry One person at the firm. LinkedIn-sourced data is not accepted. | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | no | Your own id for this person, echoed back in `people`. | | `name` | string | yes | Full name. | | `title` | string | yes | Job title. | | `seniority` | enum: "owner", "c_level", "vp", "director", "manager", "senior", "entry", "unknown" | no | Seniority, if known. | | `department` | string | no | Department or function, for example `Operations`. | | `location` | string | no | City or country. | | `tenure_months` | integer | no | Months in the current role. | ### RotateKeyRequest Rotation options. | Field | Type | Required | Description | | --- | --- | --- | --- | | `grace_period_hours` | integer | no | Hours the old key stays valid; default 24. | ### RotateKeyResponse The replacement key (secret shown once) and the old key's grace window. | Field | Type | Required | Description | | --- | --- | --- | --- | | `key` | CreatedApiKey | yes | A new API key with its secret (shown once). | | `key.id` | string | yes | The key id. | | `key.project_id` | string | yes | Project the key belongs to. | | `key.name` | string | yes | Name. | | `key.environment` | enum: "live", "test" | yes | `live` or `test`. | | `key.prefix` | string | yes | The key's first characters, to recognize it. | | `key.scopes` | array of enum: "decisions:write", "index:read", "outcomes:write", "watches:manage", "vendors:call", "account:read", "account:manage", "keys:manage" | yes | Scopes granted. | | `key.status` | enum: "active", "rotating", "revoked", "expired" | yes | Lifecycle status. | | `key.rate_limit_per_minute` | integer | yes | Requests per minute allowed. | | `key.daily_spend_cap_usd` | number or null | yes | Daily spend cap, or `null` for none. US dollars, up to 6 decimals. | | `key.ip_allowlist` | array of string | yes | CIDR blocks the key may be used from; empty means anywhere. | | `key.expires_at` | string or null (date-time) | yes | Expiry, or `null` for none. | | `key.last_used_at` | string or null (date-time) | yes | Last successful use, or `null`. | | `key.created_at` | string (date-time) | yes | When the key was created. | | `key.secret` | string | yes | The API key: `vd_live_` or `vd_test_` plus a base62 body. Shown once; store it now. | | `previous_key` | object | yes | The old key and the end of its grace window. | | `previous_key.id` | string | yes | The rotated key. | | `previous_key.valid_until` | string (date-time) | yes | When the old key stops working. | ### ScoredEventDelivery A scored event delivered to a watch. | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | Delivery id; dedupe on it, retries reuse it. | | `type` | enum: "event.scored" | yes | Payload type. | | `created_at` | string (date-time) | yes | When the delivery was created. | | `watch_id` | string | yes | The watch. | | `event` | Event | yes | Something that happened to a firm, normalized to one schema; `attributes` are typed per `type` (one of `filing`, `registration`, `officer_change`, `ownership_change`, `lei_change`, `address_change`, `fund_raise`, `insider_trade`, `patent_filing`, `litigation`, `news_mention`, `repo_activity`, `hiring_aggregate`). | | `event.id` | string | no | The event id. | | `event.firm_id` | string | no | The firm the event happened to. | | `event.occurred_at` | string (date-time) | no | When the event happened, per the source. | | `event.observed_at` | string (date-time) | no | When Verdict ingested the event. | | `event.source` | enum: "sec_edgar", "gleif", "companies_house", "wikidata" | no | The index source. | | `event.source_url` | string (uri) | no | Link to the event at the source. | | `event.confidence` | number | no | Confidence in the event's normalization and firm keying. | | `event.score` | EventScore | no | The event's score for the requested ICP, or `null` when read without `icp_id`. | | `event.score.decision_id` | string | yes | The `score_signal` decision that produced this score (billed once). | | `event.score.icp_id` | string | yes | The ICP profile the event was scored against. | | `event.score.relevant` | number | yes | Calibrated probability that the event matters for the ICP's offer. | | `event.score.act_now` | number | yes | Calibrated probability that now is the time to act. | | `event.score.window_days` | integer or null | yes | Days left to act on the event, or `null` when there is no clear window. | | `event.score.answer` | enum: "relevant", "not_relevant" | yes | `relevant` applied to the threshold. | | `event.score.reason_code` | enum: "event_relevant_and_timely", "event_relevant_but_stale", "event_type_not_relevant", "event_routine", "firm_not_in_icp", "event_low_confidence", "event_superseded", "insufficient_event_data" | yes | Why, as a `score_signal` reason code. | | `event.type` | enum: "filing" | no | Event type `filing`. | | `event.attributes` | object | no | Attributes of a `filing` event. | | `event.attributes.filing_type` | enum: "sec_8k", "sec_form_d", "sec_form_4", "sec_s1", "sec_13f", "sec_adv" | yes | The filing sub-type. | | `event.attributes.accession_number` | string | no | SEC accession number. | | `event.attributes.items` | array of string | no | 8-K item numbers reported, for `sec_8k`. | | `event.attributes.title` | string | no | Filing title or form description. | | `event.attributes.form` | string | no | The form type exactly as the source lists it, for example `8-K/A` or `13F-HR`. | | `event.attributes.file_number` | string | no | SEC file number, for example `333-299845`. | | `score` | EventScore and any | yes | | | `score.decision_id` | string | yes | The `score_signal` decision that produced this score (billed once). | | `score.icp_id` | string | yes | The ICP profile the event was scored against. | | `score.relevant` | number | yes | Calibrated probability that the event matters for the ICP's offer. | | `score.act_now` | number | yes | Calibrated probability that now is the time to act. | | `score.window_days` | integer or null | yes | Days left to act on the event, or `null` when there is no clear window. | | `score.answer` | enum: "relevant", "not_relevant" | yes | `relevant` applied to the threshold. | | `score.reason_code` | enum: "event_relevant_and_timely", "event_relevant_but_stale", "event_type_not_relevant", "event_routine", "firm_not_in_icp", "event_low_confidence", "event_superseded", "insufficient_event_data" | yes | Why, as a `score_signal` reason code. | ### ScoreSignalRequest An `event_id` from the index, or an inline `event`, and the ICP to score it for. | Field | Type | Required | Description | | --- | --- | --- | --- | | `event_id` | string | no | An event from the index. | | `icp_id` | string | no | The ICP profile to decide against. | | `event` | InlineEvent | no | An event the caller supplies, for events Verdict's index does not hold. | | `event.type` | enum: "filing", "registration", "officer_change", "ownership_change", "lei_change", "address_change", "fund_raise", "insider_trade", "patent_filing", "litigation", "news_mention", "repo_activity", "hiring_aggregate" | yes | Event type. | | `event.firm` | FirmReference | yes | A firm reference: exactly one of `firm_id`, `domain`, `name` (with an optional `hint`), `lei`, `cik`, `crd` or `companies_house`. | | `event.firm.firm_id` | string | no | A firm id from Verdict's index. | | `event.firm.domain` | string | no | The firm's web domain. | | `event.firm.name` | string | no | The firm's legal or trading name. | | `event.firm.hint` | object | no | Optional country, city or domain to disambiguate the name. | | `event.firm.lei` | string | no | The firm's Legal Entity Identifier. | | `event.firm.cik` | string | no | The firm's SEC Central Index Key. | | `event.firm.crd` | string | no | The firm's CRD number. | | `event.firm.companies_house` | string | no | The firm's UK Companies House number. | | `event.occurred_at` | string (date-time) | yes | When the event happened. | | `event.title` | string | no | One-line summary of the event. | | `event.description` | string | no | What happened, in plain text. At most 8192 bytes of UTF-8. | | `event.source_url` | string (uri) | no | Where the event was reported. | ### ScoreSignalResponse The `score_signal` decision envelope. | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The decision id. Report outcomes against it. | | `decision` | enum: "score_signal" | 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: "relevant", "not_relevant" | yes | `relevant` or `not_relevant`: `p` applied to your threshold. | | `reason_code` | enum: "event_relevant_and_timely", "event_relevant_but_stale", "event_type_not_relevant", "event_routine", "firm_not_in_icp", "event_low_confidence", "event_superseded", "insufficient_event_data" | yes | Why, from the fixed `score_signal` 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. | | `relevant` | number | yes | Probability the event matters for the ICP's offer; equal to `p`. | | `act_now` | number | yes | Probability that now is the time to act on it. | | `window_days` | integer or null | yes | Days left to act, or `null` when there is no clear window. | | `event_id` | string | yes | The scored event (an inline event is stored and given an id). | | `firm` | FirmSummary | yes | A firm's id, names, identifiers and public facts. | | `firm.id` | string | yes | The firm id. | | `firm.name` | string | yes | Canonical firm name. | | `firm.domain` | string or null | yes | Primary web domain, if known. | | `firm.identifiers` | object | yes | Registry identifiers, by kind. | | `firm.identifiers.lei` | string | no | Legal Entity Identifier. | | `firm.identifiers.cik` | string | no | SEC Central Index Key. | | `firm.identifiers.crd` | string | no | CRD number. | | `firm.identifiers.companies_house` | string | no | UK Companies House number. | | `firm.identifiers.wikidata` | string | no | Wikidata item id. | | `firm.type` | enum: "operating_company", "investment_adviser", "broker_dealer", "investment_fund", "nonprofit", "public_body", "other", "unknown" | yes | Coarse firm type. | | `firm.size_band` | enum: "micro", "small", "medium", "large", "enterprise", "unknown" | yes | Employee-count size band. | | `firm.hq` | object | yes | Headquarters location. | | `firm.hq.country` | string or null | yes | ISO 3166-1 alpha-2 country. | | `firm.hq.region` | string or null | yes | State, county or region, as the registry gives it. | | `firm.hq.city` | string or null | yes | City. | | `firm.status` | enum: "active", "inactive", "dissolved", "unknown", "merged" | yes | Registry status. | ### ShouldSendRequest A prepared message, the decisions behind it, inbox health and the ICP. | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | object | yes | The prepared message. | | `message.channel` | enum: "email", "other" | yes | Channel the message will go out on. | | `message.subject` | string | no | Subject line. | | `message.body` | string | yes | Plain-text body as it will be sent. At most 8192 bytes of UTF-8. | | `decision_ids` | array of string | yes | The decisions behind the message (for example `qualify_firm` and `pick_person`), 1 to 10. | | `inbox_health` | InboxHealth | yes | Inbox health you measure; Verdict never reads your inbox. | | `inbox_health.bounce_rate_7d` | number | no | Share of sends that bounced over the last 7 days. | | `inbox_health.spam_complaint_rate_7d` | number | no | Share of sends marked as spam over the last 7 days. | | `inbox_health.reply_rate_7d` | number | no | Share of sends replied to over the last 7 days. | | `inbox_health.sent_today` | integer | no | Messages sent from this inbox today. | | `inbox_health.daily_limit` | integer | no | Your daily send limit for this inbox. | | `inbox_health.domain_age_days` | integer | no | Age of the sending domain in days. | | `recipient` | object | no | Optional contact history of the recipient. | | `recipient.last_contacted_at` | string (date-time) | no | When you last wrote to this recipient. | | `recipient.touches_30d` | integer | no | Messages sent to this recipient in 30 days. | | `icp_id` | string | yes | The ICP profile to decide against. | ### ShouldSendResponse The `should_send` decision envelope. | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The decision id. Report outcomes against it. | | `decision` | enum: "should_send" | 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: "send", "hold" | yes | `send` or `hold`: `p` applied to your threshold. | | `reason_code` | enum: "all_checks_passed", "upstream_decision_below_threshold", "upstream_decision_missing", "inbox_health_degraded", "daily_volume_limit_reached", "recipient_recently_contacted", "message_quality_issue", "recipient_opted_out" | yes | Why, from the fixed `should_send` 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. | | `send` | number | yes | Probability that sending now is right; equal to `p`. | | `hold_reason` | enum: "upstream_decision_weak", "upstream_decision_missing", "inbox_health_degraded", "daily_volume_limit", "recently_contacted", "message_issue", "recipient_opted_out", "low_confidence", null | yes | Why to hold, when `answer` is `hold`; `null` when `answer` is `send`. | ### SignOutResponse The session was revoked and the cookie cleared. | Field | Type | Required | Description | | --- | --- | --- | --- | | `status` | enum: "signed_out" | yes | The session was revoked. | ### SignupRequest Start sign-up with an email address. | Field | Type | Required | Description | | --- | --- | --- | --- | | `email` | string (email) | yes | Work email. A verification link is sent to it; disposable domains are refused. | | `org_name` | string | no | Organization name; defaults to the email's domain. | ### SignupResponse Sign-up accepted; verify the email to get a key. | Field | Type | Required | Description | | --- | --- | --- | --- | | `status` | enum: "verification_sent" | yes | A verification email was sent. | | `email` | string (email) | yes | The address it was sent to. | | `expires_at` | string (date-time) | yes | When the verification link expires. | ### Source An open-licensed source of Verdict's index. | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The source id. | | `source` | enum: "sec_edgar", "gleif", "companies_house", "wikidata" | yes | Stable source slug, as cited in `evidence[].source`. | | `name` | string | yes | Human name. | | `phase` | integer | yes | Release phase that ships the source. | | `license` | object | yes | The source's license. | | `license.kind` | enum: "public_domain", "cc0", "ogl_v3", "cc_by_4_0", "cc_by_3_0_au" | yes | License kind. | | `license.name` | string | yes | License name. | | `license.url` | string (uri) | yes | License text. | | `license.attribution` | string or null | yes | Attribution text to show when you display the data, if required. | | `refresh_cadence` | string | yes | How often Verdict refreshes it, for example `daily`. | | `rate_limit` | string or null | yes | The source's fair-access limit Verdict observes. | | `last_successful_run` | object or null | yes | Latest successful ingestion, or `null` if none yet. | | `last_successful_run.id` | string | yes | The run id. | | `last_successful_run.status` | enum: "running", "succeeded", "partial", "failed" | yes | Run status. | | `last_successful_run.started_at` | string (date-time) | yes | When the run started. | | `last_successful_run.finished_at` | string or null (date-time) | yes | When the run finished. | | `last_successful_run.events_upserted` | integer | yes | Events inserted or updated. | | `last_run` | object or null | yes | Latest ingestion run of any status (it may still be running or have failed), or `null` if none yet. | | `last_run.id` | string | yes | The run id. | | `last_run.status` | enum: "running", "succeeded", "partial", "failed" | yes | Run status. | | `last_run.started_at` | string (date-time) | yes | When the run started. | | `last_run.finished_at` | string or null (date-time) | yes | When the run finished. | | `last_run.events_upserted` | integer | yes | Events inserted or updated. | | `freshness_seconds` | integer or null | yes | Seconds since the latest successful run finished: how fresh this source's data is. `null` if it has never succeeded. | ### StripeWebhookBody A Stripe event object (`id` `evt_...`, `type`, `data.object`), exactly as Stripe sends it. Its shape is defined by Stripe. Type: `object`. ### StripeWebhookReceipt Acknowledgement of a Stripe event. | Field | Type | Required | Description | | --- | --- | --- | --- | | `received` | enum: true | yes | The event was verified and recorded. | | `duplicate` | boolean | yes | True when this Stripe event id was already recorded; nothing changed. | ### ThreadMessage One message of a thread, oldest first. | Field | Type | Required | Description | | --- | --- | --- | --- | | `role` | enum: "sender", "prospect", "system" | yes | Who wrote the message. | | `text` | string | yes | Plain-text body of the message. At most 8192 bytes of UTF-8. | | `sent_at` | string (date-time) | yes | When the message was sent. | ### Thresholds Decision thresholds, one per decision. | Field | Type | Required | Description | | --- | --- | --- | --- | | `qualify_firm` | object | yes | Threshold for `qualify_firm`. | | `qualify_firm.value` | number | yes | The threshold `answer` applies to `p`. | | `qualify_firm.source` | enum: "calibrated", "custom" | yes | Calibrated default or your own value. | | `score_signal` | object | yes | Threshold for `score_signal`. | | `score_signal.value` | number | yes | The threshold `answer` applies to `p`. | | `score_signal.source` | enum: "calibrated", "custom" | yes | Calibrated default or your own value. | | `pick_person` | object | yes | Threshold for `pick_person`. | | `pick_person.value` | number | yes | The threshold `answer` applies to `p`. | | `pick_person.source` | enum: "calibrated", "custom" | yes | Calibrated default or your own value. | | `triage_reply` | object | yes | Threshold for `triage_reply`. | | `triage_reply.value` | number | yes | The threshold `answer` applies to `p`. | | `triage_reply.source` | enum: "calibrated", "custom" | yes | Calibrated default or your own value. | | `right_person` | object | yes | Threshold for `right_person`. | | `right_person.value` | number | yes | The threshold `answer` applies to `p`. | | `right_person.source` | enum: "calibrated", "custom" | yes | Calibrated default or your own value. | | `should_send` | object | yes | Threshold for `should_send`. | | `should_send.value` | number | yes | The threshold `answer` applies to `p`. | | `should_send.source` | enum: "calibrated", "custom" | yes | Calibrated default or your own value. | ### TopupRequest The pack to buy. | Field | Type | Required | Description | | --- | --- | --- | --- | | `pack_usd` | enum: 20 or enum: 50 or enum: 200 | yes | Pack to buy: 20, 50 or 200 US dollars. | ### TopupResponse A pending pack purchase with its Checkout URL. | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The pack purchase id. | | `pack_usd` | enum: 20 or enum: 50 or enum: 200 | yes | Pack size. | | `status` | enum: "pending", "completed", "expired", "failed" | yes | Purchase status. | | `checkout_url` | string (uri) | yes | Stripe Checkout URL; open it to pay. The balance is credited when Stripe confirms payment. | | `expires_at` | string (date-time) | yes | When the Checkout session expires. | ### TotpCodeRequest A current TOTP code. | Field | Type | Required | Description | | --- | --- | --- | --- | | `code` | string | yes | The six-digit code from the authenticator app. | ### TotpEnrolResponse A pending TOTP secret; it is active once a code is verified. | Field | Type | Required | Description | | --- | --- | --- | --- | | `secret` | string | yes | The base32 TOTP secret (shown once). Add it to an authenticator app, then confirm with a code. | | `otpauth_uri` | string | yes | The same secret as an `otpauth://` URI, to render as a QR code. | ### TotpStatusResponse The user's TOTP state after the call. | Field | Type | Required | Description | | --- | --- | --- | --- | | `totp_enabled` | boolean | yes | Whether TOTP is now on for the user. | | `mfa_verified` | boolean | yes | Whether this session has now passed TOTP. | ### TriageReplyRequest A reply thread and optional context. | 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. | ### TriageReplyResponse The `triage_reply` decision envelope. | 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. | ### UpdateIcpRequest Fields of an ICP profile to change. | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | no | New name. | | `description` | string | no | New plain-text description; re-runs summarization. At most 8192 bytes of UTF-8. | | `filters` | IcpFilters | no | Optional structured filters that sharpen the plain-text profile. | | `filters.firm_types` | array of enum: "operating_company", "investment_adviser", "broker_dealer", "investment_fund", "nonprofit", "public_body", "other", "unknown" | no | Firm types to target. | | `filters.size_bands` | array of enum: "micro", "small", "medium", "large", "enterprise", "unknown" | no | Size bands to target. | | `filters.countries` | array of string | no | Headquarters countries to target. | | `filters.exclude_countries` | array of string | no | Headquarters countries to exclude. | | `filters.exclude_domains` | array of string | no | Firms to exclude by domain (for example existing customers). | | `filters.event_types` | array of enum: "filing", "registration", "officer_change", "ownership_change", "lei_change", "address_change", "fund_raise", "insider_trade", "patent_filing", "litigation", "news_mention", "repo_activity", "hiring_aggregate" | no | Event types that signal buying intent for this ICP. | ### UpdateKeyRequest Fields of an API key to change. | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | no | New name. | | `scopes` | array of enum: "decisions:write", "index:read", "outcomes:write", "watches:manage", "vendors:call", "account:read", "account:manage", "keys:manage" | no | Replacement scopes. A key can only grant scopes the calling key holds itself. Audited as `key_scopes_changed`. | | `rate_limit_per_minute` | integer | no | Requests per minute. | | `daily_spend_cap_usd` | number or null | no | Daily spend cap; `null` removes it. US dollars, up to 6 decimals. | | `ip_allowlist` | array of string (ipv4-cidr) or string (ipv6-cidr) | no | CIDR blocks the key may be used from; empty means anywhere. | | `expires_at` | string or null (date-time) | no | Expiry; `null` removes it. | ### UpdateMemberRequest Change a member's role. | Field | Type | Required | Description | | --- | --- | --- | --- | | `role` | enum: "owner", "admin", "developer", "billing", "read_only" | yes | The new role. Only an owner may grant or remove `owner`. | ### UpdateThresholdsRequest Set or reset decision thresholds on an ICP profile. | Field | Type | Required | Description | | --- | --- | --- | --- | | `thresholds` | object | yes | Thresholds to change, by decision name. | | `thresholds.qualify_firm` | number or null | no | New threshold for `qualify_firm`, or `null` to return to the calibrated default. | | `thresholds.score_signal` | number or null | no | New threshold for `score_signal`, or `null` to return to the calibrated default. | | `thresholds.pick_person` | number or null | no | New threshold for `pick_person`, or `null` to return to the calibrated default. | | `thresholds.triage_reply` | number or null | no | New threshold for `triage_reply`, or `null` to return to the calibrated default. | | `thresholds.right_person` | number or null | no | New threshold for `right_person`, or `null` to return to the calibrated default. | | `thresholds.should_send` | number or null | no | New threshold for `should_send`, or `null` to return to the calibrated default. | ### UsageResponse Usage over a window. | Field | Type | Required | Description | | --- | --- | --- | --- | | `from` | string (date) | yes | First day included. | | `to` | string (date) | yes | Last day included. | | `group_by` | enum: "day", "project", "key", "decision" | yes | Grouping used. | | `rows` | array of object | yes | One row per group. | | `rows[].calls` | integer | yes | Calls made. | | `rows[].billed_calls` | integer | yes | Calls billed (misses and errors are free). | | `rows[].decision_usd` | number | yes | Decision charges. US dollars, up to 6 decimals. | | `rows[].vendor_usd` | number | yes | Vendor pass-through charges. US dollars, up to 6 decimals. | | `rows[].total_usd` | number | yes | Total charges. US dollars, up to 6 decimals. | | `rows[].group` | string | yes | Group key: a date, project id, key id or decision name. | | `totals` | object | yes | Totals over the window; equal to the ledger sums. | | `totals.calls` | integer | yes | Calls made. | | `totals.billed_calls` | integer | yes | Calls billed (misses and errors are free). | | `totals.decision_usd` | number | yes | Decision charges. US dollars, up to 6 decimals. | | `totals.vendor_usd` | number | yes | Vendor pass-through charges. US dollars, up to 6 decimals. | | `totals.total_usd` | number | yes | Total charges. US dollars, up to 6 decimals. | ### VendorWebhookBody The vendor's webhook payload, unchanged. Its shape is defined by the vendor. Type: `object`. ### VerifySignupRequest Complete sign-up with the emailed token. | Field | Type | Required | Description | | --- | --- | --- | --- | | `token` | string | yes | The token from the verification link. | ### VerifySignupResponse The organization, project and first key. | Field | Type | Required | Description | | --- | --- | --- | --- | | `org_id` | string | yes | The new organization. | | `project_id` | string | yes | Its default project. | | `key` | CreatedApiKey | yes | A new API key with its secret (shown once). | | `key.id` | string | yes | The key id. | | `key.project_id` | string | yes | Project the key belongs to. | | `key.name` | string | yes | Name. | | `key.environment` | enum: "live", "test" | yes | `live` or `test`. | | `key.prefix` | string | yes | The key's first characters, to recognize it. | | `key.scopes` | array of enum: "decisions:write", "index:read", "outcomes:write", "watches:manage", "vendors:call", "account:read", "account:manage", "keys:manage" | yes | Scopes granted. | | `key.status` | enum: "active", "rotating", "revoked", "expired" | yes | Lifecycle status. | | `key.rate_limit_per_minute` | integer | yes | Requests per minute allowed. | | `key.daily_spend_cap_usd` | number or null | yes | Daily spend cap, or `null` for none. US dollars, up to 6 decimals. | | `key.ip_allowlist` | array of string | yes | CIDR blocks the key may be used from; empty means anywhere. | | `key.expires_at` | string or null (date-time) | yes | Expiry, or `null` for none. | | `key.last_used_at` | string or null (date-time) | yes | Last successful use, or `null`. | | `key.created_at` | string (date-time) | yes | When the key was created. | | `key.secret` | string | yes | The API key: `vd_live_` or `vd_test_` plus a base62 body. Shown once; store it now. | | `starter_credit_usd` | number | yes | Starter credits granted. US dollars, up to 6 decimals. | | `balance_usd` | number | yes | Balance after the grant. US dollars, up to 6 decimals. | ### Watch A standing scored-event subscription. | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The watch id. | | `project_id` | string | yes | Project the watch belongs to. | | `icp_id` | string | yes | ICP profile events are scored for. | | `webhook_url` | string (uri) | yes | Delivery endpoint. | | `min_p` | number | yes | Minimum `relevant` delivered. | | `types` | array of enum: "filing", "registration", "officer_change", "ownership_change", "lei_change", "address_change", "fund_raise", "insider_trade", "patent_filing", "litigation", "news_mention", "repo_activity", "hiring_aggregate" | yes | Event types watched. | | `status` | enum: "pending_verification", "active", "paused", "deleted" | yes | Lifecycle status. | | `created_at` | string (date-time) | yes | When the watch was created. | | `updated_at` | string (date-time) | yes | When the watch last changed. | ### WebhookChallenge The registration challenge sent to a new webhook URL. | Field | Type | Required | Description | | --- | --- | --- | --- | | `type` | enum: "webhook.challenge" | yes | Payload type. | | `watch_id` | string | yes | The watch being registered. | | `challenge` | string | yes | Random string; respond 200 with `{"challenge": }` within 10 seconds. | ### WebhookReceipt Acknowledgement of a vendor webhook. | Field | Type | Required | Description | | --- | --- | --- | --- | | `received` | enum: true | yes | The payload was verified and accepted. | | `duplicate` | boolean | yes | True when this vendor event id was already processed. | | `outcome_ids` | array of string | yes | Outcomes created from the payload; empty when it maps to none. |