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 xps_live_...A chave secreta é visível em /dashboard/settings/api-keys. Nunca a uses em código frontend.
Domínios autorizados (obrigatório)
A autenticação é por chave (Bearer) — não é preciso SDK nem assinatura de pedido. Em contrapartida, a API só aceita pagamentos de domínios que registes em Definições → Domínios.
Envia o storeUrl (o URL da tua loja) em cada /payments/init. O domínio tem de constar na lista, senão a API responde 403 DOMAIN_NOT_ALLOWED; sem storeUrl nenhum, responde 400 STORE_DOMAIN_REQUIRED. Remover um domínio da lista corta o acesso de imediato.
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 activa, ou domínio não autorizado (DOMAIN_NOT_ALLOWED) |
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 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 | Sim | URL da loja. O domínio tem de estar registado em Definições → Domínios, senão 403 DOMAIN_NOT_ALLOWED. (Se vier do browser, o Origin/Referer serve.) |
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 }
],
"storeUrl": "https://aminhaloja.pt"
}Sem SDK, por REST directo — só a chave e o storeUrl:
curl -X POST https://app.xpend.pt/api/v1/payments/init \
-H "Authorization: Bearer xps_live_..." \
-H "Content-Type: application/json" \
-d '{
"orderId": "ORDER-123",
"amount": 49.90,
"storeUrl": "https://aminhaloja.pt",
"customer": { "name": "João Silva", "email": "joao@email.com", "phone": "912345678" },
"items": [{ "id": "sku-1", "name": "Ténis", "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 Xpend. 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" }
]
}