Let Revolut pull passes from you after an eligible card transaction - no user action required.
When a customer pays with their Revolut card at your business, Revolut detects the transaction and pulls the passes it produced from a callback endpoint you host. Revolut then creates the passes and offers them to the customer. No button, no user action required.
The response contains the passes array. Ignore any fields you don't recognise.
How it works
This is a server-to-server integration - the customer never interacts with the API directly. But the direction is reversed from pass creation: Revolut calls your endpoint after detecting an eligible card transaction. You return the passes that transaction produced, and Revolut creates them in the customer's wallet.
The following components interact in this flow:
| Component | Role in this flow |
|---|---|
Wallet Partner Callbacks POST /transaction-outcomes/search | The endpoint you host - Revolut calls it after detecting an eligible card transaction |
transaction_context | Match criteria Revolut sends: rrn, merchant_id, transaction_at, total |
passes array | Your response - full pass objects (same shape as create-pass, including template_id) |
| Merchant ID | Card acceptor identifier (DE 42 in ISO 8583) - registered during onboarding, used by Revolut to match transactions to you |
| RRN | Retrieval Reference Number - ISO 8583 unique transaction identifier Revolut sends for matching |
Revolut sends the transaction context to your endpoint. You match it against your records and return the passes that transaction produced. Revolut creates them in the customer's wallet.
Auto-provisioning flow
- A customer makes an eligible purchase with a Revolut card at your business.
- Revolut detects the transaction is eligible for auto-provisioning (matched by merchant ID, amount, RRN (Retrieval Reference Number), and rules).
- Revolut calls your
POST /transaction-outcomes/searchendpoint with the transaction context. - You match the transaction and return the passes it produced.
- Revolut creates the passes and notifies the customer.
Before you begin
- Complete Get started to get your Partner ID, API credentials, and
template_id - Register your callback base URL with the onboarding team
- Have at least one pass type designed and a
template_idassigned
Implement the search endpoint
Before Revolut can call you, you need to host an HTTPS endpoint on your server that accepts POST /transaction-outcomes/search requests. Register your callback base URL with the Revolut Wallet team during onboarding. The customer doesn't need to manually add the pass - it appears in their wallet automatically after they pay.
Revolut authenticates to you using one of the methods configured during onboarding (mTLS, JWS, OAuth 2.0, or a combination) - validate Revolut's credentials and reject any request without valid authentication. See Wallet Partner Callbacks authentication.
This section walks through receiving the request, matching the transaction, and returning the passes.
1. Receive the request
Revolut sends the transaction context for you to match. The request includes the rrn (Retrieval Reference Number), merchant_id, transaction_at, and optionally the total - these are the fields you'll use to identify the originating transaction in your system.
POST /transaction-outcomes/search
Host: https://passes.partner.example/revolut-wallet
Revolut-Api-Version: 2026-07-01
Request-Id: 01970c4c-5790-7fb5-8331-84dfa075bbe9
Content-Type: application/json
{
"transaction_context": {
"rrn": "123456789012",
"merchant_id": "mer_example_01",
"transaction_at": "2026-07-01T12:00:00.000Z",
"total": { "amount": "72.40", "currency": "GBP" }
}
}Header parameters:
| Parameter | Description |
|---|---|
Revolut-Api-Version | API version of the request. |
Request-Id | Unique identifier Revolut assigns to this request. Use it for tracing and idempotency on your side. |
The Request-Id header is Revolut's correlation ID for this request. Echo it back in your response for tracing.
Body parameters:
| Parameter | Description | Required |
|---|---|---|
transaction_context.rrn | ISO 8583 Retrieval Reference Number - uniquely identifies the transaction for matching | Yes |
transaction_context.merchant_id | Card acceptor identifier - identifies the sub-merchant for aggregators | Yes |
transaction_context.transaction_at | Timestamp of the original purchase | Yes |
transaction_context.total | Transaction amount and currency - supplied for stronger matching, but you may skip validating it | No |
2. Match the transaction
Use rrn, merchant_id, and transaction_at to find the originating transaction in your system. The total field is supplied for stronger matching but is optional to validate against.
def match_transaction(transaction_context):
# Match by RRN and merchant ID
transaction = db.find_by_rrn(
rrn=transaction_context["rrn"],
merchant_id=transaction_context["merchant_id"],
)
if not transaction:
return None # → 404: transaction not found
if transaction.is_refunded:
return "void" # → 410: outcome is void
if not transaction.is_finalised:
return "pending" # → 202: not ready yet
return transaction.passes # → 200: return the passes3. Return the passes
Return a 200 with the passes array. Each pass is a full pass object (same shape as pass creation, including template_id). The array may be empty if the transaction produced no passes.
HTTP/1.1 200 OK
Content-Type: application/json
Request-Id: 01970c4c-5790-7fb5-8331-84dfa075bbe9
{
"passes": [
{
"partner_reference": "skyjet-2241-R7K92M",
"type": "boarding_pass",
"version": 1,
"template_id": "tmpl_skyjet_boarding",
"relevant_at": "2026-08-02T06:15:00.000Z",
"semantics": {
"passenger": { "full_name": "Sarah Connor" },
"flight": {
"flight_number": "2241",
"carrier_code": "SJ",
"departure_airport": { "iata": "LTN", "terminal": "1" },
"arrival_airport": { "iata": "OTP" },
"scheduled_departure_at": "2026-08-02T06:15:00.000Z",
"scheduled_arrival_at": "2026-08-02T09:40:00.000Z"
},
"boarding": { "gate": "B22", "seat": "14C" },
"booking": { "pnr": "R7K92M" }
}
}
]
}Header parameters:
| Parameter | Description |
|---|---|
Request-Id | Echo back the Request-Id from the request for tracing. |
Body parameters:
| Parameter | Description |
|---|---|
passes | Array of full pass objects. Same shape as pass creation, including template_id. May be empty. |
Handling other response scenarios
The examples above show two successful scenarios - passes returned, and no passes. In other cases, your status code tells Revolut what to do next.
| Status code | When to return it | What Revolut does |
|---|---|---|
202 | Transaction accepted but outcome not ready yet (e.g. payment authorised, ticket not finalised) | Retries, honouring Retry-After |
404 | Transaction not found in your system | Stops retrying |
410 | Transaction existed but is now void (refund or cancellation) | Stops retrying |
429 / 503 | Temporary capacity issue on your side | Retries, honouring Retry-After |
Include a Retry-After header (seconds) with 202, 429, and 503 to tell Revolut when to retry. Without it, Revolut uses its default backoff schedule.
Handling pending transactions
Sometimes the transaction has happened on Revolut's side, but your system hasn't finished processing it yet. For example, the payment may have been authorised but the order or ticket hasn't been finalised in your backend.
In this case, return 202 to tell Revolut the transaction was accepted but the outcome isn't ready yet. Revolut will retry, honouring the Retry-After header you return. Use this to decouple Revolut's transaction detection from your processing pipeline without making Revolut wait or fail.
After auto-provisioning
Once Revolut creates the passes from your transaction results, it sends a push notification to the customer. The customer sees the pass in their wallet and can install it with a single tap.
From there, pass updates work the same as for any other pass. See pass updates. For auto-provisioned passes, address them by ref:<partner_reference> when pushing updates, since Revolut never returned you a pass id.
Implementation checklist
Work through these checks in order - implement, test in sandbox, then validate on production before going live.
- Complete onboarding and register your callback base URL
- Implement
POST /transaction-outcomes/searchendpoint on your server - Match transactions using
rrn,merchant_id, andtransaction_at - Return
200with passes array (may be empty) - Return
202when the transaction is accepted but outcomes aren't ready - Return
404/410for not-found/void transactions - Validate Revolut's authentication (mTLS, JWS, or OAuth 2.0)
Sandbox testing:
- Have Revolut send a sandbox transaction and confirm your endpoint returns
200with the correct passes - Return
202for a pending transaction and confirm Revolut retries - Return
404for an unmatched transaction and confirm Revolut stops retrying - Return
410for a voided transaction and confirm Revolut stops retrying - Verify that invalid authentication requests are rejected with
401
Production validation before go-live:
- Confirm Revolut can reach your production callback endpoint
- Validate that real card transactions trigger auto-provisioning and the customer receives the pass
- Confirm
Request-Idis echoed back in responses for tracing