SIBS - Multibanco Reference Generation

Overview

The Multibanco Reference endpoint generates payment references that customers can use to pay at ATMs, online banking, or mobile banking apps across Portugal. Multibanco is an asynchronous payment method where the reference is generated immediately, but payment confirmation arrives later via webhook when the customer completes the payment.

QLY Environment:

text
Note: "For development puroposes you cannot validate/pay the reference"

Endpoint

text
POST /transactions/sibs/multibanco-reference
Content-Type: application/json

Authentication

Headers

HeaderTypeRequiredDescription
x-credential-codeString✅ YesSIBS terminal credential code (max 255 chars)

Request Body

Schema: MultibancoReferenceRequest

The endpoint supports two operational modes:
  1. 🔄 Automatic Checkout Mode: Provide all required fields to create transaction and generate reference in one call
  2. 📋 Existing Transaction Mode: Provide only transactionId for an already created transaction

Required Fields (All Modes)

FieldTypeDescriptionExample
transactionIdStringUnique transaction identifier (max 255 chars)"mb-ref-20251107-001"

Required for Automatic Checkout Mode

FieldTypeDescriptionExample
notificationUrlStringURL for webhook notifications"https://merchant.com/webhooks/multibanco"
initialDatetimeStringReference activation datetime (ISO 8601)"2025-11-07T12:00:00Z"
finalDatetimeStringReference expiration datetime (ISO 8601)"2025-11-30T23:59:59Z"
paymentObjectPayment informationSee payment schema
customerObjectCustomer informationSee customer schema
shippingObjectShipping informationSee shipping schema

Optional Fields

FieldTypeDescriptionExample
checkoutIdStringCheckout identifier (auto-generated if omitted)"checkout-001"
paymentIdStringPayment identifier (auto-generated if omitted)"payment-001"
orderIdStringOrder identifier (auto-generated if omitted)"order-001"
maxAmountObjectMaximum payment amount (defaults to payment.amount.total){"value": 50.0, "currency": "EUR"}
minAmountObjectMinimum payment amount (defaults to payment.amount.total){"value": 50.0, "currency": "EUR"}

Nested Objects

Payment Object

json
{ "amount": { "total": 50.00, "currency": "EUR", "subtotal": 45.00, "iva": 5.00 }, "reference": "ORDER-12345", "description": "Product purchase" }
Field Details:
FieldTypeRequiredValidations
amountObject✅ Yes-
amount.totalNumber✅ YesMin: 0.0, Max: 999999999999.99
amount.currencyString✅ YesISO 4217 (3 letters), e.g., "EUR"
amount.subtotalNumber⚠️ OptionalMin: 0.0, Max: 999999999999.99
amount.ivaNumber⚠️ OptionalMin: 0.0, Max: 999999999999.99
referenceString⚠️ Optional1-32 chars
descriptionString⚠️ OptionalMax 250 chars

Customer Object

json
{ "id": "CUST-50506468", "name": "João", "lastName": "Silva", "email": "joao.silva@example.com", "phone": "351#912345678", "address": { "street": "Av. Eusébio da Silva Ferreira", "observation": "Estádio da Luz", "city": "Lisboa", "postalCode": "1500-313", "country": "PT" } }
Address Field Details:
FieldTypeRequiredValidations
streetString⚠️ RecommendedMax 255 chars
cityString⚠️ RecommendedMax 100 chars
postalCodeString⚠️ RecommendedMax 20 chars
countryString✅ YesISO-3166 alpha-2 (2 letters: "PT", "ES")

Shipping Object (⚠️ CRITICAL FORMAT)

IMPORTANT: Fields are DIRECT in the object, NOT nested in an address object!
json
{ "phone": "351912345678", "city": "Lisboa", "country": "PT", "address": "Av. Eusébio da Silva Ferreira", "postalCode": "1500-313", "state": "Lisboa" }
Field Details:
FieldTypeRequiredDescription
phoneString✅ YesContact phone (max 100 chars)
cityString✅ YesCity name (max 100 chars)
countryString✅ YesISO-3166 alpha-2 (2 letters)
addressString✅ YesFull address as STRING (max 255 chars)
postalCodeString✅ YesPostal code (max 20 chars)
stateString⚠️ OptionalState/province name
⚠️ Common Mistake:
json
// ❌ WRONG - Will cause "Failed to read HTTP message" "shipping": { "address": { "street": "Rua X", "city": "Lisboa" } } // ✅ CORRECT "shipping": { "address": "Rua X", "city": "Lisboa", "country": "PT", "postalCode": "1000-001", "phone": "351912345678" }

