@lexnode/legal-data-sdk 1.30.0 → 1.32.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
@@ -5,20 +5,20 @@ processos e movimentações, publicações dos diários oficiais, jurisprudênci
5
5
  monitores e webhooks.
6
6
 
7
7
  O pacote traz **os tipos gerados do contrato OpenAPI** e um **cliente de
8
- runtime** construído sobre eles: transporte com os cabeçalhos certos, erros
8
+ runtime** construído sobre eles: chamadas com os cabeçalhos certos, erros
9
9
  classificados, verificação de assinatura de webhook, espera de busca
10
- assíncrona, leitura do alcance da fonte e a composição de vigília com vários
10
+ assíncrona, leitura do frescor da fonte e a vigília de um processo para vários
11
11
  interessados.
12
12
 
13
13
  - **Sem dependências de runtime.** `fetch` global e `node:crypto`.
14
14
  - **Node >= 20**, ESM.
15
15
 
16
- > ⚠️ **Servidor apenas, e só por TLS.** A credencial é uma chave de **conta**:
17
- > no browser ela está publicada, e em `http://` ela é lida por quem estiver no
18
- > caminho — uma `baseUrl` sem https é recusada no construtor (host local é a
19
- > exceção, para desenvolvimento). Um app com usuários finais fala com o próprio
20
- > backend, e é o backend que fala com o motor — atribuindo cada chamada ao seu
21
- > sub-tenant pelo cabeçalho `X-Office-Id`.
16
+ > ⚠️ **Só no servidor, e só por TLS.** A credencial é uma chave de **conta**:
17
+ > no browser ela fica exposta, e em `http://` qualquer um no caminho a lê —
18
+ > uma `baseUrl` sem https é recusada no construtor (exceto no host local, para
19
+ > desenvolvimento). Um app com usuários finais chama o próprio backend, e o
20
+ > backend chama a API, atribuindo cada chamada ao sub-tenant dela (um
21
+ > escritório, um usuário final) pelo cabeçalho `X-Office-Id`.
22
22
 
23
23
  ## Instalação
24
24
 
@@ -34,33 +34,33 @@ import { createLexNodeClient } from "@lexnode/legal-data-sdk";
34
34
  const lexnode = createLexNodeClient({
35
35
  baseUrl: "https://api.lexnode.com.br",
36
36
  apiKey: process.env.LEXNODE_API_KEY!,
37
- // Sub-tenant padrão; cada chamada pode trocá-lo.
37
+ // Escritório padrão; cada chamada pode trocá-lo.
38
38
  officeId: "escritorio-01",
39
39
  timeoutMs: 30_000,
40
40
  userAgent: "meu-app/1.0",
41
41
  });
42
42
 
43
43
  const { data, meta } = await lexnode.processes.get("00012345620268260100", {
44
- tribunalAlias: "api_publica_tjsp",
44
+ tribunalAlias: "TJSP",
45
45
  });
46
46
 
47
47
  console.log(data.formattedNumber, meta.credits, meta.rateLimit?.remaining);
48
48
  ```
49
49
 
50
- Toda chamada manda `Authorization: Bearer`, `X-Request-Id`, `X-Trace-Id` e,
51
- quando há um, `X-Office-Id`; respeita o prazo do cliente **combinado** com o
52
- `signal` de quem chamou; e devolve `{ data, meta }` tipados pela rota. O `meta`
53
- é o do contrato mais `status` (o código HTTP) e `rateLimit` (os cabeçalhos
54
- `RateLimit-*`, lidos **inclusive no sucesso**, e o `Retry-After` da recusa).
50
+ Toda chamada envia `Authorization: Bearer`, `X-Request-Id`, `X-Trace-Id` e,
51
+ quando houver, `X-Office-Id`; respeita o prazo **e** o `signal` que você
52
+ passar, o que vier primeiro; e devolve `{ data, meta }` tipados pela rota. O
53
+ `meta` é o do contrato mais `status` (o código HTTP) e `rateLimit` (os
54
+ cabeçalhos `RateLimit-*`, lidos **também no sucesso**, e o `Retry-After` da
55
+ recusa).
55
56
 
56
- Em `rateLimit`, use `remaining` para desacelerar: `resetSeconds` repete o que o
57
- cabeçalho `RateLimit-Reset` trouxe, e o motor lhe dá dois sentidos — segundos
58
- até reabrir na recusa, comprimento da janela (60 s) no sucesso.
57
+ Em `rateLimit`, use `remaining` para desacelerar. `resetSeconds` repete o
58
+ cabeçalho `RateLimit-Reset`, que tem dois sentidos: na recusa, os segundos até
59
+ a janela reabrir; no sucesso, a duração da janela (60 s).
59
60
 
60
61
  O pacote **não valida a resposta em tempo de execução**: `data` e `meta` chegam
61
- tipados pelo contrato, e o que garante a correspondência é a versão fixada, não
62
- uma conferência a cada chamada. Quem precisa de barreira na borda — porque o
63
- dado alimenta um banco, por exemplo — valida o que consome.
62
+ tipados pelo contrato da versão instalada. Se o dado alimenta um banco, por
63
+ exemplo, valide o que consumir.
64
64
 
65
65
  ### Métodos nomeados e a chamada genérica
66
66
 
@@ -80,8 +80,8 @@ await lexnode.webhooks.unsubscribedEventTypes();
80
80
  await lexnode.usage.get();
81
81
  ```
82
82
 
83
- Qualquer rota do contrato — inclusive uma que o pacote ainda não batizou — sai
84
- pela chamada genérica, tipada por `paths`:
83
+ Qualquer rota do contrato, inclusive as que não têm método nomeado, sai pela
84
+ chamada genérica, tipada por `paths`:
85
85
 
86
86
  ```ts
87
87
  const { data } = await lexnode.request("/v1/precedents", "get", {
@@ -89,11 +89,37 @@ const { data } = await lexnode.request("/v1/precedents", "get", {
89
89
  });
90
90
  ```
91
91
 
