> ## Documentation Index
> Fetch the complete documentation index at: https://docs.motioness.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Public proxy URL

GET /v1/{projectKey}/{src}.{format} — the URL pattern that powers every embed.

# Public proxy URL

This isn't a typical REST endpoint — it's a URL pattern. Every Motioness embed (`<img>`, `<video>`, OG tags, RSS, MDX) is just a hand-crafted URL on `motioness.com`.

```text
GET https://motioness.com/v1/{projectKey}/{srcWithoutScheme}.{format}[?params]
```

## Auth

Two modes per project. Pick one:

- **Allowlist** — source URL plain text, origin must match `allowed_origins`.
- **Signed** — source URL base64url-encoded, request signed with HMAC-SHA256.

See [allowlist vs signed](/concepts/allowlist-vs-signed).

## Path

| Segment | Description |
|---|---|
| `projectKey` | Public project key (`pk_…`) |
| `srcWithoutScheme` | Source URL with `https://` stripped (allowlist) **or** base64url-encoded (signed) |
| `format` | `.mp4` for video, `.jpg` for poster |

## Query parameters

| Param | Type | Default | Effect |
|---|---|---|---|
| `motion` | `subtle` / `medium` / `kinetic` | project default | Motion intensity |
| `mode` | `loop` / `animate-in` / `animate-on-hover` | project default | Playback shape |
| `dur` | int 2..8 | project default | Output length (seconds) |
| `prompt` | string ≤500 | — | Override brand-aware prompt |
| `seed` | int | — | Deterministic seed |
| `aspect` | `1:1` / `16:9` / `9:16` / `preserve` | `preserve` | Output aspect (seedance only) |
| `idem` | string | — | Force distinct asset from same source |
| `wait_ms` | int 0..10000 | 0 | Long-poll deadline |
| `sig` | hex | — | HMAC signature (signed mode only) |
| `exp` | unix seconds | — | Signature expiry (signed mode only) |

## Responses

### 200 (mp4 ready, served from R2 / edge cache)

```
HTTP/1.1 200 OK
Content-Type: video/mp4
X-Motioness-Asset-Id: a25b330a71e2bc6b
X-Motioness-Stage: ready
X-Motioness-Loop: native
X-Request-Id: req_abc12345
Cache-Control: public, max-age=31536000, immutable
cf-cache-status: HIT
```

### 200 (jpg poster)

```
HTTP/1.1 200 OK
Content-Type: image/jpeg
Cache-Control: public, max-age=31536000, immutable
```

### 202 (mp4 generating)

```http
HTTP/1.1 202 Accepted
Retry-After: 30
X-Motioness-Asset-Id: a25b…
X-Motioness-Stage: queued
X-Motioness-Eta-Ms: 28000
Content-Type: application/json

{
  "status": "pending",
  "asset_id": "a25b330a71e2bc6b",
  "eta_seconds": 28
}
```

### 403 (forbidden)

```json
{ "error": "origin not in allowlist" }
{ "error": "unknown project key" }
{ "error": "invalid signature" }
{ "error": "signing not configured or signature missing" }
```

### 415 / 413 (source rejected)

```json
{ "error": "unsupported type: image/svg+xml — png/jpeg/webp only" }
{ "error": "file exceeds 10 MB limit" }
```

### 429 (quota)

```json
{ "error": "monthly limit reached", "upgrade_url": "https://motioness.com/pricing" }
```

### 502 (terminal failure)

```json
{
  "error": "last generation failed",
  "asset_id": "a25b…",
  "hint": "POST /api/v1/regenerate/{asset_id}"
}
```

## SSE companion

For live status, the public proxy also exposes:

```text
GET /v1/{projectKey}/events/{assetId}
```

See [SSE streaming](/guides/sse-streaming).

## Why this isn't in the OpenAPI spec

The proxy URL has variable path segments (the source URL itself is part of the path) and a polymorphic response (mp4 bytes vs JSON). OpenAPI's `paths` model doesn't express that cleanly. We document it manually here and keep the structured REST API in the auto-generated reference.