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.
| Scope | Grants |
|---|---|
investigations:read | Read the investigation list, status, and per-investigation metadata. |
participants:read | Read an investigation's participants — role-labeled, with status. Never names/emails. |
reports:read | List generated reports and mint short-lived report-PDF download links. |
questions:read | Read 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.
| Method | Path | Scope | Returns |
|---|---|---|---|
GET | /investigations | investigations:read | List the organization's investigations (metadata + participant counts). |
GET | /investigations/{id} | investigations:read | A single investigation's metadata + participant counts. |
GET | /investigations/{id}/participants | participants:read | Participants, role-labeled (e.g. “Complainant 1”) with status. |
GET | /investigations/{id}/questions | questions:read | The approved question plan. |
GET | /investigations/{id}/reports | reports:read | Generated-report metadata for an investigation. |
GET | /reports/{id}/download | reports:read | A 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
| Tool | Scope | Description |
|---|---|---|
list_investigations | investigations:read | List investigations (metadata + counts). |
get_investigation | investigations:read | One investigation by id. |
list_participants | participants:read | Role-labeled participants + status. |
list_reports | reports:read | Generated-report metadata. |
get_questions | questions:read | The approved question plan. |
get_report_download_url | reports:read | Short-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.generated | A report finished generating. |
interview.submitted | A participant submitted their interview. |
investigation.closed | An investigation was closed. |
Delivery headers
X-Arvanic-Event— the event type.X-Arvanic-Delivery— unique delivery id (for idempotency).X-Arvanic-Signature—sha256=<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.
| Status | Meaning |
|---|---|
401 | Missing, malformed, invalid, revoked, or expired API key. |
403 | The key is valid but lacks the required scope (response includes required_scope). |
404 | The resource doesn't exist in your organization (no cross-tenant existence leak). |
429 | Per-key rate limit exceeded. Honor the Retry-After header. |
503 | The 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.