Creating Payments

Payments are created via the POST /api/payment/init endpoint.

Authentication

All requests must include your API key in the x-api-key header:

Headertext
x-api-key: YOUR_API_KEY

The API key must be a valid UUID. Only CLIENT role accounts can use API keys.

POST/api/payment/init

Create a new payment

Finds a matching Relay provider from the pool and creates a payment intent.

Auth: x-api-key header (CLIENT role only)

{
"amount": 50.00,
"maxFee": 10,
"currency": "usd",
"externalId": "order-123",
"suffixClient": "SHOP",
"theme": "light"
}

Request Fields

FieldTypeRequiredDescription
amountnumberYesPayment amount (2-1000, max 2 decimal places)
maxFeenumberYesMaximum fee percentage you're willing to pay (max 2 decimal places). Must exceed any configured referral fees
currencystringYesCurrency code (currently only usd)
externalIdstringYesYour order reference (max 36 chars, alphanumeric + -_)
suffixClientstringNo4 Latin letters appended to statement descriptor
themestringNoPayment form theme: light (default) or dark

Response Fields

FieldDescription
idPayment UUID — use this to track the payment
statusPayment status (PENDING on creation)
typeAlways PAYMENT
providerIdMatched provider UUID
relayIdRelay user UUID
clientIdYour user UUID
relayPageURL to embed in an iframe for the customer to pay
externalIdYour order reference (echoed back)
suffixRelay's statement descriptor suffix
suffixClientYour statement descriptor suffix (if provided)
amountPayment amount (echoed back)
currencyCurrency code (echoed back)
feeActual fee percentage charged
maxFeeYour max fee (echoed back)
sendAmount you'll receive after fees (in major units)
relayWalletRelay's Solana wallet address
clientWalletYour Solana wallet address
createdAtPayment creation timestamp (ISO 8601)
themePayment form theme (echoed back)
exchangeRouteCross-currency conversion details (if applicable)

Provider Matching

The system tries up to 3 providers before giving up. If a provider fails (invalid key, insufficient balance, etc.), the next available provider is tried automatically.

Diagnosis Reasons

When no provider is available, the reason field explains why:

ReasonDescription
no_providers_in_poolNo providers are currently in the pool
currency_mismatchNo providers support the requested currency
fee_too_lowYour maxFee is lower than any provider's fee
amount_out_of_rangePayment amount is outside all providers' min/max range
insufficient_relay_balanceMatching providers don't have enough balance
rejected_by_all_relaysAll matching providers have rejected your client ID

Webhook Notifications

If you've configured a webhook URL, surstrom sends POST requests when payment status changes:

Webhook payload (header: X-Payment-Event: payment.event)json
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"type": "PAYMENT",
"status": "COMPLETED",
"amount": 50.00,
"currency": "usd",
"fee": 7,
"send": 46.50,
"externalId": "order-123",
"clientId": "your-user-uuid",
"relayId": "relay-user-uuid",
"createdAt": "2025-01-15T10:30:00.000Z",
"updatedAt": "2025-01-15T10:35:00.000Z",
"sent": "TRUE",
"txHash": "SolanaTxSignature...",
"theme": "light"
}