API: Vault media and signed uploads
The Vault holds a creator's photos, videos and voice notes, sorted in folders. Each item is Free, Teaser or Paid with a Stars price. Files never travel through the API: you ask for a signed upload URL, send the file there, then confirm.
Endpoints #
| Method | Path | Description | Permission | Status |
|---|---|---|---|---|
| GET | /media | List media (creator_id= required, folder_id=, type=photo,video,voice, access=free,teaser,paid) | can_setup | Planned |
| POST | /media/uploads | Get a signed URL to upload one file (valid 15 minutes) | can_setup | Planned |
| POST | /media/uploads/{upload_id}/complete | Confirm the upload; the media is created and processed | can_setup | Planned |
| GET | /media/{id} | One media, its preview and where it is used | can_setup | Planned |
| PATCH | /media/{id} | Change its description, access, price or folder | can_setup | Planned |
| DELETE | /media/{id} | Delete it (refused while a script step uses it) | can_setup | Planned |
| GET | /media/folders | Folders of a creator's Vault | can_setup | Planned |
| POST | /media/folders | Create a folder | can_setup | Planned |
| PATCH | /media/folders/{id} | Rename or move a folder | can_setup | Planned |
| DELETE | /media/folders/{id} | Delete an empty folder | can_setup | Planned |
The media object #
{
"id": "med_5521",
"creator_id": "cr_lea",
"type": "photo",
"status": "ready",
"folder_id": "vf_3",
"access": "paid",
"stars": 300,
"description": "Beach, golden hour, white bikini",
"preview_url": "https://cdn.lanachat.ai/w/7Hq2/cr_lea/med_5521/preview.jpg",
"created_at": "2026-10-06T14:21:40Z"
}
| Field | Type | Description |
|---|---|---|
id | string | Starts with med_ |
creator_id | string | Whose Vault it is in |
type | string | photo, video or voice |
status | string | processing, ready or failed (with a reason) |
folder_id | string or null | Its Vault folder |
access | string | free, teaser or paid |
stars | number or null | Price in Stars, required when access is paid (1 to 100,000) |
description | string or null | Helps the AI pick the right media; never shown to the fan |
preview_url | string or null | A preview, once the media is ready |
created_at | string | ISO 8601, UTC |
Media #
List media #
GET /media · can_setup
| Parameter | Type | Required | Description |
|---|---|---|---|
creator_id | string | Yes | The creator |
folder_id | string | No | Only this folder |
type | string | No | photo, video or voice; several separated by commas |
access | string | No | free, teaser or paid; several separated by commas |
limit, cursor | number, string | No | Pagination |
curl "https://api.lanachat.ai/v1/media?creator_id=cr_lea&type=photo&access=paid" \
-H "Authorization: Bearer $LANACHAT_TOKEN"
{
"data": [
{ "id": "med_5522", "creator_id": "cr_lea", "type": "photo", "status": "ready", "folder_id": "vf_3", "access": "paid", "stars": 250, "description": "Mirror, black dress", "preview_url": "https://cdn.lanachat.ai/w/7Hq2/cr_lea/med_5522/preview.jpg", "created_at": "2026-10-06T14:25:10Z" },
{ "id": "med_5521", "creator_id": "cr_lea", "type": "photo", "status": "ready", "folder_id": "vf_3", "access": "paid", "stars": 300, "description": "Beach, golden hour, white bikini", "preview_url": "https://cdn.lanachat.ai/w/7Hq2/cr_lea/med_5521/preview.jpg", "created_at": "2026-10-06T14:21:40Z" }
],
"next_cursor": null
}
Without creator_id the API answers 400.
Upload a file #
Three calls: ask for a signed URL, send the file there, confirm.
Step 1. POST /media/uploads · can_setup
| Parameter | Type | Required | Description |
|---|---|---|---|
creator_id | string | Yes | Whose Vault receives it |
file_name | string | Yes | The file name, for example beach-01.jpg |
content_type | string | Yes | For example image/jpeg or video/mp4 |
size_bytes | number | Yes | File size, up to 2 GB |
folder_id | string | No | The Vault folder to put it in |
curl -X POST https://api.lanachat.ai/v1/media/uploads \
-H "Authorization: Bearer $LANACHAT_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"creator_id": "cr_lea", "file_name": "beach-01.jpg", "content_type": "image/jpeg", "size_bytes": 2483201, "folder_id": "vf_3"}'
{
"upload_id": "up_7781",
"url": "https://uploads.lanachat.ai/w/7Hq2/up_7781?X-Signature=…&X-Expires=1759760100",
"method": "PUT",
"headers": { "Content-Type": "image/jpeg" },
"expires_at": "2026-10-06T14:35:00Z"
}
The URL is valid 15 minutes.
Step 2. Send the file with PUT to url, with exactly the headers given (no Authorization header there).
curl -X PUT "https://uploads.lanachat.ai/w/7Hq2/up_7781?X-Signature=…&X-Expires=1759760100" \
-H "Content-Type: image/jpeg" --data-binary @beach-01.jpg
Step 3. POST /media/uploads/{upload_id}/complete · can_setup
curl -X POST https://api.lanachat.ai/v1/media/uploads/up_7781/complete \
-H "Authorization: Bearer $LANACHAT_TOKEN" \
-H "Idempotency-Key: $(uuidgen)"
{
"id": "med_5521",
"creator_id": "cr_lea",
"type": "photo",
"status": "processing",
"folder_id": "vf_3",
"access": "paid",
"stars": null,
"description": null,
"preview_url": null,
"created_at": "2026-10-06T14:21:40Z"
}
status goes from processing to ready (previews and the Telegram copy are made) or failed with a reason. A webhook media.ready tells you when, see Webhooks. Accepted: JPEG, PNG, HEIC, MP4, MOV, OGG and M4A, up to 2 GB.
Get a media #
GET /media/{id} · can_setup
curl https://api.lanachat.ai/v1/media/med_5521 \
-H "Authorization: Bearer $LANACHAT_TOKEN"
The answer is the media object, with its preview and where it is used. An unknown id answers 404.
Price and describe #
PATCH /media/{id} · can_setup
| Parameter | Type | Required | Description |
|---|---|---|---|
access | string | No | free, teaser or paid |
stars | number | When access is paid | 1 to 100,000 |
description | string | No | What it shows, for the AI |
folder_id | string or null | No | Move it to another folder |
curl -X PATCH https://api.lanachat.ai/v1/media/med_5521 \
-H "Authorization: Bearer $LANACHAT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"access": "paid", "stars": 300, "description": "Beach, golden hour, white bikini"}'
The answer is the updated media. stars is required when access is paid (1 to 100,000), otherwise the API answers 400. The description helps the AI pick the right media; it is never shown to the fan.
Delete a media #
DELETE /media/{id} · can_setup
curl -X DELETE https://api.lanachat.ai/v1/media/med_5521 \
-H "Authorization: Bearer $LANACHAT_TOKEN"
A media that a script step still uses is refused (409): take it out of the script first.
Folders #
List folders #
GET /media/folders · can_setup
| Parameter | Type | Required | Description |
|---|---|---|---|
creator_id | string | Yes | Whose Vault |
curl "https://api.lanachat.ai/v1/media/folders?creator_id=cr_lea" \
-H "Authorization: Bearer $LANACHAT_TOKEN"
Create a folder #
POST /media/folders · can_setup
| Parameter | Type | Required | Description |
|---|---|---|---|
creator_id | string | Yes | Whose Vault |
name | string | Yes | Folder name |
curl -X POST https://api.lanachat.ai/v1/media/folders \
-H "Authorization: Bearer $LANACHAT_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"creator_id": "cr_lea", "name": "Beach"}'
Rename or move a folder #
PATCH /media/folders/{id} · can_setup
curl -X PATCH https://api.lanachat.ai/v1/media/folders/vf_3 \
-H "Authorization: Bearer $LANACHAT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Beach, summer"}'
Delete a folder #
DELETE /media/folders/{id} · can_setup
curl -X DELETE https://api.lanachat.ai/v1/media/folders/vf_3 \
-H "Authorization: Bearer $LANACHAT_TOKEN"
Only an empty folder can be deleted; otherwise the API answers 409.