humanos 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/README.md +310 -114
  2. package/dist/index.d.mts +3319 -1514
  3. package/dist/index.d.ts +3319 -1514
  4. package/dist/index.js +1621 -499
  5. package/dist/index.mjs +1578 -478
  6. package/generated/.openapi-generator/FILES +59 -33
  7. package/generated/api/actions-api.ts +273 -0
  8. package/generated/api/activity-api.ts +225 -0
  9. package/generated/api/{resource-api.ts → approval-api.ts} +65 -61
  10. package/generated/api/credentials-api.ts +365 -25
  11. package/generated/api/didapi.ts +138 -0
  12. package/generated/api/requests-api.ts +124 -134
  13. package/generated/api/user-api.ts +45 -36
  14. package/generated/api/webhooks-api.ts +84 -56
  15. package/generated/api.ts +5 -1
  16. package/generated/models/action-dto.ts +36 -0
  17. package/generated/models/action-entity.ts +57 -0
  18. package/generated/models/action-list-entity.ts +39 -0
  19. package/generated/models/action-query.ts +48 -0
  20. package/generated/models/action-ref-dto.ts +36 -0
  21. package/generated/models/action-version-detail-entity.ts +72 -0
  22. package/generated/models/action-version-list-entity.ts +39 -0
  23. package/generated/models/action-version-summary-entity.ts +48 -0
  24. package/generated/models/activities-entity.ts +39 -0
  25. package/generated/models/activity-entity.ts +90 -0
  26. package/generated/models/activity-query.ts +96 -0
  27. package/generated/models/consent.ts +36 -0
  28. package/generated/models/constraints-dto.ts +36 -0
  29. package/generated/models/{agent-detail-entity.ts → contact-entity.ts} +8 -8
  30. package/generated/models/create-presentation-dto.ts +30 -0
  31. package/generated/models/create-presentation400-response.ts +42 -0
  32. package/generated/models/create-presentation403-response.ts +42 -0
  33. package/generated/models/create-presentation404-response.ts +42 -0
  34. package/generated/models/create-subject-dto.ts +8 -2
  35. package/generated/models/create-subject-entity.ts +3 -3
  36. package/generated/models/{cancel-request404-response.ts → create400-response.ts} +5 -5
  37. package/generated/models/credential-dto-data-inner.ts +26 -0
  38. package/generated/models/credential-dto.ts +52 -6
  39. package/generated/models/credential-entity.ts +33 -7
  40. package/generated/models/{credential-subject.ts → credential-event-decision.ts} +13 -11
  41. package/generated/models/credential-event-user.ts +1 -1
  42. package/generated/models/credential-event.ts +15 -19
  43. package/generated/models/credential-placement-dto.ts +85 -0
  44. package/generated/models/credential-status.ts +5 -3
  45. package/generated/models/credentials-detail404-response.ts +42 -0
  46. package/generated/models/credentials-evidence404-response.ts +42 -0
  47. package/generated/models/credentials-issue-vp400-response.ts +42 -0
  48. package/generated/models/credentials-issue-vp403-response.ts +42 -0
  49. package/generated/models/credentials-issue-vp404-response.ts +42 -0
  50. package/generated/models/credentials-revoke400-response.ts +42 -0
  51. package/generated/models/credentials-revoke409-response.ts +42 -0
  52. package/generated/models/did-document-entity.ts +63 -0
  53. package/generated/models/evidence.ts +44 -0
  54. package/generated/models/generate-credential-entity.ts +2 -2
  55. package/generated/models/generate-request-dto.ts +18 -15
  56. package/generated/models/generate-request-entity.ts +6 -6
  57. package/generated/models/generate-subject-entity.ts +3 -3
  58. package/generated/models/get-evidence404-response.ts +42 -0
  59. package/generated/models/group-entity.ts +3 -3
  60. package/generated/models/groups-entity.ts +1 -1
  61. package/generated/models/identity-dto.ts +26 -26
  62. package/generated/models/identity-event-decision.ts +13 -1
  63. package/generated/models/identity-event-user.ts +42 -0
  64. package/generated/models/identity-event.ts +14 -18
  65. package/generated/models/{identity-event-identity.ts → identity-payload.ts} +12 -12
  66. package/generated/models/index.ts +55 -32
  67. package/generated/models/{action-type.ts → json-value.ts} +6 -12
  68. package/generated/models/{mandate-data-dto-value.ts → organization-data-entity.ts} +9 -3
  69. package/generated/models/{issuer-detail-entity.ts → otp-error.ts} +8 -8
  70. package/generated/models/otp-failed-event-otp.ts +3 -3
  71. package/generated/models/otp-failed-event.ts +11 -15
  72. package/generated/models/{subject.ts → partial-subject-entity.ts} +9 -9
  73. package/generated/models/presentation-receipt-entity.ts +42 -0
  74. package/generated/models/presentation-response-entity.ts +39 -0
  75. package/generated/models/receipt-entity.ts +36 -0
  76. package/generated/models/request-credential-entity.ts +72 -0
  77. package/generated/models/request-detail-entity.ts +26 -20
  78. package/generated/models/request-detail-subject-entity.ts +87 -0
  79. package/generated/models/request-entity.ts +14 -8
  80. package/generated/models/request-query.ts +3 -21
  81. package/generated/models/request-subject-entity.ts +21 -6
  82. package/generated/models/requests-create400-response.ts +42 -0
  83. package/generated/models/requests-detail404-response.ts +42 -0
  84. package/generated/models/requests-entity.ts +1 -1
  85. package/generated/models/requests-resend-otp403-response.ts +42 -0
  86. package/generated/models/resend-otp403-response.ts +42 -0
  87. package/generated/models/resource-entity.ts +11 -3
  88. package/generated/models/{group-resource-entity.ts → resource-minimal-entity.ts} +13 -11
  89. package/generated/models/resource-query.ts +3 -1
  90. package/generated/models/resources-entity.ts +1 -1
  91. package/generated/models/revoke-credential-dto.ts +30 -0
  92. package/generated/models/revoke-credential-entity.ts +63 -0
  93. package/generated/models/revoke-credential400-response.ts +42 -0
  94. package/generated/models/revoke-credential409-response.ts +42 -0
  95. package/generated/models/revoke-receipt-entity.ts +42 -0
  96. package/generated/models/schema-entity.ts +57 -0
  97. package/generated/models/schema-list-entity.ts +39 -0
  98. package/generated/models/schema-query.ts +48 -0
  99. package/generated/models/schema-response-entity.ts +45 -0
  100. package/generated/models/schema-version-detail-entity.ts +72 -0
  101. package/generated/models/schema-version-list-entity.ts +39 -0
  102. package/generated/models/schema-version-response-entity.ts +54 -0
  103. package/generated/models/schema-version-summary-entity.ts +48 -0
  104. package/generated/models/subject-contact-entity.ts +7 -7
  105. package/generated/models/subject-issuer-entity.ts +69 -0
  106. package/generated/models/subject-query.ts +4 -4
  107. package/generated/models/test-event.ts +11 -15
  108. package/generated/models/user-create400-response.ts +42 -0
  109. package/generated/models/user-detail400-response.ts +42 -0
  110. package/generated/models/user-detail404-response.ts +42 -0
  111. package/generated/models/verification-method-entity.ts +48 -0
  112. package/generated/models/verify-allow-response-entity.ts +61 -0
  113. package/generated/models/verify-deny-response-entity.ts +79 -0
  114. package/generated/models/verify-evaluation-entity.ts +51 -0
  115. package/generated/models/verify-presentation-dto.ts +36 -0
  116. package/generated/models/verify-receipt-entity.ts +42 -0
  117. package/generated/models/w3-cverifiable-credential-credential-subject-mandate-action.ts +44 -0
  118. package/generated/models/w3-cverifiable-credential-credential-subject-mandate-constraint-constraint-schema.ts +44 -0
  119. package/generated/models/w3-cverifiable-credential-credential-subject-mandate-constraint.ts +41 -0
  120. package/generated/models/w3-cverifiable-credential-credential-subject-mandate-context.ts +32 -0
  121. package/generated/models/{mandate-data-type.ts → w3-cverifiable-credential-credential-subject-mandate-grantor.ts} +3 -16
  122. package/generated/models/w3-cverifiable-credential-credential-subject-mandate.ts +65 -0
  123. package/generated/models/w3-cverifiable-credential-credential-subject.ts +41 -0
  124. package/generated/models/w3-cverifiable-credential-evidences-inner.ts +42 -0
  125. package/generated/models/w3-cverifiable-credential-proofs-inner.ts +62 -0
  126. package/generated/models/w3-cverifiable-credential-valid-from.ts +24 -0
  127. package/generated/models/w3-cverifiable-credential-valid-until.ts +24 -0
  128. package/generated/models/w3-cverifiable-credential.ts +24 -25
  129. package/generated/models/webhook-call-event.ts +6 -12
  130. package/package.json +1 -1
  131. package/generated/models/credential-full-entity.ts +0 -82
  132. package/generated/models/get-credential-entity.ts +0 -48
  133. package/generated/models/identity-field-placement.ts +0 -73
  134. package/generated/models/identity-field-type.ts +0 -38
  135. package/generated/models/mandate-data-dto-type.ts +0 -73
  136. package/generated/models/mandate-data-dto.ts +0 -71
  137. package/generated/models/signature-placement-item-dto.ts +0 -72
  138. package/generated/models/signature-placement.ts +0 -56
  139. package/generated/models/subject-entity.ts +0 -48
  140. package/generated/models/w3-cproof.ts +0 -82