92
+ Para consultar também os atos datados do calendário forense:
93
+
94
+ ```ts
95
+ const { data: calendar } = await lexnode.request("/v1/court-calendar", "get", {
96
+ params: {
97
+ query: {
98
+ court: "TJSP",
99
+ municipalityIbgeCode: 3509502,
100
+ from: "2026-06-01",
101
+ to: "2026-06-30",
102
+ includeCatalog: true,
103
+ },
104
+ },
105
+ });
106
+
107
+ for (const reference of calendar.catalog?.references ?? []) {
108
+ console.log(reference.date, reference.provenance.url);
109
+ }
110
+ ```
111
+
112
+ `catalog.references` exige conferir o ato e sua aplicação ao caso antes de excluir
113
+ dias da contagem. Todos os itens são `REFERENCE_ONLY`; os provisórios identificam
114
+ a fonte secundária. Não os some automaticamente a `entries`. A situação declarada
115
+ é a da coleta em `sourceAsOf`, e a cobertura parcial ou desconhecida não prova
116
+ ausência de feriados nas outras datas. Sem `includeCatalog`, esse bloco não vem.
117
+
92
118
  ### Páginas
93
119
 
94
- O motor pagina de três jeitos (cursor no `meta`, cursor no `data`, e
95
- página/tamanho ou deslocamento/teto). `paginate` percorre qualquer um deles, com
96
- teto explícito de páginas — cada página é uma chamada medida:
120
+ A API pagina de três jeitos: cursor no `meta`, cursor no `data`, e página e
121
+ tamanho ou deslocamento e limite. `paginate` percorre qualquer um deles, até
122
+ `maxPages` páginas (padrão 100) — cada página é uma chamada:
97
123
 
98
124
  ```ts
99
125
  import { paginate } from "@lexnode/legal-data-sdk";
@@ -116,7 +142,7 @@ try {
116
142
  error.class; // "RETRYABLE" | "TERMINAL"
117
143
  error.retryable;
118
144
  error.status;
119
- error.upstreamStatus; // o status real da fonte, quando preservado
145
+ error.upstreamStatus; // o status que a fonte respondeu, quando informado
120
146
  error.requestId; // o que o suporte pede
121
147
  error.fieldErrors; // recusa de validação, por campo
122
148
  error.retryAfterSeconds;
@@ -126,17 +152,17 @@ try {
126
152
  ```
127
153
 
128
154
  Prazo esgotado vira `CLIENT_TIMEOUT` (504, retentável); falha de rede vira
129
- `CLIENT_NETWORK_ERROR` (502, retentável); resposta fora do envelope vira
130
- `INVALID_RESPONSE` (502) com o corpo cru truncado na mensagem; o que o
131
- transporte recusa mandar — cabeçalho com quebra de linha, corpo que não
132
- serializa — vira `CLIENT_INVALID_REQUEST` (400, **não** retentável), porque
133
- repetir o que nunca saiu não cura.
155
+ `CLIENT_NETWORK_ERROR` (502, retentável); resposta fora do formato da API vira
156
+ `INVALID_RESPONSE` (502), com o início do corpo na mensagem; e o pedido que não
157
+ se monta — cabeçalho com quebra de linha, corpo que não vira JSON — vira
158
+ `CLIENT_INVALID_REQUEST` (400, **não** retentável), porque não chegou a ser
159
+ enviado.
134
160
 
135
161
  `LEXNODE_ERROR_CODES` traz o catálogo para o `switch`, e `LexNodeErrorCode`
136
- mantém o autocompletar sem fechar a porta a um código novo. Importa na leitura
137
- de processo: o motor responde 404 tanto para `PROCESS_NOT_FOUND` (o processo não
138
- existe na fonte) quanto para `MOVEMENTS_NOT_AVAILABLE` (existe, mas as
139
- movimentações não vêm) — quem trata os dois igual conta a história errada.
162
+ mantém o autocompletar e aceita um código novo da API. Na leitura de processo
163
+ isso importa: a API responde 404 tanto para `PROCESS_NOT_FOUND` (o processo não
164
+ existe na fonte) quanto para `MOVEMENTS_NOT_AVAILABLE` (ele existe, mas as
165
+ movimentações não vieram) — trate os dois pelo `code`.
140
166
 
141
167
  ### Retentativa (opcional, desligada por padrão)
142
168
 
@@ -148,19 +174,22 @@ const lexnode = createLexNodeClient({
148
174
  });
149
175
  ```
150
176
 
151
- Repete só o que é `RETRYABLE`, respeitando `Retry-After`. `GET` e `DELETE` são
152
- repetidos por definição do HTTP. Uma **escrita** só é repetida quando o contrato
153
- declara a repetição segura — hoje `POST /v1/publications/text-searches`, que usa
154
- o `requestId` que você fixar como chave de replay — ou quando você mesmo o
155
- declara na chamada:
177
+ Repete só o que é `RETRYABLE`, respeitando `Retry-After`: quando a recusa pede
178
+ uma espera maior que `maxDelayMs` (8 s por padrão), como a da manutenção
179
+ declarada pela fonte, ela é lançada sem repetição, e `retryAfterSeconds` diz
180
+ quando vale tentar de novo. `GET` e `DELETE` são repetidos por definição do
181
+ HTTP. Uma **escrita** só é repetida quando o contrato declara a repetição
182
+ segura — hoje `POST /v1/publications/text-searches`, que usa o `requestId` que
183
+ você fixar como chave de reenvio — ou quando você mesmo o declara na chamada:
156
184
 
157
185
  ```ts
158
186
  await lexnode.webhooks.endpoints.create(corpo, { idempotent: true });
