Skip to content

CryptoLike API — Overview

From API call to payment status. One REST API, JSON in and out, signed with an API key. This page is the map; every route, field and error is in the OpenAPI document (OpenAPI spec, also browsable as the API reference). Authentication details — Authentication; webhook contract — Webhooks; the payment page — checkout API reference.

text
Base URL      https://api.cryptolike.net      (version in the path: /v1)
Auth          X-API-Key + X-Timestamp + X-Signature (HMAC-SHA256)   https://cryptolike.net/docs/auth
Format        JSON, snake_case; amounts are decimal strings; times RFC 3339 UTC; ids UUID
Pagination    ?limit=50&cursor=…  →  {"items":[…],"next_cursor":"…"|null}
Errors        {"error":{"code","message","details"}}   details.field names the bad field
Retries       POST + Idempotency-Key  →  same response for 24 h (a 5xx included), never a second invoice

1. Quickstart — to the first confirmed payment

1. Create a key. Cabinet → API keys → Create key with create_invoice (and read). Copy the secret: it is shown once. Keep it on your server (env var / secret manager) — never in a web page or mobile app: whoever reads it can sign requests as you. Browser code talks to your backend, your backend talks to us.

2. Create a payment. One call; the response carries the page to send the payer to.

bash
# see https://cryptolike.net/docs/auth §4 for the signing lines (TS, SIG); body first, then sign it
BODY='{"amount":"10.50","currency":"USD","order_id":"A-1001","return_url":"https://shop.example/thanks"}'
curl -s https://api.cryptolike.net/v1/invoices \
  -H "Content-Type: application/json" -H "Idempotency-Key: order-A-1001" \
  -H "X-API-Key: $KEY_ID" -H "X-Timestamp: $TS" -H "X-Signature: $SIG" -d "$BODY"
json
{
  "id": "4e2bc88c-cb79-4afc-b864-c1ba8e9d50c9",
  "status": "new",
  "currency": "USD", "amount": "10.5",
  "coin_id": null, "ticker": null, "network": null, "address": null,
  "amount_crypto": null, "payer_amount": null,
  "paid_amount": "0", "remaining_amount": "0", "overpaid_amount": "0",
  "amount_fiat": "10.5", "fiat_currency": "USD", "rate_locked": null, "rate_locked_at": null,
  "late_payment": false, "fee_paid_by": "merchant",
  "order_id": "A-1001", "description": null, "return_url": "https://shop.example/thanks", "webhook_url": null,
  "metadata": null, "allowed_coin_ids": null,
  "checkout_url": "https://cryptolike.net/pay/4e2bc88c-cb79-4afc-b864-c1ba8e9d50c9",
  "ttl_expires_at": "2026-09-16T12:30:00Z", "expired_at": null,
  "created_at": "2026-09-16T12:00:00Z", "updated_at": "2026-09-16T12:00:00Z"
}

currency may be a fiat code (USD, EUR, UAH, GBP or CAD, 2 decimals each; your list is GET /v1/fiat-currencies — the payer chooses the coin on the page and the rate is locked at that moment) or a coin ticker (BTC, USDT). Pass coin_id (from GET /v1/currencies) to fix the coin now: the address and the exact amount are in the response. Without coin_id the payer chooses on the page among the coins you offer for invoices (cabinet → Coins: on and «available for invoices»), each shown with an informative ≈ amount; to narrow the choice for this payment send "allowed_coin_ids": ["<coin_id>", …] — the response's allowed_coin_ids is the kept set (coins you offer for invoices that match currency; a ticker keeps only its own networks), 422 no_allowed_coins when none is left, null when not sent. Pick the ids from the signed GET /v1/currencies (invoice_enabled: true, not maintenance). The SDKs send the list as given and refuse an empty one before the request.

Shops: one payment per order. Send "idempotency_by_order_id": true with your order_id and a repeated request for the same order (double click, reload, retry) returns the order's open payment with 200 instead of creating a second one; the same order with another amount is 409 conflict (details.reason = order_invoice_mismatch), an order that already received funds is 409 conflict (order_has_payment) — both name details.invoice_id. metadata (up to 20 string pairs, ≤ 4 KB) comes back in the API and in invoice.* webhooks and is never shown to the payer. The full contract for shop plugins is integration guides.

3. Send the payer to checkout_url. The page shows the address, QR, amount, network warning and live status. After payment it offers return_url.

4. Learn the result. Either subscribe a webhook endpoint (POST /v1/webhooks/endpoints, events invoice.paid, invoice.underpaid, invoice.expired, …; Webhooks) or poll GET /v1/invoices/{id} — no more often than every few seconds. status: paid means the funds are confirmed and credited to your balance (GET /v1/balances).

Test on test networks first: a ck_test_ key with the test API URL from CryptoLike support (on mainnet a test key gets 401 auth.test_key). The reference signature vector in Authentication §3 tells you in one call whether your signing is right.

2. Resources

