Skip to content

These endpoints read and change recordings in the token owner’s workspace. Every recording endpoint takes either the numeric id or the share_id from a share link as :id. A recording outside the workspace, or in the trash, gets 404.

GET /recordings lists the workspace’s ready recordings, newest first, 50 a page.

ParameterNotes
qMatches the title, description or page URL, case-insensitive
kindvideo, replay or screenshot
folderA folder id from GET /folders
tagOne tag
authorA member’s numeric user id
problemstrue for recordings with at least one error or failed request
statusTriage status: open, in_progress, resolved or wont_fix
priorityurgent, high, medium or low
assigneeThe assigned member’s numeric user id
sinceAn ISO 8601 time. Only recordings created at or after it
pagePage number, from 1

Unknown values for kind, status and priority are ignored rather than refused.

Terminal window
curl -G https://app.faylee.app/api/v1/recordings \
-H "Authorization: Bearer $FAYLEE_TOKEN" \
--data-urlencode "problems=true" \
--data-urlencode "since=2026-09-29T00:00:00Z"
{
"recordings": [
{
"id": 4812,
"share_id": "V1StGXR8_Z",
"kind": "video",
"title": "Checkout fails with a 500",
"status": "ready",
"error_count": 2,
"failed_request_count": 1,
"share_url": "https://app.faylee.app/s/V1StGXR8_Z",
"author": "Ann Park"
}
],
"page": 1,
"total_pages": 3
}

Each item is a full recording object. The example above is shortened. A since that is not an ISO 8601 time gets 422 with since must be an ISO 8601 time.

To poll for new recordings, pass the created_at of the newest one you have seen as since. To be told instead, subscribe to the recording.ready webhook.

GET /recordings/:id returns the recording object. Unlike the list, it also returns recordings that are still uploading or processing, or that failed.

Terminal window
curl https://app.faylee.app/api/v1/recordings/V1StGXR8_Z \
-H "Authorization: Bearer $FAYLEE_TOKEN"

PATCH /recordings/:id changes the title, description or who can open the recording, and returns the recording object.

FieldNotes
recording.title
recording.description
recording.visibilityworkspace or public. See below for password
Terminal window
curl -X PATCH https://app.faylee.app/api/v1/recordings/V1StGXR8_Z \
-H "Authorization: Bearer $FAYLEE_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "recording": { "visibility": "public" } }'

The API cannot set a share password. Setting password works only on a recording that already has a password from the app. Otherwise it gets 422 with Share password is needed for a password-protected link. A change of visibility is written to the workspace’s audit log, as it is in the app.

DELETE /recordings/:id moves the recording to the trash and returns 204. Only the recording’s author or a workspace admin can do this. Anyone else gets 403.

The recording is deleted for good after 30 days, unless someone restores it from the trash in the app.

GET /recordings/:id/capture returns the recording’s capture bundle, data.json, as it was uploaded. Its format is described in Capture format.

Terminal window
curl https://app.faylee.app/api/v1/recordings/V1StGXR8_Z/capture \
-H "Authorization: Bearer $FAYLEE_TOKEN" -o data.json

With format=evidence, the same endpoint returns text/plain: the capture written out for a language model to read. It starts with the kind, duration, title and description, the environment and any metadata the app attached. Then comes a timeline with one line per event, each starting with its time in milliseconds since the recording started:

Terminal window
curl "https://app.faylee.app/api/v1/recordings/V1StGXR8_Z/capture?format=evidence" \
-H "Authorization: Bearer $FAYLEE_TOKEN"
Terminal window
[t=4900ms] user clicked button "Pay now"

The timeline covers pages, actions, console messages, errors, requests, socket messages, page edits, the reporter’s marks and Web Vitals. Long values are shortened. When the text would pass 200,000 characters, the least telling events (plain logs, successful requests, measurements) are left out first, and a last line says how many. Stack frames that Faylee resolved through the site’s source maps point at the original source.

