Getting started
Authenticated endpoints accept either:
- A logged-in browser session using the
koalaful_sessioncookie. - A personal API key via
Authorization: Bearer <key>. - The same key via
X-API-Key: <key>.
Request conventions:
- JSON bodies:
Content-Type: application/json - File uploads:
multipart/form-data - Design routes generally accept either numeric IDs or share codes.
- Public routes include
/templates,/templates/{id},/fonts, and/categories.
curl https://dash.koalaful.io/api/designs \
-H "Authorization: Bearer kfl_..."
API keys
API keys are managed from the dashboard while logged in. Open /dashboard#api-keys to create, view, and revoke them. A new key is shown exactly once at creation time.
| Method | Endpoint | Description |
|---|---|---|
| GET | /auth/api-keys | List your existing keys. Session authentication required. |
| POST | /auth/api-keys | Create a key with {"name":"CI","expires_at":"2026-12-31 23:59:59"}. expires_at is optional. |
| DELETE | /auth/api-keys/{id} | Revoke a key immediately. |
Designs
Core design CRUD and collaboration. These routes require session or API key authentication.
| Method | Endpoint | Description |
|---|---|---|
| GET | /designs | List the current user’s designs ordered by updated_at. |
| GET | /designs/{id} | Fetch one design. HTML designs return html_content, css_content, and metadata. |
| POST | /designs | Create a design. title is optional; width, height, type, and payload are optional. |
| PUT | /designs/{id} | Partial update. You can update title, dimensions, HTML, CSS, metadata, design data, and thumbnail independently. |
| DELETE | /designs/{id} | Delete one design. |
| POST | /designs/bulk-delete | Delete or soft-delete multiple designs with {"ids":[...]} and optional permanent. |
| POST | /designs/{id}/restore | Restore a soft-deleted design. |
| POST | /designs/from-template | Clone an active template into a new user-owned HTML design. |
| POST | /designs/{id}/star | Toggle starred state. |
| POST | /designs/{id}/folder | Attach or detach a design from a folder via {"folder_id":123} or {"folder_id":null}. |
| GET | /designs/{id}/collaborators | List collaborators and return an editor link. |
| POST | /designs/{id}/collaborators | Grant edit access to an existing user with {"email":"..."}. |
| DELETE | /designs/{id}/collaborators/{collaboratorId} | Revoke collaborator edit access. |
POST /api/designs
{
"title": "Spring Campaign",
"design_type": "html",
"width": 1080,
"height": 1920,
"html_content": "<div class='html-element'>...</div>",
"css_content": "width: 1080px; height: 1920px; position: relative; overflow: hidden;",
"metadata": "{\"pages\":[...]}"
}
Folders
| Method | Endpoint | Description |
|---|---|---|
| GET | /folders | List folders with design_count. |
| POST | /folders | Create a folder with {"name":"..."}. |
| PUT | /folders/{id} | Rename a folder with {"name":"..."}. |
| DELETE | /folders/{id} | Delete a folder. Designs remain and lose the association. |
Templates, uploads, fonts, categories
| Method | Endpoint | Description |
|---|---|---|
| GET | /templates | List active templates. Requires authentication like every /api route. |
| GET | /templates/{id} | Fetch one template payload. Requires authentication. |
| GET | /uploads | List uploads for the authenticated user. |
| POST | /upload | Upload multipart image/PDF or ingest metadata JSON with url. |
| POST | /uploads/import | Import a remote image into the authenticated user's uploads. Body: {image_url} or {image_base64, mime_type?, file_name?}. Response: {success, data: { id, url, width, height, ... }}. |
| POST | /uploads/generate | Generate an image with the Koalaful image generator (gpt-image) and save it to the user's uploads (mirrored to Tonta on success). Body: {prompt, file_name?, width?, height?, orientation?, quality?, background?, output_format?}. Quality defaults to low (~$0.0004 per image). Response: {success, data: { id, url, storage: "tonta"|"local", ... }, provider, model, quality, size, usage}. |
| POST | /uploads/remove-background | Remove the background from an image (synchronous; takes ~3s; ~$0.0004 per call). Body: {image_url}. Result is cached: re-calling with the same image_url returns the cached cutout for free. Response: {success, data: { processed_url, original_url, upload_id, storage: "tonta"|"local", cached }}. The original/cutout pairing is also reflected on the upload row as bg_removed_path for clients listing uploads. |
| DELETE | /uploads/{id} | Delete one upload. |
| GET | /categories | Currently returns an empty list — the categories table is unpopulated. |
| GET | /fonts | Not working: the fonts table does not exist, so this returns 400. The editor uses a fixed Google Fonts set instead. |
| POST | /cache-image | Cache a remote image URL and receive a Koalaful-hosted URL. |
Exports, thumbnails, conversion
| Method | Endpoint | Description |
|---|---|---|
| POST | /designs/{id}/thumbnail | Save a base64 canvas screenshot and persist a smaller dashboard thumbnail. |
| POST | /designs/{id}/capture | Server-side thumbnail capture from stored HTML/CSS. |
| GET | /designs/{id}/export | Export a finished asset. format = png, jpg, jpeg, pdf or html; page selects a page and all_pages=1 exports the whole document. Images return a Tonta URL, PDFs a /exports/… URL. No SVG or MP4. |
| POST | /designs/{id}/render | Render with variable substitution. Supports variables, format, and page. |
| POST | /convert/html | Convert external HTML into Koalaful canvas JSON. |
| POST | /convert/image | Convert an uploaded/reference image into design JSON. |
| POST | /convert/pdf | Convert a PDF into design JSON. |
AI routes
These routes are live but include a mix of stable and experimental surfaces.
| Method | Endpoint | Description |
|---|---|---|
| POST | /ai/generate | General AI design generation. |
| POST | /ai/enhance-prompt | Prompt enhancement. |
| POST | /ai/manipulate | Modify an existing design with minimal changes. |
| POST | /ai-test3/placeholder-jobs | Internal: queues the editor's image-placeholder generation. Not a stable public surface. |
| POST | /ai-test2/generate | Experimental generator v2. |
| POST | /ai-test3/generate | Experimental generator v3. |
| POST | /ai-test3/generation-jobs | Create background generation jobs. |
| GET | /ai-test3/generation-jobs/status | Poll background generation job status. |
| POST | /ai-test3/generate-placeholder-image | Generate one placeholder image. |
| POST | /ai-test3/placeholder-jobs | Create multiple placeholder image jobs. |
| GET | /ai-test3/placeholder-jobs/status | Poll placeholder job status. |
| POST | /aigenlab/generate | AIGenLab design generation. |
| POST | /aigenlab/enhance-prompt | AIGenLab prompt enhancement. |
| POST | /aigenlab/generation-jobs | Create AIGenLab background jobs. |
| GET | /aigenlab/generation-jobs/status | Poll AIGenLab background job status. |
| GET | /aigenlab/generation-jobs/recent | List recent AIGenLab jobs. |
| POST | /aigenlab/generate-placeholder-image | Generate one placeholder image through AIGenLab. |
| POST | /aigenlab/placeholder-jobs | Create multiple placeholder image jobs. |
| GET | /aigenlab/placeholder-jobs/status | Poll placeholder job status. |
Async & bulk rendering
For more than a handful of renders, queue the work rather than looping synchronously.
| Method | Endpoint | Description |
|---|---|---|
| POST | /designs/{id}/export-async | Queue one render. Body: {format, page, scale, variables, text_replacements, webhook_url}. Returns a job id immediately. |
| POST | /designs/{id}/export-bulk | Up to 1000 variants in one request. Body: {defaults, jobs:[{variables|text_replacements|format|page|scale}], webhook_url}. |
| GET | /render-jobs | List jobs. Payload key is jobs, not data. |
| GET | /render-jobs/{uuid} | Status/result of one job. |
| GET | /designs/{id}/variables | List data-variable fields across all pages. |
| POST | /designs/{id}/variables | Tag elements as variables: {assignments:[{element_id, variable}]}. |
| GET | /stock/search?query=trees | Stock photo proxy. Payload key is photos. |
webhook_url is a per-job callback. There is no
account-level webhook subscription.
Guest edit links
A guest edit link lets someone without a Koalaful account open one design in the full editor and save changes to it. Use it to put an “Edit this design” button in your own product.
The URL is the credential. It grants edit access to that one design to anyone holding it, so keep the expiry short and revoke it when you are done.
| Method | Endpoint | Description |
|---|---|---|
| POST | /designs/{id}/guest-links | Create a link. Body (all optional): {permission:"edit"|"view", expires_in_hours (default 168, max 8760), expires_at, never_expires, label}. Returns data.url once. |
| GET | /designs/{id}/guest-links | List links with status of active / expired / revoked. Metadata only — never the URL. |
| GET | /designs/{id}/guest-links/{linkId} | One link, including use_count and last_used_at. |
| POST | /designs/{id}/guest-links/{linkId}/rotate | Issue a new URL on the same record. The old URL stops working at once. |
| DELETE | /designs/{id}/guest-links/{linkId} | Revoke immediately and permanently. |
The guest opens https://dash.koalaful.io/e/{token}
— the editor with no login, scoped to that design, account
navigation removed. To drive the API as the guest instead,
send X-KF-Guest-Token: kfg_…. That token is
accepted only in a header, and only for that one design:
anything else returns 403.
curl -X POST https://dash.koalaful.io/api/designs/5932/guest-links \
-H "X-API-Key: kfl_..." -H "Content-Type: application/json" \
-d '{"permission":"edit","expires_in_hours":24,"label":"socials-user-4821"}'
Apps & integrations
| Method | Endpoint | Description |
|---|---|---|
| GET | /apps | List supported app integrations and connection state. |
| POST | /apps/connect | Store or update an app connection. |
| POST | /apps/test | Validate an app connection. |
| DELETE | /apps/{provider} | Disconnect one provider. |
| GET | /apps/google-drive/oauth/start | Start Google Drive OAuth. |
| GET | /apps/google-drive/oauth/callback | Handle Google Drive OAuth callback. |
| GET | /apps/dropbox/oauth/start | Start Dropbox OAuth. |
| GET | /apps/dropbox/oauth/callback | Handle Dropbox OAuth callback. |
| GET | /apps/google-drive/files | List Google Drive files. |
| POST | /apps/google-drive/import | Import a Google Drive file. |
| POST | /apps/google-drive/export | Export to Google Drive. |
| GET | /apps/dropbox/files | List Dropbox files. |
| POST | /apps/dropbox/import | Import a Dropbox file. |
| POST | /apps/dropbox/export | Export to Dropbox. |
| POST | /apps/slack/export | Export to Slack. |
Interactive console
Use your browser session, an API key, or both. If an API key is supplied below, the request is sent with both Authorization: Bearer and X-API-Key.