> ## 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.

# Upload a source image

For sources that aren't publicly fetchable — push them to Motioness R2 first.

# Upload a source image

Motioness fetches source images from public URLs. When your source lives behind auth, on a private CDN, or in your own R2 bucket, upload it first via [`POST /api/v1/upload`](/api/upload/upload), then proxy the returned public URL.

## When you need this

- Internal dashboard screenshots
- User-uploaded content (avatars, photos)
- Sources behind a CDN that requires signed cookies
- Generated images from a vision/image model (Replicate, Anthropic, OpenAI)

## Constraints

| Field | Limit |
|---|---|
| Format | `image/png`, `image/jpeg`, `image/webp` only |
| Size | ≤ 10 MB |
| Auth | Session cookie required |

## Upload

```bash
curl -X POST https://motioness.com/api/v1/upload \
  -b cookie.txt \
  -F file=@/path/to/hero.png
# → { "url": "https://motioness.com/uploads/usr_abc/1m2…ab.png", "key": "uploads/usr_abc/..." }
```

Or in JavaScript:

```ts
async function uploadSource(file: File): Promise<string> {
  const form = new FormData();
  form.append('file', file);
  const res = await fetch('https://motioness.com/api/v1/upload', {
    method: 'POST',
    credentials: 'include',
    body: form,
  });
  if (!res.ok) throw new Error(`upload failed: ${res.status}`);
  const { url } = await res.json();
  return url; // public, immutable, edge-cached
}
```

## Use the upload URL as a source

```text
src = https://motioness.com/uploads/usr_abc/1m2x...ab.png

Proxy:
https://motioness.com/v1/pk_abc/motioness.com/uploads/usr_abc/1m2x...ab.png.mp4
```

The upload URL itself counts as a public origin. Add `https://motioness.com` to your project's `allowed_origins` (or use signed mode), then it'll proxy normally.

## Lifecycle

Uploaded sources are stored in R2 under `uploads/{userId}/{filename}`. They're:

- Permanent (no auto-delete).
- Public-readable via `https://motioness.com/uploads/...`.
- Cached at the edge with `cache-control: public, max-age=31536000, immutable`.

The filename includes a timestamp + random suffix, so re-uploading the same file produces a fresh URL each time.

## Cleaning up

There's no delete endpoint today. If you need to remove an uploaded source, delete the R2 object directly via `wrangler r2 object delete` or open a support ticket. (Public delete-by-URL is on the v1.2 roadmap.)

## Common mistakes

**Trying to upload mp4 / svg**

Only png/jpg/webp accepted — vision model only handles raster. SVGs return 415. Re-render to PNG first.

  **Forgetting credentials: 'include'**

Session cookie auth requires `credentials: 'include'` on cross-origin fetch. Without it the browser strips the cookie and you get 401.

  **Source > 10 MB**

Returns 413. Resize before upload — for hero images, 1920×1080 PNG should be well under.