Both forms return 422 when the recording’s storage is no longer connected. The evidence form also returns 422 for a capture made with an older format version, or a damaged one.

GET /recordings/:id/frames returns still images from a video or a replay.

ParameterNotes
modeoverview (default), at or range
atFor mode=at: comma-separated times in milliseconds, up to 20
from, toFor mode=range: the window in milliseconds. Defaults to the whole recording
countFor mode=range: how many frames to spread across the window, 1 to 20, 5 by default
sizesmall, medium or large: 360, 720 or 1080 pixels high. medium by default

overview returns one image of 6 to 16 evenly spaced frames in a grid, each labelled with its time. Longer recordings get more frames.

{
"mode": "overview",
"size": "medium",
"columns": 4,
"rows": 2,
"t_ms": [3000, 9000, 15000, 21000, 27000, 33000, 39000, 45000],
"url": "https://storage.example.com/recordings/V1StGXR8_Z/frames/..."
}

at and range return one image per time. Times outside the recording are moved to its start or end.

Terminal window
curl "https://app.faylee.app/api/v1/recordings/V1StGXR8_Z/frames?mode=at&at=5000,12000&size=small" \
-H "Authorization: Bearer $FAYLEE_TOKEN"
{
"mode": "at",
"size": "small",
"frames": [
{ "t_ms": 5000, "url": "https://storage.example.com/..." },
{ "t_ms": 12000, "url": "https://storage.example.com/..." }
]
}

A screenshot returns its own image as a single frame at t_ms 0. A replay’s frames are drawn on the server by rebuilding the captured page at each moment. Images, fonts and stylesheets the recorded site loaded from its own hosts are missing from them.

URLs are presigned and expire after one hour. Frames are made once and kept, so asking again for the same times is fast. The endpoint returns 422, with a message pointing at the steps, console and network instead, when there is nothing to draw: a replay with no captured page, or a replay that takes too long to draw.

GET /recordings/:id/transcript returns what the reporter said while recording a video, as timed segments.

{
"status": "ready",
"language": "english",
"segments": [
{ "startMs": 100, "endMs": 900, "text": "Here it breaks", "words": [{ "text": "Here", "startMs": 100 }] }
]
}

status is pending, ready or failed, or null when there is no transcript. segments is empty until the transcript is ready, and when the recording has no narration. Times are on the recording’s clock. words may be absent.

GET /recordings/:id/attachments lists the files added to a recording, oldest first:

{
"attachments": [
{ "id": 31, "filename": "server.log", "content_type": "text/plain", "byte_size": 48213, "url": "https://storage.example.com/..." }
]
}

url is a presigned download link that expires after one hour. A screenshot shared with marks drawn on it has an attachment named screenshot-without-marks.png: the same image without the marks, with blurred areas kept. The marks themselves are in the capture’s annotations.

POST /recordings/:id/attachments adds one. Send its metadata, then PUT the bytes to the returned upload_url with the same Content-Type.

FieldNotes
attachment.filenameRequired, up to 200 characters
attachment.content_typeDefaults to application/octet-stream
attachment.byte_sizeGreater than 0, at most 25 MB
Terminal window
curl -X POST https://app.faylee.app/api/v1/recordings/V1StGXR8_Z/attachments \
-H "Authorization: Bearer $FAYLEE_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "attachment": { "filename": "server.log", "content_type": "text/plain", "byte_size": 48213 } }'
{ "id": 31, "filename": "server.log", "upload_url": "https://storage.example.com/...?X-Amz-Signature=..." }
Terminal window
curl -X PUT "$UPLOAD_URL" -H "Content-Type: text/plain" --data-binary @server.log

A recording can have 10 attachments. The eleventh gets 422.

POST /recordings/:id/emails emails the recording’s link to up to 10 people, from the token’s owner.

FieldNotes
emailsEmail addresses. Duplicates and blanks are dropped
noteOptional message included in the email
Terminal window
curl -X POST https://app.faylee.app/api/v1/recordings/V1StGXR8_Z/emails \
-H "Authorization: Bearer $FAYLEE_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "emails": ["dana@example.com"], "note": "This is the checkout bug from standup" }'

