PT EN
Voltar ao site

API de Exportação de Conhecimento

A API de Exportação de Conhecimento entrega o conteúdo curado no DATTA — documentos otimizados, FAQ, glossário e trechos — para chatbots e sistemas externos. Você curou a base na plataforma; agora o chatbot do portal, a intranet ou o sistema de atendimento precisam desse conteúdo, e sem acesso ao DATTA inteiro. A API resolve a integração: REST versionada, com autenticação por service account, sincronização incremental e limite de requisições.

Governança de fábrica: só sai conteúdo aprovado na Curadoria de Conhecimento e, para trechos, apenas conteúdo vigente. Rascunhos e conteúdo substituído nunca são exportados. Toda resposta carrega o header de contrato X-Knowledge-Export-Version: 1.

Arquitetura e decisões de projeto: exportação de conhecimento. Autenticação geral e convenções de erro: visão geral da API do DATTA. As rotas exatas estão na referência de API.


1. Service accounts

Credencial máquina-a-máquina com escopo por domínio (contexto), permissão fixa KNOWLEDGE_EXPORT e secret revelado uma única vez.

1.1 Operações de gestão

OperaçãoPermissão exigidaEntrada
Criar contaDATA_ACCESS_MANAGE_USERS{nome, descricao, dominios: ["MEC", ...]}
Listar contasDATA_ACCESS_VIEW ou DATA_ACCESS_MANAGE_USERS
Revogar contaDATA_ACCESS_MANAGE_USERS{"reason": "..."} (obrigatório)
Trocar credenciais por tokenpública (limite de 10/min por clientId+IP){clientId, clientSecret}
Consultar situação da contauso interno entre componentes da plataforma

Por padrão de perfis, apenas administradores possuem DATA_ACCESS_MANAGE_USERS e KNOWLEDGE_EXPORT.

1.2 Criar (reveal-once)

A criação devolve 201 com a credencial completa — a única vez em que o secret aparece:

json
{ "id":"<uuid>", "nome":"bot-mec", "dominios":["MEC"],
  "clientId":"<uuid>", "clientSecret":"dsa_<base64url>",
  "aviso":"Guarde o clientSecret agora: ele não será exibido novamente." }
  • O clientSecret (dsa_ + 32 bytes aleatórios) é armazenado apenas como hash bcrypt e nunca reaparece — guarde-o no seu cofre de segredos no ato da criação.
  • A conta é persistida como um registro ServiceAccount na base de sistema datta (Neo4j).
  • Auditoria: DATA_ACCESS.SERVICE_ACCOUNT_CREATED.
  • A permissão da conta é fixa ["KNOWLEDGE_EXPORT"] (v1); não há tempo de vida por conta — o tempo de vida do token é global (datta.auth.service-account-token-ttl, padrão 900 segundos).

1.3 Obter token (client-credentials)

Enviando clientId e clientSecret, a conta recebe um token de curta duração:

json
{"accessToken":"<jwt>","tokenType":"Bearer","expiresIn":900,"scope":["MEC"]}

Claims do token (HS256, mesma chave dos tokens de usuário): sub = svc:{id}, type = service-account, scope = [domínios], permissions = ["KNOWLEDGE_EXPORT"].

Qualquer falha — clientId inexistente, secret errado, conta revogada — devolve o mesmo 401 genérico "Credenciais inválidas" (anti-enumeração, com comparação de tempo constante). Acima de 10 trocas por minuto para o mesmo clientId+IP, a resposta é 429 com Retry-After: 60.

1.4 Revogar

A revogação exige reason e é sempre suave (nunca exclusão física), idempotente, com auditoria DATA_ACCESS.SERVICE_ACCOUNT_REVOKED:

json
{"id":"<uuid>","ativo":false,"revogadoEm":"..."}

