Documentação
Portal do Cliente

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)

CampoTipoObrigatórioDescrição
nifstring✅ SimNIF do contribuinte emissor
privateKeystring✅ SimChave privada RSA do contribuinte em PEM
requestIdstring✅ SimO 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

CampoTipoDescrição
successbooleantrue — consulta realizada com sucesso
requestIdstringO ID do pedido consultado
resultCodestringCódigo de resultado global (ver tabela abaixo)
resultadostringDescrição legível do resultCode
documentosarrayLista de documentos e seus estados
documentos[].numerostringNúmero do documento
documentos[].statusstring"valido" ou "invalido"
documentos[].errosarrayLista de erros (apenas quando status: "invalido")
documentos[].erros[].codigostringCódigo do erro AGT
documentos[].erros[].descricaostringDescrição do erro

Significado do resultCode

CódigoSignificadoO que fazer
0Todos os documentos válidos ✅Documento foi aceite pela AGT
1Documentos válidos e inválidos ⚠️Verificar campo documentos[].status
2Nenhum documento válido ❌Corrigir erros e reemitir
7Consulta prematura ⏳Aguardar e repetir o pedido
8Processamento em curso ⏳Aguardar e repetir o pedido
9Processamento cancelado ❌Contactar suporte AGT

Erros

Erros de Autenticação

HTTPcodeMensagemCausa
401UNAUTHORIZEDNao autorizado.API Key não foi enviada
401UNAUTHORIZEDAPI Key invalida.API Key não existe ou foi revogada
403FORBIDDENScope insuficiente.A key não tem scope de consulta

Erros de Validação

HTTPcodeMensagemCausa
400INVALID_JSONBody JSON invalido.Corpo do request não é JSON válido
400VALIDATION_ERRORPayload invalido. Campos obrigatorios: nif, privateKey, requestId.Campos em falta
400VALIDATION_ERRORToo small: expected string to have >=1 charactersrequestId vazio

Erros da AGT

HTTPcodeMensagemCausa
502AGT_ERRORsecretOrPrivateKey must be an asymmetric key…Chave privada inválida
502AGT_ERRORErro de comunicacao com a AGTServiç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;
}

// Uso
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

# Uso com polling até o processamento terminar
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")

# Uso
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;
}

// Uso
$estado = consultarEstado(
    '5000537039',
    "-----BEGIN PRIVATE KEY-----\n...",
    '202600002471491'
);

// Se ainda estiver em processamento, aguardar
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 é:

  1. Criar série (se necessário) → POST /v2/series/solicitar
  2. Emitir documentoPOST /v2/documentos/emitir → obténs requestIdAGT
  3. Consultar estadoPOST /v2/documentos/status com o requestIdAGT
  4. Se resultCode for 7 ou 8 → aguardar e repetir passo 3
  5. Se resultCode for 0 → ✅ documento validado
  6. 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)