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
Section titled “Base URL”https://app.faylee.app/api/v1Paths 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
Section titled “Authentication”Every request sends a personal token in the Authorization header:
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:
{ "error": "Sign in to Faylee again" }What a token can do
Section titled “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.
Recording IDs
Section titled “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
Section titled “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 |
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 |
{ "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
Section titled “Errors”Every error response has one field, error, with a sentence you can show to a person:
{ "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
Section titled “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
Section titled “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.
GET /recording_links returns up to the 100 newest open links and is not paginated.
Endpoints
Section titled “Endpoints”| Method | Path | Page |
|---|---|---|
POST | /recordings | Uploading |
POST | /recordings/:id/complete | Uploading |
GET | /folders | Uploading |
GET | /recordings | Reading |
GET | /recordings/:id | Reading |
PATCH | /recordings/:id | Reading |
DELETE | /recordings/:id | Reading |
GET | /recordings/:id/capture | Reading |
GET | /recordings/:id/frames | Reading |
GET | /recordings/:id/transcript | Reading |
GET | /recordings/:id/attachments | Reading |
POST | /recordings/:id/attachments | Reading |
POST | /recordings/:id/emails | Reading |
GET | /integrations | Reading |
GET | /integrations/:id/fields | Reading |
POST | /recordings/:id/issues | Reading |
GET | /oembed, outside /api/v1 | Reading |
GET | /recording_links | Recording links |
POST | /recording_links | Recording links |
DELETE | /recording_links/:id | Recording links |
GET | /webhook_endpoints | Webhooks |
POST | /webhook_endpoints | Webhooks |
GET | /webhook_endpoints/:id | Webhooks |
PATCH | /webhook_endpoints/:id | Webhooks |
DELETE | /webhook_endpoints/:id | Webhooks |
POST | /webhook_endpoints/:id/secret | Webhooks |
Every recording carries a capture file, data.json. Its format is described in Capture format.