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
| Portal | Notas | Cobertura | Parâmetros |
|---|---|---|---|
sefaz-go | NF-e e NFC-e | Goiá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
POST /portals/{portal}/jobscom o certificado e os parâmetros. Resposta202com oid.GET /jobs/{id}a cada poucos minutos, atésucceeded,failedoucancelled.- Em
succeeded, baixe o zip pelodownload_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, "...": "..."
}
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
| Status | Significa |
|---|---|
queued | Na fila. Poucos navegadores rodam ao mesmo tempo no servidor. |
running | O navegador está no portal. |
retry_scheduled | A tentativa falhou (portal fora, captcha, tempo). Volta à fila em
next_attempt_at (30 min), até max_attempts (3). error mostra o motivo. |
succeeded | Pronto: download_url preenchido. |
failed | Acabaram as tentativas, ou o certificado ou a senha estão errados
(esse caso não é repetido). error traz Tipo: mensagem. |
cancelled | Encerrado 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
- Um job por empresa, portal e mês. Não reenvie um job que ainda está em
queued,runningouretry_scheduled: a retentativa já é do robô. - Mantenha poucos jobs seus em andamento ao mesmo tempo (até 6). O servidor é compartilhado e o portal não deve ser martelado.
- Mês fechado só precisa ser baixado uma vez; o mês corrente, uma ou duas vezes por dia.
5. Rotas
| Rota | Para quê |
|---|---|
GET /portals | Portais e parâmetros |
POST /portals/{portal}/jobs | Cria um job |
GET /jobs/{id} | Status e download_url |
POST /jobs/{id}/cancel | Cancela |
GET /jobs/{id}/file | Zip (chave ou link assinado) |
GET /health | Saúde, sem chave |
Esquemas completos e "Try it out": Swagger.
6. Erros
Erros vêm como {"detail": ...}.
| Status | Quando |
|---|---|
401 | Chave ausente ou inválida, ou link de download expirado |
404 | Portal ou job desconhecido, ou arquivo ainda não disponível |
413 | Corpo acima de 1 MB |
422 | Parâmetros ou certificado inválidos (sem repetir o que foi enviado) |