---
title: "Overview"
description: "Base URL, authentication with personal tokens, the recording object, errors and an index of every public Faylee API endpoint."
canonical: https://faylee.app/docs/api/overview
---

# Overview

The Faylee API lets scripts, internal tools and agents upload recordings, read them, open recording links and manage webhook endpoints. It is a JSON API over HTTPS. Request and response keys are snake_case.

## Base URL

```bash
https://app.faylee.app/api/v1
```

Paths on these pages are relative to this base. The one exception, oEmbed, is marked.

The API accepts cross-origin browser requests only from the Faylee extension. Call it from a server, a script or a CLI, not from JavaScript running on another website.

## Authentication

Every request sends a personal token in the `Authorization` header:

```bash
curl https://app.faylee.app/api/v1/recordings \
  -H "Authorization: Bearer $FAYLEE_TOKEN"
```

To create a token, open **Settings → Browsers and tokens** in Faylee, give the token a name and create it. Faylee shows the token once. Only a SHA-256 digest of it is stored, so copy it before you leave the page. If you lose it, create a new one.

Tokens do not expire. Revoke a token from the same settings page when you no longer need it. The page shows when each token was last used.

A missing, unknown or revoked token gets `401`:

```json
{ "error": "Sign in to Faylee again" }
```

### What a token can do

A token acts as the person who created it, with exactly their access. There are no scopes.

| Action | Who can do it |
| --- | --- |
| List and read recordings, captures, frames, transcripts and attachments | Any member, for recordings in their workspace |
| Upload a recording | Any member. Only the person who started an upload can complete it |
| Update a recording, add attachments, email it, file an issue | Any member, for recordings in their workspace |
| Move a recording to the trash | The recording's author, or a workspace admin |
| Open, list and turn off recording links | Any member |
| Manage webhook endpoints | Workspace admins only |

A request for something the token's owner may not do gets `403`. A recording, link or endpoint outside the owner's workspace gets `404`, so the API does not reveal that it exists.

:::caution
A token can read every recording its owner can read. Store it like a password, and give each script a token of its own so you can revoke one without breaking the others.
:::

## Recording IDs

Wherever a path takes a recording `:id`, you can pass either the numeric `id` or the `share_id`. The `share_id` is the last segment of a share link, so `https://app.faylee.app/s/V1StGXR8_Z` is recording `V1StGXR8_Z`.

## The recording object

Endpoints that return a recording use this shape.

