This guide walks through a complete Humanos integration over the HTTP API. Every snippet usesDocumentation Index
Fetch the complete documentation index at: https://docs.humanos.id/llms.txt
Use this file to discover all available pages before exploring further.
fetch and assumes API_KEY, SIGNATURE_SECRET, and a small signedFetch() wrapper are in scope — see Authentication for how to grab the credentials from the dashboard and sign each request.
If you’re integrating from Node.js or TypeScript, the Humanos SDK wraps every call here, including auto-signing and webhook decryption. The two surfaces are equivalent — pick whichever fits your stack.
Step 1 — Define an action
In the dashboard, create an Action. An action has three parts:executionParams— values the agent supplies at verify timeuserParams— values the user pins at decision time- Rules — deterministic CEL expressions comparing the two
| Part | Field | Type |
|---|---|---|
executionParams | amount | number |
category | string | |
userParams | maxAmount | number |
allowedCategories | array<string> |
executionParams.amount <= userParams.maxAmount and executionParams.category in userParams.allowedCategories.
Once defined, publish the action and copy its ID (urn:via:action:<uuid>). Hold the ID as a constant in your code, or store it alongside the rule it represents in your database.
Step 2 — Issue a mandate request
Ask Humanos to issue a mandate to a user. The request bundles the contact, the action ID, and theuserParams values the user is being asked to approve.
request.id if you want to track pending approvals. The mandate ID itself becomes available once the user approves (next step).
Step 3 — User approval and mandate issuance
Humanos handles the user-facing approval flow. The user reviews theuserParams from step 2 and either approves or rejects.
Identity verification. The security code (OTP) is always delivered by email or SMS.
Where the approval UI shows up. Two options:
- Hosted (default) — the email or SMS message contains a link to the Humanos-hosted approval page. The user clicks through, reviews, decides.
- Embedded iframe — your application embeds the Humanos approval UI directly. The user never leaves your app. See the iframe integration guide for setup.
credential webhook event. The mandate ID — urn:via:credential:… — is the durable artifact: persist it on the rule record in your database (e.g., ruleId → mandateUrn).
See Webhooks for the full signature verification + decryption pipeline. Once the payload is decoded, a credential event handler looks like this:
credential payload shape:
credential payload is also delivered via window.postMessage to the parent window — useful for live UI updates without a backend round-trip.
Dev shortcut: during development you can copy the mandate ID directly from the dashboard’s activity table (look for the
MANDATE_ISSUE entry) instead of wiring a webhook.Step 4 — Agent issues a VP
When the agent wants to act, your backend asks Humanos for a fresh Verifiable Presentation bound to the mandate ID captured in step 3. VPs are short-lived and single-purpose — issue a new one for every verify.mandateId(path) — the mandate ID captured in step 3.targetVerifier(optional body) — DID of the intended verifier. When provided, the VP is bound to that audience viaproof.domainplus a challenge nonce.
{ presentation, receipt } object. Pass vp.presentation to step 5.
Step 5 — Humanos verifies the VP
Hand the VP plus the agent’sexecutionParams to the verify endpoint. Humanos checks four things: signatures, expiry, revocation status, and rule compliance. 200 OK means allow; 403 means at least one check failed (the response body names the failing rule under evaluations).
presentation— the signed VP from step 4.executionParams— what the agent wants to do. Field names must match those declared on the action and are referenced inside rules asexecutionParams.<field>.
| Case | executionParams | Result |
|---|---|---|
| In-bounds | { amount: 5000, category: "BOOKS" } | allow (200) + signed receipt |
| Out-of-bounds | { amount: 50000, category: "FLIGHTS" } | deny (403) — body names the failing rule(s) |
Step 6 — Revoke a mandate
Mandates are immutable — to retire one (rule deletion, rule update, user pulling consent), call the revoke endpoint. Once revoked, the mandate is dead: VP issuance errors withcredential_revoked, and any VP still held by an agent fails verify with the same reason. Stale rules cannot be enforced; that’s the safety property.
reason— free-text label recorded on the credential and in theMANDATE_REVOKEDreceipt. Recommended values from the VIA protocol:user_initiated,organization_policy,system_expiry.
MANDATE_REVOKED receipt. Store it for audit if useful — enforcement uses the revocation status itself, not the receipt.
Audit trail
For compliance, Humanos persists every consequential event in a mandate’s lifecycle as an immutable, cryptographically signed record. You don’t need to log these yourself — they’re queryable through the activity API.| Event | Trigger | Captured |
|---|---|---|
| Mandate issued | User accepts a request | Action ID, userParams, signed credential, timestamp |
| Mandate revoked | Revoke endpoint succeeds | Mandate ID, reason, timestamp |
| Mandate canceled | Request canceled before approval | Request ID, canceler, timestamp |
| Human decision: accept | User approves a request | User, request, OTP channel (email or SMS), UI surface (hosted / iframe) |
| Human decision: reject | User rejects a request | User, request, OTP channel, UI surface |
| VP issue | VP successfully issued | Mandate ID, VP, target verifier, receipt |
| VP issue denied | VP issuance rejected | Mandate ID, reason code (e.g., credential_revoked) |
| Verify accept | Verify returns 200 | VP, executionParams, rule evaluations, signed receipt |
| Verify deny | Verify returns 403 | VP, executionParams, failing rule(s), signed receipt |
API versioning
Humanos uses date-based API versions (e.g.,2026-04-23). Send your pinned version via the API-Version header — see Versioning for the resolution rules and best practices.
Action versions are independent of API versions. When you republish an action in the dashboard, mandates already issued against the older version keep referencing that version — they don’t break.
Error handling
Non-2xx responses include a JSON body describing the failure:401on every request — signature mismatch. Double-check the signature secret matches the dashboard exactly. See Authentication.400onPOST /requestwith “name is required” — eachcredentials[]entry needs anamefield. The example in step 2 uses"Action mandate".403on verify with no obvious failing rule — check thatexecutionParamsfield names exactly match the action’s declared fields. A typo silently fails rule evaluation.400on VP issuance after revoke — expected. Once a mandate is revoked, VP issuance is blocked at source withcredential_revoked. Existing VPs also fail verify for the same reason.
Troubleshooting
Webhook never fires. Confirm the URL in Settings → Webhooks matches your live endpoint. Send a test event from the dashboard. For local dev, expose your server with ngrok and set the tunnel URL in the dashboard. Webhook fires but the handler errors with a signature mismatch. Your server must read the raw body (not parsed JSON) for HMAC verification. In Express, useexpress.text({ type: "application/json" }) — express.json() reformats the body and breaks the signature.
Mandate ID is missing from the webhook payload. It lives under payload.credential.id (the full W3C credential), not at the top level.