Top up an OAuth user wallet
Move credits from your developer wallet into a Quota user's wallet — atomically, idempotently, and observably. Common use cases: promo grants for new signups, refund credits after a support ticket, loyalty bonuses, comp credits when a model misbehaves.
01 Confirm the user's Quota UUID
Funding targets the user by their Quota UUID — the sub claim from /oauth/userinfo, not an external_user_id you assigned. If your app stores the user via NextAuth / Clerk / Auth0, that's the field labeled quota_sub / quotaProfile.id / similar in the user's session.
// From a Next.js Route Handler or Server Component:
import { getQuotaUser } from "@usequota/nextjs/server";
const user = await getQuotaUser();
if (!user) throw new Error("not signed in");
console.log(user.id);
// → "e4f8b9a1-3c2d-4e5f-9a0b-1c2d3e4f5a6b"
// Store this as quota_user_id on your own user row.02 Call the funding endpoint
POST /v1/funding/oauth_user with your developer API key. Quota debits your wallet for the same amount it credits the user's wallet, in a single DB transaction — there is no intermediate state where the money exists in neither wallet.
curl https://api.usequota.ai/v1/funding/oauth_user \
-H "Authorization: Bearer $QUOTA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: promo_2026_05_e4f8b9a1" \
-d '{
"user_id": "e4f8b9a1-3c2d-4e5f-9a0b-1c2d3e4f5a6b",
"amount": 500000,
"description": "Welcome bonus — May 2026"
}'03 Verify and react
The response contains the user's new balance_after and the ledger_id of the transfer. Persist theledger_id on your side — that's your audit trail and your reconcile key against the user's in-Quota ledger view.
{
"success": true,
"user_id": "e4f8b9a1-3c2d-4e5f-9a0b-1c2d3e4f5a6b",
"amount": 500000,
"balance_after": 1500000,
"ledger_id": "f0a92e74-2c8b-4ad5-9c1e-94b1f3c8a275"
}04 (Optional) Subscribe to balance.updated
If your app displays the user's balance live, the funding call fires a balance.updated webhook to the OAuth client that owns the user. Wire your handler at the URL you set when registering the app — Quota signs the payload so you can verify it didn't come from somewhere else.
{
"event": "balance.updated",
"user_id": "e4f8b9a1-3c2d-4e5f-9a0b-1c2d3e4f5a6b",
"new_balance": 1500000,
"amount_spent": -500000,
"model": null,
"endpoint": "/v1/funding/oauth_user"
}Note the negative amount_spent — funding events reuse the same webhook event shape as usage events, with a negative value to mean "credits added". The same path powers refunds.
05 When things go wrong
The funding endpoint returns the standard error envelope. Common cases:
- 402
insufficient_credits— your developer wallet is below the transfer amount. Top up your own wallet (Stripe Checkout viaPOST /api/payments/checkout) and retry. - 404
user_not_found— no Quota user has thatuser_id. Common cause: storing anexternal_user_idin the field that expects a Quota UUID — they look similar enough to confuse. - 409
conflict— idempotency key reused (often across/funding/creditand/funding/oauth_user). The response includes the existingledger_id— treat it as a successful duplicate, not an error. - 400
invalid_request— usually a malformed UUID or a non-positive amount.