Developers

Verify Spondeo proofs from your backend.

A single REST call confirms a holder's disclosed claims — affiliation, organization tier, country — without ever exposing their name, school, employer, or email. Free Developer tier: 500 verifications/month.


Authentication

Authenticate verification calls with an API key in the Authorization header: Authorization: Bearer sk_live_… (use sk_test_… against the sandbox). Create and manage keys in your Profile. Each key's secret is shown once at creation and stored only as a hash — keep it server-side and never ship it to a browser.

Manage your keys in your Profile →


Endpoints

Method Path Auth Purpose
POST /v1/verify API key Verify a presented proof; returns the disclosed claims and a stable pseudonymous id.
Request fields: { vp_token, expected_audience, nonce, required? }
Response: { valid, claims, pid, reason }
POST /v1/sandbox/present API key (test) Mint a sandbox test presentation to verify (test issuer; verifies only with a sk_test_ key).
Request fields: { nonce, affiliation?, audience? }
Response: { vp_token, audience, nonce }
POST /v1/credentials none Start an affiliation verification (emails a 6-digit code).
Request fields: { affiliation, email }
Response: { id, status }
POST /v1/credentials/{id}/code none Confirm the code and bind a holder key; issues the credential.
Request fields: { code, holder_jwk }
Response: { sd_jwt_vc, credential_id, holder_id, … }
DELETE /v1/credentials/{id} holder Revoke a credential (holder-scoped).
Request fields: { holder_id }
Response: { revoked }
GET /status/{listId} none Public revocation status list.
Request fields:
Response: { list_id, bits }

Quick start

curl

curl -X POST https://vouchmesh.com/v1/verify \
  -H "Authorization: Bearer sk_live_…" \
  -H "content-type: application/json" \
  -d '{"vp_token":"…","expected_audience":"spondeo:proof-link","nonce":"…","required":[{"claim":"affiliation","value":"enrolled_student"}]}'

SDK (TypeScript)

import { Spondeo } from "@spondeo/verify";

const spondeo = new Spondeo("sk_live_…", { baseUrl: "https://vouchmesh.com" });

const res = await spondeo.verify({
  vpToken,
  expectedAudience: "spondeo:proof-link",
  nonce,
  required: [{ claim: "affiliation", value: "enrolled_student" }],
});

if (res.valid) console.log(res.claims, res.pid);

The zero-dependency SDK lives in the repo at sdk/ and is published as @spondeo/verify.


Webhooks

Register a webhook in your Profile to be notified the moment a credential is revoked. Events are scoped to credentials you've verified — you only hear about revocations that matter to you. Only public http(s) URLs are accepted (no localhost or private/internal hosts).

Register a URL under Webhooks in your Profile. Each webhook returns a signing secret once — store it server-side. Manage your keys in your Profile →

Event payload

On revocation we POST your URL with header X-Spondeo-Event: credential.revoked and the JSON body below. The status_list.idx is the opaque index you already check against /status/{listId} — no PII.

{
  "type": "credential.revoked",
  "status_list": { "uri": "https://vouchmesh.com/status/1", "idx": 42 },
  "occurred_at": 1717000000000
}

Verifying the signature

Each delivery carries an X-Spondeo-Signature header: the hex HMAC-SHA256 of the exact raw request body, keyed with your webhook secret. Recompute it over the raw bytes and compare in constant time before trusting the event.

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

// rawBody is the EXACT bytes you received (do not re-serialize the parsed JSON).
function verify(rawBody, header, secret) {
  const expected = createHmac("sha256", secret).update(rawBody).digest("hex");
  const a = Buffer.from(expected), b = Buffer.from(String(header || ""));
  return a.length === b.length && timingSafeEqual(a, b);
}

Integrate with an AI agent

Three machine-readable paths let an agent discover and call Spondeo without scraping these docs.

  • Agent-readable overview https://vouchmesh.com/llms.txt A plain-text summary of what Spondeo does and how to call it, for an LLM to read directly.
  • OpenAPI 3.1 https://vouchmesh.com/openapi.json The full machine-readable spec — point a code generator at it to scaffold a typed client.
  • MCP server A Model Context Protocol server lives in the repo at mcp/. It exposes two tools — verify_affiliation_proof and sandbox_present. Run it with cd mcp && npm install && npm start (env SPONDEO_API_KEY + SPONDEO_BASE_URL), then add it to your MCP client config:
{
  "mcpServers": {
    "spondeo": {
      "command": "npx",
      "args": ["tsx", "/abs/path/to/mcp/spondeo-mcp.ts"],
      "env": {
        "SPONDEO_API_KEY": "sk_live_…",
        "SPONDEO_BASE_URL": "https://vouchmesh.com"
      }
    }
  }
}

Errors & limits

Verification calls that miss or mistype the key return 401 invalid_api_key. Past the free tier of 500 verifications/month a key returns 429 quota_exceeded. All endpoints are additionally IP rate-limited. Spondeo never logs or leaks PII — verifiers only ever see the tier/enum claims a holder chose to disclose.