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)
| Campo | Tipo | Obrigatório | Default | Descrição |
|---|---|---|---|---|
nif | string | ✅ Sim | — | NIF do contribuinte (ex: "5000537039") |
privateKey | string | ✅ Sim | — | Chave privada RSA do contribuinte em formato PEM |
seriesYear | string \| number | ✅ Sim | — | Ano de emissão (ex: "2026") |
documentType | string | ✅ Sim | — | Tipo de documento: FT, FR, NC, ND, RC, etc. |
establishmentNumber | string | Não | "SEDE" | Código do estabelecimento. Em sandbox usar "SEDE" |
seriesContingencyIndicator | string | Nã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
| Campo | Tipo | Descrição |
|---|---|---|
success | boolean | true — série criada com sucesso |
serie.codigo | string | Código único da série (ex: FT3926S9043N) |
serie.quantidadeAutorizada | string | Nº máximo de documentos autorizados nesta série |
serie.primeiroDocumento | string | Número do primeiro documento ("1") |
serie.ultimoDocumento | string | Nú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
| 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 o scope facturas:emitir |
Erros de Validação
| HTTP | code | Mensagem | Causa |
|---|---|---|---|
| 400 | INVALID_JSON | Body JSON invalido. | O corpo do request não é JSON válido |
| 400 | VALIDATION_ERROR | Payload invalido. Verifique a documentacao. | Campos obrigatórios em falta |
| 400 | VALIDATION_ERROR | Invalid option: expected one of “FT”|“FR”|… | documentType inválido |
| 400 | VALIDATION_ERROR | Invalid option: expected one of “N”|“C” | seriesContingencyIndicator inválido |
Erros da AGT
| HTTP | code | erros[].codigo | Descrição |
|---|---|---|---|
| 400 | AGT_ERROR | E99 | Estabelecimento não registado para este NIF |
| 400 | AGT_ERROR | E06 | Contribuinte não aderiu à facturação electrónica |
| 400 | AGT_ERROR | E30 | Contribuinte sem actividade registada |
| 400 | AGT_ERROR | E31 | Código de série já em utilização |
| 400 | AGT_ERROR | E33 | Ano de emissão não coincide com ano do sistema |
| 400 | AGT_ERROR | E48 | Estabelecimento desconhecido |
| 502 | AGT_ERROR | — | Erro 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
| Valor | Significado | Quando usar |
|---|---|---|
N | Normal | Emissão online — o documento é enviado em tempo real à AGT |
C | Contingência | Emissão offline — para quando não há conexão com a AGT |
Tipos de Documento
| Código | Nome | Código | Nome |
|---|---|---|---|
FT | Factura | NC | Nota de Crédito |
FR | Factura/Recibo | ND | Nota de Débito |
FA | Factura de Adiantamento | RC | Recibo Emitido |
FG | Factura Global | RG | Recibo Geral |
GF | Factura Genérica | AR | Aviso de Cobrança/Recibo |