@assinafy/sdk 2.2.0 → 2.4.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,72 +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.
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.
11
-
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.
15
-
16
- ## Contents
17
-
18
- This document runs from setup to a complete signature workflow, then to
19
- per-resource detail. Read it in order the first time; use it as a reference
20
- afterwards.
21
-
22
- **Getting set up** — [Requirements](#requirements) ·
23
- [Installation](#installation) · [Quick start](#quick-start) ·
24
- [Authentication](#authentication) · [Configuration](#configuration)
25
- ([rate limiting](#rate-limiting), [factories](#factories)) ·
26
- [Endpoint coverage](#endpoint-coverage)
27
-
28
- **The end-to-end flow** — [Document lifecycle](#document-lifecycle):
29
- [upload](#1-upload-the-pdf) → [signers](#2-create-or-reuse-the-email-signers) →
30
- [price and request signatures](#3-price-then-request-signatures) →
31
- [the signer's side](#4-complete-the-email-signer-flow) →
32
- [completion and artifacts](#5-observe-completion-and-download-artifacts)
33
-
34
- **Per-resource detail** — [Resource reference](#resource-reference):
35
- [documents](#documents) · [signers](#signers) · [assignments](#assignments) ·
36
- [paid signing branches](#paid-signing-branches) · [templates](#templates) ·
37
- [tags](#tags) · [workspaces](#workspaces) ·
38
- [field definitions](#field-definitions) ·
39
- [auth and API keys](#authentication--api-key-management) ·
40
- [the current user](#authenticated-user) · [webhooks](#webhooks)
41
- ([verification](#webhook-verification)) ·
42
- [signer-side endpoints](#signer-side-endpoints)
43
-
44
- **Everything else** [High-level helper](#high-level-helper) ·
45
- [Errors](#errors) · [Development](#development) · [License](#license)
46
-
47
- ## Requirements
48
-
49
- - Node.js 22+ for the built-in `FormData` / `Blob` APIs used by uploads. Packed
50
- CJS and ESM imports are tested on 22 (maintenance LTS), 24 (active LTS), and
51
- 26 (Current); Node 20 reached end-of-life in April 2026 and is unsupported.
52
- - or Bun 1.4.0 (the version pinned for development and CI)
53
-
54
- ## Installation
3
+ *Português · [Read in English](README.en.md)*
4
+
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.
7
+
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.
13
+
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).
16
+
17
+ ## Sumário
18
+
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
55
54
 
56
55
  ```bash
57
56
  npm install @assinafy/sdk
58
- # or
57
+ # ou
59
58
  bun add @assinafy/sdk
60
59
  ```
61
60
 
62
- 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`:
63
64
 
64
65
  ```
65
66
  @assinafy:registry=https://npm.pkg.github.com
66
67
  //npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
67
68
  ```
68
69
 
69
- ## Quick start
70
+ ## Início rápido
70
71
 
71
72
  ```ts
72
73
  import { AssinafyClient } from '@assinafy/sdk';
@@ -78,140 +79,278 @@ const client = new AssinafyClient({
78
79
  baseUrl,
79
80
  });
80
81
 
81
- const result = await client.uploadAndRequestSignatures({
82
- source: { filePath: './contract.pdf' },
82
+ const resultado = await client.uploadAndRequestSignatures({
83
+ source: { filePath: './contrato.pdf' },
83
84
  signers: [
84
- { name: 'John Doe', email: 'john@example.com' },
85
- { 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' },
86
87
  ],
87
- message: 'Please sign this contract',
88
+ message: 'Por favor, assine este contrato',
88
89
  });
89
90
 
90
- console.log('Document ID:', result.document.id);
91
- 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);
92
93
  ```
93
94
 
94
- This path uses email verification and notification for every signer. WhatsApp
95
- and ICP-Brasil certificate signing have separate prerequisites and costs; see
96
- [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.
97
102
 
98
- ## 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). |
99
108
 
100
- 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.
101
111
 
102
112
  ```ts
103
- // Preferred: X-Api-Key header
113
+ // Preferido: header X-Api-Key
104
114
  new AssinafyClient({ apiKey: 'k_xxx', accountId: 'acc_xxx' });
105
115
 
106
- // Access token: Authorization: Bearer <token>
116
+ // Token de acesso: Authorization: Bearer <token>
107
117
  new AssinafyClient({ token: 'jwt_xxx', accountId: 'acc_xxx' });
108
118
  ```
109
119
 
110
- Credentials are optional at construction time. A credentialless client uses a
111
- separate, auth-free transport for public authentication and signer-access-code
112
- 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:
113
123
 
114
124
  ```ts
115
- const publicClient = new AssinafyClient({
125
+ const clientePublico = new AssinafyClient({
116
126
  baseUrl: 'https://sandbox.assinafy.com.br/v1',
117
127
  });
118
128
 
119
- await publicClient.auth.login('me@example.com', 'password');
120
- await publicClient.documents.getPublic(documentId);
121
- 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);
122
132
  ```
123
133
 
124
- Protected methods still require `apiKey` or `token`; the API returns its normal
125
- `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.
126
136
 
127
- Every SDK transport, including public and signer-access-code requests, sends
128
- `User-Agent: Assinafy-Typescript-SDK/v<VERSION>`, where `<VERSION>` is the
129
- installed package version. The exact value is also exported as
130
- `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`.
131
140
 
132
- ## Configuration
141
+ ## Aplicações OAuth
133
142
 
134
- | Option | Type | Default | Description |
135
- | --------------- | -------- | --------------------------------------- | --------------------------------------------- |
136
- | `apiKey` | string | | Preferred credential (sent as `X-Api-Key`). |
137
- | `token` | string | — | Access token (sent as `Authorization: Bearer`). |
138
- | `accountId` | string | — | Default workspace/account ID. |
139
- | `baseUrl` | string | `https://api.assinafy.com.br/v1` | Absolute API base without credentials, query, or fragment. Must be `https` unless the host is loopback. |
140
- | `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). |
141
- | `timeout` | number | `30000` | Request timeout in milliseconds. |
142
- | `maxRetries` | number | `2` | Auto-retries eligible HTTP 429 responses, honoring `Retry-After`. `0` disables. |
143
- | `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.
144
146
 
145
- ### 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.
146
152
 
147
- On an HTTP `429`, the client automatically retries up to `maxRetries` times,
148
- waiting for the server-provided `Retry-After` (or `X-Rate-Limit-Reset`) delay
149
- before each attempt. Automatic replay is limited to read-safe `GET`, `HEAD`,
150
- `OPTIONS`, and `DELETE` requests. `GET /sign` is excluded because it records
151
- that the signer viewed the assignment. Writes are not replayed by default.
152
- A non-empty `Idempotency-Key` opts a custom request into SDK replay, but it is
153
- not part of the current Assinafy OpenAPI contract: confirm that the target
154
- 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.
155
156
 
156
- ### 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:
157
209
 
158
210
  ```ts
159
- // Positional factory
160
- const client = AssinafyClient.create('api-key', 'account-id');
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
161
246
 
162
- // From a plain object (accepts snake_case or camelCase keys)
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
301
+
302
+ ```ts
303
+ // Fábrica posicional
304
+ const client = AssinafyClient.create('chave-de-api', 'id-da-conta');
305
+
306
+ // A partir de um objeto simples (aceita chaves snake_case ou camelCase)
163
307
  const client = AssinafyClient.fromConfig({
164
308
  api_key: process.env.ASSINAFY_API_KEY!,
165
309
  account_id: process.env.ASSINAFY_ACCOUNT_ID!,
166
310
  });
167
311
  ```
168
312
 
169
- ## Endpoint coverage
170
-
171
- All 89 operations documented at https://api.assinafy.com.br/v1/docs are
172
- covered. The table below is the resource-level summary; the detailed operation
173
- ledger is in [docs/API_COVERAGE.md](docs/API_COVERAGE.md).
174
-
175
- | Resource | Endpoints |
176
- | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
177
- | `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 |
178
- | `client.signers` | create, get, list, update, delete, findByEmail |
179
- | `client.assignments` | **list**, create, estimateCost, resetExpiration, resendNotification, estimateResendCost, listWhatsAppNotifications |
180
- | `client.templates` | **create**, list, get, **update**, **delete**, downloadPage |
181
- | `client.tags` | list, create, update, delete |
182
- | `client.workspaces` | create, list, get, update, delete, getTheme, downloadLogo, uploadLogo, deleteLogo, getStats |
183
- | `client.webhooks` | register, get, inactivate, listEventTypes, listDispatches, retryDispatch |
184
- | `client.fields` | create, list, get, update, delete, validate, validateMultiple, listTypes |
185
- | `client.auth` | getSocialLoginUrl, getSocialLoginCallbackUrl, login, socialLogin, linkSocialLogin, createApiKey, getApiKey, deleteApiKey, changePassword, requestPasswordReset, resetPassword |
186
- | `client.users` | getCurrent, getStats, getNotificationPreferences, updateNotificationPreferences |
187
- | `client.signerDocuments` | getCurrent, list, **search**, download, signMultiple, declineMultiple, self, acceptTerms, verifyEmail, confirmData, uploadSignature, downloadSignature, getAssignment, sign, decline |
188
- | `client.webhookVerifier` | verify, extractEvent, getEventType, getEventData |
189
-
190
- Every HTTP wrapper has TypeScript-checked request/response shapes and
191
- method-level JSDoc covering the wire payload, return shape, validation,
192
- relevant API errors, and a copyable example. Reusable and OpenAPI schema-level
193
- payloads are exported as named types; small method-local option bags remain
194
- inline in the generated declarations. Editors expose the reference on hover,
195
- and declaration files ship with the package. The coverage ledger links those
196
- typed methods back to each upstream operation without duplicating the schema.
197
-
198
- ## Document lifecycle
199
-
200
- The normal integration has an account-owner phase, a signer phase, and a final
201
- artifact phase. The example below keeps every signer on email and uses a
202
- `virtual` assignment, so no page coordinates or paid notification channel are
203
- required.
204
-
205
- ### 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
206
346
 
207
347
  ```ts
208
- const uploaded = await client.documents.upload({ filePath: './contract.pdf' });
348
+ const enviado = await client.documents.upload({ filePath: './contrato.pdf' });
209
349
  ```
210
350
 
211
- The official multipart body contains the `file` part. The SDK also supports a
212
- display-name override (used as that file part's filename) and an optional JSON
213
- `metadata` part for deployments that accept it. A successful response is
214
- `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`:
215
354
 
216
355
  ```ts
217
356
  {
@@ -241,37 +380,36 @@ display-name override (used as that file part's filename) and an optional JSON
241
380
  }
242
381
  ```
243
382
 
244
- `DocumentStatus` covers `uploading`, `uploaded`, `metadata_processing`,
245
- `metadata_ready`, `pending_signature`, `expired`, `certificating`,
246
- `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`.
247
386
 
248
- Uploads must be PDFs, at most 25 MB and at most 2,000 pages. The SDK checks the
249
- extension, size, and `%PDF-` header before sending. A new upload can have an
250
- empty `pages` array until metadata processing finishes. Wait before creating a
251
- `collect` assignment because its fields refer to rendered page IDs; a
252
- `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.
253
391
 
254
392
  ```ts
255
- const prepared = await client.documents.waitUntilReady(uploaded.id, {
393
+ const preparado = await client.documents.waitUntilReady(enviado.id, {
256
394
  maxWaitMs: 30_000,
257
395
  pollIntervalMs: 2_000,
258
396
  });
259
397
  ```
260
398
 
261
- ### 2. Create or reuse the email signers
399
+ ### 2. Crie ou reaproveite os signatários por e-mail
262
400
 
263
401
  ```ts
264
- const signerA = await client.signers.create({
265
- full_name: 'John Doe',
266
- email: 'john@example.com',
402
+ const signatarioA = await client.signers.create({
403
+ full_name: 'João Silva',
404
+ email: 'joao@exemplo.com.br',
267
405
  });
268
- const signerB = await client.signers.create({
269
- full_name: 'Jane Smith',
270
- email: 'jane@example.com',
406
+ const signatarioB = await client.signers.create({
407
+ full_name: 'Maria Souza',
408
+ email: 'maria@exemplo.com.br',
271
409
  });
272
410
  ```
273
411
 
274
- The wire body is `{ full_name, email }`. Each response is an `ISigner`:
412
+ O corpo de rede é `{ full_name, email }`. Cada resposta é um `ISigner`:
275
413
 
276
414
  ```ts
277
415
  {
@@ -280,35 +418,34 @@ The wire body is `{ full_name, email }`. Each response is an `ISigner`:
280
418
  full_name: string;
281
419
  email: string | null;
282
420
  whatsapp_phone_number?: string | null;
283
- cpf?: string | null; // compatibility type; not echoed by the API
421
+ cpf?: string | null; // tipo de compatibilidade; a API não devolve
284
422
  has_accepted_terms?: boolean;
285
- has_signature?: boolean; // signer-self response only
286
- has_initial?: boolean; // signer-self response only
287
- 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
288
426
  metadata?: Record<string, unknown>;
289
427
  }
290
428
  ```
291
429
 
292
- When an email is present, `signers.create()` first looks up that email in the
293
- workspace and reuses the matching signer; a name-only or phone-only request
294
- 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.
295
432
 
296
- ### 3. Price, then request signatures
433
+ ### 3. Orce, depois peça as assinaturas
297
434
 
298
- Cost estimation takes channel descriptors, not signer IDs:
435
+ A estimativa de custo recebe descritores de canal, não IDs de signatário:
299
436
 
300
437
  ```ts
301
- const estimate = await client.assignments.estimateCost(uploaded.id, {
438
+ const estimativa = await client.assignments.estimateCost(enviado.id, {
302
439
  method: 'virtual',
303
- signers: [{}, {}], // `{}` selects Email for each signer
440
+ signers: [{}, {}], // `{}` seleciona Email para cada signatário
304
441
  });
305
442
 
306
- if (!estimate.has_sufficient_resources) {
307
- 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');
308
445
  }
309
446
  ```
310
447
 
311
- The response is `ICostEstimate`:
448
+ A resposta é `ICostEstimate`:
312
449
 
313
450
  ```ts
314
451
  {
@@ -326,21 +463,21 @@ The response is `ICostEstimate`:
326
463
  }
327
464
  ```
328
465
 
329
- Create the email assignment only after accepting that estimate:
466
+ Crie o assignment por e-mail depois de aceitar essa estimativa:
330
467
 
331
468
  ```ts
332
- const assignment = await client.assignments.create(uploaded.id, {
469
+ const assignment = await client.assignments.create(enviado.id, {
333
470
  method: 'virtual',
334
471
  signers: [
335
- { id: signerA.id, verification_method: 'Email', notification_methods: ['Email'] },
336
- { 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'] },
337
474
  ],
338
- message: 'Please review and sign',
475
+ message: 'Por favor, revise e assine',
339
476
  expires_at: '2027-12-31T23:59:00Z',
340
477
  });
341
478
  ```
342
479
 
343
- The request returns an `IAssignment`:
480
+ A requisição devolve um `IAssignment`:
344
481
 
345
482
  ```ts
346
483
  {
@@ -363,250 +500,232 @@ The request returns an `IAssignment`:
363
500
  }
364
501
  ```
365
502
 
366
- The URLs and delivered messages contain signer credentials; treat them as
367
- secrets.
503
+ As URLs e as mensagens entregues contêm credenciais do signatário; trate-as como segredo.
368
504
 
369
- ### 4. Complete the email signer flow
505
+ ### 4. Conclua o fluxo do signatário por e-mail
370
506
 
371
- Assinafy sends each signer a link containing their access code and sends the
372
- one-time verification code through the selected channel. Neither value is
373
- returned as a standalone owner-side API field. A custom signer portal must
374
- obtain both values from the signer-delivery flow; do not manufacture them or
375
- 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.
376
511
 
377
512
  ```ts
378
- // Signer-side client: no account API credential is needed or sent.
379
- const signerClient = new AssinafyClient({
380
- baseUrl,
381
- });
513
+ // Cliente do signatário: nenhuma credencial da conta é necessária nem enviada.
514
+ const clienteSignatario = new AssinafyClient({ baseUrl });
382
515
 
383
- const self = await signerClient.signerDocuments.self(accessCode); // ISignerSelf
516
+ const self = await clienteSignatario.signerDocuments.self(accessCode); // ISignerSelf
384
517
 
385
518
  // Query: signer-access-code=<accessCode>
386
- // Body: { 'verification-code': '<six-digit code>' }
387
- await signerClient.signerDocuments.verifyEmail({
519
+ // Corpo: { 'verification-code': '<código de seis dígitos>' }
520
+ await clienteSignatario.signerDocuments.verifyEmail({
388
521
  signerAccessCode: accessCode,
389
522
  verificationCode,
390
523
  }); // Promise<void>
391
524
 
392
- const confirmed = await signerClient.signerDocuments.confirmData(
393
- uploaded.id,
525
+ const confirmado = await clienteSignatario.signerDocuments.confirmData(
526
+ enviado.id,
394
527
  accessCode,
395
528
  { full_name: self.full_name, email: self.email ?? undefined },
396
529
  ); // ISigner
397
530
 
398
- const signable = await signerClient.signerDocuments.getAssignment(accessCode, true);
399
- // `getAssignment` returns IDocumentDetailsResponse and records that the signer
400
- // 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.
401
534
 
402
- await signerClient.signerDocuments.signMultiple([signable.id], accessCode);
403
- // 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.
404
537
  ```
405
538
 
406
- Repeat this phase separately for each signer with that signer's own access code
407
- and one-time code. The two signers in this example share the default step and
408
- 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.
409
541
 
410
- `signMultiple` is only for `virtual` assignments. For `collect`, read
411
- `signable.assignment.items`, then call `sign(documentId, assignmentId,
412
- accessCode, entries)` with a non-empty array of
413
- `{ itemId, fieldId, pageId, value }`. A virtual signer must confirm their data
414
- before signing. A `DigitalCertificate` signer cannot call `sign`; that branch
415
- uses Assinafy's certificate-start and certificate-complete flow, which is not
416
- 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).
417
548
 
418
- ### 5. Observe completion and download artifacts
549
+ ### 5. Acompanhe a conclusão e baixe os artefatos
419
550
 
420
- Subscribe to `document_ready` for event-driven completion, or fetch
421
- `documents.details(documentId)` until `status === 'certificated'`. Webhook
422
- deliveries can repeat, so use their numeric `id` as an
423
- 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:
424
554
 
425
555
  ```ts
426
- const finalDocument = await client.documents.details(uploaded.id);
427
- const signedPdf = await client.documents.download(uploaded.id, 'certificated');
428
- const certificatePage = await client.documents.download(uploaded.id, 'certificate-page');
429
- 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');
430
560
 
431
- // Validate an Assinafy signature hash when your workflow has extracted it.
432
- 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);
433
563
  ```
434
564
 
435
- `original`, `certificated`, and `certificate-page` are PDFs. `bundle` is a ZIP
436
- containing those three artifacts and also `pades` when the document had an
437
- ICP-Brasil certificate signer. The `pades` PDF exists only for documents that
438
- had certificate signers. An artifact can return `404` before generation has
439
- finished. `decline_reason` is included in document details only when the access
440
- 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.
441
570
 
442
- ## Resource reference
571
+ ## Referência de recursos
443
572
 
444
- Most account-scoped methods accept an optional `accountId` that overrides the
445
- client default. Workspace `get`, `update`, `delete`, branding, and statistics
446
- 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.
447
576
 
448
- ### Documents
577
+ ### Documentos
449
578
 
450
579
  ```ts
451
- // Upload from a file path (recommended)
580
+ // Envio a partir de um caminho de arquivo (recomendado)
452
581
  const doc = await client.documents.upload(
453
- { filePath: './contract.pdf' },
454
- { name: 'Service agreement', metadata: { type: 'service' } },
582
+ { filePath: './contrato.pdf' },
583
+ { name: 'Contrato de prestação', metadata: { tipo: 'servico' } },
455
584
  );
456
- // `name` and `metadata` are compatibility multipart parts outside the published
457
- // file-only request schema.
458
- // `name` is optional and defaults to the file's own name. The API derives the
459
- // display name from the uploaded filename and appends `.pdf` when absent, so
460
- // the document above is stored as 'Service agreement.pdf'. Accents are
461
- // 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').
462
591
  // → {
463
592
  // resource: 'document', id: '1031…', account_id: '102d…', template_id: null,
464
- // name: 'Service agreement.pdf', status: 'uploaded',
593
+ // name: 'Contrato de prestacao.pdf', status: 'uploaded',
465
594
  // artifacts: { original: 'https://…/download/original' },
466
595
  // signing_url: 'https://app…/sign/1031…',
467
- // pages: [], // populated once status reaches `metadata_ready`
596
+ // pages: [], // preenchido quando o status chega a `metadata_ready`
468
597
  // tags: [], is_closed: false, created_at: '2026-…', updated_at: '2026-…'
469
598
  // }
470
599
 
471
- // …or from a Buffer already in memory
472
- 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' });
473
602
 
474
- // List → { data: IDocumentListItem[], meta?: { current_page, per_page, total, last_page } }
603
+ // Listagem → { data: IDocumentListItem[], meta?: { current_page, per_page, total, last_page } }
475
604
  const { data, meta } = await client.documents.list({ page: 1, 'per-page': 20, sort: 'updated_at' });
476
605
 
477
- // Search is the lightweight alternative to list: same item shape, but the API
478
- // skips the expanded `assignment`/`pages`. Prefer it for name lookups.
479
- 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 });
480
609
 
481
610
  await client.documents.details(doc.id);
482
611
  await client.documents.activities(doc.id);
483
612
  await client.documents.waitUntilReady(doc.id, { maxWaitMs: 30_000 });
484
613
 
485
- // Rename. The API rejects this with 400 while the document is still in
486
- // `metadata_processing`, so await waitUntilReady() first on a fresh upload.
487
- // (Passing `name` to upload() avoids both the round-trip and the race.)
488
- 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');
489
618
 
490
- await client.documents.download(doc.id, 'certificated'); // signed PDF
619
+ await client.documents.download(doc.id, 'certificated'); // PDF assinado
491
620
  await client.documents.download(doc.id, 'certificate-page');
492
621
  await client.documents.download(doc.id, 'bundle'); // ZIP
493
- // `pades` exists only when at least one signer used DigitalCertificate.
622
+ // `pades` existe apenas quando algum signatário usou DigitalCertificate.
494
623
  await client.documents.download(doc.id, 'pades');
495
624
  await client.documents.thumbnail(doc.id);
496
625
  await client.documents.downloadPage(doc.id, pageId);
497
626
 
498
- await client.documents.statuses(); // list every status code + deletable flag
627
+ await client.documents.statuses(); // todos os códigos de status + flag deletable
499
628
  await client.documents.isFullySigned(doc.id);
500
629
  await client.documents.getSigningProgress(doc.id);
501
630
  await client.documents.delete(doc.id);
502
631
 
503
- // Verify a signed document by its Assinafy signature hash
632
+ // Verifique um documento assinado pelo hash de assinatura Assinafy
504
633
  await client.documents.verify('FE32EDDADE7CBDDCBB934E7402047450B0E59C02');
505
634
 
506
- // Public endpoints (no auth)
635
+ // Endpoints públicos (sem autenticação)
507
636
  await client.documents.getPublic(doc.id);
508
- // Official request body: { email: 'jane@example.com' }
509
- 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');
510
639
 
511
- // Explicit compatibility overload for older deployments:
640
+ // Sobrecarga explícita de compatibilidade para deploys mais antigos:
512
641
  // { recipient: '+5548999990000', channel: 'whatsapp' }
513
642
  await client.documents.sendToken(doc.id, '+5548999990000', 'whatsapp');
514
643
 
515
- // The current OpenAPI contract requires existing tag IDs.
516
- const contractsTag = await client.tags.create({ name: 'Contracts' });
517
- const quarterTag = await client.tags.create({ name: '2026-Q1' });
518
- 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' });
519
648
  await client.documents.listTags(doc.id);
520
- await client.documents.replaceTags(doc.id, [contractsTag.id, quarterTag.id]); // [] detaches all
521
- await client.documents.addTags(doc.id, [urgentTag.id]); // append
522
- 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 }
523
652
  ```
524
653
 
525
- Uploads are validated locally: only `.pdf` files up to 25 MB whose bytes begin
526
- with the PDF magic header (`%PDF-`) are accepted. The API also limits documents
527
- 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.
528
656
 
529
- Page and artifact URLs embedded in JSON responses still require the same
530
- account authentication as their download operations. Prefer
531
- `documents.downloadPage()` and `documents.download()` so the SDK applies the
532
- credential and returns a `Buffer`. `bundle` contains `original`, `certificated`,
533
- 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.
534
661
 
535
- List endpoints return `{ data, meta }`, where `meta` is populated from the
536
- `X-Pagination-*` response headers. Two API behaviours are worth knowing because
537
- both are silent rather than errors:
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:
538
664
 
539
- - Only the hyphenated `per-page` is read. `per_page` is accepted and ignored,
540
- falling back to 20 rows so the SDK rewrites `per_page` to `per-page` on
541
- every list method, and an explicit `per-page` wins when both are given.
542
- - `per-page` is clamped to **50**. Asking for 100 returns 50 rows with a `200`.
543
- The exported `MAX_LIST_PAGE_SIZE` constant is that ceiling; page through with
544
- `page` rather than requesting a larger page, and trust `meta.per_page` over
545
- the value you asked for.
665
+ - 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.
546
671
 
547
- ### Signers
672
+ ### Signatários
548
673
 
549
674
  ```ts
550
675
  await client.signers.create({
551
- full_name: 'John Doe',
552
- email: 'john@example.com',
553
- 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
554
679
  });
555
- // → { id: '19e6…', full_name: 'John Doe', email: 'john@example.com',
680
+ // → { id: '19e6…', full_name: 'João Silva', email: 'joao@exemplo.com.br',
556
681
  // whatsapp_phone_number: null, has_accepted_terms: false }
557
- // (note: `cpf` is accepted on input but never echoed back by the API)
682
+ // (observação: `cpf` é aceito na entrada, mas a API nunca o devolve)
558
683
 
559
- // Both contacts are optional. A name-only signer cannot be notified until a
560
- // contact is added.
561
- await client.signers.create({ full_name: 'Contact Pending' });
562
-
563
- await client.signers.create({
564
- full_name: 'Jane Doe',
565
- email: 'jane@example.com',
566
- });
684
+ // Ambos os contatos são opcionais. Um signatário com nome não pode ser
685
+ // notificado até que um contato seja adicionado.
686
+ await client.signers.create({ full_name: 'Contato Pendente' });
567
687
 
568
688
  await client.signers.get(signerId);
569
- await client.signers.list({ page: 1, 'per-page': 50, search: 'john' });
689
+ await client.signers.list({ page: 1, 'per-page': 50, search: 'joao' });
570
690
  await client.signers.update(signerId, {
571
- full_name: 'Johnny Doe',
572
- 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
573
693
  });
574
694
  await client.signers.delete(signerId);
575
695
 
576
- const existing = await client.signers.findByEmail('john@example.com');
696
+ const existente = await client.signers.findByEmail('joao@exemplo.com.br');
577
697
  ```
578
698
 
579
- When an `email` is supplied, `signers.create()` is idempotent by email: it
580
- reuses an existing signer when the same email is already present in the
581
- workspace. Signers without email are always created fresh. See
582
- [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.
583
702
 
584
703
  ### Assignments
585
704
 
586
705
  ```ts
587
- // List every assignment in the workspace.
706
+ // Lista todos os assignments do workspace.
588
707
  // → { data: IAssignment[], meta?: { current_page, per_page, total, last_page } }
589
708
  const { data, meta } = await client.assignments.list({ page: 1, 'per-page': 20 });
590
709
 
591
- // 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.
592
711
  await client.assignments.create(documentId, {
593
712
  method: 'virtual',
594
713
  signers: ['signer-1', 'signer-2'],
595
- message: 'Please review and sign',
714
+ message: 'Por favor, revise e assine',
596
715
  expires_at: '2027-12-31T23:59:00Z',
597
- copy_receivers: ['copy-recipient-signer-id'],
716
+ copy_receivers: ['id-do-signatario-em-copia'],
598
717
  });
599
718
 
600
- // Sequential signing: `step` controls signing order (parallel within a step).
719
+ // Assinatura sequencial: `step` controla a ordem (paralelo dentro de um passo).
601
720
  await client.assignments.create(documentId, {
602
721
  method: 'virtual',
603
722
  signers: [
604
723
  { id: 'signer-1', step: 1 },
605
- { id: 'signer-2', step: 2 }, // notified only after step 1 finishes
724
+ { id: 'signer-2', step: 2 }, // notificado depois que o passo 1 terminar
606
725
  ],
607
726
  });
608
727
 
609
- // 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.
610
729
  await client.assignments.create(documentId, {
611
730
  method: 'collect',
612
731
  signers: [{ id: signerId }],
@@ -623,8 +742,8 @@ await client.assignments.create(documentId, {
623
742
  }],
624
743
  });
625
744
 
626
- // Estimate cost (the endpoint prices channel descriptors, not signer IDs) → ICostEstimate
627
- 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
628
747
  // → {
629
748
  // documents: 1, credits: 0, needs_extra_document: false, extra_document_cost: 0,
630
749
  // total_credits: 0, breakdown: [], document_balance: 67, credit_balance: 0,
@@ -632,88 +751,108 @@ await client.assignments.estimateCost(documentId, { signers: [{}] }); // default
632
751
  // }
633
752
 
634
753
  await client.assignments.resetExpiration(documentId, assignmentId, '2027-06-30T00:00:00Z');
635
- // Compatibility only: the published request requires a date-time string.
636
- // 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.
637
756
  await client.assignments.resetExpiration(documentId, assignmentId, null);
638
757
 
639
758
  await client.assignments.resendNotification(documentId, assignmentId, signerId);
640
759
  // → { is_sent: true, document_id: '…', signer_id: '…' }
641
760
 
642
- const resendCost = await client.assignments.estimateResendCost(documentId, assignmentId, signerId);
643
- // Official response: ICostEstimate. Older deployments can return the compact
644
- // IResendCostEstimate branch with `total` and `has_sufficient_credits`; narrow
645
- // 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.
646
765
  ```
647
766
 
648
- The `create` response is an `IAssignment`: `{ id, method, signers: [...],
767
+ A resposta de `create` é um `IAssignment`: `{ id, method, signers: [...],
649
768
  items: [{ display_settings, ... }], signing_urls: [{ signer_id, url }], … }`.
650
769
 
651
- 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.
652
772
 
653
- **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:
654
776
 
655
777
  ```ts
656
- await client.documents.delete(documentId); // workspace-side
657
- 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
658
780
  ```
659
781
 
660
- ### Paid signing branches
782
+ ### Ramos pagos de assinatura
783
+
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.
786
+
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`.
789
+
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 |
661
795
 
662
- Keep the email flow as the default. Enable either branch below only after the
663
- workspace has the required plan or feature and the returned cost estimate is
664
- acceptable.
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`.
665
798
 
666
- #### WhatsApp verification and notification
799
+ #### Verificação e notificação por WhatsApp
667
800
 
668
- WhatsApp is available only on paid subscriptions and costs 0.45 credit per
669
- notification. Create a phone-only signer or add a phone to an existing signer,
670
- then request the `Whatsapp` channel explicitly:
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:
671
803
 
672
804
  ```ts
673
- const phoneSigner = await client.signers.create({
674
- full_name: 'Mobile Signer',
805
+ const signatarioTelefone = await client.signers.create({
806
+ full_name: 'Signatário Mobile',
675
807
  whatsapp_phone_number: '+5511999990000',
676
808
  });
677
809
 
678
- const whatsappCost = await client.assignments.estimateCost(documentId, {
810
+ const custoWhatsapp = await client.assignments.estimateCost(documentId, {
679
811
  method: 'virtual',
680
812
  signers: [{ verification_method: 'Whatsapp', notification_methods: ['Whatsapp'] }],
681
813
  });
682
814
 
683
- const whatsappAssignment = await client.assignments.create(documentId, {
815
+ const assignmentWhatsapp = await client.assignments.create(documentId, {
684
816
  method: 'virtual',
685
817
  signers: [{
686
- id: phoneSigner.id,
818
+ id: signatarioTelefone.id,
687
819
  verification_method: 'Whatsapp',
688
820
  notification_methods: ['Whatsapp'],
689
821
  }],
690
822
  });
691
823
 
692
- const notices = await client.assignments.listWhatsAppNotifications(
824
+ const avisos = await client.assignments.listWhatsAppNotifications(
693
825
  documentId,
694
- whatsappAssignment.id,
826
+ assignmentWhatsapp.id,
695
827
  );
696
828
  // IWhatsAppNotification[]:
697
829
  // [{ sent_at, header, body, buttons: [{ text, url? }], phone_number, signer_id }]
698
830
  ```
699
831
 
700
- The high-level helper selects this paid branch for a signer that has a phone
701
- number but no email. Button URLs can contain signer credentials; do not log or
702
- 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.
835
+
836
+ #### Certificado digital ICP-Brasil
703
837
 
704
- #### ICP-Brasil digital certificate
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.
705
841
 
706
- `DigitalCertificate` requires the account feature, a CPF or CNPJ in the
707
- signer's `government_id`, and exactly one certificate signer in that signing
708
- step. It costs two credits per certificate signer in addition to the selected
709
- notification cost.
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.
710
849
 
711
850
  ```ts
712
- const certificateSigner = await client.signers.update(signerId, {
851
+ const signatarioCertificado = await client.signers.update(signerId, {
713
852
  government_id: '390.533.447-05',
714
853
  });
715
854
 
716
- const certificateCost = await client.assignments.estimateCost(documentId, {
855
+ const custoCertificado = await client.assignments.estimateCost(documentId, {
717
856
  method: 'virtual',
718
857
  signers: [{ verification_method: 'DigitalCertificate', notification_methods: ['Email'] }],
719
858
  });
@@ -721,7 +860,7 @@ const certificateCost = await client.assignments.estimateCost(documentId, {
721
860
  await client.assignments.create(documentId, {
722
861
  method: 'virtual',
723
862
  signers: [{
724
- id: certificateSigner.id,
863
+ id: signatarioCertificado.id,
725
864
  step: 1,
726
865
  verification_method: 'DigitalCertificate',
727
866
  notification_methods: ['Email'],
@@ -729,27 +868,37 @@ await client.assignments.create(documentId, {
729
868
  });
730
869
  ```
731
870
 
732
- Before opening the assignment, the signer must confirm identity data and accept
733
- terms with `confirmData(..., { has_accepted_terms: true })` or `acceptTerms()`.
734
- The regular `sign()` endpoint rejects certificate signers; they complete the
735
- ICP-Brasil flow through Assinafy's browser integration. After completion,
736
- `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.
737
887
 
738
888
  ### Templates
739
889
 
740
- `templates.list()` is part of the current OpenAPI document. Existing
741
- integrations can also use five template-management routes—`create`, `get`,
742
- `update`, `delete`, and `downloadPage`—that are absent from that document. See
743
- [compatibility notes](docs/COMPATIBILITY.md#template-management-extensions).
744
- Template status casing can vary by deployment; normalize with
745
- `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.
746
895
 
747
896
  ```ts
748
- // Create a template by uploading a PDF (multipart). The template starts in
749
- // an uploaded state and becomes ready once its pages are processed.
750
- const created = await client.templates.create(
751
- { filePath: './nda.pdf' }, // or { buffer, fileName: 'nda.pdf' }
752
- { 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' },
753
902
  );
754
903
  // →
755
904
  // {
@@ -760,27 +909,27 @@ const created = await client.templates.create(
760
909
  // }
761
910
 
762
911
  const { data, meta } = await client.templates.list({ search: 'NDA', 'per-page': 20 });
763
- const template = await client.templates.get(created.id); // includes pages[] + default_document_tags
764
- await client.templates.update(created.id, { name: 'NDA v2', message: 'Please sign' });
765
- const firstPage = template.pages?.[0];
766
- if (firstPage) await client.templates.downloadPage(created.id, firstPage.id); // → Buffer (JPEG)
767
- await client.templates.delete(created.id);
768
-
769
- // Create a document from an existing, configured template. Fresh uploads have
770
- // only an Editor role; add signer roles in Assinafy's editor first.
771
- const configured = await client.templates.get(templateId);
772
- const signerRole = configured.roles?.find(
773
- (role) => typeof role.assignment_type === 'string'
774
- && role.assignment_type.toLowerCase() !== 'editor',
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',
775
924
  );
776
- 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');
777
926
  await client.documents.createFromTemplate(
778
927
  templateId,
779
- [{ role_id: signerRole.id, id: signerId, verification_method: 'Email', notification_methods: ['Email'] }],
780
- { 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.' },
781
930
  );
782
931
 
783
- // Estimate the cost before creating → ICostEstimate
932
+ // Estime o custo antes de criar → ICostEstimate
784
933
  await client.documents.estimateCostFromTemplate(templateId, [
785
934
  { role_id: 'role_id', verification_method: 'Email', notification_methods: ['Email'] },
786
935
  ]);
@@ -788,41 +937,42 @@ await client.documents.estimateCostFromTemplate(templateId, [
788
937
  // has_sufficient_resources: true, blocking_reason: null, breakdown: [], … }
789
938
  ```
790
939
 
791
- Template signer descriptors also accept
792
- `verification_method: 'DigitalCertificate'` with the same prerequisites under
793
- [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).
794
943
 
795
- Template creation only uploads the PDF and provisions the default editor role
796
- configure roles/fields in the Assinafy editor (or the web UI) afterwards.
797
- The `download_url` values in template page objects are protected URLs; prefer
798
- `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.
799
947
 
800
948
  ### Tags
801
949
 
802
- 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).
803
952
 
804
953
  ```ts
805
- await client.tags.list({ search: 'contract' }); // ITag[]
806
- const tag = await client.tags.create({ name: 'Contracts', color: 'ff8800' });
807
- await client.tags.update(tag.id, { name: 'Sales Contracts' });
808
- await client.tags.update(tag.id, { color: null }); // clear the color
809
- await client.tags.delete(tag.id); // 409 if still attached
810
- 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
811
960
  ```
812
961
 
813
- 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)).
814
964
 
815
965
  ### Workspaces
816
966
 
817
- The official create/update request schemas define `name` and
818
- `notification_sender_type`. The sandbox also accepts the color fields shown
819
- 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.
820
970
 
821
971
  ```ts
822
- // Colours are 6-char hex WITHOUT a leading '#' (unlike tags, which strip it).
823
- // '#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.
824
974
  await client.workspaces.create({
825
- name: 'My Workspace',
975
+ name: 'Meu Workspace',
826
976
  notification_sender_type: 'Account',
827
977
  primary_color: 'ff0066',
828
978
  secondary_color: '0066ff',
@@ -831,13 +981,13 @@ await client.workspaces.create({
831
981
  await client.workspaces.list();
832
982
  await client.workspaces.get(accountId);
833
983
  await client.workspaces.update(accountId, {
834
- name: 'Renamed',
984
+ name: 'Renomeado',
835
985
  notification_sender_type: 'User',
836
986
  primary_color: '112233',
837
987
  });
838
988
 
839
- // Branding
840
- const theme = await client.workspaces.getTheme(accountId);
989
+ // Identidade visual
990
+ const tema = await client.workspaces.getTheme(accountId);
841
991
  const logo = await client.workspaces.downloadLogo(accountId); // Buffer
842
992
  await client.workspaces.uploadLogo(accountId, { filePath: './logo.png' });
843
993
  await client.workspaces.uploadLogo(accountId, {
@@ -847,7 +997,7 @@ await client.workspaces.uploadLogo(accountId, {
847
997
  });
848
998
  await client.workspaces.deleteLogo(accountId);
849
999
 
850
- // 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.
851
1001
  await client.workspaces.getStats(accountId);
852
1002
  await client.workspaces.getStats(accountId, {
853
1003
  granularity: 'daily',
@@ -855,97 +1005,100 @@ await client.workspaces.getStats(accountId, {
855
1005
  });
856
1006
 
857
1007
  await client.workspaces.delete(accountId);
858
- // `force` cancels an active paid subscription as part of account deletion. It
859
- // is not a general bypass for unrelated deletion restrictions.
860
- 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 });
861
1011
  ```
862
1012
 
863
- ### Field definitions
1013
+ ### Definições de campo
864
1014
 
865
- Custom field types used by `collect`-method assignments.
1015
+ Tipos de campo personalizados usados por assignments com método `collect`.
866
1016
 
867
1017
  ```ts
868
- await client.fields.create({ type: 'text', name: 'Contract Number' });
1018
+ await client.fields.create({ type: 'text', name: 'Número do contrato' });
869
1019
  await client.fields.list({ include_inactive: true, include_standard: true });
870
1020
  await client.fields.get(fieldId);
871
- await client.fields.update(fieldId, { name: 'Updated Name' });
1021
+ await client.fields.update(fieldId, { name: 'Nome atualizado' });
872
1022
  await client.fields.delete(fieldId);
873
1023
 
874
- // 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)
875
1025
  await client.fields.validate(fieldId, '400.676.228-36', { signerAccessCode });
876
1026
 
877
- // Validate multiple values at once
1027
+ // Valida vários valores de uma vez
878
1028
  await client.fields.validateMultiple(
879
1029
  [
880
1030
  { field_id: 'f1', value: '1111111111111' },
881
- { field_id: 'f2', value: 'value@example.com' },
1031
+ { field_id: 'f2', value: 'valor@exemplo.com.br' },
882
1032
  ],
883
1033
  { signerAccessCode },
884
1034
  );
885
1035
 
886
- // Catalog of every field type the platform recognises
1036
+ // Catálogo de todos os tipos de campo reconhecidos pela plataforma
887
1037
  await client.fields.listTypes();
888
1038
  ```
889
1039
 
890
- ### Authentication / API key management
1040
+ ### Autenticação / gestão de chaves de API
891
1041
 
892
- 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).
893
1045
 
894
1046
  ```ts
895
- // Browser OAuth: redirect the user to this URL. The callback helper returns the
896
- // Assinafy callback URL for provider configuration; neither follows a redirect.
897
- const oauthStart = client.auth.getSocialLoginUrl('google');
898
- 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();
899
1052
 
900
- 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');
901
1054
  await client.auth.socialLogin({ provider: 'google', token: 'google-id-token', has_accepted_terms: true });
902
1055
  await client.auth.linkSocialLogin({ provider: 'google', token: 'google-id-token' });
903
1056
 
904
- // Personal API key
905
- await client.auth.createApiKey('current-password');
906
- 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
907
1060
  await client.auth.deleteApiKey();
908
1061
 
909
- // Password lifecycle
910
- await client.auth.changePassword({ email, password: 'current', new_password: 'next' });
911
- await client.auth.requestPasswordReset('me@example.com');
912
- 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' });
913
1066
  ```
914
1067
 
915
- ### Authenticated user
1068
+ ### Usuário autenticado
916
1069
 
917
1070
  ```ts
918
- const user = await client.users.getCurrent();
1071
+ const usuario = await client.users.getCurrent();
919
1072
  // → { id, name, email, telephone, government_id, is_email_verified,
920
1073
  // has_accepted_terms, created_at, to_be_deleted_at }
921
1074
 
922
- // Cross-account document funnel, latest 12 monthly periods by default.
923
- const monthly = await client.users.getStats();
924
- 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({
925
1078
  granularity: 'daily',
926
1079
  month: '2026-06',
927
1080
  });
928
- // Each row includes period, upload/send/certification totals, notification
929
- // counts for email/WhatsApp/bypass, verification counts for
930
- // 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.
931
1084
 
932
- const preferences = await client.users.getNotificationPreferences();
1085
+ const preferencias = await client.users.getNotificationPreferences();
933
1086
  await client.users.updateNotificationPreferences({
934
1087
  SignerDeclined: false,
935
1088
  DocumentExpired: false,
936
1089
  });
937
- // Updates merge: omitted keys keep their current value. Both methods return
938
- // 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.
939
1092
  ```
940
1093
 
941
1094
  ### Webhooks
942
1095
 
943
1096
  ```ts
944
1097
  await client.webhooks.register({
945
- url: 'https://example.com/webhooks/assinafy',
946
- email: 'admin@example.com',
1098
+ url: 'https://exemplo.com.br/webhooks/assinafy',
1099
+ email: 'admin@exemplo.com.br',
947
1100
  is_active: true,
948
- // events defaults to the current SDK default set below
1101
+ // `events` assume o conjunto padrão do SDK, abaixo
949
1102
  events: [
950
1103
  'document_ready',
951
1104
  'document_prepared',
@@ -956,26 +1109,25 @@ await client.webhooks.register({
956
1109
  });
957
1110
 
958
1111
  await client.webhooks.get(); // IWebhookSubscription | null
959
- await client.webhooks.inactivate(); // stop deliveries (no delete route exists)
1112
+ await client.webhooks.inactivate(); // interrompe entregas (não existe rota de exclusão)
960
1113
  await client.webhooks.listEventTypes();
961
- const history = await client.webhooks.listDispatches({
1114
+ const historico = await client.webhooks.listDispatches({
962
1115
  delivered: false,
963
1116
  page: 1,
964
1117
  'per-page': 20,
965
1118
  }); // { data: IWebhookDispatch[], meta?: PaginationMeta }
966
- const retried = await client.webhooks.retryDispatch(dispatchId); // IWebhookDispatch
1119
+ const reenviado = await client.webhooks.retryDispatch(dispatchId); // IWebhookDispatch
967
1120
  ```
968
1121
 
969
- `register` sends `{ events, is_active, url, email }` and returns
970
- `{ events, is_active, url, email, updated_at? }`. Assinafy delivers each event
971
- as an HTTP `POST` with `Content-Type: application/json` and `Connection: close`.
972
- Any `2xx` is success. There are at most two automatic attempts, separated by
973
- three seconds. After ten consecutive failed events, ordinary delivery pauses
974
- and about 5% of later events are attempted until one succeeds; use
975
- `retryDispatch()` for an immediate manual redelivery. The dispatch history
976
- 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.
977
1129
 
978
- Each history or retry result is an `IWebhookDispatch`:
1130
+ Cada item do histórico (ou resultado de reenvio) é um `IWebhookDispatch`:
979
1131
 
980
1132
  ```ts
981
1133
  {
@@ -994,25 +1146,25 @@ Each history or retry result is an `IWebhookDispatch`:
994
1146
  }
995
1147
  ```
996
1148
 
997
- Every delivery body uses this envelope:
1149
+ Todo corpo de entrega usa este envelope:
998
1150
 
999
1151
  ```ts
1000
1152
  {
1001
- id: number; // use for idempotent processing
1153
+ id: number; // use para processamento idempotente
1002
1154
  event: string;
1003
1155
  message: string | null;
1004
1156
  payload: Record<string, unknown> | null;
1005
1157
  origin: { ip?: string; 'user-agent'?: string } | null;
1006
- created_at: number; // Unix seconds
1158
+ created_at: number; // segundos Unix
1007
1159
  subject: { type: 'User' | 'Signer' | 'Account' | 'Document' | 'Template'; [key: string]: unknown };
1008
1160
  object: { type: 'User' | 'Signer' | 'Account' | 'Document' | 'Template'; [key: string]: unknown };
1009
1161
  account_id: string;
1010
1162
  }
1011
1163
  ```
1012
1164
 
1013
- Event-specific values are:
1165
+ Os valores por evento são:
1014
1166
 
1015
- | `event` | `subject.type` | `object.type` | `payload` keys |
1167
+ | `event` | `subject.type` | `object.type` | chaves de `payload` |
1016
1168
  | --- | --- | --- | --- |
1017
1169
  | `document_uploaded` | `User` | `Document` | — |
1018
1170
  | `document_metadata_ready` | `User` | `Document` | — |
@@ -1020,7 +1172,7 @@ Event-specific values are:
1020
1172
  | `assignment_created` | `User` | `Document` | `user_name`, `user_email`, `user_telephone` |
1021
1173
  | `document_ready` | `Account` | `Document` | — |
1022
1174
  | `document_processing_failed` | `Account` | `Document` | `error_message` |
1023
- | `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 |
1024
1176
  | `signer_created` | `User` | `Signer` | `signer_full_name` |
1025
1177
  | `signer_email_verified` | `Signer` | `Document` | `signer_email` |
1026
1178
  | `signer_whatsapp_verified` | `Signer` | `Document` | `signer_whatsapp_phone_number` |
@@ -1033,21 +1185,19 @@ Event-specific values are:
1033
1185
  | `template_processed` | `User` | `Template` | — |
1034
1186
  | `template_processing_failed` | `Account` | `Template` | `error_message` |
1035
1187
 
1036
- `payload`, `subject`, and `object` are event-dependent. Accept unknown fields
1037
- for forward compatibility and acknowledge only after durable, idempotent
1038
- processing. Non-`2xx` responses, timeouts, and connection failures all count as
1039
- failed deliveries. `assignment_created` and `document_metadata_ready` have no
1040
- guaranteed ordering. For account entities, Assinafy removes the `integration`
1041
- 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.
1042
1193
 
1043
- ### Webhook verification
1194
+ ### Verificação de webhooks
1044
1195
 
1045
- `WebhookVerifier` is an opt-in HMAC-SHA256 utility for integrations whose
1046
- Assinafy environment provides a shared secret and signature header. The
1047
- current official OpenAPI document does **not** define a webhook signature
1048
- scheme or header name. Confirm the delivery contract for your environment
1049
- before enabling this check; do not reject production callbacks based on an
1050
- 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.
1051
1201
 
1052
1202
  ```ts
1053
1203
  import express from 'express';
@@ -1055,39 +1205,39 @@ import express from 'express';
1055
1205
  const webhookSecret = process.env.ASSINAFY_WEBHOOK_SECRET;
1056
1206
  const signatureHeader = process.env.ASSINAFY_SIGNATURE_HEADER;
1057
1207
  if (!webhookSecret || !signatureHeader) {
1058
- 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');
1059
1209
  }
1060
1210
 
1061
1211
  const webhookClient = new AssinafyClient({ webhookSecret });
1062
1212
 
1063
1213
  app.post('/webhooks/assinafy', express.raw({ type: 'application/json' }), (req, res) => {
1064
- const signature = req.header(signatureHeader) ?? '';
1065
- const rawBody = req.body as Buffer;
1214
+ const assinatura = req.header(signatureHeader) ?? '';
1215
+ const corpoBruto = req.body as Buffer;
1066
1216
 
1067
- if (!webhookClient.webhookVerifier.verify(rawBody, signature)) {
1068
- return res.status(401).send('Invalid signature');
1217
+ if (!webhookClient.webhookVerifier.verify(corpoBruto, assinatura)) {
1218
+ return res.status(401).send('Assinatura inválida');
1069
1219
  }
1070
1220
 
1071
- const event = webhookClient.webhookVerifier.extractEvent(rawBody);
1072
- const type = webhookClient.webhookVerifier.getEventType(event);
1073
- 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);
1074
1224
 
1075
- switch (type) {
1076
- case 'document_ready': break;
1077
- case 'signer_signed_document': break;
1078
- case 'signer_rejected_document': break;
1079
- 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;
1080
1230
  }
1081
1231
  res.sendStatus(200);
1082
1232
  });
1083
1233
  ```
1084
1234
 
1085
- ### Signer-side endpoints
1235
+ ### Endpoints do signatário
1086
1236
 
1087
- For building custom signer portals. Most calls require the `signer-access-code`
1088
- URL parameter that Assinafy emails/whatsapps to the signer. Artifact download is
1089
- the documented public exception; its optional fourth access-code argument exists
1090
- 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.
1091
1241
 
1092
1242
  ```ts
1093
1243
  await client.signerDocuments.self(accessCode);
@@ -1095,119 +1245,169 @@ await client.signerDocuments.verifyEmail({ signerAccessCode: accessCode, verific
1095
1245
 
1096
1246
  await client.signerDocuments.getCurrent(signerId, accessCode);
1097
1247
  const { data } = await client.signerDocuments.list(signerId, accessCode, { 'per-page': 20 });
1098
- // Signer-side counterpart of documents.search(), authorised by the access code.
1099
- const found = await client.signerDocuments.search(signerId, accessCode, 'invoice');
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');
1100
1250
  await client.signerDocuments.download(signerId, documentId, 'original');
1101
- // 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.
1102
1252
  await client.signerDocuments.download(signerId, documentId, 'pades');
1103
1253
 
1104
1254
  await client.signerDocuments.confirmData(documentId, accessCode, {
1105
- email: 'me@example.com',
1106
- full_name: 'Example Signer',
1255
+ email: 'eu@exemplo.com.br',
1256
+ full_name: 'Signatário Exemplo',
1107
1257
  government_id: '123.456.789-00',
1108
1258
  has_accepted_terms: true,
1109
1259
  });
1110
- // Alternatively, accept terms separately before getAssignment():
1260
+ // Como alternativa, aceite os termos separadamente antes de getAssignment():
1111
1261
  // await client.signerDocuments.acceptTerms(accessCode);
1112
1262
 
1113
- // Signature image management ({ reuse: true } persists it for future documents)
1263
+ // Gestão da imagem de assinatura ({ reuse: true } guarda para documentos futuros)
1114
1264
  await client.signerDocuments.uploadSignature(accessCode, pngBuffer, { imageType: 'signature', reuse: true });
1115
1265
  await client.signerDocuments.downloadSignature(accessCode, 'signature');
1116
1266
 
1117
- // Sign / decline
1118
- const signable = await client.signerDocuments.getAssignment(accessCode);
1119
- // `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.
1120
1270
  await client.signerDocuments.sign(documentId, assignmentId, accessCode, [
1121
- { itemId, fieldId, pageId, value: 'Signed by John' },
1271
+ { itemId, fieldId, pageId, value: 'Assinado por João' },
1122
1272
  ]);
1123
- await client.signerDocuments.decline(documentId, assignmentId, accessCode, 'Not authorized');
1273
+ await client.signerDocuments.decline(documentId, assignmentId, accessCode, 'Sem autorização');
1124
1274
 
1125
- // `signMultiple()` is for virtual assignments only.
1275
+ // `signMultiple()` é para assignments virtual.
1126
1276
  await client.signerDocuments.signMultiple(['doc-1', 'doc-2'], accessCode);
1127
- await client.signerDocuments.declineMultiple(['doc-1'], 'Unfavorable terms', accessCode);
1277
+ await client.signerDocuments.declineMultiple(['doc-1'], 'Condições desfavoráveis', accessCode);
1128
1278
  ```
1129
1279
 
1130
- `sign()` also requires virtual signers to have confirmed their data first, but
1131
- virtual assignments should normally use `signMultiple()`. Certificate signers
1132
- 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).
1133
1283
 
1134
- ## High-level helper
1284
+ ## Helper de alto nível
1135
1285
 
1136
- Uploads a PDF, reuses or creates signers by email, creates a virtual assignment
1137
- 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.
1138
1288
 
1139
1289
  ```ts
1140
- const result = await client.uploadAndRequestSignatures({
1141
- source: { filePath: './contract.pdf' },
1290
+ const resultado = await client.uploadAndRequestSignatures({
1291
+ source: { filePath: './contrato.pdf' },
1142
1292
  signers: [
1143
- { name: 'John', email: 'john@example.com' },
1144
- { name: 'Jane', email: 'jane@example.com' },
1293
+ { name: 'João', email: 'joao@exemplo.com.br' },
1294
+ { name: 'Maria', email: 'maria@exemplo.com.br' },
1145
1295
  ],
1146
- message: 'Please sign',
1147
- 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
1148
1298
  waitForReady: true,
1149
1299
  waitOptions: { maxWaitMs: 30_000, pollIntervalMs: 1_000 },
1150
1300
  expiresAt: '2027-12-31T00:00:00Z',
1151
- copyReceivers: ['existing-copy-recipient-signer-id'],
1301
+ copyReceivers: ['id-de-signatario-em-copia'],
1152
1302
  });
1153
1303
 
1154
- result.document; // fully-processed IDocumentDetailsResponse (waitForReady: true, the default);
1155
- // the raw IDocumentUploadResponse when waitForReady: false
1156
- result.assignment; // IAssignment
1157
- 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[]
1158
1308
  ```
1159
1309
 
1160
- `waitForReady: false` skips post-assignment polling and returns the initial
1161
- upload response. With the default `true`, the helper creates the assignment first and
1162
- then waits for the current document details. Both production and sandbox allow
1163
- virtual assignments in `uploaded` and `metadata_processing` and promote them
1164
- automatically; only `collect` assignments require rendered pages.
1165
- Every signer above uses the default email channel. A phone-only signer selects
1166
- the paid WhatsApp branch described earlier. `copyReceivers` accepts existing
1167
- signer IDs, not email addresses; check the returned assignment before treating
1168
- 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.
1169
1317
 
1170
- The helper is not transactional. A post-assignment polling error includes the
1171
- created `documentId`, `assignmentId`, and `signerIds` in its `context` (and in
1172
- `ValidationError.errors` for timeouts); inspect those IDs before deciding
1173
- 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.
1174
1321
 
1175
- ## Errors
1322
+ ## Erros
1176
1323
 
1177
- HTTP methods reject with an `AssinafyError` subclass. Synchronous helpers such
1178
- 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` |
1179
1334
 
1180
1335
  ```ts
1181
- import { ApiError, ValidationError, NetworkError, AssinafyError } from '@assinafy/sdk';
1336
+ import { ApiError, OAuthError, ValidationError, NetworkError, AssinafyError } from '@assinafy/sdk';
1182
1337
 
1183
1338
  try {
1184
1339
  await client.documents.upload({ filePath: './x.pdf' });
1185
1340
  } catch (err) {
1186
1341
  if (err instanceof ValidationError) {
1187
- 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);
1188
1346
  } else if (err instanceof ApiError) {
1189
- 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.
1190
1350
  } else if (err instanceof NetworkError) {
1191
- console.error('Network error:', err.message);
1351
+ console.error('Erro de rede:', err.message);
1192
1352
  } else if (err instanceof AssinafyError) {
1193
- console.error('SDK error:', err.message, err.context);
1353
+ console.error('Erro do SDK:', err.message, err.context);
1194
1354
  }
1195
1355
  }
1196
1356
  ```
1197
1357
 
1198
- ## 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
1199
1390
 
1200
1391
  ```bash
1201
1392
  bun install --frozen-lockfile
1202
- bun run typecheck # source, script, and test type checks
1393
+ bun run typecheck # tipos de src, scripts e testes
1203
1394
  bun run lint
1204
- bun test # bun:test suites
1395
+ bun test # suítes bun:test
1205
1396
  bun run test:coverage
1206
1397
  bun run build # tsup → dist/ (CJS + ESM + .d.ts)
1207
1398
  bun run lint:pkg # publint + arethetypeswrong
1208
- 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
1209
1401
  ```
1210
1402
 
1211
- ## 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
1212
1412
 
1213
- MIT
1413
+ Distribuído sob a licença [MIT](LICENSE).