Skip to main content

POST /payments

Creates a payment and returns its id plus the hosted-checkout URL to redirect the customer to. Requires the X-Api-Key header and the payment:create permission (granted to gateway API keys by default).

In the default hosted mode the response is deliberately small: your server only needs to redirect the customer to paymentUrl. The hosted checkout then loads the payable assets, deposit addresses, and amounts itself, and the authoritative outcome comes from verify — so per-asset offer details are not returned here. In API checkout mode (checkoutMode: "API") the response additionally inlines the full payment object so you can render your own payment page — see the API-mode response variant below.

curl -X POST https://api-staging.finomesh.com/api/v1/payments \
-H "X-Api-Key: $FINOMESH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"currencyCode": "USD",
"amount": "49.99",
"callbackUrl": "https://shop.example.com/payment/result"
}'

Headers

HeaderTypeRequiredDescriptionExample
X-Api-KeystringYesGateway API key. Keep it server-side only.<your-api-key>
Content-TypestringYesMust be application/json.application/json

Request body

FieldTypeRequiredDefaultDescriptionExample
currencyCodestringYesInvoice currency as an ISO-style code (USD, EUR, case-insensitive). An unknown or inactive code is rejected with 422."USD"
amountstring (decimal)YesInvoice amount in currencyCode units. Decimal string — never a number, to preserve precision."49.99"
typestring (enum)NoDIRECTDIRECT for a one-off payment, or PAYLINK for a payment created from a saved payment link."DIRECT"
paylinkIdstring (UUID)NonullThe payment-link ID to bind this payment to. Provide when type is PAYLINK; omit for DIRECT."…"
checkoutModestring (enum)NoHOSTEDHOSTED — Finomesh hosts the payment page — or API — you render it yourself and drive the flow server-to-server (see Embedded Checkout / API Mode). The mode flips the callbackUrl/webhookUrl contract, per the rows below. API additionally requires your server IPs to be whitelisted in the dashboard: 422 when no allowlist is configured at all, and 403 when the calling server's IP is not on it — so even a leaked API key cannot create API-mode payments from elsewhere."API"
callbackUrlstringConditionalAbsolute URL the customer's browser is redirected to after the result. Required in HOSTED mode; forbidden in API mode (there is no hosted page to redirect from — sending it returns 422). localhost is allowed in any environment. See Hosted Checkout."https://shop.example.com/payment/result"
callbackParamsobjectNoArbitrary merchant JSON object echoed into the callback redirect's query string (e.g. your order ID). Hosted mode only.{ "orderId": "ORD-1042" }
webhookUrlstringConditionalURL Finomesh POSTs the signed webhook to when the payment reaches a terminal status. Optional in HOSTED mode (no webhook is sent if omitted); required in API mode — the webhook is the only push channel for an embedded payment's outcome."https://shop.example.com/api/finomesh-webhook"
webhookParamsobjectNoArbitrary merchant JSON object stored with the payment and echoed back for your own bookkeeping.{ "orderId": "ORD-1042" }
timeoutSecondsintegerNo3600Payment validity window in seconds. Server default (3600 = 1 hour) applies if omitted or 0. After it the payment expires.3600
txFeePayerstring (enum)Nogateway settingWho absorbs the network transaction fee: MERCHANT or CUSTOMER."CUSTOMER"
commissionFeePayerstring (enum)Nogateway settingWho absorbs the Finomesh commission: MERCHANT or CUSTOMER."CUSTOMER"
overAcceptableDiffstring (decimal)Nogateway settingOverpayment tolerance. Decimal string — never a number. Interpreted per acceptableDiffKind."0.5"
underAcceptableDiffstring (decimal)Nogateway settingUnderpayment tolerance. Decimal string — never a number."0.5"
acceptableDiffKindstring (enum)Nogateway settingHow the two tolerances are read: ABSOLUTE (flat USD) or PERCENTAGE (percent of the expected USD amount)."PERCENTAGE"
checkAmountDeviationbooleanNogateway settingWhether to apply the acceptance band at all. When false, any paid amount resolves to SUCCESS. See Amount Deviation.true
assetsarray<OfferAsset>Noauto-derivedRestrict/override the offered assets. Omit it (recommended) and the offers are derived automatically from your gateway's enabled-asset list at current prices. Element shape below.
languagestringNonullPreferred checkout display-language hint."en"
isSandboxbooleanNofalseCreate a sandbox payment — no real deposit address, checkout shows simulation buttons.false

