Developers

Four steps to your first verified result.

Chirograph Verify is a server-to-server API plus an optional hosted widget. This guide reflects the real endpoints and payloads the service exposes.

Quick start

Four steps to a verified result.

  1. Get a tenant and an API key

    Tenants are provisioned by an operator. Your secret X-API-Key authenticates server-to-server calls and is only ever stored server-side as a hash.

  2. Request a verification challenge

    Each challenge is single-use and scoped to a user reference.

    Create a challenge
    POST /v1/challenge (X-API-Key: your server secret) { "tenant_id": "UUID_OF_YOUR_TENANT", "user_reference": "user@example.com", "redirect_url": "https://app.example.com/auth/callback", "purpose": "authenticate" } → WebAuthn PublicKeyCredentialRequestOptions (the challenge)
  3. The user approves on their device

    Start a hosted flow with the widget, or drive the ceremony yourself with the returned options. The hosted page is served from the Chirograph origin so origin and relying-party checks always match.

  4. Submit the assertion and get a verified result

    Submit the assertion
    POST /v1/verify (X-API-Key: your server secret) { "challenge_id": "CHALLENGE_UUID", "response": { "id": "...", "rawId": "...", "type": "public-key", "response": { "clientDataJSON": "...", "authenticatorData": "...", "signature": "..." } } } → { "verified": true, "device_trust_score": 87 }
Hosted widget

Start a WebAuthn flow from your site.

The widget SDK (public/widget.js) starts an origin-bound, single-use hosted flow with a publishable widget key. The ceremony happens on the Chirograph origin — never in an iframe, never via your JavaScript.

In the browser

public/widget.js
const widget = ChirographWidget.init({ baseUrl: 'https://YOUR-DOMAIN', // HTTPS origin widgetKey: 'PUBLISHABLE_WIDGET_KEY' // NOT a secret }); await widget.start({ userReference: 'user@example.com', purpose: 'authenticate', redirectUrl: 'https://app.example.com/auth/callback' });

On your server

Your backend redeems the opaque result server-to-server so a redirected user is never treated as verified by itself:

Server-side redemption
POST /v1/widget/redeem (X-API-Key: your server secret) { "result_token": "OPAQUE_FLOW_RESULT" } → { "flow_id": "...", "verified": true, "device_trust_score": 42 }

Redemption is single-use and tenant-scoped. Origin and redirect allowlists are configured per widget client.

Webhooks

Learn about verifications asynchronously.

A successful verification can enqueue a verification.completed event delivered to your HTTPS endpoint. Each delivery carries an HMAC-SHA256 signature over the exact body bytes and a stable delivery_id, so you can verify authenticity and deduplicate.

Inbound (from Chirograph Verify)
POST /your/webhook (from Chirograph Verify) headers: Content-Type: application/json x-chirograph-signature: <hmac-sha256 hex> x-chirograph-delivery-id: <stable uuid> body: { "event": "verification.completed", "verification_id": "...", "tenant_id": "...", "verified": true, "device_trust_score": 87, "timestamp": "2025-01-01T00:00:00.000Z" }

Set your webhook URL and signing secret in the tenant dashboard. Delivery is asynchronous with a bounded retry schedule; failures never change a verification result.

Reference

Public surface at a glance.

RouteAuthPurpose
POST /v1/challengeX-API-KeyCreate a single-use WebAuthn verification challenge.
POST /v1/verifyX-API-KeyVerify an assertion and return a boolean result + trust score.
POST /v1/widget/flowsX-Widget-Key + OriginStart a hosted WebAuthn flow.
POST /v1/widget/redeemX-API-KeyRedeem an opaque flow result server-to-server.
GET/POST /dashboard/*dashboard sessionTenant usage, settings, API-key rotation, webhooks.
POST /v1/billing/flutterwave/webhookprovider signatureInbound Flutterwave billing webhook (idempotent).

Example values are illustrative, not valid credentials. Integration guidance lives in the repository docs.

Open the dashboard