Browse docs

API reference · Voice sessions

Voice sessions

A session is the server-owned unit of model selection, realtime attachment, wallet reservation, usage measurement, and settlement.

1 List a model2 Create a session3 Attach realtime4 End and settle

Endpoints

All session operations are scoped to the key that created the session.

POST

Create a voice session

Reserve a bounded wallet amount and create a server-owned speech-to-speech session.

/v1/voice/sessionsBearer VR_SK

Headers

FieldTypeDescription
AuthorizationrequiredstringVoiceOpenRouter secret key. Keep it on your server and never put it in a URL.
Content-Typerequiredstring
application/json
The request body is JSON.
Idempotency-KeystringOptional printable key. Repeating the same owner and request returns the same session.

Request body

application/json

Select one model from GET /v1/models. The only supported public mode is speech_to_speech.

FieldTypeDescription
modelrequiredstringCatalog model id returned by GET /v1/models.
moderequiredstring
speech_to_speech
Enables the public realtime speech-to-speech contract.
json
{
  "model": "xai/grok-voice-latest",
  "mode": "speech_to_speech"
}

Response

201 Created

The session is ready to attach through the gateway-owned realtime URL.

FieldTypeDescription
idrequiredstringOpaque VoiceOpenRouter session identifier.
statusrequiredstring
ready | active | completed | cancelled | failed | expired
Current server-owned lifecycle status.
modelrequiredstringThe catalog model id selected at session creation.
moderequiredstring
speech_to_speech
The public session mode for this contract.
realtimerequiredobjectGateway-owned WebSocket URL and protocol name.
audiorequiredobjectNegotiated public input and output audio formats.
capabilitiesrequiredobjectCapabilities available for the selected catalog model.
billingrequiredobjectPreauthorization and remaining wallet information.
usagerequiredobjectServer-measured usage and the rate snapshot used for settlement.
trace_idrequiredstringRedacted support lookup identifier; never a provider credential.
json
{
  "id": "vs_EXAMPLE",
  "object": "voice.session",
  "status": "ready",
  "model": "xai/grok-voice-latest",
  "mode": "speech_to_speech",
  "realtime": {
    "url": "wss://api.voiceopenrouter.com/v1/realtime?session_id=vs_EXAMPLE",
    "protocol": "vor-realtime-v1"
  },
  "audio": {
    "input": {
      "type": "audio/pcm",
      "encoding": "pcm16le",
      "sample_rate_hz": 24000,
      "channels": 1
    },
    "output": {
      "type": "audio/pcm",
      "encoding": "pcm16le",
      "sample_rate_hz": 24000,
      "channels": 1
    }
  },
  "capabilities": {
    "speech_to_speech": true,
    "server_vad": true,
    "barge_in": true,
    "input_transcription": "optional",
    "output_transcription": "optional"
  },
  "created_at": 1770000000,
  "expires_at": 1770000900,
  "billing": {
    "preauthorized_cents": 50,
    "balance_cents": 950,
    "reserved_cents": 50
  },
  "usage": {
    "model": "xai/grok-voice-latest",
    "provider": "xai",
    "pricing_source": "upstream_derived",
    "rate_snapshot_id": "rate_EXAMPLE",
    "audio_in_ms": 0,
    "audio_out_ms": 0,
    "billed_cents": 0
  },
  "trace_id": "trace_EXAMPLE"
}

Responses

status codes
201Session created and preauthorized.
401The key is invalid, revoked, or does not own the resource.invalid_vr_sk
402The wallet cannot cover the session preauthorization.insufficient_wallet_balance
404The selected model is not available to this key.model_not_found
409An idempotency key was reused with a different request.idempotency_conflict
  • The selected model rate is snapshotted at session creation. Settlement uses server/provider usage, not client-reported cost.
  • The realtime URL and protocol are gateway-owned public contract values; provider endpoints and keys are never returned.
GET

Retrieve a voice session

Read the current lifecycle state and server-measured usage for an owned session.

/v1/voice/sessions/{session_id}Bearer VR_SK used to create the session

Path parameters

FieldTypeDescription
session_idrequiredstringThe id returned by POST /v1/voice/sessions.

Response

200 OK

The owned session resource, including current usage and billing state.

