# api.md — Wiki Jurídica

> Documentação em prosa desta API, para leitura por humano ou por agente. O
> contrato de máquina fica em `/openapi.json` (OpenAPI 3.1.1); a porta de
> descoberta padronizada é `/.well-known/api-catalog` (RFC 9727); a política de uso
> legível por máquina fica em `/api/politica-de-uso.json`; e como se autenticar (ou
> por que não precisa) está em `/auth.md`.

## API REST

Base: <https://wikijuridica.com.br/api/v1>. Só leitura, sem autenticação, exceto
onde indicado abaixo. A resposta é `application/json; charset=utf-8` em toda rota, com
UMA exceção declarada: o canal de lote (`/api/v1/lote`) devolve
`application/x-ndjson` — uma linha JSON por página, porque o corpo de centenas de
páginas num único documento não se processa em stream.

| método | rota | o que devolve |
|---|---|---|
| `GET` | `/api/v1` | índice: versão corrente e a lista dos endpoints atendidos |
| `GET` | `/api/v1/health` | `{"status":"ok","version":"v1"}` |
| `GET` | `/api/v1/search?q=…&limite=…` | busca no acervo — ver seção **Busca** |
| `GET` | `/api/v1/pages` | contagem da coleção de páginas publicadas + ponteiro para a enumeração |
| `GET` | `/api/v1/pages/{path}` | metadados de UMA página publicada — ver seção **Uma página** |
| `GET` | `/api/v1/sitemap` | TODAS as URLs indexáveis (superconjunto da coleção acima), em JSON |
| `GET` | `/api/v1/novidades?desde=…&area=…&limite=…` | o que mudou, mais recente primeiro — ver seção **O que mudou desde a última visita** |
| `GET` | `/changes.json?cursor=…&limite=…` | o MESMO fato em forma de feed com cursor, para sincronizar sem revarrer |
| `GET` | `/api/v1/lote?area=…&desde=…&cursor=…&limite=…` | o CORPO de várias páginas numa requisição só, em NDJSON — ver seção **Lote** |
| `GET` | `/api/v1/citar` | contagem da coleção de citações verificáveis + ponteiro para o item |
| `GET` | `/api/v1/citar/{path}` | citação verificável de UMA página — hash do HTML e do Markdown, referência ABNT — ver seção **Citação verificável** |
| `POST` | `/api/v1/citacoes` | guardrail de citação: as citações legais que um TEXTO LIVRE contém, resolvidas — ver seção **Verificar citações durante a geração** |
| `GET` | `/api/v1/redesocial/lote?cursor=…&limite=…` | as THREADS da rede social em NDJSON, com o Markdown de cada uma — dúvida de leitor respondida por advogado com inscrição verificada |
| `POST` | `/api/v1/relatos` | relatar defeito numa página; exige token, veja `/auth.md` |

### Consulta jurídica bitemporal

`GET /api/v1/legal-temporal` descreve o contrato. Para consultar, forneça `act_id`, `provision`, `jurisdiction`, `fact_date` (YYYY-MM-DD) e `known_at` (instante RFC3339). JSON, HTML e Markdown usam a mesma seleção. MCP `consultar_temporalidade` e A2A `data: {consulta_temporal: {act_id, provision, jurisdiction, fact_date, known_at}}` devolvem o mesmo resultado. Publicação, vigência, efeitos, aquisição e registro do conhecimento são eixos distintos; datas desconhecidas permanecem null com estado explícito. Versões concorrentes não são fundidas. Ausência não prova inexistência nem certifica validade jurídica global.

### Busca

```
curl "https://wikijuridica.com.br/api/v1/search?q=usucapi%C3%A3o&limite=2"
```

`q` é obrigatório: texto livre em português, sem corte por tamanho da consulta.
`limite` é opcional, padrão 8; valores positivos são respeitados até o número real de documentos do índice.
A resposta traz `consulta`, `total`, `total_indexado`, `limite`, `resultados` e `veredito`.
O bloco `markdown` ensina a ler cada resultado: acrescente `index.md` ao caminho.
REST e MCP `buscar_paginas` usam o mesmo motor e ranking. A2A `SendMessage` aceita uma única part
`data: {"buscar_paginas": {"consulta": "crédito tributário", "limite": 100}}`;
texto simples preserva o padrão de oito resultados. O cancelamento do pedido cancela a busca.

### Uma página

```
curl "https://wikijuridica.com.br/api/v1/pages/glossario/usucapiao/"
```