| Field | Type | Notes |
| --- | --- | --- |
| `id` | integer | |
| `share_id` | string | 10 characters. Also accepted as `:id` in paths |
| `kind` | string | `video`, `replay` or `screenshot` |
| `title` | string or null | |
| `description` | string or null | |
| `status` | string | `uploading`, `processing`, `ready` or `failed`. See [processing states](https://faylee.app/docs/api/uploading#processing-states) |
| `visibility` | string | `workspace`, `public` or `password` |
| `duration_ms` | integer or null | |
| `page_url` | string or null | The first page captured |
| `error_count` | integer | Uncaught errors plus console errors |
| `failed_request_count` | integer | |
| `tags` | string[] | Lowercase |
| `reporter_name` | string or null | Set for recordings sent through a recording link |
| `reporter_email` | string or null | Set for recordings sent through a recording link |
| `created_at` | string | ISO 8601 |
| `share_url` | string | The recording's share link. Uses the workspace's own link domain when it has a verified one |
| `author` | string | The member who recorded it, or who made the recording link it came through |
| `similar` | object[] | Other recordings in the workspace that failed in some of the same ways. Each has `share_id`, `title` and `share_url` |
| `analysis` | object or null | The assistant's write-up, once it has read the recording. See below |

`analysis` has these fields:

| Field | Type |
| --- | --- |
| `title` | string |
| `summary` | string |
| `actual_behavior` | string |
| `expected_behavior` | string |
| `likely_cause` | string |
| `next_steps` | string[] |
| `moments` | `{ atMs, label }[]`, where `atMs` is milliseconds into the recording |

```json
{
  "id": 4812,
  "share_id": "V1StGXR8_Z",
  "kind": "video",
  "title": "Checkout fails with a 500",
  "description": null,
  "status": "ready",
  "visibility": "workspace",
  "duration_ms": 48200,
  "page_url": "https://shop.example.com/checkout",
  "error_count": 2,
  "failed_request_count": 1,
  "tags": ["checkout"],
  "reporter_name": null,
  "reporter_email": null,
  "created_at": "2026-09-30T14:03:11.000Z",
  "share_url": "https://app.faylee.app/s/V1StGXR8_Z",
  "author": "Ann Park",
  "similar": [],
  "analysis": null
}
```

## Errors

Every error response has one field, `error`, with a sentence you can show to a person:

```json
{ "error": "Media content type is not included in the list" }
```

| Status | When |
| --- | --- |
| `401` | The token is missing, unknown or revoked |
| `403` | The token's owner may not do this, for example trash someone else's recording, or manage webhooks without being an admin |
| `404` | The record does not exist or is outside the workspace. The message is `Not found` |
| `422` | A parameter is missing or invalid, a validation failed (messages are joined into one sentence), the workspace has no storage, or an upload could not be completed |
| `426` | An outdated Faylee extension or CLI sent the request. See below |

### Client versions

Faylee's own extension and CLI name themselves in an `X-Faylee-Client` header, such as `cli/0.4.0`. When that version is older than the server supports, the server answers `426` with instructions to update. Requests without the header are served normally, so your scripts do not need to send it.

## Pagination

`GET /recordings` is paginated: 50 recordings a page, with `page` and `total_pages` in the response. Pass `page` to get another page. See [Reading recordings](https://faylee.app/docs/api/reading#list-recordings).

`GET /recording_links` returns up to the 100 newest open links and is not paginated.

## Endpoints

| Method | Path | Page |
| --- | --- | --- |
| `POST` | `/recordings` | [Uploading](https://faylee.app/docs/api/uploading#1-create-the-recording) |
| `POST` | `/recordings/:id/complete` | [Uploading](https://faylee.app/docs/api/uploading#4-complete-the-upload) |
| `GET` | `/folders` | [Uploading](https://faylee.app/docs/api/uploading#choose-a-folder) |
| `GET` | `/recordings` | [Reading](https://faylee.app/docs/api/reading#list-recordings) |
| `GET` | `/recordings/:id` | [Reading](https://faylee.app/docs/api/reading#get-a-recording) |
| `PATCH` | `/recordings/:id` | [Reading](https://faylee.app/docs/api/reading#update-a-recording) |
| `DELETE` | `/recordings/:id` | [Reading](https://faylee.app/docs/api/reading#trash-a-recording) |
| `GET` | `/recordings/:id/capture` | [Reading](https://faylee.app/docs/api/reading#capture-data) |
| `GET` | `/recordings/:id/frames` | [Reading](https://faylee.app/docs/api/reading#frames) |
| `GET` | `/recordings/:id/transcript` | [Reading](https://faylee.app/docs/api/reading#transcript) |
| `GET` | `/recordings/:id/attachments` | [Reading](https://faylee.app/docs/api/reading#attachments) |
| `POST` | `/recordings/:id/attachments` | [Reading](https://faylee.app/docs/api/reading#attachments) |
| `POST` | `/recordings/:id/emails` | [Reading](https://faylee.app/docs/api/reading#email-a-recording) |
| `GET` | `/integrations` | [Reading](https://faylee.app/docs/api/reading#issues) |
| `GET` | `/integrations/:id/fields` | [Reading](https://faylee.app/docs/api/reading#issues) |
| `POST` | `/recordings/:id/issues` | [Reading](https://faylee.app/docs/api/reading#issues) |
| `GET` | `/oembed`, outside `/api/v1` | [Reading](https://faylee.app/docs/api/reading#oembed) |
| `GET` | `/recording_links` | [Recording links](https://faylee.app/docs/api/recording-links#list-open-links) |
| `POST` | `/recording_links` | [Recording links](https://faylee.app/docs/api/recording-links#open-a-link) |
| `DELETE` | `/recording_links/:id` | [Recording links](https://faylee.app/docs/api/recording-links#turn-a-link-off) |
| `GET` | `/webhook_endpoints` | [Webhooks](https://faylee.app/docs/api/webhooks#manage-endpoints-over-the-api) |
| `POST` | `/webhook_endpoints` | [Webhooks](https://faylee.app/docs/api/webhooks#manage-endpoints-over-the-api) |
| `GET` | `/webhook_endpoints/:id` | [Webhooks](https://faylee.app/docs/api/webhooks#manage-endpoints-over-the-api) |
| `PATCH` | `/webhook_endpoints/:id` | [Webhooks](https://faylee.app/docs/api/webhooks#manage-endpoints-over-the-api) |
| `DELETE` | `/webhook_endpoints/:id` | [Webhooks](https://faylee.app/docs/api/webhooks#manage-endpoints-over-the-api) |
| `POST` | `/webhook_endpoints/:id/secret` | [Webhooks](https://faylee.app/docs/api/webhooks#manage-endpoints-over-the-api) |

Every recording carries a capture file, `data.json`. Its format is described in [Capture format](https://faylee.app/docs/api/capture-format.md).

:::tip
Coding agents can read the same recordings through the [MCP server](https://faylee.app/docs/agents/mcp.md), which signs in with OAuth instead of a token.
:::
