@assinafy/sdk 2.1.2 → 2.3.0

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.
package/README.md CHANGED
@@ -1,41 +1,73 @@
1
1
  # @assinafy/sdk
2
2
 
3
- TypeScript SDK for the [Assinafy API](https://api.assinafy.com.br/v1/docs) — a Brazilian digital signature platform.
3
+ *Português · [Read in English](README.en.md)*
4
4
 
5
- Covers all 89 operations in the current official OpenAPI document: accounts,
6
- authentication, users, documents, assignments, signers, signer-side flows,
7
- templates, tags, fields, webhooks, branding, statistics, and the high-level
8
- `uploadAndRequestSignatures` workflow. Five additional template-management
9
- routes used by existing integrations and two legacy browser URL helpers are
10
- retained for compatibility.
5
+ SDK oficial em TypeScript para a [API Assinafy](https://api.assinafy.com.br/v1/docs) plataforma
6
+ brasileira de assinatura eletrônica de documentos.
11
7
 
12
- See [API coverage](docs/API_COVERAGE.md) for the operation map and
13
- [compatibility notes](docs/COMPATIBILITY.md) for deployment-specific request
14
- and response variants.
8
+ Cobre as 93 operações do documento OpenAPI oficial: contas, autenticação, aplicações OAuth 2.1,
9
+ usuários, documentos, assignments, signatários, fluxos do lado do signatário, templates, tags,
10
+ campos, webhooks, identidade visual, estatísticas e o fluxo de alto nível
11
+ `uploadAndRequestSignatures`. Cinco rotas adicionais de gestão de templates usadas por integrações
12
+ existentes e dois helpers de URL de navegador são mantidos por compatibilidade.
15
13
 
16
- ## Requirements
14
+ O mapa operação a operação está em [docs/API_COVERAGE.md](docs/API_COVERAGE.md); as variações por
15
+ deploy, em [docs/COMPATIBILITY.md](docs/COMPATIBILITY.md).
17
16
 
18
- - Node.js 22+ for the built-in `FormData` / `Blob` APIs used by uploads. Packed
19
- CJS and ESM imports are tested on 22 (maintenance LTS), 24 (active LTS), and
20
- 26 (Current); Node 20 reached end-of-life in April 2026 and is unsupported.
21
- - or Bun 1.4.0 (the version pinned for development and CI)
17
+ ## Sumário
22
18
 
23
- ## Installation
19
+ Este documento vai da instalação ao fluxo completo de assinatura e, depois, ao detalhe de cada
20
+ recurso. Leia na ordem na primeira vez; use como referência depois.
21
+
22
+ **Preparação** — [Requisitos](#requisitos) · [Instalação](#instalação) ·
23
+ [Início rápido](#início-rápido) · [Autenticação](#autenticação) ·
24
+ [Aplicações OAuth](#aplicações-oauth) · [Configuração](#configuração)
25
+ ([limite de requisições](#limite-de-requisições), [fábricas](#fábricas)) ·
26
+ [Cobertura de endpoints](#cobertura-de-endpoints)
27
+
28
+ **O fluxo ponta a ponta** — [Ciclo de vida do documento](#ciclo-de-vida-do-documento):
29
+ [envio do PDF](#1-envie-o-pdf) → [signatários](#2-crie-ou-reaproveite-os-signatários-por-e-mail) →
30
+ [orçamento e pedido de assinatura](#3-orce-depois-peça-as-assinaturas) →
31
+ [o lado do signatário](#4-conclua-o-fluxo-do-signatário-por-e-mail) →
32
+ [conclusão e artefatos](#5-acompanhe-a-conclusão-e-baixe-os-artefatos)
33
+
34
+ **Detalhe por recurso** — [Referência de recursos](#referência-de-recursos):
35
+ [documentos](#documentos) · [signatários](#signatários) · [assignments](#assignments) ·
36
+ [ramos pagos de assinatura](#ramos-pagos-de-assinatura) · [templates](#templates) · [tags](#tags) ·
37
+ [workspaces](#workspaces) · [definições de campo](#definições-de-campo) ·
38
+ [autenticação e chaves de API](#autenticação--gestão-de-chaves-de-api) ·
39
+ [usuário autenticado](#usuário-autenticado) · [webhooks](#webhooks)
40
+ ([verificação](#verificação-de-webhooks)) ·
41
+ [endpoints do signatário](#endpoints-do-signatário)
42
+
43
+ **O restante** — [Helper de alto nível](#helper-de-alto-nível) · [Erros](#erros) ·
44
+ [Ambientes](#ambientes) · [Desenvolvimento](#desenvolvimento) · [Licença](#licença)
45
+
46
+ ## Requisitos
47
+
48
+ - Node.js 22+ (usa as APIs nativas `FormData` / `Blob` para upload). Os imports CJS e ESM
49
+ empacotados são testados no 22 (LTS de manutenção), 24 (LTS ativo) e 26 (Current). O Node 20
50
+ chegou ao fim da vida em abril de 2026 e não é suportado.
51
+ - ou Bun 1.4.0 (versão fixada no desenvolvimento e na CI)
52
+
53
+ ## Instalação
24
54
 
25
55
  ```bash
26
56
  npm install @assinafy/sdk
27
- # or
57
+ # ou
28
58
  bun add @assinafy/sdk
29
59
  ```
30
60
 
31
- The package is published to both [npmjs.com](https://www.npmjs.com/package/@assinafy/sdk) and [GitHub Packages](https://github.com/assinafy/typescript-sdk/packages). To install from GitHub Packages, add to your `.npmrc`:
61
+ O pacote é publicado no [npmjs.com](https://www.npmjs.com/package/@assinafy/sdk) e no
62
+ [GitHub Packages](https://github.com/assinafy/typescript-sdk/packages). Para instalar do GitHub
63
+ Packages, adicione ao seu `.npmrc`:
32
64
 
33
65
  ```
34
66
  @assinafy:registry=https://npm.pkg.github.com
35
67
  //npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
36
68
  ```
37
69
 
38
- ## Quick start
70
+ ## Início rápido
39
71
 
40
72
  ```ts
41
73
  import { AssinafyClient } from '@assinafy/sdk';
@@ -47,140 +79,278 @@ const client = new AssinafyClient({
47
79
  baseUrl,
48
80
  });
49
81
 
50
- const result = await client.uploadAndRequestSignatures({
51
- source: { filePath: './contract.pdf' },
82
+ const resultado = await client.uploadAndRequestSignatures({
83
+ source: { filePath: './contrato.pdf' },
52
84
  signers: [
53
- { name: 'John Doe', email: 'john@example.com' },
54
- { name: 'Jane Smith', email: 'jane@example.com' },
85
+ { name: 'João Silva', email: 'joao@exemplo.com.br' },
86
+ { name: 'Maria Souza', email: 'maria@exemplo.com.br' },
55
87
  ],
56
- message: 'Please sign this contract',
88
+ message: 'Por favor, assine este contrato',
57
89
  });
58
90
 
59
- console.log('Document ID:', result.document.id);
60
- console.log('Assignment ID:', result.assignment.id);
91
+ console.log('ID do documento:', resultado.document.id);
92
+ console.log('ID do assignment:', resultado.assignment.id);
61
93
  ```
62
94
 
63
- This path uses email verification and notification for every signer. WhatsApp
64
- and ICP-Brasil certificate signing have separate prerequisites and costs; see
65
- [Paid signing branches](#paid-signing-branches) before enabling either one.
95
+ Esse caminho usa verificação e notificação por e-mail para todos os signatários. WhatsApp e
96
+ certificado digital ICP-Brasil têm pré-requisitos e custos próprios veja
97
+ [Ramos pagos de assinatura](#ramos-pagos-de-assinatura) antes de habilitar qualquer um dos dois.
98
+
99
+ ## Autenticação
100
+
101
+ A API aceita três credenciais. A escolha depende de **em qual workspace** você está agindo.
66
102
 
67
- ## Authentication
103
+ | Credencial | Age sobre | Use quando |
104
+ | --- | --- | --- |
105
+ | `apiKey` (`X-Api-Key`) | **Seu próprio** workspace | Você automatiza a sua conta. Recomendada para serviços de back-end. |
106
+ | `token` (`Authorization: Bearer`) | O usuário autenticado | Você obteve uma sessão com `auth.login()`. |
107
+ | Token de acesso OAuth (`Authorization: Bearer`) | O workspace **de outra pessoa**, com autorização dela | Você constrói um app que outras pessoas conectam. Veja [Aplicações OAuth](#aplicações-oauth). |
68
108
 
69
- The API supports two authentication methods. Prefer `apiKey` it maps to the `X-Api-Key` header recommended by Assinafy for backend services.
109
+ Prefira `apiKey` para integração servidor a servidorcorresponde ao header `X-Api-Key`
110
+ recomendado pela Assinafy.
70
111
 
71
112
  ```ts
72
- // Preferred: X-Api-Key header
113
+ // Preferido: header X-Api-Key
73
114
  new AssinafyClient({ apiKey: 'k_xxx', accountId: 'acc_xxx' });
74
115
 
75
- // Access token: Authorization: Bearer <token>
116
+ // Token de acesso: Authorization: Bearer <token>
76
117
  new AssinafyClient({ token: 'jwt_xxx', accountId: 'acc_xxx' });
77
118
  ```
78
119
 
79
- Credentials are optional at construction time. A credentialless client uses a
80
- separate, auth-free transport for public authentication and signer-access-code
81
- operations, so an API key or Bearer token is never attached accidentally:
120
+ As credenciais são opcionais na construção. Um cliente sem credenciais usa um transporte separado,
121
+ sem autenticação, para as operações públicas e as que usam código de acesso do signatário — assim
122
+ uma chave de API ou token Bearer nunca é anexada por acidente:
82
123
 
83
124
  ```ts
84
- const publicClient = new AssinafyClient({
125
+ const clientePublico = new AssinafyClient({
85
126
  baseUrl: 'https://sandbox.assinafy.com.br/v1',
86
127
  });
87
128
 
88
- await publicClient.auth.login('me@example.com', 'password');
89
- await publicClient.documents.getPublic(documentId);
90
- await publicClient.signerDocuments.self(signerAccessCode);
129
+ await clientePublico.auth.login('eu@exemplo.com.br', 'senha');
130
+ await clientePublico.documents.getPublic(documentId);
131
+ await clientePublico.signerDocuments.self(signerAccessCode);
91
132
  ```
92
133
 
93
- Protected methods still require `apiKey` or `token`; the API returns its normal
94
- `401` response if one is called without credentials.
134
+ Métodos protegidos continuam exigindo `apiKey` ou `token`; sem credencial, a API devolve o `401`
135
+ normal.
95
136
 
96
- Every SDK transport, including public and signer-access-code requests, sends
97
- `User-Agent: Assinafy-Typescript-SDK/v<VERSION>`, where `<VERSION>` is the
98
- installed package version. The exact value is also exported as
99
- `SDK_USER_AGENT` for custom transport checks and observability rules.
137
+ Todos os transportes do SDK inclusive os públicos e os por código de acesso — enviam
138
+ `User-Agent: Assinafy-Typescript-SDK/v<VERSÃO>`, onde `<VERSÃO>` é a versão instalada do pacote. O
139
+ valor exato também é exportado como `SDK_USER_AGENT`.
100
140
 
101
- ## Configuration
141
+ ## Aplicações OAuth
102
142
 
103
- | Option | Type | Default | Description |
104
- | --------------- | -------- | --------------------------------------- | --------------------------------------------- |
105
- | `apiKey` | string | | Preferred credential (sent as `X-Api-Key`). |
106
- | `token` | string | — | Access token (sent as `Authorization: Bearer`). |
107
- | `accountId` | string | — | Default workspace/account ID. |
108
- | `baseUrl` | string | `https://api.assinafy.com.br/v1` | Absolute HTTP(S) API base without credentials, query, or fragment. |
109
- | `webhookSecret` | string | — | Opt-in HMAC secret used by `WebhookVerifier`; see its [contract caveat](docs/COMPATIBILITY.md#webhook-signature-verification-is-not-in-the-openapi-contract). |
110
- | `timeout` | number | `30000` | Request timeout in milliseconds. |
111
- | `maxRetries` | number | `2` | Auto-retries eligible HTTP 429 responses, honoring `Retry-After`. `0` disables. |
112
- | `logger` | `Logger` | no-op | Optional `{debug,info,warn,error}` logger. |
143
+ Use OAuth quando o seu produto for conectado **pelos usuários dele** aos workspaces **deles** na
144
+ Assinafy, sem que você jamais tenha a senha ou a chave de API dessas pessoas. Para automatizar o seu
145
+ próprio workspace nada disso é necessário continue com a chave de API.
113
146
 
114
- ### Rate limiting
147
+ Registre a aplicação no app da Assinafy em **Configurações → Aplicações OAuth → Nova aplicação**.
148
+ Você define as URIs de redirecionamento (`https://`, sem fragmento, comparadas caractere a
149
+ caractere), as permissões máximas que a aplicação poderá pedir, e se ela é **confidencial** (roda no
150
+ seu servidor e recebe um `client_secret`) ou **pública** (roda no dispositivo do usuário, só PKCE).
151
+ Aplicações não são criadas pela API.
115
152
 
116
- On an HTTP `429`, the client automatically retries up to `maxRetries` times,
117
- waiting for the server-provided `Retry-After` (or `X-Rate-Limit-Reset`) delay
118
- before each attempt. Automatic replay is limited to read-safe `GET`, `HEAD`,
119
- `OPTIONS`, and `DELETE` requests. `GET /sign` is excluded because it records
120
- that the signer viewed the assignment. Writes are not replayed by default.
121
- A non-empty `Idempotency-Key` opts a custom request into SDK replay, but it is
122
- not part of the current Assinafy OpenAPI contract: confirm that the target
123
- route deduplicates that key server-side first. No other HTTP status is retried.
153
+ O fluxo passa por dois hosts de propósito: a tela de consentimento fica no servidor de autorização
154
+ (`https://auth.assinafy.com.br`), enquanto os endpoints de token, revogação e userinfo ficam nesta
155
+ API. Leia os dois da descoberta em vez de fixá-los no código.
124
156
 
125
- ### Factories
157
+ ```ts
158
+ import { AssinafyClient, OAuthError } from '@assinafy/sdk';
159
+
160
+ const client = new AssinafyClient(); // nenhuma credencial necessária
161
+
162
+ // 1 — antes de redirecionar o usuário. Guarde a requisição inteira na sessão dele:
163
+ // `state` e `issuer` provam que o callback é seu, `codeVerifier` completa o
164
+ // PKCE e `nonce` valida o id_token.
165
+ const requisicao = await client.oauth.createAuthorizationUrl({
166
+ clientId: process.env.ASSINAFY_CLIENT_ID!,
167
+ redirectUri: 'https://meuapp.com.br/oauth/callback',
168
+ scopes: ['documents:read', 'documents:write', 'offline_access'],
169
+ });
170
+ sessao.oauth = requisicao;
171
+ resposta.redirect(requisicao.url); // navegação de página inteira
172
+
173
+ // 2 — em https://meuapp.com.br/oauth/callback
174
+ const { code } = client.oauth.readAuthorizationCallback(query, sessao.oauth);
175
+ const tokens = await client.oauth.exchangeCode({
176
+ code,
177
+ codeVerifier: sessao.oauth.codeVerifier,
178
+ redirectUri: 'https://meuapp.com.br/oauth/callback',
179
+ clientId: process.env.ASSINAFY_CLIENT_ID!,
180
+ clientSecret: process.env.ASSINAFY_CLIENT_SECRET, // só aplicações confidenciais
181
+ });
182
+ // → { access_token, token_type: 'Bearer', expires_in: 3600,
183
+ // scope: 'documents:read documents:write',
184
+ // refresh_token?, id_token? }
185
+
186
+ // 3 — um token vale para EXATAMENTE UM workspace: o que o usuário escolheu.
187
+ const conectado = new AssinafyClient({ token: tokens.access_token });
188
+ const { data } = await conectado.workspaces.list();
189
+ const accountId = data[0]?.id; // guarde junto com os tokens
190
+
191
+ // 4 — renove antes de completar uma hora (exige `offline_access`)
192
+ const renovado = await client.oauth.refreshToken({
193
+ refreshToken: conexao.refreshToken,
194
+ clientId: process.env.ASSINAFY_CLIENT_ID!,
195
+ clientSecret: process.env.ASSINAFY_CLIENT_SECRET,
196
+ });
197
+ await conexao.save({ refreshToken: renovado.refresh_token }); // ANTES de usar
198
+
199
+ // 5 — quando o usuário desconectar
200
+ await client.oauth.revokeToken({
201
+ token: conexao.refreshToken,
202
+ tokenTypeHint: 'refresh_token',
203
+ clientId: process.env.ASSINAFY_CLIENT_ID!,
204
+ clientSecret: process.env.ASSINAFY_CLIENT_SECRET,
205
+ });
206
+ ```
207
+
208
+ Descoberta e identidade, quando precisar:
209
+
210
+ ```ts
211
+ await client.oauth.getProtectedResourceMetadata(); // RFC 9728, na raiz do host da API
212
+ await client.oauth.getAuthorizationServerMetadata(); // RFC 8414, em auth.assinafy.com.br
213
+ await client.oauth.getUserInfo(tokens.access_token); // claims OIDC; exige `openid`
214
+ ```
215
+
216
+ ### Permissões (escopos)
217
+
218
+ | Escopo | Permite ao seu app |
219
+ | --- | --- |
220
+ | `documents:read` | Ler documentos, seus signatários, assignments e atividades |
221
+ | `documents:write` | Criar documentos e enviá-los para assinatura |
222
+ | `templates:read` | Ler templates |
223
+ | `templates:write` | Criar e alterar templates |
224
+ | `account:read` | Ler perfil, tema e logotipo do workspace |
225
+ | `openid` | Receber um `id_token` identificando o usuário |
226
+ | `profile` | Ler o nome do usuário |
227
+ | `email` | Ler o e-mail do usuário e se está verificado |
228
+ | `offline_access` | Receber um refresh token |
229
+
230
+ Peça o mínimo: o usuário aprova todos ou nenhum. Leia o `scope` devolvido pelo endpoint de token em
231
+ vez de supor que o pedido foi atendido por inteiro — `offline_access` nunca aparece ali, porque é um
232
+ sinal de requisição, não uma permissão. Faturamento, membros do workspace, credenciais e
233
+ administração nunca são alcançáveis por um token OAuth, quaisquer que sejam seus escopos.
234
+
235
+ ### O que o SDK garante para você
236
+
237
+ - Um verificador RFC 7636 (S256) e um `state` novos a cada tentativa.
238
+ - `state` comparado em tempo constante e `iss` conferido (RFC 9207) **antes** de qualquer confiança
239
+ na resposta.
240
+ - O `issuer` do documento RFC 8414 conferido contra a URL de onde ele veio.
241
+ - `redirect_uri` obrigatoriamente `https://` absoluta e sem fragmento.
242
+ - O indicador de recurso RFC 8707 preenchido com a origem da API configurada e mantido idêntico
243
+ entre a etapa de autorização e a de token.
244
+
245
+ ### Tratamento de falhas
246
+
247
+ ```ts
248
+ try {
249
+ await conectado.documents.upload({ filePath: './contrato.pdf' });
250
+ } catch (erro) {
251
+ if (erro instanceof ApiError && erro.challenge?.error === 'insufficient_scope') {
252
+ // Reconecte pedindo erro.challenge.scope — por exemplo 'documents:write'.
253
+ }
254
+ }
255
+ ```
256
+
257
+ | Situação | O que você vê | O que fazer |
258
+ | --- | --- | --- |
259
+ | O usuário recusou | `OAuthError` com `error: 'access_denied'` em `readAuthorizationCallback` | Nada; avise o usuário |
260
+ | Código usado, expirado (60 s) ou divergente | `OAuthError` `invalid_grant` | Recomece o fluxo de autorização |
261
+ | Refresh token reutilizado ou expirado | `OAuthError` `invalid_grant` | A conexão inteira acabou; peça para reconectar |
262
+ | `client_id`/segredo errado, app desativado | `OAuthError` `invalid_client` | Corrija a configuração; repetir não resolve |
263
+ | Token expirado ou revogado | `ApiError` `401` | Renove; se falhar, peça para reconectar |
264
+ | Falta de permissão | `ApiError` `403` com `challenge.error === 'insufficient_scope'` | Reconecte pedindo `challenge.scope` |
265
+ | `403` sem challenge | `ApiError` `403` | Outro workspace, ou área que OAuth não alcança |
266
+
267
+ Refresh tokens **rotacionam**: cada renovação devolve um novo e aposenta o anterior, e reutilizar um
268
+ token aposentado encerra a conexão inteira. Persista o novo valor antes de usar a resposta, trate um
269
+ timeout como "talvez tenha funcionado" — releia o token guardado em vez de repetir às cegas — e
270
+ nunca renove a mesma conexão duas vezes em paralelo. Uma conexão dura 30 dias a partir da aprovação,
271
+ por mais que seja renovada, e os endpoints de autorização e token aceitam 50 requisições por minuto
272
+ por IP.
273
+
274
+ Assistentes de IA como Claude, Claude Code e ChatGPT se conectam à Assinafy pelas próprias
275
+ configurações de conector; seus usuários não precisam que você registre nada para eles.
276
+
277
+ ## Configuração
278
+
279
+ | Opção | Tipo | Padrão | Descrição |
280
+ | --------------- | -------- | -------------------------------- | --------- |
281
+ | `apiKey` | string | — | Credencial preferida (enviada como `X-Api-Key`). |
282
+ | `token` | string | — | Token de acesso (enviado como `Authorization: Bearer`). Serve também para tokens OAuth. |
283
+ | `accountId` | string | — | ID padrão da conta / workspace. |
284
+ | `baseUrl` | string | `https://api.assinafy.com.br/v1` | Base absoluta da API, sem credenciais, query ou fragmento. Precisa ser `https`, salvo em loopback. |
285
+ | `webhookSecret` | string | — | Segredo HMAC opcional usado pelo `WebhookVerifier`; veja a [ressalva de contrato](docs/COMPATIBILITY.md#webhook-signature-verification-is-not-in-the-openapi-contract). |
286
+ | `timeout` | number | `30000` | Timeout da requisição, em milissegundos. |
287
+ | `maxRetries` | number | `2` | Retenta automaticamente respostas `429` elegíveis, respeitando `Retry-After`. `0` desativa. |
288
+ | `logger` | `Logger` | no-op | Logger opcional `{debug,info,warn,error}`. |
289
+
290
+ ### Limite de requisições
291
+
292
+ Em um HTTP `429` o cliente retenta até `maxRetries` vezes, aguardando o `Retry-After` (ou
293
+ `X-Rate-Limit-Reset`) devolvido pelo servidor antes de cada tentativa. A repetição automática vale
294
+ apenas para `GET`, `HEAD`, `OPTIONS` e `DELETE`, que são seguros de repetir. `GET /sign` fica de
295
+ fora porque registra que o signatário visualizou o assignment. Escritas não são repetidas por
296
+ padrão. Um `Idempotency-Key` não vazio inscreve uma requisição customizada na repetição do SDK, mas
297
+ ele **não** faz parte do contrato OpenAPI atual: confirme antes que a rota alvo de fato deduplica
298
+ essa chave no servidor. Nenhum outro status é retentado.
299
+
300
+ ### Fábricas
126
301
 
127
302
  ```ts
128
- // Positional factory
129
- const client = AssinafyClient.create('api-key', 'account-id');
303
+ // Fábrica posicional
304
+ const client = AssinafyClient.create('chave-de-api', 'id-da-conta');
130
305
 
131
- // From a plain object (accepts snake_case or camelCase keys)
306
+ // A partir de um objeto simples (aceita chaves snake_case ou camelCase)
132
307
  const client = AssinafyClient.fromConfig({
133
308
  api_key: process.env.ASSINAFY_API_KEY!,
134
309
  account_id: process.env.ASSINAFY_ACCOUNT_ID!,
135
310
  });
136
311
  ```
137
312
 
138
- ## Endpoint coverage
139
-
140
- All 89 operations documented at https://api.assinafy.com.br/v1/docs are
141
- covered. The table below is the resource-level summary; the detailed operation
142
- ledger is in [docs/API_COVERAGE.md](docs/API_COVERAGE.md).
143
-
144
- | Resource | Endpoints |
145
- | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
146
- | `client.documents` | list, **search**, upload, details, get, **rename**, activities, waitUntilReady, download, thumbnail, downloadPage, statuses, delete, verify, createFromTemplate, estimateCostFromTemplate, getPublic, sendToken, listTags, replaceTags, addTags, detachTag, isFullySigned, getSigningProgress |
147
- | `client.signers` | create, get, list, update, delete, findByEmail |
148
- | `client.assignments` | **list**, create, estimateCost, resetExpiration, resendNotification, estimateResendCost, listWhatsAppNotifications |
149
- | `client.templates` | **create**, list, get, **update**, **delete**, downloadPage |
150
- | `client.tags` | list, create, update, delete |
151
- | `client.workspaces` | create, list, get, update, delete, getTheme, downloadLogo, uploadLogo, deleteLogo, getStats |
152
- | `client.webhooks` | register, get, inactivate, listEventTypes, listDispatches, retryDispatch |
153
- | `client.fields` | create, list, get, update, delete, validate, validateMultiple, listTypes |
154
- | `client.auth` | getSocialLoginUrl, getSocialLoginCallbackUrl, login, socialLogin, linkSocialLogin, createApiKey, getApiKey, deleteApiKey, changePassword, requestPasswordReset, resetPassword |
155
- | `client.users` | getCurrent, getStats, getNotificationPreferences, updateNotificationPreferences |
156
- | `client.signerDocuments` | getCurrent, list, **search**, download, signMultiple, declineMultiple, self, acceptTerms, verifyEmail, confirmData, uploadSignature, downloadSignature, getAssignment, sign, decline |
157
- | `client.webhookVerifier` | verify, extractEvent, getEventType, getEventData |
158
-
159
- Every HTTP wrapper has TypeScript-checked request/response shapes and
160
- method-level JSDoc covering the wire payload, return shape, validation,
161
- relevant API errors, and a copyable example. Reusable and OpenAPI schema-level
162
- payloads are exported as named types; small method-local option bags remain
163
- inline in the generated declarations. Editors expose the reference on hover,
164
- and declaration files ship with the package. The coverage ledger links those
165
- typed methods back to each upstream operation without duplicating the schema.
166
-
167
- ## Document lifecycle
168
-
169
- The normal integration has an account-owner phase, a signer phase, and a final
170
- artifact phase. The example below keeps every signer on email and uses a
171
- `virtual` assignment, so no page coordinates or paid notification channel are
172
- required.
173
-
174
- ### 1. Upload the PDF
313
+ ## Cobertura de endpoints
314
+
315
+ As 93 operações documentadas em https://api.assinafy.com.br/v1/docs estão cobertas. A tabela abaixo
316
+ é o resumo por recurso; o mapa detalhado por operação está em
317
+ [docs/API_COVERAGE.md](docs/API_COVERAGE.md).
318
+
319
+ | Recurso | Endpoints |
320
+ | --- | --- |
321
+ | `client.documents` | list, search, upload, details, get, rename, activities, waitUntilReady, download, thumbnail, downloadPage, statuses, delete, verify, createFromTemplate, estimateCostFromTemplate, getPublic, sendToken, listTags, replaceTags, addTags, detachTag, isFullySigned, getSigningProgress |
322
+ | `client.signers` | create, get, list, update, delete, findByEmail |
323
+ | `client.assignments` | list, create, estimateCost, resetExpiration, resendNotification, estimateResendCost, listWhatsAppNotifications |
324
+ | `client.templates` | create, list, get, update, delete, downloadPage |
325
+ | `client.tags` | list, create, update, delete |
326
+ | `client.workspaces` | create, list, get, update, delete, getTheme, downloadLogo, uploadLogo, deleteLogo, getStats |
327
+ | `client.webhooks` | register, get, inactivate, listEventTypes, listDispatches, retryDispatch |
328
+ | `client.fields` | create, list, get, update, delete, validate, validateMultiple, listTypes |
329
+ | `client.oauth` | **getProtectedResourceMetadata**, **getAuthorizationServerMetadata**, **createAuthorizationUrl**, **readAuthorizationCallback**, **exchangeCode**, **refreshToken**, **revokeToken**, **getUserInfo** |
330
+ | `client.auth` | getSocialLoginUrl, getSocialLoginCallbackUrl, login, socialLogin, linkSocialLogin, createApiKey, getApiKey, deleteApiKey, changePassword, requestPasswordReset, resetPassword |
331
+ | `client.users` | getCurrent, getStats, getNotificationPreferences, updateNotificationPreferences |
332
+ | `client.signerDocuments` | getCurrent, list, search, download, signMultiple, declineMultiple, self, acceptTerms, verifyEmail, confirmData, uploadSignature, downloadSignature, getAssignment, sign, decline |
333
+ | `client.webhookVerifier` | verify, extractEvent, getEventType, getEventData |
334
+
335
+ Todo wrapper HTTP tem tipos de requisição e resposta verificados pelo TypeScript e JSDoc por método,
336
+ cobrindo o payload de rede, o formato de retorno, validação, erros relevantes da API e um exemplo
337
+ copiável. As declarações acompanham o pacote.
338
+
339
+ ## Ciclo de vida do documento
340
+
341
+ A integração normal tem uma fase do dono da conta, uma fase do signatário e uma fase final de
342
+ artefatos. O exemplo abaixo mantém todos os signatários no e-mail e usa um assignment `virtual`,
343
+ então não exige coordenadas de página nem canal pago de notificação.
344
+
345
+ ### 1. Envie o PDF
175
346
 
176
347
  ```ts
177
- const uploaded = await client.documents.upload({ filePath: './contract.pdf' });
348
+ const enviado = await client.documents.upload({ filePath: './contrato.pdf' });
178
349
  ```
179
350
 
180
- The official multipart body contains the `file` part. The SDK also supports a
181
- display-name override (used as that file part's filename) and an optional JSON
182
- `metadata` part for deployments that accept it. A successful response is
183
- `IDocumentUploadResponse`:
351
+ O corpo multipart oficial contém a parte `file`. O SDK também aceita um nome de exibição (usado como
352
+ nome de arquivo dessa parte) e uma parte JSON `metadata` opcional, para deploys que a aceitem. A
353
+ resposta de sucesso é `IDocumentUploadResponse`:
184
354
 
185
355
  ```ts
186
356
  {
@@ -210,37 +380,36 @@ display-name override (used as that file part's filename) and an optional JSON
210
380
  }
211
381
  ```
212
382
 
213
- `DocumentStatus` covers `uploading`, `uploaded`, `metadata_processing`,
214
- `metadata_ready`, `pending_signature`, `expired`, `certificating`,
215
- `certificated`, `rejected_by_signer`, `rejected_by_user`, and `failed`.
383
+ `DocumentStatus` cobre `uploading`, `uploaded`, `metadata_processing`, `metadata_ready`,
384
+ `pending_signature`, `expired`, `certificating`, `certificated`, `rejected_by_signer`,
385
+ `rejected_by_user` e `failed`.
216
386
 
217
- Uploads must be PDFs, at most 25 MB and at most 2,000 pages. The SDK checks the
218
- extension, size, and `%PDF-` header before sending. A new upload can have an
219
- empty `pages` array until metadata processing finishes. Wait before creating a
220
- `collect` assignment because its fields refer to rendered page IDs; a
221
- `virtual` assignment may be created immediately.
387
+ O envio precisa ser um PDF de no máximo 25 MB e 2.000 páginas. O SDK confere extensão, tamanho e o
388
+ cabeçalho `%PDF-` antes de enviar. Um upload novo pode ter `pages` vazio até o processamento de
389
+ metadados terminar. Espere antes de criar um assignment `collect`, porque seus campos referenciam
390
+ IDs de páginas renderizadas; um assignment `virtual` pode ser criado imediatamente.
222
391
 
223
392
  ```ts
224
- const prepared = await client.documents.waitUntilReady(uploaded.id, {
393
+ const preparado = await client.documents.waitUntilReady(enviado.id, {
225
394
  maxWaitMs: 30_000,
226
395
  pollIntervalMs: 2_000,
227
396
  });
228
397
  ```
229
398
 
230
- ### 2. Create or reuse the email signers
399
+ ### 2. Crie ou reaproveite os signatários por e-mail
231
400
 
232
401
  ```ts
233
- const signerA = await client.signers.create({
234
- full_name: 'John Doe',
235
- email: 'john@example.com',
402
+ const signatarioA = await client.signers.create({
403
+ full_name: 'João Silva',
404
+ email: 'joao@exemplo.com.br',
236
405
  });
237
- const signerB = await client.signers.create({
238
- full_name: 'Jane Smith',
239
- email: 'jane@example.com',
406
+ const signatarioB = await client.signers.create({
407
+ full_name: 'Maria Souza',
408
+ email: 'maria@exemplo.com.br',
240
409
  });
241
410
  ```
242
411
 
243
- The wire body is `{ full_name, email }`. Each response is an `ISigner`:
412
+ O corpo de rede é `{ full_name, email }`. Cada resposta é um `ISigner`:
244
413
 
245
414
  ```ts
246
415
  {
@@ -249,35 +418,34 @@ The wire body is `{ full_name, email }`. Each response is an `ISigner`:
249
418
  full_name: string;
250
419
  email: string | null;
251
420
  whatsapp_phone_number?: string | null;
252
- cpf?: string | null; // compatibility type; not echoed by the API
421
+ cpf?: string | null; // tipo de compatibilidade; a API não devolve
253
422
  has_accepted_terms?: boolean;
254
- has_signature?: boolean; // signer-self response only
255
- has_initial?: boolean; // signer-self response only
256
- is_signature_reusable?: boolean; // signer-self response only
423
+ has_signature?: boolean; // na resposta de signers/self
424
+ has_initial?: boolean; // na resposta de signers/self
425
+ is_signature_reusable?: boolean; // na resposta de signers/self
257
426
  metadata?: Record<string, unknown>;
258
427
  }
259
428
  ```
260
429
 
261
- When an email is present, `signers.create()` first looks up that email in the
262
- workspace and reuses the matching signer; a name-only or phone-only request
263
- always creates a new signer.
430
+ Quando e-mail, `signers.create()` primeiro procura esse endereço no workspace e reaproveita o
431
+ signatário correspondente; uma requisição com nome ou com telefone sempre cria um novo.
264
432
 
265
- ### 3. Price, then request signatures
433
+ ### 3. Orce, depois peça as assinaturas
266
434
 
267
- Cost estimation takes channel descriptors, not signer IDs:
435
+ A estimativa de custo recebe descritores de canal, não IDs de signatário:
268
436
 
269
437
  ```ts
270
- const estimate = await client.assignments.estimateCost(uploaded.id, {
438
+ const estimativa = await client.assignments.estimateCost(enviado.id, {
271
439
  method: 'virtual',
272
- signers: [{}, {}], // `{}` selects Email for each signer
440
+ signers: [{}, {}], // `{}` seleciona Email para cada signatário
273
441
  });
274
442
 
275
- if (!estimate.has_sufficient_resources) {
276
- throw new Error(estimate.blocking_reason ?? estimate.message ?? 'Insufficient resources');
443
+ if (!estimativa.has_sufficient_resources) {
444
+ throw new Error(estimativa.blocking_reason ?? estimativa.message ?? 'Recursos insuficientes');
277
445
  }
278
446
  ```
279
447
 
280
- The response is `ICostEstimate`:
448
+ A resposta é `ICostEstimate`:
281
449
 
282
450
  ```ts
283
451
  {
@@ -295,21 +463,21 @@ The response is `ICostEstimate`:
295
463
  }
296
464
  ```
297
465
 
298
- Create the email assignment only after accepting that estimate:
466
+ Crie o assignment por e-mail depois de aceitar essa estimativa:
299
467
 
300
468
  ```ts
301
- const assignment = await client.assignments.create(uploaded.id, {
469
+ const assignment = await client.assignments.create(enviado.id, {
302
470
  method: 'virtual',
303
471
  signers: [
304
- { id: signerA.id, verification_method: 'Email', notification_methods: ['Email'] },
305
- { id: signerB.id, verification_method: 'Email', notification_methods: ['Email'] },
472
+ { id: signatarioA.id, verification_method: 'Email', notification_methods: ['Email'] },
473
+ { id: signatarioB.id, verification_method: 'Email', notification_methods: ['Email'] },
306
474
  ],
307
- message: 'Please review and sign',
475
+ message: 'Por favor, revise e assine',
308
476
  expires_at: '2027-12-31T23:59:00Z',
309
477
  });
310
478
  ```
311
479
 
312
- The request returns an `IAssignment`:
480
+ A requisição devolve um `IAssignment`:
313
481
 
314
482
  ```ts
315
483
  {
@@ -332,240 +500,232 @@ The request returns an `IAssignment`:
332
500
  }
333
501
  ```
334
502
 
335
- The URLs and delivered messages contain signer credentials; treat them as
336
- secrets.
503
+ As URLs e as mensagens entregues contêm credenciais do signatário; trate-as como segredo.
337
504
 
338
- ### 4. Complete the email signer flow
505
+ ### 4. Conclua o fluxo do signatário por e-mail
339
506
 
340
- Assinafy sends each signer a link containing their access code and sends the
341
- one-time verification code through the selected channel. Neither value is
342
- returned as a standalone owner-side API field. A custom signer portal must
343
- obtain both values from the signer-delivery flow; do not manufacture them or
344
- log them.
507
+ A Assinafy envia a cada signatário um link com o código de acesso dele e manda o código de
508
+ verificação de uso único pelo canal escolhido. Nenhum dos dois valores é devolvido como campo
509
+ autônomo do lado do dono da conta. Um portal de assinatura próprio precisa obter os dois pelo fluxo
510
+ de entrega ao signatário; não os fabrique nem os registre em log.
345
511
 
346
512
  ```ts
347
- // Signer-side client: no account API credential is needed or sent.
348
- const signerClient = new AssinafyClient({
349
- baseUrl,
350
- });
513
+ // Cliente do signatário: nenhuma credencial da conta é necessária nem enviada.
514
+ const clienteSignatario = new AssinafyClient({ baseUrl });
351
515
 
352
- const self = await signerClient.signerDocuments.self(accessCode); // ISignerSelf
516
+ const self = await clienteSignatario.signerDocuments.self(accessCode); // ISignerSelf
353
517
 
354
518
  // Query: signer-access-code=<accessCode>
355
- // Body: { 'verification-code': '<six-digit code>' }
356
- await signerClient.signerDocuments.verifyEmail({
519
+ // Corpo: { 'verification-code': '<código de seis dígitos>' }
520
+ await clienteSignatario.signerDocuments.verifyEmail({
357
521
  signerAccessCode: accessCode,
358
522
  verificationCode,
359
523
  }); // Promise<void>
360
524
 
361
- const confirmed = await signerClient.signerDocuments.confirmData(
362
- uploaded.id,
525
+ const confirmado = await clienteSignatario.signerDocuments.confirmData(
526
+ enviado.id,
363
527
  accessCode,
364
528
  { full_name: self.full_name, email: self.email ?? undefined },
365
529
  ); // ISigner
366
530
 
367
- const signable = await signerClient.signerDocuments.getAssignment(accessCode, true);
368
- // `getAssignment` returns IDocumentDetailsResponse and records that the signer
369
- // viewed the assignment. Do not issue it merely as a health check.
531
+ const assinavel = await clienteSignatario.signerDocuments.getAssignment(accessCode, true);
532
+ // `getAssignment` devolve IDocumentDetailsResponse e registra que o signatário
533
+ // visualizou o assignment. Não use como simples healthcheck.
370
534
 
371
- await signerClient.signerDocuments.signMultiple([signable.id], accessCode);
372
- // Wire body: { document_ids: [signable.id] }; acknowledgement has no data.
535
+ await clienteSignatario.signerDocuments.signMultiple([assinavel.id], accessCode);
536
+ // Corpo de rede: { document_ids: [assinavel.id] }; o reconhecimento não traz dados.
373
537
  ```
374
538
 
375
- Repeat this phase separately for each signer with that signer's own access code
376
- and one-time code. The two signers in this example share the default step and
377
- can sign in parallel.
539
+ Repita esta fase separadamente para cada signatário, com o código de acesso e o código de uso único
540
+ dele. Os dois signatários deste exemplo compartilham o passo padrão e podem assinar em paralelo.
378
541
 
379
- `signMultiple` is only for `virtual` assignments. For `collect`, read
380
- `signable.assignment.items`, then call `sign(documentId, assignmentId,
381
- accessCode, entries)` with a non-empty array of
382
- `{ itemId, fieldId, pageId, value }`. A virtual signer must confirm their data
383
- before signing. A `DigitalCertificate` signer cannot call `sign`; that branch
384
- uses Assinafy's certificate-start and certificate-complete flow, which is not
385
- part of this SDK's current 89-operation surface.
542
+ `signMultiple` serve apenas a assignments `virtual`. Para `collect`, leia `assinavel.assignment.items`
543
+ e chame `sign(documentId, assignmentId, accessCode, entries)` com um array não vazio de
544
+ `{ itemId, fieldId, pageId, value }`. Um signatário virtual precisa confirmar os dados antes de
545
+ assinar. Um signatário `DigitalCertificate` não pode usar `sign`; esse ramo passa pelo fluxo de
546
+ certificado da Assinafy, descrito em
547
+ [Certificado digital ICP-Brasil](#certificado-digital-icp-brasil).
386
548
 
387
- ### 5. Observe completion and download artifacts
549
+ ### 5. Acompanhe a conclusão e baixe os artefatos
388
550
 
389
- Subscribe to `document_ready` for event-driven completion, or fetch
390
- `documents.details(documentId)` until `status === 'certificated'`. Webhook
391
- deliveries can repeat, so use their numeric `id` as an
392
- idempotency key. Once complete:
551
+ Assine o evento `document_ready` para saber da conclusão por evento, ou consulte
552
+ `documents.details(documentId)` até `status === 'certificated'`. As entregas de webhook podem se
553
+ repetir use o `id` numérico como chave de idempotência. Concluído:
393
554
 
394
555
  ```ts
395
- const finalDocument = await client.documents.details(uploaded.id);
396
- const signedPdf = await client.documents.download(uploaded.id, 'certificated');
397
- const certificatePage = await client.documents.download(uploaded.id, 'certificate-page');
398
- const bundleZip = await client.documents.download(uploaded.id, 'bundle');
556
+ const documentoFinal = await client.documents.details(enviado.id);
557
+ const pdfAssinado = await client.documents.download(enviado.id, 'certificated');
558
+ const paginaCertificado = await client.documents.download(enviado.id, 'certificate-page');
559
+ const pacoteZip = await client.documents.download(enviado.id, 'bundle');
399
560
 
400
- // Validate an Assinafy signature hash when your workflow has extracted it.
401
- const validation = await client.documents.verify(documentSignatureHash);
561
+ // Valide um hash de assinatura Assinafy que o seu fluxo tenha extraído.
562
+ const validacao = await client.documents.verify(documentSignatureHash);
402
563
  ```
403
564
 
404
- `original`, `certificated`, and `certificate-page` are PDFs. `bundle` is a ZIP
405
- containing those three artifacts and also `pades` when the document had an
406
- ICP-Brasil certificate signer. The `pades` PDF exists only for documents that
407
- had certificate signers. An artifact can return `404` before generation has
408
- finished. `decline_reason` is included in document details only when the access
409
- token belongs to the document creator.
565
+ `original`, `certificated` e `certificate-page` são PDFs. `bundle` é um ZIP com esses três artefatos
566
+ e também o `pades` quando o documento teve signatário por certificado ICP-Brasil. O PDF `pades`
567
+ existe apenas nesses documentos. Um artefato pode devolver `404` antes de terminar de ser gerado.
568
+ `decline_reason` aparece nos detalhes do documento quando o token pertence a quem criou o
569
+ documento.
410
570
 
411
- ## Resource reference
571
+ ## Referência de recursos
412
572
 
413
- Most account-scoped methods accept an optional `accountId` that overrides the
414
- client default. Workspace `get`, `update`, `delete`, branding, and statistics
415
- methods always require an explicit account ID.
573
+ A maioria dos métodos com escopo de conta aceita um `accountId` opcional que sobrepõe o padrão do
574
+ cliente. Os métodos de workspace `get`, `update`, `delete`, identidade visual e estatísticas sempre
575
+ exigem um ID de conta explícito.
416
576
 
417
- ### Documents
577
+ ### Documentos
418
578
 
419
579
  ```ts
420
- // Upload from a file path (recommended)
580
+ // Envio a partir de um caminho de arquivo (recomendado)
421
581
  const doc = await client.documents.upload(
422
- { filePath: './contract.pdf' },
423
- { name: 'Service agreement', metadata: { type: 'service' } },
582
+ { filePath: './contrato.pdf' },
583
+ { name: 'Contrato de prestação', metadata: { tipo: 'servico' } },
424
584
  );
425
- // `name` and `metadata` are compatibility multipart parts outside the published
426
- // file-only request schema.
427
- // `name` is optional and defaults to the file's own name. The API derives the
428
- // display name from the uploaded filename and appends `.pdf` when absent, so
429
- // the document above is stored as 'Service agreement.pdf'. Accents are
430
- // transliterated by the API ('Contrato de Serviço' → 'Contrato de Servico.pdf').
585
+ // `name` e `metadata` são partes multipart de compatibilidade, fora do schema
586
+ // oficial (que só define o arquivo). `name` é opcional e assume o nome do
587
+ // arquivo. A API deriva o nome de exibição do arquivo enviado e acrescenta
588
+ // `.pdf` quando falta, então o documento acima é salvo como
589
+ // 'Contrato de prestação.pdf'. A API translitera acentos
590
+ // ('Contrato de Serviço' → 'Contrato de Servico.pdf').
431
591
  // → {
432
592
  // resource: 'document', id: '1031…', account_id: '102d…', template_id: null,
433
- // name: 'Service agreement.pdf', status: 'uploaded',
593
+ // name: 'Contrato de prestacao.pdf', status: 'uploaded',
434
594
  // artifacts: { original: 'https://…/download/original' },
435
595
  // signing_url: 'https://app…/sign/1031…',
436
- // pages: [], // populated once status reaches `metadata_ready`
596
+ // pages: [], // preenchido quando o status chega a `metadata_ready`
437
597
  // tags: [], is_closed: false, created_at: '2026-…', updated_at: '2026-…'
438
598
  // }
439
599
 
440
- // …or from a Buffer already in memory
441
- await client.documents.upload({ buffer, fileName: 'contract.pdf' });
600
+ // …ou a partir de um Buffer em memória
601
+ await client.documents.upload({ buffer, fileName: 'contrato.pdf' });
442
602
 
443
- // List → { data: IDocumentListItem[], meta?: { current_page, per_page, total, last_page } }
444
- const { data, meta } = await client.documents.list({ page: 1, per_page: 20, sort: 'updated_at' });
603
+ // Listagem → { data: IDocumentListItem[], meta?: { current_page, per_page, total, last_page } }
604
+ const { data, meta } = await client.documents.list({ page: 1, 'per-page': 20, sort: 'updated_at' });
445
605
 
446
- // Search is the lightweight alternative to list: same item shape, but the API
447
- // skips the expanded `assignment`/`pages`. Prefer it for name lookups.
448
- const hits = await client.documents.search({ search: 'agreement', status: 'pending_signature', 'per-page': 20 });
606
+ // `search` é a alternativa leve a `list`: mesmo formato de item, mas a API não
607
+ // expande `assignment`/`pages`. Prefira-a para buscar por nome.
608
+ const achados = await client.documents.search({ search: 'contrato', status: 'pending_signature', 'per-page': 20 });
449
609
 
450
610
  await client.documents.details(doc.id);
451
611
  await client.documents.activities(doc.id);
452
612
  await client.documents.waitUntilReady(doc.id, { maxWaitMs: 30_000 });
453
613
 
454
- // Rename. The API rejects this with 400 while the document is still in
455
- // `metadata_processing`, so await waitUntilReady() first on a fresh upload.
456
- // (Passing `name` to upload() avoids both the round-trip and the race.)
457
- await client.documents.rename(doc.id, 'Signed service agreement.pdf');
614
+ // Renomear. A API responde 400 enquanto o documento está em
615
+ // `metadata_processing`, então chame waitUntilReady() antes em um upload novo.
616
+ // (Passar `name` ao upload() evita tanto a ida extra quanto a corrida.)
617
+ await client.documents.rename(doc.id, 'Contrato assinado.pdf');
458
618
 
459
- await client.documents.download(doc.id, 'certificated'); // signed PDF
619
+ await client.documents.download(doc.id, 'certificated'); // PDF assinado
460
620
  await client.documents.download(doc.id, 'certificate-page');
461
621
  await client.documents.download(doc.id, 'bundle'); // ZIP
462
- // `pades` exists only when at least one signer used DigitalCertificate.
622
+ // `pades` existe apenas quando algum signatário usou DigitalCertificate.
463
623
  await client.documents.download(doc.id, 'pades');
464
624
  await client.documents.thumbnail(doc.id);
465
625
  await client.documents.downloadPage(doc.id, pageId);
466
626
 
467
- await client.documents.statuses(); // list every status code + deletable flag
627
+ await client.documents.statuses(); // todos os códigos de status + flag deletable
468
628
  await client.documents.isFullySigned(doc.id);
469
629
  await client.documents.getSigningProgress(doc.id);
470
630
  await client.documents.delete(doc.id);
471
631
 
472
- // Verify a signed document by its Assinafy signature hash
632
+ // Verifique um documento assinado pelo hash de assinatura Assinafy
473
633
  await client.documents.verify('FE32EDDADE7CBDDCBB934E7402047450B0E59C02');
474
634
 
475
- // Public endpoints (no auth)
635
+ // Endpoints públicos (sem autenticação)
476
636
  await client.documents.getPublic(doc.id);
477
- // Official request body: { email: 'jane@example.com' }
478
- await client.documents.sendToken(doc.id, 'jane@example.com');
637
+ // Corpo oficial: { email: 'maria@exemplo.com.br' }
638
+ await client.documents.sendToken(doc.id, 'maria@exemplo.com.br');
479
639
 
480
- // Explicit compatibility overload for older deployments:
640
+ // Sobrecarga explícita de compatibilidade para deploys mais antigos:
481
641
  // { recipient: '+5548999990000', channel: 'whatsapp' }
482
642
  await client.documents.sendToken(doc.id, '+5548999990000', 'whatsapp');
483
643
 
484
- // The current OpenAPI contract requires existing tag IDs.
485
- const contractsTag = await client.tags.create({ name: 'Contracts' });
486
- const quarterTag = await client.tags.create({ name: '2026-Q1' });
487
- const urgentTag = await client.tags.create({ name: 'Urgent' });
644
+ // O contrato OpenAPI atual exige IDs de tags já existentes.
645
+ const tagContratos = await client.tags.create({ name: 'Contratos' });
646
+ const tagTrimestre = await client.tags.create({ name: '2026-T1' });
647
+ const tagUrgente = await client.tags.create({ name: 'Urgente' });
488
648
  await client.documents.listTags(doc.id);
489
- await client.documents.replaceTags(doc.id, [contractsTag.id, quarterTag.id]); // [] detaches all
490
- await client.documents.addTags(doc.id, [urgentTag.id]); // append
491
- await client.documents.detachTag(doc.id, urgentTag.id); // → { detached: true }
649
+ await client.documents.replaceTags(doc.id, [tagContratos.id, tagTrimestre.id]); // [] desanexa tudo
650
+ await client.documents.addTags(doc.id, [tagUrgente.id]); // acrescenta
651
+ await client.documents.detachTag(doc.id, tagUrgente.id); // → { detached: true }
492
652
  ```
493
653
 
494
- Uploads are validated locally: only `.pdf` files up to 25 MB whose bytes begin
495
- with the PDF magic header (`%PDF-`) are accepted. The API also limits documents
496
- to 2,000 pages.
654
+ Os uploads são validados localmente: arquivos `.pdf` de até 25 MB cujos bytes começam com o
655
+ cabeçalho mágico (`%PDF-`) são aceitos. A API também limita o documento a 2.000 páginas.
497
656
 
498
- Page and artifact URLs embedded in JSON responses still require the same
499
- account authentication as their download operations. Prefer
500
- `documents.downloadPage()` and `documents.download()` so the SDK applies the
501
- credential and returns a `Buffer`. `bundle` contains `original`, `certificated`,
502
- and `certificate-page`, plus `pades` when available.
657
+ As URLs de página e de artefato embutidas nas respostas JSON continuam exigindo a mesma autenticação
658
+ de conta das operações de download. Prefira `documents.downloadPage()` e `documents.download()`,
659
+ que aplicam a credencial e devolvem um `Buffer`. `bundle` contém `original`, `certificated` e
660
+ `certificate-page`, mais o `pades` quando existir.
503
661
 
504
- List endpoints return `{ data, meta }` where `meta` is populated from the `X-Pagination-*` headers returned by the API.
662
+ As listagens devolvem `{ data, meta }`, com `meta` preenchido a partir dos headers
663
+ `X-Pagination-*`. Dois comportamentos da API merecem atenção porque são silenciosos, não erros:
505
664
 
506
- ### Signers
665
+ - Só o `per-page` com hífen é lido. `per_page` é aceito e ignorado, caindo para 20 linhas — por isso
666
+ o SDK reescreve `per_page` como `per-page` em toda listagem, e um `per-page` explícito vence
667
+ quando os dois são enviados.
668
+ - `per-page` é limitado a **50**. Pedir 100 devolve 50 linhas com `200`. A constante exportada
669
+ `MAX_LIST_PAGE_SIZE` é esse teto; pagine com `page` em vez de pedir páginas maiores, e confie no
670
+ `meta.per_page` mais do que no valor pedido.
671
+
672
+ ### Signatários
507
673
 
508
674
  ```ts
509
675
  await client.signers.create({
510
- full_name: 'John Doe',
511
- email: 'john@example.com',
512
- cpf: '123.456.789-00', // legacy compatibility input; non-digits are stripped
676
+ full_name: 'João Silva',
677
+ email: 'joao@exemplo.com.br',
678
+ cpf: '123.456.789-00', // entrada de compatibilidade; não dígitos são removidos
513
679
  });
514
- // → { id: '19e6…', full_name: 'John Doe', email: 'john@example.com',
680
+ // → { id: '19e6…', full_name: 'João Silva', email: 'joao@exemplo.com.br',
515
681
  // whatsapp_phone_number: null, has_accepted_terms: false }
516
- // (note: `cpf` is accepted on input but never echoed back by the API)
517
-
518
- // Both contacts are optional. A name-only signer cannot be notified until a
519
- // contact is added.
520
- await client.signers.create({ full_name: 'Contact Pending' });
682
+ // (observação: `cpf` é aceito na entrada, mas a API nunca o devolve)
521
683
 
522
- await client.signers.create({
523
- full_name: 'Jane Doe',
524
- email: 'jane@example.com',
525
- });
684
+ // Ambos os contatos são opcionais. Um signatário só com nome não pode ser
685
+ // notificado até que um contato seja adicionado.
686
+ await client.signers.create({ full_name: 'Contato Pendente' });
526
687
 
527
688
  await client.signers.get(signerId);
528
- await client.signers.list({ page: 1, per_page: 50, search: 'john' });
689
+ await client.signers.list({ page: 1, 'per-page': 50, search: 'joao' });
529
690
  await client.signers.update(signerId, {
530
- full_name: 'Johnny Doe',
531
- government_id: '390.533.447-05', // official update field; sent as digits
691
+ full_name: 'João da Silva',
692
+ government_id: '390.533.447-05', // campo oficial de atualização; enviado com dígitos
532
693
  });
533
694
  await client.signers.delete(signerId);
534
695
 
535
- const existing = await client.signers.findByEmail('john@example.com');
696
+ const existente = await client.signers.findByEmail('joao@exemplo.com.br');
536
697
  ```
537
698
 
538
- When an `email` is supplied, `signers.create()` is idempotent by email: it
539
- reuses an existing signer when the same email is already present in the
540
- workspace. Signers without email are always created fresh. See
541
- [Paid signing branches](#paid-signing-branches) for phone-only signers.
699
+ Quando um `email` é informado, `signers.create()` é idempotente por e-mail: reaproveita o signatário
700
+ existente com o mesmo endereço no workspace. Signatários sem e-mail são sempre criados do zero. Veja
701
+ [Ramos pagos de assinatura](#ramos-pagos-de-assinatura) para signatários com telefone.
542
702
 
543
703
  ### Assignments
544
704
 
545
705
  ```ts
546
- // List every assignment in the workspace.
706
+ // Lista todos os assignments do workspace.
547
707
  // → { data: IAssignment[], meta?: { current_page, per_page, total, last_page } }
548
708
  const { data, meta } = await client.assignments.list({ page: 1, 'per-page': 20 });
549
709
 
550
- // Signers may be ids or objectsthe SDK normalises to the API shape.
710
+ // Signatários podem ser ids ou objetoso SDK normaliza para o formato da API.
551
711
  await client.assignments.create(documentId, {
552
712
  method: 'virtual',
553
713
  signers: ['signer-1', 'signer-2'],
554
- message: 'Please review and sign',
714
+ message: 'Por favor, revise e assine',
555
715
  expires_at: '2027-12-31T23:59:00Z',
556
- copy_receivers: ['copy-recipient-signer-id'],
716
+ copy_receivers: ['id-do-signatario-em-copia'],
557
717
  });
558
718
 
559
- // Sequential signing: `step` controls signing order (parallel within a step).
719
+ // Assinatura sequencial: `step` controla a ordem (paralelo dentro de um passo).
560
720
  await client.assignments.create(documentId, {
561
721
  method: 'virtual',
562
722
  signers: [
563
723
  { id: 'signer-1', step: 1 },
564
- { id: 'signer-2', step: 2 }, // notified only after step 1 finishes
724
+ { id: 'signer-2', step: 2 }, // notificado depois que o passo 1 terminar
565
725
  ],
566
726
  });
567
727
 
568
- // Collect fields use 150-DPI page-image pixels measured from the upper-left.
728
+ // Campos de `collect` usam pixels da imagem de página a 150 DPI, medidos do canto superior esquerdo.
569
729
  await client.assignments.create(documentId, {
570
730
  method: 'collect',
571
731
  signers: [{ id: signerId }],
@@ -582,8 +742,8 @@ await client.assignments.create(documentId, {
582
742
  }],
583
743
  });
584
744
 
585
- // Estimate cost (the endpoint prices channel descriptors, not signer IDs) → ICostEstimate
586
- await client.assignments.estimateCost(documentId, { signers: [{}] }); // default Email
745
+ // Estimativa de custo (o endpoint orça descritores de canal, não IDs) → ICostEstimate
746
+ await client.assignments.estimateCost(documentId, { signers: [{}] }); // Email padrão
587
747
  // → {
588
748
  // documents: 1, credits: 0, needs_extra_document: false, extra_document_cost: 0,
589
749
  // total_credits: 0, breakdown: [], document_balance: 67, credit_balance: 0,
@@ -591,88 +751,108 @@ await client.assignments.estimateCost(documentId, { signers: [{}] }); // default
591
751
  // }
592
752
 
593
753
  await client.assignments.resetExpiration(documentId, assignmentId, '2027-06-30T00:00:00Z');
594
- // Compatibility only: the published request requires a date-time string.
595
- // Confirm target support before using `null` to clear an expiration.
754
+ // compatibilidade: a requisição publicada exige uma data-hora.
755
+ // Confirme o suporte do destino antes de usar `null` para limpar a expiração.
596
756
  await client.assignments.resetExpiration(documentId, assignmentId, null);
597
757
 
598
758
  await client.assignments.resendNotification(documentId, assignmentId, signerId);
599
759
  // → { is_sent: true, document_id: '…', signer_id: '…' }
600
760
 
601
- const resendCost = await client.assignments.estimateResendCost(documentId, assignmentId, signerId);
602
- // Official response: ICostEstimate. Older deployments can return the compact
603
- // IResendCostEstimate branch with `total` and `has_sufficient_credits`; narrow
604
- // with `'total_credits' in resendCost` before reading branch-specific fields.
761
+ const custoReenvio = await client.assignments.estimateResendCost(documentId, assignmentId, signerId);
762
+ // Resposta oficial: ICostEstimate. Deploys antigos podem devolver o ramo compacto
763
+ // IResendCostEstimate, com `total` e `has_sufficient_credits`; estreite com
764
+ // `'total_credits' in custoReenvio` antes de ler campos específicos de um ramo.
605
765
  ```
606
766
 
607
- The `create` response is an `IAssignment`: `{ id, method, signers: [...],
767
+ A resposta de `create` é um `IAssignment`: `{ id, method, signers: [...],
608
768
  items: [{ display_settings, ... }], signing_urls: [{ signer_id, url }], … }`.
609
769
 
610
- For backwards compatibility, the SDK also accepts legacy `signer_ids` and `signerIds` payloads and rewrites them to the current `signers: [{ id }]` format expected by the API.
770
+ Por compatibilidade, o SDK também aceita os payloads antigos `signer_ids` e `signerIds` e os
771
+ reescreve no formato atual `signers: [{ id }]` esperado pela API.
611
772
 
612
- **Cancelling a signature request.** Assinafy has no workspace-side "cancel" endpoint. To stop a pending request either delete the document (when its status is deletable) or have the signer decline:
773
+ **Cancelar um pedido de assinatura.** A Assinafy não tem endpoint de cancelamento do lado do
774
+ workspace. Para interromper um pedido pendente, apague o documento (quando o status permitir) ou
775
+ peça que o signatário recuse:
613
776
 
614
777
  ```ts
615
- await client.documents.delete(documentId); // workspace-side
616
- await client.signerDocuments.decline(documentId, assignmentId, accessCode, 'No longer needed'); // signer-side
778
+ await client.documents.delete(documentId); // lado do workspace
779
+ await client.signerDocuments.decline(documentId, assignmentId, accessCode, 'Não é mais necessário'); // lado do signatário
617
780
  ```
618
781
 
619
- ### Paid signing branches
782
+ ### Ramos pagos de assinatura
620
783
 
621
- Keep the email flow as the default. Enable either branch below only after the
622
- workspace has the required plan or feature and the returned cost estimate is
623
- acceptable.
784
+ Mantenha o fluxo por e-mail como padrão. Habilite os ramos abaixo apenas depois de confirmar que a
785
+ conta tem o plano ou recurso necessário e que a estimativa de custo é aceitável.
624
786
 
625
- #### WhatsApp verification and notification
787
+ O método de verificação e o de notificação são **acoplados**: envie um, os dois ou nenhum — o lado
788
+ que faltar é inferido. Sem nenhum dos dois, ambos assumem `Email`.
626
789
 
627
- WhatsApp is available only on paid subscriptions and costs 0.45 credit per
628
- notification. Create a phone-only signer or add a phone to an existing signer,
629
- then request the `Whatsapp` channel explicitly:
790
+ | Verificação | Como o signatário prova quem é | Notificação permitida | Custo por signatário |
791
+ | --- | --- | --- | --- |
792
+ | `Email` *(padrão)* | Código de uso único (OTP) por e-mail | `Email` | Gratuito |
793
+ | `Whatsapp` | Código de uso único (OTP) por WhatsApp | `Whatsapp` | 0,45 crédito (a notificação), só em planos pagos |
794
+ | `DigitalCertificate` | O signatário assina com o **próprio certificado ICP-Brasil — A1 ou A3 —** pela extensão Web PKI, gerando uma assinatura **PAdES qualificada** | `Email` **ou** `Whatsapp` | 2 créditos + a notificação |
795
+
796
+ Apenas um método de notificação por signatário. O SDK recusa uma combinação inválida antes da
797
+ requisição; a API responderia `400`.
798
+
799
+ #### Verificação e notificação por WhatsApp
800
+
801
+ Disponível apenas em assinaturas pagas, a 0,45 crédito por notificação. Crie um signatário só com
802
+ telefone (ou adicione telefone a um existente) e peça o canal `Whatsapp` explicitamente:
630
803
 
631
804
  ```ts
632
- const phoneSigner = await client.signers.create({
633
- full_name: 'Mobile Signer',
805
+ const signatarioTelefone = await client.signers.create({
806
+ full_name: 'Signatário Mobile',
634
807
  whatsapp_phone_number: '+5511999990000',
635
808
  });
636
809
 
637
- const whatsappCost = await client.assignments.estimateCost(documentId, {
810
+ const custoWhatsapp = await client.assignments.estimateCost(documentId, {
638
811
  method: 'virtual',
639
812
  signers: [{ verification_method: 'Whatsapp', notification_methods: ['Whatsapp'] }],
640
813
  });
641
814
 
642
- const whatsappAssignment = await client.assignments.create(documentId, {
815
+ const assignmentWhatsapp = await client.assignments.create(documentId, {
643
816
  method: 'virtual',
644
817
  signers: [{
645
- id: phoneSigner.id,
818
+ id: signatarioTelefone.id,
646
819
  verification_method: 'Whatsapp',
647
820
  notification_methods: ['Whatsapp'],
648
821
  }],
649
822
  });
650
823
 
651
- const notices = await client.assignments.listWhatsAppNotifications(
824
+ const avisos = await client.assignments.listWhatsAppNotifications(
652
825
  documentId,
653
- whatsappAssignment.id,
826
+ assignmentWhatsapp.id,
654
827
  );
655
828
  // IWhatsAppNotification[]:
656
829
  // [{ sent_at, header, body, buttons: [{ text, url? }], phone_number, signer_id }]
657
830
  ```
658
831
 
659
- The high-level helper selects this paid branch for a signer that has a phone
660
- number but no email. Button URLs can contain signer credentials; do not log or
661
- forward them outside the signing flow.
832
+ O helper de alto nível escolhe esse ramo pago automaticamente para um signatário que tenha telefone
833
+ e não tenha e-mail. As URLs dos botões podem conter credenciais do signatário — não registre em log
834
+ nem encaminhe fora do fluxo de assinatura.
662
835
 
663
- #### ICP-Brasil digital certificate
836
+ #### Certificado digital ICP-Brasil
664
837
 
665
- `DigitalCertificate` requires the account feature, a CPF or CNPJ in the
666
- signer's `government_id`, and exactly one certificate signer in that signing
667
- step. It costs two credits per certificate signer in addition to the selected
668
- notification cost.
838
+ `DigitalCertificate` exige o recurso **Certificado Digital** na conta (planos Standard e Pro), CPF ou
839
+ CNPJ em `government_id` do signatário, e exatamente **um signatário por certificado naquele passo de
840
+ assinatura**. Custa 2 créditos por signatário, além do custo da notificação escolhida.
841
+
842
+ **A1 e A3 são mídias de certificado**, escolhidas pelo próprio signatário no navegador na hora de
843
+ assinar: A1 fica em software (um arquivo na máquina) e A3 em hardware (token ou cartão). A API
844
+ modela as duas com o único valor `DigitalCertificate` — não existe campo `A1`/`A3` a enviar, e a
845
+ assinatura PAdES resultante é qualificada nos dois casos.
846
+
847
+ Um CPF exige o certificado daquela pessoa (e-CPF, ou e-CNPJ que a nomeie como representante legal);
848
+ um CNPJ exige um e-CNPJ da empresa, de qualquer um dos seus representantes.
669
849
 
670
850
  ```ts
671
- const certificateSigner = await client.signers.update(signerId, {
851
+ const signatarioCertificado = await client.signers.update(signerId, {
672
852
  government_id: '390.533.447-05',
673
853
  });
674
854
 
675
- const certificateCost = await client.assignments.estimateCost(documentId, {
855
+ const custoCertificado = await client.assignments.estimateCost(documentId, {
676
856
  method: 'virtual',
677
857
  signers: [{ verification_method: 'DigitalCertificate', notification_methods: ['Email'] }],
678
858
  });
@@ -680,7 +860,7 @@ const certificateCost = await client.assignments.estimateCost(documentId, {
680
860
  await client.assignments.create(documentId, {
681
861
  method: 'virtual',
682
862
  signers: [{
683
- id: certificateSigner.id,
863
+ id: signatarioCertificado.id,
684
864
  step: 1,
685
865
  verification_method: 'DigitalCertificate',
686
866
  notification_methods: ['Email'],
@@ -688,27 +868,37 @@ await client.assignments.create(documentId, {
688
868
  });
689
869
  ```
690
870
 
691
- Before opening the assignment, the signer must confirm identity data and accept
692
- terms with `confirmData(..., { has_accepted_terms: true })` or `acceptTerms()`.
693
- The regular `sign()` endpoint rejects certificate signers; they complete the
694
- ICP-Brasil flow through Assinafy's browser integration. After completion,
695
- `documents.download(documentId, 'pades')` returns the qualified PAdES artifact.
871
+ Antes de abrir o assignment, o signatário precisa confirmar os dados de identidade e aceitar os
872
+ termos, com `confirmData(..., { has_accepted_terms: true })` ou `acceptTerms()`.
873
+
874
+ O endpoint comum `sign()` **rejeita** signatários por certificado: a assinatura deles é produzida por
875
+ um handshake de dois passos com a extensão Web PKI, pela integração de navegador da Assinafy.
876
+
877
+ ```
878
+ POST /v1/signers/certificate/start → data.token (token da operação Web PKI)
879
+ ↓ o navegador assina o token com o certificado do signatário
880
+ POST /v1/signers/certificate/complete → data.signerName
881
+ ```
882
+
883
+ > Essas duas rotas são extensões implantadas **somente em produção**: o sandbox não as expõe e elas
884
+ > não constam do documento OpenAPI publicado.
885
+
886
+ Concluído o fluxo, `documents.download(documentId, 'pades')` devolve o artefato PAdES qualificado.
696
887
 
697
888
  ### Templates
698
889
 
699
- `templates.list()` is part of the current OpenAPI document. Existing
700
- integrations can also use five template-management routes—`create`, `get`,
701
- `update`, `delete`, and `downloadPage`—that are absent from that document. See
702
- [compatibility notes](docs/COMPATIBILITY.md#template-management-extensions).
703
- Template status casing can vary by deployment; normalize with
704
- `template.status.toLowerCase()` when branching on it.
890
+ `templates.list()` faz parte do documento OpenAPI atual. Integrações existentes também podem usar
891
+ cinco rotas de gestão de templates — `create`, `get`, `update`, `delete` e `downloadPage` — ausentes
892
+ daquele documento; veja as
893
+ [notas de compatibilidade](docs/COMPATIBILITY.md#template-management-extensions). A caixa do status
894
+ de template varia por deploy; normalize com `template.status.toLowerCase()` antes de comparar.
705
895
 
706
896
  ```ts
707
- // Create a template by uploading a PDF (multipart). The template starts in
708
- // an uploaded state and becomes ready once its pages are processed.
709
- const created = await client.templates.create(
710
- { filePath: './nda.pdf' }, // or { buffer, fileName: 'nda.pdf' }
711
- { name: 'NDA template' },
897
+ // Cria um template enviando um PDF (multipart). O template começa enviado e
898
+ // fica pronto quando suas páginas são processadas.
899
+ const criado = await client.templates.create(
900
+ { filePath: './nda.pdf' }, // ou { buffer, fileName: 'nda.pdf' }
901
+ { name: 'Template de NDA' },
712
902
  );
713
903
  // →
714
904
  // {
@@ -718,28 +908,28 @@ const created = await client.templates.create(
718
908
  // pages: [], tags: [], created_at: '2026-…', updated_at: '2026-…'
719
909
  // }
720
910
 
721
- const { data, meta } = await client.templates.list({ search: 'NDA', per_page: 20 });
722
- const template = await client.templates.get(created.id); // includes pages[] + default_document_tags
723
- await client.templates.update(created.id, { name: 'NDA v2', message: 'Please sign' });
724
- const firstPage = template.pages?.[0];
725
- if (firstPage) await client.templates.downloadPage(created.id, firstPage.id); // → Buffer (JPEG)
726
- await client.templates.delete(created.id);
727
-
728
- // Create a document from an existing, configured template. Fresh uploads have
729
- // only an Editor role; add signer roles in Assinafy's editor first.
730
- const configured = await client.templates.get(templateId);
731
- const signerRole = configured.roles?.find(
732
- (role) => typeof role.assignment_type === 'string'
733
- && role.assignment_type.toLowerCase() !== 'editor',
911
+ const { data, meta } = await client.templates.list({ search: 'NDA', 'per-page': 20 });
912
+ const template = await client.templates.get(criado.id); // inclui pages[] + default_document_tags
913
+ await client.templates.update(criado.id, { name: 'NDA v2', message: 'Por favor, assine' });
914
+ const primeiraPagina = template.pages?.[0];
915
+ if (primeiraPagina) await client.templates.downloadPage(criado.id, primeiraPagina.id); // → Buffer (JPEG)
916
+ await client.templates.delete(criado.id);
917
+
918
+ // Cria um documento a partir de um template configurado. Uploads novos têm
919
+ // apenas o papel Editor; adicione papéis de signatário no editor da Assinafy antes.
920
+ const configurado = await client.templates.get(templateId);
921
+ const papelSignatario = configurado.roles?.find(
922
+ (papel) => typeof papel.assignment_type === 'string'
923
+ && papel.assignment_type.toLowerCase() !== 'editor',
734
924
  );
735
- if (!signerRole) throw new Error('Template has no signer role');
925
+ if (!papelSignatario) throw new Error('O template não tem papel de signatário');
736
926
  await client.documents.createFromTemplate(
737
927
  templateId,
738
- [{ role_id: signerRole.id, id: signerId, verification_method: 'Email', notification_methods: ['Email'] }],
739
- { name: 'NDA - John Doe', message: 'Please sign at your earliest convenience.' },
928
+ [{ role_id: papelSignatario.id, id: signerId, verification_method: 'Email', notification_methods: ['Email'] }],
929
+ { name: 'NDA - João Silva', message: 'Por favor, assine assim que puder.' },
740
930
  );
741
931
 
742
- // Estimate the cost before creating → ICostEstimate
932
+ // Estime o custo antes de criar → ICostEstimate
743
933
  await client.documents.estimateCostFromTemplate(templateId, [
744
934
  { role_id: 'role_id', verification_method: 'Email', notification_methods: ['Email'] },
745
935
  ]);
@@ -747,41 +937,42 @@ await client.documents.estimateCostFromTemplate(templateId, [
747
937
  // has_sufficient_resources: true, blocking_reason: null, breakdown: [], … }
748
938
  ```
749
939
 
750
- Template signer descriptors also accept
751
- `verification_method: 'DigitalCertificate'` with the same prerequisites under
752
- [ICP-Brasil digital certificate](#icp-brasil-digital-certificate).
940
+ Os descritores de signatário de template também aceitam
941
+ `verification_method: 'DigitalCertificate'`, com os mesmos pré-requisitos de
942
+ [Certificado digital ICP-Brasil](#certificado-digital-icp-brasil).
753
943
 
754
- Template creation only uploads the PDF and provisions the default editor role
755
- configure roles/fields in the Assinafy editor (or the web UI) afterwards.
756
- The `download_url` values in template page objects are protected URLs; prefer
757
- `templates.downloadPage()` so the API credential is attached.
944
+ Criar um template apenas envia o PDF e provisiona o papel de editor padrão configure papéis e
945
+ campos no editor da Assinafy depois. Os `download_url` dos objetos de página do template são URLs
946
+ protegidas; prefira `templates.downloadPage()`, que anexa a credencial da API.
758
947
 
759
948
  ### Tags
760
949
 
761
- Workspace-scoped labels that can be attached to documents and templates. Tag names are unique per workspace (case-insensitive).
950
+ Rótulos com escopo de workspace que podem ser anexados a documentos e templates. Os nomes são únicos
951
+ por workspace (sem diferenciar maiúsculas).
762
952
 
763
953
  ```ts
764
- await client.tags.list({ search: 'contract' }); // ITag[]
765
- const tag = await client.tags.create({ name: 'Contracts', color: 'ff8800' });
766
- await client.tags.update(tag.id, { name: 'Sales Contracts' });
767
- await client.tags.update(tag.id, { color: null }); // clear the color
768
- await client.tags.delete(tag.id); // 409 if still attached
769
- await client.tags.delete(tag.id, { force: true }); // detach everywhere, then delete
954
+ await client.tags.list({ search: 'contrato' }); // ITag[]
955
+ const tag = await client.tags.create({ name: 'Contratos', color: 'ff8800' });
956
+ await client.tags.update(tag.id, { name: 'Contratos Comerciais' });
957
+ await client.tags.update(tag.id, { color: null }); // limpa a cor
958
+ await client.tags.delete(tag.id); // 409 se ainda estiver anexada
959
+ await client.tags.delete(tag.id, { force: true }); // desanexa de tudo e apaga
770
960
  ```
771
961
 
772
- Attach/detach tags on a specific document via `client.documents.listTags / replaceTags / addTags / detachTag` (see [Documents](#documents)).
962
+ Anexe/desanexe tags em um documento específico com `client.documents.listTags / replaceTags /
963
+ addTags / detachTag` (veja [Documentos](#documentos)).
773
964
 
774
965
  ### Workspaces
775
966
 
776
- The official create/update request schemas define `name` and
777
- `notification_sender_type`. The sandbox also accepts the color fields shown
778
- below; they are retained as a documented compatibility extension.
967
+ Os schemas oficiais de criação/atualização definem `name` e `notification_sender_type`. O sandbox
968
+ também aceita os campos de cor abaixo; eles ficam mantidos como extensão de compatibilidade
969
+ documentada.
779
970
 
780
971
  ```ts
781
- // Colours are 6-char hex WITHOUT a leading '#' (unlike tags, which strip it).
782
- // '#ff0066' is rejectedthe account endpoints want exactly 6 characters.
972
+ // As cores são hex de 6 caracteres SEM '#' inicial (ao contrário das tags, que o removem).
973
+ // '#ff0066' é rejeitadoos endpoints de conta querem exatamente 6 caracteres.
783
974
  await client.workspaces.create({
784
- name: 'My Workspace',
975
+ name: 'Meu Workspace',
785
976
  notification_sender_type: 'Account',
786
977
  primary_color: 'ff0066',
787
978
  secondary_color: '0066ff',
@@ -790,13 +981,13 @@ await client.workspaces.create({
790
981
  await client.workspaces.list();
791
982
  await client.workspaces.get(accountId);
792
983
  await client.workspaces.update(accountId, {
793
- name: 'Renamed',
984
+ name: 'Renomeado',
794
985
  notification_sender_type: 'User',
795
986
  primary_color: '112233',
796
987
  });
797
988
 
798
- // Branding
799
- const theme = await client.workspaces.getTheme(accountId);
989
+ // Identidade visual
990
+ const tema = await client.workspaces.getTheme(accountId);
800
991
  const logo = await client.workspaces.downloadLogo(accountId); // Buffer
801
992
  await client.workspaces.uploadLogo(accountId, { filePath: './logo.png' });
802
993
  await client.workspaces.uploadLogo(accountId, {
@@ -806,7 +997,7 @@ await client.workspaces.uploadLogo(accountId, {
806
997
  });
807
998
  await client.workspaces.deleteLogo(accountId);
808
999
 
809
- // Latest 12 months by default; daily statistics require a YYYY-MM month.
1000
+ // Últimos 12 meses por padrão; estatísticas diárias exigem um mês YYYY-MM.
810
1001
  await client.workspaces.getStats(accountId);
811
1002
  await client.workspaces.getStats(accountId, {
812
1003
  granularity: 'daily',
@@ -814,97 +1005,100 @@ await client.workspaces.getStats(accountId, {
814
1005
  });
815
1006
 
816
1007
  await client.workspaces.delete(accountId);
817
- // `force` cancels an active paid subscription as part of account deletion. It
818
- // is not a general bypass for unrelated deletion restrictions.
819
- await client.workspaces.delete(restrictedAccountId, { force: true });
1008
+ // `force` cancela uma assinatura paga ativa como parte da exclusão da conta. Não
1009
+ // é um atalho geral para outras restrições de exclusão.
1010
+ await client.workspaces.delete(contaRestrita, { force: true });
820
1011
  ```
821
1012
 
822
- ### Field definitions
1013
+ ### Definições de campo
823
1014
 
824
- Custom field types used by `collect`-method assignments.
1015
+ Tipos de campo personalizados usados por assignments com método `collect`.
825
1016
 
826
1017
  ```ts
827
- await client.fields.create({ type: 'text', name: 'Contract Number' });
1018
+ await client.fields.create({ type: 'text', name: 'Número do contrato' });
828
1019
  await client.fields.list({ include_inactive: true, include_standard: true });
829
1020
  await client.fields.get(fieldId);
830
- await client.fields.update(fieldId, { name: 'Updated Name' });
1021
+ await client.fields.update(fieldId, { name: 'Nome atualizado' });
831
1022
  await client.fields.delete(fieldId);
832
1023
 
833
- // Validate a single value (signer-access-code only required for signer-side calls)
1024
+ // Valida um único valor (o código de acesso é exigido nas chamadas do signatário)
834
1025
  await client.fields.validate(fieldId, '400.676.228-36', { signerAccessCode });
835
1026
 
836
- // Validate multiple values at once
1027
+ // Valida vários valores de uma vez
837
1028
  await client.fields.validateMultiple(
838
1029
  [
839
1030
  { field_id: 'f1', value: '1111111111111' },
840
- { field_id: 'f2', value: 'value@example.com' },
1031
+ { field_id: 'f2', value: 'valor@exemplo.com.br' },
841
1032
  ],
842
1033
  { signerAccessCode },
843
1034
  );
844
1035
 
845
- // Catalog of every field type the platform recognises
1036
+ // Catálogo de todos os tipos de campo reconhecidos pela plataforma
846
1037
  await client.fields.listTypes();
847
1038
  ```
848
1039
 
849
- ### Authentication / API key management
1040
+ ### Autenticação / gestão de chaves de API
850
1041
 
851
- Most server-side integrations should just use `X-Api-Key` directly. Use these endpoints when you need to bootstrap a session for a human user.
1042
+ A maioria das integrações de servidor deve usar `X-Api-Key` diretamente. Use estes endpoints quando
1043
+ precisar abrir uma sessão para uma pessoa. Para apps conectados por terceiros, use
1044
+ [Aplicações OAuth](#aplicações-oauth).
852
1045
 
853
1046
  ```ts
854
- // Browser OAuth: redirect the user to this URL. The callback helper returns the
855
- // Assinafy callback URL for provider configuration; neither follows a redirect.
856
- const oauthStart = client.auth.getSocialLoginUrl('google');
857
- const oauthCallback = client.auth.getSocialLoginCallbackUrl();
1047
+ // OAuth de navegador (rota de compatibilidade): redirecione o usuário para esta URL.
1048
+ // O helper de callback devolve a URL de retorno da Assinafy para configurar o
1049
+ // provedor; nenhum dos dois segue o redirecionamento.
1050
+ const inicioOauth = client.auth.getSocialLoginUrl('google');
1051
+ const callbackOauth = client.auth.getSocialLoginCallbackUrl();
858
1052
 
859
- const { access_token, user, accounts } = await client.auth.login('me@example.com', 'pw');
1053
+ const { access_token, user, accounts } = await client.auth.login('eu@exemplo.com.br', 'senha');
860
1054
  await client.auth.socialLogin({ provider: 'google', token: 'google-id-token', has_accepted_terms: true });
861
1055
  await client.auth.linkSocialLogin({ provider: 'google', token: 'google-id-token' });
862
1056
 
863
- // Personal API key
864
- await client.auth.createApiKey('current-password');
865
- await client.auth.getApiKey(); // → { api_key: '****...nBNr' } or null
1057
+ // Chave de API pessoal
1058
+ await client.auth.createApiKey('senha-atual');
1059
+ await client.auth.getApiKey(); // → { api_key: '****...nBNr' } ou null
866
1060
  await client.auth.deleteApiKey();
867
1061
 
868
- // Password lifecycle
869
- await client.auth.changePassword({ email, password: 'current', new_password: 'next' });
870
- await client.auth.requestPasswordReset('me@example.com');
871
- await client.auth.resetPassword({ email, token: 'tk', new_password: 'next' });
1062
+ // Ciclo de vida da senha
1063
+ await client.auth.changePassword({ email, password: 'atual', new_password: 'nova' });
1064
+ await client.auth.requestPasswordReset('eu@exemplo.com.br');
1065
+ await client.auth.resetPassword({ email, token: 'tk', new_password: 'nova' });
872
1066
  ```
873
1067
 
874
- ### Authenticated user
1068
+ ### Usuário autenticado
875
1069
 
876
1070
  ```ts
877
- const user = await client.users.getCurrent();
1071
+ const usuario = await client.users.getCurrent();
878
1072
  // → { id, name, email, telephone, government_id, is_email_verified,
879
1073
  // has_accepted_terms, created_at, to_be_deleted_at }
880
1074
 
881
- // Cross-account document funnel, latest 12 monthly periods by default.
882
- const monthly = await client.users.getStats();
883
- const daily = await client.users.getStats({
1075
+ // Funil de documentos entre contas, últimos 12 períodos mensais por padrão.
1076
+ const mensal = await client.users.getStats();
1077
+ const diario = await client.users.getStats({
884
1078
  granularity: 'daily',
885
1079
  month: '2026-06',
886
1080
  });
887
- // Each row includes period, upload/send/certification totals, notification
888
- // counts for email/WhatsApp/bypass, verification counts for
889
- // email/WhatsApp/bypass/digital-certificate, viewed, and completed counts.
1081
+ // Cada linha traz período, totais de envio/remessa/certificação, contagens de
1082
+ // notificação por e-mail/WhatsApp/bypass, contagens de verificação por
1083
+ // e-mail/WhatsApp/bypass/certificado digital, visualizados e concluídos.
890
1084
 
891
- const preferences = await client.users.getNotificationPreferences();
1085
+ const preferencias = await client.users.getNotificationPreferences();
892
1086
  await client.users.updateNotificationPreferences({
893
1087
  SignerDeclined: false,
894
1088
  DocumentExpired: false,
895
1089
  });
896
- // Updates merge: omitted keys keep their current value. Both methods return
897
- // the complete nine-key notification preference map.
1090
+ // A atualização é um merge: chaves omitidas mantêm o valor atual. Os dois
1091
+ // métodos devolvem o mapa completo de nove preferências.
898
1092
  ```
899
1093
 
900
1094
  ### Webhooks
901
1095
 
902
1096
  ```ts
903
1097
  await client.webhooks.register({
904
- url: 'https://example.com/webhooks/assinafy',
905
- email: 'admin@example.com',
1098
+ url: 'https://exemplo.com.br/webhooks/assinafy',
1099
+ email: 'admin@exemplo.com.br',
906
1100
  is_active: true,
907
- // events defaults to the current SDK default set below
1101
+ // `events` assume o conjunto padrão do SDK, abaixo
908
1102
  events: [
909
1103
  'document_ready',
910
1104
  'document_prepared',
@@ -915,26 +1109,25 @@ await client.webhooks.register({
915
1109
  });
916
1110
 
917
1111
  await client.webhooks.get(); // IWebhookSubscription | null
918
- await client.webhooks.inactivate(); // stop deliveries (no delete route exists)
1112
+ await client.webhooks.inactivate(); // interrompe entregas (não existe rota de exclusão)
919
1113
  await client.webhooks.listEventTypes();
920
- const history = await client.webhooks.listDispatches({
1114
+ const historico = await client.webhooks.listDispatches({
921
1115
  delivered: false,
922
1116
  page: 1,
923
1117
  'per-page': 20,
924
1118
  }); // { data: IWebhookDispatch[], meta?: PaginationMeta }
925
- const retried = await client.webhooks.retryDispatch(dispatchId); // IWebhookDispatch
1119
+ const reenviado = await client.webhooks.retryDispatch(dispatchId); // IWebhookDispatch
926
1120
  ```
927
1121
 
928
- `register` sends `{ events, is_active, url, email }` and returns
929
- `{ events, is_active, url, email, updated_at? }`. Assinafy delivers each event
930
- as an HTTP `POST` with `Content-Type: application/json` and `Connection: close`.
931
- Any `2xx` is success. There are at most two automatic attempts, separated by
932
- three seconds. After ten consecutive failed events, ordinary delivery pauses
933
- and about 5% of later events are attempted until one succeeds; use
934
- `retryDispatch()` for an immediate manual redelivery. The dispatch history
935
- retains only the first 2,000 characters of the receiver's response body.
1122
+ `register` envia `{ events, is_active, url, email }` e devolve
1123
+ `{ events, is_active, url, email, updated_at? }`. A Assinafy entrega cada evento como um `POST` HTTP
1124
+ com `Content-Type: application/json` e `Connection: close`. Qualquer `2xx` é sucesso. São no máximo
1125
+ duas tentativas automáticas, com três segundos de intervalo. Depois de dez eventos falhos
1126
+ consecutivos, a entrega comum é pausada e cerca de 5% dos eventos seguintes são tentados até um dar
1127
+ certo; use `retryDispatch()` para reenviar manualmente na hora. O histórico guarda apenas os
1128
+ primeiros 2.000 caracteres do corpo de resposta do receptor.
936
1129
 
937
- Each history or retry result is an `IWebhookDispatch`:
1130
+ Cada item do histórico (ou resultado de reenvio) é um `IWebhookDispatch`:
938
1131
 
939
1132
  ```ts
940
1133
  {
@@ -953,25 +1146,25 @@ Each history or retry result is an `IWebhookDispatch`:
953
1146
  }
954
1147
  ```
955
1148
 
956
- Every delivery body uses this envelope:
1149
+ Todo corpo de entrega usa este envelope:
957
1150
 
958
1151
  ```ts
959
1152
  {
960
- id: number; // use for idempotent processing
1153
+ id: number; // use para processamento idempotente
961
1154
  event: string;
962
1155
  message: string | null;
963
1156
  payload: Record<string, unknown> | null;
964
1157
  origin: { ip?: string; 'user-agent'?: string } | null;
965
- created_at: number; // Unix seconds
1158
+ created_at: number; // segundos Unix
966
1159
  subject: { type: 'User' | 'Signer' | 'Account' | 'Document' | 'Template'; [key: string]: unknown };
967
1160
  object: { type: 'User' | 'Signer' | 'Account' | 'Document' | 'Template'; [key: string]: unknown };
968
1161
  account_id: string;
969
1162
  }
970
1163
  ```
971
1164
 
972
- Event-specific values are:
1165
+ Os valores por evento são:
973
1166
 
974
- | `event` | `subject.type` | `object.type` | `payload` keys |
1167
+ | `event` | `subject.type` | `object.type` | chaves de `payload` |
975
1168
  | --- | --- | --- | --- |
976
1169
  | `document_uploaded` | `User` | `Document` | — |
977
1170
  | `document_metadata_ready` | `User` | `Document` | — |
@@ -979,7 +1172,7 @@ Event-specific values are:
979
1172
  | `assignment_created` | `User` | `Document` | `user_name`, `user_email`, `user_telephone` |
980
1173
  | `document_ready` | `Account` | `Document` | — |
981
1174
  | `document_processing_failed` | `Account` | `Document` | `error_message` |
982
- | `signature_requested` | `User` | `Document` | `signer_email`, `signer_full_name`, or `signer_whatsapp_phone_number`, according to channel |
1175
+ | `signature_requested` | `User` | `Document` | `signer_email`, `signer_full_name` ou `signer_whatsapp_phone_number`, conforme o canal |
983
1176
  | `signer_created` | `User` | `Signer` | `signer_full_name` |
984
1177
  | `signer_email_verified` | `Signer` | `Document` | `signer_email` |
985
1178
  | `signer_whatsapp_verified` | `Signer` | `Document` | `signer_whatsapp_phone_number` |
@@ -992,21 +1185,19 @@ Event-specific values are:
992
1185
  | `template_processed` | `User` | `Template` | — |
993
1186
  | `template_processing_failed` | `Account` | `Template` | `error_message` |
994
1187
 
995
- `payload`, `subject`, and `object` are event-dependent. Accept unknown fields
996
- for forward compatibility and acknowledge only after durable, idempotent
997
- processing. Non-`2xx` responses, timeouts, and connection failures all count as
998
- failed deliveries. `assignment_created` and `document_metadata_ready` have no
999
- guaranteed ordering. For account entities, Assinafy removes the `integration`
1000
- property before delivery.
1188
+ `payload`, `subject` e `object` dependem do evento. Aceite campos desconhecidos para compatibilidade
1189
+ futura e confirme o recebimento depois de um processamento durável e idempotente. Respostas
1190
+ não-`2xx`, timeouts e falhas de conexão contam como entregas falhas. `assignment_created` e
1191
+ `document_metadata_ready` não têm ordem garantida. Para entidades de conta, a Assinafy remove a
1192
+ propriedade `integration` antes de entregar.
1001
1193
 
1002
- ### Webhook verification
1194
+ ### Verificação de webhooks
1003
1195
 
1004
- `WebhookVerifier` is an opt-in HMAC-SHA256 utility for integrations whose
1005
- Assinafy environment provides a shared secret and signature header. The
1006
- current official OpenAPI document does **not** define a webhook signature
1007
- scheme or header name. Confirm the delivery contract for your environment
1008
- before enabling this check; do not reject production callbacks based on an
1009
- assumed header. The example below uses an application-configured header name.
1196
+ `WebhookVerifier` é um utilitário HMAC-SHA256 opcional, para integrações cujo ambiente Assinafy
1197
+ forneça um segredo compartilhado e um header de assinatura. O documento OpenAPI oficial atual
1198
+ **não** define esquema nem nome de header de assinatura de webhook. Confirme o contrato de entrega
1199
+ do seu ambiente antes de habilitar essa checagem; não rejeite callbacks de produção com base em um
1200
+ header presumido. O exemplo abaixo usa um nome de header configurado pela aplicação.
1010
1201
 
1011
1202
  ```ts
1012
1203
  import express from 'express';
@@ -1014,159 +1205,209 @@ import express from 'express';
1014
1205
  const webhookSecret = process.env.ASSINAFY_WEBHOOK_SECRET;
1015
1206
  const signatureHeader = process.env.ASSINAFY_SIGNATURE_HEADER;
1016
1207
  if (!webhookSecret || !signatureHeader) {
1017
- throw new Error('This deployment has no confirmed webhook-signature contract');
1208
+ throw new Error('Este ambiente não tem contrato confirmado de assinatura de webhook');
1018
1209
  }
1019
1210
 
1020
1211
  const webhookClient = new AssinafyClient({ webhookSecret });
1021
1212
 
1022
1213
  app.post('/webhooks/assinafy', express.raw({ type: 'application/json' }), (req, res) => {
1023
- const signature = req.header(signatureHeader) ?? '';
1024
- const rawBody = req.body as Buffer;
1214
+ const assinatura = req.header(signatureHeader) ?? '';
1215
+ const corpoBruto = req.body as Buffer;
1025
1216
 
1026
- if (!webhookClient.webhookVerifier.verify(rawBody, signature)) {
1027
- return res.status(401).send('Invalid signature');
1217
+ if (!webhookClient.webhookVerifier.verify(corpoBruto, assinatura)) {
1218
+ return res.status(401).send('Assinatura inválida');
1028
1219
  }
1029
1220
 
1030
- const event = webhookClient.webhookVerifier.extractEvent(rawBody);
1031
- const type = webhookClient.webhookVerifier.getEventType(event);
1032
- const data = webhookClient.webhookVerifier.getEventData(event);
1221
+ const evento = webhookClient.webhookVerifier.extractEvent(corpoBruto);
1222
+ const tipo = webhookClient.webhookVerifier.getEventType(evento);
1223
+ const dados = webhookClient.webhookVerifier.getEventData(evento);
1033
1224
 
1034
- switch (type) {
1035
- case 'document_ready': break;
1036
- case 'signer_signed_document': break;
1037
- case 'signer_rejected_document': break;
1038
- case 'document_processing_failed':break;
1225
+ switch (tipo) {
1226
+ case 'document_ready': break;
1227
+ case 'signer_signed_document': break;
1228
+ case 'signer_rejected_document': break;
1229
+ case 'document_processing_failed': break;
1039
1230
  }
1040
1231
  res.sendStatus(200);
1041
1232
  });
1042
1233
  ```
1043
1234
 
1044
- ### Signer-side endpoints
1235
+ ### Endpoints do signatário
1045
1236
 
1046
- For building custom signer portals. Most calls require the `signer-access-code`
1047
- URL parameter that Assinafy emails/whatsapps to the signer. Artifact download is
1048
- the documented public exception; its optional fourth access-code argument exists
1049
- only for compatibility with deployments that still expect the legacy query.
1237
+ Para construir portais de assinatura próprios. Quase todas as chamadas exigem o parâmetro de URL
1238
+ `signer-access-code` que a Assinafy envia ao signatário por e-mail/WhatsApp. O download de artefato
1239
+ é a exceção pública documentada; seu quarto argumento opcional de código de acesso existe apenas por
1240
+ compatibilidade com deploys que ainda esperam a query antiga.
1050
1241
 
1051
1242
  ```ts
1052
1243
  await client.signerDocuments.self(accessCode);
1053
1244
  await client.signerDocuments.verifyEmail({ signerAccessCode: accessCode, verificationCode: '123456' });
1054
1245
 
1055
1246
  await client.signerDocuments.getCurrent(signerId, accessCode);
1056
- const { data } = await client.signerDocuments.list(signerId, accessCode, { per_page: 20 });
1057
- // Signer-side counterpart of documents.search(), authorised by the access code.
1058
- const found = await client.signerDocuments.search(signerId, accessCode, 'invoice');
1247
+ const { data } = await client.signerDocuments.list(signerId, accessCode, { 'per-page': 20 });
1248
+ // Equivalente a documents.search() do lado do signatário, autorizado pelo código de acesso.
1249
+ const achados = await client.signerDocuments.search(signerId, accessCode, 'nota fiscal');
1059
1250
  await client.signerDocuments.download(signerId, documentId, 'original');
1060
- // Available only after an ICP-Brasil certificate signer completes signing.
1251
+ // Disponível depois que um signatário por certificado ICP-Brasil concluir a assinatura.
1061
1252
  await client.signerDocuments.download(signerId, documentId, 'pades');
1062
1253
 
1063
1254
  await client.signerDocuments.confirmData(documentId, accessCode, {
1064
- email: 'me@example.com',
1065
- full_name: 'Example Signer',
1255
+ email: 'eu@exemplo.com.br',
1256
+ full_name: 'Signatário Exemplo',
1066
1257
  government_id: '123.456.789-00',
1067
1258
  has_accepted_terms: true,
1068
1259
  });
1069
- // Alternatively, accept terms separately before getAssignment():
1260
+ // Como alternativa, aceite os termos separadamente antes de getAssignment():
1070
1261
  // await client.signerDocuments.acceptTerms(accessCode);
1071
1262
 
1072
- // Signature image management ({ reuse: true } persists it for future documents)
1263
+ // Gestão da imagem de assinatura ({ reuse: true } guarda para documentos futuros)
1073
1264
  await client.signerDocuments.uploadSignature(accessCode, pngBuffer, { imageType: 'signature', reuse: true });
1074
1265
  await client.signerDocuments.downloadSignature(accessCode, 'signature');
1075
1266
 
1076
- // Sign / decline
1077
- const signable = await client.signerDocuments.getAssignment(accessCode);
1078
- // `sign()` is for collect assignments and requires every placed field value.
1267
+ // Assinar / recusar
1268
+ const assinavel = await client.signerDocuments.getAssignment(accessCode);
1269
+ // `sign()` é para assignments collect e exige o valor de cada campo posicionado.
1079
1270
  await client.signerDocuments.sign(documentId, assignmentId, accessCode, [
1080
- { itemId, fieldId, pageId, value: 'Signed by John' },
1271
+ { itemId, fieldId, pageId, value: 'Assinado por João' },
1081
1272
  ]);
1082
- await client.signerDocuments.decline(documentId, assignmentId, accessCode, 'Not authorized');
1273
+ await client.signerDocuments.decline(documentId, assignmentId, accessCode, 'Sem autorização');
1083
1274
 
1084
- // `signMultiple()` is for virtual assignments only.
1275
+ // `signMultiple()` é para assignments virtual.
1085
1276
  await client.signerDocuments.signMultiple(['doc-1', 'doc-2'], accessCode);
1086
- await client.signerDocuments.declineMultiple(['doc-1'], 'Unfavorable terms', accessCode);
1277
+ await client.signerDocuments.declineMultiple(['doc-1'], 'Condições desfavoráveis', accessCode);
1087
1278
  ```
1088
1279
 
1089
- `sign()` also requires virtual signers to have confirmed their data first, but
1090
- virtual assignments should normally use `signMultiple()`. Certificate signers
1091
- cannot use `sign()`; see [Paid signing branches](#paid-signing-branches).
1280
+ `sign()` também exige que signatários virtuais tenham confirmado os dados, mas assignments
1281
+ virtuais normalmente devem usar `signMultiple()`. Signatários por certificado não podem usar
1282
+ `sign()`; veja [Ramos pagos de assinatura](#ramos-pagos-de-assinatura).
1092
1283
 
1093
- ## High-level helper
1284
+ ## Helper de alto nível
1094
1285
 
1095
- Uploads a PDF, reuses or creates signers by email, creates a virtual assignment
1096
- immediately, and optionally waits for processing before returning.
1286
+ Envia um PDF, reaproveita ou cria signatários por e-mail, cria um assignment virtual imediatamente e,
1287
+ opcionalmente, espera o processamento antes de retornar.
1097
1288
 
1098
1289
  ```ts
1099
- const result = await client.uploadAndRequestSignatures({
1100
- source: { filePath: './contract.pdf' },
1290
+ const resultado = await client.uploadAndRequestSignatures({
1291
+ source: { filePath: './contrato.pdf' },
1101
1292
  signers: [
1102
- { name: 'John', email: 'john@example.com' },
1103
- { name: 'Jane', email: 'jane@example.com' },
1293
+ { name: 'João', email: 'joao@exemplo.com.br' },
1294
+ { name: 'Maria', email: 'maria@exemplo.com.br' },
1104
1295
  ],
1105
- message: 'Please sign',
1106
- metadata: { year: 2026 }, // compatibility upload part; omit for file-only wire format
1296
+ message: 'Por favor, assine',
1297
+ metadata: { ano: 2026 }, // parte de compatibilidade; omita para o formato só-arquivo
1107
1298
  waitForReady: true,
1108
1299
  waitOptions: { maxWaitMs: 30_000, pollIntervalMs: 1_000 },
1109
1300
  expiresAt: '2027-12-31T00:00:00Z',
1110
- copyReceivers: ['existing-copy-recipient-signer-id'],
1301
+ copyReceivers: ['id-de-signatario-em-copia'],
1111
1302
  });
1112
1303
 
1113
- result.document; // fully-processed IDocumentDetailsResponse (waitForReady: true, the default);
1114
- // the raw IDocumentUploadResponse when waitForReady: false
1115
- result.assignment; // IAssignment
1116
- result.signer_ids; // string[]
1304
+ resultado.document; // IDocumentDetailsResponse já processado (waitForReady: true, o padrão);
1305
+ // o IDocumentUploadResponse cru quando waitForReady: false
1306
+ resultado.assignment; // IAssignment
1307
+ resultado.signer_ids; // string[]
1117
1308
  ```
1118
1309
 
1119
- `waitForReady: false` skips post-assignment polling and returns the initial
1120
- upload response. With the default `true`, the helper creates the assignment first and
1121
- then waits for the current document details. Both production and sandbox allow
1122
- virtual assignments in `uploaded` and `metadata_processing` and promote them
1123
- automatically; only `collect` assignments require rendered pages.
1124
- Every signer above uses the default email channel. A phone-only signer selects
1125
- the paid WhatsApp branch described earlier. `copyReceivers` accepts existing
1126
- signer IDs, not email addresses; check the returned assignment before treating
1127
- a copy receiver as registered.
1310
+ `waitForReady: false` pula a espera pós-assignment e devolve a resposta inicial do upload. Com o
1311
+ padrão `true`, o helper cria o assignment primeiro e só então espera os detalhes atuais do
1312
+ documento. Produção e sandbox permitem assignments virtuais em `uploaded` e `metadata_processing` e
1313
+ os promovem automaticamente; assignments `collect` exigem páginas renderizadas. Todos os
1314
+ signatários acima usam o canal de e-mail padrão. Um signatário só com telefone seleciona o ramo pago
1315
+ de WhatsApp descrito antes. `copyReceivers` aceita IDs de signatários existentes, não endereços de
1316
+ e-mail; confira o assignment devolvido antes de considerar uma cópia registrada.
1128
1317
 
1129
- The helper is not transactional. A post-assignment polling error includes the
1130
- created `documentId`, `assignmentId`, and `signerIds` in its `context` (and in
1131
- `ValidationError.errors` for timeouts); inspect those IDs before deciding
1132
- whether to retry the workflow.
1318
+ O helper não é transacional. Um erro depois do assignment inclui `documentId`, `assignmentId` e
1319
+ `signerIds` criados no `context` do erro (e em `ValidationError.errors` para timeouts); inspecione
1320
+ esses IDs antes de decidir se repete o fluxo.
1133
1321
 
1134
- ## Errors
1322
+ ## Erros
1135
1323
 
1136
- HTTP methods reject with an `AssinafyError` subclass. Synchronous helpers such
1137
- as `getSocialLoginUrl()` can throw `ValidationError` before any request.
1324
+ Os métodos HTTP rejeitam com uma subclasse de `AssinafyError`. Helpers síncronos como
1325
+ `getSocialLoginUrl()` e `readAuthorizationCallback()` podem lançar antes de qualquer requisição.
1326
+
1327
+ | Classe | Quando | O que inspecionar |
1328
+ | --- | --- | --- |
1329
+ | `ValidationError` | O SDK recusou a entrada antes de enviar | `err.errors` |
1330
+ | `OAuthError` | Um endpoint OAuth devolveu `{ error, error_description }`, ou o callback trouxe `?error=` | `err.error`, `err.errorDescription` |
1331
+ | `ApiError` | A API respondeu com status de falha | `err.statusCode`, `err.responseData`, `err.challenge` |
1332
+ | `NetworkError` | DNS, conexão ou timeout | `err.message` (já sem credenciais) |
1333
+ | `AssinafyError` | Classe base de todas as acima | `err.context` |
1138
1334
 
1139
1335
  ```ts
1140
- import { ApiError, ValidationError, NetworkError, AssinafyError } from '@assinafy/sdk';
1336
+ import { ApiError, OAuthError, ValidationError, NetworkError, AssinafyError } from '@assinafy/sdk';
1141
1337
 
1142
1338
  try {
1143
1339
  await client.documents.upload({ filePath: './x.pdf' });
1144
1340
  } catch (err) {
1145
1341
  if (err instanceof ValidationError) {
1146
- console.error('Validation failed:', err.errors);
1342
+ console.error('Falha de validação:', err.errors);
1343
+ } else if (err instanceof OAuthError) {
1344
+ // OAuthError estende ApiError; ramifique pelo código RFC 6749, não pela mensagem.
1345
+ console.error('Erro OAuth:', err.error, err.errorDescription);
1147
1346
  } else if (err instanceof ApiError) {
1148
- console.error(`API error ${err.statusCode}:`, err.responseData);
1347
+ console.error(`Erro da API ${err.statusCode}:`, err.responseData);
1348
+ // `err.challenge` traz o header WWW-Authenticate já interpretado quando a API
1349
+ // envia um — em um 403 ele nomeia o escopo OAuth que está faltando.
1149
1350
  } else if (err instanceof NetworkError) {
1150
- console.error('Network error:', err.message);
1351
+ console.error('Erro de rede:', err.message);
1151
1352
  } else if (err instanceof AssinafyError) {
1152
- console.error('SDK error:', err.message, err.context);
1353
+ console.error('Erro do SDK:', err.message, err.context);
1153
1354
  }
1154
1355
  }
1155
1356
  ```
1156
1357
 
1157
- ## Development
1358
+ ## Ambientes
1359
+
1360
+ | | |
1361
+ | --- | --- |
1362
+ | Produção | `https://api.assinafy.com.br/v1` |
1363
+ | Sandbox | `https://sandbox.assinafy.com.br/v1` |
1364
+
1365
+ O sandbox é gratuito e espelha a produção para testar a integração de ponta a ponta, com a exceção
1366
+ das rotas de certificado digital, que existem apenas em produção.
1367
+
1368
+ O sandbox tem servidor de autorização próprio em `https://auth-sandbox.assinafy.com.br`, com a tela
1369
+ de consentimento em `/oauth/authorize`. Os documentos de descoberta dele, porém, não são
1370
+ alcançáveis: o nginx do sandbox recusa qualquer caminho iniciado por ponto, então `/.well-known/…`
1371
+ nunca chega à aplicação. Informe os endpoints explicitamente para pular a descoberta:
1372
+
1373
+ ```ts
1374
+ const client = new AssinafyClient({ baseUrl: 'https://sandbox.assinafy.com.br/v1' });
1375
+
1376
+ const requisicao = await client.oauth.createAuthorizationUrl({
1377
+ clientId: process.env.ASSINAFY_CLIENT_ID!,
1378
+ redirectUri: 'https://meuapp.com.br/oauth/callback',
1379
+ scopes: ['documents:read'],
1380
+ issuer: 'https://auth-sandbox.assinafy.com.br',
1381
+ authorizationEndpoint: 'https://auth-sandbox.assinafy.com.br/oauth/authorize',
1382
+ });
1383
+ ```
1384
+
1385
+ Os endpoints de token, revogação e userinfo seguem o `baseUrl` configurado. Confirme que o deploy de
1386
+ sandbox os expõe antes de rodar a etapa de troca por lá — veja
1387
+ [docs/COMPATIBILITY.md](docs/COMPATIBILITY.md).
1388
+
1389
+ ## Desenvolvimento
1158
1390
 
1159
1391
  ```bash
1160
1392
  bun install --frozen-lockfile
1161
- bun run typecheck # source, script, and test type checks
1393
+ bun run typecheck # tipos de src, scripts e testes
1162
1394
  bun run lint
1163
- bun test # bun:test suites
1395
+ bun test # suítes bun:test
1164
1396
  bun run test:coverage
1165
1397
  bun run build # tsup → dist/ (CJS + ESM + .d.ts)
1166
1398
  bun run lint:pkg # publint + arethetypeswrong
1167
- bun run verify # complete local release gate
1399
+ bun run audit:api # confere o SDK contra o OpenAPI publicado
1400
+ bun run verify # portão completo de release local
1168
1401
  ```
1169
1402
 
1170
- ## License
1403
+ ## Documentação
1404
+
1405
+ - **[README.en.md](README.en.md)** — a mesma referência completa, em inglês
1406
+ - [docs/API_COVERAGE.md](docs/API_COVERAGE.md) — mapa de operações
1407
+ - [docs/COMPATIBILITY.md](docs/COMPATIBILITY.md) — variações de requisição e resposta por deploy
1408
+ - [docs/RELEASING.md](docs/RELEASING.md) — processo de publicação
1409
+ - [Documentação da API](https://api.assinafy.com.br/v1/docs)
1410
+
1411
+ ## Licença
1171
1412
 
1172
- MIT
1413
+ Distribuído sob a licença [MIT](LICENSE).