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

> ## Agent Instructions
> Akapulu Labs builds live AI avatars on real-time calls. Two shapes: (1) a hosted link — public page Akapulu Labs hosts; open URL, Start Call; page never sees the API key; (2) your voice pipeline (speech-to-speech or any stack that produces assistant audio) plus Akapulu Labs avatar video in a Daily room you create. Calls use conversation minutes (akapulu.com/pricing). For agent onboarding (skills, MCP, account, which path to take), follow https://docs.akapulu.com/agent-setup.md.
> Hosted links: create in the dashboard or Bearer CRUD on /api/hosted-links/ (GET/POST/DELETE collection; GET/PUT/DELETE by id). Create body is exactly label, avatar_id, system_prompt, show_ai_label. Response includes url and token.
> Plugin path: Akapulu Labs uses Daily. On a new room, create_akapulu_call from pipecat-akapulu runs on their server. It reads DAILY_API_KEY and AKAPULU_API_KEY, creates a private Daily room, mints the three meeting tokens (browser, voice bot, Akapulu Labs worker), and POST /api/live-sessions/. It returns the room URL, the bot token, the browser token, and session_id. The room lasts two hours unless they pass room_exp_secs. A bot that already has a Daily room mints the worker token and POSTs avatar_id, show_ai_label, room_url, and avatar_token itself. That POST starts the Akapulu Labs worker and returns session_id. The code is on Your bot. AkapuluAvatar (pipecat-akapulu) runs inside the voice bot after assistant audio and sends that audio to the Akapulu Labs worker. The worker joins the call as a Daily participant and publishes the audio and the video, so voice-bot audio/video out stay off. The voice bot unsubscribes from the Avatar microphone when that participant joins. The browser stays subscribed to it. Public example akapulu-examples uses Deepgram Flux (`flux-general-multi`), OpenAI `gpt-5.4-mini`, and Cartesia Sonic 3.6.
> Every avatar frame has an invisible mark; verify with POST /api/video-marks/ then POST /api/video-marks/<id>/read/. API errors include error_code — branch on that field, not the English string.

# Read a video mark

> Score an uploaded mp4 and delete it.

`POST /api/video-marks/<uuid>/read/`

Scores the object from [create](https://docs.akapulu.com/api-reference/video-marks/create.md), deletes it, and returns the result. The body is `{}`.

Required headers:

* `Authorization: Bearer <YOUR_AKAPULU_API_KEY>`
* `Content-Type: application/json`

A 1280×720 picture is scored at that size. Any other size is scored as-is and again after a scale to 1280×720. The response is the score with the higher `separation`.

```json theme={null}
{
  "id": "11111111-2222-3333-4444-555555555555",
  "match": true,
  "bits_correct": 56,
  "bits": 56,
  "separation": 3.65,
  "width": 1280,
  "height": 720,
  "scored_width": 1280,
  "scored_height": 720,
  "shift": { "dy": 0, "dx": 0 },
  "message": "akapulu",
  "mean_coef_one": 3.6,
  "mean_coef_zero": -3.6,
  "samples_per_bit_min": 3936
}
```

`match` is true when the packed bits are `akapulu`. `message` is that text when every byte is printable, otherwise `null`. `width` and `height` are the file. `scored_width` and `scored_height` are the size that won.

A second read is `409` `ALREADY_READ`. A missing upload is `404` `NOT_FOUND`, and that check stays unread. A body that is not empty is `400` `VALIDATION`. An mp4 that cannot be read is `400` `VALIDATION`, and the object stays. A file over 200MB is `400` `VALIDATION`, and the object stays. If the scored object cannot be deleted, the call is `500` `INTERNAL` and the check stays unread.

Another user's check, or an unknown id, is `404`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.