Billing Core API

Motor de faturacao fiscal multi-tenant certificavel pela Autoridade Tributaria (AT) de Portugal. API RESTful com autenticacao JWT/OAuth2, documentos fiscais com assinatura digital RSA-SHA1, cadeia de hash, ATCUD, QR Code e SAF-T PT.

Base URL

BASE URL https://api.billingcore.pt/api/v1

Tipos de Documento Suportados

FT Fatura
FR Fatura-Recibo
FS Fatura Simplificada (limite 1000€)
RC Recibo (referencia FT)
ND Nota de Debito (referencia FT)
NC Nota de Credito (referencia FT)

Autenticacao

Todos os endpoints requerem autenticacao via header Authorization. O Billing Core suporta dois metodos:

MetodoUsoValidade
JWTUtilizadores (email + password)Access: 15 min / Refresh: 7 dias
OAuth2Apps (client_id + client_secret)1 hora
Header de autenticacao
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

Erros

A API utiliza codigos HTTP convencionais para indicar sucesso ou falha.

CodigoNomeDescricao
400Bad RequestDados invalidos ou Idempotency-Key ausente
401UnauthorizedToken ausente, invalido ou expirado
403ForbiddenSem permissao para este recurso
404Not FoundRecurso nao encontrado
409ConflictDocumento ja committed/cancelado
422Validation ErrorCampos obrigatorios em falta
429Too Many RequestsRate limit excedido

Paginacao

Todos os endpoints de listagem suportam paginacao via query parameters.

ParametroTipoDefaultDescricao
pageinteger1Numero da pagina
page_sizeinteger20Itens por pagina (max 100)
Resposta paginada
{
  "items": [...],
  "total": 150,
  "page": 1,
  "page_size": 20
}

Idempotencia

Endpoints que modificam estado requerem o header Idempotency-Key com um identificador unico. Respostas sao cacheadas durante 24 horas.

Endpoints protegidos: POST /documents, POST /documents/{id}/commit, POST /documents/{id}/cancel, POST /documents/credit-notes
Exemplo
Idempotency-Key: ordem-compra-12345-v1

Login (JWT)

POST /auth/login

Autenticacao de utilizador por email e password. Retorna access token (15 min) e refresh token (7 dias).

Parametros

CampoTipoDescricao
emailobrigatoriostringEmail do utilizador
passwordobrigatoriostringPassword (min 8 caracteres)
curl
curl -X POST https://api.billingcore.pt/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "admin@empresa.pt",
    "password": "senha-segura"
  }'
Resposta 200
{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "refresh_token": "eyJhbGciOiJIUzI1NiIs...",
  "expires_in": 900
}

Refresh Token

POST /auth/refresh

Renovar access token usando refresh token. O refresh token tem validade de 7 dias.

CampoTipoDescricao
refresh_tokenobrigatoriostringRefresh token obtido no login

OAuth2 Client Credentials (Apps)

POST /auth/token

Autenticacao de app por client_id e client_secret. Retorna access token com validade de 1 hora.

CampoTipoDescricao
grant_typeobrigatoriostringDeve ser client_credentials
client_idobrigatoriostringID da app
client_secretobrigatoriostringSecret da app
curl
curl -X POST https://api.billingcore.pt/api/v1/auth/token \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "client_credentials",
    "client_id": "uuid-da-app",
    "client_secret": "secret-da-app"
  }'
Resposta 200
{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "expires_in": 3600
}

Documentos

Criar Documento

POST /documents

Criar documento fiscal em estado DRAFT. O documento fica em DRAFT ate ser committed via POST /documents/{id}/commit. Header Idempotency-Key obrigatorio.

FS: gross_total nao pode exceder 1000€. RC/ND: campo original_document_id obrigatorio.
CampoTipoDescricao
typeobrigatoriostringFT, FR, FS, RC, ND, NC
series_idobrigatoriouuidID da serie
customer_idobrigatoriouuidID do cliente
invoice_dateobrigatoriodateData da fatura (YYYY-MM-DD)
linesobrigatorioarrayLinhas do documento (min 1)
original_document_iduuidFT original (obrigatorio para RC e ND)
payment_amountdecimalValor pago (RC parcial)

Objeto linha

CampoTipoDescricao
product_iduuidID do produto (opcional)
descriptionobrigatoriostringDescricao da linha
quantityobrigatoriodecimalQuantidade (3 decimais)
unit_priceobrigatoriodecimalPreco unitario
tax_rateobrigatoriodecimalTaxa IVA (ex: 23.00)
curl
curl -X POST https://api.billingcore.pt/api/v1/documents \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: unique-key-123" \
  -d '{
    "type": "FT",
    "series_id": "uuid-da-serie",
    "customer_id": "uuid-do-cliente",
    "invoice_date": "2026-04-09",
    "lines": [
      {
        "product_id": "uuid-do-produto",
        "description": "Servico de Consultoria",
        "quantity": "2.000",
        "unit_price": "100.00",
        "tax_rate": "23.00"
      }
    ]
  }'
