Skip to main content
Taberna hosts the page the buyer pays on. You never build coin selection, exchange-rate display, deposit addresses, QR codes or confirmation progress — you redirect, and you wait for the result.
Always use the URL Taberna returned. Do not construct it yourself — the host is environment-specific and the path may change.

What the buyer sees

Pick a token

The page offers the tokens in your store’s enabledPaymentMethods and nothing else. Choosing one starts a payment attempt.

Pay a locked quote

Taberna locks a crypto quote, generates a fresh deposit wallet for this one attempt, and shows the exact amount and address. The quote holds for up to 30 minutes — precisely, until min(now + 30 minutes, the order or invoice's own expiry), because a locked quote must never outlive its parent.

Watch it confirm

Taberna watches the chain and reports progress: detected, confirming, confirmed. See confirmation progress.

Land back in your app

On completion the page shows a Continue button pointing at your successUrl. The dead-end states — expired, partial, and voided for invoices — link to cancelUrl. Without them the buyer simply stays on the hosted page, so setting at least successUrl is recommended.

If the quote lapses

Nothing is lost. An attempt that expires holding zero funds leaves the parent order or invoice untouched and still payable — the buyer picks a token again and gets a fresh quote, and can keep doing that until the parent itself expires. An attempt that expires holding a non-zero but insufficient amount turns the parent partial, which is terminal. See Payment lifecycle.

If the buyer switches token mid-attempt

Also fine, and worth understanding precisely:
  • Re-selecting the same token returns the same attempt — same address, same locked quote. Reloading the page is safe.
  • Selecting a different token abandons the live attempt and locks a new quote on a newly generated address.
An abandoned attempt’s address stays watched until its own window ends. Funds sent to it after the buyer switched still complete the order or invoice. Abandonment is a UI affordance, not a lifecycle event — it emits no webhook.

The public checkout endpoints

The hosted page reads and drives payment through the /v1/checkouts endpoints. These are deliberately unauthenticated.
Possession of the short id is the credential. It is unguessable in practice, and every payload served here is redacted for a buyer audience — no buyer email, no merchant metadata, no payer IP. Never send an API key from a browser.The corollary: do not treat the id as a secret beyond being unguessable, and never grant value based on a status a browser reported. Fulfil from a webhook or a server-side read.

Retrieve a checkout

GET /v1/checkouts/{id} — no auth. {id} accepts either an order short id or an invoice short id; one resource serves both hosted pages. The response is a tagged union: read type first, then the branch fields.
items[].delivery is null until the order is completed — see Products and delivery.Deliberately absent: customerEmail, extraContext, the payer IP. The page only learns whether an email is on file, via hasCustomerEmail.
A well-formed id that resolves to nothing answers 404 with { "error": "Checkout not found" }the same message for both resources, so the endpoint never reveals which one you probed.
A malformed id never reaches the lookup. Every route that takes a short id validates it against the short-id pattern first — 21 or 22 base58 characters — and an id that fails that check is rejected by the validator as 400 { "error": "Invalid request data" }. 404 is reserved for ids that pass the pattern and then either fail to decode or decode to nothing.

The active attempt

activeAttempt is null when no quote is live, and otherwise:
cryptoAmount and receivedAmount are integer strings in the token’s smallest unit. transactions is [] until a transfer is attributed.

Confirmation progress

confirmation is payer-facing progress, and nothing gates on it.
Do not render progress while stale is true, and do not treat confirmed as “paid”. A confirmed state says the transfers on file are buried deeply enough — it says nothing about whether they add up to the amount owed. An underpayment can sit at confirmed indefinitely. The authoritative signal is the order or invoice status.
See Payment lifecycle for how each state arises and what the per-chain depths are.

Start a payment attempt

POST /v1/checkouts/{id}/attempts — no auth. Driven by the checkout UI; you normally do not call this.
Request
Returns the refreshed checkout union — the same payload as the GET, now carrying the new activeAttempt. Works on both branches.
Taberna refuses to price an attempt on a stale exchange rate rather than risk mis-quoting it. That last error is transient — the buyer retrying a moment later normally succeeds.

Attach a receipt email (orders only)

PUT /v1/checkouts/{id}/email — no auth.
Request
200 OK
The address is never echoed back — the checkout payload only ever reports the boolean.
This route is order-only. An invoice’s recipient is set by the merchant who issued it, so passing an invoice id is a 400 — not a 404.
GET /v1/checkouts/{id}/download/{orderItemId} — no auth. Mints a short-lived presigned URL for a file_shared delivery on a completed order item.
200 OK
expiresIn is the link’s lifetime in seconds — 300, five minutes. Every substantive failure — unknown checkout, order not completed, wrong item, a delivery that is not a file, an invoice id — answers with the same 404 and the same message, { "error": "Download not available" }, so the endpoint never reveals which check failed.
Both path segments are short ids and both are validated first, as everywhere else: if {id} or {orderItemId} is not a well-formed short id the request is rejected as 400 { "error": "Invalid request data" } before any lookup runs.

Polling

You may poll GET /v1/checkouts/{id} as a fallback, and the hosted page does exactly that while an attempt is live. But for your integration:
Webhooks are the recommended completion signal. If you poll from your own server, poll the authenticated endpoints — GET /v1/orders/{id} or GET /v1/invoices/{id} — not the public checkout resource. They are the merchant view, they carry extraContext and metadata, and they are not readable by anyone holding the link.
Reasonable polling shape, if you need one:
Stop polling as soon as status leaves pending (or open, for invoices): every other state is terminal.