Resource Routes Permission
Currencies GET /v1/currencies (signed: the coins enabled for you, each with invoice_enabled — «available for invoices»; unsigned: the public catalogue) read
Rates GET /v1/rates?fiat=USD read
Fiat currencies GET /v1/fiat-currencies (your default fiat; every enabled fiat with decimals — fractional digits an amount may carry, 0 for JPY — and invoice_enabled — usable as an invoice currency; the cabinet's invoice form offers the same list) read
Balances GET /v1/balances read
Invoices POST /v1/invoices, GET /v1/invoices (`?format=csv xlsxto export),GET /v1/invoices/{id}, POST /v1/invoices/{id}/cancel`
Static addresses POST /v1/addresses, GET /v1/addresses, GET /v1/addresses/{id} (with deposit history) create_address / read
Transactions GET /v1/transactions (`?format=csv xlsxto export),GET /v1/transactions/{id}`
Webhooks GET/POST /v1/webhooks/endpoints, GET/PATCH/DELETE …/{id}, POST …/{id}/rotate-secret, GET /v1/webhooks/deliveries, GET …/{id}, POST …/{id}/retry create_invoice for changes, read for reads
Withdrawals POST /v1/withdrawals, POST /v1/withdrawals/{id}/cancel; GET /v1/withdrawals (`?format=csv xlsxto export),GET /v1/withdrawals/{id}, POST /v1/withdrawals/estimate`

3. Payment (invoice) status model

Status Meaning Terminal
new Created; nothing received. no
pending A transfer was detected; waiting for confirmations. no
paid Full amount confirmed and credited. An overpayment is still paid, with overpaid_amount > 0 (invoice.paid carries it). yes
underpaid Less than the amount confirmed; open for a top-up to the same address until ttl_expires_at, then final. remaining_amount says how much is missing. after the TTL
expired TTL passed with nothing confirmed. yes
cancelled You cancelled it (POST …/cancel, only from new). yes

Rules worth knowing:

  • Amounts. amount_crypto is what you receive; payer_amount is what the payer must send (equal, unless fee_paid_by: payer — then the platform fee is on top). A small shortfall within the underpayment tolerance of your account still counts as paid.
  • Fiat pricing. The rate is locked when the coin is known (creation with coin_id, or the payer's choice) and does not change for the TTL. If no fresh rate is available, or the two rate sources disagree on the pair beyond the limit, the request answers 503 unavailable — retry, or create the invoice in crypto. A rate is never guessed.
  • Late payments. Funds that arrive after expired/cancelled/final underpaid are still credited to your balance, the invoice gets late_payment: true and you receive invoice.late_payment. Decide with the customer what to do; the status does not change.
  • Reorganisations. If a chain reorganisation reverts a confirmed transfer, the invoice is recomputed and you receive invoice.reverted. Ship on paid, and handle reverted.

4. Static addresses and transactions

A static address is a permanent deposit address of one currency for one of your customers (customer_id). Every deposit is credited after the currency's confirmations and announced by deposit.pending / deposit.confirmed (deposit.reverted after a reorganisation). One active address per (currency, customer); archive the old one (POST /v1/addresses/{id}/archive, or in the cabinet) to issue a new one. Addresses are never deleted and an index is never reused: an archived address leaves real-time scanning but is checked for incoming transfers regularly (every 24 h by default) — a transfer found is credited as usual and restores the address automatically (static_address.restored, reason deposit). POST /v1/addresses/{id}/restore brings an archived address back (409 with details.active_address_id while the customer has another active address for the currency). GET /v1/addresses?status=archived&search=… is the archive (search by address, customer_id or label; filter coin_id; cursor pages). An address without incoming transfers for 180 days (platform setting) is archived automatically after a 7-day static_address.archive_warning.

In the SDKs the archive is archiveStaticAddress / restoreStaticAddress / listArchivedStaticAddresses (Python archive_static_address …, Go ArchiveStaticAddress …), and the data of the three archive webhooks is typed (StaticAddressArchivedData, StaticAddressRestoredData, StaticAddressArchiveWarningData in OpenAPI spec) — SDKs. In the Telegram bot an archived address card has Restore (same rule, 409 explained with a button to the active address), and a notice of an automatic archive carries the same button.

GET /v1/transactions lists every movement: invoice_payment, deposit, withdrawal, adjustment. Each row has the gross amount, the platform fee (known once confirmed) and net_amount, the chain txid, and the related invoice or static address. ?format=csv or ?format=xlsx streams the same list with the same filters as a file (also on GET /v1/invoices and GET /v1/withdrawals); every cell is text, so amounts keep their exact decimals. An XLSX of more than 100 000 rows is answered as CSV — check Content-Type / the file name.

4a. Withdrawals

A withdrawal sends amount of a coin from your available balance to to_address. Ask POST /v1/withdrawals/estimate first: it returns the platform fee, the reserved network fee and total_debit — what leaves available the moment the withdrawal is created. The whole total_debit sits in hold until the payout ends:

Status Meaning Hold
pending queued for the payout worker held
review a manual check is needed (review_reason: limit, pattern, manual, compliance, signer_limit); up to 24 h held
awaiting_approval above your account's dual-approval threshold: a second owner/admin confirms in the cabinet held
processing being signed and sent — can no longer be cancelled held
broadcast in the network (txid, confirmations growing) held
completed confirmed; amount left, platform_fee and network_fee_charged were charged released
failed the network refused or dropped the transaction (failure_reason) — everything is back in available; create a new withdrawal released
rejected refused on review — everything is back in available released
cancelled you cancelled before processing (POST …/{id}/cancel) released

Rules: the key needs withdraw; the account must be verified (403 kyb_required); when the account enforces its withdrawal whitelist only an active whitelisted address is accepted (403); a destination the platform does not accept is 422 address_refused (no detail is given); an insufficient balance is 409 with details.reason = insufficient_funds. Send your own payout_id: repeating the request with the same coin, amount and address returns the existing withdrawal (200; a repeat within the first seconds after creation may still answer 201 — the body and id are the same; use Idempotency-Key for exact replay semantics), different parameters are 409 — your retries can never pay twice. Security review: limits, unusual patterns and recent security changes of the account (a change of the allowed payout addresses, an API key that received the withdraw permission, a sign-in from a new device) send a payout to review with the generic review_reason: pattern — nothing is refused, a person looks at it. If a member froze the account in the cabinet, every key answers 403 account_frozen until support unfreezes it (incoming payments keep being credited). The network fee you are charged is either the reserved estimate (default) or the fee actually paid (network_fee_mode: actual on the estimate; the unused reservation returns to available on completion). Webhooks: withdrawal.processing, withdrawal.completed, withdrawal.failed, withdrawal.rejected (plus the progress events of Webhooks §5).

5. Errors

Every error is one envelope; branch on code, show message to a human, read details for specifics (field, required, retry_after, server_time).

HTTP code Recovery
400 validation_error Fix details.field and resend.
401 auth.missing_header, auth.invalid_key, auth.invalid_timestamp, auth.expired_timestamp, auth.invalid_signature, auth.replay Authentication §5 and §10.
401 auth.test_key A test key on an environment that runs on mainnets — nothing is created; use a live key. details.docs_url links Authentication §9.
403 auth.permission_denied (details.required), auth.ip_not_allowed Grant the permission / whitelist the IP in the cabinet.
403 kyb_required, forbidden (whitelist) Complete the account verification in the cabinet / whitelist the destination (or switch the whitelist off) in the cabinet.
403 account_frozen A member froze the account (cabinet → Settings → Security); contact support to unfreeze it.
422 address_refused The destination is not accepted by the platform; use another address.
422 no_allowed_coins None of allowed_coin_ids is a coin you offer for invoices in this currency — switch a coin on (and «available for invoices») in the cabinet or send other ids.
404 not_found Wrong id, or the resource belongs to another account.
409 conflict The state does not allow it (cancel a paid invoice, second active address, retry of a pending delivery; idempotency_by_order_id: details.reason order_invoice_mismatch / order_has_payment + details.invoice_id). Read the current state first.
409 idempotency.in_progress The first request with this key is still running; wait and retry.
413 payload_too_large Body over 1 MiB.
422 idempotency.mismatch Same Idempotency-Key, different request — use a new key.
429 rate_limited Wait Retry-After seconds; the limit is per key (600/min by default).
503 unavailable Rate feed, fee estimate, screening or coin temporarily unavailable — nothing was created; retry with backoff (the same Idempotency-Key runs again).
500, 504 internal_error, timeout The request may or may not have been applied. A retry with the same Idempotency-Key replays this answer for 24 h and never runs the request twice; check the object (GET by payout_id / order_id) and send a new key only if it does not exist (Authentication §7).

6. Conventions

  • Amounts are strings. "0.00000001", never 1e-8; up to 18 decimals; the API refuses more precision than the currency has.
  • Times are RFC 3339 in UTC (2026-09-16T12:00:00Z). Filters from (inclusive) / to (exclusive) take the same format.
  • Pagination is keyset: next_cursor is opaque, valid for the same filters, null on the last page; rows inserted meanwhile never shift or duplicate pages.
  • Stable shape. Optional blocks are present as null, so a client can parse every response the same way.
  • Versioning. Breaking changes get a new prefix (/v2); fields may be added under /v1 at any time — ignore unknown fields.
  • Providers are never named. Rates, nodes and screening vendors are internal; you see values and timestamps.

7. Client libraries

Official SDKs — @cryptolike/sdk (TypeScript / Node.js), cryptolike (Python), github.com/Petya88/Cryptolike/sdk/go (Go) and cryptolike/sdk (PHP) — with a quickstart to the first confirmed payment in each language: SDKs. They sign, retry, add Idempotency-Key to every POST and verify webhooks the same way. Any other language works with the four signing lines of Authentication §4 (curl, Node, Python, Go examples there). Reference vectors for your own SDK's tests — request signing and webhook verification, positive and negative — are in vectors.json.

The secret stays on your server. createMerchantClient refuses to run in a browser. To show the payment page inside your site, embed it (Checkout widget): your server creates the invoice, your page gets the invoice id only.

8. Versioning

/v1 is the contract version; every response carries X-API-Version: v1 and you may send the same header to pin it. Only additive changes ship within v1 — API changelog lists them and the rules.