PayphonePayphone

Bearer Token (Credits Auth)

Privy session JWTs and dial_live_* API keys for credits-mode endpoints.

Bearer Token (Credits Auth)

Credits-mode endpoints accept Authorization: Bearer … in two forms:

TokenTTLBest for
Privy access token (eyJ…)~1 hourDashboard, browser apps with Privy SDK
API key (dial_live_*)Until revokedAgents, cron, server scripts

If you would rather pay per request without an account at all, skip Bearer and use x402 — most paid endpoints also accept on-chain USDC.

When to use Bearer (vs x402)

You want…Use
Pre-buy credits, then run scripts without re-signing each callBearer (Privy JWT or API key)
Stay account-less; pay per request from a funded walletx402
Mix and match (script with credits, agent with wallet)Either — dual-payment routes support both

Internally the API resolves auth in this order (see apps/web/lib/dual-payment.ts):

  1. Authorization: Bearer <privy-access-token> or Bearer dial_live_* → debit credits.
  2. No Bearer token → fall through to x402 (signed PAYMENT-SIGNATURE).
  3. Authorization: Bearer <DIAL_ADMIN_API_KEY> → operator bypass (no payment).

API keys are scope-gated per route. Privy sessions skip scope checks. See API Keys for the full scope table.

Get credentials from the dashboard

Open /dashboard/billing and sign in with Privy.

  1. In API Keys · permanent bearer, click Quick: Cursor / agent key (or create a custom key).
  2. Copy the dial_live_* secret once — it is shown only at creation.
  3. Use Authorization: Bearer dial_live_… — the key does not expire until you revoke it.

See API Keys.

Session token (quick curl only — ~1 hour)

  1. Scroll to Session token · short-lived (Privy JWT, starts with eyJ…).
  2. Click Reveal session tokenCopy.
  3. 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://payphone.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 agents should prefer a dial_live_* API key instead of embedding Privy refresh logic.

Use the token

Once you have a token, every credits-mode endpoint accepts it:

TOKEN="<paste-from-dashboard>"

curl -sS -X POST "https://payphone.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://payphone.wtf/api/v1/account" \
  -H "Authorization: Bearer $TOKEN"
{
  "privyUserId": "did:privy:cm…",
  "credits": 9384,
  "totalCredits": 9384,
  "authMethod": "apikey",
  "apiKeyPrefix": "abc12345"
}

authMethod and apiKeyPrefix appear when authenticating with a dial_live_* key so agents can confirm the correct credential.

Lifetime, refresh, revocation

CredentialTTLRevocation
Privy JWT~1 hourSign out of Privy
dial_live_* API keyUntil revokedRevoke in /dashboard/billing
  • Privy refresh: the dashboard auto-refreshes via the Privy SDK while open. Headless callers must call getAccessToken() again after expiry.
  • API keys: no refresh needed; rotate by minting a new key and revoking the old prefix.

Security checklist

  • Treat tokens and API keys like passwords. Anyone with the string can spend your credits until it expires or is revoked.
  • Do not commit credentials to source control or paste them into chat threads.
  • Prefer dial_live_* keys for server-to-server agents over long-lived Privy JWT paste.

See also

On this page