@assinafy/sdk 2.0.0 → 2.1.2

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.
@@ -0,0 +1,207 @@
1
+ # API coverage
2
+
3
+ This ledger maps the official
4
+ [`GET /v1/docs/openapi.json`](https://api.assinafy.com.br/v1/docs/openapi.json)
5
+ contract to the public SDK API: 67 paths, 89 HTTP operations, and 39 component
6
+ schemas.
7
+
8
+ All paths below include the `/v1` prefix shown by the OpenAPI document. The
9
+ SDK's default `baseUrl` already ends in `/v1`, so resource implementations use
10
+ the corresponding relative path without repeating that prefix.
11
+
12
+ Status meanings:
13
+
14
+ - **Covered** — a public, typed SDK method sends the operation.
15
+ - **Compatibility extension** — route retained for existing integrations but
16
+ absent from the current OpenAPI path set and excluded from the 89-operation
17
+ total.
18
+
19
+ ## Accounts — 10/10
20
+
21
+ | Method | Path | SDK method | Status |
22
+ | --- | --- | --- | --- |
23
+ | `GET` | `/v1/accounts` | `client.workspaces.list()` | Covered |
24
+ | `POST` | `/v1/accounts` | `client.workspaces.create(payload)` | Covered |
25
+ | `GET` | `/v1/accounts/{accountId}` | `client.workspaces.get(accountId)` | Covered |
26
+ | `PUT` | `/v1/accounts/{accountId}` | `client.workspaces.update(accountId, payload)` | Covered |
27
+ | `DELETE` | `/v1/accounts/{accountId}` | `client.workspaces.delete(accountId, options?)` | Covered |
28
+ | `GET` | `/v1/accounts/{accountId}/theme` | `client.workspaces.getTheme(accountId)` | Covered |
29
+ | `GET` | `/v1/accounts/{accountId}/logo` | `client.workspaces.downloadLogo(accountId)` | Covered |
30
+ | `POST` | `/v1/accounts/{accountId}/logo` | `client.workspaces.uploadLogo(accountId, source)` | Covered |
31
+ | `DELETE` | `/v1/accounts/{accountId}/logo` | `client.workspaces.deleteLogo(accountId)` | Covered |
32
+ | `GET` | `/v1/accounts/{accountId}/stats` | `client.workspaces.getStats(accountId, params?)` | Covered |
33
+
34
+ ## Assignments — 7/7
35
+
36
+ | Method | Path | SDK method | Status |
37
+ | --- | --- | --- | --- |
38
+ | `GET` | `/v1/assignments` | `client.assignments.list(params?, accountId?)` | Covered |
39
+ | `POST` | `/v1/documents/{documentId}/assignments` | `client.assignments.create(documentId, payload)` | Covered |
40
+ | `POST` | `/v1/documents/{documentId}/assignments/estimate-cost` | `client.assignments.estimateCost(documentId, payload)` | Covered |
41
+ | `PUT` | `/v1/documents/{documentId}/assignments/{assignmentId}/signers/{signerId}/resend` | `client.assignments.resendNotification(documentId, assignmentId, signerId)` | Covered |
42
+ | `POST` | `/v1/documents/{documentId}/assignments/{assignmentId}/signers/{signerId}/estimate-resend-cost` | `client.assignments.estimateResendCost(documentId, assignmentId, signerId)` | Covered |
43
+ | `PUT` | `/v1/documents/{documentId}/assignments/{assignmentId}/reset-expiration` | `client.assignments.resetExpiration(documentId, assignmentId, expiresAt)` | Covered |
44
+ | `GET` | `/v1/documents/{documentId}/assignments/{assignmentId}/whatsapp-notifications` | `client.assignments.listWhatsAppNotifications(documentId, assignmentId)` | Covered |
45
+
46
+ ## Authentication — 9/9
47
+
48
+ | Method | Path | SDK method | Status |
49
+ | --- | --- | --- | --- |
50
+ | `POST` | `/v1/login` | `client.auth.login(email, password)` | Covered |
51
+ | `PUT` | `/v1/authentication/request-password-reset` | `client.auth.requestPasswordReset(email)` | Covered |
52
+ | `PUT` | `/v1/authentication/reset-password` | `client.auth.resetPassword(payload)` | Covered |
53
+ | `PUT` | `/v1/authentication/change-password` | `client.auth.changePassword(payload)` | Covered |
54
+ | `POST` | `/v1/authentication/social-login` | `client.auth.socialLogin(payload)` | Covered |
55
+ | `POST` | `/v1/auth/link-social-login` | `client.auth.linkSocialLogin(payload)` | Covered |
56
+ | `GET` | `/v1/users/api-keys` | `client.auth.getApiKey()` | Covered |
57
+ | `POST` | `/v1/users/api-keys` | `client.auth.createApiKey(password)` | Covered |
58
+ | `DELETE` | `/v1/users/api-keys` | `client.auth.deleteApiKey()` | Covered |
59
+
60
+ ## Documents — 18/18
61
+
62
+ | Method | Path | SDK method | Status |
63
+ | --- | --- | --- | --- |
64
+ | `GET` | `/v1/accounts/{accountId}/documents` | `client.documents.list(params?, accountId?)` | Covered |
65
+ | `POST` | `/v1/accounts/{accountId}/documents` | `client.documents.upload(source, options?)` | Covered |
66
+ | `GET` | `/v1/accounts/{accountId}/documents/search` | `client.documents.search(params?, accountId?)` | Covered |
67
+ | `GET` | `/v1/documents/statuses` | `client.documents.statuses()` | Covered |
68
+ | `GET` | `/v1/documents/{documentId}` | `client.documents.details(documentId)` / `get(documentId)` | Covered |
69
+ | `DELETE` | `/v1/documents/{documentId}` | `client.documents.delete(documentId)` | Covered |
70
+ | `PATCH` | `/v1/documents/{documentId}` | `client.documents.rename(documentId, name)` | Covered |
71
+ | `GET` | `/v1/documents/{documentId}/activities` | `client.documents.activities(documentId)` | Covered |
72
+ | `GET` | `/v1/documents/{documentId}/download/{artifactName}` | `client.documents.download(documentId, artifactName)` | Covered |
73
+ | `GET` | `/v1/documents/{documentSignatureHash}/verify` | `client.documents.verify(documentSignatureHash)` | Covered |
74
+ | `GET` | `/v1/documents/{documentId}/thumbnail` | `client.documents.thumbnail(documentId)` | Covered |
75
+ | `GET` | `/v1/documents/{documentId}/pages/{pageId}/download` | `client.documents.downloadPage(documentId, pageId)` | Covered |
76
+ | `GET` | `/v1/accounts/{accountId}/documents/{documentId}/tags` | `client.documents.listTags(documentId, accountId?)` | Covered |
77
+ | `PUT` | `/v1/accounts/{accountId}/documents/{documentId}/tags` | `client.documents.replaceTags(documentId, tagIds, accountId?)` | Covered |
78
+ | `POST` | `/v1/accounts/{accountId}/documents/{documentId}/tags` | `client.documents.addTags(documentId, tagIds, accountId?)` | Covered |
79
+ | `DELETE` | `/v1/accounts/{accountId}/documents/{documentId}/tags/{tagId}` | `client.documents.detachTag(documentId, tagId, accountId?)` | Covered |
80
+ | `POST` | `/v1/accounts/{accountId}/templates/{templateId}/documents` | `client.documents.createFromTemplate(templateId, signers, options?, accountId?)` | Covered |
81
+ | `POST` | `/v1/accounts/{accountId}/templates/{templateId}/documents/estimate-cost` | `client.documents.estimateCostFromTemplate(templateId, signers, accountId?)` | Covered |
82
+
83
+ ## Fields — 8/8
84
+
85
+ | Method | Path | SDK method | Status |
86
+ | --- | --- | --- | --- |
87
+ | `GET` | `/v1/accounts/{accountId}/fields` | `client.fields.list(params?, accountId?)` | Covered |
88
+ | `POST` | `/v1/accounts/{accountId}/fields` | `client.fields.create(payload, accountId?)` | Covered |
89
+ | `GET` | `/v1/accounts/{accountId}/fields/{fieldId}` | `client.fields.get(fieldId, accountId?)` | Covered |
90
+ | `PUT` | `/v1/accounts/{accountId}/fields/{fieldId}` | `client.fields.update(fieldId, payload, accountId?)` | Covered |
91
+ | `DELETE` | `/v1/accounts/{accountId}/fields/{fieldId}` | `client.fields.delete(fieldId, accountId?)` | Covered |
92
+ | `POST` | `/v1/accounts/{accountId}/fields/{fieldId}/validate` | `client.fields.validate(fieldId, value, options?)` | Covered |
93
+ | `POST` | `/v1/accounts/{accountId}/fields/validate-multiple` | `client.fields.validateMultiple(entries, options?)` | Covered |
94
+ | `GET` | `/v1/field-types` | `client.fields.listTypes()` | Covered |
95
+
96
+ ## Signers — 5/5
97
+
98
+ | Method | Path | SDK method | Status |
99
+ | --- | --- | --- | --- |
100
+ | `GET` | `/v1/accounts/{accountId}/signers` | `client.signers.list(params?, accountId?)` | Covered |
101
+ | `POST` | `/v1/accounts/{accountId}/signers` | `client.signers.create(payload, accountId?)` | Covered |
102
+ | `GET` | `/v1/accounts/{accountId}/signers/{signerId}` | `client.signers.get(signerId, accountId?)` | Covered |
103
+ | `PUT` | `/v1/accounts/{accountId}/signers/{signerId}` | `client.signers.update(signerId, payload, accountId?)` | Covered |
104
+ | `DELETE` | `/v1/accounts/{accountId}/signers/{signerId}` | `client.signers.delete(signerId, accountId?)` | Covered |
105
+
106
+ ## Signing and public flows — 17/17
107
+
108
+ All methods in this section use the auth-free transport. Most signer operations
109
+ authenticate with the `signer-access-code` query parameter required by the
110
+ OpenAPI security scheme. The artifact download is explicitly public and accepts
111
+ an optional fourth access-code argument only for legacy compatibility.
112
+
113
+ | Method | Path | SDK method | Status |
114
+ | --- | --- | --- | --- |
115
+ | `GET` | `/v1/public/documents/{documentId}` | `client.documents.getPublic(documentId)` | Covered |
116
+ | `PUT` | `/v1/public/documents/{documentId}/send-token` | `client.documents.sendToken(documentId, email)` | Covered |
117
+ | `GET` | `/v1/signers/self` | `client.signerDocuments.self(accessCode)` | Covered |
118
+ | `GET` | `/v1/signers/{signerId}/document` | `client.signerDocuments.getCurrent(signerId, accessCode)` | Covered |
119
+ | `GET` | `/v1/sign` | `client.signerDocuments.getAssignment(accessCode, hasAcceptedTerms?)` | Covered |
120
+ | `POST` | `/v1/documents/{documentId}/assignments/{assignmentId}` | `client.signerDocuments.sign(documentId, assignmentId, accessCode, entries)` | Covered |
121
+ | `PUT` | `/v1/documents/{documentId}/assignments/{assignmentId}/reject` | `client.signerDocuments.decline(documentId, assignmentId, accessCode, reason)` | Covered |
122
+ | `PUT` | `/v1/signers/documents/sign-multiple` | `client.signerDocuments.signMultiple(documentIds, accessCode)` | Covered |
123
+ | `PUT` | `/v1/signers/documents/decline-multiple` | `client.signerDocuments.declineMultiple(documentIds, reason, accessCode)` | Covered |
124
+ | `POST` | `/v1/verify` | `client.signerDocuments.verifyEmail(payload)` | Covered |
125
+ | `PUT` | `/v1/documents/{documentId}/signers/confirm-data` | `client.signerDocuments.confirmData(documentId, accessCode, payload)` | Covered |
126
+ | `PUT` | `/v1/signers/accept-terms` | `client.signerDocuments.acceptTerms(accessCode)` | Covered |
127
+ | `POST` | `/v1/signature` | `client.signerDocuments.uploadSignature(accessCode, image, options?)` | Covered |
128
+ | `GET` | `/v1/signature/{signatureType}` | `client.signerDocuments.downloadSignature(accessCode, signatureType)` | Covered |
129
+ | `GET` | `/v1/signers/{signerId}/documents` | `client.signerDocuments.list(signerId, accessCode, params?)` | Covered |
130
+ | `GET` | `/v1/signers/{signerId}/documents/search` | `client.signerDocuments.search(signerId, accessCode, search)` | Covered |
131
+ | `GET` | `/v1/signers/{signerId}/documents/{documentId}/download/{artifactName}` | `client.signerDocuments.download(signerId, documentId, artifactName)` | Covered |
132
+
133
+ ## Tags — 4/4
134
+
135
+ | Method | Path | SDK method | Status |
136
+ | --- | --- | --- | --- |
137
+ | `GET` | `/v1/accounts/{accountId}/tags` | `client.tags.list(params?, accountId?)` | Covered |
138
+ | `POST` | `/v1/accounts/{accountId}/tags` | `client.tags.create(payload, accountId?)` | Covered |
139
+ | `PUT` | `/v1/accounts/{accountId}/tags/{tagId}` | `client.tags.update(tagId, payload, accountId?)` | Covered |
140
+ | `DELETE` | `/v1/accounts/{accountId}/tags/{tagId}` | `client.tags.delete(tagId, options?)` | Covered |
141
+
142
+ ## Templates — 1/1 official
143
+
144
+ | Method | Path | SDK method | Status |
145
+ | --- | --- | --- | --- |
146
+ | `GET` | `/v1/accounts/{accountId}/templates` | `client.templates.list(params?, accountId?)` | Covered |
147
+
148
+ Five additional routes are documented separately under
149
+ [Template compatibility routes](#template-compatibility-routes).
150
+
151
+ ## Users — 4/4
152
+
153
+ | Method | Path | SDK method | Status |
154
+ | --- | --- | --- | --- |
155
+ | `GET` | `/v1/users/self` | `client.users.getCurrent()` | Covered |
156
+ | `GET` | `/v1/users/self/stats` | `client.users.getStats(params?)` | Covered |
157
+ | `GET` | `/v1/users/self/notification-preferences` | `client.users.getNotificationPreferences()` | Covered |
158
+ | `PUT` | `/v1/users/self/notification-preferences` | `client.users.updateNotificationPreferences(preferences)` | Covered |
159
+
160
+ ## Webhooks — 6/6
161
+
162
+ | Method | Path | SDK method | Status |
163
+ | --- | --- | --- | --- |
164
+ | `GET` | `/v1/accounts/{accountId}/webhooks/subscriptions` | `client.webhooks.get(accountId?)` | Covered |
165
+ | `PUT` | `/v1/accounts/{accountId}/webhooks/subscriptions` | `client.webhooks.register(payload, accountId?)` | Covered |
166
+ | `PUT` | `/v1/accounts/{accountId}/webhooks/inactivate` | `client.webhooks.inactivate(accountId?)` | Covered |
167
+ | `GET` | `/v1/webhooks/event-types` | `client.webhooks.listEventTypes()` | Covered |
168
+ | `GET` | `/v1/accounts/{accountId}/webhooks` | `client.webhooks.listDispatches(params?, accountId?)` | Covered |
169
+ | `POST` | `/v1/accounts/{accountId}/webhooks/{historyId}/retry` | `client.webhooks.retryDispatch(historyId, accountId?)` | Covered |
170
+
171
+ ## Template compatibility routes
172
+
173
+ These routes are available to existing integrations but do not appear in the
174
+ current OpenAPI path set. They are excluded from the official 89-operation
175
+ coverage count.
176
+
177
+ | Method | Path | SDK method | Status |
178
+ | --- | --- | --- | --- |
179
+ | `POST` | `/v1/accounts/{accountId}/templates` | `client.templates.create(source, options?)` | Compatibility extension |
180
+ | `GET` | `/v1/accounts/{accountId}/templates/{templateId}` | `client.templates.get(templateId, accountId?)` | Compatibility extension |
181
+ | `PUT` | `/v1/accounts/{accountId}/templates/{templateId}` | `client.templates.update(templateId, payload, accountId?)` | Compatibility extension |
182
+ | `DELETE` | `/v1/accounts/{accountId}/templates/{templateId}` | `client.templates.delete(templateId, accountId?)` | Compatibility extension |
183
+ | `GET` | `/v1/accounts/{accountId}/templates/{templateId}/pages/{pageId}/download` | `client.templates.downloadPage(templateId, pageId, accountId?)` | Compatibility extension |
184
+
185
+ ## SDK conveniences
186
+
187
+ These helpers compose or derive official operations and therefore do not add to
188
+ the endpoint count:
189
+
190
+ - `client.uploadAndRequestSignatures(options)` validates the complete input,
191
+ uploads a document, reuses or creates signers, creates its virtual assignment
192
+ while metadata may still be processing, and optionally polls before returning
193
+ current document details.
194
+ - `client.documents.waitUntilReady(documentId, options?)` polls the official
195
+ document-details operation with terminal-state and timeout handling.
196
+ - `client.documents.isFullySigned(documentId)` and
197
+ `getSigningProgress(documentId)` derive state from document details.
198
+ - `client.signers.findByEmail(email, accountId?)` uses the official signer list.
199
+ - `client.webhookVerifier` parses webhook envelopes and optionally verifies a
200
+ caller-configured HMAC contract; signature verification itself is not in the
201
+ current OpenAPI document.
202
+
203
+ For request and response payload definitions, use the exported TypeScript
204
+ interfaces and each method's JSDoc examples together with the
205
+ [official API reference](https://api.assinafy.com.br/v1/docs). Deployment
206
+ variants that require compatibility handling are recorded in
207
+ [COMPATIBILITY.md](COMPATIBILITY.md).
@@ -0,0 +1,362 @@
1
+ # API compatibility
2
+
3
+ The SDK targets the official Assinafy contract at
4
+ [`/v1/docs/openapi.json`](https://api.assinafy.com.br/v1/docs/openapi.json).
5
+ New integrations should send the published request shape. Compatibility paths
6
+ are narrow, typed, and used only when a caller selects one or when a validation
7
+ response unambiguously requests an older shape.
8
+
9
+ ## Host availability
10
+
11
+ The production contract includes these account and authenticated-user routes:
12
+
13
+ ```text
14
+ GET, POST /v1/accounts
15
+ GET, PUT, DELETE /v1/accounts/{accountId}
16
+ GET, POST, DELETE /v1/accounts/{accountId}/logo
17
+ GET /v1/accounts/{accountId}/theme
18
+ GET /v1/accounts/{accountId}/stats
19
+ GET /v1/users/self
20
+ GET, PUT /v1/users/self/notification-preferences
21
+ GET /v1/users/self/stats
22
+ ```
23
+
24
+ Sandbox deployments can return `404` for user statistics, account statistics,
25
+ or notification preferences while still accepting the production methods on
26
+ the production host. The SDK keeps the official paths and response types. A
27
+ sandbox `404` does not cause the client to route a request elsewhere.
28
+
29
+ Two browser URL helpers used by older deployments remain available:
30
+
31
+ ```text
32
+ GET /v1/auth/authenticate
33
+ GET /v1/login-callback
34
+ ```
35
+
36
+ They map to `auth.getSocialLoginUrl()` and
37
+ `auth.getSocialLoginCallbackUrl()` and are not part of the official
38
+ 89-operation total.
39
+
40
+ The SDK also includes the production contract additions for:
41
+
42
+ - the `pades` document artifact;
43
+ - `DigitalCertificate` verification on assignment and template-document
44
+ creation and cost estimation;
45
+ - typed `display_settings` for collect fields;
46
+ - signer `government_id` updates;
47
+ - signature-image `reuse`;
48
+ - the documented `400` response from `GET /sign`; and
49
+ - the dedicated `SignerSelf` response fields.
50
+
51
+ Older signer-self responses can omit `has_signature`, `has_initial`, and
52
+ `is_signature_reusable`; assignment signers can omit `notification_history`.
53
+ Those fields are optional in the SDK response types.
54
+
55
+ `DocumentStatsRow` separates notification-channel counters from verification
56
+ method counters. Notification counters are not mutually exclusive.
57
+ `signature_requests_verification_{email,whatsapp,bypass,digital_certificate}`
58
+ are mutually exclusive and sum to `signature_requests`. Older unsuffixed email
59
+ and WhatsApp counters remain optional.
60
+
61
+ ## Assignment list account context
62
+
63
+ `GET /v1/assignments` requires the workspace in the camel-case `accountId`
64
+ query parameter even though most account-scoped routes place it in the path.
65
+ The SDK obtains it from the optional method argument or the client default:
66
+
67
+ ```ts
68
+ const page = await client.assignments.list(
69
+ { page: 1, 'per-page': 20 },
70
+ 'account-id',
71
+ );
72
+ // Request query: ?page=1&per-page=20&accountId=account-id
73
+ // Response: { data: IAssignment[], meta?: PaginationMeta }
74
+ ```
75
+
76
+ If neither source supplies an account ID, the SDK throws `ValidationError`
77
+ before making the request.
78
+
79
+ ## Template management extensions
80
+
81
+ The official OpenAPI document contains only:
82
+
83
+ ```text
84
+ GET /v1/accounts/{accountId}/templates
85
+ ```
86
+
87
+ Existing integrations can also use these routes:
88
+
89
+ ```text
90
+ POST /v1/accounts/{accountId}/templates
91
+ GET /v1/accounts/{accountId}/templates/{templateId}
92
+ PUT /v1/accounts/{accountId}/templates/{templateId}
93
+ DELETE /v1/accounts/{accountId}/templates/{templateId}
94
+ GET /v1/accounts/{accountId}/templates/{templateId}/pages/{pageId}/download
95
+ ```
96
+
97
+ They map to `templates.create`, `get`, `update`, `delete`, and `downloadPage`
98
+ and are excluded from the official operation count. Template status casing can
99
+ vary, so normalize before branching:
100
+
101
+ ```ts
102
+ if (template.status.toLowerCase() === 'ready') {
103
+ // rendered pages are available
104
+ }
105
+ ```
106
+
107
+ A page object's `download_url` is protected by account authentication. Prefer
108
+ `templates.downloadPage(templateId, pageId)` so the SDK attaches credentials
109
+ and returns the JPEG bytes as a `Buffer`.
110
+
111
+ ## Public send-token request
112
+
113
+ The official operation sends an email body:
114
+
115
+ ```http
116
+ PUT /v1/public/documents/{documentId}/send-token
117
+ Content-Type: application/json
118
+
119
+ { "email": "signer@example.com" }
120
+ ```
121
+
122
+ ```ts
123
+ await client.documents.sendToken(documentId, 'signer@example.com');
124
+ ```
125
+
126
+ Older environments can require `{ recipient, channel }`. The explicit
127
+ three-argument overload sends that shape:
128
+
129
+ ```ts
130
+ await client.documents.sendToken(documentId, '+5511999990000', 'whatsapp');
131
+ ```
132
+
133
+ For a two-argument call, the SDK starts with `{ email }` and retries the older
134
+ email shape only when the validation response names `recipient` or `channel` as
135
+ required. Other errors are returned unchanged.
136
+
137
+ ## Document tags
138
+
139
+ Replace and attach requests require existing tag IDs:
140
+
141
+ ```ts
142
+ const tag = await client.tags.create({ name: 'Contracts', color: '#ff8800' });
143
+ await client.documents.addTags(documentId, [tag.id]);
144
+ await client.documents.replaceTags(documentId, [tag.id]);
145
+ const result = await client.documents.detachTag(documentId, tag.id);
146
+ // result → { detached: true }
147
+ ```
148
+
149
+ The API accepts a tag color with or without a leading `#` and returns the
150
+ stored six-character value without it. Unknown tag names are not the normal
151
+ attachment input; create or list the tag first and send its ID. An empty array
152
+ passed to `replaceTags` detaches every tag.
153
+
154
+ ## Account branding fields
155
+
156
+ The official account create/update request schemas define `name` and
157
+ `notification_sender_type`. The response includes `primary_color` and
158
+ `secondary_color`. Some deployments also accept those two color fields on
159
+ create/update, so the SDK retains them as optional inputs. Account colors must
160
+ be six hexadecimal characters without a leading `#`.
161
+
162
+ Some sandbox plans reject `notification_sender_type` during account creation
163
+ while accepting it on update. If that occurs, create with `{ name }` and apply
164
+ the sender type in a separate update. `getTheme`, `downloadLogo`, `uploadLogo`,
165
+ and `deleteLogo` are official operations.
166
+
167
+ `workspaces.delete(accountId, { force: true })` requests cancellation of an
168
+ active paid subscription as part of account deletion. It is not a general
169
+ override for unrelated deletion restrictions.
170
+
171
+ ## Field-definition extensions
172
+
173
+ The official field-create body defines `type`, `name`, nullable `regex`, and
174
+ `is_required`; update defines `name`, nullable `regex`, and `is_active`. The SDK
175
+ also retains `is_active` on create and `type` or `is_required` on update for
176
+ deployments that accept them. New code should prefer the operation-specific
177
+ official fields.
178
+
179
+ The field-validation schemas omit the `signer-access-code` query parameter used
180
+ by some signer portals. `fields.validate()` and `validateMultiple()` retain the
181
+ typed `signerAccessCode` option for those environments.
182
+
183
+ ## Signer request extensions
184
+
185
+ The official signer-create schema uses `full_name`, `email`, and
186
+ `whatsapp_phone_number`. Signer update adds `government_id`, which the SDK
187
+ normalizes to digits. Three older integration inputs remain accepted:
188
+
189
+ - `phone` is normalized to `whatsapp_phone_number` before transmission;
190
+ - `cpf` is normalized to digits and forwarded; and
191
+ - create-time `metadata` is forwarded unchanged.
192
+
193
+ New integrations should use the official create fields and `government_id` on
194
+ update. `cpf` is not an alias for the official update field, and signer
195
+ responses do not return it.
196
+
197
+ ## Digital certificate and collect placement
198
+
199
+ `DigitalCertificate` requires the account feature, a CPF or CNPJ stored in the
200
+ signer's `government_id`, and exactly one certificate signer in that signing
201
+ step. It costs two credits per signer in addition to the selected notification
202
+ cost. Notification methods remain `Email` and `Whatsapp`.
203
+
204
+ For a `collect` assignment, each field can include `display_settings`.
205
+ `left`, `top`, `width`, `height`, and `fontSize` are required; `fontFamily` and
206
+ `backgroundColor` are optional. Values use Assinafy's 150-DPI page-image pixels
207
+ from the upper-left corner and must stay within the page.
208
+
209
+ ## Document responses and artifacts
210
+
211
+ `documents.rename()` can return a document without `pages` or `assignment`.
212
+ `IRenameDocumentResponse` therefore keeps those two properties optional while
213
+ retaining the other document fields.
214
+
215
+ `documents.details()` returns `decline_reason` only when the access token
216
+ belongs to the document creator. Do not infer the absence of a decline merely
217
+ because that field is missing for another authenticated user.
218
+
219
+ Owner and signer downloads accept `original`, `certificated`,
220
+ `certificate-page`, `pades`, and `bundle`:
221
+
222
+ - `pades` exists only when the document had an ICP-Brasil certificate signer;
223
+ - `bundle` is a ZIP containing `original`, `certificated`, and
224
+ `certificate-page`, plus `pades` when it exists; and
225
+ - a generated artifact can return `404` until processing or certification is
226
+ complete.
227
+
228
+ Document and page download URLs in JSON responses are protected. Prefer the
229
+ typed download methods so credentials are applied and binary data is returned
230
+ as a `Buffer`.
231
+
232
+ ## Signer-side preconditions
233
+
234
+ The signer `confirm-data` body contains `full_name`, `email`,
235
+ `government_id`, and `has_accepted_terms`. A certificate signer must confirm
236
+ data and accept terms before `getAssignment()`; either send
237
+ `has_accepted_terms: true` with `confirmData()` or call `acceptTerms()` first.
238
+ The `has_accepted_terms` query on `getAssignment()` is too late to open that
239
+ gate for a certificate signer.
240
+
241
+ `getAssignment()` uses `GET /sign`, but the API records the signer as having
242
+ viewed the assignment. The SDK therefore excludes this request from automatic
243
+ HTTP 429 replay.
244
+
245
+ `sign()` sends a non-empty array of `{ itemId, fieldId, pageId, value }` and is
246
+ intended for collect assignments. A virtual signer must confirm their data
247
+ before signing and should use `signMultiple()`, which accepts only virtual
248
+ documents. Certificate signers cannot use `sign()`; they complete the
249
+ certificate-start and certificate-complete browser flow.
250
+
251
+ Signature image upload sends raw PNG bytes with `Content-Type: image/png`.
252
+ `reuse: true` persists the image for later documents. A deprecated
253
+ `contentType` option remains for older integrations, but the official contract
254
+ supports PNG only.
255
+
256
+ ## Upload metadata and expiration reset
257
+
258
+ Document upload accepts a PDF of at most 25 MB and 2,000 pages. The multipart
259
+ schema documents the file; the SDK's optional `metadata` JSON part is retained
260
+ for deployments that accept application-owned opaque metadata.
261
+
262
+ The reset-expiration schema requires an ISO-8601 date-time string. The SDK also
263
+ accepts `null` for older integrations that clear an expiration this way. Confirm
264
+ support in the target deployment before sending `null`.
265
+
266
+ ## Resend-cost response variants
267
+
268
+ The official resend-cost response is `ICostEstimate`. Older deployments can
269
+ return a compact branch, so the SDK exposes `IResendCostEstimate`:
270
+
271
+ ```ts
272
+ const estimate = await client.assignments.estimateResendCost(
273
+ documentId,
274
+ assignmentId,
275
+ signerId,
276
+ );
277
+
278
+ if ('total_credits' in estimate) {
279
+ console.log(estimate.has_sufficient_resources);
280
+ } else {
281
+ console.log(estimate.total, estimate.has_sufficient_credits);
282
+ }
283
+ ```
284
+
285
+ The SDK does not add absent balance fields.
286
+
287
+ ## Public document and signer download
288
+
289
+ The official public-document response is the full `Document` schema. Compact
290
+ responses can contain only `id`, `name`, `page_count`, and `created_by`.
291
+ `IPublicDocumentInfo` requires `id` and `name`, keeps expanded fields optional,
292
+ and retains the compact fields:
293
+
294
+ ```ts
295
+ const document = await client.documents.getPublic(documentId);
296
+ const pages = document.pages?.length ?? Number(document.page_count ?? 0);
297
+ ```
298
+
299
+ Signer artifact download is public in the OpenAPI document and requires no
300
+ access-code query in the official three-argument call. An optional fourth code
301
+ is available only for older deployments that require it.
302
+
303
+ ## Empty acknowledgements
304
+
305
+ Several write operations return a success envelope without `data`; older
306
+ responses can use an empty array or object. Methods with no meaningful result
307
+ resolve to `Promise<void>` after validating the HTTP and envelope status. This
308
+ applies to token dispatch, signer OTP and terms, signature-image upload, bulk
309
+ signer actions, and deletions without a documented result. Tag deletion and
310
+ document-tag detachment preserve `{ deleted: boolean }` and
311
+ `{ detached: boolean }`.
312
+
313
+ ## WhatsApp notification buttons
314
+
315
+ The published notification button schema requires `text`. Some deployments
316
+ also return `url`, so the SDK types it as optional. A button URL can contain a
317
+ signer access or verification value; treat it as a credential and never log it.
318
+
319
+ ## Webhook delivery
320
+
321
+ Assinafy sends webhook events as HTTP `POST` JSON requests with
322
+ `Connection: close`. Any `2xx` is successful. There are at most two automatic
323
+ attempts per event with a three-second wait. After ten consecutive failed
324
+ events, ordinary delivery pauses and about 5% of later events are attempted
325
+ until one succeeds. `webhooks.retryDispatch()` requests immediate redelivery.
326
+ Only the first 2,000 characters of the receiver response body are retained in
327
+ dispatch history.
328
+
329
+ The common body contains `id`, `event`, nullable `message`, nullable `payload`,
330
+ nullable `origin`, Unix-second `created_at`, polymorphic `subject` and `object`,
331
+ and `account_id`. Use `id` for idempotent handling and accept unknown fields.
332
+
333
+ ## Webhook signature verification is not in the OpenAPI contract
334
+
335
+ The official webhook operations define subscription management, event types,
336
+ delivery history, and retry. They do not define a shared-secret field,
337
+ signature algorithm, digest encoding, or signature header for incoming events.
338
+
339
+ `client.webhookVerifier` is an opt-in HMAC-SHA256 utility for environments whose
340
+ separate Assinafy agreement provides a shared secret and hex digest. Confirm the
341
+ header name and signing procedure for the target environment before enforcing
342
+ it. Do not assume an `X-Assinafy-Signature` header solely from this SDK.
343
+
344
+ ## Authentication isolation
345
+
346
+ Public documents, login and social login, password reset, OAuth URLs, and
347
+ signer-access-code flows use a separate HTTP transport without `X-Api-Key` or
348
+ `Authorization` defaults. A credentialless `new AssinafyClient()` can drive
349
+ those flows, and credentials configured for protected resources are not sent.
350
+
351
+ Protected methods use the authenticated transport and receive the API's normal
352
+ `401` response when credentials are absent or invalid. The authenticated
353
+ transport accepts same-origin absolute URLs, rejects cross-origin requests
354
+ before dispatch, and treats credentials as redirect-sensitive. Use a separate
355
+ HTTP client for unrelated origins.
356
+
357
+ ## Authenticated-user response variants
358
+
359
+ `GET /users/self` officially returns `AuthUser` directly. Some sandbox
360
+ deployments return `{ user, accounts }` and add `user.is_password_set`.
361
+ `users.getCurrent()` normalizes both forms to `IAuthenticatedUser`, where
362
+ `is_password_set` is optional.