Sandbox
Help

Auto-provision transaction results

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:

ComponentRole in this flow
Wallet Partner Callbacks POST /transaction-outcomes/searchThe endpoint you host - Revolut calls it after detecting an eligible card transaction
transaction_contextMatch criteria Revolut sends: rrn, merchant_id, transaction_at, total
passes arrayYour response - full pass objects (same shape as create-pass, including template_id)
Merchant IDCard acceptor identifier (DE 42 in ISO 8583) - registered during onboarding, used by Revolut to match transactions to you
RRNRetrieval 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

  1. A customer makes an eligible purchase with a Revolut card at your business.
  2. Revolut detects the transaction is eligible for auto-provisioning (matched by merchant ID, amount, RRN (Retrieval Reference Number), and rules).
  3. Revolut calls your POST /transaction-outcomes/search endpoint with the transaction context.
  4. You match the transaction and return the passes it produced.
  5. 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_id assigned

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.

Revolut request
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:

ParameterDescription
Revolut-Api-VersionAPI version of the request.
Request-IdUnique 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:

ParameterDescriptionRequired
transaction_context.rrnISO 8583 Retrieval Reference Number - uniquely identifies the transaction for matchingYes
transaction_context.merchant_idCard acceptor identifier - identifies the sub-merchant for aggregatorsYes
transaction_context.transaction_atTimestamp of the original purchaseYes
transaction_context.totalTransaction amount and currency - supplied for stronger matching, but you may skip validating itNo

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.

Example matching logic
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 passes

3. 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.

Example response - passes returned
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:

ParameterDescription
Request-IdEcho back the Request-Id from the request for tracing.

Body parameters:

ParameterDescription
passesArray 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 codeWhen to return itWhat Revolut does
202Transaction accepted but outcome not ready yet (e.g. payment authorised, ticket not finalised)Retries, honouring Retry-After
404Transaction not found in your systemStops retrying
410Transaction existed but is now void (refund or cancellation)Stops retrying
429 / 503Temporary capacity issue on your sideRetries, 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/search endpoint on your server
  • Match transactions using rrn, merchant_id, and transaction_at
  • Return 200 with passes array (may be empty)
  • Return 202 when the transaction is accepted but outcomes aren't ready
  • Return 404/410 for 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 200 with the correct passes
  • Return 202 for a pending transaction and confirm Revolut retries
  • Return 404 for an unmatched transaction and confirm Revolut stops retrying
  • Return 410 for 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-Id is echoed back in responses for tracing

What's next

Update a pass
Keep auto-provisioned passes current via push or poll
Create a pass
Create boarding passes, event tickets, and loyalty cards via the Partner API
Wallet Partner Callbacks
Endpoints you host: poll update, transaction results
Rate this page