Documentação
Portal do Cliente

Solicitar Criação de Série

POST /v2/series/solicitar

Solicita à AGT a criação de uma nova série de numeração para facturação electrónica. Cada série define um intervalo de números que podem ser usados para emitir documentos de um determinado tipo.

Porquê usar este endpoint? Antes de emitir o primeiro documento de um novo tipo ou ano, precisas de criar uma série. Por exemplo, para emitir facturas em 2026, primeiro crias uma série FT para 2026, que te devolve o código FT3926S9043N. Depois usas esse código para compor o número do documento: FT FT3926S9043N/1.


Request

Autenticação

Inclui a tua API Key num destes headers:

Authorization: Bearer feak_...

ou

x-api-key: feak_...

Scope necessário: facturas:emitir ou *

Body (JSON)

CampoTipoObrigatórioDefaultDescrição
nifstring✅ SimNIF do contribuinte (ex: "5000537039")
privateKeystring✅ SimChave privada RSA do contribuinte em formato PEM
seriesYearstring \| number✅ SimAno de emissão (ex: "2026")
documentTypestring✅ SimTipo de documento: FT, FR, NC, ND, RC, etc.
establishmentNumberstringNão"SEDE"Código do estabelecimento. Em sandbox usar "SEDE"
seriesContingencyIndicatorstringNão"N""N" = Normal, "C" = Contingência

Response

Sucesso (200 OK)

{
  "success": true,
  "serie": {
    "codigo": "FT3926S9043N",
    "quantidadeAutorizada": "999999999999",
    "primeiroDocumento": "1",
    "ultimoDocumento": "999999999999"
  }
}

Campos da Resposta

CampoTipoDescrição
successbooleantrue — série criada com sucesso
serie.codigostringCódigo único da série (ex: FT3926S9043N)
serie.quantidadeAutorizadastringNº máximo de documentos autorizados nesta série
serie.primeiroDocumentostringNúmero do primeiro documento ("1")
serie.ultimoDocumentostringNúmero do último documento

Nota: O número do documento a emitir é formado como TIPO CODIGO/SEQ. Exemplo: FT FT3926S9043N/1.

Erro da AGT com detalhes (400)

Quando a AGT rejeita o pedido mas devolve erros específicos:

{
  "success": false,
  "error": "O estabelecimento com o código 99999 não se encontra registado...",
  "code": "AGT_ERROR",
  "erros": [
    {
      "codigo": "E99",
      "descricao": "O estabelecimento com o código 99999 não se encontra registado para o contribuinte identificado pelo NIF 5000537039"
    }
  ]
}

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 o scope facturas:emitir

Erros de Validação

HTTPcodeMensagemCausa
400INVALID_JSONBody JSON invalido.O corpo do request não é JSON válido
400VALIDATION_ERRORPayload invalido. Verifique a documentacao.Campos obrigatórios em falta
400VALIDATION_ERRORInvalid option: expected one of “FT”|“FR”|…documentType inválido
400VALIDATION_ERRORInvalid option: expected one of “N”|“C”seriesContingencyIndicator inválido

Erros da AGT

HTTPcodeerros[].codigoDescrição
400AGT_ERRORE99Estabelecimento não registado para este NIF
400AGT_ERRORE06Contribuinte não aderiu à facturação electrónica
400AGT_ERRORE30Contribuinte sem actividade registada
400AGT_ERRORE31Código de série já em utilização
400AGT_ERRORE33Ano de emissão não coincide com ano do sistema
400AGT_ERRORE48Estabelecimento desconhecido
502AGT_ERRORErro de comunicação com a AGT

Exemplos de Código

TypeScript / Node.js

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