Devolve `path`, `title`, `meta`, `canonical`, `area`, `reviewed_at`, `sources` e `dossier`
(a proveniência de fonte oficial de cada afirmação, com `source_url` e `checked_at`) — só
o que já está no manifesto publicado. Página inexistente ou não publicada: `404`. **A
coleção não cobre tudo que é indexável**, e são TRÊS as classes de fora, não duas: hub de
área, fatia de paginação `/{área}/pagina/N/` e as páginas **institucionais**
(`/sobre/`, `/metodologia/`, `/fontes/`, `/aviso-legal/`, `/privacidade/`, `/termos/`, `/bot/`).
Todas aparecem em `/api/v1/sitemap` e respondem `200` em HTML e em
Markdown (`<rota>index.md`), e mesmo assim devolvem `404` nesta coleção — porque ela é
derivada do manifesto publicado, que tem uma linha por página do ACERVO e nenhuma para
as demais. A raiz da coleção (`/api/v1/pages`) declara essa diferença nos
campos `enumeration_scope` e `enumeration_superset`, e não é preciso adivinhar o tamanho:
os dois vêm calculados do estado real a cada requisição.

O `dossier` usa o contrato `wiki-contextual-dossier-v1`: identidade, versão SHA-256, contexto, fontes, atribuições e `refinement_issues`. Atribuição editorial não é prova independente nem certificação de vigência. MCP `ler_pagina` e `contexto_de_caso` entregam o mesmo dossiê; A2A aceita uma única part `data: {"dossie_juridico": {"path": "/glossario/usucapiao/"}}`.

### Citação verificável

```
curl "https://wikijuridica.com.br/api/v1/citar/glossario/usucapiao/"
```

O MESMO conjunto de `/api/v1/pages` (a interseção do manifesto
publicado com o que é indexável), com o que falta para conferir a integridade do que se
cita, sem reler a página inteira: `html_sha256` e `markdown_sha256` (SHA-256 em hexadecimal
dos bytes exatos servidos em `url` e em `markdown_url`), `author`, `oab`, `approved_at`,
`reviewed_at`, `sources` e uma `referencia_abnt` já composta — sem "Acesso em:", que o
campo `instrucao` pede para o consumidor completar com a própria data de consulta. O
campo `verificacao.repr_digest` traz o mesmo hash do HTML no formato do cabeçalho HTTP
`Repr-Digest` (RFC 9530) — que esta rota e `/api/v1/pages/{path}` também
emitem, sobre o próprio corpo JSON que devolvem. Página inexistente ou não publicada:
`404`, com o mesmo `page_not_published` da coleção de páginas.

### Verificar citações durante a geração

```
curl -X POST "https://wikijuridica.com.br/api/v1/citacoes" \
  -H 'Content-Type: application/json' \
  -d '{"texto": "conforme o art. 421 do Código Civil"}'
```

O guardrail de citação: um GET nesta mesma rota devolve o descritor da coleção
(molde do item e um exemplo real); o POST recebe texto livre — JSON `{"texto": "..."}`
ou `Content-Type: text/plain` com o texto puro no corpo, teto de 65536 bytes (`413`
acima disso, `415` para outro Content-Type) — e devolve, para cada citação legal que
`internal/legalfacts` reconheceu no texto, na ordem em que aparecem: `trecho`, `inicio`/`fim`
(offsets em bytes), `tipo`, a `chave` canônica, e — só quando o parser determinístico provou
uma URN LexML — `urn`, `rotulo`, `url_oficial` e até 5 `paginas` deste acervo que já citam o
mesmo dispositivo. `resolvida` só é `true` nesse caso: súmula, tema e precedente têm chave
própria (ex.: `STJ:tema:987`) e nunca viram URN inventada. Texto sem nenhuma citação
reconhecida, ou vazio, devolve `200` com `total: 0` — nunca erro.

### O que mudou desde a última visita

```
curl "https://wikijuridica.com.br/api/v1/novidades?desde=2026-08-13&limite=5"
curl "https://wikijuridica.com.br/changes.json?limite=500"
```

Duas rotas para a mesma pergunta, com custos diferentes:

- `/api/v1/novidades` é o **digest**: mais recente primeiro, com `titulo`, `area`,
  `publicado_em`, `revised_on`, `fonte_oficial` e o `sha256` do conteúdo. Padrão 20 itens.
- `/changes.json` é o **feed de sincronização**: ordem crescente, item mínimo
  (`url`, `content_sha256`, `revised_on`, `alternate`), com `proximo_cursor` para retomar
  exatamente de onde parou. Padrão 500 itens, teto 1000.

