Dial x402

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 callBearer
Stay account-less; pay per request from a funded walletx402
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):

  1. Authorization: Bearer <privy-access-token> → debit credits.
  2. No Bearer token → fall through to x402 (signed PAYMENT-SIGNATURE).
  3. Authorization: Bearer <DIAL_ADMIN_API_KEY> → operator bypass (no payment).
  1. Open /dashboard/billing and sign in with Privy (email, wallet, or social — same provider as everywhere else).
  2. Find the API Token card.
  3. Click Reveal token → click Copy.
  4. Paste it into your shell or script as Authorization: Bearer <token>.

The token rotates every ~1 hour. The dashboard auto-refreshes silently while it is open, but anything pasted into a long-running script will need to be re-fetched after expiry. Click Refresh in the API Token card to mint a fresh one.

Get a 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

On this page