Developer docs

Build on verified skill data.

Use Humanproof's public API, sandbox fixtures, and signed webhooks to plug a credit score for your skills into search, profile, marketplace, and agent workflows.

Quickstart

A practical order for getting from dashboard setup to a production integration.

01

Create a scoped API key

Use least-privilege scopes. Candidate reads need candidates:read, score lookups need scores:read, webhook registration needs webhooks:write, and private agent reports need agent-decisions:write.

Manage API keys
02

Wire against sandbox shapes

Use fictional candidate and score payloads before touching live metered endpoints. The sandbox is not billed and mirrors the public response envelopes.

Open sandbox
03

Call the public v1 API

Send your API key as a bearer token. Include Humanproof-Version when you want explicit version pinning.

Open OpenAPI
04

Subscribe to signed events

Register an HTTPS endpoint, store the one-time signing secret, send a test event, then monitor recent delivery attempts.

Manage webhooks
05

Report agent outcomes

POST private decision reports with an idempotency key, then review the account-level audit trail in the dashboard.

View reports

API reference

The OpenAPI document is the source of truth for route schemas. This table is the operator view.

GET/api/v1/candidates/searchcandidates:read or x402

Search public candidate previews by skill, display name, title, or handle.

GET/api/v1/candidates/{username}candidates:read or x402

Read one public candidate profile. Private evidence and worker ids are never returned.

GET/api/v1/scores/{scoreId}scores:read or x402

Read one active public score envelope for an onboarded public profile.

POST/api/v1/score-vc/verifypublic, optional scores:read

Verify a presented Score VC with issuer pinning, Ed25519 signature checks, and revocation lookup.

POST/api/v1/agent-decisionsagent-decisions:write

Report a private decision or outcome after an agent uses candidate or score data.

POST/api/v1/webhooks/endpointswebhooks:write

Create an HTTPS webhook endpoint and receive the one-time signing secret.

DELETE/api/v1/webhooks/endpoints/{endpointId}webhooks:write

Disable a webhook endpoint. Disabled endpoints stop receiving future deliveries.

Requests

Copy these shapes into your client, then swap in your saved API key and endpoint URL.

Authenticated request

Bearer auth works across paid v1 reads. Version pinning is optional, but recommended for production clients.

curl "https://humanproof.io/api/v1/candidates/search?q=react&limit=5" \
  -H "Authorization: Bearer hp_live_..." \
  -H "Humanproof-Version: 2026-05-14"
Create a webhook endpoint

Use an idempotency key when creating endpoints. Store the returned signing secret immediately.

curl "https://humanproof.io/api/v1/webhooks/endpoints" \
  -X POST \
  -H "Authorization: Bearer hp_live_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: endpoint-prod-001" \
  -d '{
    "url": "https://api.example.com/humanproof/webhooks",
    "description": "Production events",
    "events": ["score.updated", "score.revised"]
  }'
Report an agent decision

Use this after an agent shortlists, rejects, contacts, or otherwise acts on Humanproof data.

curl "https://humanproof.io/api/v1/agent-decisions" \
  -X POST \
  -H "Authorization: Bearer hp_live_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: decision-req-frontend-123" \
  -d '{
    "subject": { "kind": "candidate", "username": "maya-chen" },
    "decision": "shortlisted",
    "outcome": "pending",
    "agentName": "Talent routing agent",
    "jobRef": "req-frontend-123",
    "reasonCodes": ["react_match", "verified_score"]
  }'
Webhook payload envelope

Every event has a stable id, type, creation timestamp, and event-specific data.

{
  "schemaVersion": "humanproof.webhook_event.v1",
  "id": "evt_score_test_00001",
  "type": "score.updated",
  "createdAt": "2026-06-01T00:00:00.000Z",
  "data": {
    "workerId": "00000000-0000-4000-8000-00000000f001",
    "snapshotId": "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
    "score": 742,
    "confidenceLow": 710,
    "confidenceHigh": 774,
    "evidenceCount": 12,
    "sourceCount": 4,
    "scoreVcSha256": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
    "scoreVcStatusToken": "status_test_0001",
    "issuedAt": "2026-06-01T00:00:00.000Z"
  }
}
Verify webhook signatures

Compute HMAC SHA-256 over timestamp.rawBody and compare it with the v1 signature.

import { createHmac, timingSafeEqual } from 'node:crypto';

export function verifyHumanproofWebhook(rawBody, signatureHeader, secret) {
  if (typeof rawBody !== 'string' || typeof signatureHeader !== 'string') return false;
  if (signatureHeader.trim() === '' || typeof secret !== 'string' || secret === '') return false;

  const parts = Object.fromEntries(
    signatureHeader.split(',').map((part) => {
      const [key, value] = part.split('=');
      return [key, value];
    }),
  );
  const timestamp = parts.t;
  const received = parts.v1;
  if (!timestamp || !received) return false;
  const timestampSeconds = Number(timestamp);
  if (!Number.isFinite(timestampSeconds)) return false;
  if (!/^[0-9a-f]+$/i.test(received)) return false;
  if (Math.abs(Date.now() / 1000 - timestampSeconds) > 300) return false;

  const expected = createHmac('sha256', secret)
    .update(`${timestamp}.${rawBody}`)
    .digest('hex');

  const expectedBytes = Buffer.from(expected, 'hex');
  const receivedBytes = Buffer.from(received, 'hex');
  return expectedBytes.length === receivedBytes.length
    && timingSafeEqual(expectedBytes, receivedBytes);
}

Webhooks

Humanproof signs every delivery. Your endpoint should verify the signature before processing event data.

Event catalog
  • score.updated

    A worker score has a new issued value.

  • score.revised

    A previously issued score was corrected or replaced.

  • evidence.revoked

    A source record was removed from the active evidence set.

  • dispute.opened

    A worker opened a review or score dispute.

  • dispute.resolved

    A dispute reached a terminal decision.

  • candidate.consent_changed

    A candidate sharing permission changed.

Delivery headers
  • Humanproof-Webhook-Id

    Stable event id. Use it for idempotent processing.

  • Humanproof-Webhook-Event

    Event type, for example score.updated.

  • Humanproof-Webhook-Delivery

    Unique delivery attempt row id.

  • Humanproof-Webhook-Timestamp

    Unix timestamp used in the signed payload.

  • Humanproof-Webhook-Signature

    HMAC header in the form t=<timestamp>,v1=<hex digest>.

Retries and diagnostics
  • Only HTTPS endpoints are accepted. URLs with credentials, fragments, whitespace, or private network targets are rejected.
  • A 2xx response marks the delivery as delivered. Network failures and non-2xx responses retry up to 8 attempts.
  • Retry delay starts at 30 seconds, doubles between attempts, and caps at 6 hours.
  • Payloads are JSON, capped at 256 KB, and include schemaVersion, id, type, createdAt, and data.
  • The dashboard can queue one test event per endpoint every 60 seconds.