Credits
3 endpoints. Every path below is relative to the API base URL and already includes the /api/v1 prefix — see conventions for authentication, pagination, errors and idempotency.
| Endpoint | ||
|---|---|---|
| GET | /billing/auto-reload | Return the organization's auto-reload configuration and its failure history |
| PUT | /billing/auto-reload | Enable or update auto reload with its threshold, reload amount, payment method and monthly caps |
| POST | /billing/credits/purchase | Create a Stripe PaymentIntent to top up credits and return the client secret for the browser |
Endpoints
Return the organization's auto-reload configuration and its failure history
GET/billing/auto-reload
- Requires
billing.read. datais literally null when auto reload has never been configured, not an empty object
bash
curl -X GET "$CALLAGENT_API_URL/billing/auto-reload" \
-H "Authorization: Bearer $CALLAGENT_API_KEY" \
-H "Accept: application/json"js
const response = await fetch(`${process.env.CALLAGENT_API_URL}/billing/auto-reload`, {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.CALLAGENT_API_KEY}`,
Accept: 'application/json',
},
});
if (!response.ok) {
const { error } = await response.json();
throw new Error(`${error.type}: ${error.message}`);
}
const { data } = await response.json();php
<?php
$handle = curl_init(getenv('CALLAGENT_API_URL') . '/billing/auto-reload');
curl_setopt_array($handle, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('CALLAGENT_API_KEY'),
'Accept: application/json',
],
]);
$body = json_decode(curl_exec($handle), true);
$status = curl_getinfo($handle, CURLINFO_HTTP_CODE);
curl_close($handle);
if ($status >= 400) {
throw new RuntimeException("{$body['error']['type']}: {$body['error']['message']}");
}
$data = $body['data'];python
import os, requests
response = requests.get(
f"{os.environ['CALLAGENT_API_URL']}/billing/auto-reload",
headers={"Authorization": f"Bearer {os.environ['CALLAGENT_API_KEY']}"},
timeout=30,
)
response.raise_for_status()
data = response.json()["data"]Responses
| Status | Body |
|---|---|
200 | data-wrapped object |
Errors
| Status | When it happens |
|---|---|
403 | Neither the API key scope nor the session user's permission allows billing.read |
Example response, generated from the schema:
json
{
"data": {
"enabled": true,
"threshold_amount": "string",
"reload_amount": "string",
"currency": "string",
"payment_method_id": "01JQ0000000000000000000000",
"monthly_reload_limit": 25,
"monthly_reload_cap_amount": "string",
"cooldown_minutes": 1,
"consecutive_failures": 1,
"disabled_reason": "string",
"last_succeeded_at": "2026-01-01T12:00:00+00:00",
"last_failed_at": "2026-01-01T12:00:00+00:00"
}
}Enable or update auto reload with its threshold, reload amount, payment method and monthly caps
PUT/billing/auto-reload
- Requires
billing.write. - Setting enabled=true also clears a dunning-triggered disable and resets the consecutive-failure counter
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
enabled | boolean | yes | |
threshold_amount | number | — | |
reload_amount | number | — | |
payment_method_id | string | null | — | |
monthly_reload_limit | integer | null | — | min 1. max 100 |
monthly_reload_cap_amount | number | null | — |
bash
curl -X PUT "$CALLAGENT_API_URL/billing/auto-reload" \
-H "Authorization: Bearer $CALLAGENT_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"enabled": true,
"threshold_amount": 1,
"reload_amount": 1,
"payment_method_id": "01JQ0000000000000000000000",
"monthly_reload_limit": 25,
"monthly_reload_cap_amount": 1
}'js
const response = await fetch(`${process.env.CALLAGENT_API_URL}/billing/auto-reload`, {
method: 'PUT',
headers: {
Authorization: `Bearer ${process.env.CALLAGENT_API_KEY}`,
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
"enabled": true,
"threshold_amount": 1,
"reload_amount": 1,
"payment_method_id": "01JQ0000000000000000000000",
"monthly_reload_limit": 25,
"monthly_reload_cap_amount": 1
}),
});
if (!response.ok) {
const { error } = await response.json();
throw new Error(`${error.type}: ${error.message}`);
}
const { data } = await response.json();php
<?php
$handle = curl_init(getenv('CALLAGENT_API_URL') . '/billing/auto-reload');
curl_setopt_array($handle, [
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('CALLAGENT_API_KEY'),
'Accept: application/json',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'enabled' => true,
'threshold_amount' => 1,
'reload_amount' => 1,
'payment_method_id' => '01JQ0000000000000000000000',
'monthly_reload_limit' => 25,
'monthly_reload_cap_amount' => 1,
]),
]);
$body = json_decode(curl_exec($handle), true);
$status = curl_getinfo($handle, CURLINFO_HTTP_CODE);
curl_close($handle);
if ($status >= 400) {
throw new RuntimeException("{$body['error']['type']}: {$body['error']['message']}");
}
$data = $body['data'];python
import os, requests
response = requests.put(
f"{os.environ['CALLAGENT_API_URL']}/billing/auto-reload",
headers={"Authorization": f"Bearer {os.environ['CALLAGENT_API_KEY']}"},
json={
"enabled": True,
"threshold_amount": 1,
"reload_amount": 1,
"payment_method_id": "01JQ0000000000000000000000",
"monthly_reload_limit": 25,
"monthly_reload_cap_amount": 1
},
timeout=30,
)
response.raise_for_status()
data = response.json()["data"]Responses
| Status | Body |
|---|---|
200 | data-wrapped object |
Errors
| Status | When it happens |
|---|---|
403 | Neither the API key scope nor the session user's permission allows billing.write |
404 | The supplied payment_method_id is not a usable card on this organization |
422 | Field validation failed, or the reload amount exceeds the platform maximum |
Example response, generated from the schema:
json
{
"data": {
"enabled": true,
"threshold_amount": "string",
"reload_amount": "string",
"currency": "string",
"payment_method_id": "01JQ0000000000000000000000",
"monthly_reload_limit": 25,
"monthly_reload_cap_amount": "string",
"cooldown_minutes": 1,
"consecutive_failures": 1,
"disabled_reason": "string",
"last_succeeded_at": "2026-01-01T12:00:00+00:00",
"last_failed_at": "2026-01-01T12:00:00+00:00"
}
}Create a Stripe PaymentIntent to top up credits and return the client secret for the browser
POST/billing/credits/purchase
- Requires
billing.write. - Accepts an
Idempotency-Keyheader; replaying a key returns the original result instead of acting twice. - Idempotency uses the request-body field
idempotency_key, not an Idempotency-Key header. Credits are granted only when the Stripe webhook settles the charge.
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
amount | number | yes | |
payment_method_id | string | null | — | |
idempotency_key | string | null | — | max 191 chars |
bash
curl -X POST "$CALLAGENT_API_URL/billing/credits/purchase" \
-H "Authorization: Bearer $CALLAGENT_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"amount": 1,
"payment_method_id": "01JQ0000000000000000000000"
}'js
const response = await fetch(`${process.env.CALLAGENT_API_URL}/billing/credits/purchase`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.CALLAGENT_API_KEY}`,
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
"amount": 1,
"payment_method_id": "01JQ0000000000000000000000"
}),
});
if (!response.ok) {
const { error } = await response.json();
throw new Error(`${error.type}: ${error.message}`);
}
const { data } = await response.json();php
<?php
$handle = curl_init(getenv('CALLAGENT_API_URL') . '/billing/credits/purchase');
curl_setopt_array($handle, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('CALLAGENT_API_KEY'),
'Accept: application/json',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'amount' => 1,
'payment_method_id' => '01JQ0000000000000000000000',
]),
]);
$body = json_decode(curl_exec($handle), true);
$status = curl_getinfo($handle, CURLINFO_HTTP_CODE);
curl_close($handle);
if ($status >= 400) {
throw new RuntimeException("{$body['error']['type']}: {$body['error']['message']}");
}
$data = $body['data'];python
import os, requests
response = requests.post(
f"{os.environ['CALLAGENT_API_URL']}/billing/credits/purchase",
headers={"Authorization": f"Bearer {os.environ['CALLAGENT_API_KEY']}"},
json={
"amount": 1,
"payment_method_id": "01JQ0000000000000000000000"
},
timeout=30,
)
response.raise_for_status()
data = response.json()["data"]Responses
| Status | Body |
|---|---|
200 | JSON object |
Errors
| Status | When it happens |
|---|---|
402 | Stripe declined the charge; the body carries failure_message |
403 | Neither the API key scope nor the session user's permission allows billing.write |
404 | The supplied payment_method_id is not a usable card on this organization |
422 | amount is missing or not greater than zero |
503 | Stripe is not configured or unreachable |
Example response, generated from the schema:
json
"string"