Aauti MediaAauti MediaDocsSign in

Aauti Media Platform - Integration Guide

Everything you need to upload files, stream video, and deliver media from your application.



Quick Start

1. Upload a file

curl -X POST https://upload.example.com/upload/direct-upload \
  -H "API-Key: your_tenant_api_key" \
  -F "file=@video.mp4" \
  -F "userId=user123" \
  -F "destination=lectures"

2. Get a signed playback URL

curl -X POST https://api.example.com/auth/url-token \
  -H "API-Key: your_tenant_api_key" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://cdn.example.com/fileId/path/video.mp4", "expiresIn": "4h"}'

3. Play the file

https://delivery.example.com/<signed_token>/path/video.mp4

Authentication

API Key (Tenant Authentication)

All upload and API calls require a tenant API key.

Header Value
API-Key Your tenant's app_token
curl -H "API-Key: tk_abc123def456" https://api.example.com/files/duration?file=...

Signed URL Tokens (File Access)

Protected files require a JWT token in the URL path. Generate tokens via the API:

POST /auth/url-token

The token replaces the first path segment in the delivery URL:

https://delivery.example.com/<jwt_token>/tenant/user/file.mp4

Environment URLs

Service Development Test QA Demo Production
API media-apidev.aauti.com media-apitest.aauti.com media-apiqa.aauti.com media-apidemo.aauti.com media-api.aauti.com
Upload upload-dev.aauti.com upload-test.aauti.com upload-qa.aauti.com upload-demo.aauti.com upload.aauti.com
Delivery media-dev.aauti.com media-test.aauti.com media-qa.aauti.com media-demo.aauti.com media.aauti.com
Dashboard media-dashboarddev.aauti.com media-dashboardtest.aauti.com media-dashboardqa.aauti.com media-dashboarddemo.aauti.com media-dashboard.aauti.com

In all examples, {API_BASE} = API Service URL, {UPLOAD_BASE} = Upload Service URL, and {DELIVERY_BASE} = Delivery Service URL for your environment.


Upload Service API

Direct Upload

Upload a file in a single request using multipart/form-data.

POST /upload/direct-upload

Headers:

Header Required Description
API-Key Yes Tenant API key

Form Fields:

Field Required Description
file Yes The file binary
bucket No Target bucket name (uses default if omitted)
userId No Uploader ID (default: anonymous)
destination No Subfolder path (e.g., course/materials)
requireSignedUrl No true to require signed URLs for access (default: false)

Response 201 Created:

{
  "key": "tenant_id/user_id/destination/filename.mp4",
  "url": "https://cdn.example.com/file_id/tenant_id/user_id/destination/filename.mp4",
  "duration": 120.5
}

Duplicate handling: If a completed upload with the same filename already exists, a sequential suffix is appended: video.mp4 -> video_1.mp4 -> video_2.mp4.

Errors:

Status Reason
400 Missing API-Key or file
404 Invalid API key or bucket not found

URL Upload (Async)

Upload a file from a public URL. Returns immediately; processing happens in background.

POST /upload/from-url

Headers:

Header Required Description
API-Key Yes Tenant API key
Content-Type Yes application/json

Body:

{
  "url": "https://example.com/video.mp4",
  "bucket": "my-bucket",
  "userId": "user123",
  "destination": "course/materials",
  "filename": "custom-name.mp4",
  "requireSignedUrl": false,
  "meta": {
    "recordingId": "rec_abc123",
    "name": "Lecture 1"
  }
}
Field Required Description
url Yes Public URL to download from
bucket No Target bucket
userId No Uploader ID
destination No Subfolder path
filename No Override filename
requireSignedUrl No Require signed access
meta No Custom metadata (passed through to webhooks)

Response 202 Accepted:

{
  "jobId": "unique-job-id",
  "status": "queued",
  "message": "Upload from URL queued for processing. Webhook will be triggered on completion."
}

cURL Example:

curl -X POST https://upload.aauti.com/upload/from-url \
  -H "API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/lecture-video.mp4",
    "userId": "user-123",
    "destination": "courses/math",
    "filename": "lecture-1.mp4",
    "meta": { "recordingId": "64f1a2b3c4d5e6f7a8b9c0d1", "courseName": "Math 101" }
  }'

Node.js Example:

const response = await fetch('https://upload.aauti.com/upload/from-url', {
  method: 'POST',
  headers: { 'API-Key': 'your-api-key', 'Content-Type': 'application/json' },
  body: JSON.stringify({
    url: 'https://example.com/lecture-video.mp4', userId: 'user-123',
    destination: 'courses/math', meta: { recordingId: '64f1a2b3c4d5e6f7a8b9c0d1' },
  }),
});
const { jobId } = await response.json(); // now wait for webhook

Webhook Payloads

Success:

{
  "eventType": "file",
  "data": {
    "key": "tenantId/user-123/courses/math/lecture-1.mp4",
    "fileUrl": "https://media.aauti.com/fileId/tenantId/user-123/courses/math/lecture-1.mp4",
    "filename": "lecture-1.mp4",
    "file_type": "video/mp4",
    "file_size": 5242880,
    "user_id": "user-123",
    "source_url": "https://example.com/lecture-video.mp4",
    "meta": { "recordingId": "64f1a2b3c4d5e6f7a8b9c0d1", "courseName": "Math 101" },
    "duration": 120.5
  }
}

duration only included for video files. meta only included if provided in the original request.

Failure:

{
  "eventType": "file",
  "data": {
    "source_url": "https://example.com/lecture-video.mp4",
    "user_id": "user-123",
    "status": "failed",
    "errorMessage": "URL returned HTTP 403 Forbidden",
    "meta": { "recordingId": "64f1a2b3c4d5e6f7a8b9c0d1" }
  }
}

Retry Behavior

Failure Type Retries Examples
4xx from source URL None 403 Forbidden, 404 Not Found
Private/internal URL None Redirect to 192.168.x.x
File too large / invalid tenant None Exceeds max size, revoked API key
5xx from source URL 3 502 Bad Gateway, 503
Network timeout / storage failure 3 DNS failure, temporary S3/R2 error

Retries use exponential backoff: 5s → 10s → 20s

Limits

Setting Value
Max file size 20 GB (configurable per tenant)
Download timeout 5 minutes overall, 30s connection
Max URL redirects 5 hops
URL max length 2048 characters
Webhook delivery 5 attempts, exponential backoff

Errors:

Status Reason
400 Missing API-Key, invalid URL, or URL not accessible

TUS Resumable Upload

For large files, use the TUS protocol for resumable, chunked uploads.

Base path: /files

Required headers for all TUS requests:

Header Value
API-Key Tenant API key
Tus-Resumable 1.0.0

Upload metadata (base64-encoded in Upload-Metadata header):

Key Description
filename Original filename
filetype MIME type
userId Uploader ID
destination Subfolder path
requireSignedUrl true / false

TUS lifecycle:

1. POST   /files                  -- Create upload, get Location header
2. PATCH  /files/:upload_id       -- Send chunks (repeat until complete)
3. HEAD   /files/:upload_id       -- Check progress (Upload-Offset)
4. DELETE /files/:upload_id       -- Cancel upload

Example - Create upload:

curl -X POST https://upload.example.com/files \
  -H "API-Key: your_api_key" \
  -H "Tus-Resumable: 1.0.0" \
  -H "Upload-Length: 104857600" \
  -H "Upload-Metadata: filename dmlkZW8ubXA0,filetype dmlkZW8vbXA0,userId dXNlcjEyMw=="

Example - Upload chunk:

curl -X PATCH https://upload.example.com/files/abc123 \
  -H "API-Key: your_api_key" \
  -H "Tus-Resumable: 1.0.0" \
  -H "Upload-Offset: 0" \
  -H "Content-Type: application/offset+octet-stream" \
  --data-binary @chunk1.bin

Check upload status:

HEAD /files/:uploadId

Response headers:

Upload-Offset: 5242880
Upload-Length: 104857600
Tus-Resumable: 1.0.0

Cancel upload:

DELETE /files/:uploadId

Returns 204 — upload cancelled and cleaned up.

TUS JavaScript Client Example:

import * as tus from "tus-js-client";

const file = document.getElementById("fileInput").files[0];
const upload = new tus.Upload(file, {
  endpoint: "https://upload.aauti.com/files",
  headers: { "API-Key": "your-api-key", "Bucket": "my-bucket" },
  metadata: { filename: file.name, filetype: file.type, userId: "user123", destination: "course/materials" },
  chunkSize: 5 * 1024 * 1024,
  onProgress: (bytesUploaded, bytesTotal) => {
    console.log(`${((bytesUploaded / bytesTotal) * 100).toFixed(2)}%`);
  },
  onSuccess: () => console.log("Upload complete:", upload.url),
  onError: (error) => console.error("Upload failed:", error),
});
upload.start();

API Service

Generate Signed URL Token

POST /auth/url-token

Headers:

Header Required Description
API-Key Yes Tenant API key
Content-Type Yes application/json

Body:

{
  "url": "https://cdn.example.com/file_id/path/video.mp4",
  "expiresIn": "4h"
}
Field Required Description
url Yes Full file access URL
expiresIn No Token lifetime: "5m", "1h", "2d", 3600 (default: "4h")

Response 200 OK:

{
  "token": "eyJhbGciOiJSUzI1NiIs...",
  "url": "https://cdn.example.com/eyJhbGci.../path/video.mp4",
  "signed": true
}

For video files, token expiry is automatically extended to max(duration * 2, requested_expiresIn).


Get Video Duration

GET /files/duration?file=<access_url>

Headers:

Header Required
API-Key Yes

Response 200 OK:

{
  "status": 200,
  "duration": 125.5
}

YouTube URLs are automatically detected and duration is fetched via YouTube Data API.


Get Download URL

GET /files/download?file=<access_url>

Headers:

Header Required
API-Key Yes

Response 200 OK:

{
  "url": "https://account.r2.cloudflarestorage.com/bucket/...?X-Amz-Algorithm=...",
  "filename": "original_filename.mp4",
  "file_size": 1024000
}

Register Existing File

Register metadata for a file that's already in your storage bucket (e.g., uploaded outside this platform).

POST /files/register

Headers:

Header Required
API-Key Yes
Content-Type application/json

Body:

{
  "filePath": "tenant_id/user_id/destination/file.mp4",
  "bucket": "bucket-name",
  "userId": "user123",
  "requireSignedUrl": true
}

Response 201 Created:

{
  "key": "file_id",
  "url": "https://cdn.example.com/file_id/path",
  "filename": "file.mp4",
  "file_type": "video/mp4",
  "file_size": 1024000,
  "duration": 120.5
}

Delete File

DELETE /files/delete

Headers:

Header Required
API-Key Yes
Content-Type application/json

Body:

{
  "file": "https://cdn.example.com/file_id/path"
}

Response 200 OK:

{
  "success": true,
  "message": "File deleted successfully"
}

Create Stream Copies

Create transcoded stream copies from source video URLs using Cloudflare Stream or VdoCipher.

POST /streams/v1/create

Headers:

Header Required
API-Key Yes
Content-Type application/json

Body:

{
  "streams": [
    {
      "url": "https://cdn.example.com/file_id/video.mp4",
      "meta": {
        "name": "Lecture 1",
        "recordingId": "rec_abc123"
      },
      "required_signed_url": false,
      "provider": "cloudflare_stream"
    }
  ]
}
Field Required Description
streams[].url Yes Source video URL
streams[].meta Yes Metadata (must include name)
streams[].required_signed_url No Require signed playback
streams[].provider No cloudflare_stream (default) or vdocipher

Response 201 Created:

{
  "created": true,
  "count": 1,
  "ids": ["stream_doc_id"],
  "existing": [
    {
      "id": "existing_id",
      "url": "https://cdn.example.com/file_id/video.mp4",
      "status": "ready",
      "uid": "cf_stream_uid",
      "hls_url": "https://stream.cloudflarestream.com/.../manifest/video.m3u8",
      "dash_url": "https://stream.cloudflarestream.com/.../manifest/video.mpd",
      "thumbnail": "https://cloudflarestream.com/thumbnail.jpg"
    }
  ]
}