async function solicitarSerie(
  nif: string,
  privateKey: string,
  tipo: string,
  ano: string,
) {
  const response = await fetch(`${GATEWAY_URL}/v2/series/solicitar`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "x-api-key": API_KEY,
    },
    body: JSON.stringify({
      nif,
      privateKey,
      seriesYear: ano,
      documentType: tipo,
      establishmentNumber: "SEDE",
      seriesContingencyIndicator: "N",
    }),
  });

  const data = await response.json();

  if (!data.success) {
    const detalhes = data.erros
      ? data.erros.map((e: any) => `  [${e.codigo}] ${e.descricao}`).join("\n")
      : "";
    throw new Error(`${data.code}: ${data.error}\n${detalhes}`);
  }

  console.log(`Série criada: ${data.serie.codigo}`);
  console.log(`Primeiro documento: ${data.serie.primeiroDocumento}`);
  console.log(`Último documento: ${data.serie.ultimoDocumento}`);

  return data.serie;
}

// Uso — criar série FT para 2026
const serieFT = await solicitarSerie(
  "5000537039",
  "-----BEGIN PRIVATE KEY-----\n...",
  "FT",
  "2026",
);

Python

import requests

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

def solicitar_serie(nif: str, private_key: str, tipo: str, ano: str):
    response = requests.post(
        f"{GATEWAY_URL}/v2/series/solicitar",
        json={
            "nif": nif,
            "privateKey": private_key,
            "seriesYear": ano,
            "documentType": tipo,
            "establishmentNumber": "SEDE",
            "seriesContingencyIndicator": "N",
        },
        headers={
            "Content-Type": "application/json",
            "x-api-key": API_KEY,
        },
    )
    data = response.json()

    if not data.get("success"):
        detalhes = "\n".join(
            f"  [{e['codigo']}] {e['descricao']}"
            for e in data.get("erros", [])
        )
        raise Exception(f"{data.get('code')}: {data.get('error')}\n{detalhes}")

    serie = data["serie"]
    print(f"Série criada: {serie['codigo']}")
    print(f"Primeiro documento: {serie['primeiroDocumento']}")
    return serie

# Uso
serie_ft = solicitar_serie("5000537039", "-----BEGIN PRIVATE KEY-----\n...", "FT", "2026")

PHP

<?php
define('API_KEY', 'feak_bfc76ba48c0c286c9ce4bb2fcd7e178a399e329c59e529776bb5e01b0bfe8712');
define('GATEWAY_URL', 'https://connect.factflexi.com');

function solicitarSerie(
    string $nif,
    string $privateKey,
    string $tipo,
    string $ano
): array {
    $ch = curl_init(GATEWAY_URL . '/v2/series/solicitar');
    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,
            'seriesYear' => $ano,
            'documentType' => $tipo,
            'establishmentNumber' => 'SEDE',
            'seriesContingencyIndicator' => 'N',
        ]),
        CURLOPT_RETURNTRANSFER => true,
    ]);

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

    if (!$data['success']) {
        $detalhes = '';
        if (!empty($data['erros'])) {
            foreach ($data['erros'] as $e) {
                $detalhes .= "  [{$e['codigo']}] {$e['descricao']}\n";
            }
        }
        throw new Exception("{$data['code']}: {$data['error']}\n{$detalhes}");
    }

    $serie = $data['serie'];
    echo "Série criada: {$serie['codigo']}\n";
    return $serie;
}

// Uso
$serieFT = solicitarSerie(
    '5000537039',
    "-----BEGIN PRIVATE KEY-----\n...",
    'FT',
    '2026'
);

Indicador de Contingência

ValorSignificadoQuando usar
NNormalEmissão online — o documento é enviado em tempo real à AGT
CContingênciaEmissão offline — para quando não há conexão com a AGT

Tipos de Documento

CódigoNomeCódigoNome
FTFacturaNCNota de Crédito
FRFactura/ReciboNDNota de Débito
FAFactura de AdiantamentoRCRecibo Emitido
FGFactura GlobalRGRecibo Geral
GFFactura GenéricaARAviso de Cobrança/Recibo