Audio

Text-to-speech, transcriptions, and realtime session minting

OpenAI-compatible audio surfaces on Hypery. All three endpoints require OAuth scope ai:audio (or a first-party API key / session). Chat scopes alone are not accepted.

Billing

Audio is not token-metered the same way as chat. Hypery always charges the billed team:

ModeHow audio bills
vag_passthroughStripe invoice item after a successful call (list-price USD)
metered / prepaidCredit reserve → finalize
Non-Vercel upstream under vagDowngraded to the credit ledger

Also enforced on every call: DISABLE_AI_SPEND, rate limits, app + team spending caps, model permission spend caps, usageRecords.

Realtime sessions are billed by duration: nothing is charged at mint (the 25-minute worst case is only held), and the client reports the session length to POST /api/v1/realtime/sessions/{id}/settle when it disconnects. See Realtime sessions. A POST /api/v1/usage/charge with category=realtime is acknowledged and charges nothing.

Text-to-speech

POST /api/v1/audio/speech

FieldNotes
inputRequired, max 4096 characters
modeltts-1 ($0.015/1K chars) or tts-1-hd ($0.030/1K)
voicealloy, echo, fable, onyx, nova, shimmer, …
response_formatmp3 (default), opus, aac, flac, wav, pcm

Optional header: Idempotency-Key (prevents double-charge on retries).

Transcriptions

POST /api/v1/audio/transcriptions — multipart/form-data

FieldNotes
fileRequired, max 25 MB
modelDefault whisper-1

Billing estimates minutes from file size (~32 kbps speech heuristic, capped at 45 min) × $0.006/min.

Realtime sessions

POST /api/v1/realtime/sessions — mints a single-use, short-lived Vercel AI Gateway client secret for a realtime voice session over WebSocket (the contract of gateway.experimental_realtime.getToken in @ai-sdk/gateway).

FieldNotes
modelA gateway realtime model: openai/gpt-realtime-2 (default), openai/gpt-realtime-2.1, openai/gpt-realtime-1.5, openai/gpt-realtime-mini. Bare ids (gpt-realtime-2) are accepted. Anything else is refused with 400 model_not_realtime.

Voice, instructions, turn detection and tools are set by the client on the WebSocket (sessionConfig), not at mint; older OpenAI-style mint fields are accepted and ignored.

Response:

{
  "id": "rts_…",
  "token": "vcst_…",
  "url": "wss://ai-gateway.vercel.sh/v4/ai/realtime-model?ai-model-id=openai%2Fgpt-realtime-2",
  "model": "openai/gpt-realtime-2",
  "expires_at": 1767225600,
  "max_duration_ms": 1500000,
  "usd_per_minute": 0.096,
  "hypery": { "billing": "duration", "settle_url": "/api/v1/realtime/sessions/rts_…/settle" }
}

Connect before expires_at (epoch seconds). In the browser, pass { token, url } to experimental_useRealtime from @ai-sdk/react with gateway.experimental_realtime(model) as the model:

import { experimental_useRealtime as useRealtime } from '@ai-sdk/react';
import { gateway } from '@ai-sdk/gateway';
 
const model = useMemo(() => gateway.experimental_realtime('openai/gpt-realtime-2'), []);
const realtime = useRealtime({
  model,
  // Your server route calls Hypery and returns { token, url }.
  api: { token: '/api/realtime/token' },
  sessionConfig: { voice: 'alloy', turnDetection: { type: 'server-vad' } },
});

Gateway limits: a session lasts at most 25 minutes and closes after 5 minutes idle.

Billing and settle

A card on file is required — a team without one is refused with 402 card_required ("Realtime sessions require a card on file (metered billing)"). The gateway's Stripe meter does not cover a minted WebSocket session, so Hypery bills it by duration at the model's per-minute estimate (usd_per_minute: a minute of input audio plus a minute of output audio at the gateway's list prices; charged at the normal 2× media price):

  1. Mint — the 25-minute worst case is held (app grants, then purchased credits, then the card's unpaid-usage allowance). Nothing is charged.
  2. Settle — when the WebSocket closes, POST /api/v1/realtime/sessions/{id}/settle with { "durationMs": 183000 }. The duration is capped at 25 minutes and at the time since mint. Purchased credits are drawn first; the rest is one Stripe invoice item. Idempotent: the first settle wins, retries return the same result. Only the team the session was minted for can settle it.
  3. Never settled — about 30 minutes after mint the full 25-minute hold is charged.

Settle response:

{ "id": "rts_…", "status": "settled", "billed_ms": 183000, "credits": 59, "amount_usd": 0.59 }

Errors

StatusCodeMeaning
403insufficient_scopeMissing ai:audio
400model_not_realtimeRealtime mint for a model that isn't a gateway realtime model
402card_requiredRealtime mint for a team without a card on file
402insufficient_creditsTeam cannot spend / spend cap
404session_not_foundSettle for an unknown session, or one minted for another team
409idempotency_conflictSame Idempotency-Key already billed
429rate_limit_exceededRate limited
503—No audio upstream configured