# EchoWave — full agent reference

> EchoWave is an online video editor and media toolkit that is fully operable by AI agents. This document is the complete LLM-readable reference for the EchoWave API and MCP server.

## Endpoints & discovery

- Site: `https://echowave.io`
- Web app / editor: `https://echowave.io/app`
- REST API base: `https://echowave.io/api/v1`
- MCP server (Streamable HTTP): `https://echowave.io/mcp`
- OpenAPI 3.1: `https://echowave.io/openapi.json`
- API catalog (RFC 9727): `https://echowave.io/.well-known/api-catalog`
- MCP server card: `https://echowave.io/.well-known/mcp/server-card.json`
- Agent skills: `https://echowave.io/.well-known/agent-skills/index.json`
- OAuth 2.1 / OIDC: `https://echowave.io/.well-known/oauth-authorization-server`, `…/openid-configuration`, `…/oauth-protected-resource`, JWKS at `…/jwks.json`
- Health: `https://echowave.io/api/v1/health`

## Authentication

Three options, in order of preference:

1. **OAuth 2.1 (authorization code + PKCE)** — for agents acting on a user's behalf.
   - Discover endpoints at `/.well-known/oauth-authorization-server`.
   - Authorize: `GET https://echowave.io/api/auth/oauth2/authorize?response_type=code&client_id=<id>&redirect_uri=<uri>&scope=api:read%20api:write&code_challenge=<S256>&code_challenge_method=S256&state=<state>`
   - Token: `POST https://echowave.io/api/auth/oauth2/token` (`grant_type=authorization_code`, `code`, `redirect_uri`, `code_verifier`, `client_id`). Returns a JWT access token (alg EdDSA) + refresh token.
   - Send `Authorization: Bearer <access_token>`. Tokens are scoped to resource `https://echowave.io/api/v1` and the MCP server `https://echowave.io/mcp`.
2. **API key** — for server-to-server. Create one in account settings (`/app/settings`). Send `X-API-Key: <key>` (or as a bearer token).
3. **Firebase ID token** — first-party clients may send a Firebase ID token as `Authorization: Bearer <idToken>`.

Scopes: `api:read` (read), `api:write` (create/edit/render).

## Conventions

- All responses are JSON `{ "data": ... }`; list endpoints add `{ "data": [...], "pagination": { page, limit, total, totalPages } }`.
- Errors: `{ "error": "message", "code": "string", "details"?: any }` with standard HTTP status (400 validation, 401 unauthorized, 403 forbidden, 404 not found, 429 rate limited, 500 server, 503 service unavailable).
- Times are ISO‑8601; timeline positions (`startTime`, `endTime`) are milliseconds.

## REST endpoints

### Account
- `GET /account` — profile, plan, status, credits, counts.

### Projects
- `GET /projects?page&limit` — list.
- `POST /projects` — create. Body: `{ title?, size?: { ratio: "16:9"|"9:16"|"1:1"|"4:5"|"custom", custom?: {width,height} }, fps?=30, backgroundColor? }`.
- `GET /projects/{id}` — full project incl. `segments` map.
- `PATCH /projects/{id}` — update `{ title?, size?, fps?, backgroundColor?, duration? }`.
- `DELETE /projects/{id}`.

### Segments (the timeline)
A project's `segments` is a map keyed by segment id. Common fields: `type` (required), `startTime`, `endTime` (ms), `track`, `zIndex`, `opacity`, `rotation`, `position{x,y}`, `scale{x,y}`, `width`, `height`.
Types & extra fields:
- `text` — `text`, `font{family,files}`, `fontSize`, `bold`, `italic`, `color`, `align`, `strokeWidth`, `shadowBlur`.
- `video` / `audio` / `image` — `fileId` (an upload id), `cut`, `speed`, `volume`, `muted`.
- `subtitles` — `parentId`, `fileId`, `fontSize`, `color`, `align`, `verticalAlign`, `caption{presetId}`, `subtitles{segments:[{id,start,end,text}]}`.
- `wave` — `wave` (style id), `color`, `bars`, `corners`.
- `progress_bar` — `barType`, `color`.
- `shape` — `shapeType`, `color`.