Platform-billed gateways. On a gateway with a platform-billed arrangement, Finomesh covers the network fee and the commission on every payment — nothing is deducted from what you receive, and those fees are billed to you separately instead of per payment. A platform-billed gateway creates open-amount payments only: the fixed-amount call above returns 403 on such a gateway, and the fee-payer and tolerance fields do not apply (the arrangement fixes them). If you also need fixed-amount payments, use a separate standard gateway alongside it. This is set up per gateway — talk to your Finomesh contact.

Each element of assets[] is an OfferAsset:

FieldTypeRequiredDefaultDescriptionExample
assetIdstring (UUID)YesThe asset to offer. Must be one of your gateway's enabled assets."a1b2c3d4-…"
assetPriceUsdstring (decimal)YesThe USD price to quote this asset at. Decimal string — never a number, to preserve precision."1.00"

See Payment Statuses → How SUCCESS / ACCEPTABLE / MISMATCH is decided for exactly how the tolerance fields are applied.

Response — 201 Created

{
"success": true,
"data": {
"paymentUid": "0d9f3a64-4f0c-4b6e-9f6e-2f4f4c1b8a21",
"paymentUrl": "https://checkout-staging.finomesh.com/p/0d9f3a64-4f0c-4b6e-9f6e-2f4f4c1b8a21"
}
}
FieldTypeNullableDescriptionExample
paymentUidstring (UUID)NoThe payment UUID — store it; it keys the callback, the webhook, and verify.0d9f3a64-…
paymentUrlstringNoThe hosted-checkout URL (<checkout host>/p/{paymentUid}). Redirect the customer here.https://checkout-staging.finomesh.com/p/0d9f3a64-…
paymentobjectYesThe full public payment object. Omitted entirely in HOSTED mode; present only when checkoutMode is API — see the API-mode response variant below.(API mode only)

Next: redirect the customer to paymentUrl — see Hosted Checkout.

API-mode response variant

When checkoutMode is "API", a third field — payment — is attached: the full public payment object, in exactly the shape GET /public/payments/{id} serves (amounts, fees, status, timeoutSeconds, checkoutMode, and offers[] with assetSymbol, assetLogoUrl, networkLogoUrl, amount, txFee, commissionFee, totalValue, …). It saves the otherwise-mandatory first read — you can render your payment page straight from the create response:

{
"success": true,
"data": {
"paymentUid": "0d9f3a64-4f0c-4b6e-9f6e-2f4f4c1b8a21",
"paymentUrl": "https://checkout-staging.finomesh.com/p/0d9f3a64-…",
"payment": {
"id": "0d9f3a64-4f0c-4b6e-9f6e-2f4f4c1b8a21",
"status": "PENDING",
"checkoutMode": "API",
"amount": "49.99",
"timeoutSeconds": 3600,
"offers": [ { "id": "…", "assetSymbol": "USDT", "totalValue": "51.69", "…": "…" } ]
}
}
}
FieldTypeNullableDescriptionExample
paymentUidstring (UUID)NoThe payment UUID — keys the select call, the webhook, and verify.0d9f3a64-…
paymentUrlstringNoThe hosted-checkout URL. Present but typically unused in API mode (you render your own page).https://checkout-staging.finomesh.com/p/0d9f3a64-…
paymentobjectNoThe full public payment object — same shape GET /public/payments/{id} serves. See its complete field table, including the nested offers[].(see example above)

The hosted response is unchanged — payment never appears on a HOSTED create. Payment objects on every surface (public GET, verify) now also carry checkoutMode. See Embedded Checkout / API Mode for the full API-mode walkthrough.

Open-amount payments

Availability. Open-amount payments are a special arrangement, enabled per gateway by Finomesh. If your gateway has not been provisioned for it the endpoint returns 403 — talk to your Finomesh contact first.

An open-amount payment has no fixed amount: the payer decides how much to send, and the first on-chain deposit at or above your gateway's configured minimum completes the payment. Use it for donations, tips, top-ups, and "pay what you want" flows.

