OpenAPI
Machine-readable spec at /v3/api-docs.
Open-protocol payment API for DID-bound callers — AI agents, agent operators, or humans with a digital identity. Identity-bound, idempotent payments with signed receipts and multi-rail settlement.
Machine-readable spec at /v3/api-docs.
@kanko/sdk on npm handles the 402-retry dance.
Clone, docker compose up, and call POST /v0/pay in five minutes.
Reference gateway lives at gitlab.kaneky.dev/platforms/kanko.
The protocol is identity-bound, not agent-bound. Any party that can present a verified DID assertion may call the gateway.
curl or a hand-written client, as long as they control the signing keys behind a did:web (or future DID method) identity.402 + WWW-Authenticate: Kanko challenge; the middleware works for any HTTP runtime.Public routes served by this gateway. The table is loaded from /api-endpoints.json and kept in sync with the controller layer by a build-time test.
Loading endpoint inventory.
POST /v0/pay requires proof of DID control, not just a DID string. The caller signs a canonical message binding the money-relevant fields (DID, idempotency key, timestamp, amount, currency, provider URL) with the ed25519 private key behind their did:web document, and presents the signature in X-Kanko-Signature with the matching X-Kanko-Timestamp. The gateway resolves the DID document, verifies the signature against the published key, and rejects a missing or invalid signature with 401 unauthenticated before any rail is touched. The @kanko/sdk libraries compute these headers for you.
| Header | Required on | Description |
|---|---|---|
X-Kanko-Agent-DID |
POST /v0/pay |
Caller DID, e.g. did:web:agent.example.com. Must resolve to a DID document whose ed25519 key verifies the request signature. |
X-Kanko-Signature |
POST /v0/pay |
Required. base64url(no-pad) ed25519 signature over the canonical pay message (see the OpenAPI PayCallerSignature scheme for the exact field order). Proves the caller controls the DID's key. |
X-Kanko-Timestamp |
POST /v0/pay |
Required. RFC-3339 instant, echoed verbatim into the signed message. Rejected if it skews more than ±5 min from gateway time (replay window). |
Idempotency-Key |
POST /v0/pay |
Client-chosen unique key (UUIDv4 recommended). Same key + same body returns the cached response; same key + different body returns 409. |
Content-Type |
All POST bodies | Must be application/json. |
The kanko protocol is a three-party 402 handshake. A provider gates a resource with 402 Payment Required and a WWW-Authenticate: Kanko challenge. The caller calls the gateway to settle, then retries the original request with the receipt.
curl -X POST https://kanko.kaneky.dev/v0/pay \
-H "X-Kanko-Agent-DID: did:web:agent.example.com" \
-H "X-Kanko-Timestamp: 2026-07-01T12:00:00Z" \
-H "X-Kanko-Signature: base64url-ed25519-over-canonical-message" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"provider_url": "https://api.example.com/data",
"amount_minor": 50,
"currency": "USD",
"rail_preferences": ["stripe", "x402"],
"metadata": { "job_id": "abc-123" }
}'
The X-Kanko-Signature / X-Kanko-Timestamp pair is proof of DID control — the SDK computes them from your signing key. A hand-rolled client must sign the canonical message itself (field order in the OpenAPI PayCallerSignature scheme); without a valid signature the gateway returns 401.
{
"receipt_id": "rcpt_01HZabc123def456ghi789jkl",
"signature": "MEUCIQD...base64url...",
"receipt_url": "https://kanko.kaneky.dev/v0/receipts/rcpt_01HZabc123def456ghi789jkl",
"livemode": true
}
In sandbox mode (KANKO_MODE=sandbox) the gateway returns deterministic fake settlements with livemode: false and makes no external rail calls — the honest way to demo the full pay → receipt round-trip without credentials.
Rail status. stripe is a preview rail (settles a real Stripe PaymentIntent in live mode). x402 is a sandbox-only stub: in live mode it refuses with 503 rail_unavailable rather than sign a receipt for a settlement that did not happen. Prefer stripe for a live demo; use either in sandbox.
Verify a did:web identity assertion and optional verifiable credential. The response tells you the resolved DID, KYC tier, and whether the assertion signature is valid.
curl -X POST https://kanko.kaneky.dev/v0/identity/verify \
-H "Content-Type: application/json" \
-d '{
"assertion": {
"issuer": "did:web:agent.example.com",
"subject": "did:web:agent.example.com",
"issued_at": 1718900000,
"expires_at": 1718903600,
"nonce": "base64url-nonce"
},
"signature": "base64url-ed25519-sig",
"verifiable_credential": null
}'
{
"agent_did": "did:web:agent.example.com",
"kyc_tier": "email_verified",
"verified": true,
"reason": "verified",
"skew_seconds": 0
}
Receipts are signed JSON bodies. Fetch one by its public id to verify it on the provider side, or render it as a PDF for human review.
curl https://kanko.kaneky.dev/v0/receipts/rcpt_01HZabc123def456ghi789jkl
The receipt carries the gateway's ed25519 signature and a signing_key_id. Verify the signature offline with the key published at GET /.well-known/kanko-public-key, or use the JWK Set at GET /.well-known/kanko-public-keys for historic receipts signed under retired keys.
Inbound: Stripe posts events to POST /webhooks/stripe. The gateway verifies the Stripe signature before persisting the event.
Outbound: Partners can receive signed lifecycle events (receipt.settled, receipt.refunded, receipt.disputed) by registering a DID-to-URL mapping. Each push carries Kanko-Signature, Kanko-Key-Id, and Kanko-Event-Type headers and can be verified against /.well-known/kanko-public-keys.
Runtime banner from GET /banner.