Documentação
Portal do Cliente

Exemplos de Código

Exemplos completos de integração com o Connect em várias linguagens. Cada exemplo inclui todos os endpoints da API v2.


TypeScript / Node.js — Cliente Completo

const API_KEY = "feak_bfc76ba48c0c286c9ce4bb2fcd7e178a399e329c59e529776bb5e01b0bfe8712";
const GATEWAY_URL = "https://connect.factflexi.com";

interface Empresa {
  nif: string;
  privateKey: string;
}

class GatewayClient {
  constructor(
    private apiKey: string = API_KEY,
    private baseUrl: string = GATEWAY_URL,
  ) {}

  private async request(path: string, body: Record<string, any>) {
    const res = await fetch(`${this.baseUrl}${path}`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "x-api-key": this.apiKey,
      },
      body: JSON.stringify(body),
    });
    const data = await res.json();
    if (!data.success) throw new Error(`[${data.code}] ${data.error}`);
    return data;
  }

  // Séries
  async listarSeries(empresa: Empresa) {
    return this.request("/v2/series", empresa);
  }

  async solicitarSerie(
    empresa: Empresa,
    tipo: string,
    ano: string,
  ) {
    return this.request("/v2/series/solicitar", {
      ...empresa,
      seriesYear: ano,
      documentType: tipo,
      establishmentNumber: "SEDE",
      seriesContingencyIndicator: "N",
    });
  }

  // Documentos
  async emitirDocumento(empresa: Empresa & { companyName: string }, payload: any) {
    return this.request("/v2/documentos/emitir", {
      nif: empresa.nif,
      privateKey: empresa.privateKey,
      companyName: empresa.companyName,
      ...payload,
    });
  }

  async consultarEstado(empresa: Empresa, requestId: string) {
    return this.request("/v2/documentos/status", {
      ...empresa,
      requestId,
    });
  }

  // High-level: emitir e aguardar validação
  async emitirEAguardar(
    empresa: Empresa & { companyName: string },
    payload: any,
    maxTentativas = 10,
  ) {
    const { requestIdAGT } = await this.emitirDocumento(empresa, payload);
    console.log(`Submetido. requestIdAGT: ${requestIdAGT}`);

    for (let i = 0; i < maxTentativas; i++) {
      await new Promise((r) => setTimeout(r, 3000));
      const estado = await this.consultarEstado(empresa, requestIdAGT);
      if (estado.resultCode === "7" || estado.resultCode === "8") {
        console.log(`Tentativa ${i + 1}: ${estado.resultado}`);
        continue;
      }
      return estado;
    }
    throw new Error("Timeout: validacao ainda em curso");
  }
}

// ─── Uso ───
const client = new GatewayClient();
const empresa = {
  nif: "5000537039",
  privateKey: "-----BEGIN PRIVATE KEY-----\n...",
};

// Listar séries
const { series } = await client.listarSeries(empresa);
console.log(`${series.length} séries encontradas`);

// Criar série FT 2026 (se necessário)
const { serie } = await client.solicitarSerie(empresa, "FT", "2026");
console.log(`Série criada: ${serie.codigo}`);

// Emitir factura e aguardar validação
const resultado = await client.emitirEAguardar(
  { ...empresa, companyName: "SONHE TESTE" },
  {
    documentos: [{
      numero: `FT ${serie.codigo}/1`,
      tipo: "FT",
      data: "2026-07-25",
      cliente: { nome: "Cliente", nif: "999999999", pais: "AO" },
      itens: [{
        codigo: "PROD001", descricao: "Produto",
        quantidade: 2, unidade: "UN", precoUnitario: 5000, taxa: 14,
      }],
    }],
  },
);

console.log(`Resultado: ${resultado.resultado}`);
for (const doc of resultado.documentos) {
  console.log(`  ${doc.status === "valido" ? "✅" : "❌"} ${doc.numero}`);
}

Python — Cliente Completo

import requests
import time

API_KEY = "feak_bfc76ba48c0c286c9ce4bb2fcd7e178a399e329c59e529776bb5e01b0bfe8712"
GATEWAY_URL = "https://connect.factflexi.com"

