Skip to content

API reference

Developers

The API

A REST API over accounts, decisions, evidence, campaigns and dossiers. JSON in, JSON out, cursor paginated.

/docs/api17 / 20

A REST API over the things the system decides: accounts, decisions, the evidence behind them, campaigns and dossiers. JSON in, JSON out, cursor paginated, authenticated with a bearer token.

The API is read-heavy by design. You can request work — a dossier, a re-qualification — and you can record outcomes. You cannot write an evidence record or set a verdict directly, because a claim that arrived without a source would break the one rule the rest of the system depends on.

Base URL and versioning#

Base URL
https://api.arvyn.io/v1
Transport
HTTPS only — plain HTTP is refused, not redirected
Content type
application/json
Dates
ISO 8601, UTC, e.g. 2026-08-14T09:12:44Z
Versioning
in the path; additive changes ship without a version bump

These are treated as backwards-compatible and can arrive at any time: new fields on a response, new enum values, new endpoints, and new optional request parameters. Write clients that ignore fields they do not recognise.

Authentication#

A bearer token in the Authorization header. Tokens are created per workspace in Settings → API, are scoped to that workspace, and are shown once at creation.

curl https://api.arvyn.io/v1/accounts \
  -H "Authorization: Bearer $ARVYN_API_KEY"
ScopeGrantsUse for
readEvery GET endpointReporting, dashboards, warehouse sync
writeRequesting dossiers and re-qualifications, recording outcomesTwo-way CRM integrations
adminMailbox, campaign and webhook configurationProvisioning; avoid in day-to-day integrations

Errors#

Conventional status codes, with a machine-readable code and a human-readable message. The message is for your logs; branch on the code.

Error body
{
  "error": {
    "code": "evidence_expired",
    "message": "Decision DEC-4102 rests on evidence that has since been superseded.",
    "status": 409,
    "request_id": "req_01J9F2K4Q7",
    "docs": "https://arvyn.io/docs/concepts/evidence#expiry"
  }
}
StatusCodeMeans
400invalid_requestMalformed body, or a parameter outside its allowed range
401unauthenticatedMissing, malformed or revoked token
403insufficient_scopeValid token, wrong scope for this endpoint
404not_foundNo such record in this workspace — including records that exist in another
409conflictThe resource changed underneath you; re-read and retry
422unprocessableWell-formed but not permitted, e.g. requesting a dossier for a D-tier account
429rate_limitedSee rate limits below
5xxinternalRetry with backoff; include request_id if you report it

Always log request_id. It is the only thing that makes a support conversation about a specific failure tractable.

Pagination#

Cursor-based. Offsets are not supported, because the underlying records are re-ranked continuously and page 4 of an offset-paginated qualification list is not a stable thing.

limitintegeroptional
Page size, 1–200. Defaults to 50.
cursorstringoptional
The next_cursor from the previous response. Omit for the first page.
orderstringoptional
created_at or contact_by. Defaults to contact_by on decisions, because that is the order the work should actually be done in.
Paginated envelope
{
  "data": [ /* … */ ],
  "next_cursor": "eyJvIjoiMjAyNi0wOC0wN1QwOTowMDowMFoifQ",
  "has_more": true
}

Stop when has_more is false. Do not stop on an empty data array — a page can be empty and still have more behind it when filtering is applied server-side.

Rate limits#

Read endpoints
600 requests / minute / workspace
Write endpoints
60 requests / minute / workspace
Dossier requests
20 / hour / workspace
Headers
X-RateLimit-Limit · X-RateLimit-Remaining · X-RateLimit-Reset
On 429
Retry-After, in seconds

Honour Retry-After rather than retrying on a fixed schedule. Repeated retries against a limited endpoint extend the window rather than shortening it.

Idempotency#

Every POST accepts an Idempotency-Key header. Requests with the same key inside 24 hours return the original response instead of doing the work twice.

A safely retryable write
curl -X POST https://api.arvyn.io/v1/dossiers \
  -H "Authorization: Bearer $ARVYN_API_KEY" \
  -H "Idempotency-Key: dossier-ACC-19022-2026-08-12" \
  -H "Content-Type: application/json" \
  -d '{ "account_id": "ACC-19022", "focus": "operations" }'

Resources#

Accounts#

A company in the workspace graph, with its current tier, window state and score.

GET/v1/accounts
GET/v1/accounts/{account_id}
GET/v1/accounts/{account_id}/score
POST/v1/accounts/{account_id}/requalify
tierA | B | C | Doptional
Filter by current tier.
windowopen | closed | heldoptional
Filter by window state.
icp_idstringoptional
Restrict to accounts qualified under one profile.
updated_sincedateoptional
Only accounts whose tier, score or evidence changed after this time. The right filter for an incremental sync.
Account
{
  "id": "ACC-19022",
  "name": "Northwind Freight",
  "domain": "example-northwind.test",
  "tier": "B",
  "score": 76,
  "window": { "state": "open", "opened_at": "2026-06-19", "estimated_close": "2026-10-22" },
  "latest_decision": "DEC-4417",
  "contact_by": "2026-08-07",
  "updated_at": "2026-07-24T06:02:11Z"
}

Decisions#

Immutable qualification records. A re-qualification creates a new record that supersedes the old one; nothing is edited in place. See reading a decision record.

GET/v1/decisions
GET/v1/decisions/{decision_id}
GET/v1/decisions/{decision_id}/deliberation

The deliberation endpoint returns the full transcript — every turn, its move, and the citation it leaned on. It is the endpoint to use if you want to audit whether the adversarial pass is doing anything in your workspace.

Evidence#

Read-only. Records are created by ingestion, never by clients.

GET/v1/evidence
GET/v1/evidence/{evidence_id}
GET/v1/accounts/{account_id}/evidence
kindstringoptional
Signal family: hiring, funding, leadership, expansion, tech_change, commitment, distress.
strengthstrong | moderate | weakoptional
Minimum strength to return.
stateverified | stale | changed | deadoptional
Verification state. Defaults to excluding dead records.

Campaigns#

GET/v1/campaigns
GET/v1/campaigns/{campaign_id}
GET/v1/campaigns/{campaign_id}/messages
POST/v1/campaigns/{campaign_id}/pause
POST/v1/outcomes

POST /v1/outcomes is how a reply that arrived somewhere other than the connected mailbox — in your CRM, on the phone, at a conference — gets into the learning loop.

POST /v1/outcomes
{
  "account_id": "ACC-19022",
  "kind": "reply",
  "classification": "not_now",
  "revisit_at": "2026-10-01",
  "source": "crm",
  "note": "Told us on a call: budget reopens in Q4."
}

Dossiers#

POST/v1/dossiers
GET/v1/dossiers/{dossier_id}
GET/v1/accounts/{account_id}/dossiers

Creation is asynchronous. The POST returns immediately with a pending state; subscribe to dossier.completed rather than polling.

  • Formats. GET returns structured JSON by default; pass Accept: application/pdf for the rendered document.
  • Citations are inline. Every claim in the JSON body carries its evidence id, so a client can render the sources without a second request.
  • Focus and meeting time. Both optional; see requesting one.

Next: webhooks, which is how you find out about any of this without polling.