# 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 2026-10-10 · Payaries · https://www.payaries.com/guides/crypto-payment-api-quickstart

## Before 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`.

## 1. One-time setup in the dashboard

1. [Create an account](https://www.payaries.com/signup). Passwords need at least 10 characters and can't be a well-known one.
2. Create 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. Submit 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. Register your **webhook URL** (a public `https://` address, no redirects) and create the **webhook secret**. It is shown once and is not the API secret.

> **These steps are session-only on purpose.** Creating keys, the webhook secret, the webhook URL and your redirect domains can't be changed with an API key. That is what stops a leaked key from minting a sibling or pointing your webhooks somewhere else.

## 2. 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"] }, ... ] }
```

## 3. Create an invoice

Every request is signed; the [signing guide](https://www.payaries.com/guides/sign-crypto-payment-api-requests) 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](https://www.payaries.com/guides/hosted-crypto-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.

## 4. Handle the webhook

Payaries POSTs a signed event on every status change. Verify `X-Gateway-Signature` against the raw body first ([how](https://www.payaries.com/guides/verify-crypto-webhook-signatures)), 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 }
}
```

| Status | Meaning | Do |
|---|---|---|
| `pending` | Waiting for the customer | Nothing |
| `detected`, `confirming` | Seen on-chain, collecting confirmations | Show "payment received, confirming" |
| `confirmed` | Confirmations reached; your ledger is credited | **Fulfil the order** |
| `forwarding`, `settled` | Swept to your Payaries wallet; now withdrawable | Nothing (reconcile) |
| `underpaid` | Less than the amount due arrived | See [underpaid payments](https://www.payaries.com/guides/handle-underpaid-and-late-crypto-payments) |
| `expired` | No payment before the deadline | Release the order. Final, unless an event later moves it out |
| `failed` | The invoice failed | Don'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.

## 5. 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](https://www.payaries.com/guides/crypto-withdrawals-api).

## Go-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](https://www.payaries.com/guides/hosted-crypto-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.

## Try it

Create a test key and run a real invoice: [Payaries — www.payaries.com](https://www.payaries.com/signup). Payaries is a multi-chain crypto payment gateway — https://www.payaries.com

## Keep reading

- https://www.payaries.com/ai-agents — Build a crypto payment integration with an AI agent, safely
- https://www.payaries.com/guides/sign-crypto-payment-api-requests — How to sign crypto payment API requests (HMAC-SHA256)
- https://www.payaries.com/guides/hosted-crypto-checkout — Hosted crypto checkout: send the customer, get them back
- https://www.payaries.com/guides/crypto-payment-api-errors-and-retries — Crypto payment API errors, rate limits and safe retries
- https://www.payaries.com/guides/crypto-withdrawals-api — Withdrawing crypto through the API: quote, idempotency and approval
- https://www.payaries.com/crypto-payment-api — A crypto payment API that behaves like the rest of your stack
- https://www.payaries.com/guides/accept-crypto-on-shopify — Accept crypto on Shopify without payments-partner approval
