
# Video API

Use the Video API to upload and manage video assets, captions, player settings, webhooks, and usage estimates.

## Base URL and authentication

The API base URL is:

```text
https://rabata.io/api/v1/video
```

Create a Video API key from **Dashboard → Video → API keys**. Send it as a Bearer token with every request:

```bash
curl https://rabata.io/api/v1/video/assets \
  --header "Authorization: Bearer YOUR_VIDEO_API_KEY"
```

Keep the key in a secret manager and send it only over HTTPS. A missing, malformed, disabled, or unknown key returns `401 Unauthorized`.

Requests are limited to 60 per minute for each authenticated account. A throttled request returns `429 Too Many Requests`.

## Errors

Successful requests return JSON. Errors use this shape:

```json
{
  "error": "A description of the problem"
}
```

Common statuses are:

| Status | Meaning |
| --- | --- |
| `400` | The request could not be parsed. |
| `401` | The Bearer token is missing or invalid. |
| `403` | The account is blocked, the Video subscription is inactive, or a metered operation has no active card or spendable promotional credit. |
| `404` | The requested resource does not exist in the authenticated account. |
| `422` | A parameter, file, or resource state is invalid. |
| `429` | The request limit was exceeded. |
| `503` | A dependent storage service is temporarily unavailable. |

Resource identifiers are UUIDs. A valid UUID owned by another account is treated as not found.

## Assets

### List assets

```http
GET /api/v1/video/assets?page=1&limit=20
```

`page` starts at 1. `limit` defaults to 20 and is constrained to 1–100.

```json
{
  "data": [
    {
      "id": "ASSET_UUID",
      "title": "Product tour",
      "status": "ready",
      "original_filename": "tour.mp4",
      "file_size": 73400320,
      "duration": 83.4,
      "width": 1920,
      "height": 1080,
      "thumbnail_url": "https://...",
      "allowed_domains": ["example.com"],
      "created_at": "2026-07-30T10:00:00Z",
      "embed_url": "https://rabata.io/embed/ASSET_UUID",
      "stream_url": "https://..."
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 1,
    "pages": 1
  }
}
```

Retrieve one asset with:

```http
GET /api/v1/video/assets/ASSET_UUID
```

`stream_url` is returned only when the asset is ready and its owner has valid,
funded Video access. If metered access is paused because the account has no active
card, active legacy trial, or at least $0.01 of promotional credit remaining,
list and show
requests still return asset metadata but omit `stream_url`; embeds also refuse
playback. Mutating requests return `403` until funding is restored.

### Upload an asset

Uploads go directly to object storage with presigned multipart URLs.

1. Create the asset and upload session:

   ```bash
   curl https://rabata.io/api/v1/video/assets \
     --request POST \
     --header "Authorization: Bearer YOUR_VIDEO_API_KEY" \
     --header "Content-Type: application/json" \
     --data '{
       "title": "Product tour",
       "original_filename": "tour.mp4",
       "file_size": 73400320
     }'
   ```

   `file_size` is in bytes. Supported extensions are `.mp4`, `.mov`, `.avi`, `.mkv`, `.webm`, `.flv`, `.wmv`, `.m4v`, `.3gp`, and `.ogv`. The maximum file size is 10 GB.

   The response contains the new asset `id`, an `upload.upload_id`, the `upload.chunk_size`, and one presigned URL per part.

2. Split the file into chunks of `upload.chunk_size` bytes. `PUT` each chunk to the corresponding presigned URL in order, without the Rabata Authorization header. Save the `ETag` response header from every upload.

3. Complete the upload:

   ```bash
   curl https://rabata.io/api/v1/video/assets/ASSET_UUID/confirm \
     --request POST \
     --header "Authorization: Bearer YOUR_VIDEO_API_KEY" \
     --header "Content-Type: application/json" \
     --data '{
       "upload_id": "UPLOAD_ID",
       "parts": [
         { "part_number": 1, "etag": "PART_1_ETAG" },
         { "part_number": 2, "etag": "PART_2_ETAG" }
       ]
     }'
   ```

The asset moves through `waiting`, `uploaded`, `processing`, and `ready`. Poll the asset or listen for an `asset.ready` webhook before using its stream URL. A response with `transcoding_started: false` includes a warning and can be retried.

If the upload cannot be completed, cancel its multipart session and discard the waiting asset:

```http
POST /api/v1/video/assets/ASSET_UUID/abort
Content-Type: application/json

{
  "upload_id": "UPLOAD_ID"
}
```

Cancellation remains available when the Video subscription has expired so abandoned storage uploads can still be cleaned up.

### Update or delete an asset

Update the domain allowlist or asset-specific player settings:

```bash
curl https://rabata.io/api/v1/video/assets/ASSET_UUID \
  --request PATCH \
  --header "Authorization: Bearer YOUR_VIDEO_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "allowed_domains": ["example.com", "www.example.com"],
    "player_settings": {
      "accent_color": "#28CDD9",
      "autoplay": false,
      "muted": false,
      "loop_video": false,
      "show_controls": true,
      "show_title": false
    }
  }'
```

An empty `allowed_domains` array allows embedding from any domain. At most 20 hostnames can be supplied; enter hostnames without paths.

Delete an asset with:

```http
DELETE /api/v1/video/assets/ASSET_UUID
```

Deleting an asset removes it from the API and schedules its stored media for cleanup.

### Get embed code

When the asset is ready:

```http
GET /api/v1/video/assets/ASSET_UUID/embed
```

The response contains `embed_url` and a ready-to-use `iframe` string.

