Creating A Session (Server-To-Server)
This is the default way to open the widget. Your backend authenticates with the Pagamio API key, creates a session for one customer, and gives the returned widgetUrl to your app.
If your integration uses signed redirects instead, see Partner-Initiated Launch.
Path: POST {WIDGET_BASE_URL}/partner/v1/sessions
Authorization: ApiKey <YOUR_API_KEY>
Required scope: sessions:create
Content-Type: application/json
Call this from your backend only. Never put the API key in your app or web page.
Request Body
Unknown fields are rejected with 400 BAD_REQUEST.
| Field | Type | Required | Description |
|---|---|---|---|
customerId | string (1-128, printable ASCII) | Yes | Your identifier for the customer. Returned to you unchanged in transactions and statements. |
walletId | string (max 128, printable ASCII) | No | Your identifier for the wallet Pagamio should hold funds in. Sent to your wallet API on every hold. |
customer.displayName | string (max 60) | No | Shown in the widget. |
customer.firstName | string (max 60) | No | Prefill only. See below. |
customer.surname | string (max 60) | No | Prefill only. |
customer.mobileNumber | string (max 20; digits, +, spaces, brackets, dashes) | No | Prefill only. |
locale | string (max 20) | No | Reserved for the customer's language. |
preselect, returnUrl | any | No | Accepted but ignored in this release. Do not rely on them. |
Identity numbers, dates of birth and bank details are not accepted anywhere in this API.
Prefill Attributes
customer.firstName, customer.surname and customer.mobileNumber are optional. Some products need the customer's name or mobile number at the point of sale; when you send them, the widget fills those fields in and the customer can still edit them. When you do not send them, the customer types the values at each sale.
- They are stored on the session only, not on transactions, receipts or statements.
- A mobile number in
+27...,0027...or27...form is converted to the local0...form. Any other value is passed through (trimmed, with spaces, dashes and brackets removed) and validated by the widget. - They reach the widget as a
prefillmap keyed byfirstname,surnameandmobileNumber.
Example Request
curl -X POST "$WIDGET_BASE_URL/partner/v1/sessions" \
-H "Authorization: ApiKey $PAGAMIO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"customerId": "customer-123",
"walletId": "wallet-456",
"customer": {
"displayName": "Thabo",
"firstName": "Thabo",
"surname": "Mokoena",
"mobileNumber": "+27821234567"
}
}'
Response 201 Created
{
"sessionId": "0b7e6a0e-6f2c-4d0b-9c61-3f0f8f6f2a11",
"widgetUrl": "https://vas-widget-sandbox.pagamio.tech/w/acme/#session=<opaque-token>",
"expiresAt": "2026-09-30T10:15:00Z"
}
| Field | Description |
|---|---|
sessionId | Identifier you can use to revoke the session. |
widgetUrl | Load this URL in the web-view, iframe or browser. The session token sits in the URL fragment (#session=), so it is never sent to a server or in a Referer header. The widget reads it and removes it from the address bar. |
expiresAt | When the session will expire if the customer is idle. |
Loading The Widget
- Android web-view:
webView.loadUrl(widgetUrl). Enable JavaScript and DOM storage. - iOS WKWebView:
webView.load(URLRequest(url: URL(string: widgetUrl)!)). - iframe:
<iframe src="{widgetUrl}">. Your page's origin must be on the allowed origins list, or the browser will refuse to frame the widget.
Treat widgetUrl as a credential: do not log it, and do not reuse it for another customer.
Session Lifetime
A session expires after an idle period (default 15 minutes, extended by each widget request) or an absolute lifetime (default 60 minutes), whichever comes first. An expired session shows "Your session has expired" and the customer must be sent back through your app to open a new one. Create a fresh session each time the customer opens the widget.
Revoking A Session
Path: DELETE {WIDGET_BASE_URL}/partner/v1/sessions/{sessionId}
Required scope: sessions:create
Returns 204 No Content. Use it when the customer signs out of your app. An unknown session, or one that belongs to another partner, returns 404 SESSION_NOT_FOUND.
Errors
| Status | code | Meaning |
|---|---|---|
400 | BAD_REQUEST | Malformed JSON, or a field this API does not accept. |
401 | UNAUTHORIZED | Missing, malformed or unknown API key. |
403 | FORBIDDEN | The key lacks the sessions:create scope. |
422 | VALIDATION | A field failed validation; see fieldErrors. |
429 | RATE_LIMITED | Over your per-key limit. Wait for Retry-After seconds. |
The full list is in the Error Reference.
Last updated: September 2026