Propagação: a situação da conta é consultada com cache de 60 segundos (chave datta:knowledge:export:sa-status:{id}) — tokens ainda válidos param de funcionar em até 60 segundos, com 401 "Credencial revogada. Solicite uma nova credencial ao administrador."

Interface: a gestão de service accounts na página de acesso a dados ainda não foi implementada — hoje ela é feita 100% pelas operações acima.


2. Coleções exportadas

Cada coleção é lida por domínio. O {dominio} aceita [a-zA-Z0-9_-]{1,64} e é comparado ao escopo da credencial de forma normalizada (minúsculas, alfanumérico). A autorização acontece em camadas: authority KNOWLEDGE_EXPORT → escopo de domínio → verificação de revogação → limite de requisições.

Nos exemplos abaixo, $EXPORT é o endereço base de exportação do seu ambiente (veja a referência de API) e $TOKEN é o token obtido na seção 1.3.

2.1 Manifesto — inventário + ETag

bash
curl -H "Authorization: Bearer $TOKEN" "$EXPORT/MEC/manifest"
json
{
  "dominio": "MEC",
  "snapshotVersion": "9f2c3a1b0d4e5f67",
  "geradoEm": "2026-07-09T12:00:00Z",
  "colecoes": {
    "documentos": {"total": 12, "lastModified": "2026-07-08T18:20:11Z"},
    "faq":        {"total": 9,  "lastModified": "2026-07-08T18:20:14Z"},
    "glossario":  {"total": 4,  "lastModified": "2026-07-07T10:02:00Z"},
    "chunks":     {"total": 3210, "lastModified": "2026-07-08T18:19:55Z"}
  }
}

Polling barato com If-None-Match (o snapshotVersion vira o ETag):

bash
curl -i -H "Authorization: Bearer $TOKEN" \
  -H 'If-None-Match: "9f2c3a1b0d4e5f67"' "$EXPORT/MEC/manifest"
# HTTP/1.1 304 Not Modified   (cache de 60s; nenhuma consulta ao índice no acerto)

2.2 Documentos (?format=json|md&since=...)

Documentos otimizados aprovados. Com format=md a resposta é text/markdown (renderização por seção); com format=json (padrão) vem um array de:

json
{"documentCode":"url-3f2a...","titulo":"...","tituloDocumento":"...",
 "dominio":"MEC","versao":2,"geradoEm":"...",
 "secoes":[{"titulo":"Resumo","conteudo":"...","ordem":1,"chunkIdsOrigem":["..."]}]}

2.3 FAQ e glossário (?since=...)

Itens achatados — um objeto por par pergunta/resposta ou por termo:

json
{"documentCode":"...","versao":1,"geradoEm":"...","pergunta":"...","resposta":"...","ordem":1,"chunkIdsOrigem":["..."]}
{"termo":"FIES","definicao":"...","siglaDe":"Fundo de Financiamento Estudantil","aliases":[],"origem":"url-...","versao":1,"geradoEm":"...","chunkIdsOrigem":["..."]}

2.4 Trechos (chunks) — NDJSON em streaming

bash
curl -H "Authorization: Bearer $TOKEN" \
  "$EXPORT/MEC/chunks?since=2026-07-01T00:00:00Z&includeEmbeddings=false" \
  -o chunks.ndjson
# Content-Type: application/x-ndjson — uma linha JSON por trecho
  • Somente trechos vigentes do índice do contexto, paginados internamente por cursor sobre (createdAt, _id) (página interna padrão de 500) — a coleção nunca é montada inteira em memória: 100 mil trechos fluem com consumo constante.
  • includeEmbeddings=true inclui embedding + embeddingModel (payload ~4–8 KB a mais por trecho; por padrão o campo nem é buscado no índice).
  • Linha: {"id","documentCode","dominio","texto","secao","ordem","lastModified"[,"embedding","embeddingModel"]}.

2.5 Bundle — pacote zip

