API reference
Version 1.0.0. Generated from the API's OpenAPI 3.1.0 description at build time.
Base URL https://api.tillsafe.com
Early access: keys are issued on request. The API is not open to the public yet. Request early access
Errors Every error's doc_url points into the error codes.
Tillsafe: non-custodial stablecoin and crypto payments.
- Authenticate with
Authorization: Bearer <secret key>(cp_test_sk_…/cp_live_sk_…). Test keys see only test-mode objects on testnets; live keys only live ones. - Every POST requires an
Idempotency-Keyheader. - Money is always a decimal string plus an asset or currency code, never a JSON number.
- Errors share one shape:
{"error": {type, code, message, param, doc_url, request_id}}. - Webhooks are signed:
x-cryptopay-signature: t=<unix>,v1=<hex HMAC-SHA256(secret, "{t}.{body}")>.
Authentication
publishable_key:Authorization: Bearercp_{test|live}_pk_…Publishable key: checkout endpoints only.
publishable_key_query: query parameterkeyPublishable key as a query parameter (for EventSource). Secret keys are refused here.
secret_key:Authorization: Bearercp_{test|live}_sk_…Secret key. Server-side only; never in a browser or a URL.
Endpoints
Invoices
Requests for payment
- GET
/v1/invoicesList invoices, newest first. - POST
/v1/invoicesCreate an invoice. - GET
/v1/invoices/{id}Retrieve an invoice. - POST
/v1/invoices/{id}/cancelCancel an invoice nobody has paid. - POST
/v1/invoices/{id}/resolve_reviewAccept or reject a held payment.
Payments
On-chain transfers attributed to invoices
- GET
/v1/paymentsList payments (on-chain transfers attributed to invoices), newest first.
Webhooks
Delivery log and replay
- GET
/v1/webhook_deliveriesList webhook delivery attempts, newest first: status code, latency, error and a response snippet for each. - POST
/v1/webhook_deliveries/{id}/replayDeliver a delivery's event again, to the current webhook URL, as a new delivery.
Merchant
Settings for the key's mode
- GET
/v1/merchant/settingsRetrieve the merchant settings for this mode. - POST
/v1/merchant/settingsUpdate the merchant settings for this mode. Omitted fields are unchanged. - POST
/v1/merchant/webhook_secret/rollReplace the webhook signing secret. The response is the only time the new secret is readable; deliveries are signed with it from now on.
Checkout
Payer-facing, publishable-key endpoints
- GET
/v1/checkout/exchangesExchange withdrawal networks and fees. - GET
/v1/checkout/invoices/{id}The payer's view of an invoice. - GET
/v1/checkout/invoices/{id}/lookup"Already sent? Find my payment." - POST
/v1/checkout/invoices/{id}/notifyEmail me a receipt. - POST
/v1/checkout/invoices/{id}/payment_optionPick a payment option: lease its deposit address. - POST
/v1/checkout/invoices/{id}/wallet_bindingPay from a connected wallet. - GET
/v1/checkout/links/{slug}A payment link, as the payer sees it. - POST
/v1/checkout/links/{slug}/invoicesCreate this payer's invoice from a payment link. - GET
/v1/invoices/{id}/eventsLive invoice status as Server-Sent Events.
Payment links
Reusable, shareable links that create one invoice per payer
- GET
/v1/payment_linksList payment links, newest first. - POST
/v1/payment_linksCreate a payment link. - GET
/v1/payment_links/{id}Retrieve a payment link. - POST
/v1/payment_links/{id}Update a payment link.
Refunds
Non-custodial refunds: the payer claims, we screen, you send from your wallet
- POST
/v1/invoices/{id}/refundsCreate a refund for an invoice. - GET
/v1/refundsList refunds, newest first. - GET
/v1/refunds/{id}Retrieve a refund. - POST
/v1/refunds/{id}/cancelCancel a refund that has not been sent. Its claim link stops working. - POST
/v1/refunds/{id}/mark_sentRecord that you sent the refund.
Reconciliation
The ledger vs the chain vs the invoices for a period, with every difference explained or flagged
- GET
/v1/reconciliationReconcile a period. - GET
/v1/reconciliation/balancesLedger balances as of a date. - GET
/v1/reconciliation/exportExport a reconciliation as CSV.
Refund claims
Payer-facing; the claim token from the refund link is the credential
- GET
/v1/refund_claims/{token}A refund offered to you (the payer). - POST
/v1/refund_claims/{token}Choose where to receive the refund.
Test helpers
Test mode only: simulate chain activity
- POST
/v1/test_helpers/invoices/{id}/simulate_paymentSimulate a payment (test mode only).
Health
Liveness and readiness
- GET
/healthzLiveness: the process is up and serving HTTP. Never touches dependencies. - GET
/readyzReadiness: the payment service can take traffic.
Subscriptions
Plans, customers and invoice-based recurring billing: renewal invoices, reminders, retries, prepaid credit
- GET
/v1/customersList customers, newest first. - POST
/v1/customersCreate a customer. - GET
/v1/customers/{id}Retrieve a customer, with their prepaid credit. - POST
/v1/customers/{id}/top_upTop up a customer's prepaid credit. - GET
/v1/plansList plans, newest first. - POST
/v1/plansCreate a plan. - GET
/v1/plans/{id}Retrieve a plan. - POST
/v1/plans/{id}/archiveArchive a plan: it takes no new subscriptions, and existing ones keep renewing. - GET
/v1/subscriptionsList subscriptions, newest first. - POST
/v1/subscriptionsSubscribe a customer to a plan. - GET
/v1/subscriptions/{id}Retrieve a subscription. - POST
/v1/subscriptions/{id}/cancelCancel a subscription, now or at the end of the paid period. - POST
/v1/subscriptions/{id}/pausePause a subscription: no invoices and no reminders until it is resumed. An open renewal invoice with nothing paid is cancelled. - POST
/v1/subscriptions/{id}/resumeResume a paused subscription. Inside a paid period it simply continues; after it, billing restarts now with a new period (and a new invoice). Resuming a subscription that is not paused (active, past due or incomplete) changes nothing and returns it.