# Shotto reference for agents

Product language: Hebrew. API messages (`error.message`, status labels, MCP summaries) are Hebrew; field names and
codes are English. Human docs (Hebrew): https://www.shottoai.com/developers

## Endpoints

- MCP: `POST https://www.shottoai.com/mcp` (Streamable HTTP, stateless, JSON responses, no SSE, no sessions).
- REST base: `https://www.shottoai.com/api/v1`
- Auth for both: `Authorization: Bearer sk_shotto_…` (key from https://www.shottoai.com/settings/api).
- Rate limit: 60 requests per minute per key, REST and MCP together. Edits: at most 3 per 10 minutes.
- Every action through a key counts as the account owner's action and spends the owner's credits.

## MCP tools and their REST equivalents

| MCP tool | REST | Cost |
|---|---|---|
| `shotto_balance` | `GET /me` | free |
| `shotto_presenters` (`gender?`) | `GET /presenters?gender=female\|male` | free |
| `shotto_create_video` | `POST /videos` | presenter 1 credit/s (10/20/30), `quality: "hd"` 2 credits/s (20/40/60), comic 0.4 credit/s (4/8/12), charged at creation |
| `shotto_video_status` (`id`) | `GET /videos/{id}` | free |
| `shotto_approve_video` (`id`, `script?`) | `POST /videos/{id}/approve` | free (no extra charge) |
| `shotto_upload_url` (`type`, `size`, `filename?`) | `POST /uploads` | free |
| `shotto_create_edit` (`paths`, `seconds`, `options?`) | `POST /edits` | free seconds first, then 10 credits/min |
| `shotto_edit_status` (`id`) | `GET /edits/{id}` | free |
| `shotto_list_jobs` (`limit?`) | `GET /jobs?limit=20` | free |

## Create video: fields

| Field | Required | Values |
|---|---|---|
| `business_name` | yes | up to 80 chars, as it should be spoken |
| `vertical` | yes | clinic, fitness, food, realestate, education, ecommerce, home, professional, other |
| `brief` | yes | 10-600 chars, Hebrew |
| `target_seconds` | yes | 10, 20, 30 (decides the price) |
| `presenter_id` | unless faceless | uuid from presenters |
| `format` | no | `presenter` (default) or `faceless` (comic) |
| `quality` | no | `regular` (default, 1 credit/s) or `hd` (2 credits/s); presenter videos only, ignored for faceless |
| `presenter_gender` | no | female / male, the voice of a comic |
| `tone` | no | friendly (default), energetic, calm, professional |
| `cta_type` | no | leads (default), whatsapp (+`whatsapp`), website (+`website`), call |
| `cta` | no | custom Hebrew CTA sentence, up to 80 chars |

Script shape by length: 10 s = hook + 1 line; 20 s and 30 s = hook + 3 lines (30 s is filmed as 3 takes).
Budgets enforced by Shotto: hook up to 17 words, each line up to 15, hook + first line up to 22 words, whole 20 s
script up to 40 words, no digits (numbers in words), sentences up to 12 words. You do not write the script; Shotto
does, from the brief. Only facts from the brief are used, so a vague brief gives a vague script.

## Writing the brief

1. Ask the user for: business name, what they sell, who the customer is, one concrete fact or offer, the CTA
   (WhatsApp number / website / leave details / call).
2. Write 1-2 Hebrew sentences. One offer, not five. Numbers in words ("שלוש מאות שקל", not "300").
3. Do not add anything the user did not say: no prices, discounts, cities, guarantees, reviews or results.
4. Show the brief and the price together, and ask for confirmation before creating.

Examples:
- `מרפאת שיניים בפתח תקווה, הלבנת שיניים בטיפול אחד. בדיקה ראשונה בלי עלות.`
- `חנות אונליין לקפה טרי קלוי, משלוח עד הבית תוך יומיים. מאה גרם ראשונים במתנה בהזמנה ראשונה.`

## Statuses

Video: `queued` → `scripting` → `awaiting_approval` → `approved` → `generating` → `assembling` → `qa` →
`in_review` (team check before release) → `done` (has `video_url`). Terminal: `failed` (credits return), `cancelled`,
`expired` (script not approved in time; credits return). Edit: `approved` → … → `done` / `failed`.
`video_url` is non-null only when `status = done`.

Typical times: the script in under a minute; after approval a video is ready in about 5 / 7 / 9 minutes
(10 / 20 / 30 s; `estimated_seconds` on create and approve); an edit takes about 2 minutes + 1 per minute of footage.
Under load it can take a few minutes longer.

Poll politely: every 20-30 s while waiting for the script, every 30-60 s while filming. Do not exceed the rate limit.

## Approving with edits

`script` = `{ "hook": "…", "lines": ["…", "…"] }` with exactly the same number of lines as the written script.
Small wording fixes only (a name, a word the user dislikes). Hebrew, no digits. A larger change → tell the user
it is a new video (new charge) or that they can request new scripts on the website before paying.

## Errors

Body: `{ "error": { "code": "…", "message": "<Hebrew>", "details"?: {…} } }`. MCP tool errors return the same JSON
with `isError: true`; MCP auth/rate errors are JSON-RPC errors (-32001 unauthorized, -32029 rate limit).

| HTTP | Code | What to do |
|---|---|---|
| 400 | `VALIDATION`, `BAD_REQUEST`, `BAD_SCRIPT`, `BAD_TYPE`, `EMPTY` | fix the input; message says what |
| 401 | `UNAUTHORIZED` | key missing/revoked: user creates one at /settings/api |
| 402 | `NO_CREDITS` | not enough credits: user buys at https://www.shottoai.com; never retry |
| 404 | `NOT_FOUND` | wrong id or another account's job |
| 409 | `NOT_READY` | script not written yet: keep polling, approve later |
| 409 | `NOT_AWAITING` | already approved, expired or failed: read the status |
| 413 | `TOO_BIG` | clip over 250 MB |
| 429 | `RATE_LIMIT` | wait `Retry-After` / `retry_after_seconds` |
| 500 | `SERVER_ERROR` | for a paid call: check `GET /jobs` before anything else; never auto-retry |

## Policies (from https://www.shottoai.com/ugc/terms)

- The script is approved before filming; after approval the video is produced as approved.
- A produced video is not remade or refunded for taste, style or pronunciation. Only a technical failure returns
  credits, automatically.
- The free 10 s teaser is once per phone number (plus one retry) and the ₪5 first full video is once per customer;
  both happen on the website, not through the API.
- The content of the script speaks for the business; the account owner is responsible for it.

## REST quickstart (curl)

```bash
export SHOTTO_API_KEY=sk_shotto_...
B=https://www.shottoai.com/api/v1
H="Authorization: Bearer $SHOTTO_API_KEY"

curl -s $B/me -H "$H"                                   # balance
curl -s "$B/presenters?gender=female" -H "$H"           # pick a presenter id

# after the user confirmed "20 s presenter video = 20 credits":
curl -s -X POST $B/videos -H "$H" -H "Content-Type: application/json" -d '{
  "business_name": "סטודיו נועה", "vertical": "fitness",
  "brief": "סטודיו פילאטיס בתל אביב לנשים. שיעור ניסיון ראשון במתנה.",
  "target_seconds": 20, "presenter_id": "<uuid>", "cta_type": "whatsapp", "whatsapp": "0501234567"
}'                                                      # → 201 { "id": "…", "credits_charged": 20, … }

curl -s $B/videos/<id> -H "$H"                          # until status = awaiting_approval; show script
curl -s -X POST $B/videos/<id>/approve -H "$H"          # only after the user approved the script
curl -s $B/videos/<id> -H "$H"                          # until status = done → video_url
```

Edit:

```bash
curl -s -X POST $B/uploads -H "$H" -H "Content-Type: application/json" \
  -d '{"type":"video/mp4","size":48211337,"filename":"clip.mp4"}'     # → path, upload_url (valid 2 h)
curl -X PUT -H "Content-Type: video/mp4" --data-binary @clip.mp4 "<upload_url>"
# after the user confirmed the cost:
curl -s -X POST $B/edits -H "$H" -H "Content-Type: application/json" \
  -d '{"paths":["<path>"],"seconds":42.5,"options":{"skin":"bold","captions":true,"music":true}}'
curl -s $B/edits/<id> -H "$H"                           # until done → video_url
```

MCP smoke test:

```bash
curl -s -X POST https://www.shottoai.com/mcp -H "$H" -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"shotto_balance","arguments":{}}}'
```
