Bearer Token (Credits Auth)
Where the Bearer token comes from, how to grab one from the dashboard, and how to use it on credits-mode API endpoints.
Bearer Token (Credits Auth)
The Authorization: Bearer … header on credits-mode endpoints carries a
Privy access token — the same JWT the dashboard uses to authenticate
your session. There is no separate signup, no API console, no manual key
issuance: if you can sign in to the dashboard, you can mint one.
If you would rather pay per request without an account at all, skip this page and use x402 — every credits-mode endpoint also works with x402 and a USDC-funded wallet.
When to use Bearer (vs x402)
| You want… | Use |
|---|---|
| Pre-buy credits, then run scripts without re-signing each call | Bearer |
| Stay account-less; pay per request from a funded wallet | x402 |
| Mix and match (script with credits, agent with wallet) | Either — every paid endpoint supports both |
Internally the API resolves auth in this order (see
apps/web/lib/dual-payment.ts):
Authorization: Bearer <privy-access-token>→ debit credits.- No Bearer token → fall through to x402 (signed
PAYMENT-SIGNATURE). Authorization: Bearer <DIAL_ADMIN_API_KEY>→ operator bypass (no payment).
Get credentials from the dashboard
Open /dashboard/billing and sign in with Privy.
Permanent API key (recommended for Cursor, cron, bots)
- In API Keys · permanent bearer, click Quick: Cursor / agent key (or create a custom key).
- Copy the
dial_live_*secret once — it is shown only at creation. - Use
Authorization: Bearer dial_live_…— the key does not expire until you revoke it.
See API Keys.
Session token (quick curl only — ~1 hour)
- Scroll to Session token · short-lived (Privy JWT, starts with
eyJ…). - Click Reveal session token → Copy.
- Fine for a one-off test while the dashboard is open. Not for Cursor or long-running scripts.
The session token rotates every ~1 hour. Click Refresh after expiry.
Get a session token programmatically
If your app is already using the Privy SDK on
the same app.id as Dial, call getAccessToken():
import { usePrivy } from "@privy-io/react-auth";
function MyComponent() {
const { getAccessToken } = usePrivy();
const send = async () => {
const token = await getAccessToken();
if (!token) throw new Error("Sign in first");
await fetch("https://x402.dial.wtf/api/v1/sms/send", {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ to: "+18148835337", message: "hi from credits" }),
});
};
}Server-side, you can verify a Privy token with @privy-io/server-auth
the same way Dial does internally — but that's only useful if you're
proxying / caching Dial calls behind your own service.
Use the token
Once you have a token, every credits-mode endpoint accepts it:
TOKEN="<paste-from-dashboard>"
curl -sS -X POST "https://x402.dial.wtf/api/v1/sms/send" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"to":"+18148835337","message":"Hello via credits"}'Sample success response:
{
"success": true,
"provider": "dial",
"messageId": "msg_…",
"to": "+18148835337",
"segments": 1
}If you have no credits left, you'll see:
{
"success": false,
"error": "insufficient_credits",
"credits": 0,
"required": 1
}(HTTP 402.) Top up via the dashboard or call
/api/v1/credits/top-up once with x402
to refill, then keep using the same Bearer token.
Check your balance
curl -sS "https://x402.dial.wtf/api/v1/account" \
-H "Authorization: Bearer $TOKEN"{
"credits": 9384,
"privyUserId": "did:privy:cm…"
}Lifetime, refresh, revocation
- TTL: ~1 hour (set by Privy; not configurable per-app).
- Refresh: the dashboard auto-refreshes via the Privy SDK while open.
Headless callers must call
getAccessToken()(or click Refresh in the dashboard card) again after expiry. - Revocation: signing out of Privy invalidates the current token and every script using it. There is no per-token revoke today — that is tracked in #12 along with long-lived programmatic API keys.
Security checklist
- Treat the token like a password. Anyone with the string can spend your credits until it expires.
- Do not commit it to source control or paste it into chat threads.
- Prefer
getAccessToken()over hard-coding tokens — the SDK refresh loop is the only thing that survives a redeploy without manual intervention. - For server-to-server agents that need a stable identity, watch #12 or use x402 signed payments instead.
See also
- Getting Started — pick between Bearer and x402.
/api/v1/credits/top-up— buy credits with x402./api/v1/account— verify your balance.