class GatewayClient:
    def __init__(self, api_key: str = API_KEY, base_url: str = GATEWAY_URL):
        self.api_key = api_key
        self.base_url = base_url
        self.headers = {
            "Content-Type": "application/json",
            "x-api-key": api_key,
        }

    def _request(self, path: str, body: dict) -> dict:
        res = requests.post(f"{self.base_url}{path}", json=body, headers=self.headers)
        data = res.json()
        if not data.get("success"):
            raise Exception(f"[{data.get('code')}] {data.get('error')}")
        return data

    # ── Séries ──
    def listar_series(self, nif: str, private_key: str) -> dict:
        return self._request("/v2/series", {"nif": nif, "privateKey": private_key})

    def solicitar_serie(self, nif: str, private_key: str, tipo: str, ano: str) -> dict:
        return self._request("/v2/series/solicitar", {
            "nif": nif,
            "privateKey": private_key,
            "seriesYear": ano,
            "documentType": tipo,
            "establishmentNumber": "SEDE",
            "seriesContingencyIndicator": "N",
        })

    # ── Documentos ──
    def emitir_documento(self, nif: str, private_key: str, company_name: str, payload: dict) -> dict:
        return self._request("/v2/documentos/emitir", {
            "nif": nif,
            "privateKey": private_key,
            "companyName": company_name,
            **payload,
        })

    def consultar_estado(self, nif: str, private_key: str, request_id: str) -> dict:
        return self._request("/v2/documentos/status", {
            "nif": nif,
            "privateKey": private_key,
            "requestId": request_id,
        })

    # ── High-level ──
    def emitir_e_aguardar(self, nif: str, private_key: str, company_name: str,
                          payload: dict, max_tentativas: int = 10) -> dict:
        resultado = self.emitir_documento(nif, private_key, company_name, payload)
        request_id = resultado["requestIdAGT"]
        print(f"Submetido. requestIdAGT: {request_id}")

        for i in range(max_tentativas):
            time.sleep(3)
            estado = self.consultar_estado(nif, private_key, request_id)
            if estado["resultCode"] in ("7", "8"):
                print(f"Tentativa {i + 1}: {estado['resultado']}")
                continue
            return estado
        raise Exception("Timeout: validacao ainda em curso")


# ─── Uso ───
client = GatewayClient()

# Listar séries
series_data = client.listar_series("5000537039", "-----BEGIN PRIVATE KEY-----\n...")
print(f"{series_data['total']} séries encontradas")

# Emitir factura e aguardar
estado = client.emitir_e_aguardar(
    "5000537039",
    "-----BEGIN PRIVATE KEY-----\n...",
    "SONHE TESTE",
    {
        "documentos": [{
            "numero": "FT FT3926S9043N/10",
            "tipo": "FT",
            "data": "2026-07-25",
            "cliente": {"nome": "Cliente", "nif": "999999999", "pais": "AO"},
            "itens": [{
                "codigo": "PROD001", "descricao": "Produto",
                "quantidade": 2, "unidade": "UN",
                "precoUnitario": 5000, "taxa": 14,
            }],
        }],
    },
)

print(f"Resultado: {estado['resultado']}")
for doc in estado["documentos"]:
    icon = "✅" if doc["status"] == "valido" else "❌"
    print(f"  {icon} {doc['numero']}")

PHP — Cliente Completo

<?php

class GatewayClient {
    private string $apiKey;
    private string $baseUrl;

    public function __construct(
        string $apiKey = 'feak_bfc76ba48c0c286c9ce4bb2fcd7e178a399e329c59e529776bb5e01b0bfe8712',
        string $baseUrl = 'https://connect.factflexi.com'
    ) {
        $this->apiKey = $apiKey;
        $this->baseUrl = $baseUrl;
    }

    private function request(string $path, array $body): array {
        $ch = curl_init("{$this->baseUrl}{$path}");
        curl_setopt_array($ch, [
            CURLOPT_POST => true,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER => [
                'Content-Type: application/json',
                'x-api-key: ' . $this->apiKey,
            ],
            CURLOPT_POSTFIELDS => json_encode($body),
        ]);

        $response = curl_exec($ch);
        curl_close($ch);
        $data = json_decode($response, true);

        if (!$data['success']) {
            throw new Exception("[{$data['code']}] {$data['error']}");
        }
        return $data;
    }

    // ── Séries ──
    public function listarSeries(string $nif, string $privateKey): array {
        return $this->request('/v2/series', [
            'nif' => $nif,
            'privateKey' => $privateKey,
        ]);
    }

