agent-skill-layerproof plugin for Cursor
Public API export (X-API-KEY). Export PNG ZIP, PPTX, or video (async), get status, cancel. PublicApiExportController.
# Skill: Export Slides
## Description
Export presentations as PNG ZIP, PPTX, or **narrated video** (Pro / credits). This skill documents the **public API** at `/api/v2/projects/{projectId}/exports` (PublicApiExportController). Exports are async: POST returns `export_id` (same id used for job-style polling); poll GET `.../exports/{export_id}` for status; when COMPLETED, use `download_url` (presigned, ~1 hour). Authenticate with `X-API-KEY` header.
---
## TypeScript types (request / response)
Mirrors `PublicApiExportController` data classes.
```typescript
// --- Export PNG / PPTX / Video (POST) — 202 ---
type PublicExportStartedResponse = {
export_id: string; // UUID — poll GET .../exports/{export_id}
status?: string; // default "PENDING"
};
// --- Video export body (POST .../exports/video) — optional ---
type PublicVideoExportRequest = {
quality?: string | null; // export quality enum as returned by API
include_notes?: boolean; // default false
voice?: string | null; // default "Puck"
language_code?: string | null; // default "en-US"
};
// --- Get Export Status (GET) ---
type PublicExportProgress = { current: number; total: number };
type PublicExportStatusData = {
export_id: string;
status: string;
format: string;
download_url?: string | null;
expires_at?: string | null;
file_size_bytes?: number | null;
progress?: PublicExportProgress | null;
error_message?: string | null;
};
type PublicExportStatusResponse = { data: PublicExportStatusData };
// --- Cancel Export (POST) ---
type PublicCancelExportResponse = {
export_id: string;
status: string; // "CANCELLED" or "ALREADY_TERMINAL"
};
```
---
## Export PNG ZIP (async)
Response (202): `PublicExportStartedResponse`. Poll GET .../exports/{exportId} for status.
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/exports/png" \
-H "X-API-KEY: $LAYERPROOF_API_KEY"
```
---
## Export PPTX (async)
Response (202): `PublicExportStartedResponse`. Poll GET .../exports/{exportId} for status.
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/exports/pptx" \
-H "X-API-KEY: $LAYERPROOF_API_KEY"
```
---
## Export Video (async)
Narrated video with TTS. May return **402** (quota) or **403** (Pro plan required). Optional JSON body: `PublicVideoExportRequest`.
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/exports/video" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"include_notes":false,"voice":"Puck","language_code":"en-US"}'
```
---
## Get Export Status
Response: `PublicExportStatusResponse`. When status is COMPLETED, `data.downloadUrl` and `data.expiresAt` are set.
```bash
curl "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/exports/<export_id>" \
-H "X-API-KEY: $LAYERPROOF_API_KEY"
```
---
## Cancel Export
Response: `PublicCancelExportResponse`.
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/exports/<export_id>/cancel" \
-H "X-API-KEY: $LAYERPROOF_API_KEY"
```
---
## Agent behavior
When the user asks to export a project (PNG ZIP or PPTX) or check/cancel an export, do the following.
### 1. Choose the right endpoint
Base path: `/api/v2/projects/{projectId}/exports`. All require `projectId`.
| User intent | Endpoint | Method |
|-------------|----------|--------|
| Export as PNG ZIP | `.../exports/png` | POST |
| Export as PPTX | `.../exports/pptx` | POST |
| Export as video (TTS) | `.../exports/video` | POST |
| Get export status / download URL | `.../exports/{exportId}` | GET |
| Cancel in-progress export | `.../exports/{exportId}/cancel` | POST |
### 2. Build and run
- **Auth**: Include `X-API-KEY: $LAYERPROOF_API_KEY`. Read env vars; if missing, tell the user.
- **Path**: Resolve projectId from context; exportId from the 202 response of export PNG/PPTX.
- Run curl and show the result.
### 3. After starting export
- Response contains `export_id`. Tell the user to poll GET .../exports/{export_id} until status is COMPLETED (or FAILED). When COMPLETED, use `data.download_url` (valid ~1 hour).
### 4. Response handling
- Always show raw JSON in a code block.
- If `download_url` is present, show the URL and mention the file can be downloaded.
- On 410, download URL expired; suggest triggering a new export.
### 5. Example workflows
**Workflow A — User**: "Export my project as PPTX and give me the download link."
1. Resolve projectId. POST `.../projects/{projectId}/exports/pptx`; capture `export_id` from 202 response.
2. Poll GET `.../projects/{projectId}/exports/{export_id}` until `data.status` is COMPLETED or FAILED. If FAILED, show `data.error_message` and suggest retry or checking project/slides.
3. When COMPLETED, show `data.download_url` and `data.expires_at`; remind user the URL is temporary (~1 hour).
**Workflow B — User**: "Export the same project as both PNG ZIP and PPTX; if one fails, still get the other."
1. POST `.../exports/png`; get `export_id_png`. POST `.../exports/pptx`; get `export_id_pptx`.
2. Poll both: GET `.../exports/{export_id_png}` and GET `.../exports/{export_id_pptx}` (e.g. in sequence or inform user to poll). For each: when COMPLETED, show `download_url`; when FAILED, show `error_message` and which format failed.
3. Optionally: if user wants to cancel the slower one, POST `.../exports/{export_id}/cancel` for the relevant export id.
**Workflow C — User**: "Start a PPTX export; I might cancel it if it takes too long."
1. POST `.../exports/pptx`; get `export_id`. Tell user polling has started.
2. If user says "cancel the export" before COMPLETED: POST `.../exports/{export_id}/cancel`; show response (status CANCELLED or ALREADY_TERMINAL).
3. If not canceled: poll until COMPLETED and show `download_url`, or FAILED and show `error_message`.
---
## Response format (required)
- (if response contains url to show image) please show image and show json response instead of table
- Always show the **raw JSON response** (verbatim) in a JSON code block.
- If the response contains a URL for an image, **render/show the image** and also show the **JSON response** (do not convert to a table).Public API job status polling (X-API-KEY). Poll async operations (outline, slides, exports, etc.) by activityId. Types follow PublicApiJobController (/api/v2/jobs/{activityId}).
# Skill: Job Monitoring
## Description
Poll the status of asynchronous operations. This skill documents the **public API** at `/api/v2/jobs/{activityId}` (PublicApiJobController). The `activityId` is returned when you start async operations (e.g. outline/generate, batch-generate, export PNG/PPTX). Authenticate with `X-API-KEY` header.
---
## TypeScript types (request / response)
Mirrors `PublicApiJobController` response.
```typescript
// --- Get Job Status (GET) ---
type JobStatusResponse = {
activity_id: string;
status: 'SCHEDULED' | 'RUNNING' | 'PENDING' | 'DONE' | 'CANCELED';
message: string;
created_at?: string | null; // ISO 8601
updated_at?: string | null; // ISO 8601
live_object_id?: string | null; // when derivable from activity input
output?: Record<string, unknown> | null; // structured map when DONE
failure_reason?: string | null;
};
```
---
## Get Job Status
Response: `JobStatusResponse`.
Poll until `status` is `DONE` or `CANCELED`. When DONE, use `output` (object map when present) for results; when failed, use `failure_reason`.
```bash
curl "$LAYERPROOF_BASE_URL/api/v2/jobs/<activity_id>" \
-H "X-API-KEY: $LAYERPROOF_API_KEY"
```
---
## Agent behavior
When the user asks to check job status or poll an async operation, do the following.
### 1. Endpoint
| User intent | Endpoint | Method |
|-------------|----------|--------|
| Check/poll job status | `/api/v2/jobs/{activityId}` | GET |
### 2. Build and run
- **Auth**: Include `X-API-KEY: $LAYERPROOF_API_KEY`. Read env vars; if missing, tell the user.
- **Path**: Use the `activity_id` returned from a previous async call (outline generate, batch generate, export, etc.). Run curl and show the result.
### 3. After response
- If status is SCHEDULED/RUNNING/PENDING, suggest polling again after a few seconds.
- If DONE, use `output` (object) if present and summarize the result.
- If CANCELED or DONE with `failure_reason`, report the outcome.
### 4. Response handling
- Always show the **raw JSON response** in a JSON code block.
- On 404, job not found or user does not own the associated project.
### 5. Example workflows
**Workflow A — User**: "Check status of job [activity_id]."
1. GET `/api/v2/jobs/{activityId}`; show JSON. If SCHEDULED/RUNNING/PENDING, suggest re-polling in a few seconds. If DONE, show `output` (and summarize). If CANCELED or failure_reason, report and stop.
**Workflow B — User**: "I started an outline generate and a batch generate; poll both and tell me when they’re done or if either failed."
1. User provides two activity IDs (or they are in context from prior slide-deck calls). Poll GET `/api/v2/jobs/{activityId1}` and GET `/api/v2/jobs/{activityId2}`.
2. For each: if DONE, report success and optionally parse `output`; if CANCELED or DONE with `failure_reason`, report which job failed and the reason.
3. If both still in progress, suggest polling again; if one is DONE and the other not, report current state and continue polling the other until terminal.
**Workflow C — User**: "Wait for this job and then get the slide deck / campaign / export result."
1. Poll GET `/api/v2/jobs/{activityId}` until status is DONE or CANCELED. If failed, report and stop.
2. When DONE: depending on workflow type (from initial trigger), hand off to the right skill — e.g. slide-decks GET deck, social-campaigns GET campaign, exports GET export status — using IDs from context or from `output` (e.g. project_id, slide_deck_id, campaign_id, export_id) and show the resulting resource or download URL.
---
## Response format (required)
- (if response contains url to show image) please show image and show json response instead of table
- Always show the **raw JSON response** (verbatim) in a JSON code block.
- If the response contains a URL for an image, **render/show the image** and also show the **JSON response** (do not convert to a table).Public API project file management (X-API-KEY). Prepare upload/update, confirm, AI files, subdirectories, resolve assets/paths, preview URL, get, download, delete. PublicApiProjectFileController.
# Skill: Project Files
## Description
Manage files in project directories. This skill documents the **public API** at `/api/v2/projects/{projectId}` (PublicApiProjectFileController). Upload flow: 1) POST prepare with directoryId; 2) PUT file to `upload_url`; 3) POST confirm with `file_id`. All paths require `projectId`; prepare/prepare-update also need `directoryId`. Authenticate with `X-API-KEY` header.
---
## TypeScript types (request / response)
Mirrors `PublicApiProjectFileController` data classes.
```typescript
// --- Prepare upload (POST) — 201 ---
type PrepareFileUploadRequest = {
path: string;
file_name: string;
mime_type: string;
size: number;
};
type PrepareFileUploadResponse = {
upload_url: string;
file_id: string;
s3_key: string;
expires_at: string;
};
// --- Prepare update (POST) ---
type PrepareUpdateFileRequest = {
path: string;
mime_type?: string | null;
size?: number | null;
};
type PrepareUpdateFileResponse = {
upload_url: string;
file_id: string;
s3_key: string;
expires_at: string;
};
// --- Confirm (POST) ---
type ConfirmFileUploadRequest = { metadata?: Record<string, unknown> | null };
type FileResponse = {
id: string;
name: string;
file_type: string;
mime_type: string;
s3_key: string;
size: number;
uploaded_at: string;
uploaded_by: string;
status: string;
metadata: Record<string, unknown> | null;
};
// --- Download URL (GET), Get file (GET), Delete (DELETE) ---
type DownloadUrlResponse = {
download_url: string;
expires_at: string;
};
// --- Preview URL (POST .../preview-url) ---
type PreviewUrlResponse = {
preview_url: string;
expires_at: string;
};
// --- Create subdirectory (POST .../subdirectories) ---
type CreateDirectoryRequest = { name: string };
type CreateDirectoryResponse = { id: string; name: string };
// --- Resolve assets (POST .../resolve-assets) ---
type ResolveAssetsRequest = { paths: string[] };
type ResolvedAsset = {
original_path: string;
presigned_url: string | null;
error: string | null;
};
type ResolveAssetsResponse = { assets: ResolvedAsset[] };
// --- Resolve paths to IDs (POST .../resolve-paths-to-ids) ---
type ResolvePathsToIdsRequest = { paths: string[] };
type ResolvedPathToId = {
original_path: string;
file_id: string | null;
error: string | null;
};
type ResolvePathsToIdsResponse = { resolved: ResolvedPathToId[] };
// --- AI file (POST .../ai-files) — 201 ---
type CreateAiFileRequest = {
filename: string;
path: string;
workflow_type: string;
project_type_id?: string | null;
form_input_data?: Record<string, unknown> | null;
};
type CreateAiFileResponse = {
file_id: string;
filename: string;
workflow_type: string;
};
// --- Trigger / cancel AI file ---
type TriggerAiFileRequest = { form_input_data?: Record<string, unknown> | null };
type TriggerAiFileResponse = {
activity_id: string;
workflow_type: string;
live_object_id: string;
};
type CancelAiFileResponse = {
live_object_id: string;
cancelled_children: number;
};
```
---
## Prepare Project File Upload
Request body: `PrepareFileUploadRequest`. Response (201): `PrepareFileUploadResponse`.
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/directories/<directory_id>/files/prepare" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"path":"/","file_name":"doc.pdf","mime_type":"application/pdf","size":2048}'
```
---
## Prepare Project File Update
Request body: `PrepareUpdateFileRequest`. Response: `PrepareUpdateFileResponse`.
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/directories/<directory_id>/files/prepare-update" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"path":"/doc.pdf","mime_type":"application/pdf","size":2048}'
```
---
## Confirm Project File Upload
Request body: `ConfirmFileUploadRequest`. Response: `FileResponse`.
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/files/<file_id>/confirm" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"metadata":{}}'
```
---
## Get File Download URL
Response: `DownloadUrlResponse`.
```bash
curl "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/files/<file_id>/download-url" \
-H "X-API-KEY: $LAYERPROOF_API_KEY"
```
---
## Get File Details
Response: `FileResponse`.
```bash
curl "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/files/<file_id>" \
-H "X-API-KEY: $LAYERPROOF_API_KEY"
```
---
## Delete Project File
Response: 204 No Content.
```bash
curl -X DELETE "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/files/<file_id>" \
-H "X-API-KEY: $LAYERPROOF_API_KEY"
```
---
## Create Subdirectory
Creates a child directory under `directory_id`. Response (201): `CreateDirectoryResponse`.
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/directories/<directory_id>/subdirectories" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"name":"assets"}'
```
---
## Preview URL (HTML)
For HTML files: processes assets and returns a presigned preview URL.
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/directories/<directory_id>/files/<file_id>/preview-url" \
-H "X-API-KEY: $LAYERPROOF_API_KEY"
```
---
## Resolve Asset Paths to URLs
Body: `{ "paths": ["./img.png", "..."] }`. Response: `ResolveAssetsResponse`.
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/directories/<directory_id>/resolve-assets" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"paths":["/relative/path.png"]}'
```
---
## Resolve Paths to Live Object IDs
Body: `{ "paths": ["~/file.txt", "/abs/path"] }`. Response: `ResolvePathsToIdsResponse`.
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/directories/<directory_id>/resolve-paths-to-ids" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"paths":["/notes.txt"]}'
```
---
## Create AI File
Creates an `.ai` configuration file in the directory. Response (201): `CreateAiFileResponse`.
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/directories/<directory_id>/ai-files" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"filename":"workflow.ai","path":"/","workflow_type":"YOUR_WORKFLOW"}'
```
---
## Trigger AI File Workflow
Poll `GET /api/v2/jobs/{activity_id}` after.
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/ai-files/<ai_file_id>/trigger" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{}'
```
---
## Cancel AI File Workflow
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/ai-files/<ai_file_id>/cancel" \
-H "X-API-KEY: $LAYERPROOF_API_KEY"
```
---
## Agent behavior
When the user asks to manage project files (upload, update, confirm, download, delete), do the following.
### 1. Choose the right endpoint
Base path: `/api/v2/projects/{projectId}`. Replace `project_id`, `directory_id`, `file_id` as needed.
| User intent | Endpoint | Method |
|-------------|----------|--------|
| Get upload URL for new file | `.../directories/{directoryId}/files/prepare` | POST |
| Get upload URL to update file | `.../directories/{directoryId}/files/prepare-update` | POST |
| Confirm upload after PUT | `.../files/{fileId}/confirm` | POST |
| Get download URL | `.../files/{fileId}/download-url` | GET |
| Get file metadata | `.../files/{fileId}` | GET |
| Delete file | `.../files/{fileId}` | DELETE |
| Create subdirectory | `.../directories/{directoryId}/subdirectories` | POST |
| HTML preview URL | `.../directories/{directoryId}/files/{fileId}/preview-url` | POST |
| Resolve paths → presigned URLs | `.../directories/{directoryId}/resolve-assets` | POST |
| Resolve paths → file IDs | `.../directories/{directoryId}/resolve-paths-to-ids` | POST |
| Create AI file | `.../directories/{directoryId}/ai-files` | POST |
| Trigger AI file | `.../ai-files/{aiFileId}/trigger` | POST |
| Cancel AI file | `.../ai-files/{aiFileId}/cancel` | POST |
### 2. Build and run the request
- **Auth**: Include `X-API-KEY: $LAYERPROOF_API_KEY`. Read env vars; if missing, tell the user.
- **Path**: Resolve projectId, directoryId, fileId from context or user input.
- **POST**: Build JSON body from types above; run curl and show result.
- **GET/DELETE**: Build path only; run curl and show result.
### 3. Upload flow
Prepare → PUT file to `upload_url` → confirm with `file_id`. Then use `file_id` for get/download/delete.
### 4. Response handling
- Always show raw JSON in a code block; show image + JSON if image URL present.
- On error, show body and status code.
### 5. Example workflows
**Workflow A — User**: "Upload a PDF to project X in the root directory."
1. Resolve projectId and directoryId (e.g. root directory from project or list directories). POST `.../directories/{directoryId}/files/prepare` with `{"path":"/","file_name":"brief.pdf","mime_type":"application/pdf","size":<bytes>}`; get `upload_url`, `file_id`, `s3_key`.
2. Tell user to PUT the file to `upload_url` with `Content-Type: application/pdf`. Then POST `.../files/{file_id}/confirm` with `{}` (or `{"metadata":{}}`).
3. Show JSON; use `file_id` for later get/download/delete.
**Workflow B — User**: "Upload two reference docs to the project, then use them when generating the slide deck outline."
1. For each file: prepare with path (e.g. `/ref1.pdf`, `/ref2.docx`) → user uploads to `upload_url` → confirm. Capture both `file_id` and `s3_key` (if returned) for each.
2. If outline generation accepts project file references: pass the project file identifiers (e.g. s3_keys or file_ids) into the slide-deck outline/generate request (e.g. `file_s3_keys` or equivalent). Otherwise use public-files for outline references and keep project files for project-scoped use only.
3. Hand off to slide-decks: POST outline/generate with the resolved keys; poll job; get deck.
**Workflow C — User**: "Update an existing file in the project (new version) and get a download URL after."
1. Resolve projectId, directoryId, and existing file path. POST `.../directories/{directoryId}/files/prepare-update` with `{"path":"/brief.pdf","mime_type":"application/pdf","size":<new_bytes>}`; get `upload_url`, `file_id`.
2. User PUTs new content to `upload_url`; then POST `.../files/{file_id}/confirm`. GET `.../files/{file_id}/download-url` to show the user a temporary download link.
3. On error (e.g. path not found), show response body and suggest verifying path and project/directory IDs.
---
## Response format (required)
- (if response contains url to show image) please show image and show json response instead of table
- Always show the **raw JSON response** (verbatim) in a JSON code block.
- If the response contains a URL for an image, **render/show the image** and also show the **JSON response** (do not convert to a table).Public API project management (X-API-KEY). Create, list, get, update, delete, clone, vote, visibility, recent/deleted/restore. Cursor pagination. Types follow PublicApiProjectController (/api/v2/projects).
# Skill: Projects Management
## Description
Manage Layerproof projects (create, list, get, update, delete, clone, vote, public list, visibility, recent, soft-delete restore). This skill documents the **public API** at `/api/v2/projects` (PublicApiProjectController). Authenticate with `X-API-KEY` header. **List** uses cursor pagination (`limit`, `cursor`). **List deleted** uses offset pagination (`page`, `page_size`).
---
## Requirements
Environment variables:
- `LAYERPROOF_BASE_URL`
- `LAYERPROOF_API_KEY`
All requests must include:
- `X-API-KEY: $LAYERPROOF_API_KEY`
---
## TypeScript types (request / response)
Mirrors `PublicApiProjectController` data classes.
```typescript
// --- Create (POST) — 201 ---
type PublicApiCreateProjectRequest = {
name: string; // required, 1–255 chars
description?: string | null; // max 10000 chars
status?: string | null; // e.g. "DRAFT"; defaults to DRAFT if omitted
tags?: string[] | null;
metadata?: Record<string, any> | null;
project_kind?: "SLIDE_DECK" | "GENERIC" | "MINDMAP" | "THEME" | "BLOG_POST" | "SOCIAL_CAMPAIGN"; // default: SLIDE_DECK
workspace_id?: string | null; // UUID
theme_id?: string | null; // UUID; optional theme for slide deck
aspect_ratio?: string | null; // e.g. "16:9"
};
// --- Update (PUT) ---
type PublicApiUpdateProjectRequest = {
name?: string | null; // 1–255 chars
description?: string | null; // max 10000 chars
status?: string | null;
tags?: string[] | null;
metadata?: Record<string, any> | null;
workspace_id?: string | null; // UUID
is_public?: boolean | null;
};
// --- Clone (POST /{projectId}/clone) — 201 ---
type CloneProjectRequest = {
name?: string | null; // defaults to "Copy of <source name>"
workspace_id?: string | null; // UUID; if null, auto-creates hidden workspace
};
// --- Project response ---
type PublicApiProjectResponse = {
id: string;
name: string;
description: string | null;
status: string; // e.g. "ACTIVE", "DRAFT"
project_kind: string; // e.g. "SLIDE_DECK"
metadata: Record<string, any>;
slide_deck_id: string | null;
workspace_id?: string | null; // UUID
thumbnail_url?: string | null; // optional preview image URL
slide_deck_type?: string | null; // e.g. "PRESENTATION", "SOCIAL_POST", "WEBPAGE", "VIDEO"
tags: string[];
created_at: string; // ISO 8601
updated_at: string; // ISO 8601
is_public: boolean;
vote_count: number;
has_voted?: boolean | null;
};
// --- List response (cursor-paginated) ---
type PublicApiProjectListResponse = {
data: PublicApiProjectResponse[];
next_cursor?: string | null; // pass as `cursor` param for next page
has_more?: boolean;
};
// --- Recent projects (GET /list/recent) ---
type PublicApiRecentProjectsResponse = {
data: PublicApiProjectResponse[];
};
// --- Update visibility only (PUT /{project_id}/visibility) ---
type PublicApiUpdateProjectVisibilityRequest = {
is_public: boolean; // required
};
```
---
## List Projects
Query params: `limit` (default 20, max 100), `cursor` (from previous `next_cursor`).
```bash
curl "$LAYERPROOF_BASE_URL/api/v2/projects?limit=20" \
-H "X-API-KEY: $LAYERPROOF_API_KEY"
```
Example JSON response:
```json
{"data":[{"id":"2b12d232-eca6-4161-96df-9f954fbbb36f","name":"Exit Strategy Overview","description":"...","status":"ACTIVE","project_kind":"SLIDE_DECK","metadata":{},"slide_deck_id":"ab2c64a5-0b59-45ec-baef-1d5b68dbf5fe","created_at":"2026-03-09T03:15:51.548959Z","updated_at":"2026-03-09T03:16:17.069810Z","is_public":false,"vote_count":0,"has_voted":null}],"next_cursor":null,"has_more":false}
```
---
## Get Project
```bash
curl "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>" \
-H "X-API-KEY: $LAYERPROOF_API_KEY"
```
Example JSON response:
```json
{"id":"2b12d232-eca6-4161-96df-9f954fbbb36f","name":"Exit Strategy Overview","description":"...","status":"ACTIVE","project_kind":"SLIDE_DECK","metadata":{"original_prompt":"Create an exit strategy overview"},"slide_deck_id":"ab2c64a5-0b59-45ec-baef-1d5b68dbf5fe","created_at":"2026-03-09T03:15:51.548959Z","updated_at":"2026-03-09T03:16:17.069810Z","is_public":false,"vote_count":0,"has_voted":false}
```
---
## Create Project
Request body: `PublicApiCreateProjectRequest`. Response (201): `PublicApiProjectResponse`.
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/projects" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{
"name": "<project_name>",
"description": "<description>",
"project_kind": "SLIDE_DECK"
}'
```
Example JSON response:
```json
{"id":"68f8270f-f36a-4817-8d0b-2c05efcd1c9a","name":"tmp-skill-project","description":"tmp","status":"DRAFT","project_kind":"SLIDE_DECK","metadata":{},"slide_deck_id":"a877d0c0-9514-40cc-8d27-cee3a1e73934","created_at":"2026-03-10T07:07:46.211172Z","updated_at":"2026-03-10T07:07:46.211176Z","is_public":false,"vote_count":0,"has_voted":null}
```
---
## Update Project
Request body: `PublicApiUpdateProjectRequest`. Only provided fields are updated. Response: `PublicApiProjectResponse`.
```bash
curl -X PUT "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"name":"<new_name>","description":"<new_description>","is_public":true}'
```
---
## Delete Project
Soft-deletes the project and all associated resources. Response: 204 No Content.
```bash
curl -X DELETE "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>" \
-H "X-API-KEY: $LAYERPROOF_API_KEY"
```
---
## List Public Projects
Query params: `limit` (default 20, max 100), `cursor`, `slide_deck_type` (one of `PRESENTATION`, `SOCIAL_POST`, `WEBPAGE`, `VIDEO`; optional).
```bash
curl "$LAYERPROOF_BASE_URL/api/v2/projects/public?limit=20" \
-H "X-API-KEY: $LAYERPROOF_API_KEY"
```
---
## Vote for a Project
Records a vote on a public project. Response: 204 No Content.
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/vote" \
-H "X-API-KEY: $LAYERPROOF_API_KEY"
```
---
## Remove Vote from a Project
Removes a vote from a public project. Response: 204 No Content.
```bash
curl -X DELETE "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/vote" \
-H "X-API-KEY: $LAYERPROOF_API_KEY"
```
---
## Clone Project
Clones an owned or public project into a new private project. Request body: `CloneProjectRequest`. Response (201): `PublicApiProjectResponse`.
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/clone" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"name":"<cloned_project_name>","workspace_id":"<workspace_id>"}'
```
---
## Update Project Visibility
Sets public or private without sending a full project update. Request body: `PublicApiUpdateProjectVisibilityRequest`. Response: `PublicApiProjectResponse`.
```bash
curl -X PUT "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/visibility" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"is_public":true}'
```
---
## List Recent Projects
Query: `limit` (default 4, max 100). Response: `PublicApiRecentProjectsResponse`.
```bash
curl "$LAYERPROOF_BASE_URL/api/v2/projects/list/recent?limit=8" \
-H "X-API-KEY: $LAYERPROOF_API_KEY"
```
---
## List Deleted Projects
Soft-deleted projects for the current user. Query: `page` (default 0), `page_size` (default 20). Response: `PublicApiProjectListResponse` (cursor fields may be absent; use `has_more`).
```bash
curl "$LAYERPROOF_BASE_URL/api/v2/projects/deleted?page=0&page_size=20" \
-H "X-API-KEY: $LAYERPROOF_API_KEY"
```
---
## Restore Deleted Project
Restores a soft-deleted project. Response (200): `{"restored": true}`.
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/restore" \
-H "X-API-KEY: $LAYERPROOF_API_KEY"
```
---
## Permanently Delete Project
Hard-deletes a project that is already soft-deleted. Response: 204 No Content.
```bash
curl -X DELETE "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/permanently" \
-H "X-API-KEY: $LAYERPROOF_API_KEY"
```
---
## Agent behavior
When the user asks to manage projects, do the following.
### 1. Choose the right endpoint
| User intent | Endpoint | Method |
|-------------|----------|--------|
| List own projects | `/api/v2/projects?limit=20` | GET |
| Get project by ID | `/api/v2/projects/{projectId}` | GET |
| Create project | `/api/v2/projects` | POST |
| Update project | `/api/v2/projects/{projectId}` | PUT |
| Delete project | `/api/v2/projects/{projectId}` | DELETE |
| List public projects | `/api/v2/projects/public?limit=20` | GET |
| Vote for a project | `/api/v2/projects/{projectId}/vote` | POST |
| Remove vote | `/api/v2/projects/{projectId}/vote` | DELETE |
| Clone a project | `/api/v2/projects/{projectId}/clone` | POST |
| Set visibility only | `/api/v2/projects/{projectId}/visibility` | PUT |
| Recent projects | `/api/v2/projects/list/recent?limit=8` | GET |
| List deleted projects | `/api/v2/projects/deleted?page=0&page_size=20` | GET |
| Restore deleted project | `/api/v2/projects/{projectId}/restore` | POST |
| Permanently delete (after soft delete) | `/api/v2/projects/{projectId}/permanently` | DELETE |
### 2. Build and run the request
- **Auth**: Include `X-API-KEY: $LAYERPROOF_API_KEY`. Read `LAYERPROOF_BASE_URL` and `LAYERPROOF_API_KEY` from the environment; if missing, tell the user to set them.
- **GET list**: Use `limit` (default 20, max 100) and `cursor` for pagination. Pass `next_cursor` from a previous response as `cursor` to fetch the next page.
- **POST/PUT**: Build JSON body from the appropriate request type. Run curl and show result.
- **DELETE project / vote**: Build path; run curl. Response is 204 with no body.
### 3. Response handling
- Always show the **raw JSON response** in a JSON code block.
- For 204 responses, indicate success and no body.
- On error, show the response body and status code.
### 4. Example workflows
**Workflow A — User**: "Create a project called Q3 Roadmap."
1. Choose POST `/api/v2/projects`.
2. Body: `{"name":"Q3 Roadmap","project_kind":"SLIDE_DECK"}`.
3. Run curl; show JSON. The returned `id` is the project ID; `slide_deck_id` is the associated slide deck.
**Workflow B — User**: "Create a slide deck project in workspace W, then generate an outline for it."
1. Resolve `workspace_id` (e.g. from GET workspaces or user). POST `/api/v2/projects` with `{"name":"...", "project_kind":"SLIDE_DECK", "workspace_id":"<workspace_id>"}`; capture `id` and `slide_deck_id`.
2. Hand off to slide-decks skill: use `projectId = id` and `slideDeckId = slide_deck_id`; POST `.../projects/{projectId}/slide-deck/{slideDeckId}/outline/generate` with prompt and slide_count; poll jobs; get deck or update outline as needed.
**Workflow C — User**: "List my projects, find the one named 'Launch Deck', export it as PPTX, and if there’s a clone or vote option show me how."
1. GET `/api/v2/projects` with `limit` and optional `cursor`; paginate if needed. Locate project where `name` matches "Launch Deck"; capture `id`.
2. Hand off to exports skill: POST `.../projects/{id}/exports/pptx`; poll GET `.../exports/{exportId}` until COMPLETED; show `downloadUrl`.
3. Optionally: POST `.../projects/{id}/clone` to clone, or POST/DELETE `.../projects/{id}/vote` per API; show response and explain usage.
---
## Response format (required)
- (if response contains url to show image) please show image and show json response instead of table
- Always show the **raw JSON response** (verbatim) in a JSON code block.
- If the response contains a URL for an image, **render/show the image** and also show the **JSON response** (do not convert to a table).Public API reference files (X-API-KEY). Prepare upload, confirm, delete, get download URL. Use s3_key from prepare in outline generation. Types follow PublicApiFileController (/api/v2/files).
# Skill: Public Files
## Description
Upload reference files (images, documents) for use in outline generation. This skill documents the **public API** at `/api/v2/files` (PublicApiFileController). Flow: 1) POST /prepare → get `upload_url` and `s3_key`; 2) PUT file to `upload_url`; 3) POST /confirm; use `s3_key` in `file_s3_keys` when calling slide-deck outline/generate. Authenticate with `X-API-KEY` header. Max 20 MB; allowed types: PDF, DOCX, PPTX, TXT, MD, PNG, JPEG, WebP, SVG, BMP, TIFF, HEIC, HEIF, AVIF, ICO.
---
## TypeScript types (request / response)
Mirrors `PublicApiFileController` (/api/v2/files) data classes.
```typescript
// --- Prepare (POST) ---
type PrepareFileUploadRequest = {
file_name: string; // max 255 chars
mime_type: string;
size: number; // 1 to 20*1024*1024
};
type PrepareFileUploadResponse = {
upload_url: string;
s3_key: string;
expires_at: string; // ISO 8601, ~10 min
};
// --- Confirm (POST) ---
type PublicApiConfirmFileUploadRequest = { s3_key: string };
type PublicApiConfirmFileUploadResponse = { s3_key: string; uploaded: boolean };
// --- Delete (POST) ---
type PublicApiDeleteFileRequest = { s3_key: string };
type PublicApiDeleteFileResponse = { success: boolean };
// --- Download URL (POST) ---
type PublicApiDownloadUrlRequest = {
s3_key: string;
expiry_seconds?: number; // 60–604800, default 3600
};
type PublicApiDownloadUrlResponse = {
download_url: string;
expires_at: string;
};
```
---
## Prepare Public File Upload
Request body: `PrepareFileUploadRequest`. Response: `PrepareFileUploadResponse`. Then PUT file to `upload_url` with matching Content-Type.
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/files/prepare" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"file_name":"doc.pdf","mime_type":"application/pdf","size":1024}'
```
---
## Confirm Public File Upload
Request body: `PublicApiConfirmFileUploadRequest`. Response: `PublicApiConfirmFileUploadResponse`.
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/files/confirm" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"s3_key":"<s3_key_from_prepare>"}'
```
---
## Delete Public File
Request body: `PublicApiDeleteFileRequest`. Response: `PublicApiDeleteFileResponse`.
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/files/delete" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"s3_key":"<s3_key>"}'
```
---
## Get Public File Download URL
Request body: `PublicApiDownloadUrlRequest`. Response: `PublicApiDownloadUrlResponse`.
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/files/download-url" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"s3_key":"<s3_key>","expiry_seconds":3600}'
```
---
## Agent behavior
When the user asks to upload or manage reference files (for outlines), do the following.
### 1. Choose the right endpoint
| User intent | Endpoint | Method |
|-------------|----------|--------|
| Get upload URL for a file | `/api/v2/files/prepare` | POST |
| Confirm file uploaded to S3 | `/api/v2/files/confirm` | POST |
| Delete a file by s3_key | `/api/v2/files/delete` | POST |
| Get download URL for a file | `/api/v2/files/download-url` | POST |
### 2. Build and run the request
- **Auth**: Include `X-API-KEY: $LAYERPROOF_API_KEY`. Read `LAYERPROOF_BASE_URL` and `LAYERPROOF_API_KEY` from the environment; if missing, tell the user to set them.
- **Body**: All endpoints use JSON body (`file_name`, `mime_type`, `size` for prepare; `s3_key` for confirm/delete/download-url). Run curl and show the result.
### 3. Upload flow
After prepare: tell the user to PUT the file to `upload_url` with `Content-Type: <mime_type>`, then call confirm with the returned `s3_key`. Use that `s3_key` in `file_s3_keys` when calling slide-deck outline/generate.
### 4. Response handling
- Always show the **raw JSON response** in a JSON code block.
- If the response contains a URL for an image, show the image and the JSON.
- On error, show the response body and status code.
### 5. Example workflows
**Workflow A — User**: "Upload a reference PDF for outline generation."
1. POST `/api/v2/files/prepare` with `{"file_name":"brief.pdf","mime_type":"application/pdf","size":<bytes>}`; get `upload_url`, `s3_key`, `expires_at`.
2. User PUTs file to `upload_url` with `Content-Type: application/pdf`. POST `/api/v2/files/confirm` with `{"s3_key":"<s3_key>"}`.
3. Use this `s3_key` in slide-deck POST `.../outline/generate` body as `file_s3_keys: ["<s3_key>"]`.
**Workflow B — User**: "Upload two reference files (PDF + DOCX), confirm both, then generate an outline that uses them."
1. For first file: prepare → user uploads → confirm; capture `s3_key_1`. For second: prepare → upload → confirm; capture `s3_key_2`.
2. Hand off to slide-decks: resolve projectId and slideDeckId. POST `.../outline/generate` with `{"prompt":"...", "slide_count":5, "file_s3_keys":["<s3_key_1>","<s3_key_2>"]}`.
3. Poll jobs for `activity_id`; when DONE, GET deck to show outline that was informed by the references.
**Workflow C — User**: "Get a download URL for a file I uploaded earlier (I have the s3_key), then delete it when done."
1. POST `/api/v2/files/download-url` with `{"s3_key":"<s3_key>","expiry_seconds":3600}`; show `download_url` and `expires_at`.
2. When user is done: POST `/api/v2/files/delete` with `{"s3_key":"<s3_key>"}`; show success response. Warn that delete is irreversible.
---
## Response format (required)
- (if response contains url to show image) please show image and show json response instead of table
- Always show the **raw JSON response** (verbatim) in a JSON code block.
- If the response contains a URL for an image, **render/show the image** and also show the **JSON response** (do not convert to a table).Public API slide deck operations (X-API-KEY). Generate outlines, update outline, get deck, batch generate slides, generate transcript/image/content, cancel, theme, transcript, duplicate section. Types follow PublicApiSlideDeckController (/api/v2/projects/{projectId}/slide-deck/{slideDeckId}).
# Skill: Slide Deck Generation
## Description
Manage AI-generated slide decks and outlines. This skill documents the **public API** at `/api/v2/projects/{projectId}/slide-deck/{slideDeckId}` (PublicApiSlideDeckController). All endpoints require `projectId` and `slideDeckId` in the path. Authenticate with `X-API-KEY` header. Poll async operations via `GET /api/v2/jobs/{activityId}`.
---
## TypeScript types (request / response)
Mirrors `PublicApiSlideDeckController` data classes.
```typescript
// --- Outline section (used in UpdateOutlineRequest and responses) ---
type PublicApiSlideIconAsset = { query: string; slot?: string | null };
type PublicApiSlideImageAsset = { prompt: string; slot?: string | null };
type PublicApiOutlineSection = {
id: string;
section_title: string; // 1–500 chars
content?: string | null; // max 5000
key_points?: string[] | null;
visual_suggestion?: string | null; // max 500
speaker_notes?: string | null; // max 2000
url_references?: string | null;
layout?: string | null;
icon_assets?: PublicApiSlideIconAsset[];
image_assets?: PublicApiSlideImageAsset[];
reference_image_paths?: string[];
slide_intent?: Record<string, unknown> | null;
};
// --- Generate Outline (POST) — async ---
type GenerateOutlineRequest = {
prompt: string; // 3–2000 chars, required
slide_count?: number; // default 5, min 1
language?: string; // 2–10 chars, e.g. "en"
file_s3_keys?: string[]; // from POST /api/v2/files/prepare
web_search_enabled?: boolean;
text_detail_level?: string | null;
tone?: string | null;
};
type GenerateOutlineResponse = {
activity_id: string;
status: string;
message: string;
estimated_completion_seconds?: number;
};
// --- Update Outline (PUT) ---
type UpdateOutlineRequest = {
title: string; // 1–500 chars
sections: PublicApiOutlineSection[]; // at least one required
};
type PublicApiOutline = {
id: string;
title: string;
sections: PublicApiOutlineSection[];
total_sections: number;
suggested_slide_count?: number | null;
updated_at?: string;
};
type UpdateOutlineResponse = { outline: PublicApiOutline };
// --- Get Deck (GET) ---
type PublicApiSlideDeck = {
id: string;
project_id: string;
title: string;
description?: string | null;
deck_type?: string | null;
slide_numbers?: number | null;
aspect_ratio: string;
theme?: string | null;
outline_generation_live_object_id?: string | null;
created_at: string;
updated_at: string;
};
type PublicApiSlide = {
id: string;
index: number;
section_id: string;
section_title: string;
content?: string | null;
key_points?: string[] | null;
visual_suggestion?: string | null;
speaker_notes?: string | null;
transcript?: string | null;
image_url?: string | null;
image_expires_at?: string | null;
generation_status: string;
error_message?: string | null;
created_at: string;
updated_at: string;
};
type PublicApiDeckMetadata = {
total_slides: number;
completed_slides: number;
pending_slides: number;
overall_progress: number;
};
type GetDeckResponse = {
slide_deck: PublicApiSlideDeck;
outline: PublicApiOutline;
slides: PublicApiSlide[];
metadata: PublicApiDeckMetadata;
};
// --- Batch Generate Slides (POST) — async ---
type BatchGenerateSlidesRequest = {
generation_type?: string; // default "BOTH"
speaking_style?: string | null;
target_duration_minutes?: number | null; // min 1
transcript_tone?: string | null;
aspect_ratio?: string | null;
output_language?: string | null; // 2–10 chars
text_detail_level?: string | null;
tone?: string | null;
};
type BatchGenerateSlidesResponse = {
activity_id: string;
total_slides: number;
status: string;
message: string;
estimated_completion_seconds: number;
};
// --- Generate Slide Content / Transcript / Image (POST) — async ---
type GenerateSlideContentRequest = {
slide_section_id: string;
speaking_style?: string | null;
target_duration_minutes?: number | null; // min 1
transcript_tone?: string | null;
generation_type?: string | null; // default "TRANSCRIPT_AND_IMAGE"
};
type GenerateSlideContentResponse = {
activity_id: string;
slide_section_id: string;
transcript_gen_live_object_id?: string | null;
image_gen_live_object_id?: string | null;
status: string;
message: string;
};
type GenerateSlideTranscriptRequest = {
slide_section_id: string;
speaking_style?: string | null;
target_duration_minutes?: number | null; // min 1
transcript_tone?: string | null;
};
type GenerateSlideTranscriptResponse = {
activity_id: string;
slide_section_id: string;
transcript_gen_live_object_id: string;
status: string;
message: string;
estimated_completion_seconds?: number;
};
type GenerateSlideImageRequest = {
slide_section_id: string;
aspect_ratio?: string; // "16:9" | "4:3" | "1:1", default "16:9"
};
type GenerateSlideImageResponse = {
activity_id: string;
slide_section_id: string;
image_gen_live_object_id: string;
status: string;
message: string;
estimated_completion_seconds?: number;
};
// --- Cancel Generation (POST) ---
type CancelGenerationRequest = { live_object_ids?: string[] | null };
type CancelGenerationResponse = {
cancelled_live_objects: string[];
failed_to_cancel_live_objects: string[];
total_activities_cancelled: number;
message: string;
};
// --- Generate Theme (POST) — async ---
type GenerateThemeRequest = {
prompt: string; // 10–2000 chars
reference_image_paths?: string[] | null;
};
type GenerateThemeResponse = {
activity_id: string;
status: string;
message: string;
estimated_completion_seconds: number;
};
// --- Update Transcript (PUT) ---
type UpdateTranscriptRequest = { transcript: string };
type UpdateTranscriptResponse = {
slide_section_id: string;
version_id: string;
version_number: number;
transcript_gen_live_object_id: string;
transcript: string;
updated_at: string;
};
// --- Duplicate Section (POST) ---
type DuplicateSectionRequest = {
copy_content?: boolean;
insert_after?: boolean;
};
type DuplicateSectionResponse = {
new_section_id: string;
new_section: PublicApiOutlineSection;
transcript_gen_live_object_id?: string | null;
image_gen_live_object_id?: string | null;
message: string;
};
```
---
## Generate Outline (async)
Request body: `GenerateOutlineRequest`. Response (202): `GenerateOutlineResponse`.
Starts outline generation from a prompt. Use `file_s3_keys` from files uploaded via `/api/v2/files/prepare` and confirm. Poll `GET /api/v2/jobs/{activityId}` for status.
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/slide-deck/<slide_deck_id>/outline/generate" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"prompt":"Create a product launch deck","slide_count":6}'
```
With reference files and language:
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/slide-deck/<slide_deck_id>/outline/generate" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"prompt":"Q4 strategy","slide_count":5,"language":"en","file_s3_keys":["public-api/.../file.pdf"],"web_search_enabled":true}'
```
---
## Update Outline
Request body: `UpdateOutlineRequest`. Response: `UpdateOutlineResponse`.
Updates outline title and sections (add, remove, reorder). At least one section required.
```bash
curl -X PUT "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/slide-deck/<slide_deck_id>/outline" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"title":"My Deck","sections":[{"id":"section-uuid","section_title":"Intro","content":"...","speaker_notes":"..."}]}'
```
---
## Get Full Slide Deck
Query: `include_images` (default true), `image_expiry_seconds` (default 3600). Response: `GetDeckResponse`.
Returns deck, outline, slides with presigned image URLs, and metadata.
```bash
curl "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/slide-deck/<slide_deck_id>/?include_images=true&image_expiry_seconds=3600" \
-H "X-API-KEY: $LAYERPROOF_API_KEY"
```
---
## Batch Generate Slides (async)
Request body: `BatchGenerateSlidesRequest`. Response (202): `BatchGenerateSlidesResponse`.
Generate transcript and/or images for all outline sections. Requires an outline first. Poll jobs with `activity_id`.
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/slide-deck/<slide_deck_id>/slides/batch-generate" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"generation_type":"BOTH"}'
```
---
## Generate Slide Content (async)
Request body: `GenerateSlideContentRequest`. Response (202): `GenerateSlideContentResponse`.
Generate transcript and image for one section. Poll `GET /api/v2/jobs/{activityId}`.
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/slide-deck/<slide_deck_id>/slides/generate-content" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"slide_section_id":"<section_uuid>"}'
```
---
## Generate Slide Transcript (async)
Request body: `GenerateSlideTranscriptRequest`. Response (202): `GenerateSlideTranscriptResponse`.
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/slide-deck/<slide_deck_id>/slides/generate-transcript" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"slide_section_id":"<section_uuid>"}'
```
---
## Generate Slide Image (async)
Request body: `GenerateSlideImageRequest`. Response (202): `GenerateSlideImageResponse`.
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/slide-deck/<slide_deck_id>/slides/generate-image" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"slide_section_id":"<section_uuid>","aspect_ratio":"16:9"}'
```
---
## Cancel Generation
Request body: `CancelGenerationRequest`. Response: `CancelGenerationResponse`.
Provide `live_object_ids` from active generation responses to cancel.
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/slide-deck/<slide_deck_id>/cancel" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"live_object_ids":["<live_object_uuid>"]}'
```
---
## Generate Deck Theme (async)
Request body: `GenerateThemeRequest`. Response (202): `GenerateThemeResponse`.
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/slide-deck/<slide_deck_id>/theme/generate" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"prompt":"Minimal corporate blue"}'
```
---
## Update Slide Transcript
Request body: `UpdateTranscriptRequest`. Response: `UpdateTranscriptResponse`.
Path includes `slideSectionId`: `PUT .../slides/{slideSectionId}/transcript`.
```bash
curl -X PUT "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/slide-deck/<slide_deck_id>/slides/<slide_section_id>/transcript" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"transcript":"Updated speaker notes."}'
```
---
## Duplicate Slide Section
Request body: `DuplicateSectionRequest`. Response: `DuplicateSectionResponse`.
Path includes `slideSectionId`: `POST .../slides/{slideSectionId}/duplicate`.
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/slide-deck/<slide_deck_id>/slides/<slide_section_id>/duplicate" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"copy_content":true,"insert_after":true}'
```
---
## Agent behavior
When the user asks to work with slide decks (outline, deck, slides, theme, transcript), do the following.
### 1. Choose the right endpoint
Base path: `/api/v2/projects/{projectId}/slide-deck/{slideDeckId}`. All require `projectId` and `slideDeckId`.
| User intent | Endpoint | Method |
|-------------|----------|--------|
| Generate outline from prompt | `.../outline/generate` | POST |
| Update outline structure | `.../outline` | PUT |
| Get full deck (outline + slides + URLs) | `.../` (with optional query) | GET |
| Batch generate all slides | `.../slides/batch-generate` | POST |
| Generate one slide content | `.../slides/generate-content` | POST |
| Generate one slide transcript | `.../slides/generate-transcript` | POST |
| Generate one slide image | `.../slides/generate-image` | POST |
| Cancel generations | `.../cancel` | POST |
| Generate deck theme | `.../theme/generate` | POST |
| Update slide transcript | `.../slides/{slideSectionId}/transcript` | PUT |
| Duplicate section | `.../slides/{slideSectionId}/duplicate` | POST |
| Improve section (AI) | `.../slides/improve` | POST |
| Manual slide generation | `.../slides/generate-manual` | POST |
| Patch visual style description | `.../theme/visual-style-description` | PATCH |
| List / restore transcript versions | `.../slides/{id}/transcript/versions` (GET), `.../versions/{version_id}/restore` (POST) | GET / POST |
| Mark transcript / improvement / live object read | `.../mark-transcript-read`, `.../mark-improvement-read`, `.../live-objects/{id}/mark-read` | PATCH |
| Text-to-speech | `.../slides/{id}/generate-tts` (POST), `.../slides/{audio_id}/audio/download-url` (GET) | POST / GET |
| Update deck settings (aspect ratio, etc.) | `.../` (slide-deck root) | PATCH |
| Tone settings | `.../tone-settings` | GET / PUT |
| Batch Konva layout | `.../slides/batch-generate-layout` | POST |
| PPTX import | `.../import/prepare-upload`, `.../import` | POST |
| Citations | `.../citations`, `.../citations/slide/{index}`, `.../citations/{citation_id}` | GET |
### 2. Build and run the request
- **Auth**: Every request must include `X-API-KEY: $LAYERPROOF_API_KEY`. Read `LAYERPROOF_BASE_URL` and `LAYERPROOF_API_KEY` from the environment; if missing, tell the user to set them.
- **Path**: Resolve `projectId` and `slideDeckId` (and `slideSectionId` where needed) from context or user input. If missing, ask.
- **GET**: Build curl with path and query params (`include_images`, `image_expiry_seconds` for get deck). Run and show result.
- **POST/PUT**: Build JSON body from the types above. Use `-X POST` or `-X PUT`, `-H "Content-Type: application/json"`, and `-d '...'`. Run and show result.
### 3. After async endpoints
- Responses include `activity_id`. Tell the user the job was started and suggest polling `GET $LAYERPROOF_BASE_URL/api/v2/jobs/{activityId}` until `status` is `DONE` or `CANCELED`.
- Typical flow: generate outline → poll until DONE → get deck or update outline → batch generate slides or generate single slide content/transcript/image → poll jobs.
### 4. Response handling
- Always show the **raw JSON response** in a JSON code block; do not convert to a table.
- If the response contains image URLs (e.g. in get deck `slides[].image_url`), show images and the JSON.
- On error (4xx/5xx), show the response body and status code; suggest fixing API key, projectId/slideDeckId/sectionId, or request body.
### 5. Example workflows
**Workflow A — User**: "Generate an outline for a product launch deck in project X."
1. Resolve projectId and slideDeckId (e.g. from GET projects, then `project.slide_deck_id`).
2. Choose `POST .../outline/generate`.
3. Build body: `{"prompt":"Product launch deck","slide_count":5}`.
4. Run curl; show JSON. Tell user to poll `GET /api/v2/jobs/{activityId}` and then call get deck or update outline as needed.
**Workflow B — User**: "Full deck: outline from a prompt and reference PDF, then tweak the outline, apply a theme, batch generate slides, and fix one slide’s image."
1. Resolve projectId and slideDeckId. Get `file_s3_keys` from public-files (prepare → upload → confirm) or project-files if the API accepts them for outline.
2. POST `.../outline/generate` with `{"prompt":"Product launch with pricing","slide_count":6,"file_s3_keys":["<s3_key>"],"language":"en"}`; capture `activity_id`.
3. Poll `GET /api/v2/jobs/{activity_id}` until DONE. On failure, report and stop.
4. GET deck; from `outline.sections` identify a section to change. PUT `.../outline` with `title` and updated `sections` (e.g. edit `section_title`, `key_points`, `visual_suggestion` for one section).
5. If user wants a theme: use themes skill — **POST `/api/v2/themes/apply`** with `slide_deck_id` and `theme_id` (set `regenerate_slides` if images should be regenerated); poll job when `activity_id` is returned.
6. POST `.../batch-generate` with optional `generation_type`, `aspect_ratio`, `speaking_style`; capture `activity_id`.
7. Poll `GET /api/v2/jobs/{activity_id}` until DONE.
8. GET deck; check `metadata.completed_slides` and `slides[].generation_status`. If one slide’s image is wrong, POST `.../slides/{sectionId}/generate-image` (or generate-content) with section id; poll that job until DONE; GET deck again to show result.
**Workflow C — User**: "Duplicate a section in the outline and regenerate slides for the new section only."
1. GET deck; from `outline.sections` pick a `section_id` (UUID string) to duplicate.
2. POST `.../slides/{slide_section_id}/duplicate` with `{"copy_content":true,"insert_after":true}`; read `new_section_id` from the response.
3. PUT `.../outline` with updated `title` and `sections` array (merge/reorder the duplicated section as needed).
4. POST `.../slides/generate-transcript` | `generate-image` | `generate-content` with the new section id; poll `GET /api/v2/jobs/{activity_id}`; GET deck to confirm.
---
## Response format (required)
- (if response contains url to show image) please show image and show json response instead of table
- Always show the **raw JSON response** (verbatim) in a JSON code block.
- If the response contains a URL for an image, **render/show the image** and also show the **JSON response** (do not convert to a table).Public API slide editing (X-API-KEY). Edit slide images with AI, accept/revert edits, object removal, text extraction, save Konva nodes. Types follow PublicApiSlideController (/api/v2/projects/{projectId}/slides).
# Skill: Slide Editing
## Description
Modify slides after generation. This skill documents the **public API** at `/api/v2/projects/{projectId}/slides` (PublicApiSlideController). All slide endpoints require `projectId` and `slideId` in the path. Authenticate with `X-API-KEY` header.
---
## TypeScript types (request / response)
Mirrors `PublicApiSlideController` (/api/v2/projects/{projectId}/slides) data classes.
```typescript
// Shared: cropped region for region-based editing. Must lie fully within image bounds.
// Constraints: x >= 0, y >= 0, width > 0, height > 0; x + width <= image width, y + height <= image height.
type CroppedRegion = {
x: number;
y: number;
width: number;
height: number;
};
// --- Edit Slide Image (POST) — async, poll jobs ---
type SlideImageEditRequest = {
/** AI instruction for the edit (required, non-blank) */
instruction: string;
/** Path to input image in project working dir (required, non-blank) */
input_image_path: string;
/** Optional extra reference image paths */
other_reference_image_paths?: string[];
/** Optional region to edit; only this region is modified */
cropped_region?: CroppedRegion;
};
// --- Accept Image Edit (POST) ---
type AcceptImageEditRequest = {
live_object_id: string; // UUID from image-edit or object-removal response (required)
target_node_id?: string;
override_image_path?: string; // Optional path to use instead of workflow output
};
// --- Revert Slide (POST) ---
type RevertSlideRequest = {
history_entry_id: string; // UUID from slide history (required)
node_id?: string;
};
// --- Object Removal (POST) — async, poll jobs ---
type ObjectRemovalRequest = {
input_image_path: string; // required, non-blank
mask_path?: string;
cropped_region?: CroppedRegion;
};
// --- Extract Text (POST) — async, poll jobs ---
type ExtractTextRequest = {
node_id?: string;
node_image_path?: string;
};
// --- Save Konva Nodes (PUT) ---
type SaveKonvaNodesRequest = {
konva_nodes: Record<string, unknown>; // required
konva_order: string[]; // required
base_snapshot_id?: string; // UUID, optional
};
// --- Responses ---
type TriggerWorkflowResponse = {
activity_id: string; // UUID – poll GET /api/v2/jobs/{activityId}
workflow_type: string;
live_object_id: string; // UUID – pass to accept-image-edit when done
};
type AcceptImageEditResponse = {
id: string; // slide UUID
slide_section_id: string;
image_path: string | null;
status: string;
};
```
---
## Edit Slide Image (async)
Request body: `SlideImageEditRequest`. Response: `TriggerWorkflowResponse`.
Triggers AI-powered slide image editing. Upload input image first, then call with `instruction` and `input_image_path`. Poll `GET /api/v2/jobs/{activityId}` for status; when DONE, call accept-image-edit with `live_object_id`.
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/slides/<slide_id>/image-edit" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"instruction":"Make the background blue","input_image_path":"/slides/slide-1.png"}'
```
With optional region and reference images:
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/slides/<slide_id>/image-edit" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"instruction":"Replace this area","input_image_path":"/slides/slide-1.png","cropped_region":{"x":0,"y":0,"width":200,"height":100},"other_reference_image_paths":["/ref.png"]}'
```
---
## Accept Image Edit
Request body: `AcceptImageEditRequest`. Response: `AcceptImageEditResponse`.
Call after the image-edit or object-removal workflow completes successfully. Use `live_object_id` from the trigger response.
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/slides/<slide_id>/accept-image-edit" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"live_object_id":"<live_object_uuid>"}'
```
---
## Revert Slide
Request body: `RevertSlideRequest`. Response: `AcceptImageEditResponse`.
Reverts the slide to a previous version using `history_entry_id` from slide history.
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/slides/<slide_id>/revert" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"history_entry_id":"<history_entry_uuid>"}'
```
---
## Remove Objects (async)
Request body: `ObjectRemovalRequest`. Response: `TriggerWorkflowResponse`.
Triggers AI-powered object removal. Optional `mask_path` for mask-based removal. Poll jobs; when DONE, call accept-image-edit with `live_object_id`.
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/slides/<slide_id>/object-removal" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"input_image_path":"/slides/slide-1.png"}'
```
With mask:
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/slides/<slide_id>/object-removal" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"input_image_path":"/slides/slide-1.png","mask_path":"/masks/mask.png"}'
```
---
## Extract Text (async)
Request body: `ExtractTextRequest` (optional). Response: `TriggerWorkflowResponse`.
Performs OCR, removes text from image via inpainting, creates editable Konva text nodes. Poll `GET /api/v2/jobs/{activityId}` for status.
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/slides/<slide_id>/extract-text" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{}'
```
With optional node:
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/slides/<slide_id>/extract-text" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"node_id":"node-1","node_image_path":"/nodes/node1.png"}'
```
---
## Save Konva Nodes
Request body: `SaveKonvaNodesRequest`. Response: 200, no body.
Persists Konva canvas nodes and order (positions, layer order, flattened image path).
```bash
curl -X PUT "$LAYERPROOF_BASE_URL/api/v2/projects/<project_id>/slides/<slide_id>/konva-nodes" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"konva_nodes":{},"konva_order":[]}'
```
---
## Agent behavior
When the user asks to edit slides (image edit, accept edit, revert, remove objects, extract text, save canvas), do the following.
### 1. Choose the right endpoint
| User intent | Endpoint | Method |
|-------------|----------|--------|
| Edit slide image with AI instruction | `/api/v2/projects/{projectId}/slides/{slideId}/image-edit` | POST |
| Accept edited image after workflow completes | `/api/v2/projects/{projectId}/slides/{slideId}/accept-image-edit` | POST |
| Revert slide to previous version | `/api/v2/projects/{projectId}/slides/{slideId}/revert` | POST |
| Remove objects from slide image (AI) | `/api/v2/projects/{projectId}/slides/{slideId}/object-removal` | POST |
| Extract text from slide (OCR + inpainting) | `/api/v2/projects/{projectId}/slides/{slideId}/extract-text` | POST |
| Save Konva canvas nodes/order | `/api/v2/projects/{projectId}/slides/{slideId}/konva-nodes` | PUT |
All paths require `projectId` and `slideId` (replace placeholders with actual UUIDs).
### 2. Build and run the request
- **Auth**: Every request must include `X-API-KEY: $LAYERPROOF_API_KEY`. Read `LAYERPROOF_BASE_URL` and `LAYERPROOF_API_KEY` from the environment; if missing, tell the user to set them.
- **Path**: Resolve `project_id` and `slide_id` from context (e.g. from project/slide-deck list or user input). If missing, ask the user.
- **POST/PUT**: Build JSON body from the types above. Use `-X POST` or `-X PUT`, `-H "Content-Type: application/json"`, and `-d '...'`. Run the curl and show the result.
### 3. After async endpoints (image-edit, object-removal, extract-text)
- Response includes `activity_id` and `live_object_id`. Tell the user the workflow was started.
- Suggest polling `GET $LAYERPROOF_BASE_URL/api/v2/jobs/<activity_id>` until `status` is `DONE` or `CANCELED`.
- For image-edit and object-removal: when DONE, tell the user to call **accept-image-edit** with `live_object_id` to apply the result.
### 4. Response handling
- Always show the **raw JSON response** in a JSON code block; do not convert to a table.
- If the response contains a URL for an image (e.g. `image_path`), show the image and the JSON.
- On error (4xx/5xx), show the response body and status code; suggest fixing API key, projectId/slideId, or request body.
### 5. Example workflows
**Workflow A — User**: "Edit slide image in project X, slide Y: make the background darker."
1. Choose `POST /api/v2/projects/{projectId}/slides/{slideId}/image-edit`.
2. Resolve projectId and slideId (from user or ask).
3. Build body: `{"instruction":"Make the background darker","input_image_path":"/path/to/slide/image"}` (user may need to provide image path).
4. Run curl; show JSON. Mention polling jobs with `activity_id` and calling accept-image-edit with `live_object_id` when done.
**Workflow B — User**: "Edit two slides: darker background on slide 1, remove the logo from slide 3; then revert slide 1 if I don’t like it."
1. Resolve projectId; get slide ids from slide-deck GET deck (e.g. `slides[0].id`, `slides[2].id`).
2. For slide 1: POST `.../slides/{slideId1}/image-edit` with `{"instruction":"Make the background darker"}`; capture `activity_id_1` and `live_object_id_1`. For slide 3: POST `.../slides/{slideId3}/object-removal` with region/instruction; capture `activity_id_3` and `live_object_id_3`.
3. Poll `GET /api/v2/jobs/{activity_id_1}` and `.../jobs/{activity_id_3}` until both DONE. If either fails, report which slide and reason.
4. POST `.../slides/{slideId1}/accept-image-edit` with `{"live_object_id":"<live_object_id_1>"}`; POST `.../slides/{slideId3}/accept-image-edit` with `{"live_object_id":"<live_object_id_3>"}`. Show updated slide images (GET deck or slide detail).
5. If user says "revert slide 1": GET slide history (if available) for slideId1 to get `history_entry_id`; POST `.../slides/{slideId1}/revert` with `{"history_entry_id":"<id>"}`. Confirm with GET deck.
**Workflow C — User**: "Extract text from slide 2 and replace the old text (OCR + inpainting)."
1. Resolve projectId and slideId for slide 2. POST `.../slides/{slideId}/extract-text` (body per API); capture `activity_id`.
2. Poll `GET /api/v2/jobs/{activity_id}` until DONE. When DONE, `output` may contain extracted text or updated asset reference.
3. If a follow-up accept or apply step is required (e.g. accept-image-edit), use the returned `live_object_id`; otherwise show the result (e.g. updated slide or transcript) from GET deck.
---
## Response format (required)
- (if response contains url to show image) please show image and show json response instead of table
- Always show the **raw JSON response** (verbatim) in a JSON code block.
- If the response contains a URL for an image, **render/show the image** and also show the **JSON response** (do not convert to a table).Public API social campaigns (X-API-KEY). CRUD campaigns, generate, confirm outline, topics, variations, captions, exports, theme, citations. PublicApiSocialCampaignController (/api/v2/social-campaigns).
# Skill: Social Campaigns
## Description
**Social campaigns** are projects with `project_kind` **SOCIAL_CAMPAIGN**. This skill documents **PublicApiSocialCampaignController** at `/api/v2/social-campaigns`. Authenticate with `X-API-KEY`.
- **Campaign generation** returns `activity_id` — poll **`GET /api/v2/jobs/{activity_id}`** (same as slide workflows).
- **ZIP exports** return `export_id` — poll **`GET /api/v2/social-campaigns/{campaign_id}/exports/{export_id}`** (not the jobs endpoint).
---
## TypeScript types (selected)
```typescript
// --- Create / list / get ---
type PublicApiCreateCampaignRequest = {
name: string;
description?: string | null;
status?: string | null;
tags?: string[] | null;
metadata?: Record<string, unknown> | null;
workspace_id?: string | null;
theme_id?: string | null;
aspect_ratio?: string | null;
};
type PublicApiCampaignResponse = {
campaign_id: string;
name: string;
description?: string | null;
campaign_live_object_id: string;
created_at: string;
updated_at: string;
};
type PublicApiCampaignListResponse = {
data: PublicApiCampaignResponse[];
next_cursor?: string | null;
has_more: boolean;
};
// --- Generate campaign ---
type PublicApiTrendSnapshot = {
key: string;
label: string;
platform: string;
region?: string | null;
score?: number | null;
// ... additional optional analytics fields
};
type PublicApiGenerateCampaignRequest = {
prompt: string;
theme_id?: string | null;
tone_config?: {
output_language?: string | null;
text_detail_level?: string | null;
tone?: string | null;
voice?: string | null;
audience?: string | null;
} | null;
reference_file_paths?: string[] | null;
web_search_enabled?: boolean;
target_topic_count?: number | null;
trend_snapshot?: PublicApiTrendSnapshot | null;
trend_snapshots?: PublicApiTrendSnapshot[] | null;
auto_select?: boolean; // default false — if true, skips outline confirmation
};
type PublicApiGenerateCampaignResponse = {
campaign_live_object_id: string;
activity_id: string;
workflow_type: string;
};
// --- Confirm outline (after outline step when auto_select is false) ---
type PublicApiConfirmOutlineRequest = {
selections: { topic_index: number; option_index: number }[];
};
// --- Topics (“posts” in paths) ---
type PublicApiCreateTopicRequest = {
topic?: string | null;
core_message?: string | null;
key_points?: string[] | null;
visual_suggestion?: string | null;
};
type PublicApiTopicResponse = {
topic_id: string;
live_object_id: string;
status: string;
topic?: string | null;
core_message?: string | null;
key_points?: string[] | null;
visual_suggestion?: string | null;
version: number;
created_at: string;
updated_at: string;
};
// --- Export ---
type PublicApiCampaignExportStartedResponse = {
export_id: string;
status: string;
};
type PublicApiCampaignExportStatusResponse = {
export_id: string;
status: 'IN_PROGRESS' | 'COMPLETED' | 'FAILED';
download_url?: string | null;
expires_at?: string | null;
file_size_bytes?: number | null;
error_message?: string | null;
};
```
---
## Campaign CRUD
| Action | Method | Path |
|--------|--------|------|
| Create | POST | `/api/v2/social-campaigns` |
| List (cursor) | GET | `/api/v2/social-campaigns?limit=20&cursor=` |
| Get + posts | GET | `/api/v2/social-campaigns/{campaign_id}` |
| Update | PUT | `/api/v2/social-campaigns/{campaign_id}` |
| Update settings (theme) | PUT | `/api/v2/social-campaigns/{campaign_id}/settings` |
| Delete | DELETE | `/api/v2/social-campaigns/{campaign_id}` |
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/social-campaigns" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"name":"Q1 Launch","workspace_id":"<workspace_uuid>"}'
```
---
## Generate & confirm
| Action | Method | Path |
|--------|--------|------|
| Generate from prompt | POST | `/api/v2/social-campaigns/{campaign_id}/generate` |
| Confirm outline picks | POST | `/api/v2/social-campaigns/{campaign_id}/confirm-outline` |
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/social-campaigns/<campaign_id>/generate" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"prompt":"Holiday campaign","target_topic_count":5,"web_search_enabled":true,"auto_select":false}'
```
Poll **`GET /api/v2/jobs/{activity_id}`**. If `auto_select` is false, user picks outline options, then **POST confirm-outline** with `selections`.
---
## Topics (paths use `/posts`)
| Action | Method | Path |
|--------|--------|------|
| Create topic | POST | `/api/v2/social-campaigns/{campaign_id}/posts` |
| Update topic | PUT | `/api/v2/social-campaigns/{campaign_id}/posts/{post_id}` |
| Delete topic | DELETE | `/api/v2/social-campaigns/{campaign_id}/posts/{post_id}` |
| Reorder | PUT | `/api/v2/social-campaigns/{campaign_id}/reorder` body `{"topic_ids":["uuid",...]}` |
---
## Variations & images
| Action | Method | Path |
|--------|--------|------|
| Generate variations | POST | `.../posts/{post_id}/generate-variations` |
| More variations | POST | `.../posts/{post_id}/generate-more-variations` |
| Delete variation | DELETE | `.../posts/{post_id}/variations/{variation_id}` |
| Retry variation | POST | `.../posts/{post_id}/variations/{variation_id}/retry` |
| Aspect ratio variant | POST | `.../posts/{post_id}/variations/{variation_id}/generate-aspect-ratio` |
| Edit variation image | POST | `.../posts/{post_id}/variations/{variation_id}/edit-image` |
| Accept edit (if needed) | POST | `.../posts/{post_id}/variations/{variation_id}/accept-edit` |
Poll **`GET /api/v2/jobs/{activity_id}`** for async variation work.
---
## Captions
| Action | Method | Path |
|--------|--------|------|
| Update caption / hashtags | PUT | `.../posts/{post_id}/caption` |
| Generate caption (async) | POST | `.../posts/{post_id}/generate-caption` |
---
## Theme
| Action | Method | Path |
|--------|--------|------|
| Generate campaign theme (async) | POST | `/api/v2/social-campaigns/{campaign_id}/generate-theme` |
Poll jobs with returned activity id.
---
## Exports (ZIP)
| Action | Method | Path |
|--------|--------|------|
| Export full campaign ZIP | POST | `/api/v2/social-campaigns/{campaign_id}/exports/zip` |
| Export one topic ZIP | POST | `/api/v2/social-campaigns/{campaign_id}/posts/{post_id}/exports/zip` |
| Poll export | GET | `/api/v2/social-campaigns/{campaign_id}/exports/{export_id}` |
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/social-campaigns/<campaign_id>/exports/zip" \
-H "X-API-KEY: $LAYERPROOF_API_KEY"
```
---
## Citations & history
| Action | Method | Path |
|--------|--------|------|
| Campaign citations | GET | `/api/v2/social-campaigns/{campaign_id}/citations` |
| Post citations | GET | `.../posts/{post_id}/citations` |
| Platform post citations | GET | `.../posts/{post_id}/platforms/{platform}/citations` |
| Platform generation history | GET | `.../posts/{post_id}/platforms/{platform}/history` |
| Platform edit history | GET | `.../posts/{post_id}/platforms/{platform}/edits` |
---
## Agent behavior
1. Resolve **`campaign_id`** (create or list). **`campaign_live_object_id`** in responses is the campaign root live object.
2. **Generate** → poll **jobs** until `DONE` / handle `failure_reason`.
3. **Exports** → poll **`.../social-campaigns/{id}/exports/{export_id}`** until `COMPLETED` or `FAILED`.
4. **Topics** are listed under **`posts`** in API paths; treat `post_id` as **topic id**.
5. There is **no** `generate-platform` or per-platform image-edit in this public controller; platform-specific data appears in **get** campaign and **citations/history** endpoints when present.
---
## Response format (required)
- When responses include **image URLs** for posts or variations, **render previews** and show raw JSON.
- Always show **verbatim JSON** in a code block.Public API theme management (X-API-KEY). List, get, save, update, delete, generate, regenerate, apply, unapply. PublicThemeController (/api/v2/themes).
# Skill: Theme Management
## Description
Themes define visual styling for slides. This skill documents the **public API** at `/api/v2/themes` (PublicThemeController). Authenticate with `X-API-KEY` header.
---
## TypeScript types (request / response)
Mirrors `PublicThemeController` (/api/v2/themes) data classes.
```typescript
// --- List Themes (GET) ---
// Query: offset (default 0), limit (default 20), search (optional)
type PublicThemeListResponse = {
data: PublicThemeResponse[];
total: number;
offset: number;
limit: number;
};
// --- Get Theme By ID (GET) ---
type PublicThemeResponse = {
id: string;
name: string;
description: string | null;
visibility: string; // "PRIVATE" | "SYSTEM" | "SHARED"
preview_url: string;
created_at: string; // ISO 8601
updated_at: string; // ISO 8601
};
// --- Generate Theme (POST) — async ---
type PublicGenerateThemeRequest = {
prompt: string; // required, max 5000 chars
project_id?: string | null; // optional THEME project UUID; if omitted, a project is created automatically
};
type PublicGenerateThemeResponse = {
activity_id: string; // UUID – poll GET /api/v2/jobs/{activity_id}
theme_id: string; // UUID
};
// --- Apply Theme (POST) ---
type PublicApplyThemeRequest = {
slide_deck_id: string; // UUID, required
theme_id: string; // UUID, required
regenerate_slides?: boolean; // default false; if true, triggers batch image regeneration – poll activity_id
};
type PublicApplyThemeResponse = {
theme_id: string;
theme_name: string;
slide_deck_id: string;
applied: boolean; // true when applied successfully
activity_id: string | null; // set when regenerate_slides is true – poll GET /api/v2/jobs/{activity_id}
};
// --- Save theme (POST /api/v2/themes) — 201 ---
type PublicSaveThemeRequest = {
name: string; // required, max 256
visual_style_description: string; // required, max 5000
preview_s3_key: string; // required — S3 key of preview image
description?: string | null; // max 1000
source_prompt?: string | null;
source_project_id?: string | null;
tags?: string[] | null;
};
// --- Update theme (PUT /{theme_id}) ---
type PublicUpdateThemeRequest = {
name?: string | null;
description?: string | null;
tags?: string[] | null;
visual_style_description?: string | null;
};
// --- Regenerate theme (POST /{theme_id}/regenerate) — 202 ---
type PublicRegenerateThemeRequest = {
project_id: string; // UUID — project whose working dir is used
adjustment_prompt: string;
reference_image_paths?: string[] | null;
preview_only?: boolean;
};
type PublicRegenerateThemeResponse = {
activity_id: string;
theme_id: string;
live_object_id: string;
};
// --- Unapply (POST /unapply) ---
type PublicUnapplyThemeRequest = {
project_id: string;
slide_deck_id: string;
};
```
---
## List Themes
Query: `offset` (default 0), `limit` (default 20), `search` (optional). Response: `PublicThemeListResponse`.
```bash
curl "$LAYERPROOF_BASE_URL/api/v2/themes?offset=0&limit=20" \
-H "X-API-KEY: $LAYERPROOF_API_KEY"
```
---
## Get Theme By ID
Response: `PublicThemeResponse`.
```bash
curl "$LAYERPROOF_BASE_URL/api/v2/themes/<theme_id>" \
-H "X-API-KEY: $LAYERPROOF_API_KEY"
```
---
## Generate Theme
Request body: `PublicGenerateThemeRequest`. Response (202): `PublicGenerateThemeResponse`.
Only `prompt` is required. Optional `project_id` uses an existing THEME project; if omitted, one is created automatically. Poll `GET /api/v2/jobs/{activityId}` for status.
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/themes/generate" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"prompt":"Clean, minimal style with SF Pro"}'
```
With optional `project_id`:
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/themes/generate" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"prompt":"Clean, minimal style","project_id":"<theme_project_uuid>"}'
```
---
## Apply Theme
Request body: `PublicApplyThemeRequest`. Response: `PublicApplyThemeResponse`.
Theme and slide deck are specified in the body. When `regenerate_slides` is true, poll `GET /api/v2/jobs/{activityId}` for batch image regeneration status.
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/themes/apply" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"slide_deck_id":"<slide_deck_uuid>","theme_id":"<theme_uuid>"}'
```
With slide regeneration:
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/themes/apply" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"slide_deck_id":"<slide_deck_uuid>","theme_id":"<theme_uuid>","regenerate_slides":true}'
```
---
## Save Theme (manual)
Creates a saved private theme. Requires `visual_style_description` and `preview_s3_key` (upload preview via project/public files first). Response (201): `PublicThemeResponse`.
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/themes" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"name":"My brand","visual_style_description":"Minimal dark UI, rounded cards","preview_s3_key":"<s3_key>"}'
```
---
## Update Theme
Request body: `PublicUpdateThemeRequest`. Response: `PublicThemeResponse`.
```bash
curl -X PUT "$LAYERPROOF_BASE_URL/api/v2/themes/<theme_id>" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"name":"Renamed theme"}'
```
---
## Delete Theme
Soft-deletes a user-owned theme. Response: 204 No Content.
```bash
curl -X DELETE "$LAYERPROOF_BASE_URL/api/v2/themes/<theme_id>" \
-H "X-API-KEY: $LAYERPROOF_API_KEY"
```
---
## Regenerate Theme (async)
Request body: `PublicRegenerateThemeRequest`. Response (202): `PublicRegenerateThemeResponse`. Poll `GET /api/v2/jobs/{activity_id}`.
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/themes/<theme_id>/regenerate" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"project_id":"<project_uuid>","adjustment_prompt":"More contrast, warmer palette"}'
```
---
## Unapply Theme from Slide Deck
Removes applied theme from a deck. Request body: `PublicUnapplyThemeRequest`. Response: 204 No Content.
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/themes/unapply" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"project_id":"<project_uuid>","slide_deck_id":"<slide_deck_uuid>"}'
```
---
## List My Themes (by user)
Query: `offset`, `limit`, optional `visibility` (`PRIVATE` \| `SYSTEM` \| `SHARED`), optional `search`. Response: `PublicThemeListResponse`.
```bash
curl "$LAYERPROOF_BASE_URL/api/v2/themes/by-user-id?offset=0&limit=20" \
-H "X-API-KEY: $LAYERPROOF_API_KEY"
```
---
## Agent behavior
When the user asks to work with themes (list, get, generate, apply), do the following.
### 1. Choose the right endpoint
| User intent | Endpoint | Method |
|-------------|----------|--------|
| List/browse themes, search themes | `/api/v2/themes` | GET |
| Get one theme by ID | `/api/v2/themes/<theme_id>` | GET |
| Save a theme manually | `/api/v2/themes` | POST |
| Update theme metadata | `/api/v2/themes/<theme_id>` | PUT |
| Delete theme | `/api/v2/themes/<theme_id>` | DELETE |
| Generate theme from prompt (async) | `/api/v2/themes/generate` | POST |
| Regenerate / adjust theme (async) | `/api/v2/themes/<theme_id>/regenerate` | POST |
| Apply a theme to a slide deck | `/api/v2/themes/apply` | POST |
| Unapply theme from deck | `/api/v2/themes/unapply` | POST |
| List current user’s themes | `/api/v2/themes/by-user-id` | GET |
### 2. Build and run the request
- **Auth**: Every request must include `X-API-KEY: $LAYERPROOF_API_KEY`. Read `LAYERPROOF_BASE_URL` and `LAYERPROOF_API_KEY` from the environment; if missing, tell the user to set them.
- **GET**: Build `curl` with the chosen path and query params (`offset`, `limit`, `search` for list). Run the curl and show the result.
- **POST**: Build a JSON body from the user's input (prompt, theme ID, slide deck ID, etc.). Use `-X POST`, `-H "Content-Type: application/json"`, and `-d '...'`. Run the curl and show the result.
### 3. After generate or apply (with regeneration)
- **Generate theme**: Response includes `activity_id` and `theme_id`. Tell the user the theme was started and give `theme_id`. Optionally poll `GET $LAYERPROOF_BASE_URL/api/v2/jobs/<activity_id>` until `status` is `DONE` or `CANCELED`, then report outcome.
- **Apply theme with `regenerate_slides: true`**: Response may include `activity_id`. If present, tell the user regeneration was started and optionally poll `GET .../api/v2/jobs/<activity_id>` for status.
### 4. Response handling
- Always show the **raw JSON response** in a JSON code block; do not convert to a table.
- If the response contains a URL for an image (e.g. `preview_url`), show the image and the JSON.
- On error (4xx/5xx), show the response body and status code; suggest fixing missing/invalid API key, IDs, or request body.
### 5. Example workflows
**Workflow A — User**: "Generate a theme with prompt: minimal dark mode."
1. Choose `POST /api/v2/themes/generate`.
2. Build body: `{"prompt":"minimal dark mode"}`.
3. Run: `curl -X POST "$LAYERPROOF_BASE_URL/api/v2/themes/generate" -H "Content-Type: application/json" -H "X-API-KEY: $LAYERPROOF_API_KEY" -d '{"prompt":"minimal dark mode"}'`.
4. Show the JSON response; if it contains `activity_id`, mention they can poll `/api/v2/jobs/{activityId}` for status and use `theme_id` once done.
**Workflow B — User**: "List themes, generate a new 'corporate blue' theme, wait for it to finish, then apply it to my slide deck and regenerate slides."
1. GET `/api/v2/themes` with optional `limit`, `offset`, `search`; show list. User may pick existing or request new.
2. POST `/api/v2/themes/generate` with `{"prompt":"corporate blue"}`; capture `activity_id` and `theme_id`.
3. Poll `GET /api/v2/jobs/{activity_id}` until status is DONE (or CANCELED). If DONE, theme is ready; if failed, report `failure_reason`.
4. Resolve projectId and slideDeckId (projects + slide-deck). POST or PUT the slide-deck theme/settings endpoint with `theme_id` (e.g. PUT `.../slide-deck/.../settings` with `{"theme_id":"<theme_id>"}`).
5. Use **POST `/api/v2/themes/apply`** with `regenerate_slides: true` if you want slide images regenerated; capture `activity_id` and poll jobs until DONE. Otherwise apply with `regenerate_slides: false` or omit.
**Workflow C — User**: "I have a theme ID; apply it to deck X and only update the look (no slide regeneration)."
1. **POST `/api/v2/themes/apply`** with `{"slide_deck_id":"<slide_deck_uuid>","theme_id":"<theme_uuid>"}` (omit `regenerate_slides` or set `false`).
2. Confirm with GET deck; no job polling needed unless `regenerate_slides` was true.
---
## Response format (required)
- (if response contains url to show image) please show image and show json response instead of table
- Always show the **raw JSON response** (verbatim) in a JSON code block.
- If the response contains a URL for an image, **render/show the image** and also show the **JSON response** (do not convert to a table).Public API tone presets (X-API-KEY). List, create, get, update, delete, duplicate, apply to slide deck, save-as-preset. PublicApiToneController (/api/v2/tones).
# Skill: Tone Presets
## Description
Manage **tone presets** (voice, audience, detail level) and apply them to a slide deck. This skill documents **PublicApiToneController** at `/api/v2/tones`. Slide decks also expose **per-deck** tone settings under `.../slide-deck/{id}/tone-settings` (see **slide-decks** skill); this skill covers the **shared preset library** endpoints.
Authenticate with `X-API-KEY`.
---
## TypeScript types (request / response)
Mirrors `PublicApiToneController` / client tone DTOs (field names follow API JSON; use snake_case if your client sends snake_case per server config).
```typescript
// --- List (GET) ---
// Query: offset (default 0), limit (default 20), search (optional),
// visibility (optional, repeat or array: PRIVATE, SYSTEM)
type TonePresetListResponse = {
data: TonePresetResponse[];
total: number;
offset: number;
limit: number;
};
type TonePresetResponse = {
id: string;
name: string;
description?: string | null;
visibility?: string | null;
output_language?: string | null;
text_detail_level?: string | null;
tone?: string | null;
voice?: string | null;
audience?: string | null;
created_at?: string | null;
updated_at?: string | null;
};
// --- Create (POST) ---
type CreateTonePresetRequest = {
name: string;
description?: string | null;
output_language?: string | null;
text_detail_level?: string | null;
tone?: string | null;
voice?: string | null;
audience?: string | null;
};
// --- Update (PUT /{tone_id}) ---
type UpdateTonePresetRequest = {
name?: string | null;
description?: string | null;
output_language?: string | null;
text_detail_level?: string | null;
tone?: string | null;
voice?: string | null;
audience?: string | null;
};
// --- Apply (POST /{tone_id}/apply) ---
type ApplyTonePresetRequest = {
project_id: string;
slide_deck_id: string;
};
type ApplyTonePresetResponse = TonePresetResponse; // applied settings snapshot
// --- Save as preset from deck (POST /save-as-preset) — 201 ---
type SaveAsPresetRequest = {
slide_deck_id: string;
name: string;
description?: string | null;
};
// --- Duplicate (POST /{tone_id}/duplicate) — 201 ---
type DuplicateTonePresetRequest = {
name: string;
};
```
---
## List Tone Presets
```bash
curl "$LAYERPROOF_BASE_URL/api/v2/tones?offset=0&limit=20" \
-H "X-API-KEY: $LAYERPROOF_API_KEY"
```
---
## Get Tone Preset
```bash
curl "$LAYERPROOF_BASE_URL/api/v2/tones/<tone_id>" \
-H "X-API-KEY: $LAYERPROOF_API_KEY"
```
---
## Create Tone Preset
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/tones" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"name":"Boardroom","output_language":"en-US","tone":"Professional"}'
```
---
## Update Tone Preset
```bash
curl -X PUT "$LAYERPROOF_BASE_URL/api/v2/tones/<tone_id>" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"description":"Stricter pacing"}'
```
---
## Delete Tone Preset
Response: 204 No Content (cannot delete system presets — 403).
```bash
curl -X DELETE "$LAYERPROOF_BASE_URL/api/v2/tones/<tone_id>" \
-H "X-API-KEY: $LAYERPROOF_API_KEY"
```
---
## Apply Preset to Slide Deck
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/tones/<tone_id>/apply" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"project_id":"<project_uuid>","slide_deck_id":"<slide_deck_uuid>"}'
```
---
## Save Current Deck Settings as Preset
Persists the slide deck’s current tone settings as a new preset. Response (201): `TonePresetResponse`.
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/tones/save-as-preset" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"slide_deck_id":"<slide_deck_uuid>","name":"From deck settings"}'
```
---
## Duplicate Preset
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/tones/<tone_id>/duplicate" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"name":"Copy of Boardroom"}'
```
---
## Agent behavior
| User intent | Endpoint | Method |
|-------------|----------|--------|
| List presets | `/api/v2/tones` | GET |
| Get preset | `/api/v2/tones/{toneId}` | GET |
| Create preset | `/api/v2/tones` | POST |
| Update preset | `/api/v2/tones/{toneId}` | PUT |
| Delete preset | `/api/v2/tones/{toneId}` | DELETE |
| Apply to slide deck | `/api/v2/tones/{toneId}/apply` | POST |
| Save deck settings as preset | `/api/v2/tones/save-as-preset` | POST |
| Duplicate preset | `/api/v2/tones/{toneId}/duplicate` | POST |
**Note:** Per-deck tone fields without creating a preset are under **GET/PUT** `.../slide-deck/{slideDeckId}/tone-settings` (slide-decks skill).
---
## Response format (required)
- Always show the **raw JSON response** in a JSON code block.
- If the response contains image URLs, show images and the JSON.Public API workspace management (X-API-KEY). Create, list, get, update, delete workspaces. Types follow PublicApiWorkspaceController (/api/v2/workspaces).
# Skill: Workspace Management
## Description
Manage workspaces. This skill documents the **public API** at `/api/v2/workspaces` (PublicApiWorkspaceController). Authenticate with `X-API-KEY` header. List uses `page` and `page_size` (`page_size` default 20, max 100). **For the first page of results, pass `page=0` explicitly.**
---
## TypeScript types (request / response)
Mirrors `PublicApiWorkspaceController` data classes.
```typescript
// --- Create (POST) — 201 ---
type PublicApiCreateWorkspaceRequest = {
name: string; // required, 1–255 chars
description?: string | null;
default_theme_id?: string | null; // optional theme UUID
};
type PublicApiWorkspaceResponse = {
id: string;
user_id: string;
name: string;
description: string | null;
default_theme_id: string | null;
shared_working_dir_live_object_id: string;
project_count: number;
created_at: string; // ISO 8601
updated_at: string; // ISO 8601
thumbnail_url?: string | null;
};
// --- List (GET) ---
// Query: page (use 0 for first page), page_size (default 20, max 100)
type PublicApiWorkspaceListResponse = {
data: PublicApiWorkspaceResponse[];
total: number;
page: number;
page_size: number;
};
// --- Restore (POST /{workspace_id}/restore) ---
type PublicApiRestoreWorkspaceRequest = {
restore_projects?: boolean; // default true
};
type PublicApiRestoreWorkspaceResponse = {
workspace_restored: boolean;
projects_restored: number;
};
// --- List files (GET /{workspace_id}/files) ---
// Optional query: type (file classification filter, if supported by server)
type PublicApiWorkspaceFileEntry = {
id: string;
name: string;
path: string;
file_type: string;
mime_type: string;
size: number;
uploaded_at: string;
uploaded_by: string;
status: string;
presigned_url?: string | null;
url_expires_at?: string | null;
};
type PublicApiWorkspaceFilesResponse = {
files: PublicApiWorkspaceFileEntry[];
total: number;
};
// --- Update (PUT) ---
type PublicApiUpdateWorkspaceRequest = {
name?: string | null; // max 255 if provided
description?: string | null;
default_theme_id?: string | null; // optional theme UUID
};
```
---
## Create Workspace
Request body: `PublicApiCreateWorkspaceRequest`. Response (201): `PublicApiWorkspaceResponse`.
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/workspaces" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"name":"<workspace_name>","description":"<description>"}'
```
---
## List Workspaces
Query: `page` (use `0` for the first page), `page_size` (default 20, max 100). Response: `PublicApiWorkspaceListResponse`.
```bash
curl "$LAYERPROOF_BASE_URL/api/v2/workspaces?page=0&page_size=20" \
-H "X-API-KEY: $LAYERPROOF_API_KEY"
```
---
## List Deleted Workspaces
Soft-deleted workspaces for the current user. Query: `page` (default 0), `page_size` (default 20, max 100).
```bash
curl "$LAYERPROOF_BASE_URL/api/v2/workspaces/deleted?page=0&page_size=20" \
-H "X-API-KEY: $LAYERPROOF_API_KEY"
```
---
## Restore Workspace
Body optional: `{"restore_projects": true}` (default). Response: `PublicApiRestoreWorkspaceResponse`.
```bash
curl -X POST "$LAYERPROOF_BASE_URL/api/v2/workspaces/<workspace_id>/restore" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"restore_projects":true}'
```
---
## Permanently Delete Workspace
Hard-deletes a workspace that is already soft-deleted. Response: 204 No Content.
```bash
curl -X DELETE "$LAYERPROOF_BASE_URL/api/v2/workspaces/<workspace_id>/permanently" \
-H "X-API-KEY: $LAYERPROOF_API_KEY"
```
---
## List Files in Workspace
Lists files in the workspace shared working directory. Optional query: `type` (file classification).
```bash
curl "$LAYERPROOF_BASE_URL/api/v2/workspaces/<workspace_id>/files" \
-H "X-API-KEY: $LAYERPROOF_API_KEY"
```
---
## Get Workspace
Response: `PublicApiWorkspaceResponse`.
```bash
curl "$LAYERPROOF_BASE_URL/api/v2/workspaces/<workspace_id>" \
-H "X-API-KEY: $LAYERPROOF_API_KEY"
```
---
## Update Workspace
Request body: `PublicApiUpdateWorkspaceRequest`. Response: `PublicApiWorkspaceResponse`.
```bash
curl -X PUT "$LAYERPROOF_BASE_URL/api/v2/workspaces/<workspace_id>" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $LAYERPROOF_API_KEY" \
-d '{"name":"New name","description":"New description"}'
```
---
## Delete Workspace
Response: 204 No Content.
```bash
curl -X DELETE "$LAYERPROOF_BASE_URL/api/v2/workspaces/<workspace_id>" \
-H "X-API-KEY: $LAYERPROOF_API_KEY"
```
---
## Agent behavior
When the user asks to manage workspaces (create, list, get, update, delete), do the following.
### 1. Choose the right endpoint
| User intent | Endpoint | Method |
|-------------|----------|--------|
| Create workspace | `/api/v2/workspaces` | POST |
| List workspaces | `/api/v2/workspaces?page=0&page_size=20` | GET |
| List deleted workspaces | `/api/v2/workspaces/deleted?page=0&page_size=20` | GET |
| Restore deleted workspace | `/api/v2/workspaces/{workspaceId}/restore` | POST |
| Permanently delete (after soft delete) | `/api/v2/workspaces/{workspaceId}/permanently` | DELETE |
| List files in workspace | `/api/v2/workspaces/{workspaceId}/files` | GET |
| Get workspace by ID | `/api/v2/workspaces/{workspaceId}` | GET |
| Update workspace | `/api/v2/workspaces/{workspaceId}` | PUT |
| Delete workspace | `/api/v2/workspaces/{workspaceId}` | DELETE |
### 2. Build and run
- **Auth**: Include `X-API-KEY: $LAYERPROOF_API_KEY`. Read `LAYERPROOF_BASE_URL` and `LAYERPROOF_API_KEY` from the environment; if missing, tell the user to set them.
- **GET**: Build path and query params (`page`, `page_size` for list). Run curl and show result.
- **POST/PUT**: Build JSON body (name, description for create; name/description optional for update). Run curl and show result.
- **DELETE**: Build path; run curl. Response is 204 with no body.
### 3. Response handling
- Always show the **raw JSON response** in a JSON code block.
- For 204 delete, indicate success and no body.
- On error, show response body and status code.
### 4. Example workflows
**Workflow A — User**: "Create workspace Marketing."
1. Choose POST /api/v2/workspaces.
2. Body: `{"name":"Marketing"}` (or with description).
3. Run curl; show JSON. Returned `id` is the workspace ID for get/update/delete.
**Workflow B — User**: "Set up a workspace for Q2, create it if it doesn’t exist, then list my workspaces and show the one I’ll use for new projects."
1. GET `/api/v2/workspaces` with `page`, `page_size`; inspect list for a workspace named "Q2" or similar.
2. If not found: POST `/api/v2/workspaces` with `{"name":"Q2","description":"Q2 campaigns and decks"}`; capture `id` as `workspace_id`.
3. If found: use that workspace’s `id`. Optionally GET `/api/v2/workspaces/{id}` to show full details.
4. Tell user: "Use workspace_id <id> when creating projects (e.g. POST /api/v2/projects with workspace_id in body)."
**Workflow C — User**: "Rename workspace X to 'Marketing 2025' and add a description."
1. Resolve workspace ID (from list or user). PUT `/api/v2/workspaces/{workspaceId}` with `{"name":"Marketing 2025","description":"..."}`.
2. Run curl; show JSON. Optionally GET the workspace again to confirm name and description.
---
## Response format (required)
- (if response contains url to show image) please show image and show json response instead of table
- Always show the **raw JSON response** (verbatim) in a JSON code block.
- If the response contains a URL for an image, **render/show the image** and also show the **JSON response** (do not convert to a table).