Consultar Estado do Documento
POST /v2/documentos/status
Consulta o resultado da validação de documentos previamente submetidos à AGT. A AGT processa os documentos de forma diferida — este endpoint permite saber se cada documento foi validado ou rejeitado, e quais os erros encontrados.
Porquê usar este endpoint? Após emitir um documento via emitir, recebes um requestIdAGT. Usa esse ID aqui para verificar se a AGT aceitou ou rejeitou o documento. O processamento da AGT pode levar de alguns segundos a vários minutos.
Request
Autenticação
Authorization: Bearer feak_...
ou
x-api-key: feak_...
Scope necessário: facturas:consultar ou facturas:emitir ou *
Body (JSON)
| Campo | Tipo | Obrigatório | Descrição |
|---|
nif | string | ✅ Sim | NIF do contribuinte emissor |
privateKey | string | ✅ Sim | Chave privada RSA do contribuinte em PEM |
requestId | string | ✅ Sim | O requestIdAGT devolvido pelo endpoint emitir |
Response
Sucesso — Todos os documentos válidos (200 OK)
{
"success": true,
"requestId": "202600002471491",
"resultCode": "0",
"resultado": "Todos os documentos validos",
"documentos": [
{
"numero": "FT FT3926S9043N/1",
"status": "valido"
}
]
}
Sucesso — Documento inválido (200 OK)
{
"success": true,
"requestId": "202600002471572",
"resultCode": "2",
"resultado": "Nenhum documento valido",
"documentos": [
{
"numero": "FT FT3926S9043N/1",
"status": "invalido",
"erros": [
{
"codigo": "E09",
"descricao": "Factura especificada já consta no repositório do sistema"
}
]
}
]
}
Processamento ainda em curso (200 OK)
{
"success": true,
"requestId": "202600002471491",
"resultCode": "8",
"resultado": "Processamento em curso",
"documentos": []
}
Campos da Resposta
| Campo | Tipo | Descrição |
|---|
success | boolean | true — consulta realizada com sucesso |
requestId | string | O ID do pedido consultado |
resultCode | string | Código de resultado global (ver tabela abaixo) |
resultado | string | Descrição legível do resultCode |
documentos | array | Lista de documentos e seus estados |
documentos[].numero | string | Número do documento |
documentos[].status | string | "valido" ou "invalido" |
documentos[].erros | array | Lista de erros (apenas quando status: "invalido") |
documentos[].erros[].codigo | string | Código do erro AGT |
documentos[].erros[].descricao | string | Descrição do erro |
Significado do resultCode
| Código | Significado | O que fazer |
|---|
0 | Todos os documentos válidos ✅ | Documento foi aceite pela AGT |
1 | Documentos válidos e inválidos ⚠️ | Verificar campo documentos[].status |
2 | Nenhum documento válido ❌ | Corrigir erros e reemitir |
7 | Consulta prematura ⏳ | Aguardar e repetir o pedido |
8 | Processamento em curso ⏳ | Aguardar e repetir o pedido |
9 | Processamento cancelado ❌ | Contactar suporte AGT |
Erros
Erros de Autenticação
| HTTP | code | Mensagem | Causa |
|---|
| 401 | UNAUTHORIZED | Nao autorizado. | API Key não foi enviada |
| 401 | UNAUTHORIZED | API Key invalida. | API Key não existe ou foi revogada |
| 403 | FORBIDDEN | Scope insuficiente. | A key não tem scope de consulta |
Erros de Validação
| HTTP | code | Mensagem | Causa |
|---|
| 400 | INVALID_JSON | Body JSON invalido. | Corpo do request não é JSON válido |
| 400 | VALIDATION_ERROR | Payload invalido. Campos obrigatorios: nif, privateKey, requestId. | Campos em falta |
| 400 | VALIDATION_ERROR | Too small: expected string to have >=1 characters | requestId vazio |
Erros da AGT
| HTTP | code | Mensagem | Causa |
|---|
| 502 | AGT_ERROR | secretOrPrivateKey must be an asymmetric key… | Chave privada inválida |
| 502 | AGT_ERROR | Erro de comunicacao com a AGT | Serviço AGT indisponível |
Exemplos de Código
TypeScript / Node.js
const API_KEY = "feak_bfc76ba48c0c286c9ce4bb2fcd7e178a399e329c59e529776bb5e01b0bfe8712";
const GATEWAY_URL = "https://connect.factflexi.com";
async function consultarEstado(
nif: string,
privateKey: string,
requestId: string,
) {
const response = await fetch(`${GATEWAY_URL}/v2/documentos/status`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": API_KEY,
},
body: JSON.stringify({ nif, privateKey, requestId }),
});
const data = await response.json();
if (!data.success) {
throw new Error(`${data.code}: ${data.error}`);
}
console.log(`Resultado: ${data.resultado} (code: ${data.resultCode})`);
for (const doc of data.documentos) {
if (doc.status === "valido") {
console.log(` ✅ ${doc.numero} — Válido`);
} else {
console.log(` ❌ ${doc.numero} — Inválido`);
for (const erro of doc.erros || []) {
console.log(` [${erro.codigo}] ${erro.descricao}`);
}
}
}
return data;
}
const estado = await consultarEstado(
"5000537039",
"-----BEGIN PRIVATE KEY-----\n...",
"202600002471491",
);
if (estado.resultCode === "8") {
console.log("Ainda em processamento, tente novamente em alguns segundos...");
}
Python
import requests
import time
API_KEY = "feak_bfc76ba48c0c286c9ce4bb2fcd7e178a399e329c59e529776bb5e01b0bfe8712"
GATEWAY_URL = "https://connect.factflexi.com"
def consultar_estado(nif: str, private_key: str, request_id: str) -> dict:
response = requests.post(
f"{GATEWAY_URL}/v2/documentos/status",
json={"nif": nif, "privateKey": private_key, "requestId": request_id},
headers={
"Content-Type": "application/json",
"x-api-key": API_KEY,
},
)
data = response.json()
if not data.get("success"):
raise Exception(f"{data.get('code')}: {data.get('error')}")
print(f"Resultado: {data['resultado']} (code: {data['resultCode']})")
for doc in data["documentos"]:
if doc["status"] == "valido":
print(f" ✅ {doc['numero']} — Válido")
else:
print(f" ❌ {doc['numero']} — Inválido")
for erro in doc.get("erros", []):
print(f" [{erro['codigo']}] {erro['descricao']}")
return data
def aguardar_validacao(nif: str, private_key: str, request_id: str, max_tentativas=10):
for _ in range(max_tentativas):
estado = consultar_estado(nif, private_key, request_id)
if estado["resultCode"] not in ("7", "8"):
return estado
time.sleep(3)
raise Exception("Timeout: processamento ainda em curso")
estado = aguardar_validacao(
"5000537039",
"-----BEGIN PRIVATE KEY-----\n...",
"202600002471491",
)
PHP
<?php
define('API_KEY', 'feak_bfc76ba48c0c286c9ce4bb2fcd7e178a399e329c59e529776bb5e01b0bfe8712');
define('GATEWAY_URL', 'https://connect.factflexi.com');
function consultarEstado(
string $nif,
string $privateKey,
string $requestId
): array {
$ch = curl_init(GATEWAY_URL . '/v2/documentos/status');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'x-api-key: ' . API_KEY,
],
CURLOPT_POSTFIELDS => json_encode([
'nif' => $nif,
'privateKey' => $privateKey,
'requestId' => $requestId,
]),
CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true);
if (!$data['success']) {
throw new Exception("{$data['code']}: {$data['error']}");
}
echo "Resultado: {$data['resultado']} (code: {$data['resultCode']})\n";
foreach ($data['documentos'] as $doc) {
if ($doc['status'] === 'valido') {
echo " ✅ {$doc['numero']} — Válido\n";
} else {
echo " ❌ {$doc['numero']} — Inválido\n";
foreach ($doc['erros'] ?? [] as $erro) {
echo " [{$erro['codigo']}] {$erro['descricao']}\n";
}
}
}
return $data;
}
$estado = consultarEstado(
'5000537039',
"-----BEGIN PRIVATE KEY-----\n...",
'202600002471491'
);
if (in_array($estado['resultCode'], ['7', '8'])) {
echo "Ainda em processamento. Tente novamente.\n";
}
Fluxo Completo: Emitir → Validar
O fluxo típico de emissão de um documento é:
- Criar série (se necessário) →
POST /v2/series/solicitar - Emitir documento →
POST /v2/documentos/emitir → obténs requestIdAGT - Consultar estado →
POST /v2/documentos/status com o requestIdAGT - Se
resultCode for 7 ou 8 → aguardar e repetir passo 3 - Se
resultCode for 0 → ✅ documento validado - Se
resultCode for 1 ou 2 → verificar erros[] e corrigir
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ 1. Solicitar │ ──▶ │ 2. Emitir │ ──▶ │ 3. Status │
│ Série │ │ Documento │ │ (polling) │
└──────────────┘ └──────────────┘ └──────┬───────┘
│
┌────────────┴────────────┐
│ │
▼ ▼
✅ Validado ❌ Rejeitado
(corrigir e
reemitir)