Resposta 201
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "type": "FT",
  "status": "DRAFT",
  "document_no": null,
  "invoice_date": "2026-04-09",
  "gross_total": null,
  "hash": null,
  "atcud": null,
  "lines": [...]
}

Listar Documentos

GET/documents

Listar documentos do tenant com filtros e paginacao.

ParametroTipoDescricao
statusstringFiltrar por status (DRAFT, COMMITTED, CANCELLED)
typestringFiltrar por tipo (FT, NC, etc.)
series_iduuidFiltrar por serie
customer_iduuidFiltrar por cliente

Obter Documento

GET/documents/{id}

Retorna documento com todas as linhas. Filtrado pelo tenant autenticado.

Atualizar Documento

PATCH/documents/{id}

Atualizar documento em estado DRAFT. Documentos COMMITTED ou CANCELLED sao imutaveis.

Eliminar Documento

DELETE/documents/{id}

Eliminar documento em DRAFT. Documentos COMMITTED ou CANCELLED nao podem ser eliminados.

Commit Fiscal

POST /documents/{id}/commit

Transforma documento DRAFT em COMMITTED. Processo irreversivel que inclui: calculo de totais, numeracao sequencial, assinatura RSA-SHA1, ATCUD e QR Code.

Apos committed, o documento so pode ser cancelado (nao editado). Header Idempotency-Key obrigatorio.

Sequencia atomica

  1. Lock pessimista na serie (SELECT FOR UPDATE)
  2. Calcular totais (net, tax, gross)
  3. Atribuir numero sequencial
  4. Buscar previous_hash (cadeia de hash)
  5. Assinar com RSA-SHA1 (PKCS1v15)
  6. Gerar ATCUD e QR Code
  7. Marcar COMMITTED
curl
curl -X POST https://api.billingcore.pt/api/v1/documents/{id}/commit \
  -H "Authorization: Bearer <token>" \
  -H "Idempotency-Key: commit-unique-key"
Resposta 200
{
  "message": "Document committed successfully",
  "document": {
    "id": "550e8400-...",
    "type": "FT",
    "status": "COMMITTED",
    "document_no": 1,
    "gross_total": "246.00",
    "tax_total": "46.00",
    "net_total": "200.00",
    "hash": "base64-rsa-sha1-signature...",
    "atcud": "ABCD1234-1",
    "qr_payload": "A:500000001*B:999999990*..."
  }
}

Cancelar Documento

POST/documents/{id}/cancel

Cancelar documento COMMITTED. O cancelamento e irreversivel. O campo reason e obrigatorio. Campos fiscais permanecem imutaveis.

CampoTipoDescricao
reasonobrigatoriostringMotivo do cancelamento (max 500 chars)

Nota de Credito

POST/documents/credit-notes

Criar nota de credito (NC) referenciando fatura original. Criada em DRAFT, deve ser committed separadamente. So pode referenciar faturas (FT) COMMITTED.

CampoTipoDescricao
original_document_idobrigatoriouuidID da fatura original (FT COMMITTED)
series_idobrigatoriouuidID da serie
invoice_dateobrigatoriodateData da nota de credito
reasonobrigatoriostringMotivo da nota de credito
linesobrigatorioarrayLinhas (min 1)

Descarregar PDF

GET/documents/{id}/pdf

Obter URL publico de download do PDF. Retorna JSON com download_url.

GET/documents/{id}/pdf/download

Descarregar ficheiro PDF com QR code embebido. Inclui dados do emitente, cliente, linhas, totais, ATCUD e hash. So documentos COMMITTED ou CANCELLED.

Clientes

Criar Cliente

POST/customers

Criar cliente. O NIF e validado pelo algoritmo mod 11 portugues.

CampoTipoDescricao
nameobrigatoriostringNome do cliente
nifstringNIF portugues (9 digitos, validado)
addressobjectMorada (street, city, postal_code, country)
tax_exemptbooleanIsento de IVA (default: false)
curl
curl -X POST https://api.billingcore.pt/api/v1/customers \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Empresa XYZ Lda.",
    "nif": "999999990",
    "address": {
      "street": "Rua da Liberdade 100",
      "city": "Lisboa",
      "postal_code": "1250-096",
      "country": "PT"
    }
  }'

Listar Clientes

GET/customers

Listar clientes com paginacao. Filtros: nif, search (pesquisa por nome).

Obter Cliente

GET/customers/{id}

Obter cliente por ID.

Atualizar Cliente

PATCH/customers/{id}

Atualizar campos do cliente. Todos os campos sao opcionais.

