Zahlungen
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 | Beschreibung |
|---|---|---|
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/chargesList charges. Filter with ?status= and ?payment_link=. -
POST
/v1/chargesCreate 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}/cancelCancel a pending charge. Answers 409 once it is paid, expired or already canceled. -
POST
/v1/charges/{id}/refundsRecord 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.