    public function solicitarSerie(
        string $nif, string $privateKey,
        string $tipo, string $ano
    ): array {
        return $this->request('/v2/series/solicitar', [
            'nif' => $nif,
            'privateKey' => $privateKey,
            'seriesYear' => $ano,
            'documentType' => $tipo,
            'establishmentNumber' => 'SEDE',
            'seriesContingencyIndicator' => 'N',
        ]);
    }

    // ── Documentos ──
    public function emitirDocumento(
        string $nif, string $privateKey,
        string $companyName, array $payload
    ): array {
        return $this->request('/v2/documentos/emitir', array_merge([
            'nif' => $nif,
            'privateKey' => $privateKey,
            'companyName' => $companyName,
        ], $payload));
    }

    public function consultarEstado(
        string $nif, string $privateKey, string $requestId
    ): array {
        return $this->request('/v2/documentos/status', [
            'nif' => $nif,
            'privateKey' => $privateKey,
            'requestId' => $requestId,
        ]);
    }

    // ── High-level ──
    public function emitirEAguardar(
        string $nif, string $privateKey, string $companyName,
        array $payload, int $maxTentativas = 10
    ): array {
        $resultado = $this->emitirDocumento($nif, $privateKey, $companyName, $payload);
        $requestId = $resultado['requestIdAGT'];
        echo "Submetido. requestIdAGT: {$requestId}\n";

        for ($i = 0; $i < $maxTentativas; $i++) {
            sleep(3);
            $estado = $this->consultarEstado($nif, $privateKey, $requestId);
            if (in_array($estado['resultCode'], ['7', '8'])) {
                echo "Tentativa " . ($i + 1) . ": {$estado['resultado']}\n";
                continue;
            }
            return $estado;
        }
        throw new Exception("Timeout: validacao ainda em curso");
    }
}

// ─── Uso ───
$client = new GatewayClient();

// Listar séries
$seriesData = $client->listarSeries('5000537039', "-----BEGIN PRIVATE KEY-----\n...");
echo "{$seriesData['total']} séries encontradas\n";

// Emitir factura e aguardar
$estado = $client->emitirEAguardar(
    '5000537039',
    "-----BEGIN PRIVATE KEY-----\n...",
    'SONHE TESTE',
    [
        'documentos' => [[
            'numero' => 'FT FT3926S9043N/15',
            'tipo' => 'FT',
            'data' => '2026-07-25',
            'cliente' => ['nome' => 'Cliente', 'nif' => '999999999', 'pais' => 'AO'],
            'itens' => [[
                'codigo' => 'PROD001', 'descricao' => 'Produto',
                'quantidade' => 2, 'unidade' => 'UN',
                'precoUnitario' => 5000, 'taxa' => 14,
            ]],
        ]],
    ],
);

echo "Resultado: {$estado['resultado']}\n";
foreach ($estado['documentos'] as $doc) {
    $icon = $doc['status'] === 'valido' ? '✅' : '❌';
    echo "  {$icon} {$doc['numero']}\n";
}

cURL — Receitas Rápidas

Listar séries

curl -s -X POST https://connect.factflexi.com/v2/series 
  -H "Content-Type: application/json" 
  -H "x-api-key: feak_..." 
  -d '{"nif":"5000537039","privateKey":"-----BEGIN PRIVATE KEY-----\n..."}'

Solicitar série FT 2026

curl -s -X POST https://connect.factflexi.com/v2/series/solicitar 
  -H "Content-Type: application/json" 
  -H "x-api-key: feak_..." 
  -d '{"nif":"5000537039","privateKey":"...","seriesYear":"2026","documentType":"FT","establishmentNumber":"SEDE","seriesContingencyIndicator":"N"}'

Emitir factura

curl -s -X POST https://connect.factflexi.com/v2/documentos/emitir 
  -H "Content-Type: application/json" 
  -H "x-api-key: feak_..." 
  -d '{"nif":"5000537039","privateKey":"...","companyName":"SONHE TESTE","documentos":[{"numero":"FT FT3926S9043N/1","tipo":"FT","data":"2026-07-25","cliente":{"nome":"Cliente","nif":"999999999","pais":"AO"},"itens":[{"codigo":"P001","descricao":"Produto","quantidade":2,"unidade":"UN","precoUnitario":5000,"taxa":14}]}]}'

Consultar estado

curl -s -X POST https://connect.factflexi.com/v2/documentos/status 
  -H "Content-Type: application/json" 
  -H "x-api-key: feak_..." 
  -d '{"nif":"5000537039","privateKey":"...","requestId":"202600002471491"}'