Designing with Sleek
Overview
Sleek is an AI-powered mobile app design tool. You interact with it via a REST API at
to create projects, describe what you want built in plain language, and get back rendered screens. All communication is standard HTTP with bearer token auth.
Base URL:
Auth:
Authorization: Bearer <API_KEY>
on every
request
Content-Type:
(requests and responses)
CORS: Enabled on all
endpoints
Prerequisites: API Key
Create API keys at
https://sleek.design/dashboard/api-keys. The full key value is shown only once at creation — store it securely.
Required plan: Pro+ (API access is gated)
Key scopes
| Scope | What it unlocks |
|---|
| List / get projects |
| Create / delete projects |
| List components in a project |
| Get chat run status |
| Send chat messages |
| Render component screenshots |
Create a key with only the scopes needed for the task.
Handling high-level requests
When the user says something like "design a fitness tracking app" or "build me a settings screen":
- Create a project if one doesn't exist yet (ask the user for a name, or derive one from the request)
- Send a chat message describing what to build — you can use the user's words directly as ; Sleek's AI interprets natural language
- Follow the screenshot delivery rule below to show the result
You do not need to decompose the request into screens first. Send the full intent as a single message and let Sleek decide what screens to create.
Screenshot delivery rule
After every chat run that produces or operations, always take screenshots and show them to the user. Never silently complete a chat run without delivering the visuals.
When screens are created for the first time on a project (i.e. the run includes
operations), deliver:
- One screenshot per newly created screen (individual )
- One combined screenshot of all screens in the project (
componentIds: [all screen ids]
)
When only existing screens are updated, deliver one screenshot per affected screen.
Use
background: "transparent"
for all screenshots unless the user explicitly requests otherwise.
Quick Reference — All Endpoints
| Method | Path | Scope | Description |
|---|
| | | List projects |
| | | Create project |
| | | Get project |
| | | Delete project |
| /api/v1/projects/:id/components
| | List components |
| /api/v1/projects/:id/chat/messages
| | Send chat message |
| /api/v1/projects/:id/chat/runs/:runId
| | Poll run status |
| | | Render screenshot |
All IDs are stable string identifiers.
Endpoints
Projects
List projects
http
GET /api/v1/projects?limit=50&offset=0
json
{
"data": [
{
"id": "proj_abc",
"name": "My App",
"slug": "my-app",
"createdAt": "2026-01-01T00:00:00Z",
"updatedAt": "..."
}
],
"pagination": { "total": 12, "limit": 50, "offset": 0 }
}
Create project
http
POST /api/v1/projects
{ "name": "My New App" }
Response
— same shape as a single project.
Get / Delete project
http
GET /api/v1/projects/:projectId
DELETE /api/v1/projects/:projectId → 204 No Content
Components
List components
http
GET /api/v1/projects/:projectId/components?limit=50&offset=0
json
{
"data": [
{
"id": "cmp_xyz",
"name": "Hero Section",
"activeVersion": 3,
"versions": [{ "id": "ver_001", "version": 1, "createdAt": "..." }],
"createdAt": "...",
"updatedAt": "..."
}
],
"pagination": { "total": 5, "limit": 50, "offset": 0 }
}
Chat — Send Message
This is the core action: describe what you want in
and the AI creates or modifies screens.
http
POST /api/v1/projects/:projectId/chat/messages?wait=false
{
"message": { "text": "Add a pricing section with three tiers" },
"imageUrls": ["https://example.com/ref.png"],
"target": { "screenId": "scr_abc" }
}
| Field | Required | Notes |
|---|
| Yes | 1+ chars, trimmed |
| No | HTTPS URLs only; included as visual context |
| No | Edit a specific screen; omit to let AI decide |
| No | Sync wait mode (default: false) |
| header | No | Replay-safe re-sends |
Response — async (default, )
Status
.
and
are absent until the run reaches a terminal state.
json
{
"data": {
"runId": "run_111",
"status": "queued",
"statusUrl": "/api/v1/projects/proj_abc/chat/runs/run_111"
}
}
Response — sync ()
Blocks up to
300 seconds. Returns
when completed,
if timed out.
json
{
"data": {
"runId": "run_111",
"status": "completed",
"statusUrl": "...",
"result": {
"assistantText": "I added a pricing section with...",
"operations": [
{ "type": "screen_created", "screenId": "scr_xyz", "screenName": "Pricing" },
{ "type": "screen_updated", "screenId": "scr_abc" },
{ "type": "theme_updated" }
]
}
}
}
Chat — Poll Run Status
Use this after async send to check progress.
http
GET /api/v1/projects/:projectId/chat/runs/:runId
Response — same shape as send message
object:
json
{
"data": {
"runId": "run_111",
"status": "queued",
"statusUrl": "..."
}
}
When completed successfully,
is present:
json
{
"data": {
"runId": "run_111",
"status": "completed",
"statusUrl": "...",
"result": {
"assistantText": "...",
"operations": [...]
}
}
}
json
{
"data": {
"runId": "run_111",
"status": "failed",
"statusUrl": "...",
"error": { "code": "execution_failed", "message": "..." }
}
}
Run status lifecycle:
→
→
Screenshots
Takes a snapshot of one or more rendered components.
http
POST /api/screenshots
{
"componentIds": ["cmp_xyz", "cmp_abc"],
"projectId": "proj_abc",
"format": "png",
"scale": 2,
"gap": 40,
"padding": 40,
"background": "transparent"
}
| Field | Default | Notes |
|---|
| | or |
| | 1–3 (device pixel ratio) |
| | Pixels between components |
| | Canvas padding |
| | Any CSS color |
Always use
"background": "transparent"
unless the user explicitly requests a specific background color.
Response: raw binary
or
with
Content-Disposition: attachment
.
Error Shapes
json
{ "code": "UNAUTHORIZED", "message": "..." }
| HTTP | Code | When |
|---|
| 401 | | Missing/invalid/expired API key |
| 403 | | Valid key, wrong scope or plan |
| 404 | | Resource doesn't exist |
| 400 | | Validation failure |
| 409 | | Another run is active for this project |
| 500 | | Server error |
Chat run-level errors (inside
):
| Code | Meaning |
|---|
| Organization has no credits left |
| AI execution error |
Flows
Flow 1: Create project and generate a UI (async + polling)
1. POST /api/v1/projects → get projectId
2. POST /api/v1/projects/:id/chat/messages → get runId (202)
3. Poll GET /api/v1/projects/:id/chat/runs/:runId
until status == "completed" or "failed"
4. Collect screenIds from result.operations
(screen_created and screen_updated entries)
5. Screenshot each affected screen individually
6. If any screen_created: also screenshot all project screens combined
7. Show all screenshots to the user
Polling recommendation: start at 2s interval, back off to 5s after 10s, give up after 5 minutes.
Flow 2: Sync mode (simple, blocking)
Best for short tasks or when latency is acceptable.
1. POST /api/v1/projects/:id/chat/messages?wait=true
→ blocks up to 300s
→ 200 if completed, 202 if timed out
2. If 202, fall back to Flow 1 polling with the returned runId
3. On completion, screenshot and show affected screens (see screenshot delivery rule)
Flow 3: Edit a specific screen
1. GET /api/v1/projects/:id/components → find screenId
2. POST /api/v1/projects/:id/chat/messages
body: { message: { text: "..." }, target: { screenId: "scr_xyz" } }
3. Poll or wait as above
4. Screenshot the updated screen and show it to the user
Flow 4: Idempotent message (safe retries)
Add
header on the send request. If the network drops and you retry with the same key, the server returns the existing run rather than creating a duplicate. The key must be ≤255 chars.
POST /api/v1/projects/:id/chat/messages
idempotency-key: my-unique-request-id-abc123
Flow 5: One run at a time (conflict handling)
Only one active run is allowed per project. If you send a message while one is running, you get
. Wait for the active run to complete before sending the next message.
409 response → poll existing run → completed → send next message
Pagination
All list endpoints accept
(1–100, default 50) and
(≥0). The response always includes
so you can page through all results.
http
GET /api/v1/projects?limit=10&offset=20
Common Mistakes
| Mistake | Fix |
|---|
| Sending to without header | Add Authorization: Bearer <key>
to every request |
| Using wrong scope | Check key's scopes match the endpoint (e.g. for sending messages) |
| Sending next message before run completes | Poll until / before next send |
| Using on long generations | It blocks 300s max; have a fallback to polling for response |
| HTTP URLs in | Only HTTPS URLs are accepted |
| Assuming is present on | is absent until status is |