package/README.md CHANGED
@@ -1,45 +1,73 @@
1
1
  # Humanos SDK for TypeScript/JavaScript
2
2
 
3
- Official TypeScript/JavaScript SDK for the [Humanos](https://humanos.id) API. Provides automatic request signing, webhook verification and decryption, and full API access for credential management.
3
+ Official TypeScript/JavaScript SDK for the [Humanos](https://humanos.id) API. Auto-signs every request, verifies and decrypts webhooks, and ships full type definitions.
4
4
 
5
5
  [![npm version](https://badge.fury.io/js/%40humanos%2Fsdk.svg)](https://www.npmjs.com/package/humanos)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
7
 
8
- ## Features
8
+ ## Concepts
9
9
 
10
- - **Automatic Request Signing** - All API requests are signed with HMAC-SHA256
11
- - **Webhook Verification** - Verify signatures and decrypt encrypted payloads
12
- - **TypeScript Support** - Full type definitions included
13
- - **Simple API** - Clean interface for all Humanos endpoints
10
+ Humanos gives your agent a cryptographic permission slip issued by the user, scoped to a specific action, and verified at runtime.
14
11
 
15
- ## Getting Started
12
+ - **Action** — a reusable policy template you publishable through the dashboard. Declares `userParams` (values the human approves), `executionParams` (what the agent must supply at execution time), and the rules that compare them.
13
+ - **Mandate** — a signed W3C credential (type `POLICY`) the user issues to your agent. References one action plus the `userParams` values that bind this specific approval.
14
+ - **Verifiable Presentation (VP)** — a short-lived, signed proof derived from a mandate. The agent presents one each time it wants to act.
15
+ - **Verify** — Humanos checks the VP's signatures, expiry, revocation status, and the action's rules against the agent's `executionParams`. Returns allow or deny.
16
+ - **Revoke** — kills a mandate. Any further VP issuance or `verify` is denied.
16
17
 
17
- ### 1. Install the SDK
18
+ ## Lifecycle
19
+
20
+ A mandate's lifetime breaks into two flows: **request and approval** (one-time, gets you a mandate ID) and **issuance and verification** (repeats every time the agent acts).
21
+
22
+ **Flow 1 — Request and approval**
23
+
24
+ ```
25
+ ┌──────────┐ ┌──────────────┐ ┌──────────┐ ┌──────────┐ ┌─ accepts ─▶ mandate issued
26
+ │ Action │ ─▶ │ Agent needs │ ─▶ │ Agent │ ─▶ │ User │ ─▶ ─────┤
27
+ │ defined │ │ permission │ │ requests │ │ decides │ └─ rejects ─▶ flow ends
28
+ └──────────┘ └──────────────┘ └──────────┘ └──────────┘
29
+ step 1 step 2 step 3
30
+ ```
31
+
32
+ **Flow 2 — Issuance and verification**
33
+
34
+ ```
35
+ ┌──────────┐ ┌──────────────────────────────┐ ┌─ true ─▶ allow (agent acts)
36
+ │ Agent │ ─▶ │ Humanos verifies VP against │ ─▶ ─────┤
37
+ │issues VP │ │ executionParams │ └─ false ─▶ deny (blocked)
38
+ └──────────┘ └──────────────────────────────┘
39
+ step 4 step 5
40
+ ```
41
+
42
+ ## Installation
18
43
 
19
44
  ```bash
20
45
  npm install humanos
21
46
  ```
22
47
 
23
- ### 2. Create an Account
48
+ Requires Node 18 or newer.
24
49
 
25
- Sign up at [humanos.id](https://humanos.id) and create your organization.
50
+ ## Configuration
26
51
 
27
- ### 3. Get Your API Keys
52
+ Sign up at [humanos.id](https://humanos.id), then grab two sets of credentials from the dashboard:
28
53
 
29
- In the Humanos Dashboard, go to **Settings > API Keys** and copy:
54
+ 1. **API credentials** **Settings API Keys**. Copy the **API Key** and **Signature Secret**.
55
+ 2. **Webhook credentials** — **Settings → Webhooks**. Copy the **Webhook Signature Secret**, **Webhook Encryption Secret**, and **Webhook Encryption Salt**.
30
56
 
31
- - **API Key** - Used to authenticate requests
32
- - **Signature Secret** - Used to sign requests with HMAC-SHA256
57
+ Add them to your `.env`:
33
58
 
34
- ### 4. Get Your Webhook Keys
59
+ ```env
60
+ HUMANOS_API_KEY=<your-api-key>
61
+ HUMANOS_SIGNATURE_SECRET=<your-signature-secret>
35
62
 
36
- In the Humanos Dashboard, go to **Settings > Webhooks** and copy:
63
+ HUMANOS_WEBHOOK_SIGNATURE_SECRET=<your-webhook-signature-secret>
64
+ HUMANOS_WEBHOOK_ENCRYPTION_SECRET=<your-webhook-encryption-secret>
65
+ HUMANOS_WEBHOOK_ENCRYPTION_SALT=<your-webhook-encryption-salt>
66
+ ```
37
67
 
38
- - **Webhook Signature Secret** - Used to verify incoming webhook signatures
39
- - **Webhook Encryption Secret** - Used to decrypt webhook payloads
40
- - **Webhook Encryption Salt** - Used alongside the encryption secret
68
+ Everything else (action URNs, mandate URNs, etc.) is application data — keep it in code or your database, not in `.env`.
41
69
 
42
- ### 5. Initialize the Client
70
+ ## Quick start
43
71
 
44
72
  ```typescript
45
73
  import { HumanosClient } from "humanos";
@@ -49,69 +77,86 @@ const client = new HumanosClient({
49
77
  apiKey: process.env.HUMANOS_API_KEY!,
50
78
  signatureSecret: process.env.HUMANOS_SIGNATURE_SECRET!,
51
79
  });
80
+
81
+ // Sanity check — confirms the API key, signature secret, and signing handshake.
82
+ const { data } = await client.requests.list();
83
+ console.log(data);
52
84
  ```
53
85
 
54
- ### 6. Make Your First API Call
86
+ A `200 OK` with a (possibly empty) list means you're wired up. The walkthrough below exercises the rest of the flow.
55
87
 
56
- Fetch all your credential requests:
88
+ ## Integration walkthrough
57
89
 
58
- ```typescript
59
- const response = await client.requests.getRequests();
60
- console.log(response.data);
61
- ```
90
+ Six steps, matching the lifecycle diagram above. Run through them once with a test action to get an end-to-end feel.
62
91
 
63
- ## Usage Examples
92
+ ### 1. Define an action
64
93
 
65
- ### Create a Credential Request
94
+ In the dashboard, create an **action**. An action has three parts:
66
95
 
67
- Send a credential request to one or more contacts using pre-configured resources:
96
+ - **`executionParams`** the values the agent supplies at `verify()` time.
97
+ - **`userParams`** — the constants the user pins at decision time.
98
+ - **Rules** — deterministic CEL expressions comparing the two above.
68
99
 
69
- ```typescript
70
- const request = await client.requests.generate({
71
- contacts: ["user@example.com"],
72
- securityLevel: "CONTACT",
73
- resourcesIds: ["your-resource-id"],
74
- });
100
+ Example:
75
101
 
76
- console.log("Request ID:", request.data.id);
77
- console.log("Credentials:", request.data.credentials);
78
- ```
102
+ | Part | Field | Type |
103
+ | ----------------- | ------------------- | --------------- |
104
+ | `executionParams` | `amount` | number |
105
+ | | `category` | string |
106
+ | `userParams` | `maxAmount` | number |
107
+ | | `allowedCategories` | array\<string\> |
79
108
 
80
- You can also use group IDs to include all resources in a group:
109
+ Rules then express things like `executionParams.amount <= userParams.maxAmount` and `executionParams.category in userParams.allowedCategories`.
81
110
 
82
- ```typescript
83
- const request = await client.requests.generate({
84
- contacts: ["user@example.com"],
85
- securityLevel: "CONTACT",
86
- groupIds: ["your-group-id"],
87
- });
88
- ```
111
+ Once defined, **publish the action** and **copy its ID**. Hold the ID as a constant in your code or store it alongside the rule it represents.
112
+
113
+ ### 2. Issue a mandate request
89
114
 
90
- Or provide inline credential data directly:
115
+ Ask Humanos to issue a mandate to a user. The request bundles the contact, the action ID, and the `userParams` values the user is being asked to approve.
91
116
 
92
117
  ```typescript
93
- const request = await client.requests.generate({
118
+ const actionId = "urn:via:action:<uuid>"; // from step 1
119
+
120
+ const request = await client.requests.create({
94
121
  contacts: ["user@example.com"],
95
122
  securityLevel: "CONTACT",
96
123
  credentials: [
97
124
  {
98
- scope: "onboarding",
99
- type: "JSON",
100
- name: "Service Agreement",
101
- data: [
102
- { label: "Company", type: "string", value: "Acme Corp" },
103
- { label: "Plan", type: "string", value: "Enterprise" },
104
- ],
125
+ scope: "agent.execute",
126
+ type: "POLICY",
127
+ name: "Action mandate", // required, shown to the user
128
+ action: {
129
+ id: actionId,
130
+ userParams: {
131
+ maxAmount: 10000,
132
+ allowedCategories: ["BOOKS", "OFFICE"],
133
+ },
134
+ },
105
135
  },
106
136
  ],
107
137
  });
138
+
139
+ console.log("Request ID:", request.data.id);
108
140
  ```
109
141
 
110
- ### Receive Webhooks
142
+ Persist `request.data.id` if you want to track pending approvals. The mandate ID itself becomes available once the user approves (next step).
143
+
144
+ ### 3. User approval and mandate issuance
111
145
 
112
- Humanos sends webhook events when credentials are signed, identity checks complete, or OTPs fail. Payloads are encrypted and signed.
146
+ Humanos handles the user-facing approval flow. The user reviews the `userParams` from step 2 and either approves or rejects.
113
147
 
114
- The SDK provides `createWebhookHandler` which handles signature verification and payload decryption automatically:
148
+ **Identity verification.** The security code (OTP) is always delivered by **email or SMS**.
149
+
150
+ **Where the approval UI shows up.** Two options:
151
+
152
+ - **Hosted (default)** — the email or SMS message contains a link to the Humanos-hosted approval page. The user clicks through, reviews, decides.
153
+ - **Embedded iframe** — your application embeds the Humanos approval UI directly. The user never leaves your app. See the [iframe integration guide](https://humanos.mintlify.app/essentials/iframe-integration) for setup.
154
+
155
+ If KYC is required on the action, the user completes that first. If the user rejects (or KYC fails), no mandate is issued.
156
+
157
+ **On accept, Humanos issues the mandate and emits a `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`).
158
+
159
+ The SDK's `createWebhookHandler` verifies signatures and decrypts payloads automatically:
115
160
 
116
161
  ```typescript
117
162
  import express from "express";
@@ -119,7 +164,8 @@ import { createWebhookHandler, WebhookConfig } from "humanos";
119
164
 
120
165
  const app = express();
121
166
 
122
- // Use express.text() to preserve the raw body for signature verification
167
+ // IMPORTANT: use express.text() so the raw body is preserved for signature verification.
168
+ // express.json() reformats the payload and breaks the HMAC check.
123
169
  app.use(express.text({ type: "application/json" }));
124
170
 
125
171
  const webhookConfig: WebhookConfig = {
@@ -130,112 +176,262 @@ const webhookConfig: WebhookConfig = {
130
176
 
131
177
  app.post(
132
178
  "/webhook",
133
- createWebhookHandler(webhookConfig, (payload) => {
134
- console.log("Event type:", payload.eventType);
135
-
179
+ createWebhookHandler(webhookConfig, async (payload) => {
136
180
  switch (payload.eventType) {
137
- case "credential":
138
- console.log("Credential signed:", payload.requestId);
181
+ case "credential": {
182
+ if (payload.decision.action !== "accept") {
183
+ // user rejected — mark rule unenforceable, notify operator
184
+ return;
185
+ }
186
+ const mandateUrn = payload.credential.id;
187
+ // persist: ruleId → mandateUrn
139
188
  break;
189
+ }
140
190
  case "identity":
141
- console.log("Identity verified:", payload.requestId);
191
+ // KYC / identity verification completed
142
192
  break;
143
193
  case "otp.failed":
144
- console.log("OTP failed:", payload.requestId);
194
+ // user failed OTP — possible fraud signal
145
195
  break;
146
196
  case "test":
147
- console.log("Test event received");
197
+ // "Send test event" from the dashboard
148
198
  break;
149
199
  }
150
200
  }),
151
201
  );
152
202
 
153
- app.listen(3000, () => {
154
- console.log("Webhook server listening on port 3000");
155
- });
203
+ app.listen(3000);
204
+ ```
205
+
206
+ The `credential` payload shape:
207
+
208
+ ```typescript
209
+ {
210
+ eventType: "credential",
211
+ requestId: string,
212
+ internalId?: string,
213
+ issuerDid: string,
214
+ user: { contact: string; id: string; internalId?: string },
215
+ credential: CredentialEntity, // full W3C VC; .id is the mandate URN
216
+ decision: { action: "accept" | "reject", date: string }, // ISO 8601
217
+ }
156
218
  ```
157
219
 
158
- For local development, use [ngrok](https://ngrok.com) to expose your server:
220
+ For local development, expose your server with [ngrok](https://ngrok.com):
159
221
 
160
222
  ```bash
161
223
  ngrok http 3000
162
224
  ```
163
225
 
164
- Then set the ngrok URL (e.g. `https://xxxx.ngrok-free.app/webhook`) as your webhook URL in the Humanos Dashboard under **Settings > Webhooks**.
226
+ Set the ngrok URL (e.g. `https://xxxx.ngrok-free.app/webhook`) under **Settings Webhooks**.
227
+
228
+ If you're using the iframe channel, the same `credential` payload is also delivered via `window.postMessage` to the parent window — useful for live UI updates without a backend round-trip.
229
+
230
+ > **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.
231
+
232
+ ### 4. Agent issues a VP
233
+
234
+ When the agent wants to act, your backend (or whichever component enforces the rule) 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.
235
+
236
+ ```typescript
237
+ const mandateId = "urn:via:credential:<id>"; // captured in step 3
238
+
239
+ const vp = await client.credentials.issueVP(mandateId, {
240
+ // targetVerifier: "did:web:your-verifier.example", // optional, see below
241
+ });
242
+ ```
243
+
244
+ Field-by-field:
245
+
246
+ - **`mandateId`** (first positional arg) — the mandate ID captured in step 3.
247
+ - **`targetVerifier`** (optional body field) — DID of the intended verifier. When provided, the VP is bound to that audience via `proof.domain` plus a challenge nonce.
248
+
249
+ The response is a `PresentationResponseEntity` — `{ presentation, receipt }`. Pass `vp.data.presentation` to step 5.
165
250
 
166
- ## API Reference
251
+ ### 5. Humanos verifies the VP
167
252
 
168
- ### Resources
253
+ Hand the VP plus the agent's `executionParams` to `verify()`. 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`).
169
254
 
170
255
  ```typescript
171
- // List resources
172
- const resources = await client.resources.getResources();
256
+ await client.credentials.verify({
257
+ presentation: vp.data.presentation,
258
+ executionParams: { amount: 5000, category: "BOOKS" },
259
+ });
260
+ ```
261
+
262
+ Field-by-field:
263
+
264
+ - **`presentation`** — the signed VP from step 4. Read it from `vp.data.presentation`.
265
+ - **`executionParams`** — what the agent wants to do. Field names must match those declared on the action and are referenced inside rules as `executionParams.<field>`.
266
+
267
+ Expected outcomes for the example action:
268
+
269
+ | Case | `executionParams` | Result |
270
+ | ------------- | ---------------------------------------- | ------------------------------------------- |
271
+ | In-bounds | `{ amount: 5000, category: "BOOKS" }` | allow (200) + signed receipt |
272
+ | Out-of-bounds | `{ amount: 50000, category: "FLIGHTS" }` | deny (403) — body names the failing rule(s) |
173
273
 
174
- // List resource groups
175
- const groups = await client.resources.getGroups();
274
+ ### 6. Revoke a mandate
176
275
 
177
- // Download a credential file
178
- const file = await client.resources.download(credentialId);
276
+ Mandates are immutable to retire one (rule deletion, rule update, user pulling consent), call `credentials.revoke()`. Once revoked, the mandate is dead: `issueVP` errors with `credential_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.
277
+
278
+ ```typescript
279
+ await client.credentials.revoke(
280
+ mandateUrn,
281
+ undefined, // aPIVersion — leave undefined to use the SDK default
282
+ { reason: "user_initiated" },
283
+ );
179
284
  ```
180
285
 
181
- ### Requests
286
+ Field-by-field:
287
+
288
+ - **`credentialId`** (first arg) — the mandate ID.
289
+ - **`reason`** — free-text label recorded on the credential and in the `MANDATE_REVOKED` receipt. Recommended values from the VIA protocol: `user_initiated`, `organization_policy`, `system_expiry`.
290
+
291
+ The response is a `RevokeCredentialEntity` — `credentialId`, `status: "REVOKED"`, `revokedAt`, and a `MANDATE_REVOKED` receipt. Store it for audit if useful. Enforcement uses the revocation status itself, not the receipt.
292
+
293
+ ## Humanos audit trail
294
+
295
+ 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 available for query at any time via `client.activity.list()`.
296
+
297
+ | Event | Trigger | Captured |
298
+ | ---------------------- | ---------------------------------- | ----------------------------------------------------------------------- |
299
+ | Mandate issued | User accepts a request | Action ID, `userParams`, signed credential, timestamp |
300
+ | Mandate revoked | `credentials.revoke()` succeeds | Mandate ID, reason, timestamp |
301
+ | Mandate canceled | Request canceled before approval | Request ID, canceler, timestamp |
302
+ | Human decision: accept | User approves a request | User, request, OTP channel (email or SMS), UI surface (hosted / iframe) |
303
+ | Human decision: reject | User rejects a request | User, request, OTP channel, UI surface |
304
+ | VP issue | `credentials.issueVP()` succeeds | Mandate ID, VP, target verifier, receipt |
305
+ | VP issue denied | `credentials.issueVP()` rejected | Mandate ID, reason code (e.g., `credential_revoked`) |
306
+ | Verify accept | `credentials.verify()` returns 200 | VP, `executionParams`, rule evaluations, signed receipt |
307
+ | Verify deny | `credentials.verify()` returns 403 | VP, `executionParams`, failing rule(s), signed receipt |
308
+
309
+ ## Webhook details
310
+
311
+ `createWebhookHandler` decrypts and verifies the payload, then dispatches a discriminated union — switch on `eventType` to narrow:
312
+
313
+ | Event | Trigger |
314
+ | ------------ | --------------------------------------------------------------------------------- |
315
+ | `credential` | A credential request was approved or rejected. `credential.id` is the mandate ID. |
316
+ | `identity` | An identity / KYC check completed. |
317
+ | `otp.failed` | A user failed OTP entry. |
318
+ | `test` | "Send test event" from the dashboard. |
319
+
320
+ **Operational notes**
321
+
322
+ - **Send a test event** from **Settings → Webhooks** before going live — confirms your URL is reachable and your handler decodes the payload.
323
+ - **Retries:** failed deliveries are retried with backoff. Make your handler idempotent (key off `requestId` + `eventType`).
324
+ - **Order:** events for unrelated requests may arrive out of order. Within a single request, `credential` precedes any follow-up events.
325
+
326
+ ## API reference
327
+
328
+ Every method below is fully typed; signatures live in the generated definitions.
182
329
 
183
330
  ```typescript
184
- // List requests
185
- const requests = await client.requests.getRequests();
331
+ // Requests — credential request lifecycle
332
+ client.requests.list(); // paginate / filter your requests
333
+ client.requests.create({ ... }); // issue a credential request to a user
334
+ client.requests.detail(requestId); // full request including credentials and subjects
335
+ client.requests.cancel(requestId); // cancel a pending request
336
+ client.requests.resendOtp(requestId); // resend OTP via the original channel
337
+
338
+ // Credentials — mandates and verification
339
+ client.credentials.detail(credentialId); // fetch a credential by ID
340
+ client.credentials.evidence(evidenceId); // download attached evidence
341
+ client.credentials.issueVP(vcId, { targetVerifier }); // issue a fresh VP from a mandate
342
+ client.credentials.verify({ presentation, executionParams }); // evaluate a VP at runtime
343
+ client.credentials.revoke(credentialId, undefined, { reason }); // permanently revoke a mandate
344
+
345
+ // Actions — published policy templates
346
+ client.actions.list();
347
+ client.actions.versions(actionId);
348
+
349
+ // Activity — audit log
350
+ client.activity.list();
351
+
352
+ // Approval workflows (forms, consents, documents)
353
+ client.approval.list();
354
+ client.approval.workflows();
355
+
356
+ // Users
357
+ client.users.create([{ contact, internalId, identity }]);
358
+ client.users.detail({ contact });
359
+
360
+ // DID resolution
361
+ client.did.resolve(did);
362
+ ```
186
363
 
187
- // Get request details
188
- const detail = await client.requests.getRequestDetail(requestId);
364
+ ## API versioning
189
365
 
190
- // Create a credential request
191
- const request = await client.requests.generate({ ... });
366
+ Humanos uses **date-based API versions** (e.g., `2026-03-24`). The SDK pins each release to a default version and sends it as an `api-version` header on every request — your integration stays isolated from API changes, and you upgrade by bumping the SDK on your own schedule.
192
367
 
193
- // Cancel a request
194
- await client.requests.cancelRequest(requestId);
368
+ The current default is exported as `API_VERSION`:
195
369
 
196
- // Resend OTP
197
- await client.requests.resendOtp(requestId);
370
+ ```typescript
371
+ import { API_VERSION } from "humanos";
372
+ console.log(API_VERSION); // "2026-03-24"
198
373
  ```
199
374
 
200
- ### Users
375
+ To pin a different version globally, override the header via `axiosConfig`:
201
376
 
202
377
  ```typescript
203
- // Create or update users
204
- const users = await client.users.create([
205
- {
206
- contact: "user@example.com",
207
- internalId: "your-internal-id",
208
- identity: {
209
- fullName: "John Doe",
210
- birth: "1990-01-01",
211
- docId: "123456789",
212
- countryAlpha3: "USA",
213
- },
378
+ const client = new HumanosClient({
379
+ basePath: "https://api.humanos.id",
380
+ apiKey: process.env.HUMANOS_API_KEY!,
381
+ signatureSecret: process.env.HUMANOS_SIGNATURE_SECRET!,
382
+ axiosConfig: {
383
+ headers: { "api-version": "2025-12-01" },
214
384
  },
215
- ]);
385
+ });
386
+ ```
387
+
388
+ Every method also accepts an optional `aPIVersion` parameter for per-call overrides — useful when testing a new version against a single endpoint before bumping globally:
389
+
390
+ ```typescript
391
+ await client.credentials.verify(
392
+ { presentation: vp.data.presentation, executionParams: { ... } },
393
+ "2025-12-01", // api version for this call only
394
+ );
216
395
  ```
217
396
 
218
- ## Error Handling
397
+ **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. List published versions with `client.actions.versions(actionId)`.
398
+
399
+ ## Error handling
219
400
 
220
- The SDK throws errors for failed requests. The error object includes the HTTP response when available:
401
+ The SDK throws on non-2xx responses. The error includes the underlying HTTP response when available:
221
402
 
222
403
  ```typescript
223
404
  try {
224
- const request = await client.requests.generate({ ... });
405
+ await client.requests.create({ ... });
225
406
  } catch (error) {
226
407
  if (error.response) {
227
- console.error('Status:', error.response.status);
228
- console.error('Body:', error.response.data);
408
+ console.error("Status:", error.response.status);
409
+ console.error("Body:", error.response.data);
229
410
  } else {
230
- console.error('Error:', error.message);
411
+ console.error("Error:", error.message);
231
412
  }
232
413
  }
233
414
  ```
234
415
 
235
- ## Documentation
416
+ ## Troubleshooting
417
+
418
+ **`401` on every request.** Signature mismatch. Double-check the signature secret matches the dashboard exactly — no trailing whitespace or stray newline.
419
+
420
+ **`400` on `requests.create()` with "name is required".** Each `credentials[]` entry needs a `name` field. The example in step 2 uses `"Action mandate"`.
421
+
422
+ **Webhook never fires.** Confirm the URL in **Settings → Webhooks** matches your live endpoint. Send a test event from the dashboard. For local dev, the ngrok tunnel must be open when the user approves.
423
+
424
+ **Webhook fires but the handler errors with a signature mismatch.** Make sure your server uses `express.text({ type: "application/json" })`. `express.json()` parses + reformats the body, which breaks HMAC verification.
425
+
426
+ **`verify()` returns 403 with no obvious failing rule.** Check that the `executionParams` field names exactly match the action's declared fields. A typo silently fails rule evaluation.
427
+
428
+ **`issueVP()` returns 400 after revoke.** Expected — once a mandate is revoked, VP issuance is blocked at source with `credential_revoked`. Existing VPs also fail `verify()` for the same reason.
429
+
430
+ ## Documentation & related
236
431
 
237
- - [API Documentation](https://humanos.mintlify.app/)
238
- - [Humanos Website](https://humanos.id)
432
+ - [Full API documentation](https://humanos.mintlify.app/)
433
+ - [Humanos website](https://humanos.id)
434
+ - Sister SDKs: [Python](../python/README.md), [C#](../csharp/README.md)
239
435
 
240
436
  ## Support
241
437
 
@@ -243,4 +439,4 @@ try {
243
439
 
244
440
  ## License
245
441
 
246
- MIT License - see [LICENSE](./LICENSE) file for details.
442
+ MIT License see [LICENSE](./LICENSE) file for details.