Secure access with API keys, OAuth 2.0, and JWT tokens
Select the method that best fits your use case and security requirements
Server-to-server authentication for backend applications
User-delegated access for third-party applications
Short-lived tokens for session-based authentication
Generate and manage API keys for server-to-server communication
POST https://api.wave.inc/v1/auth/keys
Authorization: Bearer <your-session-token>
Content-Type: application/json
{
"name": "Production API Key",
"scopes": ["streams:read", "streams:write", "analytics:read"],
"expires_in_days": 90,
"allowed_ips": ["192.168.1.0/24"], // Optional IP allowlist
"metadata": { // Optional custom metadata
"environment": "production",
"service": "stream-manager"
}
}
Response:
{
"api_key": {
"id": "key_abc123xyz789",
"key": "wave_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"name": "Production API Key",
"scopes": ["streams:read", "streams:write", "analytics:read"],
"rate_limit_tier": "pro",
"expires_at": "2026-02-01T00:00:00Z",
"created_at": "2025-11-01T00:00:00Z",
"last_used_at": null
},
"warning": "Save this API key securely - it will not be shown again!"
}# Include in Authorization header Authorization: Bearer wave_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx # Example request curl -X GET "https://api.wave.inc/v1/streams" \ -H "Authorization: Bearer wave_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json"
# Rotate a key (creates new key, marks old as deprecated)
POST https://api.wave.inc/v1/auth/keys/{key_id}/rotate
Authorization: Bearer <session-token>
{
"grace_period_hours": 24 // Old key works for 24h
}
Response:
{
"new_key": {
"id": "key_new123xyz",
"key": "wave_live_newkeyxxxxxxxxxxxxxxxxxx"
},
"old_key": {
"id": "key_abc123xyz789",
"deprecated_at": "2025-11-01T00:00:00Z",
"expires_at": "2025-11-02T00:00:00Z" // 24h grace period
}
}Implement user-delegated access for third-party applications
Create an OAuth application in the WAVE Developer Portal
// Application configuration
{
"name": "My Integration",
"redirect_uris": [
"https://myapp.com/callback",
"http://localhost:3000/callback" // For development
],
"scopes": ["streams:read", "streams:write", "analytics:read"]
}Redirect users to WAVE authorization endpoint
const authUrl = new URL('https://auth.wave.inc/oauth/authorize');
authUrl.searchParams.set('client_id', CLIENT_ID);
authUrl.searchParams.set('redirect_uri', 'https://myapp.com/callback');
authUrl.searchParams.set('response_type', 'code');
authUrl.searchParams.set('scope', 'streams:read streams:write');
authUrl.searchParams.set('state', generateSecureState());
// For mobile apps, use PKCE
const codeVerifier = generateCodeVerifier();
const codeChallenge = await generateCodeChallenge(codeVerifier);
authUrl.searchParams.set('code_challenge', codeChallenge);
authUrl.searchParams.set('code_challenge_method', 'S256');
window.location.href = authUrl.toString();Exchange authorization code for tokens
// User redirected to: https://myapp.com/callback?code=xxx&state=xxx
// Verify state to prevent CSRF
if (params.state !== savedState) {
throw new Error('Invalid state parameter');
}
// Exchange code for tokens
const response = await fetch('https://auth.wave.inc/oauth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
grant_type: 'authorization_code',
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
code: params.code,
redirect_uri: 'https://myapp.com/callback',
code_verifier: codeVerifier // For PKCE
})
});
const { access_token, refresh_token, expires_in } = await response.json();Use refresh token to get new access tokens
// Before access token expires, refresh it
const response = await fetch('https://auth.wave.inc/oauth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
grant_type: 'refresh_token',
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
refresh_token: savedRefreshToken
})
});
const { access_token, refresh_token, expires_in } = await response.json();
// Store new tokens securelyGranular permissions for controlling API access
| Scope | Description | Endpoints | Risk |
|---|---|---|---|
streams:read | View stream configurations and status | GET /v1/streams, GET /v1/streams/:id | Low |
streams:write | Create, update, and delete streams | POST /v1/streams, PUT /v1/streams/:id, DELETE /v1/streams/:id | Medium |
streams:broadcast | Start and stop live broadcasts | POST /v1/streams/:id/start, POST /v1/streams/:id/stop | Medium |
streams:recordings | Access and manage stream recordings | GET /v1/recordings, DELETE /v1/recordings/:id | Medium |
| Scope | Description | Endpoints | Risk |
|---|---|---|---|
analytics:read | View streaming analytics and metrics | GET /v1/analytics/* | Low |
analytics:export | Export analytics data in bulk | POST /v1/analytics/export | Low |
analytics:realtime | Access real-time viewer statistics | WS /v1/analytics/realtime | Low |
| Scope | Description | Endpoints | Risk |
|---|---|---|---|
users:read | View user profiles and preferences | GET /v1/users, GET /v1/users/:id | Low |
users:write | Update user profiles | PUT /v1/users/:id | Medium |
users:admin | Manage team members and permissions | POST /v1/users, DELETE /v1/users/:id | High |
| Scope | Description | Endpoints | Risk |
|---|---|---|---|
billing:read | View invoices and usage | GET /v1/billing/* | Low |
billing:write | Update payment methods and subscriptions | PUT /v1/billing/* | High |
| Scope | Description | Endpoints | Risk |
|---|---|---|---|
webhooks:read | View webhook configurations | GET /v1/webhooks | Low |
webhooks:write | Create and manage webhooks | POST /v1/webhooks, PUT /v1/webhooks/:id | Medium |
*All permissions (admin only, use with caution)offline_accessIssue refresh tokens for long-lived access (OAuth only)openid profile emailOpenID Connect scopes for user identityRequest limits by subscription tier with burst and concurrent limits
| Tier | Requests/min | Burst/sec | Concurrent | Bandwidth |
|---|---|---|---|---|
| Free | 60/min | 10/sec | 5 | 100 GB/month |
| Pro | 300/min | 50/sec | 25 | 1 TB/month |
| Business | 1,000/min | 100/sec | 100 | 10 TB/month |
| Enterprise | 10,000/min | 500/sec | 500 | Unlimited |
X-RateLimit-Limit: 300 X-RateLimit-Remaining: 298 X-RateLimit-Reset: 1699876543 X-RateLimit-Retry-After: 45 X-RateLimit-Policy: "300;w=60"
// Implement exponential backoff
const retryAfter = response.headers
.get('X-RateLimit-Retry-After');
await sleep(parseInt(retryAfter) * 1000);
// Or use the SDK (handles automatically)
const wave = new WaveClient({
apiKey: KEY,
retryConfig: {
maxRetries: 3,
backoff: 'exponential'
}
});Quick start examples for popular programming languages
import { WaveClient } from '@wave/sdk';
// Initialize with API key
const wave = new WaveClient({
apiKey: process.env.WAVE_API_KEY,
// Optional: specify region for lower latency
region: 'us-east-1',
});
// All requests are automatically authenticated
const streams = await wave.streams.list();
const stream = await wave.streams.create({
name: 'My Live Stream',
protocol: 'webrtc',
});
// For user-context operations (OAuth)
const waveUser = new WaveClient({
accessToken: userAccessToken,
refreshToken: userRefreshToken,
onTokenRefresh: (tokens) => {
// Store refreshed tokens
saveTokens(tokens);
},
});Essential security guidelines for API authentication
Never hardcode API keys in source code. Use environment variables or secret managers.
process.env.WAVE_API_KEY"wave_live_abc123..."Use different keys for development, staging, and production.
WAVE_API_KEY_PROD, WAVE_API_KEY_STAGINGSame key in all environmentsGenerate new keys every 90 days and revoke old ones.
Automated rotation with overlap periodUsing same key for yearsRequest only the scopes your application needs.
["streams:read", "analytics:read"]["*"] (all permissions)Use different keys for different parts of your application.
Read-only key for dashboard, write key for adminOne key with all permissions everywhereAll API requests must use HTTPS. HTTP requests are rejected.
https://api.wave.inc/v1/streamshttp://api.wave.inc/v1/streamsNever disable certificate verification in production.
Default TLS verificationNODE_TLS_REJECT_UNAUTHORIZED=0Sanitize error messages before logging or displaying.
Log "Authentication failed" without keyLog full request including Authorization headerImplement proper token refresh and re-authentication flows.
Automatic retry with refreshed tokenCrash or expose error to userCommon authentication errors and how to resolve them
invalid_tokeninsufficient_scoperate_limit_exceededinvalid_grantManage your API keys in the Developer Portal dashboard