Request Example

Automatic Checkout (Complete)

json
{ "transactionId": "mb-ref-20251107-001", "notificationUrl": "https://merchant.com/webhooks/multibanco", "initialDatetime": "2025-11-07T12:00:00Z", "finalDatetime": "2025-11-30T23:59:59Z", "payment": { "amount": { "total": 50.00, "subtotal": 45.00, "iva": 5.00, "currency": "EUR" }, "reference": "ORDER-12345", "description": "Product purchase" }, "customer": { "id": "CUST-50506468", "name": "João", "lastName": "Silva", "email": "joao.silva@example.com", "phone": "351#912345678", "address": { "street": "Av. Eusébio da Silva Ferreira", "observation": "Estádio da Luz", "city": "Lisboa", "postalCode": "1500-313", "country": "PT" } }, "shipping": { "phone": "351912345678", "city": "Lisboa", "country": "PT", "address": "Av. Eusébio da Silva Ferreira", "postalCode": "1500-313" }, "maxAmount": { "value": 50.00, "currency": "EUR" }, "minAmount": { "value": 50.00, "currency": "EUR" } }

Response

Schema: MultibancoReferenceResponse

Success Response (200 OK)

json
{ "paymentStatus": "Pending", "paymentReference": { "entity": "11604", "reference": "123456789", "amount": { "value": 50.0, "currency": "EUR" }, "expireDate": "2025-11-30T23:59:59Z" } }

Response Fields

FieldTypeDescription
paymentStatusStringTransaction status (typically "Pending")
paymentReferenceObjectMultibanco reference details
paymentReference.entityStringEntity code (typically "11604" for SIBS)
paymentReference.referenceString9-digit unique payment reference
paymentReference.amount.valueNumberReference amount
paymentReference.amount.currencyStringCurrency code
paymentReference.expireDateStringISO 8601 expiration datetime

Transaction Status Values

StatusDescription
PendingReference generated, awaiting customer payment
SuccessPayment completed (received via webhook)
ExpiredReference expired without payment

Customer Payment Flow

What Happens After API Call

text
1. API Response: Returns reference immediately ↓ 2. Customer receives: Entity (11604) + Reference (123456789) ↓ 3. Customer chooses payment method: • 🏧 ATM (Caixa Multibanco) • 💻 Online Banking • 📱 Mobile Banking App ↓ 4. Customer enters: • Entity: 11604 • Reference: 123456789 • Confirms amount: €50.005. Customer authorizes with PIN/Password ↓ 6. SIBS processes payment (1-5 seconds) ↓ 7. Webhook notification sent to your notificationUrl ↓ 8. Transaction status updated to "Success"

Payment Methods Available

1. 🏧 ATM (Multibanco Terminal)

text
1. Insert bank card 2. Select "Payments/Purchases" → "Service Payments" 3. Enter Entity: 11604 4. Enter Reference: 123456789 5. Confirm amount: €50.00 6. Authorize with PIN

2. 💻 Online Banking

text
1. Login to online banking 2. Go to "Multibanco Payments" section 3. Enter Entity: 11604 4. Enter Reference: 123456789 5. Confirm and authorize

3. 📱 Mobile Banking App

text
1. Open banking app 2. Scan QR code (if available) OR enter manually 3. Enter Entity + Reference 4. Authorize with biometrics/PIN

Error Responses

400 Bad Request - Invalid JSON Structure

json
{ "status": "ERROR", "errorCode": "HTTP_ERROR", "message": "Failed to read HTTP message", "timestamp": "2025-11-07T17:30:19.042Z", "path": "/transactions/sibs/multibanco-reference", "traceId": "53b5879f-17" }
Common Causes:
  • shipping.address as nested object instead of string
  • ❌ Malformed JSON syntax
  • ❌ Type mismatch (string instead of number)

