Describes the token authentication mechanism for Realtime API, including how to obtain an API key and how to authenticate when connecting via WebSocket, WebRTC, and AOQ.
Overview
Realtime API uses API keys for authentication. Regardless of the protocol you use -- WebSocket, WebRTC, or AOQ -- authentication is performed by passing a Bearer token in the Authorization HTTP header.
Authentication occurs at the connection phase . Once the connection is established, subsequent audio/video and data transmission requires no re-authentication.
Authentication differences across protocols:
Protocol When Method Notes WebSocket During WebSocket handshake HTTP Header Authorization: Bearer $DASHSCOPE_API_KEY Client or server carries the API key directly when connecting WebRTC During SDP exchange HTTP request HTTP Header Authorization: Bearer $DASHSCOPE_API_KEY Client or server carries the API key when initiating the SDP exchange AOQ When AppServer requests the gateway HTTP Header Authorization: Bearer $DASHSCOPE_API_KEY API key is used only on the server side; the client uses the token returned by the gateway
Get an API key
Step 1: Activate the service
Go to the QwenCloud console and log in with your account.
If this is your first time using the service, follow the on-screen instructions to activate it.
Step 2: Create an API key
In the left navigation pane of the console, select API Key Management .
Click Create API Key and select the workspace to associate.
After the API key is created, copy and save it immediately .
Security note : Your API key is the sole credential for accessing the service. Do not hard-code it in client-side code or commit it to a repository. Manage it through environment variables or a backend service.
Connection authentication details
AOQ protocol authentication
AOQ uses a server-side proxy authentication model: the API key is used only on the AppServer side. The client connects using a temporary token returned by the gateway, which prevents the API key from being exposed on the client.
Gateway request curl example
curl -X POST \
"https://{endpoint}/api/v1/webrtc/realtime?model=qwen3.5-omni-plus-realtime" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $DASHSCOPE_API_KEY" \
-H "x-dashscope-rtc-transport: moq" \
-d '{"clientIp": ${client_real_ip}}'
Request fields
Field Value Description endpoint Select based on your deployment Specify the endpoint domain Content-Type application/json-- Authorization Bearer $DASHSCOPE_API_KEYRequired x-dashscope-rtc-transport moqSpecifies AOQ protocol clientIp Optional. Client's real public IP When not provided, the IP of the request to the QwenCloud gateway is used as the client IP. When provided, the clientIp is used as the client IP. The Realtime API uses the client IP to provide optimal Relay access point information
Response example
{
"sid" : "1d06b55683db49bba67a407902f62d02:1782706970:69aecdc5..." ,
"aoqTokenForClient" : "ecc1a46015d5496ca4ff7a48281eb739" ,
"clientRelayEndpoints" : [{ "endpoint" : "121.199.XX.XX" , "port" : 8443 }],
"clientRelayCertFingerprint" : "sha256/99843495..." ,
"sidExpiresInSecs" : 7200 ,
"extraInfo" : { "workspaceIdHash" : "2021b6f98cea4cff" }
}
Response fields
Field Description sid Session ID aoqTokenForClient Client connection token; pass this to the SDK's token field clientRelayEndpoints Array of Relay endpoints (endpoint + port) clientRelayCertFingerprint TLS certificate fingerprint for the Relay sidExpiresInSecs Session expiry time in seconds extraInfo.workspaceIdHash Workspace ID hash
AOQ Client SDK connection example
let resp = try JSONDecoder (). decode (AllocateResponse. self , from : responseData)
let config = AoqConnectConfig ()
config. token = resp. aoqTokenForClient
config. sid = resp. sid
config. certFingerprint = resp. clientRelayCertFingerprint
config. relayEndpoints = resp. clientRelayEndpoints . map { item in
let ep = AoqRelayEndpoint ()
ep. endpoint = item. endpoint
ep. port = item. port
return ep
}
config. workspaceIdHash = resp. extraInfo ? . workspaceIdHash ?? ""
let audioTrack = AoqTrackParam ()
audioTrack. trackType = . audio
let dataTrack = AoqTrackParam ()
dataTrack. trackType = . data
config. publishTracks = [audioTrack, dataTrack]
config. subscribeTracks = [audioTrack, dataTrack]
engine. connect (config)
JSONObject obj = new JSONObject (responseText);
AoqClientEngine . AoqConnectConfig cfg = new AoqClientEngine. AoqConnectConfig ();
cfg . token = obj . optString ( "aoqTokenForClient" , "" );
cfg . sid = obj . optString ( "sid" , "" );
cfg . certFingerprint = obj . optString ( "clientRelayCertFingerprint" , "" );
JSONArray arr = obj . optJSONArray ( "clientRelayEndpoints" );
if (arr != null ) {
for ( int i = 0 ; i < arr . length (); i ++ ) {
JSONObject o = arr . optJSONObject (i);
AoqClientEngine . AoqRelayEndpoint ep = new AoqClientEngine. AoqRelayEndpoint ();
ep . endpoint = o . optString ( "endpoint" , "" );
ep . port = o . optInt ( "port" , 0 );
cfg . relayEndpoints . add (ep);
}
}
JSONObject ext = obj . optJSONObject ( "extraInfo" );
cfg . workspaceIdHash = ext != null ? ext . optString ( "workspaceIdHash" , "" ) : "" ;
AoqClientEngine . AoqTrackParam audio = new AoqClientEngine. AoqTrackParam ();
audio . trackType = AoqClientEngine . AoqTrackType . AoqTrackTypeAudio ;
AoqClientEngine . AoqTrackParam data = new AoqClientEngine. AoqTrackParam ();
data . trackType = AoqClientEngine . AoqTrackType . AoqTrackTypeData ;
cfg . publishTracks . add (audio);
cfg . publishTracks . add (data);
cfg . subscribeTracks . add (audio);
cfg . subscribeTracks . add (data);
engine . connect (cfg);
const obj = JSON . parse ( responseText ) as Record < string , Object | undefined >;
const cfg : AoqConnectConfig = {
token: String ( obj [ 'aoqTokenForClient' ] ?? '' ),
sid: String ( obj [ 'sid' ] ?? '' ),
certFingerprint: String ( obj [ 'clientRelayCertFingerprint' ] ?? '' ),
relayEndpoints: ( obj [ 'clientRelayEndpoints' ] as Array < any >). map ( item => ({
endpoint: String ( item [ 'endpoint' ] ?? '' ),
port: Number ( item [ 'port' ] ?? 0 )
})),
workspaceIdHash: String (( obj [ 'extraInfo' ] as any )?.[ 'workspaceIdHash' ] ?? '' ),
publishTracks: [
{ trackType: AoqTrackType . AoqTrackTypeAudio },
{ trackType: AoqTrackType . AoqTrackTypeData }
],
subscribeTracks: [
{ trackType: AoqTrackType . AoqTrackTypeAudio },
{ trackType: AoqTrackType . AoqTrackTypeData }
]
};
engine . connect ( cfg );
clientIp is an optional field in the request body. When not provided, the IP of the request to the QwenCloud gateway is used as the client IP. When provided, the clientIp is used as the client IP. It is recommended that the AppServer obtains the client's real IP on the server side and fills it in to get optimal Relay access points.
WebRTC protocol authentication
WebRTC authentication is completed through an HTTP POST request during the SDP exchange. The client sends an Offer SDP to the server, and the server returns an Answer SDP.
Field Value Description Method POST -- URL https://{endpoint}/api/v1/webrtc/realtime?model={model_name}Replace endpoint and model_name Content-Type application/sdpRequest body is an SDP string Authorization Bearer $DASHSCOPE_API_KEYRequired Response HTTP 200 with Answer SDP Returns 4xx on failure
WebRTC is currently available on an allowlist basis. Contact your account manager to obtain an endpoint.
const pc = new RTCPeerConnection ();
const stream = await navigator . mediaDevices . getUserMedia ({ audio: true });
stream . getAudioTracks (). forEach ( t => pc . addTrack ( t , stream ));
pc . createDataChannel ( 'oai-events' );
const offer = await pc . createOffer ();
await pc . setLocalDescription ( offer );
// Send after ICE gathering is complete
const resp = await fetch ( API_URL , {
method: 'POST' ,
headers: {
'Content-Type' : 'application/sdp' ,
'Authorization' : `Bearer ${ API_KEY } ` ,
},
body: pc . localDescription . sdp ,
});
const answerSdp = await resp . text ();
await pc . setRemoteDescription ({ type: 'answer' , sdp: answerSdp });
WebSocket protocol authentication
WebSocket authentication is straightforward: the client passes the API key in an HTTP header when establishing the WebSocket connection.
Field Value Description URL wss://dashscope-intl.aliyuncs.com/api-ws/v1/realtime?model={model_name}-- Authorization Bearer $DASHSCOPE_API_KEYRequired
import websocket, os
API_KEY = os.getenv( "DASHSCOPE_API_KEY" )
URL = "wss://dashscope-intl.aliyuncs.com/api-ws/v1/realtime?model=qwen3.5-omni-plus-realtime"
ws = websocket.WebSocketApp( URL , header = [ "Authorization: Bearer " + API_KEY ])
ws.run_forever()
You can also connect using the DashScope SDK.