Setting the file. One moment.
Chapter 04 · Cloudflare Deploy
Subchapter 4.235
references/turn/api.mdMarkdown5 KBView on GitHub
Complete API documentation for Cloudflare TURN service credentials and key management.
All endpoints require Cloudflare API token with “Calls Write” permission.
Base URL: https://api.cloudflare.com/client/v4
GET /accounts/{account_id}/calls/turn_keysGET /accounts/{account_id}/calls/turn_keys/{key_id}POST /accounts/{account_id}/calls/turn_keys
Content-Type: application/json
{
"name": "my-turn-key"
}Response includes:
uid: Key identifierkey: The actual secret key (only returned on creation—save immediately)name: Human-readable namecreated: ISO 8601 timestampmodified: ISO 8601 timestampPUT /accounts/{account_id}/calls/turn_keys/{key_id}
Content-Type: application/json
{
"name": "updated-name"
}DELETE /accounts/{account_id}/calls/turn_keys/{key_id}POST https://rtc.live.cloudflare.com/v1/turn/keys/{key_id}/credentials/generate
Authorization: Bearer {key_secret}
Content-Type: application/json
{
"ttl": 86400
}| Parameter | Min | Max | Default | Notes |
|---|---|---|---|---|
| ttl | 1 | 172800 (48hrs) | varies | API rejects values >172800 |
CRITICAL: Maximum TTL is 48 hours (172800 seconds). API will reject requests exceeding this limit.
{
"iceServers": {
"urls": [
"stun:stun.cloudflare.com:3478",
"turn:turn.cloudflare.com:3478?transport=udp",
"turn:turn.cloudflare.com:3478?transport=tcp",
"turn:turn.cloudflare.com:53?transport=udp",
"turn:turn.cloudflare.com:80?transport=tcp",
"turns:turn.cloudflare.com:5349?transport=tcp",
"turns:turn.cloudflare.com:443?transport=tcp"
],
"username": "1738035200:user123",
"credential": "base64encodedhmac=="
}
}Port 53 Warning: Filter port 53 URLs for browser clients—blocked by Chrome/Firefox. See gotchas.md.
POST https://rtc.live.cloudflare.com/v1/turn/keys/{key_id}/credentials/revoke
Authorization: Bearer {key_secret}
Content-Type: application/json
{
"username": "1738035200:user123"
}Response: 204 No Content
Billing stops immediately. Active connection drops after short delay (~seconds).
interface CloudflareTURNConfig {
keyId: string;
keySecret: string;
ttl?: number; // Max 172800 (48 hours)
}
interface TURNCredentialsRequest {
ttl?: number; // Max 172800 seconds
}
interface TURNCredentialsResponse {
iceServers: {
urls: string[];
username: string;
credential: string;
};
}
interface RTCIceServer {
urls: string | string[];
username?: string;
credential?: string;
credentialType?: "password";
}
interface TURNKeyResponse {
uid: string;
key: string; // Only present on creation
name: string;
created: string;
modified: string;
}function validateRTCIceServer(obj: unknown): obj is RTCIceServer {
if (!obj || typeof obj !== 'object') {
return false;
}
const server = obj as Record<string, unknown>;
if (typeof server.urls !== 'string' && !Array.isArray(server.urls)) {
return false;
}
if (server.username && typeof server.username !== 'string') {
return false;
}
if (server.credential && typeof server.credential !== 'string') {
return false;
}
return true;
}async function fetchTURNServers(
config: CloudflareTURNConfig
): Promise<RTCIceServer[]> {
// Validate TTL constraint
const ttl = config.ttl ?? 3600;
if (ttl > 172800) {
throw new Error('TTL cannot exceed 172800 seconds (48 hours)');
}
const response = await fetch(
`https://rtc.live.cloudflare.com/v1/turn/keys/${config.keyId}/credentials/generate`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${config.keySecret}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ ttl })
}
);
if (!response.ok) {
throw new Error(`TURN credential generation failed: ${response.status}`);
}
const data = await response.json();
// Filter port 53 for browser clients
const filteredUrls = data.iceServers.urls.filter(
(url: string) => !url.includes(':53')
);
const iceServers = [
{ urls: 'stun:stun.cloudflare.com:3478' },
{
urls: filteredUrls,
username: data.iceServers.username,
credential: data.iceServers.credential,
credentialType: 'password' as const
}
];
// Validate before returning
if (!iceServers.every(validateRTCIceServer)) {
throw new Error('Invalid ICE server configuration received');
}
return iceServers;
}