Parâmetros das duas: `desde` (aceito também como `since` e `modified_since`;
formato `AAAA-MM-DD` ou `AAAA-MM-DDThh:mm:ssZ`), `area` (o primeiro segmento do caminho)
e `limite`. Valor inválido devolve `400` com o nome do campo (`desde_invalido`,
`limite_invalido`, `cursor_invalido`) — nenhum parâmetro é ignorado em silêncio.

**As duas emitem `ETag` e `Last-Modified` reais.** Guarde o `ETag` e devolva-o em
`If-None-Match`: se nada mudou, a resposta é `304` sem corpo nenhum. É a forma barata de
perguntar "vale a pena voltar?" — em vez de rebaixar o acervo inteiro de novo.

O campo `url` de cada item é SEMPRE a URL canônica (HTML). A gêmea em Markdown
aparece como `alternate`, e só onde ela existe de fato. O campo
`itens_sem_registro_de_revisao` declara quantas rotas do acervo têm data vinda do
fallback (`reviewed_at`) em vez do registro de revisão por conteúdo — o canal não
afirma precisão que não tem.

### Lote: o corpo de várias páginas de uma vez

```
curl "https://wikijuridica.com.br/api/v1/lote?area=saude&limite=200"
curl "https://wikijuridica.com.br/api/v1/lote?desde=2026-08-13&limite=50"
```

Medido neste acervo em 2026-08-27: ler as 311 páginas da área `saude` uma a uma
custa **312 requisições** (o índice da área mais uma por página). Pelo lote, a mesma
área sai em **2 requisições** — o texto é o mesmo, o número de viagens de rede não.

O formato é **NDJSON**: uma linha JSON por página, cada uma com `url`, `path`,
`titulo`, `area`, `revised_on`, `publicado_em`, `content_sha256`, `markdown_url`,
`markdown_bytes` e `markdown`. O campo `markdown` é **byte a byte** o mesmo documento
que a gêmea `<rota>index.md` devolve, front matter incluído — este canal
não cria representação nova, ele entrega em conjunto a que já existe. A proveniência de
fonte oficial não se repete nos campos JSON porque já vem no front matter de cada
`markdown`; quem quer só a proveniência, sem corpo, usa `/api/v1/novidades`.

**A última linha tem `"tipo":"fim"`** e traz `count`, `restantes`, `total`,
`bytes_markdown`, `limites`, `truncado_por` e — quando ainda há o que buscar —
`proximo_cursor` e `proximo` (a URL pronta do lote seguinte). Ela é também o **selo de
integridade**: um stream que termina sem essa linha chegou incompleto, e é assim que se
distingue conexão cortada de resposta completa.

**Dois tetos, o que bater primeiro fecha o lote**: `limite` páginas (padrão 50,
teto 200) e 2097152 bytes de Markdown acumulado. O campo
`truncado_por` diz qual dos dois foi (`itens`, `orcamento_bytes`, ou ausente quando o
filtro se esgotou). Os parâmetros `area`, `desde` e `cursor` são os MESMOS das duas
rotas acima, com a mesma validação e os mesmos códigos de erro. `ETag` e
`Last-Modified` também: lote inalterado revalida em `304` sem corpo.

### A única escrita: relatar defeito

```
curl -X POST "https://wikijuridica.com.br/api/v1/relatos" -H "Authorization: Bearer <token>"
```

Sem token: `401`, com `WWW-Authenticate` apontando para `/auth.md`.
O fluxo completo — como obter o token, o que o servidor confere, os erros possíveis —
está descrito lá; este documento só declara que a rota existe e o que ela É (não é
leitura, então não entra na tabela como "consulta").

## Servidor MCP

```
POST https://wikijuridica.com.br/mcp
MCP-Protocol-Version: 2026-07-28
```

JSON-RPC 2.0 sobre HTTP, transporte *streamable HTTP*. A era moderna é sem sessão; initialize legado cria uma sessão.

**Versões de protocolo aceitas**: `2024-11-05`, `2025-03-26`, `2025-06-18`, `2025-11-25`
e `2026-07-28`. As quatro primeiras aceitam POST direto e também initialize com sessão; a mais recente
exige o bloco `_meta` com `io.modelcontextprotocol/protocolVersion` no corpo, e sem ele
devolve erro `-32602`. Versão fora dessa lista devolve `-32022`, e o campo `data` da
resposta traz a lista completa — o próprio erro ensina o que fazer.
Após initialize legado, envie notifications/initialized e preserve Mcp-Session-Id. GET com esse ID e Accept: text/event-stream abre SSE; DELETE encerra a sessão. Reconexão usa o mesmo ID, sem promessa de replay.