400 Bad Request - Missing Required Fields

json
{ "status": "ERROR", "errorCode": "INVALID_ARGUMENT", "message": "Transaction does not exist and no checkout data provided for automatic checkout. Please provide payment, customer, shipping, notificationUrl, initialDatetime and finalDatetime for automatic checkout or create the transaction first.", "timestamp": "2025-11-07T17:30:19.042Z", "path": "/transactions/sibs/multibanco-reference", "traceId": "53b5879f-17" }

400 Bad Request - Duplicate Transaction

json
{ "status": "ERROR", "errorCode": "INVALID_ARGUMENT", "message": "The transaction ID already exists", "timestamp": "2025-11-07T17:30:19.042Z", "path": "/transactions/sibs/multibanco-reference", "traceId": "53b5879f-17" }

400 Bad Request - Invalid Credential Header

json
{ "status": "ERROR", "errorCode": "INVALID_ARGUMENT", "message": "Header X-Credential-Code is required", "timestamp": "2025-11-07T17:30:19.042Z", "path": "/transactions/sibs/multibanco-reference", "traceId": "53b5879f-17" }

400 Bad Request - Validation Error

json
{ "status": "ERROR", "errorCode": "HTTP_ERROR", "message": "Validation failed: Country must be an ISO-3166 alpha-2 code; Phone is required", "timestamp": "2025-11-07T17:30:19.042Z", "path": "/transactions/sibs/multibanco-reference", "traceId": "53b5879f-17" }

404 Not Found - Transaction Not Found

json
{ "status": "ERROR", "errorCode": "TRANSACTION_ERROR", "message": "Error during Multibanco reference generation: Transaction not found. Please create a checkout first.", "provider": "sibs", "timestamp": "2025-11-07T17:30:19.042Z", "path": "/transactions/sibs/multibanco-reference", "traceId": "53b5879f-17" }

500 Internal Server Error

json
{ "status": "ERROR", "errorCode": "TRANSACTION_ERROR", "message": "Error processing transaction: class java.lang.String cannot be cast to class java.lang.Number", "provider": "sibs", "timestamp": "2025-11-07T17:30:19.042Z", "path": "/transactions/sibs/multibanco-reference", "traceId": "53b5879f-17" }

Business Rules

  1. Two Operational Modes:
    • Automatic Checkout: Provide all required fields (payment, customer, shipping, dates, notificationUrl)
    • Existing Transaction: Provide only transactionId (transaction must exist)
  2. Transaction ID Uniqueness:
    • Each transactionId must be unique
    • ❌ Cannot reuse existing IDs
    • ✅ Use timestamp or UUID in ID generation
  3. Entity Code:
    • Automatically obtained from SIBS terminal credentials
    • Typically "11604" for SIBS
    • ❌ Do NOT send entity in request
  4. Date Validation:
    • initialDatetime should be current or future
    • finalDatetime must be after initialDatetime
    • Typical expiration: 1-30 days
    • Maximum expiration: Usually 365 days
  5. Amount Ranges:
    • If not specified, maxAmount and minAmount default to payment.amount.total
    • SIBS validates paid amount is within range
    • Useful for partial payments or overpayments
  6. Country Code Format:
    • Must use ISO-3166 alpha-2 (2 uppercase letters)
    • ✅ "PT", "ES", "FR"
    • ❌ "PRT", "Portugal", "pt"
  7. Currency:
    • Multibanco primarily supports EUR (Euro)
    • Required for Portugal-based transactions
  8. Webhook Notifications:
    • SIBS sends webhook after customer payment
    • Typically within 1-5 seconds of payment
    • Implement webhook handler for notificationUrl

Best Practices

1. Transaction ID Generation

Do:
  • Use unique identifiers with timestamp: mb-ref-20251107-{unique-id}
  • Include date for easier tracking
  • Store mapping between your order ID and transaction ID
Don't:
  • Reuse transaction IDs
  • Use sequential numbers (security risk)
  • Use customer-provided IDs without validation

2. Shipping Structure

