Enter at least two characters to search. Searching documentation… No matching sections. Try an endpoint, field or event name.
API keys
View as MarkdownKeys can be managed through the API too, for setting up a new server or rotating keys from code. Both permissions are sensitive, so a key has them only when they are ticked on it.
On this page
The API key object
| Field | Type | Description |
|---|---|---|
id
string
|
string | The key's id, used in the paths below. It is not the key itself. |
object
"api_key"
|
"api_key" | The kind of object. |
name
string
|
string | What the key is for, up to 100 characters. |
prefix
string
|
string | The start of the key, such as vp_live_a1b2, to tell keys apart. |
key
string
|
string | The key itself, only in the answer that creates it. It is never kept, so it cannot be read again. |
mode
string
|
string | live or test: the mode the key works in, the same as the key that made it. |
permissions
array|null
|
array|null | What the key may do. Null for full access, which leaves out the sensitive permissions. |
allowed_ips
array|null
|
array|null | The IPv4 and IPv6 addresses and ranges it may be used from. Null for anywhere. |
created_by
string|null
|
string|null | The name of the member who made it, or in whose name it was made. |
last_used_at
string|null
|
string|null | When it last made a request, as an ISO 8601 timestamp in UTC. |
created_at
string
|
string | When it was made. |
Endpoints
-
GET
/v1/api-keysList the account's active keys in the mode of the key, newest first. Needs api_keys:read. -
POST
/v1/api-keysCreate a key from name and, optionally, permissions and allowed_ips. Answers 201 with key, shown this once. Needs api_keys:write. -
PATCH
/v1/api-keys/{id}Change a key's name, permissions or allowed_ips, only those given; null permissions gives full access and null allowed_ips anywhere. Needs api_keys:write. -
DELETE
/v1/api-keys/{id}Revoke a key: it is refused from its next request. Answers 204. Needs api_keys:write.
Copy failed. Select the code and copy it manually.
curl -X POST 'https://api.velirapay.com/v1/api-keys' \
--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 '{
"name": "Shop server",
"permissions": [
"charges:read",
"charges:write"
],
"allowed_ips": ["203.0.113.7"]
}'
<?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');
$apiKey = $velirapay->apiKeys->create([
'name' => 'Shop server',
'permissions' => [
'charges:read',
'charges:write',
],
'allowed_ips' => ['203.0.113.7'],
]);
echo json_encode($apiKey, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR), PHP_EOL;
use VeliraPay\Laravel\Facades\VeliraPay;
$apiKey = VeliraPay::apiKeys()->create([
'name' => 'Shop server',
'permissions' => [
'charges:read',
'charges:write',
],
'allowed_ips' => ['203.0.113.7'],
]);
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/api-keys', {
method: 'POST',
headers: {
Authorization: `Bearer ${secretKey}`,
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'Shop server',
permissions: [
'charges:read',
'charges:write',
],
allowed_ips: ['203.0.113.7'],
}),
redirect: 'error',
signal: AbortSignal.timeout(30_000),
});
if (!response.ok) {
throw new Error(`VeliraPay ${response.status}: ${await response.text()}`);
}
const apiKey = response.status === 204 ? null : (await response.json()).data;
console.log(JSON.stringify(apiKey, null, 2));
import json
import os
import requests
api_key = os.environ["VELIRAPAY_API_KEY"]
response = requests.post(
"https://api.velirapay.com/v1/api-keys",
headers={
"Authorization": f"Bearer {api_key}",
"Accept": "application/json",
},
json={
"name": "Shop server",
"permissions": [
"charges:read",
"charges:write",
],
"allowed_ips": ["203.0.113.7"],
},
timeout=(5, 30),
allow_redirects=False,
)
if not 200 <= response.status_code < 300:
raise RuntimeError(f"VeliraPay {response.status_code}: {response.text}")
apiKey = None if response.status_code == 204 else response.json()["data"]
print(json.dumps(apiKey, 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/api-keys");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", secretKey);
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
request.Content = new StringContent("""
{
"name": "Shop server",
"permissions": [
"charges:read",
"charges:write"
],
"allowed_ips": ["203.0.113.7"]
}
""", 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? apiKey = null;
if (response.StatusCode != HttpStatusCode.NoContent)
{
using var document = JsonDocument.Parse(responseBody);
apiKey = document.RootElement.GetProperty("data").Clone();
}
Console.WriteLine(apiKey?.GetRawText() ?? "null");
Copy failed. Select the code and copy it manually.
{
"data": {
"id": "42",
"object": "api_key",
"name": "Shop server",
"prefix": "vp_live_8Kq2",
"key": "vp_live_8Kq2…",
"mode": "live",
"permissions": ["charges:read", "charges:write"],
"allowed_ips": ["203.0.113.7"],
"created_by": "Ada Lovelace",
"last_used_at": null,
"created_at": "2026-11-06T09:12:44.000000Z"
}
}
No further than the key asking
A key cannot hand out more than it has. The keys it makes or changes can only have permissions it has itself, and full access only if it has every permission that full access gives. A key limited to some addresses can only make or change keys limited to addresses within those, never one usable from anywhere. Nor can it change or revoke a key that reaches further than it does, such as a full-access key, which leaves the keys an owner made in the dashboard out of its reach. A request that would answers 403, naming the permission or the addresses that are out of reach.
Keys are made in the mode of the key that asks, and in the name of the member who made it, so a sensitive permission on the new key only works while that member is still an owner or admin. An account can have up to 10 active keys in each mode. Every change is in the activity log, naming the key that made it.
The key is shown once, in the answer that creates it. Store it then: a request repeated with the same Idempotency-Key answers 409 rather than replaying it. Signed in to the MCP server with your account, an owner or admin reaches every key of the organizations they picked, as in the dashboard.