Designing with Sleek
Overview
sleek.design 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 $SLEEK_API_KEY
on every
request
Content-Type:
(requests and responses)
CORS: Enabled on all
endpoints
API docs: OpenAPI spec at
https://sleek.design/api/v1/spec.json
; browsable docs at
https://sleek.design/api/v1/docs
. Fetch the spec for any contract detail not covered here.
Prerequisites: API Key
If
is not set, use the device flow so the user never handles the raw key:
POST https://sleek.design/api/v1/device/start
(no auth) with body {"source": "your-tool-slug"}
. The response contains a , a human-checkable , a secret , and a poll in seconds.
- Show the user the and the , and tell them to confirm the code matches before approving.
- Poll
POST https://sleek.design/api/v1/device/poll
with every seconds. When the user approves, the poll returns {"status": "approved", "key": "sk_..."}
exactly once: store it as . Codes expire after 15 minutes; on , start over.
Fallback: send the user to
https://sleek.design/agents/setup, which handles sign-in, plan upgrade, and key creation in one place, and ask them to paste the key back to you. Keys can also be managed at
https://sleek.design/dashboard/api-keys. The full key value is shown only once at creation.
Plans: free accounts can try the API with their one-time trial credits (about one design run), so a new user can see their first design before any payment decision. Sustained use requires the Pro plan or higher ($49.99/month, or $30/month billed yearly at $360/year; includes 20,000 monthly AI credits, roughly 650 screens). When cost becomes relevant (the user asks, an upgrade is needed to continue, or you're about to send them to a payment page), state this pricing plainly, including the yearly option. Never let a payment step come as a surprise.
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.
Security & Privacy
- Single host: All requests go exclusively to . No data is sent to third parties.
- HTTPS only: All communication uses HTTPS. The API key is transmitted only in the header to Sleek endpoints.
- Minimal scopes: Create API keys with only the scopes required for the task. Prefer short-lived or revocable keys.
- Image URLs: When using in chat messages, those URLs are fetched by Sleek's servers. Avoid passing URLs that contain sensitive content.
Designing
The full request/response shapes for every endpoint used below are in the
API reference.
1. Create a project
Create a project with
if one doesn't exist yet. Derive a name from the request.
Each project has its own theme, style, and design system. If the user wants multiple design variations, create a separate project for each variation.
2. Send a chat message
Send the request with
POST /api/v1/projects/:id/chat/messages
. Sleek has its own AI that plans screen content, visual style, and layout: pass the user's request as-is and let it plan. Don't add details the user didn't ask for, and don't decompose the request into screens; send the full intent as a single message. If the user described specific screens and styling, include those. Sleek produces richer designs when given room to plan.
Seed a style with a reference: Sleek curates a catalog of design references. When the user wants a specific look or asks for style options, list them with
(each has a
and
you can show) and pass the chosen id as
on the first message to a project, so its style guide seeds the whole design.
Identify your tool: always send
, the slug of the tool making the request. The Sleek editor uses it to show the user who is designing while the run streams. Recognized values:
,
,
,
,
,
,
. If your tool isn't listed, send a short kebab-case slug for it anyway (max 64 chars). Unrecognized values are fine and get a generic label.
Watch it live: runs render in the Sleek editor in real time. After sending the first message to a project, tell the user they can watch their screens being designed live in Sleek, and share the editor link:
https://sleek.design/project/:projectId
. Don't open a browser yourself unless the user asks.
Polling: chat messages are async by default: you get a
and poll
GET /api/v1/projects/:id/chat/runs/:runId
. Start at 2s interval, back off to 5s after 10s, give up after 5 minutes. You can also use
for a blocking call (up to 300s; falls back to polling if it times out with
).
Editing a specific screen: use
to direct changes to the right screen (uses the screen ID from operations, not the component ID).
One run at a time: only one active run is allowed per project. If you get
, wait for the current run to complete before sending the next message. If the user changed their mind or a stale run is blocking the project, cancel it (see
Cancel Run). Messages to different projects can run in parallel; use async polling (not
) when running multiple projects concurrently.
Safe retries: add an
header (≤255 chars) to replay-safe re-sends. The server returns the existing run rather than creating a duplicate.
3. Show the results
After every chat run that produces
or
operations,
take screenshots and show them to the user using
. The step is done only when the user has seen a screenshot of every screen the run created or updated; never complete a run silently.
- New screens: one screenshot per screen + one combined screenshot of all screens in the project.
- Updated screens: one screenshot per affected screen.
Use
background: "transparent"
unless the user explicitly requests a specific background color.
Save screenshots in the project directory (not a temporary folder) so the user can easily view them.
Implementing Designs
When the user wants to implement the designs in code (not just preview them), always fetch the component HTML code. Do not rely on screenshots alone.
Use
GET /api/v1/projects/:id/components/:componentId
to fetch each screen's code. The
comes from the chat run's
.
Component code can be large. When saving it to files, avoid writing the content through your text output: it's slow and wastes tokens. Instead, use shell commands to fetch the API response and write it directly to disk (e.g., pipe the response body into a file).
Which version to use
Each component carries a
array and an
.
By default, use the entry where versions[i].version === activeVersion
: that's the code currently shown in Sleek.
If the user's prompt pins specific versions, follow those instead (see
Pinned versions below).
Pinned versions
The user's prompt may include a pin block telling you to implement specific historical versions instead of the current ones, like this:
... at this exact state instead of the project's current version:
- component cmp_abc: version ver_001
- component cmp_def: version ver_002
- theme thm_ghi: version ver_003
When you see a pin block, implement those exact versions instead of
. Components not named in the pin block continue to use their active version. Theme IDs surface only inside pin blocks; this skill exposes no separate endpoint to enumerate them.
Fetching the right code
For each pinned component, find the entry in
where
matches the given version id (e.g.
) and use its
. Do
not fall back to
for pinned components.
Screenshots of pinned versions
Pass
componentVersionOverrides
and
to
:
json
{
"componentIds": ["cmp_abc"],
"projectId": "proj_xyz",
"componentVersionOverrides": { "cmp_abc": "ver_001" },
"themeVersionOverrides": { "thm_ghi": "ver_003" }
}
Keys are component / theme public ids; values are the corresponding
. Entities missing from a map fall back to their active version. Include the override maps whenever the prompt specified pinned versions.
HTML prototypes
The component
is a complete HTML document. Save it directly to a
file. No build step needed.
Native frameworks (React Native, SwiftUI, etc.)
Use both the HTML code and the screenshots together:
- HTML code is the implementation reference: it contains the exact structure, layout, styling, colors, spacing, content, image URLs, and icon names.
- Screenshots are the visual target: use them to verify your implementation matches the intended look.
The HTML tells you how to build it; the screenshot tells you what it should look like.
Icons
Sleek uses
Iconify icons in the format
(e.g.,
,
material-symbols:search-rounded
,
). The most common sets are
Solar,
Hugeicons,
Material Symbols and
MDI.
Use the exact icons from the HTML code. Do not substitute with a different icon set. Matching icons is important for design fidelity.
When implementing icons:
-
Check if the project already has an icon system that supports the same sets Sleek uses (Solar, Hugeicons, Material Symbols, MDI). If so, use it. Note:
does
not support these sets, so do not use it as a substitute.
-
Otherwise, fetch the SVGs from the Iconify API and embed them in the code:
GET https://api.iconify.design/{prefix}/{name}.svg
Example:
https://api.iconify.design/solar/heart-bold.svg
Collect all icon names from the HTML, fetch their SVGs, and save them as static assets or string constants in the codebase. For
React Native / Expo, render them with
's
component, which works in Expo Go with no additional native dependencies.
Fonts
The HTML includes Google Fonts via
tags in the
. Use the same fonts and weights when implementing in a native framework. Extract the font family names and weights from the
tags.
Navigation
The designs may include navigation elements like tab bars and headers. Update the project's navigation styling and structure to match the designs. Don't just implement the screen content while leaving the default navigation untouched.
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/components/:componentId
| | Get component |
| | any valid key | List references |
| /api/v1/projects/:id/chat/messages
| | Send chat message |
| /api/v1/projects/:id/chat/runs/:runId
| | Poll run status |
| /api/v1/projects/:id/chat/runs/:runId/cancel
| | Cancel run |
| | | Render screenshot |
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
Both list and get accept an optional
query param (default
). When omitted, icons render as
web components and the HTML pulls in the Iconify script, so leave it off by default. Pass
only when the consumer needs self-contained SVGs in the HTML (for example, importing into tools that don't run scripts).
json
{
"data": [
{
"id": "cmp_xyz",
"name": "Hero Section",
"activeVersion": 3,
"versions": [
{
"id": "ver_001",
"version": 1,
"code": "<!DOCTYPE html>...</html>",
"createdAt": "..."
}
],
"createdAt": "...",
"updatedAt": "..."
}
],
"pagination": { "total": 5, "limit": 50, "offset": 0 }
}
Get component
Fetches a single component by ID. Use this when you need the code for a specific screen (e.g., after a chat run returns a
in its operations).
http
GET /api/v1/projects/:projectId/components/:componentId
Response
:
with a single component in the same shape as a list item.
References
References are curated design styles from featured Sleek projects. They are world-readable: any valid API key can list them, no scope needed.
http
GET /api/v1/references?limit=50&offset=0
json
{
"data": [
{
"id": "proj_ref1",
"name": "Ember Fitness",
"previewImageUrls": ["https://.../screenshot.png"]
}
],
"pagination": { "total": 44, "limit": 50, "offset": 0 }
}
To use one, pass its
as
on
Send Message.
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" },
"source": "claude-code",
"imageUrls": ["https://example.com/ref.png"],
"target": { "screenId": "scr_abc" },
"referenceId": "proj_ref1"
}
| Field | Required | Notes |
|---|
| Yes | 1+ chars, trimmed |
| Treat as required | Slug of the tool sending the request (see step 2 of Designing) |
| No | HTTPS URLs only; included as visual context |
| No | Edit a specific screen using its (not ); omit to let AI decide |
| No | Seed the design style from a reference (see References); invalid id → |
| 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",
"componentId": "cmp_xyz"
},
{
"type": "screen_updated",
"screenId": "scr_abc",
"componentId": "cmp_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
The response has the same
shape as send message:
is present when
,
when
:
json
{
"data": {
"runId": "run_111",
"status": "failed",
"statusUrl": "...",
"error": { "code": "execution_failed", "message": "..." }
}
}
Run status lifecycle:
→
→
Chat: Cancel Run
http
POST /api/v1/projects/:projectId/chat/runs/:runId/cancel
Marks a
or
run as
with error code
and returns the updated run; already-finished runs are returned unchanged. Use it when the user changes their mind mid-run or a stale run is blocking the project with
.
Screenshots
Takes a snapshot of one or more rendered components.
http
POST /api/v1/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 |
| | Uniform padding on all sides |
| (optional) | Horizontal padding; overrides for left/right when provided |
| (optional) | Vertical padding; overrides for top/bottom when provided |
| (optional) | Top padding; overrides when provided |
| (optional) | Right padding; overrides when provided |
| (optional) | Bottom padding; overrides when provided |
| (optional) | Left padding; overrides when provided |
| | Any CSS color (hex, named, ) |
| | Overlay a subtle dot grid on the background |
| | Squircle corner radius per component in pixels (integer ≥ 0); pass for sharp corners |
componentVersionOverrides
| (optional) | Map of → to render at a pinned version instead of (see Pinned versions) |
| (optional) | Map of → to render with a pinned theme version (see Pinned versions) |
Padding resolves with a cascade: per-side → axis → uniform. For example,
falls back to
, which falls back to
. So
{ "padding": 20, "paddingX": 10, "paddingLeft": 5 }
gives top/bottom 20px, right 10px, left 5px.
When
is
, a dot pattern is drawn over the background color. The dots automatically adapt to the background: dark backgrounds get light dots, light backgrounds get dark dots. This has no effect when
is
.
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 |
| 429 | | Too many requests; back off and retry later |
| 500 | | Server error |
,
, and
bodies may include
: a page where the user can fix the condition (create a key, upgrade the plan). When present, share that URL with the user instead of improvising one.
Chat run-level errors (inside
):
| Code | Meaning |
|---|
| Organization has no credits left |
| AI execution error |
| Run cancelled via the cancel endpoint |
An
error includes
, the page where the user can top up credits. Relay it to the user; don't retry the run until they have.
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 |
|---|
| Omitting on chat messages | Always send so the run is attributed in the Sleek editor |
| Using on long generations | It blocks 300s max; have a fallback to polling for response |
| Assuming is present on | is absent until status is |
| Using as in screenshots | and are different; always use from operations for screenshots |
| Confusing (number) with (string) | When resolving pinned versions, match by (e.g. ); is the numeric index |