API reference

Cobros

A charge is one payment attempt. Creating one locks the exchange rate for 30 minutes, allocates a deposit address for exactly that payment and returns a checkout_url the customer can open. Use a charge when you already know the coin (your own checkout asked), or as an invoice you email to a customer.

The charge object

Field Type Descripción
id string Public code, also the last path segment of the checkout URL.
status "pending" | "underpaid" | "paid" | "expired" | "canceled" Pending until a confirmed payment arrives, the timer runs out or you cancel it.
payment_link string|null The link it was created through, if any.
fiat_amount, fiat_currency string What the customer owes, in your currency.
asset string The coin and network the customer pays with: BTC, LTC, DOGE, BCH, ETH, XMR, SOL, USDT, USDC.
asset_amount string Exact crypto amount to send, rounded up at the locked rate.
exchange_rate string Fiat per one unit of the asset at creation.
received_amount, remaining_amount string|null, string What has been credited so far and what is still missing.
overpaid boolean True when the surplus exceeds your overpayment threshold.
refunded_amount, refunds string, array What you recorded as sent back: amount, txid, reason, explorer_url, created_at per refund.
deposit_address string Your wallet address for the asset. Charges are told apart by their exact amount.
transactions array On-chain transfers matched to the charge: txid, amount, confirmations, required_confirmations, credited, explorer_url, seen_at.
customer_email string|null Receives a receipt when the charge is paid.
customer_name string|null Collected by the checkout when the link asks for it, or taken from the invoice.
custom_fields array The customer's answers to the link's extra questions: label, value.
delivered_item string|null The license key or code the link delivered for this charge. Webhooks only carry item_delivered, never the content.
invoice string|null The invoice this charge is an attempt to pay, if any.
timeline array What happened so far, oldest first: event, time.
description, metadata string|null, object Yours to fill: order numbers, references. Metadata values must be scalar, 20 keys max.
checkout_url string The hosted payment page.
receipt_url string|null A printable receipt for the customer, once the charge is paid.
expires_at, paid_at, created_at string|null ISO 8601 timestamps in UTC.

Endpoints

  • GET /v1/charges List charges. Filter with ?status= and ?payment_link=.
  • POST /v1/charges Create a charge. Body: amount, currency, asset, customer_email, description, metadata, or payment_link plus asset to charge through a link.
  • GET /v1/charges/{id} Retrieve a charge and its current status.
  • POST /v1/charges/{id}/cancel Cancel a pending charge. Answers 409 once it is paid, expired or already canceled.
  • POST /v1/charges/{id}/refunds Record a refund you sent from your wallet. Body: amount (in the asset), txid, reason. 422 when it exceeds what was received.

Example: invoice a customer

curl -X POST https://api.velirapay.com/v1/charges \
  -H "Authorization: Bearer vp_live_…" \
  -H "Idempotency-Key: 4d6c1e1a-…" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": "150.00",
    "currency": "USD",
    "asset": "BTC",
    "customer_email": "[email protected]",
    "description": "Invoice #1042",
    "metadata": {"order_id": "1042"}
  }'

HTTP/1.1 201 Created
{
  "data": {
    "id": "2t4bcuqkpkoy",
    "object": "charge",
    "status": "pending",
    "fiat_amount": "150.00",
    "fiat_currency": "USD",
    "asset": "BTC",
    "asset_amount": "0.0025",
    "received_amount": null,
    "remaining_amount": "0.0025",
    "overpaid": false,
    "exchange_rate": "60000",
    "deposit_address": "tb1q…",
    "transactions": [],
    "checkout_url": "https://velirapay.com/c/2t4bcuqkpkoy",
    "expires_at": "2026-09-17T00:30:00.000000Z",
    …
  }
}

Poll GET /v1/charges/{id} if you must, but a webhook endpoint tells you about paid, underpaid, expired and canceled charges without polling.

How payments are detected

Every open charge's deposit address is checked about once a minute. A transfer shows up under transactions as soon as the network reports it, and the checkout page tells the customer it was seen. It is credited once it has the required confirmations (2 for Bitcoin, 6 on Ethereum by default, other networks have their own); only then does the charge become paid or underpaid and the webhook fire. A charge does not expire while a transfer is still confirming.

All charges of one asset are paid to the same wallet address, so the amount identifies the payment: when two open charges would be due the same amount, the later one is raised by the smallest unit. Ask customers to send the exact amount shown.

A payment that arrives after a charge expired or was canceled is still recorded against it for a few hours. The charge keeps its status, the dashboard flags the late payment, and you decide whether to accept it as paid.

Refunds

A refund is a transfer you make yourself, from your own wallet: a payment that has been forwarded to you cannot be pulled back. Record it on the charge, in the dashboard or with POST /v1/charges/{id}/refunds, and the charge shows what was sent back, the customer is emailed, and a charge.refunded event goes out. Recording the refund of a late payment also clears its warning.

Underpayments and overpayments

Customers rarely send the exact amount: wallets and exchanges take fees out of it. Each account sets an underpayment tolerance under Payment settings. A payment that lands within the tolerance settles the charge as paid; one that falls further short moves it to underpaid, where received_amount and remaining_amount tell you where it stands. The customer can top it up on the same checkout page until the rate lock ends, and you can accept it as paid from the dashboard.

Paying too much never blocks a charge. When the surplus exceeds your overpayment threshold, overpaid is true so you can refund the difference.

A charge can only be created for an asset your account has a wallet address for; anything else fails validation with 422.