Charge a saved card to top up the wallet
https://api.voxrouter.ai/v1/billing/topupCharge a previously-saved Stripe payment method off-session and credit the caller's organization wallet. Accepts both `vr_session_*` (CLI) and raw better-auth session tokens (dashboard server-side). `pk_*` keys are rejected — minting credit purchases from an API key would be a privilege-escalation footgun. Idempotency is REQUIRED. Pass a per-attempt `idempotency_key` — Stripe uses it to dedupe retries. The SDK generates a fresh UUID per call when callers don't supply one. If the card requires a 3D Secure challenge, the response is `402 stripe_3ds_required` with a URL pointing at the dashboard's PaymentIntents flow. The CLI prints the URL; the user finishes the challenge in a browser, after which the card becomes off-session-friendly for subsequent topups.
curl -X POST https://api.voxrouter.ai/v1/billing/topup \
-H "Authorization: Bearer $VOXROUTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount_cents": 0,
"payment_method_id": "string",
"idempotency_key": "string"
}'Headers
AuthorizationstringRequiredAuthorization: Bearer pk_....Content-TypestringRequiredapplication/json.Body
amount_centsintegerRequiredpayment_method_idstringRequiredidempotency_keystringRequiredResponse
Charge succeeded; webhook will credit the wallet.
{
"ok": false,
"payment_intent_id": "string",
"amount_cents": 0
}okbooleanRequiredpayment_intent_idstringRequiredamount_centsintegerRequiredErrors
Non-2xx responses return a JSON body with a machine-readable error code and an optional details string.
| Status | Meaning |
|---|---|
| 400 | Request body failed validation or model string is malformed. |
| 401 | Missing or invalid API key. |
| 402 | Stripe declined the charge. Either the card was rejected (`error: "card_declined"`) or 3DS verification is required (`error: "stripe_3ds_required"`). |
| 403 | Bearer is a `pk_*` API key (scope_required). |
| 503 | Stripe not configured on this deployment. |