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.txtA 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.jsonThe 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.
401 invalid_api_keyMissing or unrecognized API key.429 quota_exceededPast the free tier of 500 verifications/month.429 rate_limitedPer-IP rate limit; retry after the window resets.