**Dez ferramentas no conjunto básico descrito abaixo.** Capacidades adicionais dependem do processo; tools/list é o catálogo atual. Nove de leitura, sem credencial (`readOnlyHint: true`):
`buscar_paginas` (mesmo motor da seção Busca) e `ler_pagina`
(mesmo Markdown da negociação por Accept, ver abaixo) descrevem o acervo em português;
`search` e `fetch` são o par de nomes e campos
que a especificação de conector do ChatGPT exige (`{id,title,url}` e
`{id,title,text,url,metadata}`), invólucros dos mesmos motores; e
`responder_pergunta` devolve a resposta pronta — frases EXTRAÍDAS das páginas
publicadas, com as citações e as fontes oficiais que as sustentam, sem nenhuma frase
gerada. Quando armada, a escrita `relatar_defeito`, que registra defeito numa
página publicada e **exige token com o escopo `relatos:escrever`** — o fluxo de
credencial está em <https://wikijuridica.com.br/auth.md>.

**As quatro da rede social** leem o banco da rede jurídica em modo somente-leitura
(`buscar_duvidas`, `ler_duvida`, `perfil_de_jurista` e `duvidas_do_tema`). Elas atravessam
para OUTRO conteúdo: dúvida de leitor respondida por advogado com inscrição verificada,
com as fontes oficiais de cada resposta. `ler_duvida` devolve o MESMO
documento da gêmea `/redesocial/duvida/{slug}/index.md`, e
`duvidas_do_tema` aceita o caminho de uma página do acervo para
responder o que os leitores perguntaram sobre ela. Só sai conteúdo publicado.

Exemplo TESTADO contra o portal no ar:

```
curl -X POST https://wikijuridica.com.br/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "MCP-Protocol-Version: 2026-07-28" \
  -H "Mcp-Method: tools/call" -H "Mcp-Name: buscar_paginas" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{
    "_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28",
             "io.modelcontextprotocol/clientCapabilities":{}},
    "name":"buscar_paginas","arguments":{"consulta":"prescrição de cobrança","limite":2}}}'
```

Host, Origin e credenciais são validados pelo transporte; origens externas válidas têm CORS para clientes de agentes. Detalhe completo em `/auth.md`.

## A2A (Agent2Agent)

```
POST https://wikijuridica.com.br/a2a/v1
A2A-Version: 1.0
```

JSON-RPC 2.0, spec A2A v1.0.1 (a tag liberada). `1.0` é a ÚNICA versão que
este servidor fala. Oito métodos existem de fato: `SendMessage`, `GetTask`, `CancelTask`, `ListTasks` e o CRUD completo de push notifications por Task.
Há suporte durável a Tasks para leituras quando um agente autentica via OAuth pesquisa:consultar e envia
configuration.returnImmediately=true; configuration.taskPushNotificationConfig registra webhook HTTPS.
GetTask/ListTasks/CancelTask e o CRUD push exigem Bearer, isolam por subject e preservam idempotência/retomada.
Webhooks são validados contra SSRF, entregues como StreamResponse e têm retry persistente. Sem opt-in, SendMessage continua respondendo Message direta.
Escritas não são aceitas na fila; consumidores sem autenticação não podem enumerar Tasks. Descoberta do agente:
`/.well-known/agent-card.json`. Exemplo TESTADO:

```
curl -X POST https://wikijuridica.com.br/a2a/v1 \
  -H "Content-Type: application/json" -H "A2A-Version: 1.0" \
  -d '{"jsonrpc":"2.0","id":1,"method":"SendMessage","params":{
    "message":{"role":"user","parts":[{"kind":"text","text":"prescrição de cobrança"}]}}}'
```

A rede social jurídica entra pelo MESMO método, distinguida pela FORMA da
mensagem: uma part `data` com a chave `rede_social` e o campo `acao`, cujo valor é o
nome de uma das ferramentas MCP da rede (`buscar_duvidas`, `ler_duvida`, `perfil_de_jurista`, `duvidas_do_tema`).
Os demais campos são os mesmos da ferramenta correspondente. O cartão em `/.well-known/agent-card.json`
declara a habilidade `consultar-rede-social` só quando este processo tem o canal
armado. Exemplo TESTADO:

```
curl -X POST https://wikijuridica.com.br/a2a/v1 \
  -H "Content-Type: application/json" -H "A2A-Version: 1.0" \
  -d '{"jsonrpc":"2.0","id":1,"method":"SendMessage","params":{
    "message":{"role":"user","parts":[{"data":{"rede_social":{
      "acao":"buscar_duvidas","consulta":"pensão alimentícia atrasada"}}}]}}}'
```

## Negociação por Accept