159
187
  ```
160
188
 
161
- A afirmação é sua: sem ela, um `POST` repetido cria um **segundo** recurso — dois
162
- endpoints para a mesma URL entregam cada evento em dobro, e o segredo do
163
- primeiro fica órfão. Em toda outra rota o `requestId` é só rastreio.
189
+ A declaração é sua: sem ela, um `POST` repetido cria um **segundo** recurso —
190
+ dois endpoints para a mesma URL entregam cada evento em dobro, e o segredo do
191
+ primeiro se perde. Nas outras rotas, o `requestId` só serve para rastrear a
192
+ chamada.
164
193
 
165
194
  A exceção é o `UPSTREAM_TIMEOUT` da leitura de um processo, que a política não
166
195
  repete — ver [a seção da fonte lenta](#ler-um-processo-quando-a-fonte-está-lenta).
@@ -173,15 +202,16 @@ import {
173
202
  webhookRejectionStatus,
174
203
  } from "@lexnode/legal-data-sdk";
175
204
 
176
- // O corpo CRU, nunca um JSON reserializado: o digest é sobre os bytes.
205
+ // O corpo como chegou, nunca um JSON serializado de novo: a assinatura vale
206
+ // para os bytes.
177
207
  const resultado = parseWebhookEvent(
178
208
  rawBody,
179
209
  request.headers,
180
210
  process.env.LEXNODE_WEBHOOK_SIGNING_SECRET!,
181
211
  );
182
212
 
183
- // O status de cada recusa vem junto: um corpo que não se autentica é 401 e não
184
- // volta; segredo ausente é 503, porque a falha é SUA e o motor precisa insistir.
213
+ // Cada recusa tem o seu status: a assinatura que não confere é 401; o segredo
214
+ // ausente no seu receptor é 503.
185
215
  if (!resultado.ok) {
186
216
  return reply.status(webhookRejectionStatus(resultado.reason)).send();
187
217
  }
@@ -210,22 +240,22 @@ switch (resultado.event.type) {
210
240
  }
211
241
  break;
212
242
  case "PROCESS_LOOKUP_COMPLETED":
213
- // Um ponteiro: o processo se lê em payload.pollUrl, sem nova cobrança.
243
+ // Aponta a consulta: o processo se lê em payload.pollUrl, sem nova cobrança.
214
244
  resultado.event.payload.lookupId;
215
245
  break;
216
246
  case "WEBHOOK_TEST":
217
- // A entrega de teste do console. Chega a qualquer endpoint, não se assina
218
- // e não se recusa — é ela que valida o receptor antes do primeiro evento.
247
+ // A entrega de teste (`webhooks.endpoints.test` ou o portal). Chega a
248
+ // qualquer endpoint sem constar de `eventTypes`; use-a para validar o
249
+ // receptor antes do primeiro evento.
219
250
  break;
220
251
  default:
221
252
  break;
222
253
  }
223
254
  ```
224
255
 
225
- O envelope precisa ser endereçável: entrega sem `id` ou sem `occurredAt` é
226
- recusada como `INVALID_ENVELOPE`. O `id` é a chave de idempotência de todo
227
- receptor — aceitá-lo vazio faria a primeira entrega ocupar a única vaga de
228
- deduplicação e todas as seguintes parecerem repetição dela.
256
+ Entrega sem `id` ou sem `occurredAt` é recusada como `INVALID_ENVELOPE`. A
257
+ mesma entrega pode chegar mais de uma vez: use o `id` para descartar a
258
+ repetida.
229
259
 
230
260
  `WEBHOOK_EVENT_TYPES` é o catálogo **assinável** (o que entra em `eventTypes`
231
261
  ao cadastrar um endpoint) e não inclui `WEBHOOK_TEST`, que chega sem ser
@@ -234,22 +264,20 @@ pedido — use `isWebhookTest(event)` para separá-lo.
234
264
  `verifyWebhookSignature(rawBody, headers, secret)` faz só a conferência e
235
265
  devolve o motivo da recusa (`MISSING_SIGNATURE`, `MALFORMED_SIGNATURE`,
236
266
  `TIMESTAMP_MISMATCH`, `TIMESTAMP_OUT_OF_TOLERANCE`, `SIGNATURE_MISMATCH`,
237
- `RAW_BODY_REQUIRED`), em vez de um `false` que faria vários incidentes
238
- diferentes virarem a mesma resposta.
267
+ `RAW_BODY_REQUIRED`), para você tratar cada caso.
239
268
 
240
- O corpo entra como `string` ou `Buffer` — os bytes como chegaram. Passar o JSON
241
- já parseado é recusado como `RAW_BODY_REQUIRED`, e não confundido com
242
- adulteração: reserializar reordena as chaves e o digest deixa de bater.
269
+ O corpo entra como `string` ou `Buffer`, como chegou. Um objeto já convertido
270
+ de JSON é recusado como `RAW_BODY_REQUIRED`: serializar de novo muda a ordem
271
+ das chaves, e a assinatura deixa de conferir.
243
272
 
244
- A janela de replay e o digest saem do `t` que viaja **dentro** da assinatura; o
245
- cabeçalho `x-lexnode-timestamp` é redundante e só é conferido quando vem. Um
246
- receptor escrito à mão que o exigia continua correto — o SDK apenas não faz da
247
- ausência dele um motivo de recusa, porque ela não prova nada.
273
+ A tolerância do instante e o digest usam o `t` que vem **dentro** da
274
+ assinatura. O cabeçalho `x-lexnode-timestamp` repete esse valor: quando vem,
275
+ precisa ser igual; quando falta, não é motivo de recusa.
248
276
 
249
277
  ## Busca síncrona ou assíncrona
250
278
 
251
279
  `POST /v1/processes/search` responde **200** com o resultado pronto ou **202**
252
- com um trabalho enfileirado. O SDK diz qual foi, no tipo:
280
+ com uma busca enfileirada. O SDK diz qual foi, no tipo:
253
281
 