FieldTypeDescription
idrequiredstringOpaque VoiceOpenRouter session identifier.
statusrequiredstring
ready | active | completed | cancelled | failed | expired
Current server-owned lifecycle status.
modelrequiredstringThe catalog model id selected at session creation.
moderequiredstring
speech_to_speech
The public session mode for this contract.
realtimerequiredobjectGateway-owned WebSocket URL and protocol name.
audiorequiredobjectNegotiated public input and output audio formats.
capabilitiesrequiredobjectCapabilities available for the selected catalog model.
billingrequiredobjectPreauthorization and remaining wallet information.
usagerequiredobjectServer-measured usage and the rate snapshot used for settlement.
trace_idrequiredstringRedacted support lookup identifier; never a provider credential.
json
{
  "id": "vs_EXAMPLE",
  "object": "voice.session",
  "status": "ready",
  "model": "xai/grok-voice-latest",
  "mode": "speech_to_speech",
  "realtime": {
    "url": "wss://api.voiceopenrouter.com/v1/realtime?session_id=vs_EXAMPLE",
    "protocol": "vor-realtime-v1"
  },
  "audio": {
    "input": {
      "type": "audio/pcm",
      "encoding": "pcm16le",
      "sample_rate_hz": 24000,
      "channels": 1
    },
    "output": {
      "type": "audio/pcm",
      "encoding": "pcm16le",
      "sample_rate_hz": 24000,
      "channels": 1
    }
  },
  "capabilities": {
    "speech_to_speech": true,
    "server_vad": true,
    "barge_in": true,
    "input_transcription": "optional",
    "output_transcription": "optional"
  },
  "created_at": 1770000000,
  "expires_at": 1770000900,
  "billing": {
    "preauthorized_cents": 50,
    "balance_cents": 950,
    "reserved_cents": 50
  },
  "usage": {
    "model": "xai/grok-voice-latest",
    "provider": "xai",
    "pricing_source": "upstream_derived",
    "rate_snapshot_id": "rate_EXAMPLE",
    "audio_in_ms": 0,
    "audio_out_ms": 0,
    "billed_cents": 0
  },
  "trace_id": "trace_EXAMPLE"
}

Responses

status codes
200Session returned.
401The key is invalid or does not own the session.invalid_vr_sk
404No session exists for this owner and id.session_not_found
410The session has expired and is no longer attachable.session_expired
  • A read may expire and settle an idle session as part of the server lifecycle.
  • Session ownership is checked before any usage or billing fields are returned.
POST

End a voice session

Close the session and settle from accepted server/provider usage.

/v1/voice/sessions/{session_id}/endBearer VR_SK used to create the session

Path parameters

FieldTypeDescription
session_idrequiredstringThe id returned by POST /v1/voice/sessions.

Request body

application/json

The body is optional. Only a bounded close reason is accepted.

FieldTypeDescription
reasonstring
client_closed | timeout | error
Optional bounded reason for closing the session.
json
{
  "reason": "client_closed"
}

Response

200 OK

The terminal session resource. Repeating the request does not duplicate settlement.

FieldTypeDescription
idrequiredstringOpaque VoiceOpenRouter session identifier.
statusrequiredstring
ready | active | completed | cancelled | failed | expired
Current server-owned lifecycle status.
modelrequiredstringThe catalog model id selected at session creation.
moderequiredstring
speech_to_speech
The public session mode for this contract.
realtimerequiredobjectGateway-owned WebSocket URL and protocol name.
audiorequiredobjectNegotiated public input and output audio formats.
capabilitiesrequiredobjectCapabilities available for the selected catalog model.
billingrequiredobjectPreauthorization and remaining wallet information.
usagerequiredobjectServer-measured usage and the rate snapshot used for settlement.
trace_idrequiredstringRedacted support lookup identifier; never a provider credential.
json
{
  "id": "vs_EXAMPLE",
  "object": "voice.session",
  "status": "ready",
  "model": "xai/grok-voice-latest",
  "mode": "speech_to_speech",
  "realtime": {
    "url": "wss://api.voiceopenrouter.com/v1/realtime?session_id=vs_EXAMPLE",
    "protocol": "vor-realtime-v1"
  },
  "audio": {
    "input": {
      "type": "audio/pcm",
      "encoding": "pcm16le",
      "sample_rate_hz": 24000,
      "channels": 1
    },
    "output": {
      "type": "audio/pcm",
      "encoding": "pcm16le",
      "sample_rate_hz": 24000,
      "channels": 1
    }
  },
  "capabilities": {
    "speech_to_speech": true,
    "server_vad": true,
    "barge_in": true,
    "input_transcription": "optional",
    "output_transcription": "optional"
  },
  "created_at": 1770000000,
  "expires_at": 1770000900,
  "billing": {
    "preauthorized_cents": 50,
    "balance_cents": 950,
    "reserved_cents": 50
  },
  "usage": {
    "model": "xai/grok-voice-latest",
    "provider": "xai",
    "pricing_source": "upstream_derived",
    "rate_snapshot_id": "rate_EXAMPLE",
    "audio_in_ms": 0,
    "audio_out_ms": 0,
    "billed_cents": 0
  },
  "trace_id": "trace_EXAMPLE"
}

Responses

status codes
200Session closed; repeated calls are idempotent.
400The close body is malformed or contains an unsupported field.invalid_request
401The key is invalid or does not own the session.invalid_vr_sk
404No session exists for this owner and id.session_not_found
409Settlement could not complete consistently.session_termination_failed
503The session store is unavailable.session_store_unavailable
  • Client-reported usage_cents, elapsed time, and token counts are not accepted by this endpoint.
  • The terminal decision comes from the server/worker response lifecycle; a short silence is not by itself completion.

Lifecycle notes

The gateway owns terminal state and billing decisions.

  • New sessions start in ready; they may transition to active after realtime input begins.
  • Terminal statuses are completed, cancelled, failed, and expired.
  • Repeated end requests return the existing terminal resource and do not duplicate wallet settlement.
  • Use the returned realtime URL; do not construct a provider connection directly.