202 returns { "sent_to": ["dana@example.com"] }. Addresses are lowercased. An empty list, more than 10 addresses, or an invalid address gets 422.

File an issue in one of the workspace’s connected issue trackers. See Issue trackers for connecting one.

GET /integrations lists the trackers you can file to:

{
"trackers": [{ "id": 3, "provider": "linear", "name": "Linear" }],
"recent": [],
"available": [{ "provider": "github", "name": "GitHub" }],
"connect_url": "https://app.faylee.app/settings/integrations",
"can_connect": false
}
FieldNotes
trackersConnected trackers that are ready to use. File with their id
recentWhere the token’s owner filed last, with the fields they used
availableTrackers the workspace has not connected
connect_urlWhere an admin connects one
can_connectWhether the token’s owner is an admin

GET /integrations/:id/fields returns the choices a tracker offers, by field. Which fields appear depends on the tracker. A field’s values are { value, label } pairs.

{
"assignee": [{ "value": "u_81", "label": "Dana Ruiz" }],
"labels": [{ "value": "bug", "label": "bug" }],
"priority": [{ "value": "1", "label": "Urgent" }]
}

The response is {} when the tracker could not be reached.

POST /recordings/:id/issues:

FieldNotes
integration_idRequired. A tracker id from GET /integrations
issue.titleThe issue title
issue.bodyMarkdown
issue.fields.assigneeOptional. A value from the tracker’s fields
issue.fields.priorityOptional
issue.fields.labelsOptional. A list of values
Terminal window
curl -X POST https://app.faylee.app/api/v1/recordings/V1StGXR8_Z/issues \
-H "Authorization: Bearer $FAYLEE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"integration_id": 3,
"issue": {
"title": "Checkout fails with a 500",
"body": "Paying with a saved card returns a 500 from /api/orders.",
"fields": { "labels": ["bug"] }
}
}'

201 means the issue was created:

{ "id": 58, "status": "created", "key": "WEB-142", "url": "https://linear.app/acme/issue/WEB-142" }

Faylee adds a line with the recording’s share link to the end of the body.

202 with "status": "pending" means the tracker was down. Faylee keeps retrying, and key and url are null until it succeeds. When the tracker refuses the issue, the response is 422 with the tracker’s reason, and nothing is kept. The issue.created webhook is sent once the tracker has created the issue.

GET /oembed returns an embeddable player for a share link. It lives at the site root, not under /api/v1, and takes no token.

ParameterNotes
urlRequired. A share or embed link, such as https://app.faylee.app/s/V1StGXR8_Z
maxwidthOptional. At most 960
maxheightOptional. At most 540
formatOptional. Only json is supported
Terminal window
curl -G https://app.faylee.app/oembed --data-urlencode "url=https://app.faylee.app/s/V1StGXR8_Z"
{
"version": "1.0",
"type": "rich",
"provider_name": "Faylee",
"provider_url": "https://app.faylee.app/",
"title": "Checkout fails with a 500",
"author_name": "Ann Park",
"width": 960,
"height": 540,
"html": "<iframe src=\"https://app.faylee.app/embed/V1StGXR8_Z\" width=\"960\" height=\"540\" frameborder=\"0\" allow=\"fullscreen\" allowfullscreen></iframe>",
"thumbnail_url": "https://app.faylee.app/s/V1StGXR8_Z/thumbnail",
"thumbnail_width": 640
}

The player keeps a 16:9 shape inside the size you allow. thumbnail_url and thumbnail_width are present only when the recording has a thumbnail.

Only public recordings can be embedded. A recording that is not public, or whose share link has expired, gets 401. A link to another site or an unknown recording gets 404, and a format other than json gets 501. Links on a workspace’s verified link domain work too.

Share pages advertise this endpoint with a <link rel="alternate" type="application/json+oembed"> tag, so tools that support oEmbed discovery embed pasted share links on their own.