Remover Cliente

DELETE/customers/{id}

Remover cliente. Falha com 409 se tiver documentos associados.

Produtos

Criar Produto

POST/products

Criar produto/servico.

CampoTipoDescricao
codeobrigatoriostringCodigo do produto
nameobrigatoriostringNome
priceobrigatoriodecimalPreco unitario
tax_codestringCodigo IVA: NOR, INT, RED, ISE (default: NOR)
unitstringUnidade (default: UN)

Listar Produtos

GET/products

Listar produtos. Filtros: code, is_active, search.

Obter Produto

GET/products/{id}

Obter produto por ID.

Atualizar Produto

PATCH/products/{id}

Atualizar campos do produto.

Desativar Produto

DELETE/products/{id}

Desativar produto (soft delete). O produto nao e eliminado, apenas marcado como inativo.

Series

Criar Serie

POST/series

Criar serie de documentos.

CampoTipoDescricao
codeobrigatoriostringCodigo da serie (ex: FT2026)
prefixstringPrefixo (ex: FT)
validation_codestringCodigo ATCUD da AT

Listar Series

GET/series

Listar series. Filtro: status (active, suspended, closed).

Obter Serie

GET/series/{id}

Obter serie por ID.

Atualizar Serie

PATCH/series/{id}

Atualizar serie (status, validation_code). Transicoes: active → suspended → closed. Serie fechada nao pode ser reaberta.

Utilizadores

Criar Utilizador

POST/users

Criar utilizador. Email deve ser unico dentro do tenant.

CampoTipoDescricao
emailobrigatoriostringEmail (unico por tenant)
passwordobrigatoriostringPassword (min 8 chars)
rolestringadmin, operator, viewer (default: viewer)

Listar Utilizadores

GET/users

Listar utilizadores. Filtros: role, is_active.

Obter Utilizador

GET/users/{id}

Obter utilizador por ID.

Atualizar Utilizador

PATCH/users/{id}

Atualizar role ou estado do utilizador.

Desativar Utilizador

DELETE/users/{id}

Desativar utilizador (soft delete).

Apps OAuth2

Criar App

POST/apps

Criar app OAuth2. O client_secret so e retornado nesta resposta — guardar de imediato.

CampoTipoDescricao
nameobrigatoriostringNome da app
webhook_urlstringURL para webhooks
scopesarrayLista de scopes (ex: documents:write)

Listar Apps

GET/apps

Listar apps do tenant.

Obter App

GET/apps/{id}

Obter app por ID.

Atualizar App

PATCH/apps/{id}

Atualizar nome, webhook_url ou scopes.

Remover App

DELETE/apps/{id}

Remover app permanentemente.

SAF-T

Exportar SAF-T

POST/saft/export

Solicitar exportacao SAF-T PT (assincrona). Conforme Portaria 321-A/2007 (v1.04_01). O ficheiro XML e gerado em background.

CampoTipoDescricao
start_dateobrigatoriodateInicio do periodo
end_dateobrigatoriodateFim do periodo
Resposta 202
{
  "id": "uuid-do-export",
  "status": "pending",
  "start_date": "2026-01-01",
  "end_date": "2026-12-31"
}

Estado da Exportacao

GET/saft/export/{id}

Verificar estado: pending, processing, completed, failed.

Descarregar SAF-T

GET/saft/export/{id}/download

Obter URL de download do ficheiro XML. So disponivel apos status completed.

Estatisticas

Obter Estatisticas

GET/stats

Estatisticas agregadas do tenant. Revenue so conta documentos COMMITTED (subtrai NC). DRAFT excluido por default.

ParametroTipoDescricao
start_datedateInicio do periodo (default: 1 Jan ano corrente)
end_datedateFim do periodo (default: hoje)
include_draftsbooleanIncluir documentos DRAFT (default: false)
curl
curl "https://api.billingcore.pt/api/v1/stats?start_date=2026-01-01&end_date=2026-12-31" \
  -H "Authorization: Bearer <token>"
Resposta 200
{
  "period": { "start": "2026-01-01", "end": "2026-12-31" },
  "documents": {
    "total": 150,
    "by_type": { "FT": 100, "FR": 20, "NC": 5 },
    "by_status": { "COMMITTED": 140, "CANCELLED": 10 }
  },
  "revenue": {
    "gross_total": "18500.00",
    "net_total": "15041.46",
    "tax_total": "3458.54",
    "currency": "EUR"
  },
  "top_customers": [
    {
      "customer_id": "uuid",
      "name": "Cliente X",
      "gross_total": "5000.00",
      "document_count": 10
    }
  ],
  "series": [
    {
      "series_id": "uuid",
      "code": "FT2026",
      "last_document_no": 100,
      "status": "active"
    }
  ]
}