Create one at a separate endpointPOST /payments/open-amount. It is deliberately not a flag on the call above: there is no amount, and no fee-payer or tolerance fields (those are set by your gateway's billing arrangement, not per payment). There is also no assets[] override — the offered assets are always your gateway's enabled-asset list at current prices, so per-payment asset restriction is not available here. The arrangement may additionally limit open-amount offers to a specific asset list (for example stablecoins only, or stablecoins plus native coins such as ETH); assets outside it are dropped, and if none remain the create is rejected with 422. Fixed-amount payments are unaffected by this restriction.

How the received value is priced. An open-amount payment has no quoted amount, so its recorded USD value is set when the deposit confirms, at the asset's market price at that moment — not at the price when the payment was created. For stablecoins the difference is negligible. Against the minimum, the same rule applies strictly: the deposit's USD value at confirmation must clear the floor, or the payment resolves MISMATCH (the funds are still credited).

If your gateway accepts non-stable assets. You are credited in the asset the payer sent — an ETH deposit becomes an ETH balance. Its USD value keeps moving with the market until you withdraw; Finomesh does not convert it for you, and the USD figures shown on payments and balances are point-in-time valuations, not a guarantee. Platform fees are the exception: they are computed in USD at confirmation time and do not float afterwards.

curl -X POST https://api-staging.finomesh.com/api/v1/payments/open-amount \
-H "X-Api-Key: $FINOMESH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"callbackUrl": "https://shop.example.com/donation/thanks",
"timeoutSeconds": 604800
}'
FieldTypeRequiredDefaultDescription
currencyCodestringNoUSDDisplay currency for the amount, back-filled once the deposit confirms.
timeoutSecondsintegerNo3600Validity window. Donation/tip links are often long-lived — pass a larger value (e.g. 604800 for a week).
checkoutModestring (enum)NoHOSTEDSame meaning and callbackUrl/webhookUrl contract as above.
callbackUrl / callbackParams / webhookUrl / webhookParams / languageSame as the fixed-amount create.

The response shape is identical to the fixed-amount create ({ paymentUid, paymentUrl }). The checkout shows the payer a deposit address and invites any amount rather than a fixed figure.

What differs downstream:

  • The first on-chain deposit settles the payment, exactly as with a fixed-amount payment: at or above your gateway's configured minimum — surfaced to integrations as minAmountUsd on the public payment object — it resolves SUCCESS; below the minimum it resolves MISMATCH, with the minimum standing in as the expected amount. Either way the funds are captured and credited; a MISMATCH tells you the payer sent less than the minimum, and how you handle that (partial credit, refund, contacting the payer) is a commercial decision on your side. There is no second chance to top up — make the minimum visible to the payer before they send.
  • The payment can never be ACCEPTABLE — your acceptance-tolerance settings do not apply. Branch on SUCCESS vs MISMATCH (plus EXPIRED when nothing arrived), the same statuses your fixed-amount handling already covers.
  • On verify, amount / amountUsd read 0 until a deposit confirms; then they reflect what arrived on SUCCESS, or the minimum (the expected amount) on MISMATCH — read the actually-received figure from the transaction fields, as with any mismatch.

Errors

HTTP statusError codeMeaningHow to fix
401auth_api_key_invalidBad or missing API key.Send a valid X-Api-Key from a verified gateway.
403forbiddenOn /payments/open-amount: your gateway is not provisioned for open-amount payments. On /payments: your gateway is platform-billed, which creates open-amount payments only — use /payments/open-amount (or a separate standard gateway for fixed amounts). Otherwise (API mode): the calling server's source IP is not on the gateway's allowlist.Use the endpoint matching your gateway's arrangement; for the IP case, whitelist the observed source IP (echoed in the message) — behind a CDN/NAT/proxy it can differ from your DNS-advertised address.
422validation_errorMissing currencyCode / amount, unknown or inactive currency code, malformed amount, invalid checkoutMode; missing callbackUrl (HOSTED); missing webhookUrl, callbackUrl sent, or no whitelisted server IP configured (API).Fix the named field per the mode's contract — see the request body rows for HOSTED vs API rules.