bash
curl -H "Authorization: Bearer $TOKEN" -OJ "$EXPORT/MEC/bundle"
# conhecimento-MEC.zip:
#   manifest.json
#   documentos/<documentCode>-v<versao>.md
#   faq.json
#   glossario.json

O zip é montado em streaming, com as quatro coleções buscadas em paralelo (concorrência 4).

2.6 Sincronização incremental (since)

Todas as coleções aceitam since em ISO-8601 (instante ou com offset — valor inválido retorna 400 com exemplo). Fluxo recomendado do consumidor:

  1. Poll do manifesto com If-None-Match (ex.: a cada 5 min) — 304 = nada a fazer;
  2. snapshotVersion mudou → baixar apenas os deltas, informando since=<último lastModified conhecido> em cada coleção;
  3. Persistir o novo snapshotVersion + lastModified por coleção.

O filtro since compara com geradoEm do ativo (coleções) e createdAt (trechos) — quando um documento é regerado, a nova versão aparece no delta depois de aprovada na curadoria.

Exemplo prático — conectando um chatbot externo

  1. O administrador cria a conta bot-mec com escopo ["MEC"] (seção 1.2) e entrega clientId/clientSecret ao time do chatbot — que os guarda em cofre.
  2. O chatbot troca as credenciais por um token (seção 1.3) e baixa a carga inicial pelo bundle.
  3. A cada 5 minutos, faz poll do manifesto com If-None-Match. Custa um 304 na maioria das vezes.
  4. Quando o snapshotVersion muda, baixa só os deltas com since e atualiza a base local.
  5. Se a credencial vazar, o administrador revoga com reason (seção 1.4) — em até 60 segundos o consumo para, e a trilha de auditoria mostra tudo o que foi exportado.

3. Limite de requisições, erros e auditoria

  • Limite de requisições: contagem por identidade (service account ou usuário) no cache da plataforma, sob a chave datta:knowledge:export:rl:{serviceAccountId|usuário}, com janela de 1 minuto e padrão de 60 req/min (datta.knowledge.export.rate-limit-per-minute, variável de ambiente KNOWLEDGE_EXPORT_RATE_LIMIT). Excedido → 429 com Retry-After e {"error": ..., "reason": "rate_limit"}.
  • Erros estruturados (sempre {"error": <mensagem pt-BR>, "reason": <código estável>}): 403 fora_do_escopo (domínio fora do escopo da conta — auditado), 401 credencial_revogada, 400 para since, format ou domínio inválidos.
  • Auditoria (registrada em datta.audit.knowledge, indo para otel-logs-*): KNOWLEDGE.EXPORTED por coleção servida (autor, domínio, coleção, itens, formato) e KNOWLEDGE.EXPORT_DENIED com o motivo.
  • Métricas: knowledge_export_requests_total{colecao,outcome=success|error|not_modified} e knowledge_export_items_total{colecao}.

4. Solução de problemas

SintomaCausaAção
401 "Credenciais inválidas" ao pedir o tokenclientId/secret errados ou conta revogada (resposta proposital e idêntica)Conferir a conta na listagem de service accounts; se revogada, criar nova (reveal-once)
403 fora_do_escopoDomínio pedido não está nos dominios da conta (comparação normalizada)Criar conta com o domínio correto — o escopo não é editável depois
401 credencial_revogada até 60s após revogarCache da situação da conta (60s)Esperado — teto documentado de propagação
Coleções vazias com ativos existentesAtivos ainda em rascunho (não passaram pela curadoria)Aprovar na tela de curadoria; a exportação só serve conteúdo aprovado
Exportação continua funcionando com a autenticação fora do arA verificação de revogação degrada aberta (decisão de projeto documentada)Restaurar o serviço de autenticação; a janela se limita à indisponibilidade + os 60s de cache
429 constante de um consumidorPolling sem If-None-Match/sinceOrientar o consumidor ao fluxo incremental; se o volume for legítimo, subir KNOWLEDGE_EXPORT_RATE_LIMIT