S
docs.syncra.money
Operations

Payment passes (Passes)

Using the payment pass system for guaranteed payment routing and fee management in Syncra V2

Payment passes (Passes)

The payment passes (Passes) mechanism is an advanced Syncra V2 feature that lets merchants reserve specific payment details (cards, SBP phone numbers, IBANs for international transfers) for particular payers for a defined period.

This guarantees that payments from VIP clients go through, optimizes routing, and protects the payment details from overload (Anti-Spam). A pass bypasses the standard cascade routing — a deal with a matching pass is pinned to a specific provider.


Creating a pass

POST /api/v1/p2p/merchant/passes

Request parameters (JSON Body)

FieldTypeRequiredDescription
amountint64YesThe reservation amount in the currency's minor units (the only required field).
currency_idstring(UUID)NoCurrency UUID; empty = any currency.
payment_method_idstring(UUID)NoA specific payment method UUID; empty = any method. UUID only — a slug does not resolve on the pass branch (unlike PayIn/PayOut deals).
requisite_typestringNoPayment details type: BANK_CARD, SBP, E_WALLET, SIM, or BANK_TRANSFER (IBAN, for international markets — Havale/SEPA/...); empty = any type. The full reference is in Requisite types.
expires_atstring(RFC3339)NoPass expiration time. Optional: an empty or missing value means the pass never expires (expires_at = NULL).
provider_idstring(UUID)NoA specific provider UUID (when pinning is required).

Request example (with expiration)

{
  "amount": 1000000,
  "currency_id": "4ac148fe-19a3-45bb-b992-019fac55b72c",
  "requisite_type": "BANK_CARD",
  "expires_at": "2026-07-12T18:30:00Z"
}

Request example (no expiration)

{
  "amount": 1000000,
  "currency_id": "4ac148fe-19a3-45bb-b992-019fac55b72c",
  "requisite_type": "BANK_CARD"
}

If expires_at is omitted or empty, the pass is valid indefinitely (NULL in the database). This is useful for permanent VIP routes. To limit the validity period, pass an RFC3339 timestamp.

Response example (200 OK)

{
  "pass": {
    "pass_id": "812d77a0-0d3a-4422-b91c-7cb052a21abc",
    "status": "ACTIVE",
    "amount": "1000000",
    "currency_id": "4ac148fe-19a3-45bb-b992-019fac55b72c",
    "payment_method_id": "",
    "requisite_type": "BANK_CARD",
    "expires_at": "2026-07-12T18:30:00Z"
  }
}

Pass statuses

A pass goes through a two-phase commit that prevents pass loss when a deal expires. There are 4 canonical states (the domain enum PassStatus):

StatusDescriptionTransition
ACTIVEThe pass is created, the payment details are reserved, and it awaits matching with a deal.→ RESERVED (a deal claimed the pass) or → EXPIRED (the TTL ran out).
RESERVEDA deal claimed this pass but has not finished yet. No other deal can match an ACTIVE pass while it is RESERVED.→ USED (the deal completed successfully) or → ACTIVE (the deal expired or was canceled — the pass returns to the pool via ReleasePass).
USEDThe deal that claimed the pass completed successfully. A terminal state — the pass cannot be reused.Terminal.
EXPIREDThe pass TTL ran out before it was consumed. A terminal state — checked by the expires_at guard condition in the MatchActivePass request.Terminal.

Transition diagram

        ┌─────────┐  MatchAndReserve   ┌───────────┐
        │ ACTIVE  │ ─────────────────► │ RESERVED  │
        └─────────┘                    └───────────┘
            │                            │       │
   TTL      │ ConfirmPass (deal done)    │       │ ReleasePass
   exceeded │                            ▼       │ (deal expired/canceled)
            │                         ┌─────────┐│
            ▼                         │   USED  ││
        ┌─────────┐                   └─────────┘│
        │ EXPIRED │                              ▼
        └─────────┘                         (back to ACTIVE)

Two-phase commit (loss protection)

  1. MatchAndReserve — ACTIVE → RESERVED when a deal is created.
  2. ConfirmPass — RESERVED → USED when the deal completes successfully.
  3. ReleasePass — RESERVED → ACTIVE when the deal expires or is canceled (via PaymentTimeoutWorker).

If the deal holding a pass (RESERVED) expires or is canceled, the pass is not lost — it returns to the pool (ACTIVE) and becomes available for matching again. This guarantees that VIP payment details never get stuck on failed deals.


Competitive reservation

If two deals try to reserve the same pass at the same time, the first transaction wins and the second one gets an error (RFC 9457 problem+json):

HTTP/1.1 409 Conflict

{
  "type": "about:blank",
  "title": "Conflict",
  "status": 409,
  "detail": "{\"code\":10,\"message\":\"pass was concurrently reserved\",\"details\":[]}",
  "instance": "/api/v1/p2p-engine"
}

This conflict is retry-safe: repeat the request with the same idempotency_key after a short pause (200–500 ms). See the Retry matrix.


On this page