Endpoints:
- `POST /projects/{id}/segments` — add (body = a Segment). Returns the created segment with its id.
- `PATCH /projects/{id}/segments/{segmentId}` — update.
- `DELETE /projects/{id}/segments/{segmentId}` — remove.

### Media
- `POST /media` — import by URL: `{ url, type: "video"|"audio"|"image", projectId? }` → `{ uploadId, source, type }`.
- `POST /media/upload` — multipart `file` (and optional `projectId`).

### AI voice-over (TTS) & voices
- `GET /voices` — `{ presets:[...], clones:[...] }`.
- `POST /tts` — `{ projectId, text (≤5000), voiceId, speed?=1, instruct?, model? }` → `{ uploadId, source, url, durationMs, cost, voiceId }`. Then `add_segment` type=audio with that `uploadId`.
- `POST /voices/clones` — `{ name, storagePath, sampleScript?, language? }`.
- `DELETE /voices/clones/{id}`.

### Subtitles
- `POST /subtitles/transcribe` — `{ uploadId }` → `{ jobId }`. Cues land on the upload asynchronously.
- `POST /subtitles/translate` — `{ uploadId, targetLanguage }` → `{ jobId, alreadyExists }`.

### Renders
- `POST /renders` — `{ projectId }` → `202 { renderId, projectId, status: "queued" }`.
- `GET /renders?page&limit&projectId` — list.
- `GET /renders/{id}` — `{ renderId, projectId, status: "queued"|"rendering"|"completed"|"failed", progress, outputUrl, thumbnailUrl, watermark, durationMs, errorMessage, createdAt, completedAt }`.
- `POST /renders/{id}/download` — `{ downloadUrl, filename }`.

Render lifecycle: `queued → rendering → completed | failed`. Poll `GET /renders/{id}` (or supply a webhook). `outputUrl` is present when `completed`. Renders on free accounts carry a watermark.

### Media tools (one-shot)
- `POST /tools/{tool}` — `tool` ∈ `trim|crop|compress|convert|rotate|flip|speed|reverse|loop|mute|split`. Body `{ src (url), name?, options }` (options vary by tool, e.g. convert: `{ targetFormat, videoCodec?, audioCodec? }`; trim: `{ segments:[{start,end}] }`; compress: `{ quality }`). Returns `202 { renderId }`; poll `GET /renders/{id}`.

### Stock
- `GET /stock/photos?query&page&perPage` — search stock photos.

### Status
- `GET /api/v1/health` — `{ status, timestamp, checks }`. No auth.

## MCP server

Connect an MCP client to `https://echowave.io/mcp` (Streamable HTTP). Authenticate via OAuth (the server advertises `WWW-Authenticate` → `/.well-known/oauth-protected-resource`) or an API key. Tools mirror the REST surface: `get_account`, `list_projects`, `create_project`, `get_project`, `update_project`, `delete_project`, `add_segment`, `update_segment`, `delete_segment`, `import_media`, `list_voices`, `generate_speech`, `create_voice_clone`, `transcribe_subtitles`, `translate_subtitles`, `queue_render`, `get_render`, `list_renders`, `get_render_download`, `search_stock_photos`, `run_tool`.

## Example: caption a video end-to-end

1. `create_project` → `{ id }`
2. `import_media { url, type:"video" }` → `{ uploadId }`
3. `add_segment { type:"video", fileId:uploadId, startTime:0, endTime:<ms> }`
4. `transcribe_subtitles { uploadId }`
5. `add_segment { type:"subtitles", parentId:<videoSegId>, fileId:uploadId, ... }`
6. `queue_render { projectId:id }` → `{ renderId }`
7. poll `get_render { renderId }` until `status:"completed"`, then `get_render_download`.