254
282
  ```ts
255
283
  import { searchProcesses, waitForSearchJob } from "@lexnode/legal-data-sdk";
@@ -259,9 +287,8 @@ const desfecho = await searchProcesses(lexnode, { tribunalAlias, number });
259
287
  if (desfecho.kind === "sync") {
260
288
  desfecho.result.items;
261
289
  } else {
262
- // O orçamento é explícito: num ambiente serverless, esperar sem teto gasta
263
- // a requisição inteira. Quem integrou por webhook nem espera — recebe o
264
- // SEARCH_JOB_COMPLETED.
290
+ // Espere com orçamento — num ambiente serverless, dentro do prazo da
291
+ // função — ou receba o SEARCH_JOB_COMPLETED por webhook.
265
292
  const job = await waitForSearchJob(lexnode, desfecho.job.jobId, {
266
293
  budgetMs: 20_000,
267
294
  });
@@ -272,30 +299,30 @@ if (desfecho.kind === "sync") {
272
299
  ## Um número, várias instâncias
273
300
 
274
301
  A fonte processual guarda um documento por instância do processo — 1º grau, 2º
275
- grau, turma recursal… —, às vezes dois do mesmo grau, e o motor guarda cada um
276
- na sua instância.
302
+ grau, turma recursal… —, às vezes dois do mesmo grau, e o LexNode guarda cada
303
+ um na sua instância.
277
304
 
278
305
  **O topo não troca.** O topo da ficha (`data`) e do retrato (`data.process` e
279
306
  `data.movements`) é sempre a instância principal do número, com o grau em
280
307
  `degree`: a mesma de uma leitura para a outra, qualquer que seja a ordem em que
281
308
  a fonte devolve os documentos. Ela só muda quando uma resposta completa da
282
- fonte pelo número deixa de trazê-la. Quem compara movimentos pela chave não vê
283
- o histórico de outro grau aparecer como novo no topo — e o topo pode não ser o
309
+ fonte pelo número deixa de trazê-la. Se você compara movimentos pela chave, o
310
+ histórico de outro grau não aparece como novo no topo — e o topo pode não ser o
284
311
  grau que anda: o 2º grau guardado primeiro continua no topo enquanto o 1º se
285
312
  move.
286
313
 
287
314
  **Todos os graus em `instances`.** A ficha e o retrato listam em `instances`
288
- todos os documentos que o motor guarda do número, a principal primeiro
315
+ todos os documentos guardados do número, a principal primeiro
289
316
  (`primary: true`). Cada um diz de onde veio nesta resposta (`origin`: `SOURCE`
290
- quando a fonte o trouxe, `LAKE` quando saiu do acervo), quando foi buscado
291
- (`fetchedAt`) e desde quando a resposta completa da fonte vem sem ele
292
- (`absentSince`). No retrato, cada instância traz os movimentos da última captura
293
- dela, do mais recente ao mais antigo e sem o payload bruto da fonte; a que o
294
- motor nunca capturou vem com `movementsCaptured: false` e `movements` nulo, nunca
295
- com lista vazia, que diria que o grau não andou. Todos os documentos levam a
296
- classificação do número inteiro — basta um em segredo de justiça para todos
297
- saírem marcados —, e a ficha retida pelo controle de confidencialidade sai sem a
298
- lista.
317
+ quando a fonte o trouxe, `LAKE` quando veio da cópia guardada), quando foi
318
+ buscado (`fetchedAt`) e desde quando a resposta completa da fonte vem sem ele
319
+ (`absentSince`). No retrato, cada instância traz os movimentos da última
320
+ captura dela, do mais recente ao mais antigo e sem o payload bruto da fonte; a
321
+ instância cujos movimentos nunca foram capturados vem com
322
+ `movementsCaptured: false` e `movements` nulo, nunca com lista vazia, que diria
323
+ que o grau não andou. Todos os documentos levam a classificação do número
324
+ inteiro — basta um em segredo de justiça para todos saírem marcados —, e a
325
+ ficha retida pelo controle de confidencialidade sai sem a lista.
299
326
 
300
327
  ```ts
301
328
  const { data } = await lexnode.processes.snapshot(numero, { tribunalAlias });
