Multibanco
Multibanco is Portugal's ATM and home-banking voucher network. Moonip returns an Entity + Reference + Amount triplet that the customer pays at any ATM, in their bank's app, or via online banking. Settlement: 1–3 business days.
#Flow at a glance
1. POST /v1/payments ──────▶ Moonip returns the payment: id, mbEntity / mbReference / mbExpiresAt, checkoutUrl
2. Display Entity, Reference, Amount, and expiry to the customer
3. Customer pays at any ATM or via home-banking
4. Webhook charge.paid arrives at your endpoint (1–3 business days later)#1. Create the payment
Endpoint POST /v1/payments
curl -X POST https://api.moonip.io/v1/payments \
-H "Authorization: Bearer $MOONIP_API_KEY" \
-H "Idempotency-Key: ORD-3311" \
-H "Content-Type: application/json" \
-d '{
"amount": 12500,
"currency": "EUR",
"paymentMethods": ["MULTIBANCO"],
"customerName": "Rui Mendes",
"customerEmail": "rui@example.com",
"metadata": { "orderId": "ORD-3311" }
}'Idempotency. POST /v1/payments accepts the Idempotency-Key header (preferred) or the body field idempotencyKey as a fallback when the header is absent; send at least one. If you send both, they must match — divergent values are 400, and neither at all is 400 on this route. Any non-blank string, scoped per merchant ((merchant, key)): replaying the same key and body returns the stored response (the same Entity/Reference).
Body — the fields shared by every /v1/payments create (method-specific artefacts come back in the response):
| Field | Notes |
|---|---|
amount | Integer > 0, smallest unit (cents). 12500 = € 125,00. |
currency | 3–8 chars, default "BRL" (Multibanco pays in EUR). |
paymentMethods | ["MULTIBANCO"] here. The API accepts PIX, CRYPTO, CREDIT_CARD, BOLETO, MBWAY, MULTIBANCO, IDEAL, BANCONTACT, SEPA, BIZUM, SWAP_NOW. |
checkout | Optional checkout template slug (1–80). Unknown slug → 404; narrows paymentMethods to the template's methods. |
customerName / customerDocument / customerEmail / customerPhone | Flat customer fields. customerEmail must be an e-mail address; customerPhone is free-form (E.164 recommended). |
customer | Alternative nested { name?, document?, email?, phone? }; flattened into the fields above (flat fields win). |
expectedPayerDocument | Digits-only expected payer CPF/CNPJ; becomes the payer lock where the provider applies it. |
metadata | Free-form key/value. metadata.returnUrl / metadata.cancelUrl, if present, must be http(s) URLs. |
returnUrl | Where to send the customer after the flow. http(s) URL without fragment. |
cancelUrl | Where to send the customer on abort. Same URL rule; requires returnUrl. |
items | Optional { name, quantity, unitPrice, totalPrice }[], integers > 0. |
webhookUrl | Optional per-transaction webhook URL override. |
idempotencyKey | Body fallback for the Idempotency-Key header. |
country | Optional 2–3 chars; provider-account routing input. |
riskTier | Optional LOW, MEDIUM or HIGH. |
environment | Optional SANDBOX, STAGING, PRODUCTION (default PRODUCTION). |
expiresIn | Optional expiry in seconds, 60–604800 (a provider-returned expiry wins). |
Response 201 Created — the voucher artefacts are already here; you do not need a follow-up GET to see them.
{
"id": "d4e5f6a7-b8c9-4012-9def-012345678901",
"status": "WAITING_PAYMENT",
"amount": 12500,
"currency": "EUR",
"paymentMethods": ["MULTIBANCO"],
"mbEntity": "12345",
"mbReference": "987 654 321",
"mbExpiresAt": "2026-05-02T16:00:11.000Z",
"checkoutUrl": "https://checkout.moonip.io/d4e5f6a7-b8c9-4012-9def-012345678901",
"createdAt": "2026-04-25T16:00:11.000Z"
}With the official SDK (same neutral package for every brand — configure it with https://api.moonip.io):
| node | python | php | dotnet |
|---|---|---|---|
client.payments.create(input, { idempotencyKey }) | client.payments.create(input, idempotency_key) | $client->payments->create($input, $idempotencyKey) | client.Payments.CreateAsync(input, idempotencyKey) |
client.payments.retrieve(id) | client.payments.retrieve(id) | $client->payments->retrieve($id) | client.Payments.RetrieveAsync(id) |
client.payments.list(query) | client.payments.list(**query) | $client->payments->list($query) | client.Payments.ListAsync(query) |
payments.list returns the page envelope { data, total, page, limit }.
#2. Fetch the voucher
GET /v1/payments/{id} re-reads the same voucher (and checkoutUrl):
curl https://api.moonip.io/v1/payments/d4e5f6a7-b8c9-4012-9def-012345678901 \
-H "Authorization: Bearer $MOONIP_API_KEY"{
"id": "d4e5f6a7-b8c9-4012-9def-012345678901",
"status": "WAITING_PAYMENT",
"amount": 12500,
"currency": "EUR",
"paymentMethods": ["MULTIBANCO"],
"mbEntity": "12345",
"mbReference": "987 654 321",
"mbExpiresAt": "2026-05-02T16:00:11.000Z",
"checkoutUrl": "https://checkout.moonip.io/d4e5f6a7-b8c9-4012-9def-012345678901",
"createdAt": "2026-04-25T16:00:11.000Z"
}| Field | Description |
|---|---|
mbEntity | 5-digit Moonip Entity (Entidade). |
mbReference | 9-digit Reference (Referência) — payment-specific. |
mbExpiresAt | Voucher expiry. After this, status moves to EXPIRED. |
#3. Display the voucher
The customer needs all three values. Recommended UI layout:
┌─────────────────────────────────────────┐
│ Pagamento Multibanco │
├─────────────────────────────────────────┤
│ Entidade 12345 │
│ Referência 987 654 321 │
│ Valor € 125,00 │
│ Validade 02 Maio 2026 │
└─────────────────────────────────────────┘
Pague em qualquer caixa Multibanco ou
através do seu home-banking até à data
de validade.Email confirmations should include the same three values plus the amount — Portuguese customers expect this format.
#4. Confirm via webhook
{
"id": "evt_00000000000000000000000000000001",
"object": "event",
"api_version": "2026-07-23",
"type": "charge.paid",
"created_at": "2026-04-26T11:32:18.000Z",
"data": {
"object": {
"id": "ch_d4e5f6a7-b8c9-4012-9def-012345678901",
"object": "charge",
"status": "paid",
"amount": 12500,
"currency": "EUR",
"payment_method": "multibanco",
"settlement": {}
}
}
}#Lifecycle
WAITING_PAYMENT ──▶ PAID ✓ fulfill order
──▶ EXPIRED voucher window passed (default 7 days)There is no REFUSED — the voucher is either paid or it expires.
#Edge cases & FAQ
Q: How long does the customer have to pay? A: Default 7 days. Email suporte@moonip.io to extend up to 30 days.
Q: Can the customer pay a different amount? A: No. Multibanco vouchers are exact-match on amount and reference. A wrong amount is rejected at the ATM.
Q: My customer paid but the webhook still hasn't fired.
A: Multibanco settlement files are batched — expect up to 24h between payment-at-ATM and the charge.paid webhook. If it's been over 48h, contact suporte@moonip.io.
Q: Can I generate the same voucher twice?
A: Use the same idempotencyKey and you'll get back the original transaction (with the original Entity/Reference). Issue a new transaction only if you want a new voucher.
Q: Bank fees for the customer? A: ATM payments are typically free. Home-banking may charge a small processing fee depending on the bank — out of Moonip's control.