buscador-s · notas de saída

RPA Portais

Baixa as notas fiscais emitidas pela empresa nos portais fiscais que não têm API, entrando com o certificado A1 dela. Você cria um job, acompanha e baixa um zip com os XMLs.

1. Acesso

Endereço: https://buscador-s.khontra.com. Só HTTPS.

Toda rota, menos /health, leva o cabeçalho X-API-Key com a chave que a Khontra entregou para a sua ferramenta. Cada chave só enxerga os jobs que ela mesma criou: o job de outra chave responde 404. A chave fica só no seu backend: o corpo do job leva o certificado e a senha, então ele nunca deve sair do navegador do usuário direto para cá.

curl https://buscador-s.khontra.com/portals -H "X-API-Key: $RPA_API_KEY"

2. Portais

PortalNotasCoberturaParâmetros
sefaz-goNF-e e NFC-eGoiás cnpj (14 dígitos), period (MM/AAAA)

GET /portals devolve a lista atual e o JSON Schema dos parâmetros de cada portal.

3. O fluxo

  1. POST /portals/{portal}/jobs com o certificado e os parâmetros. Resposta 202 com o id.
  2. GET /jobs/{id} a cada poucos minutos, até succeeded, failed ou cancelled.
  3. Em succeeded, baixe o zip pelo download_url.

Criar o job

curl -X POST https://buscador-s.khontra.com/portals/sefaz-go/jobs \
  -H "X-API-Key: $RPA_API_KEY" -H "Content-Type: application/json" \
  -d '{
    "certificate": {"pfx_base64": "MIIK...", "password": "senha-do-pfx"},
    "params": {"cnpj": "55616701000141", "period": "10/2026"}
  }'
{
  "id": "6f1c...", "portal": "sefaz-go", "status": "queued",
  "params": {"cnpj": "55616701000141", "period": "10/2026"},
  "attempts": 0, "max_attempts": 3, "next_attempt_at": null, "error": null,
  "status_url": "https://buscador-s.khontra.com/jobs/6f1c...",
  "download_url": null, "...": "..."
}
O certificado não é guardado. Ele fica só na memória do job e em arquivos temporários apagados no fim, mesmo em erro. Não vai para banco, log nem resposta, e um 422 nunca repete o que foi enviado. Por isso, se o serviço reiniciar, os jobs na fila terminam failed com "interrompido por reinício do serviço" e precisam ser enviados de novo.

Acompanhar

StatusSignifica
queuedNa fila. Poucos navegadores rodam ao mesmo tempo no servidor.
runningO navegador está no portal.
retry_scheduledA tentativa falhou (portal fora, captcha, tempo). Volta à fila em next_attempt_at (30 min), até max_attempts (3). error mostra o motivo.
succeededPronto: download_url preenchido.
failedAcabaram as tentativas, ou o certificado ou a senha estão errados (esse caso não é repetido). error traz Tipo: mensagem.
cancelledEncerrado por POST /jobs/{id}/cancel.

Um job leva de minutos a mais de uma hora: o portal monta o zip numa fila própria, que à noite já passou de 15 minutos. Consulte a cada 2 a 5 minutos e considere perdido o job sem resposta em 6 horas.

Baixar

download_url é um link assinado que vale 1 hora e abre sem cabeçalho. Se expirar, chame GET /jobs/{id} de novo para um link novo, ou baixe direto em GET /jobs/{id}/file com X-API-Key. O zip traz os XMLs como o portal os entrega: confira se o emitente é o CNPJ pedido antes de gravar.

curl -L -o notas.zip -H "X-API-Key: $RPA_API_KEY" https://buscador-s.khontra.com/jobs/6f1c.../file

4. Boas práticas

5. Rotas

RotaPara quê
GET /portalsPortais e parâmetros
POST /portals/{portal}/jobsCria um job
GET /jobs/{id}Status e download_url
POST /jobs/{id}/cancelCancela
GET /jobs/{id}/fileZip (chave ou link assinado)
GET /healthSaúde, sem chave

Esquemas completos e "Try it out": Swagger.

6. Erros

Erros vêm como {"detail": ...}.

StatusQuando
401Chave ausente ou inválida, ou link de download expirado
404Portal ou job desconhecido, ou arquivo ainda não disponível
413Corpo acima de 1 MB
422Parâmetros ou certificado inválidos (sem repetir o que foi enviado)