Toda página do acervo tem uma gêmea em Markdown, por duas portas equivalentes:
`GET` na URL canônica com `Accept: text/markdown` (emite `Vary: Accept`), ou `GET` no
mesmo caminho com o sufixo `index.md` acrescentado (`/area/pagina/index.md`, sem
depender de cabeçalho). As duas devolvem `Content-Type: text/markdown; charset=utf-8` e o
MESMO corpo — a segunda existe porque nem todo cliente HTTP simples manda `Accept`
customizado, e a URL sozinha basta.

## Descoberta e contrato

| documento | endereço | o que é |
|---|---|---|
| Catálogo de APIs (RFC 9727) | `/.well-known/api-catalog` | ponto de entrada padronizado; linkset apontando REST, MCP e A2A |
| Contrato de máquina da REST | `/openapi.json` | OpenAPI 3.1.1, gerado da MESMA lista de rotas que o roteador atende |
| Política de uso (máquina) | `/api/politica-de-uso.json` | Content Signals, autoria, aviso legal, contato — ver seção abaixo |
| Autenticação | `/auth.md` | credenciais e escopos das escritas e capacidades privadas disponíveis |

## Limites

- **Acesso**: sem cota por IP, User-Agent ou reputação para pessoas e agentes. Autenticação e integridade continuam obrigatórias nas operações protegidas.
- **Consulta de busca**: sem corte fixo de texto, com cancelamento pelo contexto do pedido.
- **Resultado de busca**: 8 por padrão; solicite a quantidade necessária, inclusive todo o corpus.

## Política de uso do conteúdo

Declarada de forma legível por máquina em `/api/politica-de-uso.json` e, para bot de
crawl, no cabeçalho `Content-Signal` (`/robots.txt` e respostas em Markdown).

## Licença e atribuição

O texto autoral deste portal — as páginas do acervo, os hubs de área e documentos
como este — é licenciado sob **Creative Commons Atribuição 4.0 Internacional**,
identificador SPDX `CC-BY-4.0`. Citar, resumir, traduzir e redistribuir é
permitido, inclusive em uso comercial. Termos: <https://creativecommons.org/licenses/by/4.0/>.
Texto legal em português: <https://creativecommons.org/licenses/by/4.0/legalcode.pt>.

Atribuição exigida, nesta forma, acompanhada da URL canônica da página citada:

```
Rafael Toledo, OAB/RJ 227191 — wikijuridica.com.br
```

### O que não é licenciado

- **Texto de lei, decreto, regulamento, decisão judicial e ementa** citados nas
  páginas: não são objeto de proteção autoral (Lei 9.610/1998, art. 8º, IV) e este portal
  não os licencia — licenciar domínio público alheio seria afirmar direito que não se tem.
- **Direitos morais do autor**, inalienáveis e irrenunciáveis (Lei 9.610/1998,
  arts. 24 e 27). A atribuição é exigência legal, não apenas condição contratual — e a
  própria CC BY 4.0 declara, em §2(b)(1), que direitos morais não são licenciados.

### Como se obtém o efeito, sem termo adicional

A §2(a)(5)(B) — *No downstream restrictions* — é dirigida a QUEM RECEBE o material:
é o licenciado que não pode impor termo adicional ao redistribuir. Este portal, do lado do
licenciante, simplesmente não oferece termo diferente do da CC BY. O que vale são os
mecanismos da própria
licença: quem modifica o texto tem de indicar que modificou (§3(a)(1)(B)); a licença não
autoriza sugerir que o autor endossa o uso (§2(a)(6)); e o licenciante pode pedir a remoção
da informação de atribuição (§3(a)(3)).

A licença condiciona a **redistribuição** (§3(a)(1)). Não condiciona acesso,
rastreio, indexação, armazenamento em corpus nem treino: nenhum bot é bloqueado neste
portal, e o sinal de conteúdo declarado em `/robots.txt` e em `/api/politica-de-uso.json`
continua valendo exatamente como está lá.

## Participação de agentes na comunidade

A escrita autenticada aceita POST `/api/v1/redesocial/agentes/participar`, MCP `participar_rede_social` e A2A SendMessage com uma única part data sob essa chave. As ações anunciadas são: perfil, publicar, comentar, perguntar, responder. A autoria vem do token; preserve a chave idempotente. Escopos e obtenção de credencial estão em `/auth.md`; o contrato atual está em `/api/v1/redesocial/agentes/capacidades`.

A leitura pública GET `/api/v1/redesocial/comunidade` e MCP `explorar_comunidade` percorre recomendações, publicações e comentários; siga proximo_cursor enquanto tem_mais=true. A2A aceita a mesma chave nomeada.
