API and webhooks
Connect your own tools to your Lassu workspace. Your servers can read and manage videos, find the videos visitors send through recording buttons, and get a webhook the moment something changes.
Get started
- In Workspace settings › API & webhooks, choose Create key. Workspace admins can create keys.
- Copy the key into your server’s secret store. Lassu shows it only once.
- Send it as a bearer token. This request checks that the key works:
curl https://api.lassu.ai/v1/api/workspace \
-H "Authorization: Bearer $LASSU_API_KEY"{
"id": "0192…",
"name": "Acme",
"key": { "id": "0193…", "name": "Support tool", "access": "read" }
}Every request goes to https://api.lassu.ai/v1/api. Responses are JSON. Lists return { data, nextCursor }; to get the next page, pass nextCursor back as cursor.
API keys
- Full access
- Reads everything below, and can rename, share and trash videos, create and change recording buttons, and manage webhooks.
- Read only
- Reads videos, received videos, recording buttons and webhooks. Any request that would change something returns 403.
- A key belongs to one workspace and acts with the access of the admin who created it. If that person stops being an admin, their keys stop working. Create a new key from another admin.
- Keys only work with this API. They can’t sign in to Lassu or change members, billing or settings.
- Keep keys on your server. If a key leaks, revoke it in Workspace settings; it stops working immediately.
Videos
Every video in the workspace, newest first, including the ones visitors send through recording buttons.
| Request | Key | What it does |
|---|---|---|
GET /videos | Any key | List videos. Filters: recordingButtonId, createdAfter, createdBefore, limit (up to 100), cursor. |
GET /videos/:id | Any key | One video, with its summary, chapters and transcript. |
PATCH /videos/:id | Full access | Change the title, or set privacy to team or public. |
DELETE /videos/:id | Full access | Move a video to Trash. It can be restored in Lassu for 30 days. |
curl "https://api.lassu.ai/v1/api/videos?limit=25" \
-H "Authorization: Bearer $LASSU_API_KEY"{
"id": "0193…",
"title": "Onboarding walkthrough",
"status": "ready",
"durationMs": 184000,
"privacy": "team",
"createdAt": "2026-10-07T17:02:11.000Z",
"updatedAt": "2026-10-07T17:05:40.000Z",
"shareUrl": "https://www.lassu.ai/share/vid_0193…",
"source": "member",
"receivedVideoId": null
}curl -X PATCH https://api.lassu.ai/v1/api/videos/VIDEO_ID \
-H "Authorization: Bearer $LASSU_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "title": "Q4 kickoff", "privacy": "public" }'status is processing, ready or failed. source is recordingButton for a visitor’s video, and receivedVideoId then points at its received video.
Received videos
Videos visitors sent through your recording buttons, with who sent them, the page they came from and their details. A received video keeps its id when the visitor records a new take; videoId always points at the latest one.
| Request | Key | What it does |
|---|---|---|
GET /received-videos | Any key | List received videos. Filters: buttonId, submissionKey, receivedAfter, receivedBefore, limit, cursor. |
GET /received-videos/:id | Any key | One received video. |
To check whether someone sent a video for an item, look it up by the submission key you gave the button:
curl "https://api.lassu.ai/v1/api/received-videos?submissionKey=order-482" \
-H "Authorization: Bearer $LASSU_API_KEY"{
"data": [{
"id": "0193…",
"buttonId": "0192…",
"buttonName": "Customer feedback",
"submissionKey": "order-482",
"sender": { "email": "pat@example.com", "verifiedBy": "lassu" },
"details": { "plan": "pro" },
"signedDetails": {},
"pageUrl": "https://www.example.com/orders/482",
"receivedAt": "2026-10-07T17:02:11.000Z",
"videoId": "0193…",
"status": "ready",
"durationMs": 42000,
"shareUrl": "https://www.lassu.ai/share/vid_0193…"
}],
"nextCursor": null
}sender.email is set when the visitor confirmed it with Lassu (verifiedBy: "lassu") or your site signed it ("website"). signedDetails come from your server’s identity token and can’t be changed by the page.
Recording buttons
| Request | Key | What it does |
|---|---|---|
GET /recording-buttons | Any key | List the workspace’s buttons. |
GET /recording-buttons/:id | Any key | One button. |
POST /recording-buttons | Full access | Create a button: name, websites, and optionally prompt, whoCanRecord, maxDurationMs (whole minutes, 60000 to 3600000; up to 600000 on the Free plan), details. |
PATCH /recording-buttons/:id | Full access | Change any of those, or set state to active, paused or archived. |
curl -X POST https://api.lassu.ai/v1/api/recording-buttons \
-H "Authorization: Bearer $LASSU_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Bug reports",
"prompt": "Show us what went wrong.",
"websites": ["https://app.example.com"],
"whoCanRecord": "email"
}'whoCanRecord is email (visitors confirm their email), anonymous or external (your site signs who they are).
Webhooks
Add an endpoint in Workspace settings › API & webhooks, or with the API. Lassu then sends a signed POST to it for each event it subscribes to.
receivedVideo.ready- A visitor’s video, or a new take of it, is ready to watch. Includes the received video.
receivedVideo.deleted- A visitor deleted a video they sent. Includes its id, button and submission key.
ping- A test event, sent when you choose Send test event.
POST /lassu-webhooks
Content-Type: application/json
Lassu-Event: receivedVideo.ready
Lassu-Delivery: 0193…
Lassu-Signature: t=1791392531,v1=5f2b…
{
"id": "evt_9Qz…",
"type": "receivedVideo.ready",
"createdAt": "2026-10-07T17:02:13.000Z",
"workspaceId": "0192…",
"data": { "receivedVideo": { "id": "0193…", "submissionKey": "order-482", "videoId": "0193…", … } }
}- Reply with any 2xx status within 10 seconds. Lassu doesn’t follow redirects.
- If a delivery fails, Lassu tries again after 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours and 12 hours. After 30 failed attempts in a row the endpoint is paused; resume it in Workspace settings.
- An event can occasionally arrive more than once. Use its
idto ignore repeats, and the API to catch up after downtime. - Endpoint URLs must use https and be reachable from the internet.
| Request | Key | What it does |
|---|---|---|
GET /webhooks | Any key | List endpoints. |
POST /webhooks | Full access | Add an endpoint: url, events, optional description. The response includes its signing secret. |
PATCH /webhooks/:id | Full access | Change the url, events or description, or set enabled to turn it off or on. |
DELETE /webhooks/:id | Full access | Remove an endpoint. |
POST /webhooks/:id/test | Full access | Send a ping and return the result. |
POST /webhooks/:id/rotate-secret | Full access | Replace the signing secret. The old one stops working immediately. |
curl -X POST https://api.lassu.ai/v1/api/webhooks \
-H "Authorization: Bearer $LASSU_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/lassu-webhooks",
"events": ["receivedVideo.ready", "receivedVideo.deleted"]
}'Verify webhooks
Each request has a Lassu-Signature header: a timestamp and an HMAC-SHA256 of "<timestamp>.<raw body>" made with the endpoint’s signing secret. Check it before trusting the request.
import { createHmac, timingSafeEqual } from "node:crypto";
// Use the raw request body, before any JSON parsing.
export function verifyLassuWebhook(rawBody, signatureHeader, secret) {
const parts = Object.fromEntries(signatureHeader.split(",").map((part) => part.split("=")));
const timestamp = Number(parts.t);
// Reject old requests so a captured one can't be replayed.
if (!timestamp || Math.abs(Date.now() / 1000 - timestamp) > 300) return false;
const expected = createHmac("sha256", secret).update(`${timestamp}.${rawBody}`).digest("hex");
const received = Buffer.from(parts.v1 ?? "", "hex");
return received.length === expected.length / 2 && timingSafeEqual(received, Buffer.from(expected, "hex"));
}Errors and limits
{
"error": {
"code": "API_KEY_READ_ONLY",
"message": "This key is read-only. Use a full-access key to make changes."
}
}- 400
- The request isn’t valid. The message says what to change.
- 401
- The key is missing, revoked, or its creator is no longer an admin.
- 403
- The key is read-only, or the change isn’t allowed in this workspace.
- 404
- Nothing with that id exists in the workspace.
- 409
- A limit was reached, or something changed at the same time. Try again.
- 429
- Too many requests. Each key can make 180 requests a minute; wait and retry.