Adjust reseller billing
Apply a signed delta to a reseller's balance. The endpoint targets the right column automatically: in credits mode it moves credits, in users mode it moves max_users. You never pass the mode explicitly.
The update runs as a single atomic relative SQL statement (credits = credits + delta, or the max_users equivalent with a cast to SIGNED). Two concurrent adjustments sum instead of racing. The read path never goes through the 60-second cache used by other reseller endpoints, so a burst of top-ups from a monthly job cannot silently overwrite each other. Every successful adjustment is written to reg_userlog with the type panel_api_billing_adjust, so it appears in the same audit trail as CMS-driven changes.
Endpoint
POST https://<your-panel-domain>/panel-api/v1/resellers/{id}/billing/adjust
Authentication
Send the API key in the Authorization: Bearer <your-api-key> header. Only admin keys can call this endpoint.
Required scope
resellers:write
Idempotency
Every POST must include an Idempotency-Key header. Reusing the same key with the same body replays the cached response; reusing it with a different body returns 409 idempotency_conflict. For a monthly billing job, use the invoice ID as the key so that a retry after a timeout never stacks a second charge on top.
Path parameters
| Name | Type | Description |
|---|---|---|
id |
int | The reg-user ID of the reseller. |
Request body
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
delta |
float | yes | Signed change. Positive to top up, negative to claw back. In users mode the value is cast to int on the server. |
|
reason |
string | no | "" |
Free-form label written to the audit log. Truncated to 100 characters. |
{
"delta": 1,
"reason": "docs sample adjust +1"
}
Response
The response is the billing snapshot after the adjustment, in the same shape as GET /resellers/{id}/billing.
{
"mode": "credits",
"credits": 11,
"max_users": null,
"active_users": null,
"billing_expires": null
}
Fractional credits are preserved. Adding 0.10 to a balance of 0.25 produces 0.35, not 0. Do not floor or round in your own code before sending the delta.
Examples
cURL
curl -X POST https://<your-panel-domain>/panel-api/v1/resellers/262260/billing/adjust \
-H "Authorization: Bearer <your-api-key>" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: invoice-INV-2026-00814" \
-d '{"delta":1,"reason":"docs sample adjust +1"}'
PHP SDK
require __DIR__ . '/api-panel-php-sdk-1.0.0/autoload.php';
use XtreamAI\PanelApi\PanelApiClient;
$client = new PanelApiClient(baseUrl: 'https://<your-panel-domain>', token: '<your-api-key>');
$billing = $client->resellers->adjustBilling(
id: 262260,
delta: 1,
reason: 'docs sample adjust +1',
idempotencyKey: 'invoice-INV-2026-00814',
);
echo $billing->credits, PHP_EOL;
Python SDK
from xtream_ai_panel_api import PanelApiClient
client = PanelApiClient(base_url="https://<your-panel-domain>", token="<your-api-key>")
billing = client.resellers.adjust_billing(
262260,
delta=1,
reason="docs sample adjust +1",
idempotency_key="invoice-INV-2026-00814",
)
print(billing.credits)
Errors
| HTTP | Error slug | When it happens | How to fix |
|---|---|---|---|
| 400 | missing_idempotency_key |
The Idempotency-Key header was not sent. |
Add the header on every POST. |
| 400 | invalid_body |
The delta field is missing from the body. |
Include delta as a signed number. |
| 401 | invalid_key |
Missing, malformed, or unknown API key. | Send a live key in Authorization: Bearer <token>. |
| 403 | admin_only_endpoint |
A reseller key called this endpoint. | Use an admin key. |
| 403 | insufficient_scope |
The key does not carry resellers:write. |
Rotate the key with the correct scopes. |
| 404 | not_found |
No reseller exists with that ID. | Verify the ID with GET /resellers/{id} first. |
| 409 | idempotency_conflict |
The Idempotency-Key was reused with a different body. |
Generate a new key or resend the original body. |
| 422 | negative_balance_not_allowed |
A negative delta in credits mode is larger than the current balance. The balance is untouched; no partial deduction happens. |
Lower the delta or top up first. |
| 422 | cap_below_active_users |
A negative delta in users mode would drop max_users below the currently used slot count. No lines are auto-disabled to make room. |
Free the slots first (disable or delete lines), then retry. |
| 422 | negative_cap_not_allowed |
A negative delta in users mode would take max_users below zero. |
Lower the delta. |
| 422 | mismatched_mode |
The reseller is stored in a billing mode this endpoint does not recognize (should not happen on a healthy panel). | Contact support. |
| 429 | rate_limited |
The per-key request budget for this minute is spent. | Back off and retry after the minute rolls over. |