InOut

Create payment

POST /v1/payment

Create an inbound payment. The merchant collects funds; InOut routes the operation to a configured provider and returns the current state.

Payment details must be ready within 2 seconds of arrival at the gateway. The API caps synchronous processing at 3 seconds, including cancellation admission. A smaller shop wait setting shortens the result window; a larger setting cannot extend it. An unresolved provider request continues in the background so a late payment can be cancelled, without returning its details. Retrying with the same idempotency key does not restart the deadline.

Endpoint

POST /v1/payment
Content-Type: application/json
X-API-Key: 01234567-89ab-4cde-8f01-23456789abcd

The X-API-Key header is required — see Authentication. The key fixes the merchant and shop scope; do not send merchant_id or shop_id in the body.

Request

FieldTypeRequiredNotes
merchant_operation_idstringyesIdempotency key within your shop. Any non-empty string (UUID, hex, integer-as-string, …). A retry returns the existing operation; changing its type, method, currency, amount or client causes 412 Precondition Failed.
client_idstringyesYour end-user identifier. Any non-empty string.
client_ipstringnoClient IP address, used for antifraud.
methodstringyesLogical payment method, e.g. WT_RUB_C2C. See Payment methods.
amountstringyesDecimal string with scale 2, e.g. "1500.00".
currencystringyesISO 4217 code, e.g. "RUB".

InOut allocates the payment destination and returns it in the response.

A retry does not update the existing operation's metadata. Changing client_ip does not itself cause an idempotency conflict.

Example

curl -X POST https://api.alva.co.nl/v1/payment \
  -H "Content-Type: application/json" \
  -H "X-API-Key: 01234567-89ab-4cde-8f01-23456789abcd" \
  -d '{
    "merchant_operation_id": "8a26f5cf-1f56-4cda-8d8a-2a915b5e4b58",
    "client_id":   "57b8a4ca-3a4b-4f01-9a5e-1f29c1cefb9b",
    "client_ip":   "203.0.113.10",
    "method":      "WT_RUB_PHONE",
    "amount":      "1500.00",
    "currency":    "RUB"
  }'

Response

200 OK with the operation object, subject to the 2-second result window and 3-second synchronous limit described above. The gateway does not return a synthetic pending response when no provider accepted the operation; that path rejects. An accepted payment may remain non-terminal and its final state then arrives by webhook.

When the chosen route requires a receipt confirmation, the create response status is awaiting_confirmation (instead of pending). This means the end user must upload a receipt via POST /v1/confirm_payment before the payment can proceed. A webhook fires immediately on entry to awaiting_confirmation so you can prompt the user; the terminal callback fires once the receipt is reviewed and the payment reaches a terminal status.

Operation object

{
  "operation_id": "f6c2e7d4-3e5b-4f1f-8a02-9d6c7e8a1b22",
  "type": "payment",
  "status": "awaiting_confirmation",
  "method": "WT_RUB_PHONE",
  "expected_amount": "1500.00",
  "actual_amount": "0.00",
  "currency": "RUB",
  "fx_source": "rapira",
  "fx_rate": "0.013000",
  "settlement_amount": "19.11",
  "settlement_currency": "USDT",
  "commission_amount": "0.39",
  "external_confirmation": {
    "required": true,
    "methods": ["receipt"]
  },
  "number": "+79991234567",
  "name": "Ivan Ivanov",
  "bankname": "sber",
  "bankname_details": "Сбербанк"
}

expected_amount mirrors what you sent. actual_amount is the physical fact known for the operation; do not infer it from status or replace zero with the requested amount. It is normally non-zero after money is processed.

When a route uses auto-conversion, fx_source and fx_rate are present as soon as the route is chosen — this is a quote (expected amount × rate), available well before the operation resolves. recorded_currency and recorded_amount are the ledger fact (actual amount × rate) and appear only once actual_amount is non-zero, i.e. once the operation has actually processed money (completed, or a self-matching partial). A pending response therefore carries only the fx_rate/fx_source quote — never recorded_amount/recorded_currency. Example of a completed response on the same operation:

{
  "operation_id": "f6c2e7d4-3e5b-4f1f-8a02-9d6c7e8a1b22",
  "type": "payment",
  "status": "completed",
  "method": "WT_RUB_PHONE",
  "expected_amount": "1500.00",
  "actual_amount": "1500.00",
  "currency": "RUB",
  "recorded_currency": "USDT",
  "recorded_amount": "19.50",
  "fx_source": "rapira",
  "fx_rate": "0.013000",
  "number": "+79991234567",
  "name": "Ivan Ivanov",
  "bankname": "sber",
  "bankname_details": "Сбербанк"
}

settlement_amount is the amount that moves on your balance net of the InOut commission, in settlement_currency (the ledger currency, equal to currency without conversion): for a payment it is what is credited to you after commission, for a payout the total charged to you including commission. commission_amount is the InOut commission. Before a terminal status these are the expected figures for the routed amount and settle to the captured amount at terminal.

The destination fields (number, name, bankname, bankname_details) are populated once InOut has allocated an inbound destination — until then they are absent from the response. See Destinations for which fields each method uses.

expires_at (RFC3339, e.g. "2026-08-18T12:30:00Z") is the payment expiry when one is available. It is omitted if no expiry is reported.

While status is manual_check, the operation also contains manual_check_reason: potential_fake when the receipt may be forged or otherwise suspicious, or not_terminal when the shown transfer is still in progress — or when the uploaded file is not a PDF or automatic validation was temporarily unavailable, so it could not be validated, which is never treated as a fraud signal. It is omitted for every other status. If the reason is unknown, InOut returns potential_fake.

Hosted payment page

Instead of rendering the requisites yourself you can hand the payer a ready-made hosted checkout page, with either a fixed amount (POST /v1/payment/link) or an amount the payer picks from what the matching pool can actually serve right now (POST /v1/payment/link/flexible). Both are documented in Hosted payment link.

Errors

See Errors. The most common ones for this endpoint:

  • 400 — invalid JSON or a required field is missing/malformed.
  • 401 — X-API-Key missing or invalid.
  • 412 — merchant_operation_id collision with different operation parameters listed in Request.

On this page