Developer documentation

Build with Arvanic

Three programmatic surfaces, one set of scoped keys: a versioned REST read API, a native Model Context Protocol (MCP) server for AI agents, and signed webhooks. Everything here is read-only today and de-identified by default — see Confidentiality.

Authentication

Create an API key in Settings → API keys. You choose the scopes; the secret is shown once and stored only as a hash — copy it then. Keys are bound to your organization and revocable instantly. Authenticate every request with a bearer header:

Authorization: Bearer arv_live_…

Keys are formatted arv_<live|test>_<random>. Treat them like passwords — never embed a key in client-side code or a public repo.

Scopes

Each key carries only the scopes you grant. A request for an endpoint outside the key's scopes returns 403 with a required_scope. All scopes are read-only in the current release.

ScopeGrants
investigations:readRead the investigation list, status, and per-investigation metadata.
participants:readRead an investigation's participants — role-labeled, with status. Never names/emails.
reports:readList generated reports and mint short-lived report-PDF download links.
questions:readRead the approved question plan for an investigation.

REST API

Base URL: https://arvanic.io/api/v1

All responses are JSON. Collections return { data, count }; single resources return { data }. Investigation descriptions are intentionally omitted (they can contain real names) — see Confidentiality.

MethodPathScopeReturns
GET/investigationsinvestigations:readList the organization's investigations (metadata + participant counts).
GET/investigations/{id}investigations:readA single investigation's metadata + participant counts.
GET/investigations/{id}/participantsparticipants:readParticipants, role-labeled (e.g. “Complainant 1”) with status.
GET/investigations/{id}/questionsquestions:readThe approved question plan.
GET/investigations/{id}/reportsreports:readGenerated-report metadata for an investigation.
GET/reports/{id}/downloadreports:readA short-lived (60s) signed URL for the report PDF.

Example — list investigations

curl https://arvanic.io/api/v1/investigations \
  -H "Authorization: Bearer arv_live_your_key_here"
{
  "data": [
    {
      "id": "b3c1…",
      "reference": "ARV-2026-0001",
      "type": "harassment",
      "custom_title": null,
      "status": "open",
      "report_label": "initial",
      "created_at": "2026-06-01T12:00:00.000Z",
      "closed_at": null,
      "participants": 3,
      "participants_submitted": 1
    }
  ],
  "count": 1
}

Example — participants (role-labeled)

{
  "data": [
    { "id": "…", "label": "Complainant 1", "role": "complainant",
      "status": "submitted", "invited": true, "submitted_at": "2026-06-02T09:00:00.000Z" },
    { "id": "…", "label": "Witness 1", "role": "witness",
      "status": "invited", "invited": true, "submitted_at": null }
  ],
  "count": 2
}

MCP server

Endpoint: https://arvanic.io/api/v1/mcp

Arvanic speaks the Model Context Protocol over the Streamable-HTTP transport (JSON-RPC: initialize, tools/list, tools/call, ping). Point any MCP-capable client at the endpoint with your key and it discovers the tools automatically — verified against the official MCP SDK client.

Connect an MCP client

{
  "mcpServers": {
    "arvanic": {
      "type": "http",
      "url": "https://arvanic.io/api/v1/mcp",
      "headers": { "Authorization": "Bearer arv_live_your_key_here" }
    }
  }
}

Or call it directly

# initialize → list tools → call a tool (raw JSON-RPC over HTTP)
curl https://arvanic.io/api/v1/mcp \
  -H "Authorization: Bearer arv_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

Available tools

ToolScopeDescription
list_investigationsinvestigations:readList investigations (metadata + counts).
get_investigationinvestigations:readOne investigation by id.
list_participantsparticipants:readRole-labeled participants + status.
list_reportsreports:readGenerated-report metadata.
get_questionsquestions:readThe approved question plan.
get_report_download_urlreports:readShort-lived signed report-PDF URL.

Webhooks

Register an endpoint in Settings → Webhooks (HTTPS only; public hosts only). Arvanic POSTs a signed JSON body when a subscribed event occurs. The signing secret is shown once at creation.

Events

report.generatedA report finished generating.
interview.submittedA participant submitted their interview.
investigation.closedAn investigation was closed.

Delivery headers

  • X-Arvanic-Event — the event type.
  • X-Arvanic-Delivery — unique delivery id (for idempotency).
  • X-Arvanic-Signaturesha256=<hex> HMAC of the raw body.

Payload

Payloads are minimal and non-identifying — IDs, not contents. Fetch detail under your own key.

{
  "event": "report.generated",
  "occurred_at": "2026-06-13T15:30:00.000Z",
  "organization_id": "…",
  "data": {
    "investigation_id": "…",
    "report_id": "…"
  }
}

Verify the signature

import crypto from "node:crypto";

// Express-style handler. Use the RAW request body for the HMAC.
function verifyArvanicWebhook(rawBody, signatureHeader, secret) {
  // Header format: "sha256=<hex>"
  const expected =
    "sha256=" + crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
  const a = Buffer.from(signatureHeader || "");
  const b = Buffer.from(expected);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Respond 2xx to acknowledge. Non-2xx (or timeout) is retried with exponential backoff; an endpoint that keeps failing is auto-disabled. Always verify the signature before trusting a payload.

Errors & rate limits

Errors are JSON: { "error": "…", "type": "https://arvanic.io/errors/…" }. Programmatic surfaces are rate-limited per key and fail closed.

StatusMeaning
401Missing, malformed, invalid, revoked, or expired API key.
403The key is valid but lacks the required scope (response includes required_scope).
404The resource doesn't exist in your organization (no cross-tenant existence leak).
429Per-key rate limit exceeded. Honor the Retry-After header.
503The rate limiter is temporarily unreachable — the API fails closed. Retry shortly.

Confidentiality & the agent posture

  • De-identified by default. Structured reads return participants role-labeled(“Complainant 1”), never as names or emails. The one place real names appear is the report PDF you download under reports:read— it is the Case Manager's finished work product.
  • Tenant-isolated.Every key is bound to one organization; there is no path to another organization's data.
  • Human stays accountable. The interface is read-only. Legally-significant actions — sending invitations, finalizing reports, certifications — remain with the named human investigator. An agent acts on your behalf; it never becomes you.
  • Audited. Key creation and revocation are recorded in the immutable audit trail.

More on the platform vision and roadmap: Platform & API. How we handle data: Trust Center. Questions about a specific integration? Talk to us.