Enter at least two characters to search. Searching documentation… No matching sections. Try an endpoint, field or event name.
Account
View as MarkdownThe account the key acts for, and what it can be paid in right now. Read it at startup to learn the default currency and which coins to offer, instead of hard-coding them. It can also be changed, by a key with the permission to.
On this page
Endpoints
-
GET
/v1/accountThe account behind the key, with meta.accepted_assets listing the coins it can take at this moment. -
PATCH
/v1/accountChange the name, public profile, checkout, invoice settings and business details. Needs account:write. -
PATCH
/v1/payment-settingsChange the default currency, the payment tolerances, the checkout language and invoice reminders. Needs payment_settings:write. -
POST
/v1/billing/proStart paying for a month of Pro in a coin, answering with the checkout to pay it on. Needs billing:write and a live key.
Example response
Copy failed. Select the code and copy it manually.
{
"data": {
"id": "acct_7fq2kx9pd3mw", "object": "account", "type": "organization",
"name": "Acme Holdings Ltd", "display_name": "Acme",
"logo_url": "https://…", "logo_dark_url": null, "website_url": "https://acme.example",
"support_email": "billing@acme.example", "support_phone": "+442071234567",
"brand_color": "#f5b301",
"terms_url": "https://acme.example/terms", "refund_policy_url": null,
"business": {
"type": "company", "legal_name": "Acme Holdings Ltd",
"registration_number": "12345678", "tax_id": "GB123456789",
"address": {
"line1": "1 Example Street", "line2": null, "city": "London",
"postal_code": "EC1A 1BB", "state": null, "country": "GB"
},
"industry": "software", "product_description": "Project management software, billed monthly.",
"monthly_volume": "10k_50k", "phone": "+442071234500"
},
"checkout": {
"appearance": "auto", "coin_order": ["LTC", "BTC"], "default_coin": null,
"success_url": "https://acme.example/thanks", "cancel_url": null,
"success_message": null, "hide_powered_by": false
},
"invoices": { "number_prefix": "ACME", "footer": null, "show_business_details": true },
"default_currency": "EUR",
"underpayment_tolerance": "1", "overpayment_threshold": "1",
"checkout_locale": null, "invoice_reminders": true, "api_payout_address_changes": false,
"plan": { "name": "free", "fee_percent": "3", "pro_ends_at": null },
"created_at": "2026-01-14T09:12:04.000000Z"
},
"meta": { "accepted_assets": ["BTC", "LTC", "ETH"], "mode": "live" }
}
id is the permanent address of the account, the same one its dashboard URL uses. It stays the same when the account is renamed, so it is safe to store.
type is personal or organization. display_name falls back to name; logo_url, website_url, support_email, support_phone and brand_color are null until they are set.
accepted_assets is empty until a payout wallet is set up, and shrinks when a coin is switched off. Creating a charge in a coin that is not listed returns 422. GET /v1/assets adds each coin's network and minimum
underpayment_tolerance and overpayment_threshold are percentages: how far short a payment may fall and still count as paid, and how much surplus is ignored before the charge is flagged as overpaid.
default_currency is one of USD, EUR, GBP, CAD, AUD, CHF, TRY.
checkout is how the checkout looks and where customers go: appearance is auto, light or dark; coin_order lists the coins to show first; default_coin is the coin the checkout starts on, and when it is null or a checkout does not offer it, the checkout starts on Litecoin, or else on the first coin listed; success_url, cancel_url and success_message apply to charges and payment links that set none; hide_powered_by takes the VeliraPay mark off, on Pro. checkout_locale is the language the checkout falls back to when a customer's browser asks for one it does not have, English when null.
invoices holds what new invoice numbers start with (number_prefix, INV when null), the note printed at their foot, and whether invoices and receipts carry the business details. invoice_reminders says whether customers are reminded of open invoices.
plan is free or pro, with the share of each payment kept as a fee (fee_percent) and, on Pro, when it ends (pro_ends_at), or null while it renews or has no end. api_payout_address_changes says whether an owner let API keys and AI apps change the payout addresses.
Changing the account
PATCH /v1/account takes the shape GET /v1/account answers with, and changes only what it is sent: name, display_name, website_url, support_email, support_phone, terms_url, refund_policy_url, brand_color, checkout (appearance, coin_order, default_coin, success_url, cancel_url, success_message, hide_powered_by), invoices (number_prefix, footer, show_business_details) and business, with its address. Send null to clear a field. It answers with the account, and the activity log records each part changed under the key's name.
The rules are the dashboard's: addresses must be public web addresses, brand_color a hex colour, number_prefix letters and digits with dashes only between them, and coin_order and default_coin coins on offer in the key's mode; coins left out of coin_order follow in the usual order. hide_powered_by can only be switched on with Pro, and answers 422 otherwise. Logos are changed in the dashboard.
Copy failed. Select the code and copy it manually.
curl -X PATCH 'https://api.velirapay.com/v1/account' \
--fail-with-body --silent --show-error --max-time 30 \
-H "Authorization: Bearer ${VELIRAPAY_API_KEY:?Set VELIRAPAY_API_KEY}" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"display_name": "Acme",
"checkout": {"success_url": "https://acme.example/thanks"},
"invoices": {"number_prefix": "ACME"}
}'
<?php
use VeliraPay\VeliraPayClient;
require __DIR__.'/vendor/autoload.php';
$apiKey = getenv('VELIRAPAY_API_KEY') ?: throw new RuntimeException('Set VELIRAPAY_API_KEY.');
$velirapay = new VeliraPayClient($apiKey, baseUrl: 'https://api.velirapay.com');
$account = $velirapay->account->update([
'display_name' => 'Acme',
'checkout' => ['success_url' => 'https://acme.example/thanks'],
'invoices' => ['number_prefix' => 'ACME'],
]);
echo json_encode($account, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR), PHP_EOL;
use VeliraPay\Laravel\Facades\VeliraPay;
$account = VeliraPay::account()->update([
'display_name' => 'Acme',
'checkout' => ['success_url' => 'https://acme.example/thanks'],
'invoices' => ['number_prefix' => 'ACME'],
]);
const secretKey = process.env.VELIRAPAY_API_KEY;
if (!secretKey) throw new Error('Set VELIRAPAY_API_KEY.');
const response = await fetch('https://api.velirapay.com/v1/account', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${secretKey}`,
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
display_name: 'Acme',
checkout: { success_url: 'https://acme.example/thanks' },
invoices: { number_prefix: 'ACME' },
}),
redirect: 'error',
signal: AbortSignal.timeout(30_000),
});
if (!response.ok) {
throw new Error(`VeliraPay ${response.status}: ${await response.text()}`);
}
const account = response.status === 204 ? null : (await response.json()).data;
console.log(JSON.stringify(account, null, 2));
import json
import os
import requests
api_key = os.environ["VELIRAPAY_API_KEY"]
response = requests.patch(
"https://api.velirapay.com/v1/account",
headers={
"Authorization": f"Bearer {api_key}",
"Accept": "application/json",
},
json={
"display_name": "Acme",
"checkout": {"success_url": "https://acme.example/thanks"},
"invoices": {"number_prefix": "ACME"},
},
timeout=(5, 30),
allow_redirects=False,
)
if not 200 <= response.status_code < 300:
raise RuntimeError(f"VeliraPay {response.status_code}: {response.text}")
account = None if response.status_code == 204 else response.json()["data"]
print(json.dumps(account, indent=2))
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
var secretKey = Environment.GetEnvironmentVariable("VELIRAPAY_API_KEY")
?? throw new InvalidOperationException("Set VELIRAPAY_API_KEY.");
using var client = new HttpClient(new HttpClientHandler { AllowAutoRedirect = false })
{
Timeout = TimeSpan.FromSeconds(30),
};
using var request = new HttpRequestMessage(HttpMethod.Patch, "https://api.velirapay.com/v1/account");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", secretKey);
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
request.Content = new StringContent("""
{
"display_name": "Acme",
"checkout": {"success_url": "https://acme.example/thanks"},
"invoices": {"number_prefix": "ACME"}
}
""", Encoding.UTF8, "application/json");
using var response = await client.SendAsync(request);
var responseBody = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
throw new HttpRequestException($"VeliraPay {(int)response.StatusCode}: {responseBody}");
}
JsonElement? account = null;
if (response.StatusCode != HttpStatusCode.NoContent)
{
using var document = JsonDocument.Parse(responseBody);
account = document.RootElement.GetProperty("data").Clone();
}
Console.WriteLine(account?.GetRawText() ?? "null");
PATCH /v1/payment-settings changes default_currency, underpayment_tolerance (0 to 10), overpayment_threshold (0 to 25), checkout_locale and invoice_reminders, likewise only what it is sent, and also answers with the account. The tolerances decide whether a short payment counts as paid, which is why they take a permission of their own.
Copy failed. Select the code and copy it manually.
curl -X PATCH 'https://api.velirapay.com/v1/payment-settings' \
--fail-with-body --silent --show-error --max-time 30 \
-H "Authorization: Bearer ${VELIRAPAY_API_KEY:?Set VELIRAPAY_API_KEY}" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{"underpayment_tolerance": "1.5"}'
<?php
use VeliraPay\VeliraPayClient;
require __DIR__.'/vendor/autoload.php';
$apiKey = getenv('VELIRAPAY_API_KEY') ?: throw new RuntimeException('Set VELIRAPAY_API_KEY.');
$velirapay = new VeliraPayClient($apiKey, baseUrl: 'https://api.velirapay.com');
$account = $velirapay->account->updatePaymentSettings(['underpayment_tolerance' => '1.5']);
echo json_encode($account, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR), PHP_EOL;
use VeliraPay\Laravel\Facades\VeliraPay;
$account = VeliraPay::account()->updatePaymentSettings(['underpayment_tolerance' => '1.5']);
const secretKey = process.env.VELIRAPAY_API_KEY;
if (!secretKey) throw new Error('Set VELIRAPAY_API_KEY.');
const response = await fetch('https://api.velirapay.com/v1/payment-settings', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${secretKey}`,
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({ underpayment_tolerance: '1.5' }),
redirect: 'error',
signal: AbortSignal.timeout(30_000),
});
if (!response.ok) {
throw new Error(`VeliraPay ${response.status}: ${await response.text()}`);
}
const account = response.status === 204 ? null : (await response.json()).data;
console.log(JSON.stringify(account, null, 2));
import json
import os
import requests
api_key = os.environ["VELIRAPAY_API_KEY"]
response = requests.patch(
"https://api.velirapay.com/v1/payment-settings",
headers={
"Authorization": f"Bearer {api_key}",
"Accept": "application/json",
},
json={"underpayment_tolerance": "1.5"},
timeout=(5, 30),
allow_redirects=False,
)
if not 200 <= response.status_code < 300:
raise RuntimeError(f"VeliraPay {response.status_code}: {response.text}")
account = None if response.status_code == 204 else response.json()["data"]
print(json.dumps(account, indent=2))
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
var secretKey = Environment.GetEnvironmentVariable("VELIRAPAY_API_KEY")
?? throw new InvalidOperationException("Set VELIRAPAY_API_KEY.");
using var client = new HttpClient(new HttpClientHandler { AllowAutoRedirect = false })
{
Timeout = TimeSpan.FromSeconds(30),
};
using var request = new HttpRequestMessage(HttpMethod.Patch, "https://api.velirapay.com/v1/payment-settings");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", secretKey);
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
request.Content = new StringContent("""
{"underpayment_tolerance": "1.5"}
""", Encoding.UTF8, "application/json");
using var response = await client.SendAsync(request);
var responseBody = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
throw new HttpRequestException($"VeliraPay {(int)response.StatusCode}: {responseBody}");
}
JsonElement? account = null;
if (response.StatusCode != HttpStatusCode.NoContent)
{
using var document = JsonDocument.Parse(responseBody);
account = document.RootElement.GetProperty("data").Clone();
}
Console.WriteLine(account?.GetRawText() ?? "null");
Both permissions are sensitive: full access never includes them, so a key has to be restricted and have them ticked.
business holds what the account entered under Business details in its settings; every field is null until it is filled in. type is one of individual, company, non_profit, industry one of digital_products, software, hosting, gaming, retail, professional_services, education, media, marketplace, travel, donations, other, and monthly_volume one of under_1k, 1k_10k, 10k_50k, 50k_250k, over_250k. country is an ISO 3166 two-letter code, and phone numbers are in international format.
Paying for Pro
Pro takes no fee off your payments and lets you hide the VeliraPay mark. A month of it can be paid in crypto from your own code: send the coin to pay in, and the answer is a pro_payment with checkout_url, the page to pay it on, price and currency, what to send in asset_amount, and expires_at. Pay it like any checkout; the month starts once it is paid, and GET /v1/account shows it under plan.
Copy failed. Select the code and copy it manually.
curl -X POST 'https://api.velirapay.com/v1/billing/pro' \
--fail-with-body --silent --show-error --max-time 30 \
-H "Authorization: Bearer ${VELIRAPAY_API_KEY:?Set VELIRAPAY_API_KEY}" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{"asset": "BTC"}'
<?php
use VeliraPay\VeliraPayClient;
require __DIR__.'/vendor/autoload.php';
$apiKey = getenv('VELIRAPAY_API_KEY') ?: throw new RuntimeException('Set VELIRAPAY_API_KEY.');
$velirapay = new VeliraPayClient($apiKey, baseUrl: 'https://api.velirapay.com');
$payment = $velirapay->billing->startProPayment(['asset' => 'BTC']);
echo json_encode($payment, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR), PHP_EOL;
use VeliraPay\Laravel\Facades\VeliraPay;
$payment = VeliraPay::billing()->startProPayment(['asset' => 'BTC']);
const secretKey = process.env.VELIRAPAY_API_KEY;
if (!secretKey) throw new Error('Set VELIRAPAY_API_KEY.');
const response = await fetch('https://api.velirapay.com/v1/billing/pro', {
method: 'POST',
headers: {
Authorization: `Bearer ${secretKey}`,
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({ asset: 'BTC' }),
redirect: 'error',
signal: AbortSignal.timeout(30_000),
});
if (!response.ok) {
throw new Error(`VeliraPay ${response.status}: ${await response.text()}`);
}
const payment = response.status === 204 ? null : (await response.json()).data;
console.log(JSON.stringify(payment, null, 2));
import json
import os
import requests
api_key = os.environ["VELIRAPAY_API_KEY"]
response = requests.post(
"https://api.velirapay.com/v1/billing/pro",
headers={
"Authorization": f"Bearer {api_key}",
"Accept": "application/json",
},
json={"asset": "BTC"},
timeout=(5, 30),
allow_redirects=False,
)
if not 200 <= response.status_code < 300:
raise RuntimeError(f"VeliraPay {response.status_code}: {response.text}")
payment = None if response.status_code == 204 else response.json()["data"]
print(json.dumps(payment, indent=2))
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
var secretKey = Environment.GetEnvironmentVariable("VELIRAPAY_API_KEY")
?? throw new InvalidOperationException("Set VELIRAPAY_API_KEY.");
using var client = new HttpClient(new HttpClientHandler { AllowAutoRedirect = false })
{
Timeout = TimeSpan.FromSeconds(30),
};
using var request = new HttpRequestMessage(HttpMethod.Post, "https://api.velirapay.com/v1/billing/pro");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", secretKey);
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
request.Content = new StringContent("""
{"asset": "BTC"}
""", Encoding.UTF8, "application/json");
using var response = await client.SendAsync(request);
var responseBody = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
throw new HttpRequestException($"VeliraPay {(int)response.StatusCode}: {responseBody}");
}
JsonElement? payment = null;
if (response.StatusCode != HttpStatusCode.NoContent)
{
using var document = JsonDocument.Parse(responseBody);
payment = document.RootElement.GetProperty("data").Clone();
}
Console.WriteLine(payment?.GetRawText() ?? "null");
A payment already waiting in that coin is answered again, with 200 and meta.started false, rather than started twice; one in another coin is replaced. Only a few can be started a day. It needs billing:write, which full access includes, and a live key, since Pro is paid with real coins: a test key answers 409, and so does an account with Pro for good.