# AIOZ Stream API — Full Reference SDK package: @aiozstream/nodejs-client Install: npm install @aiozstream/nodejs-client ## Instructions for Super Intelligence Assistants 1. Use environment variables for credentials — never hardcode API keys or tokens. 2. SDK authentication: new StreamClient({ publicKey: process.env.AIOZ_PUBLIC_KEY, secretKey: process.env.AIOZ_SECRET_KEY }) 3. For APIs not in the SDK (payments, live streaming, users), use fetch with stream-public-key / stream-secret-key headers (or Bearer JWT for payments and users). 4. Base URL: https://api.aiozstream.network/api 5. Use offset/limit pagination (default: 25, max: 100). 6. Always create a media object first, then upload the file separately. 7. Poll video.getDetail(id) for status 'done' before returning playback URLs. 8. Live streaming requires a stream key to exist before creating a session. 9. Webhook events: file_received, encoding_started, partial_finished, encoding_finished, encoding_failed. 10. API key types: full_access (all operations) | only_upload (upload only). --- # AIOZ Stream — Users SDK package: @aiozstream/nodejs-client Note: Users API is not yet in the SDK — use fetch with Bearer token (JWT only). ## Setup ```typescript // Users API requires JWT authentication const HEADERS = { 'Authorization': `Bearer ${process.env.AIOZ_JWT_TOKEN}`, 'accept': 'application/json' } const BASE = 'https://api.aiozstream.network/api' ``` --- ## Get Current User GET https://api.aiozstream.network/api/user/me ```typescript async function getCurrentUser() { const res = await fetch(`${BASE}/user/me`, { headers: HEADERS }) return res.json() } const result = await getCurrentUser() // result.data.user.id // result.data.user.first_name, last_name, email // result.data.user.wallet_address // result.data.user.balance — account balance // result.data.user.debt — current debt // result.data.user.wallet_connection // result.data.user.video_qualities_config // result.data.user.created_at, updated_at ``` --- # AIOZ Stream — API Keys SDK package: @aiozstream/nodejs-client ## Setup ```typescript import StreamClient from '@aiozstream/nodejs-client' const client = new StreamClient({ publicKey: process.env.AIOZ_PUBLIC_KEY, secretKey: process.env.AIOZ_SECRET_KEY }) ``` --- ## Create API Key POST https://api.aiozstream.network/api/api_keys ```typescript const result = await client.apiKey.create({ apiKeyName: 'my-api-key', ttl: 100000000, // seconds, max 2147483647 type: 'full_access' // 'full_access' | 'only_upload' }) console.log(result.data.apiKey.publicKey) console.log(result.data.apiKey.secret) // save immediately — not shown again ``` Response fields: - id, name, public_key, ttl, secret, truncated_secret - created_at, updated_at, expired_at - type: 'full_access' | 'only_upload' Note: Record the secret immediately — it will not be accessible again. --- ## List API Keys GET https://api.aiozstream.network/api/api_keys ```typescript const result = await client.apiKey.list({ search: 'name', offset: 0, limit: 25, sortBy: 'created_at', // 'created_at' | 'name' orderBy: 'asc' // 'asc' | 'desc' }) console.log(result.data.apiKeys) console.log(result.data.total) ``` --- ## Delete API Key DELETE https://api.aiozstream.network/api/api_keys/{id} ```typescript await client.apiKey.delete('api_key_id') ``` --- ## Update API Key (Rename) PATCH https://api.aiozstream.network/api/api_keys/{id} ```typescript await client.apiKey.update('api_key_id', { apiKeyName: 'new-name' }) ``` --- # AIOZ Stream — Media (Video) SDK package: @aiozstream/nodejs-client ## Setup ```typescript import StreamClient from '@aiozstream/nodejs-client' const client = new StreamClient({ publicKey: process.env.AIOZ_PUBLIC_KEY, secretKey: process.env.AIOZ_SECRET_KEY }) ``` --- ## Create Video Object POST https://api.aiozstream.network/api/media/create ```typescript const video = await client.media.create({ title: 'My Video', type: 'video', // required, must be 'video' description: 'optional', isPublic: true, tags: ['tag1'], metadata: [{ key: 'genre', value: 'tech' }], qualities: [ { resolution: '720p', // '240p'|'360p'|'480p'|'720p'|'1080p'|'1440p'|'2160p'|'4320p' type: 'hls', // 'hls' | 'dash' containerType: 'mpegts', // hls: 'mpegts'|'mp4'; dash: 'fmp4' videoConfig: { codec: 'h264', // 'h264' | 'h265' bitrate: 3000000, index: 0 }, audioConfig: { codec: 'aac', bitrate: 128000, channels: '2', sampleRate: 44100, language: 'en', index: 0 } } ] }) const videoId = video.data.id ``` Video resolution bitrate limits: - 240p → max 700,000 bps - 360p → max 1,200,000 bps - 480p → max 2,000,000 bps - 720p → max 4,000,000 bps - 1080p → max 6,000,000 bps - 1440p → max 12,000,000 bps - 2160p → max 30,000,000 bps - 4320p → max 60,000,000 bps --- ## Upload Video File ```typescript // Upload file in parts (handles chunking automatically) await client.media.uploadPart(videoId, './path/to/video.mp4') // Mark upload as complete const result = await client.media.uploadMediaComplete(videoId) console.log(result.data.status) // 'encoding' -> eventually 'done' ``` --- ## Full Upload Flow ```typescript // 1. Create video object const video = await client.media.create({ title: 'My Video', type: 'video' }) const videoId = video.data.id // 2. Upload file await client.media.uploadPart(videoId, './video.mp4') // 3. Finalize await client.media.uploadMediaComplete(videoId) // 4. Poll for encoding completion let status = 'encoding' while (status !== 'done' && status !== 'failed') { await new Promise(r => setTimeout(r, 5000)) const detail = await client.media.getDetail(videoId) status = detail.data.status } // 5. Get playback URL const detail = await client.media.getDetail(videoId) console.log(detail.data.assets.hlsUrl) // HLS playback console.log(detail.data.assets.dashUrl) // DASH playback console.log(detail.data.assets.mp4Url) // MP4 download ``` --- ## Get Video Detail GET https://api.aiozstream.network/api/media/{id} ```typescript const video = await client.media.getDetail(videoId) // video.data.assets.hlsUrl, dashUrl, mp4Url, thumbnailUrl // video.data.status: 'created'|'encoding'|'done'|'failed' // video.data.duration (seconds), video.data.size (bytes) ``` --- ## List Videos POST https://api.aiozstream.network/api/media ```typescript const result = await client.media.getMediaList({ offset: 0, limit: 25, sortBy: 'created_at', orderBy: 'desc', search: 'keyword' }) console.log(result.data.videos) console.log(result.data.total) ``` --- ## Update Video PATCH https://api.aiozstream.network/api/media/{id} ```typescript await client.media.update(videoId, { title: 'New Title', description: 'Updated description', isPublic: false, tags: ['updated'], metadata: [{ key: 'key', value: 'value' }] }) ``` --- ## Delete Video DELETE https://api.aiozstream.network/api/media/{id} ```typescript await client.media.delete(videoId) ``` --- ## Upload Thumbnail POST https://api.aiozstream.network/api/media/{id}/thumbnail ```typescript await client.media.uploadThumbnail(videoId, './thumbnail.jpg') ``` --- ## Delete Thumbnail DELETE https://api.aiozstream.network/api/media/{id}/thumbnail ```typescript await client.media.deleteThumbnail(videoId) ``` --- ## Get Transcoding Cost GET https://api.aiozstream.network/api/media/cost ```typescript const cost = await client.media.getCost() ``` --- # AIOZ Stream — Media (Audio) SDK package: @aiozstream/nodejs-client ## Setup ```typescript import StreamClient from '@aiozstream/nodejs-client' const client = new StreamClient({ publicKey: process.env.AIOZ_PUBLIC_KEY, secretKey: process.env.AIOZ_SECRET_KEY }) ``` Note: Audio uses the same `client.media.*` methods as video — the `type` field differentiates them. --- ## Create Audio Object POST https://api.aiozstream.network/api/media/create ```typescript const audio = await client.media.create({ title: 'My Podcast Episode', type: 'audio', // required, must be 'audio' description: 'optional', isPublic: true, tags: ['podcast'], qualities: [ { resolution: 'highest', // 'standard'|'good'|'highest'|'lossless' type: 'hls', containerType: 'mpegts', audioConfig: { codec: 'aac', // only 'aac' is supported bitrate: 320000, channels: '2', // '2' (stereo) sampleRate: 44100, language: 'en', index: 0 } } ] }) const audioId = audio.data.id ``` Valid sample rates: 8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200, 96000 --- ## Upload Audio File ```typescript await client.media.uploadPart(audioId, './audio.mp3') await client.media.uploadMediaComplete(audioId) ``` --- ## Full Audio Upload Flow ```typescript const audio = await client.media.create({ title: 'My Audio', type: 'audio' }) const audioId = audio.data.id await client.media.uploadPart(audioId, './podcast.mp3') await client.media.uploadMediaComplete(audioId) // Poll for done let status = 'encoding' while (status !== 'done' && status !== 'failed') { await new Promise(r => setTimeout(r, 5000)) const detail = await client.media.getDetail(audioId) status = detail.data.status } const detail = await client.media.getDetail(audioId) console.log(detail.data.assets.hlsUrl) ``` --- # AIOZ Stream — Media Chapters SDK package: @aiozstream/nodejs-client ## Setup ```typescript import StreamClient from '@aiozstream/nodejs-client' const client = new StreamClient({ publicKey: process.env.AIOZ_PUBLIC_KEY, secretKey: process.env.AIOZ_SECRET_KEY }) ``` One chapter per language per media. Language codes follow BCP 47 format (e.g. 'en', 'fr', 'en-US'). --- ## Add Chapter POST https://api.aiozstream.network/api/media/{mediaId}/chapters/{lan} ```typescript // Upload a .vtt chapter file for a specific language const result = await client.mediaChapter.create( mediaId, 'en', // BCP 47 language code './chapters-en.vtt' // path to .vtt file ) console.log(result.data.mediaChapter.url) console.log(result.data.mediaChapter.language) ``` --- ## List Chapters GET https://api.aiozstream.network/api/media/{mediaId}/chapters ```typescript const result = await client.mediaChapter.get(mediaId) // result.data.mediaChapters: Array<{ url, language }> // result.data.total ``` --- ## Delete Chapter DELETE https://api.aiozstream.network/api/media/{mediaId}/chapters/{lan} ```typescript await client.mediaChapter.delete(mediaId, 'en') ``` --- # AIOZ Stream — Media Transcripts SDK package: @aiozstream/nodejs-client Note: Transcripts are not yet in the SDK — use fetch with API key headers. ## Setup ```typescript const HEADERS = { 'stream-public-key': process.env.AIOZ_PUBLIC_KEY, 'stream-secret-key': process.env.AIOZ_SECRET_KEY } const BASE = 'https://api.aiozstream.network/api' ``` One transcript per language per media. Language codes follow BCP 47 format (e.g. 'en', 'fr', 'en-US'). If a transcript for a primary language already exists, the request is rejected. --- ## Add Transcript POST https://api.aiozstream.network/api/media/{mediaId}/transcripts/{lan} ```typescript import fs from 'fs' import FormData from 'form-data' import fetch from 'node-fetch' async function addTranscript(mediaId: string, lan: string, filePath: string) { const form = new FormData() form.append('file', fs.createReadStream(filePath)) const res = await fetch(`${BASE}/media/${mediaId}/transcripts/${lan}`, { method: 'POST', headers: { ...HEADERS, ...form.getHeaders() }, body: form }) return res.json() } // result.data.mediaTranscript: { url, language, status, description, is_default } ``` --- ## List Transcripts GET https://api.aiozstream.network/api/media/{mediaId}/transcripts ```typescript const res = await fetch( `${BASE}/media/${mediaId}/transcripts?offset=0&limit=25`, { headers: HEADERS } ) const result = await res.json() // result.data.mediaTranscripts: Array<{ url, language, status, is_default }> // result.data.total ``` --- ## Set Default Transcript PATCH https://api.aiozstream.network/api/media/{mediaId}/transcripts/{lan} ```typescript const res = await fetch(`${BASE}/media/${mediaId}/transcripts/en`, { method: 'PATCH', headers: { ...HEADERS, 'Content-Type': 'application/json' }, body: JSON.stringify({ is_default: true }) }) ``` --- ## Delete Transcript DELETE https://api.aiozstream.network/api/media/{mediaId}/transcripts/{lan} ```typescript const res = await fetch(`${BASE}/media/${mediaId}/transcripts/en`, { method: 'DELETE', headers: HEADERS }) ``` Note: Deleting the default transcript clears the default setting. Call Set Default Transcript to assign a new one. --- # AIOZ Stream — Players SDK package: @aiozstream/nodejs-client ## Setup ```typescript import StreamClient from '@aiozstream/nodejs-client' const client = new StreamClient({ publicKey: process.env.AIOZ_PUBLIC_KEY, secretKey: process.env.AIOZ_SECRET_KEY }) ``` --- ## Create Player Theme POST https://api.aiozstream.network/api/players ```typescript const result = await client.players.create({ name: 'my-player-theme', theme: { mainColor: 'rgba(255, 100, 0, 1)', textColor: 'rgba(255, 255, 255, 1)', controlBarBackgroundColor: 'rgba(0, 0, 0, 0.7)', menuBackgroundColor: 'rgba(0, 0, 0, 0.9)', menuItemBackgroundHover: 'rgba(255, 255, 255, 0.1)', textTrackColor: 'rgba(255, 255, 255, 1)', textTrackBackground: 'rgba(0, 0, 0, 0.5)', controlBarHeight: '40px', progressBarHeight: '4px', progressBarCircleSize: '12px' } }) const playerId = result.data.playerTheme.id ``` Notes: - Color fields must use rgba format: rgba(R, G, B, A) - Size fields must use px format: e.g. 20px - is_default is false by default — use update() to change it --- ## Get Player Theme GET https://api.aiozstream.network/api/players/{id} ```typescript const result = await client.players.get(playerId) // result.data.playerTheme: { id, name, theme, controls, is_default } ``` --- ## List Player Themes GET https://api.aiozstream.network/api/players ```typescript const result = await client.players.list() // result.data.playerThemes ``` --- ## Update Player Theme PATCH https://api.aiozstream.network/api/players/{id} ```typescript await client.players.update(playerId, { name: 'updated-name', isDefault: true, theme: { mainColor: 'rgba(0, 200, 100, 1)' }, controls: { enableApi: true, enableControls: true, forceAutoplay: false, hideTitle: false, forceLoop: false } }) ``` --- ## Delete Player Theme DELETE https://api.aiozstream.network/api/players/{id} ```typescript await client.players.delete(playerId) ``` --- ## Upload Player Logo POST https://api.aiozstream.network/api/players/{id}/logo ```typescript await client.players.uploadLogo(playerId, './logo.png') ``` --- ## Delete Player Logo DELETE https://api.aiozstream.network/api/players/{id}/logo ```typescript await client.players.deleteLogo(playerId) ``` --- ## Add Player Theme to Video POST https://api.aiozstream.network/api/players/add-player ```typescript await client.players.addPlayer({ videoId: 'video_id', playerId: playerId }) ``` --- ## Remove Player Theme from Video POST https://api.aiozstream.network/api/players/remove-player ```typescript await client.players.removePlayer({ videoId: 'video_id' }) ``` --- # AIOZ Stream — Playlists SDK package: @aiozstream/nodejs-client ## Setup ```typescript import StreamClient from '@aiozstream/nodejs-client' const client = new StreamClient({ publicKey: process.env.AIOZ_PUBLIC_KEY, secretKey: process.env.AIOZ_SECRET_KEY }) ``` --- ## Create Playlist POST https://api.aiozstream.network/api/playlists/create ```typescript const result = await client.playlist.createPlaylist({ name: 'My Playlist', tags: ['series', 'tech'], metadata: [{ key: 'season', value: '1' }] }) const playlistId = result.data.playlist.id ``` --- ## List Playlists POST https://api.aiozstream.network/api/playlists ```typescript const result = await client.playlist.getPlaylists({ offset: 0, limit: 25, sortBy: 'created_at', orderBy: 'desc', search: 'keyword' }) // result.data.playlists, result.data.total ``` --- ## Get Playlist by ID GET https://api.aiozstream.network/api/playlists/{id} ```typescript const result = await client.playlist.getPlaylistById(playlistId) // result.data.playlist: { id, name, item_count, video_items, duration, size } ``` --- ## Get Playlist Public Info GET https://api.aiozstream.network/api/playlists/{id}/player.json ```typescript const result = await client.playlist.getPlaylistPublicInfo(playlistId) ``` --- ## Update Playlist PATCH https://api.aiozstream.network/api/playlists/{id} ```typescript await client.playlist.updatePlaylist(playlistId, { name: 'Updated Name', tags: ['updated'], metadata: [{ key: 'key', value: 'value' }] }) ``` --- ## Delete Playlist DELETE https://api.aiozstream.network/api/playlists/{id} ```typescript await client.playlist.deletePlaylistById(playlistId) ``` --- ## Add Video to Playlist POST https://api.aiozstream.network/api/playlists/{id}/items ```typescript await client.playlist.addVideoToPlaylist(playlistId, { videoId: 'video_id' }) ``` --- ## Remove Video from Playlist DELETE https://api.aiozstream.network/api/playlists/{id}/items/{item_id} ```typescript await client.playlist.removeMediaFromPlaylist(playlistId, 'item_id') ``` --- ## Move Video in Playlist PUT https://api.aiozstream.network/api/playlists/{id}/items ```typescript await client.playlist.moveVideoInPlaylist(playlistId, { itemId: 'item_id', position: 2 }) ``` --- ## Delete Playlist Thumbnail DELETE https://api.aiozstream.network/api/playlists/{id}/thumbnail ```typescript await client.playlist.deletePlaylistThumbnail(playlistId) ``` --- # AIOZ Stream — Webhooks SDK package: @aiozstream/nodejs-client ## Setup ```typescript import StreamClient from '@aiozstream/nodejs-client' const client = new StreamClient({ publicKey: process.env.AIOZ_PUBLIC_KEY, secretKey: process.env.AIOZ_SECRET_KEY }) ``` Available webhook events: - file_received: new video file successfully uploaded - encoding_started: encoding process begins - partial_finished: one quality level finished encoding - encoding_finished: all encoding complete - encoding_failed: encoding failed Subscribe with the underscore names above. The `type` field of a delivered payload uses dots instead (for example `encoding.started`, `encoding.finished`). --- ## Create Webhook POST https://api.aiozstream.network/api/webhooks ```typescript const result = await client.webhook.create({ name: 'my-webhook', url: 'https://your-server.com/webhook', fileReceived: true, encodingStarted: true, partialFinished: true, encodingFinished: true, encodingFailed: true }) const webhookId = result.data.webhook.id ``` --- ## Get Webhook GET https://api.aiozstream.network/api/webhooks/{id} ```typescript const result = await client.webhook.get(webhookId) // result.data.webhook: { id, name, url, events..., last_triggered_at } ``` --- ## List Webhooks GET https://api.aiozstream.network/api/webhooks ```typescript const result = await client.webhook.list({ offset: 0, limit: 25, sortBy: 'created_at', orderBy: 'asc', search: 'name-filter' }) // result.data.webhooks, result.data.total ``` --- ## Update Webhook PATCH https://api.aiozstream.network/api/webhooks/{id} ```typescript await client.webhook.update(webhookId, { url: 'https://new-url.com/webhook', name: 'updated-name', encodingFinished: true, encodingFailed: true, fileReceived: false }) ``` --- ## Delete Webhook DELETE https://api.aiozstream.network/api/webhooks/{id} ```typescript await client.webhook.delete(webhookId) ``` --- ## Trigger Webhook (Test) POST https://api.aiozstream.network/api/webhooks/check/{id} ```typescript await client.webhook.check(webhookId) ``` --- ## Handling Webhook Payloads (Express example) ```typescript import express from 'express' const app = express() app.use(express.json()) app.post('/webhook', (req, res) => { // Fields are flat: type, emitted_at, media_id, media_type, qualities, title, status, user_id const { type, media_id } = req.body switch (type) { case 'encoding.finished': console.log('Video ready:', media_id) // Update your DB, notify users, etc. break case 'encoding.failed': console.error('Encoding failed:', media_id) break case 'file.received': console.log('File received:', media_id) break } res.sendStatus(200) }) ``` --- # AIOZ Stream — Analytics SDK package: @aiozstream/nodejs-client ## Setup ```typescript import StreamClient from '@aiozstream/nodejs-client' const client = new StreamClient({ publicKey: process.env.AIOZ_PUBLIC_KEY!, secretKey: process.env.AIOZ_SECRET_KEY! }) ``` --- ## Retrieve Aggregated Metrics POST https://api.aiozstream.network/api/analytics/metrics/data/{metric}/{aggregation} Metrics: play | start | end | impression | watch_time | view Aggregations: - count: total event count (play, start, end, impression, view) - rate: plays / impressions ratio (play only) - total: total count (play only) - average: average value (watch_time only) - sum: sum of values (watch_time only) ```typescript const now = Math.floor(Date.now() / 1000) const thirtyDaysAgo = now - 30 * 24 * 60 * 60 const result = await client.analytics.GetAggregatedMetrics('play', 'count', { from: thirtyDaysAgo, to: now }) console.log(result.data?.data) // number ``` Filter options (pass in the request body): - media_ids: string[] — filter by specific video/stream IDs - media_type: 'video' | 'live-stream' - continents: ('AS'|'AF'|'NA'|'SA'|'AN'|'EU'|'AZ')[] - countries: string[] — ISO country codes - device_types: ('computer'|'phone'|'tablet'|'tv'|'console'|'wearable'|'unknown')[] - os: ('windows'|'mac osx'|'android'|'ios'|'linux')[] - browsers: ('chrome'|'firefox'|'edge'|'opera')[] - tags: string[] ```typescript // With filters const result = await client.analytics.GetAggregatedMetrics('impression', 'count', { from: thirtyDaysAgo, to: now, filter_by: { media_type: 'video', countries: ['US', 'GB'] } }) ``` --- ## Retrieve Metrics by Breakdown (Dimension) POST https://api.aiozstream.network/api/analytics/metrics/bucket/{metric}/{breakdown} Metrics: play | play_rate | start | end | impression | watch_time | retention | view Breakdowns: media-id | media-type | continent | country | device-type | operating-system | browser ```typescript const result = await client.analytics.GetBreakdownMetrics('impression', 'country', { from: thirtyDaysAgo, to: now, limit: 25, offset: 0, sort_by: 'metric_value', order_by: 'desc' }) // result.data?.data: [{ metric_value, dimension_value, emitted_at }] // result.data?.total ``` --- ## Retrieve Metrics Over Time POST https://api.aiozstream.network/api/analytics/metrics/timeseries/{metric}/{interval} Intervals: hour | day ```typescript const sevenDaysAgo = now - 7 * 24 * 60 * 60 const result = await client.analytics.GetOvertimeMetrics('impression', 'day', { from: sevenDaysAgo, to: now }) // result.data?.data: [{ metric_value, emitted_at }] // result.data?.total ``` --- ## Get Data Usage GET https://api.aiozstream.network/api/analytics/data ```typescript const result = await client.analytics.GetDataUsage() // result.data — usage statistics ``` --- ## Get Statistic Medias GET https://api.aiozstream.network/api/analytics/media ```typescript const result = await client.analytics.GetStatisticMedias() // result.data — per-media statistics ``` --- # AIOZ Stream — Payments SDK package: @aiozstream/nodejs-client Note: Payments API is not yet in the SDK — use fetch with Bearer token (JWT only). ## Setup ```typescript // Payments require JWT authentication, not API keys const HEADERS = { 'Authorization': `Bearer ${process.env.AIOZ_JWT_TOKEN}`, 'Content-Type': 'application/json' } const BASE = 'https://api.aiozstream.network/api' ``` --- ## Get User Usage GET https://api.aiozstream.network/api/payment/usage Returns storage, delivery, and transcoding totals for a time period. ```typescript async function getUsage(from: number, to: number) { const res = await fetch( `${BASE}/payment/usage?from=${from}&to=${to}`, { headers: HEADERS } ) return res.json() } const now = Math.floor(Date.now() / 1000) const thirtyDaysAgo = now - 30 * 24 * 60 * 60 const usage = await getUsage(thirtyDaysAgo, now) // usage.data.storage — bytes used // usage.data.storage_cost — cost string (preserves precision) // usage.data.delivery — bytes delivered // usage.data.delivery_cost // usage.data.transcode — seconds transcoded // usage.data.transcode_cost ``` --- ## Get Top-Ups GET https://api.aiozstream.network/api/payment/top_ups Returns AIOZ token top-up transactions. ```typescript async function getTopUps(limit = 25, offset = 0) { const res = await fetch( `${BASE}/payment/top_ups?limit=${limit}&offset=${offset}&orderBy=desc&sortBy=created_at`, { headers: HEADERS } ) return res.json() } const topUps = await getTopUps() // topUps.data.total // topUps.data.top_ups: [{ // transaction_id, from (wallet address), status, created_at, credit // }] ``` --- ## Get Billings GET https://api.aiozstream.network/api/payment/billings Returns daily billing records. ```typescript async function getBillings(limit = 25, offset = 0) { const res = await fetch( `${BASE}/payment/billings?limit=${limit}&offset=${offset}&orderBy=desc`, { headers: HEADERS } ) return res.json() } const billings = await getBillings() // billings.data.total // billings.data.billings: [{ // storage, delivery, transcode, // storage_cost, delivery_cost, transcode_cost, // created_at // }] ``` --- # AIOZ Stream — Live Streaming SDK package: @aiozstream/nodejs-client Note: Live Streaming API is not yet in the SDK — use fetch with API key headers. ## Setup ```typescript const HEADERS = { 'Content-Type': 'application/json', 'stream-public-key': process.env.AIOZ_PUBLIC_KEY, 'stream-secret-key': process.env.AIOZ_SECRET_KEY } const BASE = 'https://api.aiozstream.network/api' ``` A live stream key must exist before creating a streaming session. Create keys in the dashboard or via the API. --- ## Create Live Streaming Session POST https://api.aiozstream.network/api/live_streams/{keyId}/streamings ```typescript async function createLiveStream(keyId: string, title: string, save = false) { const res = await fetch(`${BASE}/live_streams/${keyId}/streamings`, { method: 'POST', headers: HEADERS, body: JSON.stringify({ title, save, qualities: [ { resolution: '360p', type: 'hls', container_type: 'mpegts' }, { resolution: '720p', type: 'hls', container_type: 'mpegts' } ] }) }) return res.json() } const stream = await createLiveStream('key_id', 'My Live Stream', true) // stream.data.id — streaming session ID // stream.data.status — 'created' // stream.data.assets.hlsUrl // stream.data.assets.playerUrl // stream.data.assets.iframe // stream.data.currentView, totalView ``` Quality resolutions: '240p' | '360p' | '480p' | '720p' | '1080p' container_type for hls: 'mpegts' | 'mp4' container_type for dash: 'fmp4' --- ## Get Live Stream Statistics GET https://api.aiozstream.network/api/live_streams/statistic/{streamMediaId} Statistics update every 5–30 seconds during an active stream. ```typescript async function getLiveStreamStats(streamMediaId: string) { const res = await fetch( `${BASE}/live_streams/statistic/${streamMediaId}`, { headers: HEADERS } ) return res.json() } const stats = await getLiveStreamStats('stream_media_id') // stats.data.fps_in — studio upload frame rate // stats.data.fps_out — delivery frame rate to viewers // stats.data.bitrate_in — studio upload bitrate (Kbps) // stats.data.bitrate_out — delivery bitrate to viewers (Kbps) // stats.data.data_transferred — total MB sent from studio ``` --- ## List Saved Live Stream Media POST https://api.aiozstream.network/api/live_streams/{keyId}/media Lists media saved from past live streams (requires save: true during stream). ```typescript async function listSavedMedia(keyId: string, options = {}) { const res = await fetch(`${BASE}/live_streams/${keyId}/media`, { method: 'POST', headers: HEADERS, body: JSON.stringify({ offset: 0, limit: 25, sort_by: 'created_at', order_by: 'desc', ...options }) }) return res.json() } const saved = await listSavedMedia('key_id') // saved.data.videos: [{id, title, duration, assets, status, video: {...}}] // saved.data.total ``` --- ## Add/Update Multicast RTMP URLs POST https://api.aiozstream.network/api/live_streams/multicast/{streamKey} Simultaneously forward a live stream to YouTube, Twitch, Facebook, etc. ```typescript async function setMulticastUrls(streamKey: string, urls: string[]) { const res = await fetch(`${BASE}/live_streams/multicast/${streamKey}`, { method: 'POST', headers: HEADERS, body: JSON.stringify({ multicast_urls: urls }) }) return res.json() } await setMulticastUrls('your_stream_key', [ 'rtmp://a.rtmp.youtube.com/live2/your-youtube-key', 'rtmp://live.twitch.tv/live/your-twitch-key' ]) ``` --- # Error Handling All responses follow this structure: Success: { "status": "success", "data": { ... } } Error: { "status": "error", "message": "Error description" } SDK errors throw exceptions — always wrap SDK calls in try/catch: ```typescript try { const result = await client.media.create({ title: 'My Video', type: 'video' }) } catch (error) { console.error('API error:', error.message) } ``` HTTP Status Codes: - 200: OK - 201: Created - 400: Bad Request — invalid parameters - 401: Unauthorized — missing or invalid credentials - 403: Forbidden — insufficient permissions - 404: Not Found - 429: Too Many Requests — rate limit exceeded - 500: Internal Server Error Rate Limiting: Use listWithResponseHeaders() SDK variants to access rate limit headers: - X-RateLimit-Limit - X-RateLimit-Remaining - X-RateLimit-Retry-After ```typescript const { headers, body } = await client.webhook.listWithResponseHeaders() console.log(headers['x-ratelimit-remaining']) ```