Sub-Account Auto-Recharge (Custom Payment Provider)
If you prefer to handle sub-account credit payments through your own provider instead of Stripe (bank transfers, local payment gateways, custom billing systems), the platform exposes a webhook + API pair so you can run the full charge-and-grant loop yourself. The non-technical overview lives on the Agency Accounts page; this page covers the exact webhook payload and the API call you make to grant credits afterwards.
The webhook is only one of the ways automatic top-ups get paid for. If you connected Stripe or PayPal in SaaS Mode, the platform charges the client’s saved card or PayPal account itself and grants the credits, so there is nothing on this page for you to build. Read on only if you want to handle the payment yourself.
The flow at a glance
- A sub-account’s credit balance drops below their auto-recharge threshold.
- The platform calls your webhook URL with the sub-account details and how many credits they need.
- Your server charges the customer through whichever provider you use.
- Your server calls the Grant Credits API to add credits to that sub-account.
- Your server responds
200to acknowledge the webhook.
1. Webhook: agency_sub_account_auto_recharge
Configure the webhook URL by clicking SaaS Mode in the main sidebar, choosing Use a Custom Payment Provider Instead (or, once configured, opening the Custom Payment Provider card), and filling in the Auto-Recharge Webhook field.
When it fires
When a sub-account’s credit balance drops below their configured auto-recharge threshold.
Payload
{
"event": "agency_sub_account_auto_recharge",
"sub_account_id": "<sub-account-id>",
"sub_account_email": "customer@example.com",
"sub_account_name": "John Doe",
"agency_id": "<agency-id>",
"credits_requested": 500,
"current_balance": 42,
"threshold": 100,
"price_per_credit_cents": 10,
"price_per_credit_currency": "usd",
"total_amount_cents": 5000,
"timestamp": "2026-04-13T12:00:00.000Z",
"idempotency_key": "auto_recharge_abc123_1681387200000"
}
| Field | Description |
|---|---|
event |
Always agency_sub_account_auto_recharge for this webhook. |
sub_account_id |
The unique ID of the sub-account that needs credits. |
sub_account_email |
The sub-account’s email address. |
sub_account_name |
The sub-account’s display name. |
agency_id |
Your agency account’s unique ID. |
credits_requested |
How many credits to grant. This is your configured recharge amount, but if the balance has fallen further below the threshold than that amount would cover, it is automatically raised to whatever is needed to bring the balance back above the threshold in one go. Always charge for (and grant) the credits_requested value from the payload — don’t hard-code your recharge amount. |
current_balance |
The sub-account’s credit balance at the time the webhook was sent. |
threshold |
The balance threshold that triggered the recharge. |
price_per_credit_cents |
Your configured price per credit, in cents. |
price_per_credit_currency |
The currency for the price (e.g., usd). |
total_amount_cents |
The total amount to charge, in cents (credits_requested × price_per_credit_cents). |
timestamp |
When the webhook was sent (ISO 8601 format). |
idempotency_key |
A unique key for this specific recharge request. Use this to prevent granting credits twice if your server receives the same webhook more than once. |
Important notes
- 5-minute cooldown — After a recharge attempt for a sub-account, the platform will not send another webhook for that sub-account for at least 5 minutes, even if their balance drops further. Prevents duplicate charges during processing.
- Use the idempotency key — Always check
idempotency_keybefore granting credits. If your server crashed after granting credits but before responding, the platform may resend the webhook on the next credit drop. - Failures are safe and self-healing — If your webhook URL is unreachable or returns an error, no credits are granted. The platform keeps re-sending the webhook (once per 5-minute cooldown) as long as the sub-account stays below the threshold — it does not require the balance to climb back above the threshold and drop again first. A single missed charge can no longer leave a sub-account permanently un-recharged.
- Set your recharge amount at or above the threshold — Your configured recharge amount must be greater than or equal to the auto-recharge threshold, so one recharge always restores the balance above the threshold. (If you ever need to fix a sub-account that drifted far below the threshold, the platform tops it up by the full deficit automatically — see
credits_requestedabove.)
2. Grant Credits API
After your server has processed payment, call this endpoint to add the credits to the sub-account.
Request
POST https://api.dmchamp.com/v1/subaccounts/credits
X-API-Key: YOUR_API_KEY
Content-Type: application/json
{
"email": "customer@example.com",
"amount": 500,
"description": "Auto-recharge via webhook"
}
| Field | Required | Description |
|---|---|---|
email |
Yes | The sub-account’s email address (must match an existing sub-account under your agency). |
amount |
Yes | The number of credits to grant. |
description |
No | A note describing why credits were added (shown in the credit transaction history). |
Authentication: Send your API key in a request header — either X-API-Key: YOUR_API_KEY or Authorization: Bearer YOUR_API_KEY. Headers are the recommended way, because a key in the URL ends up in browser history, proxy logs and server access logs. The ?apiKey=YOUR_API_KEY query parameter and an apiKey field in the JSON body also still work, so older integrations keep running unchanged.
Response
{
"success": true,
"sub_account_id": "abc123xyz",
"credits_added": 500,
"new_balance": 542
}
Tip: Store the idempotency_key from the webhook payload and check it before calling this endpoint. This prevents accidentally granting credits twice if your server receives the same webhook more than once.
What happens to granted credits at the monthly reset
This depends on how the sub-account is set up:
- Reselling (the sub-account pays you for credits): credits you grant here are recorded as purchased credits and carry over every month. Whatever the sub-account has not spent stays on the balance.
- Allocation (you give the sub-account a monthly allowance): the balance is refilled to the monthly allowance on the reset date, so anything unspent is not carried over. This is intentional — the allowance is granted fresh each month.
If you want a sub-account’s leftover balance to be added on top of its new monthly allowance instead of replacing it, turn on credit roll-over:
PUT https://api.dmchamp.com/v1/subaccounts/{subAccountUid}/limits
X-API-Key: YOUR_API_KEY
Content-Type: application/json
{
"usageLimits": { "roll_over_to_next_month": true }
}
If the sub-account — or the plan it is on — also has a roll-over cap or an expiry set, the reset is the moment those apply: the carried-over allowance is trimmed to the months you allow, and allowance left unused for longer than the expiry window is dropped, before the new allowance is added. Credits granted through this endpoint, auto-recharges and the client’s own top-ups are one-time credits and are never trimmed. See Capping what rolls over.
End-to-end example
A typical handler looks like this:
on POST /your-recharge-webhook:
payload = request.body
if seen(payload.idempotency_key):
return 200 // already processed, ack and exit
charge_result = your_payment_provider.charge(
email = payload.sub_account_email,
amount_cents = payload.total_amount_cents,
currency = payload.price_per_credit_currency,
)
if not charge_result.ok:
return 500 // platform will retry on next balance drop
api.post("/v1/subaccounts/credits", {
email = payload.sub_account_email,
amount = payload.credits_requested,
description = "Auto-recharge via " + your_provider_name,
})
mark_seen(payload.idempotency_key)
return 200