Duplicate URLs in the same batch or already in the database are deduplicated. Existing ready streams are returned in the existing array.

Stream Status Flow:

pending → processing → downloading → ready
pending → processing → failed (after all retries exhausted)

Webhook Integration

Setting Up Webhooks

Configure a webhook_url in your tenant's storage or streaming settings via the dashboard. The platform sends webhooks for:

  1. File uploads (direct, TUS, URL)
  2. Stream encoding status changes

Webhook Signature Verification

All webhooks include an HMAC-SHA256 signature:

Header: Webhook-Signature: sha256=<hex_digest>

Verify in Node.js:

const crypto = require('crypto');

function verifyWebhook(body, signature, secret) {
  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(body)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

Webhook: File Upload Complete

{
  "eventType": "file",
  "data": {
    "key": "64f1a2b3c4d5e6f7a8b9c0d1",
    "recordingUrl": "https://cdn.example.com/file_id/tenant/user/video.mp4",
    "filename": "video.mp4",
    "file_type": "video/mp4",
    "file_size": 1024000,
    "user_id": "user123",
    "duration": 120.5,
    "meta": { "recordingId": "rec_abc123" }
  }
}

Webhook: Stream Status Update

{
  "eventType": "stream",
  "data": {
    "uid": "cf_stream_uid",
    "provider": "cloudflare_stream",
    "url": "https://cdn.example.com/file_id/video.mp4",
    "hlsUrl": "https://stream.cloudflarestream.com/.../manifest/video.m3u8",
    "dashUrl": "https://stream.cloudflarestream.com/.../manifest/video.mpd",
    "thumbnail": "https://cloudflarestream.com/thumbnail.jpg",
    "status": "ready",
    "duration": 120.5,
    "encodingData": { "name": "Lecture 1" }
  }
}

Webhook: Recording Complete

{
  "eventType": "recording",
  "data": {
    "key": "507f1f77bcf86cd799439011",
    "recordingUrl": "https://media.aauti.com/abc123/tenant123/user456/video.mp4",
    "filename": "video.mp4",
    "file_type": "video/mp4",
    "file_size": 104857600,
    "user_id": "user123",
    "duration": 245.6
  }
}

Webhook Delivery

  • Retries: 5 attempts with exponential backoff
  • Timeout: 30 seconds per attempt
  • Signing: HMAC-SHA256 via webhook_signing_key from your tenant config
  • Your endpoint must return HTTP 2xx to acknowledge delivery. Non-2xx triggers a retry.

Accessing Files

Public Files

Uploaded without requireSignedUrl. Directly accessible via access URL:

https://media.aauti.com/{fileId}/{tenantId}/{userId}/{destination}/{filename}

Private Files

Uploaded with requireSignedUrl: true. Requires a signed JWT in the path:

https://media.aauti.com/{jwt_token}/{tenantId}/{userId}/{destination}/{filename}

Private File Access Flow

  1. Upload with requireSignedUrl: true
  2. Call POST /auth/url-token with file's access URL
  3. Use returned signed URL (expires in 4h by default)

Video Playback

<!-- Public video -->
<video src="https://media.aauti.com/fileId/tenant/user/video.mp4" controls></video>

<!-- Private video (use signed URL from /auth/url-token) -->
<video src="https://media.aauti.com/eyJhbGciOi.../tenant/user/video.mp4" controls></video>

Range requests are fully supported. Seek and scrub operations work efficiently without re-downloading the full file.

Domain Whitelisting

The delivery service enforces origin restrictions for your uploaded content:

  • Browser requests (with Referer): hostname must be in your tenant's whitelisted_domains
  • Browser requests (no Referer, image loads): allowed (for email client compatibility)
  • Mobile apps (X-App-ID header): app ID must be in your whitelist
  • Empty whitelist: no restrictions (all origins allowed)

Configure whitelisted domains in the General Settings page of your dashboard.


Embed & Player Integration

Embedding the Player

Use the embed page in an iframe:

<iframe
  src="https://dashboard.example.com/embed?file=FILE_ID&api_key=YOUR_API_KEY"
  width="640"
  height="360"
  frameborder="0"
  allow="autoplay; fullscreen; encrypted-media"
  allowfullscreen>
</iframe>

Query parameters:

Param Description
file File ID (required)
api_key Tenant API key (required)
player Player config ID
playStream true (default) or false
autoplay true / false
muted true / false
loop true / false
controls true / false
user_id User ID for watermark
user_name User name for watermark
user_email User email for watermark
loader Show loading spinner

postMessage API

Events emitted by the player:

Event Payload
ready { source: 'aauti-embed', event: 'ready', playerType, duration }
statechange { source: 'aauti-embed', event: 'statechange', state: { playing, muted, currentTime, duration, volume, ended } }
timeupdate { source: 'aauti-embed', event: 'timeupdate', currentTime, duration }
ended { source: 'aauti-embed', event: 'ended' }
error { source: 'aauti-embed', event: 'error', message }

Actions you can send to the player:

Action Data Description
play / pause { action: 'play' } Start / pause playback
mute / unmute / toggleMute { action: 'mute' } Control audio mute state
seek { action: 'seek', value: 30 } Seek to 30 seconds
volume { action: 'volume', value: 0.5 } Set volume (0–1)
getState { action: 'getState' } Request current player state

Example:

const iframe = document.getElementById('myPlayer');
window.addEventListener('message', (e) => {
  if (e.data?.source !== 'aauti-embed') return;
  if (e.data.event === 'ready') console.log('Player ready, duration:', e.data.duration);
  if (e.data.event === 'ended') console.log('Playback finished');
});
iframe.contentWindow.postMessage({ action: 'play' }, '*');
iframe.contentWindow.postMessage({ action: 'seek', value: 60 }, '*');

Error Reference

Standard Error Format

{
  "statusCode": 400,
  "message": "Error description",
  "error": "BadRequest"
}

Common Status Codes

Code Meaning
200 OK
201 Created
202 Accepted (async processing queued)
206 Partial Content (range request)
304 Not Modified (cache hit)
400 Bad Request (missing or invalid parameters)
401 Unauthorized (missing or invalid API key / token)
403 Forbidden (domain not whitelisted / access denied)
404 Not Found (file, tenant, or resource doesn't exist)
500 Internal Server Error

Common Errors

Error Cause
APP_NOT_WHITELISTED Mobile app ID not in tenant whitelist
ORIGIN_NOT_WHITELISTED Browser origin not in tenant whitelist
SIGNED_URL_REQUIRED File requires signed URL but request is unauthenticated
JWT_REQUIRED Non-browser request to protected file without JWT

Rate Limits & Quotas

Limit Value
Max file size (URL upload) Configurable, default 10 GB
Max file size (TUS) Configurable, default 10 GB
Webhook retry attempts 5
Webhook timeout per attempt 30 seconds

Quick Reference

Action Method URL Sync/Async
Direct upload POST {UPLOAD_BASE}/upload/direct-upload Sync
TUS create POST {UPLOAD_BASE}/files Sync
TUS chunk PATCH {UPLOAD_BASE}/files/{id} Sync
TUS status HEAD {UPLOAD_BASE}/files/{id} Sync
TUS cancel DELETE {UPLOAD_BASE}/files/{id} Sync
Upload from URL POST {UPLOAD_BASE}/upload/from-url Async
Get signed URL POST {API_BASE}/auth/url-token Sync
Get duration GET {API_BASE}/files/duration?file={url} Sync
Download file GET {API_BASE}/files/download?file={url} Sync
Register file POST {API_BASE}/files/register Sync
Delete file DELETE {API_BASE}/files/delete Sync
Create streams POST {API_BASE}/streams/v1/create Async
Embed player GET {DASHBOARD_BASE}/embed?file={id}&api_key={key} Sync

Aauti Media Platform — Integration Guide