Guide · Developers

Crypto payment API quickstart: from signup to your first payout

The shortest correct path through a real integration: what a person sets up once, what your backend calls, what comes back and how you know the money is yours.

Updated · 3 min read

Build it with an AI agent

Give this to your AI agent

Paste it into Claude Code, Cursor or any agent that can read a URL. It reads the OpenAPI contract and the signing recipe, and follows the rules for handling real funds.

Read https://www.payaries.com/ai-agents.md and integrate Payaries crypto payments into this project. Follow its rules for handling real funds.
OpenAPI specDocs as markdown

Never paste your API secret into an agent. Keep it in an environment variable.

01Before you start

  • Payaries runs on real mainnet networks only. There is no sandbox. Test-mode keys transact on the same chains as live keys, so plan the final test as a small real payment.
  • You need a server. The API secret must never reach browser code.
  • The API lives at https://bqpay-api-gateway.buildquick.tech. The contract is at https://www.payaries.com/openapi.json.

021. One-time setup in the dashboard

  1. 01Create an account. Passwords need at least 10 characters and can't be a well-known one.
  2. 02Create a test API key from the dashboard. The key and secret are shown once; store them as PAYARIES_API_KEY and PAYARIES_API_SECRET. A test key works immediately; live keys are issued once your account is approved.
  3. 03Submit your business profile. This is mandatory before any invoice, test or live: until then POST /v1/invoices returns 403 with code: "ONBOARDING_REQUIRED". Submitting is enough to start testing.
  4. 04Register your webhook URL (a public https:// address, no redirects) and create the webhook secret. It is shown once and is not the API secret.

032. See what you can accept

GET /v1/chains is public and always authoritative. An asset appears only once it is actually configured.

Response
{ "chains": [ { "chainId": "tron-mainnet", "name": "Tron Mainnet", "assets": ["TRX", "USDT_TRC20"] }, ... ] }

043. Create an invoice

Every request is signed; the signing guide has the exact recipe and a test vector for Node, Python, Go and shell. referenceId is your idempotency key: sending the same one again returns the existing invoice, never a duplicate.

curl
# body: {"referenceId":"order_10231","fiat":{"amount":"49.99","currency":"USD"},"crypto":{"asset":"USDT_TRC20","chainId":"tron-mainnet"}}
# X-Timestamp and X-Signature come from the signing guide's helper
curl -s -X POST https://bqpay-api-gateway.buildquick.tech/v1/invoices \
  -H "X-Api-Key: $PAYARIES_API_KEY" -H "X-Timestamp: $TS" -H "X-Signature: $SIG" \
  -H "Content-Type: application/json" \
  -d "$BODY"

The response carries the one-time depositAddress.value, the locked rate in crypto.rateLocked, the amount in crypto.amountDue, expiresAt (one hour by default) and a checkoutId. Show the customer the address and amount, or send them to /pay/<checkoutId>, a no-login payment page. To bring them back to your store afterwards, pass a successUrl and cancelUrl (hosted checkout). Never share the invoice id.

Common refusals: 400 for an unsupported or disabled asset, 400 BELOW_MINIMUM_INVOICE on chains where Payaries pays the gas (read minimumUsd from the error), 403 ONBOARDING_REQUIRED, 400 REDIRECT_URL_NOT_ALLOWED when a successUrl or cancelUrl host isn't one of your redirect domains, and 402 FEE_WALLET_INSUFFICIENT for assets that require a funded fee wallet.

054. Handle the webhook

Payaries POSTs a signed event on every status change. Verify X-Gateway-Signature against the raw body first (how), then dedupe on idempotencyKey and make your handler idempotent on (invoiceId, status).

invoice.status_changed
{
  "event": "invoice.status_changed",
  "idempotencyKey": "3f9c1a...",
  "invoiceId": "66f...",
  "status": "confirmed",
  "referenceId": "order_10231",
  "timestamp": 1725700000000,
  "data": { "attempt": 0 }
}
StatusMeaningDo
pendingWaiting for the customerNothing
detected, confirmingSeen on-chain, collecting confirmationsShow "payment received, confirming"
confirmedConfirmations reached; your ledger is creditedFulfil the order
forwarding, settledSwept to your Payaries wallet; now withdrawableNothing (reconcile)
underpaidLess than the amount due arrivedSee underpaid payments
expiredNo payment before the deadlineRelease the order. Final, unless an event later moves it out
failedThe invoice failedDon't fulfil; contact support if funds were sent

Fulfil on confirmed, not on a wallet or explorer: a different token sent to the address, including a fake one with a real-looking name, is flagged on the invoice and never counted.

065. Check the balance and withdraw

GET /v1/merchants/me/balance returns four buckets per chain and asset: available (withdrawable now), pendingSweep (credited, awaiting the sweep to your wallet), pending (reserved by a withdrawal in flight) and frozen (an admin hold, normally zero). A withdrawal is debited from available only; asking for more is a 409.

Quote first, then create the withdrawal with an Idempotency-Key. The details are in the withdrawals guide.

07Go-live checklist

  • Signing tested against a GET with a query string and a POST, using the published test vector.
  • Every retry re-signs; every withdrawal retry reuses its Idempotency-Key.
  • Webhook endpoint: public HTTPS, no redirects, verifies the signature on the raw body, dedupes, answers 2xx within 10 seconds.
  • Order logic tolerates a status change out of expired, and alerts on unclaimedDeposits or latePaymentDetectedAt.
  • If you use hosted checkout: redirect domains are registered, and the return route shows a result but fulfils only from the webhook.
  • Secrets live in a secrets manager; the API-key IP whitelist is set to your servers.
  • A small real payment ran end to end: create, pay, settled, withdraw.

Questions

Why does POST /v1/invoices return ONBOARDING_REQUIRED?
Submitting the business profile is mandatory before any invoice, test or live, from the API or the dashboard. Submit it once in the dashboard; submitting is enough to start testing.
When should I fulfil the order?
When the invoice reaches confirmed, learned from a signature-verified webhook or from GET /v1/invoices/:id. Your ledger is credited at that moment; the amount becomes withdrawable once the sweep settles.
How do I avoid creating duplicate invoices?
Use a unique referenceId per order. Sending the same referenceId again returns the existing invoice.

Next

Create a test key and run a real invoice

Test keys work immediately, with no approval step. Payaries runs on real mainnet networks, so use a small real payment to test.