Do:
json
"shipping": { "address": "Rua Principal", // STRING, not object "city": "Lisboa", "country": "PT", "postalCode": "1000-001", "phone": "351912345678" }
Don't:
json
"shipping": { "address": { // ❌ Object causes error "street": "Rua Principal" } }

3. Date Management

Do:
  • Use ISO 8601 format: 2025-11-07T12:00:00Z
  • Set reasonable expiration periods (7-30 days)
  • Consider business hours and holidays
Don't:
  • Use past dates for finalDatetime
  • Set expiration too short (< 1 day)
  • Exceed SIBS maximum (typically 365 days)

4. Error Handling

Do:
  • Validate shipping structure before API call
  • Check country code format (2 letters)
  • Implement retry logic for network errors
  • Store reference details for customer support
Don't:
  • Skip JSON structure validation
  • Retry on 400 errors without fixing data
  • Ignore validation errors
  • Assume immediate success

5. Webhook Implementation

Do:
  • Return 200 OK immediately
  • Process webhook asynchronously
  • Validate webhook signature (if provided)
  • Log all webhook attempts
  • Handle duplicate webhooks idempotently
Don't:
  • Perform long operations in webhook handler
  • Return errors for processing issues
  • Skip webhook validation
  • Process same webhook multiple times

Troubleshooting

Issue: "Failed to read HTTP message"

Symptoms: 400 error before reaching endpoint logic
Common Causes:
  1. Shipping structure wrong (Most common)
    json
    // ❌ Wrong "shipping": { "address": { "street": "Rua X", "city": "Lisboa" } } // ✅ Correct "shipping": { "address": "Rua X", "city": "Lisboa", "country": "PT", "postalCode": "1000-001", "phone": "351912345678" }
  2. JSON syntax errors
    • Missing commas
    • Missing quotes
    • Unclosed brackets
  3. Type mismatches
    • String where number expected
    • Number where string expected
Solution: Validate JSON structure against examples above

Issue: "Country must be an ISO-3166 alpha-2 code"

Symptoms: Validation error on country fields
Cause: Using wrong country code format
Solutions:
text
❌ Wrong: "PRT" (3 letters) ❌ Wrong: "Portugal" (full name) ❌ Wrong: "pt" (lowercase) ✅ Correct: "PT" (2 uppercase letters) ✅ Correct: "ES" (Spain) ✅ Correct: "FR" (France)

Issue: "Transaction does not exist and no checkout data provided"

Symptoms: 400 error in existing transaction mode
Cause: Transaction doesn't exist, but not providing full checkout data
Solutions:
Option 1: Create transaction first
http
600;">POST /transactions/sibs/checkout [Create transaction] Then: 600;">POST /transactions/sibs/multibanco-reference {"transactionId": "existing-tx-123"}
Option 2: Use automatic checkout
http
600;">POST /transactions/sibs/multibanco-reference {Full payload with all required fields}

Issue: "The transaction ID already exists"

Symptoms: 409 conflict error
Cause: Reusing a transactionId
Solution: Generate new unique ID
javascript
// Good ID generation const txId = `mb-ref-${Date.now()}-${uuidv4()}`; // Or with date const txId = `mb-ref-20251107-${orderNumber}`;

Issue: Reference not working at ATM

Symptoms: ATM rejects reference or shows "not found"
Possible Causes:
  1. Reference not yet active
    • Check initialDatetime hasn't been reached
  2. Reference expired
    • Check finalDatetime hasn't passed
  3. Wrong entity entered
    • Verify customer entered correct entity (11604)
  4. SIBS system delay
    • Wait 1-2 minutes after generation
Resolution:
  • Verify API response was successful
  • Confirm dates are valid
  • Provide customer with correct entity and reference
  • Wait a few minutes and retry

Issue: Webhook not received

Symptoms: Payment completed but no webhook notification
Possible Causes:
  1. Incorrect notificationUrl
    • URL not publicly accessible
    • HTTPS required (not HTTP)
    • Firewall blocking SIBS IPs
  2. Server not responding
    • Server down or timeout
    • SSL certificate issues
    • Response not 200 OK
  3. Webhook processing error
    • Server returns error status
    • Processing takes too long
Resolution:
  • Verify URL is publicly accessible
  • Test webhook endpoint manually
  • Check server logs
  • Implement status polling as fallback
http
GET /transactions/sibs/status/{transactionId}

Checking Payment Status

Method 1: Webhooks (Recommended)

Configure notificationUrl to receive automatic updates:
json
{ "transactionId": "mb-ref-20251107-001", "paymentStatus": "Success", "paymentMethod": "REFERENCE", "merchant": { "terminalId": "12345", "merchantTransactionId": "mb-ref-20251107-001" }, "returnStatus": { "statusCode": "000", "statusMsg": "Success" }, "paymentReference": { "entity": "11604", "reference": "123456789", "amount": 50.00, "status": "PAID" } }

Method 2: Status Polling

Poll the status endpoint:

http
600;">GET /transactions/sibs/status/{transactionId} x-credential-code: your-credential-code
Polling Recommendations:
  • First check: After 1 minute
  • Subsequent checks: Every 5-10 minutes
  • Continue until: Payment received or reference expires

Testing

Test Scenarios

  1. ✅ Successful Automatic Checkout
    • Provide all required fields
    • Expected: Reference generated with 9 digits
  2. ✅ Successful Existing Transaction
    • Create transaction via checkout first
    • Generate reference with transaction ID only
    • Expected: Reference linked to existing transaction
  3. ❌ Missing Required Fields (Automatic Mode)
    • Omit shipping.phone
    • Expected: 400 validation error
  4. ❌ Invalid Shipping Structure
    • Send shipping.address as object
    • Expected: 400 "Failed to read HTTP message"
  5. ❌ Invalid Country Code
    • Use "PRT" instead of "PT"
    • Expected: 400 validation error
  6. ❌ Duplicate Transaction ID
    • Create reference with ID "test-001"
    • Attempt to create another with same ID
    • Expected: 409 conflict error
  7. ❌ Nonexistent Transaction (Existing Mode)
    • Use transaction ID that doesn't exist
    • Expected: 404 transaction not found
  8. ✅ Payment Flow End-to-End
    • Generate reference
    • Simulate ATM payment (sandbox)
    • Verify webhook received
    • Confirm status updated

  • MBWAY - Alternative Portuguese mobile payment
  • Checkout - Create SIBS transaction
  • Status - Query transaction status
  • Refund - Refund Multibanco payment

Common Questions

Q: What's the difference between Multibanco and MB WAY?
A:
  • Multibanco: Generates reference for payment at ATMs/online banking. Asynchronous, customer pays anytime within expiration.
  • MB WAY: Direct mobile payment with phone number. Synchronous, customer must approve within 5 minutes.
Q: Can I use the same transactionId multiple times?
A: No. Each transactionId must be unique. Generate new IDs for new references.
Q: What currency does Multibanco support?
A: Multibanco primarily supports EUR (Euro) for Portugal-based transactions.
Q: How long until the reference expires?
A: You control this via finalDatetime. Typical range: 1-30 days. SIBS maximum: ~365 days.
Q: When will I receive payment confirmation?
A: SIBS sends webhook to your notificationUrl immediately after customer payment (typically 1-5 seconds).
Q: What if I don't provide maxAmount and minAmount?
A: They default to payment.amount.total, meaning customer must pay exactly that amount.
Q: Why is shipping.address a string and not an object?
A: The Unified API's ShippingData DTO uses a simplified structure where address is a single string field, unlike customer.address which has separate fields.
Q: Can I generate multiple references for the same transaction?
A: No. One transaction = one reference. For new reference, create new transaction.
Q: What's the entity code?
A: The entity (typically "11604" for SIBS) identifies the payment service provider in the Multibanco network. It's configured via your SIBS terminal credentials.
Q: Can customers pay more or less than the reference amount?
A: Only if you specify maxAmount and minAmount with a range. Otherwise, exact amount required.
Q: What happens if customer doesn't pay before expiration?
A: Reference becomes invalid. Transaction status remains "Pending" or changes to "Expired". Customer cannot pay. Must generate new reference.

Support

For technical support or questions:


Changelog

VersionDateChanges
1.0.12025-11-11QLY environment note
1.02025-11-07Initial documentation

Note: This documentation is based on the Unified API implementation as of November 2025. Always refer to the latest API version for current information.