Referência da API
Endpoints, parâmetros, respostas e códigos de erro.
Base URL
https://app.xpend.pt/api/v1Autenticação
Todas as chamadas requerem o header Authorization com a tua chave secreta.
Authorization: Bearer vps_live_...A chave secreta é visível em /dashboard/settings/api-keys. Nunca a uses em código frontend.
Request Signing (obrigatório)
Além do Bearer, todas as chamadas a /api/v1/payments/* e /api/v1/shipments requerem um header adicional X-VP-Trace — uma assinatura HMAC-SHA256 do payload do pedido, para prevenir replay attacks e garantir integridade. Pedidos sem esta assinatura são rejeitados com 403 INTEGRATION_SIGNATURE_REQUIRED.
A forma correcta (e única suportada) de gerar esta assinatura é usar o SDK oficial. O SDK trata automaticamente da geração da assinatura, timestamp anti-replay, e context envelope. Chamadas HTTP directas (curl, Postman, fetch sem passar pelo SDK) não funcionarão — não tentes replicar o algoritmo de assinatura manualmente, é intencionalmente opaco e sujeito a mudança sem aviso.
Formato do header (referência):
X-VP-Trace: <hmac12>.<base64payload>A janela anti-replay é de 5 minutos — o timestamp no context envelope deve estar dentro dessa janela ou o request é rejeitado com SIGNATURE_EXPIRED.
Códigos de erro
| Código | Significado |
|---|---|
400 | Body inválido ou parâmetros em falta |
401 | Header Authorization em falta ou chave inválida |
403 | Conta não está activa OU assinatura em falta/inválida (INTEGRATION_SIGNATURE_REQUIRED, INVALID_SIGNATURE, SIGNATURE_EXPIRED) |
404 | Recurso não encontrado (ex: transactionId inexistente) |
429 | Rate limit excedido |
500 | Erro interno do Xpend |
502 | Erro a comunicar com a rede bancária |
Formato dos erros:
{
"error": "Missing required checkout data: customer.email, items",
"fields": ["customer.email", "items"]
}Rate limits
Pedidos POST em /api/v1/* têm rate-limit de 30 pedidos por minuto por IP. Acima deste limite recebes 429 Too Many Requests com o headerRetry-After em segundos. Faz backoff exponencial e tenta de novo.
POST /payments/init
Cria uma transacção pending. Devolve um transactionId para usar nos passos seguintes.
Chamada server-side apenas. Não existe widget Xpend no checkout do lojista. Recolhe customer e items no teu checkout e envia-os neste endpoint a partir do teu backend (SDK Node.js, PHP, plugin WooCommerce, etc.).
Envia customer e items em todos os pagamentos — mapeia os campos que o cliente preenche no checkout (nome, email, morada, produtos). Ficam guardados na transacção, alimentam o VorkRastreio e o registo interno de leads. Lojistas com vendas pagas anteriores ficam isentos (grandfather). Contas novas (0 vendas paid) exigem customer + items por defeito.
Request body
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
orderId | string | Sim | O teu identificador interno do pedido |
amount | number | Sim | Valor em EUR (positivo) |
currency | string | Não | Default EUR (única suportada) |
customer | object | Recomendado | name, email, phone, address, city, postalCode, country |
shippingAddress | object | Não | Alternativa: line1, city, postalCode, country |
items | array | Recomendado* | Produtos: id, name, quantity, priceInCents (ou price em EUR) |
storeUrl | string | Não | URL da loja (compliance / domínio) |
trackingParameters | object | Não | UTMs e clids: utm_source, utm_medium, utm_campaign, fbclid, gclid, ttclid, src, sck |
* customer + items obrigatórios para contas sem vendas pagas. Lojistas com histórico de vendas paid ficam isentos. Rollback: XPEND_REQUIRE_CHECKOUT_DATA=false. Aliases aceites: customerName no topo, shippingAddress.line1, address_line1, zip → postalCode.
{
"orderId": "ORDER-123",
"amount": 49.90,
"customer": {
"name": "João Silva",
"email": "joao@email.com",
"phone": "912345678",
"address": "Rua das Flores 12",
"city": "Lisboa",
"postalCode": "1000-001",
"country": "Portugal"
},
"items": [
{ "id": "sku-1", "name": "Ténis Running", "quantity": 1, "priceInCents": 4990 }
]
}Response
{
"transactionId": "txn_42",
"amount": 49.90,
"currency": "EUR"
}POST /payments/mbway
Dispara uma notificação push MB WAY para o cliente.
Request body
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
transactionId | string | Sim | ID devolvido por /init |
phoneNumber | string | Sim | 9 dígitos, formato 9XXXXXXXX |
Response
{
"success": true,
"message": "Notificação enviada. O cliente tem 4 minutos para confirmar."
}POST /payments/multibanco
Gera uma referência multibanco (entidade + referência).
Request body
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
transactionId | string | Sim | ID devolvido por /init |
Response
{
"entity": "24000",
"reference": "123456789",
"amount": 49.90,
"expiresAt": "2026-05-13T14:32:00Z"
}GET /payments/status
Consulta o estado actual de uma transacção já persistido na base de dados. Este endpoint não consulta o banco em tempo real nem confirma pagamentos — a confirmação vem do webhook SIBS. Usa-o como fallback de UX no checkout (polling) ou para consultas pontuais; a confirmação oficial deve ser o webhook payment.success.
Query params
| Parâmetro | Tipo | Obrigatório |
|---|---|---|
transactionId | string | Sim |
Response — MB WAY pago
{
"transactionId": "txn_42",
"externalOrderId": "ORDER-123",
"status": "paid",
"paymentMethod": "MBWAY",
"amount": 49.90,
"currency": "EUR",
"paidAt": "2026-05-10T14:32:00Z"
}Response — Multibanco pendente (com referência)
{
"transactionId": "txn_42",
"externalOrderId": "ORDER-123",
"status": "pending",
"paymentMethod": "REFERENCE",
"amount": 49.90,
"currency": "EUR",
"paidAt": null,
"mb": {
"entity": "24000",
"reference": "123456789",
"expiresAt": "2026-05-13T14:32:00Z"
}
}O objecto mb só está presente quando paymentMethod = "REFERENCE" e a referência já foi gerada.
Estados possíveis: pending · paid · failed · cancelled
Nota: algumas vendas internamente liquidadas podem continuar a aparecer como pending na API do lojista (política interna de settlement). O webhook payment.success reflecte o estado visível ao lojista.
GET /payments/methods
Lista os métodos de pagamento activos na plataforma.
Response
{
"methods": [
{ "id": "MBWAY", "name": "MB WAY", "active": true },
{ "id": "REFERENCE", "name": "Referência Multibanco", "active": true },
{ "id": "CARD", "name": "Cartão", "active": false, "reason": "Em breve" }
]
}POST /shipments · GET /shipments
API VorkRastreio — cria e consulta envios. Requer vorkrastreiosEnabled na conta (contacta suporte@xpend.pt para activar).
POST /shipments — Request body
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
orderId | string | Sim | ID do pedido na tua loja |
carrierId | number | Não | Transportadora (default da conta) |
shippingFee | number | Não | Portes em EUR |
customer | object | Não | name, email, phone |
shippingAddress | object | Não | Morada de entrega |
items | array | Não | Produtos do envio |
POST /shipments — Response
{
"shipmentId": "shp_123",
"trackingToken": "abc...",
"trackingCode": "VR123456",
"trackingUrl": "https://app.xpend.pt/track/abc...",
"carrier": { "id": 1, "name": "CTT", "logoUrl": "..." },
"shippingFee": 4.99,
"estimatedDelivery": { "min": "2026-07-15T00:00:00Z", "max": "2026-07-20T00:00:00Z" }
}GET /shipments
Query: ?orderId=ORDER-123 ou ?shipmentId=shp_123
