Skip to main content
The merchant API gives you programmatic payments with three endpoints and zero payment UI: create an intent, redirect the buyer to the returned checkoutUrl, and react to the webhook. Amounts are per-order, titles are per-order — everything a shop needs. You’ll need an API key with the Write scope (plus Read for fetching).

Step 1 — Create a payment intent

Response (201 Created)
Once a payment settles, the same object also tells you what you were credited. feeAmount (Qint’s fee) and netAmount (the invoice amount less that fee) are in currency, computed on the quote, and null until the status is settled. creditAmount, creditCurrency and creditRate are what the payment added to your balance: creditAmount (2 decimal places) in creditCurrency, your Account Currency at the time of the quote, at creditRate = units of creditCurrency per 1 unit of currency (1 when they match), fixed when the buyer was quoted. All three are null until the payment is credited, and on payments settled before 20 October 2026. settledAmount is the fiat amount the payment partner realised, booked after settlement (null until then); it is for information and changes neither the fee, the net nor the credit. If the buyer paid slightly short within your accepted underpayment, underpaid is true, receivedAmount is the fiat amount actually paid and underpaidBps how short it was (100 = 1 %); the fee, the net and the credit are computed on receivedAmount. reversed / reversedAt are set when the execution partner reversed a settled payment — treat it as not paid. Field rules (full reference):

Currency availability

Qint can switch a currency off for new payments, and back on, at any time. While a currency is off, creating an intent in it returns 400 with a detail such as GBP is currently disabled on Qint. currency must be one of: CHF, EUR, USD. Intents, links and invoices already created in that currency keep working and can still be paid, and existing balances are unaffected. This 400 has no machine-readable code — the problem document carries only the usual type, title, status, detail and traceId. If you must recognise it after the fact, match detail against is currently disabled on Qint; the list after “must be one of” is the currencies enabled at that moment. The reliable way is to check before you create: call the public, unauthenticated list currencies endpoint — GET https://api.qint.ch/api/v1/public/currencies returns every currency with enabled: true|false — and hide or disable crypto payment for a currency that is off. The response may be cached for 30 seconds; cache it on your side for a minute or so rather than calling it on every checkout, and still handle the 400 for the moment a currency is switched off between your check and the create.

Payment limits

Every payment must be between CHF 10 and CHF 100’000. For EUR, USD and GBP the limit is judged on the CHF equivalent at the current exchange rate. Qint can agree a different cap for your merchant account; contact support@qint.ch if your tickets exceed CHF 100’000. The check runs when you create the intent, and again when the buyer starts paying on the hosted checkout. Outside the band, the API answers 400 with code: "amount_out_of_range":
Response (400 Bad Request)
minChf and maxChf are your effective limits (maxChf is your agreed cap if you have one), and amountChf is the CHF equivalent of the amount you sent. Do not retry; fix the amount. If the exchange rate source is unavailable, the API fails closed with 503 and code: "rate_unavailable" (“Exchange rate unavailable — try again in a minute.”). Retrying after a minute is safe, especially with the same idempotencyKey. If you create an intent and your cap is lowered before the buyer pays, the hosted checkout shows the buyer “This payment is outside the merchant’s allowed range — please contact the merchant.” (HTTP 409, code: "amount_out_of_range").

Terms acceptance

The Qint terms, which include the fee schedule, apply from their effective date. If your account has not accepted the terms in force by that date, Qint pauses new payments until an Owner or Admin accepts them in the dashboard. Creating an intent then answers 403 with code: "terms_acceptance_required":
Response (403 Forbidden)
The same response comes back when a buyer starts paying through a payment link or an invoice, so the hosted checkout shows them that message. Nothing else is paused: payments already in progress continue to settle, a retry with the idempotencyKey of an existing intent still returns it, your balance stays available and payouts continue as normal. Owners and Admins receive one email when the pause takes effect. Acceptance takes effect immediately, and the next request succeeds. If you do not want to continue under new terms, the agreement can be ended with the ordinary 90-day notice period. When Qint publishes new terms with a later effective date, the dashboard asks for acceptance straight away, but nothing is paused before that date.

Step 2 — Redirect the buyer

Send the buyer to checkoutUrl. The hosted checkout shows your title, amount and branding, lets the buyer pick an asset and network, and displays the exact crypto amount and deposit address. If the amount is outside your allowed range at that moment, the buyer sees “This payment is outside the merchant’s allowed range — please contact the merchant.” instead. The whole attempt lives inside the 15-minute window that started at creation (expiresAt).

Step 3 — Learn the outcome

Webhooks are the source of truth. Create a webhook endpoint and handle payment.status events — you’ll get one for every status transition. Fulfil the order when the status reaches settled. Reconcile with GET. Whether as a fallback for a missed webhook, on the buyer’s return to your site, or in a periodic sweep:
The reported status is live — an intent whose window lapsed reads expired immediately. You can also list intents with a status filter and paging.

The status lifecycle

Terminal states never change — with one deliberate exception: you can accept an underpaid, expired payment from the dashboard review queue, which moves it expired → settled with all settlement side effects.

Idempotency

idempotencyKey is required and unique per merchant. If a create request is retried with a key you’ve used before, the API returns the original intent with 200 OK instead of minting a duplicate (a fresh create returns 201 Created). A request without one fails with 400.
Derive the idempotency key from your own records — your order id is perfect. Then a network timeout is never scary: retry the exact same request and you’ll get the same intent back.

The return URL

Pass returnUrl (absolute https, ≤ 500 characters) and the hosted checkout shows a “Return to merchant” action. When the buyer clicks it, they navigate back to your URL with two query parameters appended:
The return redirect is a UX convenience, not a payment confirmation — the buyer may return before settlement, or never return at all. Verify with the webhook or GET /api/v1/intents/{id} before fulfilling.