### Manage a custom thumbnail

Request a presigned image upload:

```http
POST /api/v1/video/assets/ASSET_UUID/thumbnail
Content-Type: application/json

{
  "filename": "poster.jpg",
  "content_type": "image/jpeg",
  "file_size": 245760
}
```

Upload the image to the returned `url`, then confirm its returned storage `key`:

```http
PUT /api/v1/video/assets/ASSET_UUID/thumbnail
Content-Type: application/json

{
  "key": "RETURNED_STORAGE_KEY"
}
```

Remove the custom thumbnail with:

```http
DELETE /api/v1/video/assets/ASSET_UUID/thumbnail
```

## Captions

List or retrieve captions:

```http
GET /api/v1/video/assets/ASSET_UUID/captions
GET /api/v1/video/assets/ASSET_UUID/captions/CAPTION_UUID
```

Upload a WebVTT or SubRip file as multipart form data:

```bash
curl https://rabata.io/api/v1/video/assets/ASSET_UUID/captions \
  --request POST \
  --header "Authorization: Bearer YOUR_VIDEO_API_KEY" \
  --form "file=@captions.vtt" \
  --form "label=English" \
  --form "language=en"
```

Caption files can be up to 2 MB. Language values use a lowercase BCP 47 tag such as `en`, `en-us`, or `pt-br`. Each asset can have one active caption per language and up to 30 captions.

Delete a caption with:

```http
DELETE /api/v1/video/assets/ASSET_UUID/captions/CAPTION_UUID
```

## Player defaults

Get the account-wide player defaults:

```http
GET /api/v1/video/player_config
```

Update any subset of the settings:

```http
PATCH /api/v1/video/player_config
Content-Type: application/json

{
  "accent_color": "#28CDD9",
  "autoplay": false,
  "muted": false,
  "loop_video": false,
  "show_controls": true,
  "show_title": false
}
```

Asset-specific player settings override these account defaults.

## Analytics

Get views and watch-time totals plus a time series:

```http
GET /api/v1/video/assets/ASSET_UUID/analytics?from=2026-07-01&to=2026-07-30&granularity=day
```

`granularity` can be `day` or `hour`; it defaults to `day`. `from` defaults to 30 days ago and `to` defaults to now.

Get the audience-retention curve for the same period:

```http
GET /api/v1/video/assets/ASSET_UUID/retention?from=2026-07-01&to=2026-07-30
```

## Usage and pricing

Retrieve the current calendar month's completed usage and estimated charges:

```http
GET /api/v1/video/usage
```

The existing `data` array remains unchanged for clients that consume current-period usage records. The additive `summary` object contains the usage-pricing contract:

```json
{
  "data": [
    {
      "period_start": "2026-07-31T00:00:00Z",
      "period_end": "2026-07-31T23:59:59Z",
      "storage_bytes": 1200000000,
      "egress_bytes": 410000000
    }
  ],
  "summary": {
    "period_start": "2026-07-01",
    "period_end": "2026-07-31",
    "data_through": "2026-07-30",
    "currency": "usd",
    "source_storage": {
      "current_bytes": 1200000000,
      "average_bytes": "1000000000.000000",
      "rate_per_gb_month": "0.006000",
      "estimated_cost": "0.0060"
    },
    "delivery": {
      "720p": {
        "seconds": "6000.000",
        "minutes": "100.0",
        "rate_per_minute": "0.000600",
        "estimated_cost": "0.0600"
      },
      "1080p": {
        "seconds": "0.000",
        "minutes": "0.0",
        "rate_per_minute": "0.000800",
        "estimated_cost": "0.0000"
      },
      "2k": {
        "seconds": "0.000",
        "minutes": "0.0",
        "rate_per_minute": "0.001300",
        "estimated_cost": "0.0000"
      },
      "4k": {
        "seconds": "0.000",
        "minutes": "0.0",
        "rate_per_minute": "0.002500",
        "estimated_cost": "0.0000"
      }
    },
    "estimated_total": "0.0700",
    "transcoding": {
      "standard_on_demand_included": true
    }
  }
}
```

`delivery` always contains the `720p`, `1080p`, `2k`, and `4k` rate tiers. `data_through` is the last contiguous completed UTC day, or `null` when no day is ready. Current-month values are estimates; invoice charges are finalized only after every billable day in the month is complete.

## Webhooks

Create a public HTTP or HTTPS callback for any combination of `asset.created`, `asset.ready`, `asset.errored`, and `asset.deleted`. Use HTTPS in production:

```bash
curl https://rabata.io/api/v1/video/webhooks \
  --request POST \
  --header "Authorization: Bearer YOUR_VIDEO_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "callback_url": "https://example.com/rabata-webhooks",
    "events": ["asset.ready", "asset.errored"]
  }'
```

The callback hostname must resolve only to public IP addresses. An empty `events` array subscribes to all supported events.
The create response returns the signing secret once; store it in your secret manager because later responses do not expose it.

List, retrieve, update, or delete webhook configurations with:

```http
GET    /api/v1/video/webhooks
GET    /api/v1/video/webhooks/WEBHOOK_UUID
PATCH  /api/v1/video/webhooks/WEBHOOK_UUID
DELETE /api/v1/video/webhooks/WEBHOOK_UUID
```

Delivery requests use `Content-Type: application/json`, identify the event in `X-Rabata-Event`, and include an HMAC-SHA256 digest in `X-Rabata-Signature`. Respond with a `2xx` status after accepting the event. Failed deliveries are retried with backoff.