@@ -309,9 +336,9 @@ for (const instancia of data.instances) {
309
336
 
310
337
  No lote (`processes.snapshotBatch`), `instances` traz só o cabeçalho de cada
311
338
  documento; os movimentos de cada instância vêm no retrato de um processo. A
312
- busca por número completo servida do acervo devolve um item por documento que a
313
- fonte ainda traz do número, a principal primeiro; quando eles não cabem em
314
- `limit`, a busca vai à fonte, que pagina.
339
+ busca por número completo respondida com os dados guardados devolve um item por
340
+ documento que a fonte ainda traz do número, a principal primeiro; quando eles
341
+ não cabem em `limit`, a busca vai à fonte, que pagina.
315
342
 
316
343
  **Recorte por grau.** A lista de movimentações aceita `degree` e devolve só os
317
344
  movimentos que o documento daquele grau trouxe na última captura; cada item diz
@@ -327,21 +354,21 @@ data.items; // só o que o 1º grau trouxe
327
354
  ```
328
355
 
329
356
  **O grau novo chega num evento só.** Quando outro grau de um processo assinado
330
- aparece na fonte, o histórico dele entra no acervo sem virar
331
- `PROCESS_MOVEMENT_CREATED` movimento a movimento: chega um
332
- `PROCESS_INSTANCE_DISCOVERED`, com o grau, o órgão julgador e as contagens —
333
- `historyMovementCount` entrou sem aviso próprio, `announcedMovementCount` saiu
334
- também como movimento novo. O `PROCESS_MOVEMENT_CREATED` diz o grau do
335
- movimento mais recente (`degree`, `instanceSourceId`) e quantos movimentos novos
336
- vieram de cada grau (`instances`). Assine os dois: quem assina só o movimento
337
- não fica sabendo do grau que apareceu.
357
+ aparece na fonte, o histórico dele não sai movimento a movimento em
358
+ `PROCESS_MOVEMENT_CREATED`: chega um `PROCESS_INSTANCE_DISCOVERED`, com o grau,
359
+ o órgão julgador e as contagens — `historyMovementCount` não saiu em aviso
360
+ próprio, `announcedMovementCount` saiu também como movimento novo. O
361
+ `PROCESS_MOVEMENT_CREATED` diz o grau do movimento mais recente (`degree`,
362
+ `instanceSourceId`) e quantos movimentos novos vieram de cada grau
363
+ (`instances`). Assine os dois: só com o movimento, você não fica sabendo do
364
+ grau que apareceu.
338
365
 
339
366
  **`meta.otherInstances`: o resumo do que veio.** Quando a resposta da fonte traz
340
367
  outros documentos do número além da principal, `meta.otherInstances` resume cada
341
368
  um com o grau (`grau`), a data em que a fonte o atualizou (`updatedAt`) e o
342
369
  órgão julgador (`courtBody`, quando vem). A lista não sai para processo restrito
343
- e não inclui documento que o motor classificaria como segredo de justiça; os
344
- documentos inteiros estão em `instances`.
370
+ e não inclui documento em segredo de justiça; os documentos inteiros estão em
371
+ `instances`.
345
372
 
346
373
  ```ts
347
374
  for (const outra of meta.otherInstances ?? []) {
@@ -350,24 +377,24 @@ for (const outra of meta.otherInstances ?? []) {
350
377
  ```
351
378
 
352
379
  **Continuação.** Na API nada é cortado: o retrato traz os movimentos guardados
353
- de cada instância, e a lista de movimentações lê do acervo, grau a grau, sem ir
354
- à fonte. No canal para agentes (MCP), `consultar_processo` cabe no teto de uma
355
- resposta: de cada grau vêm os movimentos mais recentes, o grau que se moveu por
356
- último primeiro, e a mesma tool com `grau` e `pagina` traz os seguintes do que o
357
- motor guardou, por 1 crédito e sem ir à fonte — só para quem já leu o processo
358
- ou o acompanha.
380
+ de cada instância, e a lista de movimentações lê os movimentos guardados, grau a
381
+ grau, sem ir à fonte. No canal para agentes (MCP), que não passa por este
382
+ pacote, `consultar_processo` cabe numa resposta: de cada grau vêm os movimentos
383
+ mais recentes, o grau que se moveu por último primeiro, e a mesma tool com
384
+ `grau` e `pagina` traz os seguintes, por 1 crédito e sem ir à fonte — só para
385
+ quem já leu o processo ou o acompanha.
359
386
 
360
387
  ## Ler um processo quando a fonte está lenta
361
388
 
362
- A ficha (`processes.get`) vai à fonte processual quando o acervo não tem o
363
- processo fresco; o retrato (`processes.snapshot`) vai sempre. Os dois só
364
- recorrem ao acervo quando a fonte falha (seção seguinte). A fonte às vezes leva
365
- perto de um minuto por consulta. `waitSeconds` diz quanto o motor espera por
366
- ela: de 10 a 180 segundos, padrão 20 na API.
389
+ A ficha (`processes.get`) vai à fonte processual quando não há uma cópia recente
390
+ guardada; o retrato (`processes.snapshot`) vai sempre. Os dois só respondem com
391
+ a cópia guardada quando a fonte falha (seção seguinte). A fonte às vezes leva
392
+ perto de um minuto por consulta. `waitSeconds` diz quanto a API espera por ela:
393
+ de 10 a 180 segundos, padrão 20.
367
394
 
368
395
  ```ts
369
396
  const { data, meta } = await lexnode.processes.get(numero, {
370
- tribunalAlias: "api_publica_tjsp",
397
+ tribunalAlias: "TJSP",
371
398
  waitSeconds: 120,
372
399
  });
373
400
  ```
@@ -384,27 +411,27 @@ chamada genérica (`request`) nessas duas rotas.
384
411
  duas leituras é lentidão da fonte, não ausência do processo, e a política de
385
412
  retentativa não o repete: a espera já correu inteira, e repetir na hora a
386
413
  dobraria diante da mesma fonte lenta. As outras recusas retentáveis seguem a
387
- política. Para insistir, a decisão é sua — uma nova chamada com espera maior,
388
- ou a vigília do processo (`watchProcess`), que consulta em segundo plano.
414
+ política. Para insistir, a decisão é sua — uma nova chamada com espera maior, a
415
+ leitura que segue em segundo plano (`readProcessWithRecovery`, abaixo) ou a
416
+ vigília do processo (`watchProcess`).
389
417
 
390
418
  **`meta.partialIndex`: o processo veio de parte do índice.** Quando a fonte
391
419
  responde com só parte do índice e o processo estava na parte que respondeu, ele
392
420
  é servido, e `meta.partialIndex` diz quantas partes falharam (`shardsFailed`),
393
421
  de quantas (`shardsTotal`, nulo quando a fonte não as contou) e se o prazo
394
- interno dela estourou (`timedOut`). O documento que o acervo já guardava e não
395
- veio sai em `instances` do acervo (`origin: "LAKE"`), com a data dele, e outro
396
- grau que o acervo não conhece, se existir, pode estar na parte que não
397
- respondeu. A resposta parcial sem o processo é 502 `UPSTREAM_UNAVAILABLE`
398
- retentável, nunca 404.
422
+ interno dela estourou (`timedOut`). O documento já guardado que não veio sai em
423
+ `instances` com `origin: "LAKE"` e a data dele, e outro grau ainda não guardado,
424
+ se existir, pode estar na parte que não respondeu. A resposta parcial sem o
425
+ processo é 502 `UPSTREAM_UNAVAILABLE` retentável, nunca 404.
399
426
 
400
427
  ```ts
401
428
  if (meta.partialIndex) {
402
- // Outro grau deste número que o acervo não conhece pode não ter vindo.
429
+ // Um grau deste número ainda não guardado pode não ter vindo.
403
430
  }
404
431
  ```
405
432
 
406
433
  Antes de recusar a resposta parcial que veio sem o processo porque partes do
407
- índice ficaram fora, o motor consulta a fonte de novo — até duas vezes, com uma
434
+ índice ficaram fora, a API consulta a fonte de novo — até duas vezes, com uma
408
435
  pausa curta, enquanto a espera couber.
409
436
 
410
437
  **Agentes têm a espera deles.** No canal para agentes (MCP), as tools
@@ -415,7 +442,7 @@ mesma faixa e com padrão de 120 s; esse canal não passa pelo SDK.
415
442
 
416
443
  Quando a fonte processual falha — não responde dentro de `waitSeconds`, fica
417
444
  indisponível, responde com parte do índice sem poder servir o processo ou pede
418
- para esperar por volume — e o acervo guarda uma cópia do processo, a ficha
445
+ para esperar por volume — e há uma cópia guardada do processo, a ficha
419
446
  (`processes.get`) e o retrato (`processes.snapshot`) respondem com essa cópia em
420
447
  vez do erro. A resposta diz que é cópia, e de quando:
421
448
 
@@ -423,38 +450,39 @@ vez do erro. A resposta diz que é cópia, e de quando:
423
450
  `UPSTREAM_UNAVAILABLE` ou `UPSTREAM_RATE_LIMITED`;
424
451
  - `meta.lakeRescue.retryAfterSeconds`, repetido no header `Retry-After`, diz na
425
452
  recusa por volume quanto esperar antes de consultar a fonte de novo. É a
426
- espera da fonte, não do teto do motor: `meta.rateLimit.retryAfterSeconds`
427
- fica `null` no sucesso;
453
+ espera pedida pela fonte, não pelo limite de vazão da API:
454
+ `meta.rateLimit.retryAfterSeconds` fica `null` no sucesso;
428
455
  - `meta.sourceFetchedAt` diz quando a cópia foi buscada na fonte. Esse campo
429
- acompanha toda resposta das duas leituras, socorrida ou não, menos a ficha
430
- que o controle de confidencialidade entrega retida, sem o teor.
456
+ acompanha toda resposta das duas leituras, venha ela da fonte ou da cópia,
457
+ menos a ficha que o controle de confidencialidade entrega retida, sem o teor.
431
458
 
432
459
  A cópia custa 1 crédito, na ficha e no retrato. O retrato confere o saldo pelo
433
460
  preço cheio, 8, antes de ir à fonte; a ficha confere pelo preço do que
434
461
  entregou, depois da leitura. No retrato guardado, cada instância traz os
435
- movimentos da última vez que o motor a buscou, com a data em
462
+ movimentos da última vez que ela foi buscada, com a data em
436
463
  `instances[].fetchedAt`, e `sourcePayload` é nulo: o que andou depois dessas
437
464
  datas não está na resposta. O número que a resposta completa mais recente da
438
- fonte já não trazia em documento nenhum não é socorrido. Na ficha guardada, a
439
- data vale para os dados do processo, e o resumo das publicações (`djen`) é
440
- relido na hora.
465
+ fonte já não trazia em documento nenhum não tem cópia servida. Na ficha
466
+ guardada, a data vale para os dados do processo, e o resumo das publicações
467
+ (`djen`) é relido na hora.
441
468
 
442
- **Até que idade.** Sem pedir nada, só a cópia de até 24 h socorre a leitura —
443
- nessas duas rotas, uma resposta de sucesso já podia ser cópia do acervo dentro
444
- desse prazo. A cópia de qualquer idade vem com `staleOnFailure: true`:
469
+ **Até que idade.** Sem pedir nada, só a cópia de até 24 h responde na falha da
470
+ fonte — nessas duas rotas, uma resposta de sucesso já pode ser uma cópia
471
+ guardada dentro desse prazo. A cópia de qualquer idade vem com
472
+ `staleOnFailure: true`:
445
473
 
446
474
  ```ts
447
475
  import { describeFreshness, lakeRescueOf } from "@lexnode/legal-data-sdk";
448
476
 
449
477
  const retrato = await lexnode.processes.snapshot(numero, {
450
- tribunalAlias: "api_publica_tjsp",
478
+ tribunalAlias: "TJSP",
451
479
  staleOnFailure: true,
452
480
  });
453
481
 
454
- const socorro = lakeRescueOf(retrato);
455
- if (socorro) {
482
+ const copia = lakeRescueOf(retrato);
483
+ if (copia) {
456
484
  // A fonte falhou e isto é a cópia guardada: o que andou depois de
457
- // socorro.sourceFetchedAt não está aqui.
485
+ // copia.sourceFetchedAt não está aqui.
458
486
  mostrar(describeFreshness(retrato));
459
487
  // "cópia guardada em 26/09/2026, servida porque a fonte não respondeu a tempo"
460
488
  }
@@ -465,16 +493,17 @@ if (socorro) {
465
493
  devolve a data de qualquer resposta dessas leituras. As duas aceitam o envelope
466
494
  inteiro e o item do lote.
467
495
 
468
- **O que não é socorrido.** A resposta da fonte que não traz o processo segue
469
- 404 `PROCESS_NOT_FOUND`, com cópia guardada ou sem ela: é a fonte dizendo que
470
- não tem o número, e não uma falha dela. A cópia passa pelo mesmo controle de
471
- confidencialidade da leitura; além de 24 h, só a recebe quem já tem interesse
472
- registrado no processo — de uma leitura anterior dele ou de uma assinatura
473
- ativa —, e os demais recebem a falha da fonte, como se não houvesse cópia.
496
+ **Quando a cópia não é servida.** A resposta da fonte que não traz o processo segue 404
497
+ `PROCESS_NOT_FOUND`, com cópia guardada ou sem ela: é a fonte dizendo que não
498
+ tem o número, e não uma falha dela. A cópia passa pelo mesmo controle de
499
+ confidencialidade da leitura; além de 24 h, só a recebe a conta que já tem
500
+ interesse registrado no processo — de uma leitura anterior dele ou de uma
501
+ assinatura ativa —, e as demais recebem a falha da fonte, como se não houvesse
502
+ cópia.
474
503
 
475
- **No lote.** `processes.snapshotBatch` socorre só com a cópia de até 24 h. O
476
- item socorrido traz `lakeRescue` com a causa e a data, e custa 1 crédito em vez
477
- de 8.
504
+ **No lote.** `processes.snapshotBatch` só usa a cópia de até 24 h. O item
505
+ respondido com ela traz `lakeRescue`, com a causa e a data, e custa 1 crédito
506
+ em vez de 8.
478
507
 
479
508
  ## Leitura que não coube na espera
480
509
 
@@ -488,23 +517,23 @@ em segundo plano em vez de virar erro:
488
517
  `PROCESS`) ou `snapshot` (visão `SNAPSHOT`), com as instâncias do número em
489
518
  `instances`, o mesmo `meta` e o mesmo preço da leitura direta;
490
519
  - **202**: a fonte falhou — não respondeu a tempo, ficou indisponível ou pediu
491
- para esperar —, e o motor abriu uma consulta que tenta sozinha, até 60 s por
492
- tentativa, por até 6 h (`giveUpAt`). Nada é cobrado na hora: o preço da
493
- leitura fica retido em `creditsReserved`, o máximo que a entrega cobra, e
520
+ para esperar —, e a API abriu uma consulta que segue tentando sozinha, até
521
+ 60 s por tentativa, por até 6 h (`giveUpAt`). Nada é cobrado na hora: o preço
522
+ da leitura fica retido em `creditsReserved`, o máximo que a entrega cobra, e
494
523
  só é cobrado se o processo vier. Se não vier, a retenção volta;
495
524
  - qualquer outra recusa é a da leitura direta: 404 quando a fonte diz não ter o
496
525
  processo, 403 no segredo de justiça, 402 sem saldo.
497
526
 
498
527
  Para uma integração que precisa continuar mesmo quando a fonte demora, use
499
- `readProcessWithRecovery`. A aceitação da cópia antiga é explícita; a espera
500
- total é limitada. O fim da espera devolve `pending`, com a mesma consulta e
501
- a próxima leitura útil, sem cancelar nem abrir outra compra:
528
+ `readProcessWithRecovery`. A aceitação da cópia antiga é explícita, e a espera
529
+ total é limitada. O fim da espera devolve `pending`, com a mesma consulta e a
530
+ próxima leitura útil, sem cancelar a consulta nem abrir outra:
502
531
 
503
532
  ```ts
504
533
  import { readProcessWithRecovery } from "@lexnode/legal-data-sdk";
505
534
 
506
535
  const leitura = await readProcessWithRecovery(lexnode, {
507
- tribunalAlias: "api_publica_tjsp",
536
+ tribunalAlias: "TJSP",
508
537
  processNumber: numeroDoProcesso,
509
538
  view: "PROCESS",
510
539
  waitSeconds: 5,
@@ -523,10 +552,10 @@ if (leitura.kind === "ready") {
523
552
  }
524
553
  ```
525
554
 
526
- Sem `completionBudgetMs`, o helper entrega o 200 ou o 202 imediatamente após
527
- a abertura. O orçamento adicional cobre somente o acompanhamento: cada chamada
528
- mantém seu próprio timeout. Abortar a espera local não cancela a consulta no motor.
529
- O custo, o gate de confidencialidade e a cobrança única são os do endpoint existente.
555
+ Sem `completionBudgetMs`, a função devolve na hora o desfecho da abertura (200
556
+ ou 202). O orçamento cobre só o acompanhamento, e cada chamada mantém o próprio
557
+ prazo. Abortar a espera não cancela a consulta. O custo, o controle de
558
+ confidencialidade e a cobrança única são os de `processLookups.create`.
530
559
 
531
560
  `lookupProcess` diz, no tipo, qual dos dois desfechos aconteceu, e
532
561
  `waitForProcessLookup` espera a consulta terminar:
@@ -535,7 +564,7 @@ O custo, o gate de confidencialidade e a cobrança única são os do endpoint ex
535
564
  import { lookupProcess, waitForProcessLookup } from "@lexnode/legal-data-sdk";
536
565
 
537
566
  const desfecho = await lookupProcess(lexnode, {
538
- tribunalAlias: "api_publica_tjsp",
567
+ tribunalAlias: "TJSP",
539
568
  processNumber: numero,
540
569
  view: "SNAPSHOT",
541
570
  });
@@ -543,9 +572,9 @@ const desfecho = await lookupProcess(lexnode, {
543
572
  if (desfecho.kind === "ready") {
544
573
  desfecho.data.movements; // o retrato, como processes.snapshot o devolveria
545
574
  } else {
546
- // O orçamento é explícito. A leitura seguinte só acontece na hora da
547
- // próxima tentativa (nextAttemptAt): antes dela, o estado não muda. Com
548
- // uma tentativa em curso, a consulta é lida de novo a cada 10 s.
575
+ // Espere com orçamento. A consulta é lida de novo na hora da próxima
576
+ // tentativa (nextAttemptAt), quando o estado pode mudar, e a cada 10 s
577
+ // enquanto uma tentativa está em curso.
549
578
  const consulta = await waitForProcessLookup(
550
579
  lexnode,
551
580
  desfecho.lookup.lookupId!,
@@ -565,22 +594,23 @@ chamada repetida tenta a fonte na hora, dentro da espera dela. O tempo
565
594
  esgotado da fonte continua sem repetição automática.
566
595
 
567
596
  **A consulta se lê de graça, por 24 h.** `processLookups.get(lookupId)` custa 0
568
- e, depois da entrega, traz o processo por 24 h (`resultExpiresAt`), relido do
569
- acervo e passado de novo pelo controle de confidencialidade: o processo que
570
- passou a segredo de justiça depois responde 403. O retrato lido da consulta
571
- traz a instância principal no topo e cada instância com os movimentos da
572
- última captura dela, e `sourcePayload` é nulo. `processLookups.list` e `processLookups.cancel`
573
- completam a superfície; o cancelamento devolve a retenção e não gera evento.
574
-
575
- **Por webhook, sem esperar nada.** O fim sai em `PROCESS_LOOKUP_COMPLETED`, um
576
- ponteiro para a consulta que nunca traz o processo; o movimento novo que a
577
- entrega trouxer chega no `PROCESS_MOVEMENT_CREATED` de sempre.
597
+ e, depois da entrega, traz o processo por 24 h (`resultExpiresAt`), relido e
598
+ passado de novo pelo controle de confidencialidade: o processo que passou a
599
+ segredo de justiça depois responde 403. O retrato lido da consulta traz a
600
+ instância principal no topo e cada instância com os movimentos da última
601
+ captura dela, e `sourcePayload` é nulo. `processLookups.list` e
602
+ `processLookups.cancel` completam a superfície; o cancelamento devolve a
603
+ retenção e não gera evento.
604
+
605
+ **Por webhook, sem esperar nada.** O fim sai em `PROCESS_LOOKUP_COMPLETED`, que
606
+ aponta a consulta e nunca traz o processo; o movimento novo que a entrega
607
+ trouxer chega no `PROCESS_MOVEMENT_CREATED` de sempre.
578
608
 
579
609
  **A cópia guardada e a atualização, uma compra só.** Com `staleOnFailure: true`,
580
- o 202 traz em `stale` a cópia que o acervo guarda, com a data e a causa, por 1
581
- crédito, e a consulta segue buscando a versão atual. A entrega cobra só o resto
582
- do preço (`alreadyCharged` diz quanto já foi): as duas juntas custam o mesmo que
583
- a leitura direta, e a atualização que não vier não cobra mais nada.
610
+ o 202 traz em `stale` a cópia guardada, com a data e a causa, por 1 crédito, e
611
+ a consulta segue buscando a versão atual. A entrega cobra só o resto do preço
612
+ (`alreadyCharged` diz quanto já foi): as duas juntas custam o mesmo que a
613
+ leitura direta, e a atualização que não vier não cobra mais nada.
584
614
 
585
615
  **A leitura direta fecha a consulta.** A ficha ou o retrato do mesmo processo
586
616
  lidos com sucesso por `processes.get` ou `processes.snapshot` fecham a consulta
@@ -593,8 +623,8 @@ consulta.
593
623
  ## Frescor: o `total: 0` qualificado
594
624
 
595
625
  Um resultado vazio não distingue sozinho "não existe" de "a fonte ainda não
596
- indexou". O motor publica a data-limite do índice; o SDK a lê nos dois eixos
597
- (processo e diário):
626
+ indexou". A API publica a data-limite do índice da fonte; o SDK a lê nos dois
627
+ eixos (processo e diário):
598
628
 
599
629
  ```ts
600
630
  import {
@@ -610,15 +640,15 @@ if (isNotYetIndexed(busca, { since: "2026-09-15" })) {
610
640
  mostrar(describeFreshness(busca)); // "fonte lida até 10/09/2026"
611
641
  }
612
642
 
613
- readThrough(busca); // Date | null — null é ESTADO indeterminado, não erro
643
+ readThrough(busca); // Date | null — null quando a resposta não declara a data-limite
614
644
  ```
615
645
 
616
646
  ## Vigília com vários interessados
617
647
 
618
- O motor separa duas primitivas: o **sensor** (`Monitor`), único por alvo na
619
- conta e com teto, e a **assinatura** (`ProcessSubscription`), única por
620
- `externalRef`, sem teto e ecoada no aviso. N interessados no mesmo processo são
621
- um sensor e N assinaturas.
648
+ A vigília de um processo tem duas partes: o **monitor**, que consulta a fonte —
649
+ um por processo na conta, e o plano limita quantos —, e a **assinatura**, que
650
+ recebe o aviso — uma por `externalRef`, sem limite, e o `externalRef` volta no
651
+ evento. N interessados no mesmo processo são um monitor e N assinaturas.
622
652
 
623
653
  ```ts
624
654
  import { unwatchProcess, watchProcess } from "@lexnode/legal-data-sdk";
@@ -627,23 +657,26 @@ const { monitorId, monitorAlreadyExisted, subscription } = await watchProcess(
627
657
  lexnode,
628
658
  {
629
659
  processNumber: "00012345620268260100",
630
- tribunalAlias: "api_publica_tjsp",
660
+ tribunalAlias: "TJSP",
631
661
  officeId: "usuario-4711",
632
662
  externalRef: "caso-42",
633
663
  },
634
664
  );
635
665
 
636
- // Um interessado desiste; o sensor continua varrendo para os demais.
666
+ // Um interessado sai; o monitor continua ativo para os demais.
637
667
  await unwatchProcess(lexnode, {
638
668
  externalRef: "caso-42",
639
669
  officeId: "usuario-4711",
640
670
  });
641
671
  ```
642
672
 
643
- **Limitação conhecida:** uma assinatura criada **antes** de o processo entrar no
644
- acervo não recebe `PROCESS_MOVEMENT_CREATED` até a primeira aparição do alvo na
645
- fonte. O contorno é refazer a assinatura quando chegar o
646
- `MONITOR_SYNC_SUCCEEDED` com `firstAppearance: true`:
673
+ Quando o último interessado sair, remova o monitor com
674
+ `lexnode.monitors.delete(monitorId)`.
675
+
676
+ **Limitação conhecida:** uma assinatura criada **antes** de o processo aparecer
677
+ na fonte não recebe `PROCESS_MOVEMENT_CREATED` até a primeira aparição dele. O
678
+ contorno é refazer a assinatura quando chegar o `MONITOR_SYNC_SUCCEEDED` com
679
+ `firstAppearance: true`:
647
680
 
648
681
  ```ts
649
682
  import { resubscribeOnFirstAppearance } from "@lexnode/legal-data-sdk";
@@ -653,10 +686,9 @@ await resubscribeOnFirstAppearance(lexnode, evento, [
653
686
  ]);
654
687
  ```
655
688
 
656
- ## Só os tipos
689
+ ## Os tipos do contrato
657
690
 
658
- O uso antigo continua valendo — o pacote exporta `paths` e `operations` como
659
- sempre:
691
+ O pacote exporta `paths` e `operations`, gerados do contrato OpenAPI:
660
692
 
661
693
  ```ts
662
694
  import type { operations, paths } from "@lexnode/legal-data-sdk";
@@ -669,7 +701,7 @@ type Tribunals =
669
701
  operations["listTribunals"]["responses"][200]["content"]["application/json"]["data"];
670
702
  ```
671
703
 
672
- E os atalhos derivados deles, que o cliente usa internamente:
704
+ E atalhos derivados deles:
673
705
 
674
706
  ```ts
675
707
  import type { Body, Data, Query } from "@lexnode/legal-data-sdk";
@@ -681,11 +713,11 @@ type Tribunais = Data<"/v1/tribunals", "get">;
681
713
 
682
714
  ## A chave de teste
683
715
 
684
- A chave `lapi_test_` lê o acervo já formado, mas **não consulta a fonte viva nem
685
- cria trabalho de fundo**: monitor, vigília, colheita e busca assíncrona
686
- respondem 403 `SANDBOX_UPSTREAM_DISABLED`. Na prática, `watchProcess` e o ramo
687
- `async` de `searchProcesses` só fecham com chave viva — vale saber antes de
688
- achar que a integração está errada.
716
+ A chave `lapi_test_` relê o que a conta já buscou com a chave de produção, mas
717
+ **não consulta a fonte nem cria trabalho em segundo plano**: monitor, vigília,
718
+ colheita e busca assíncrona respondem 403 `SANDBOX_UPSTREAM_DISABLED`. Por isso
719
+ `watchProcess` e o ramo `async` de `searchProcesses` só funcionam com a chave
720
+ de produção.
689
721
 
690
722
  ## Referências
691
723