Download OpenAPI specification:
Abyssale is a programmatic creative asset generation API for creating marketing visuals, ad creatives, videos, and print-ready PDFs at scale. Design once in Abyssale, then generate thousands of personalized variations — across every format, channel, and language — via a single REST API call.
This documentation is about the Abyssale Rest API. If you would like to discover other Abyssale features or to have a comprehensive understanding on how everything works, go to the Abyssale developers hub.
Base URL: https://api.abyssale.com
Content-Type: application/json header.| Use case | Recommended approach |
|---|---|
| Personalized images in emails (rendered on open) | Static design → Dynamic image URL |
| Embed a live, customizable image on a website | Static design → Dynamic image URL |
| Single image on demand (synchronous response) | Synchronous generation |
| Bulk campaign creatives across multiple formats | Async generation |
| Animated GIF or video for social/ads | Animated design → Async generation with gif or video options |
| HTML5 banner ads for ad networks | Animated design → Async generation with html5 options |
| Print-ready PDF (single page) | Printer design → Async generation with print options |
| Multi-page print document (brochure, catalog) | Printer multipage design → Multi-page PDF |
Abyssale uses API Key Authentication. Your API key is passed along with every API call, allowing Abyssale to identify your company account and access your data.
401 Unauthorized — missing or invalid API key429 Too Many Requests — rate limit exceeded (10 req/s) or insufficient credits500 Internal Server Error — unexpected server-side failureA design is a layout including at least:
All elements have their own properties that can be customized (color, text payload, image...).
Once designed, all properties of the design will act as a reference for subsequent image generation. Obviously, those properties can be overridden by sending new parameters in the generation API call.
Abyssale provides a list of free template designs in the Template Library page. Feel free to use one to start designing your design instead of doing it from scratch.
All of your designs are listed in the different project pages, only those are accessible via API.
Retrieve all designs available in your Abyssale workspace. Each design defines the visual layout, available formats (dimensions), and configurable elements (text, images, shapes, etc.) that can be overridden during image generation. Filter by design type or project using query parameters.
A design lives in a project. Organisation-level master designs are workspace
templates, live in a category, and are listed by GET /workspace-templates
instead — they never appear here.
| project_id | string <uuid> Unique identifier (UUID) of a project. Filter designs by project. |
| category_id | string <uuid> Deprecated Deprecated alias of |
| type | string Enum: "static" "animated" "printer" "printer_multipage" Filter designs by one of these types static, animated, printer, printer_multipage |
[- {
- "id": "64238d01-d402-474b-8c2d-fbc957e9d290",
- "name": "Ad campaign fall 2025",
- "type": "static",
- "created_at": 1649942114,
- "updated_at": 1649942114,
- "project_id": "9d1f2b7c-5a44-4c3e-9f21-0b8e6d4a1c73",
- "project_name": "Fall campaigns",
- "category_id": "9d1f2b7c-5a44-4c3e-9f21-0b8e6d4a1c73",
- "category_name": "Fall campaigns",
}
]Retrieve the full specification of a design: its formats (dimensions and preview URLs), all configurable elements with their properties, and any template variables defined in text layers. Use this endpoint to discover what data to send in a generation request.
For printer designs, each format additionally carries its print settings read-only:
dpi (integer render DPI computed at import time, capped at 300), and bleed_size /
safe_size (floats in the format's unit, present only when the corresponding zone is
enabled).
For animated designs, the response additionally carries the animation model: a
top-level animation object (duration — timeline length in seconds; screenshot_at_s
— the HTML5 backup-screenshot moment, nullable on legacy designs), a per-element
animation object (start_at_s, end_at_s, tweens [{id, type, keyframes}] with
raw keyframe objects) on elements that carry timing or tweens, and video/audio media
attributes (video_url/audio_url, video_duration, video_muted/audio_muted,
max_volume) in the standard attribute shape.
Multipage print designs (printer_multipage) return a different shape. A multipage
design has no formats — each page is one format row — so the response carries pages and
elements_per_page instead of formats, elements, variables and
dynamic_image_url. A client coded against formats[] will not work on a multipage
design. pages[] items carry id (page_1 … page_N), width, height, unit,
preview_url and the read-only dpi/bleed_size/safe_size print settings;
elements_per_page is an object keyed by page id (not an array), each value being
that page's element list with group layers included.
| designId required | string <uuid> Unique identifier (UUID) of the design |
{- "id": "64238d01-d402-474b-8c2d-fbc957e9d290",
- "name": "Ad campaign fall 2025",
- "type": "static",
- "created_at": 1649942114,
- "updated_at": 1649942114,
- "project_id": "9d1f2b7c-5a44-4c3e-9f21-0b8e6d4a1c73",
- "project_name": "Fall campaigns",
- "category_id": "9d1f2b7c-5a44-4c3e-9f21-0b8e6d4a1c73",
- "category_name": "Fall campaigns",
- "formats": [
- {
- "id": "facebook-post",
- "uid": "9b57d65e-eb2c-4a74-a51e-4482917c248a",
- "width": 1200,
- "height": 1200,
- "unit": "px",
- "dpi": 300,
- "bleed_size": 3.5,
- "safe_size": 5
}
], - "elements": [
- {
- "name": "element-name",
- "type": "text",
- "animation": {
- "start_at_s": 0.79,
- "end_at_s": 8,
- "tweens": [
- {
- "id": "tb-text_0-slide_2",
- "type": "slide",
- "keyframes": [
- { }
]
}
]
}, - "attributes": [
- {
- "id": "payload",
- "help": "Text content (i.e. Lorem ipsum)",
- "values": {
- "facebook-post": "My image title"
}
}
]
}
], - "variables": {
- "name": "{name}",
- "title": "{title}"
}, - "animation": {
- "duration": 8,
- "screenshot_at_s": 8
}, - "pages": [
- {
- "id": "facebook-post",
- "uid": "9b57d65e-eb2c-4a74-a51e-4482917c248a",
- "width": 1200,
- "height": 1200,
- "unit": "px",
- "dpi": 300,
- "bleed_size": 3.5,
- "safe_size": 5
}
], - "elements_per_page": {
- "property1": [
- {
- "name": "element-name",
- "type": "text",
- "animation": {
- "start_at_s": 0.79,
- "end_at_s": 8,
- "tweens": [
- {
- "id": "tb-text_0-slide_2",
- "type": "slide",
- "keyframes": [
- { }
]
}
]
}, - "attributes": [
- {
- "id": "payload",
- "help": "Text content (i.e. Lorem ipsum)",
- "values": {
- "facebook-post": "My image title"
}
}
]
}
], - "property2": [
- {
- "name": "element-name",
- "type": "text",
- "animation": {
- "start_at_s": 0.79,
- "end_at_s": 8,
- "tweens": [
- {
- "id": "tb-text_0-slide_2",
- "type": "slide",
- "keyframes": [
- { }
]
}
]
}, - "attributes": [
- {
- "id": "payload",
- "help": "Text content (i.e. Lorem ipsum)",
- "values": {
- "facebook-post": "My image title"
}
}
]
}
]
}
}Retrieve detailed information for a specific format within a design, including dimensions, unit, preview URL, dynamic image URL, element layout, and variables. Useful for inspecting a single format before generating or building dynamic image URLs.
For printer designs the response additionally carries the format's print settings
read-only: dpi (integer render DPI computed at import time, capped at 300), and
bleed_size / safe_size (floats in the format's unit, present only when the
corresponding zone is enabled).
For animated designs the response additionally carries the design's animation
object (duration, screenshot_at_s — seconds), per-element animation
timing/tween data, and video/audio media attributes — the same read surface as
GET /designs/{designId}.
Multipage print designs (printer_multipage): a multipage design has no formats —
each page is one format row named page_1 … page_N, so page_N is the formatSpecifier
for this endpoint. The response is that page's object at the root (id, width,
height, unit, preview_url, plus the read-only dpi/bleed_size/safe_size print
settings) together with a design object (id, name, type, category_name,
created_at, updated_at) and the page's elements array (group layers included).
dynamic_image_url and variables are not returned for print designs. An unknown or
malformed page identifier yields 404 with id: format_not_found.
| designId required | string Unique identifier of the design |
| formatSpecifier required | string Format name or UID. For a |
{- "id": "new-format",
- "uid": "6248b96f-581a-43f0-9863-85e1b7d0ec05",
- "width": 3333,
- "height": 666,
- "unit": "px",
- "dpi": 300,
- "bleed_size": 3.5,
- "safe_size": 5,
- "animation": {
- "duration": 8,
- "screenshot_at_s": 8
}, - "design": {
- "id": "64238d01-d402-474b-8c2d-fbc957e9d290",
- "name": "Ad campaign fall 2025",
- "type": "static",
- "created_at": 1649942114,
- "updated_at": 1649942114,
- "project_id": "9d1f2b7c-5a44-4c3e-9f21-0b8e6d4a1c73",
- "project_name": "Fall campaigns",
- "category_id": "9d1f2b7c-5a44-4c3e-9f21-0b8e6d4a1c73",
- "category_name": "Fall campaigns"
}, - "elements": [
- {
- "name": "root",
- "type": "container",
- "settings": {
- "is_mandatory": false
}, - "attributes": [
- {
- "id": "background_color",
- "help": "6 or 8 digits hexadecimal background color (i.e. #F3F3F3) of the banner",
- "value": "#FFFFFF"
}
], - "layout": {
- "x": 0,
- "y": 0,
- "width": 3333,
- "height": 666
}
}
], - "variables": {
- "name": "{name}",
- "title": "{title}"
}
}Generate images, animated videos, GIFs, HTML5 banners, and print-ready PDFs programmatically from your Abyssale designs.
The generation workflow is simple:
Use the synchronous endpoint for single-image generation with an immediate response. Use the asynchronous endpoint for bulk banner creation across multiple formats at once — ideal for marketing automation, personalized ad campaigns, and batch asset production at scale.
Synchronously generate a single image from a design. Send your dynamic data (text, images, colors) as element overrides in the request body and receive the generated file URL immediately in the response.
Best for: real-time image generation, single-asset workflows, or when you need the result inline without polling.
For bulk generation across multiple formats, use the asynchronous endpoint instead.
| designId required | string <uuid> Unique identifier (UUID) of the design |
object (Elements) A | |
| template_format_name | string Corresponds to the format ID you would like to generate (only mandatory when your design contains several formats). |
| image_file_type | string Enum: "png" "jpeg" "webp" "avif" "pdf" "auto" Output file type. Defaults to the design's configured type when omitted. |
| file_compression_level | integer [ 1 .. 100 ] Percentage of compression applied. |
{- "elements": {
- "element-name1": {
- "background_color": "#FF0000"
}, - "element-name2": {
- "background_color": "#FF0000"
}
}, - "template_format_name": "facebook-post",
- "image_file_type": "png",
- "file_compression_level": 80
}{- "id": "64238d01-d402-474b-8c2d-fbc957e9d290",
- "version": 1,
- "sharing_id": "5fcec999-2bfb-4dd7-ba38-2d9e16c49149",
- "file": {
- "type": "jpeg",
- "filename": "996739f4-b563-428a-a6e8-ec3cb8bd03d4.jpeg"
}, - "format": {
- "id": "facebook-post",
- "width": 1200,
- "height": 1200
}, - "template": {
- "id": "64238d01-d402-474b-8c2d-fbc957e9d290",
- "name": "Ad campaign fall 2025",
- "type": "static",
- "created_at": 1649942114,
- "updated_at": 1649942114,
- "project_id": "9d1f2b7c-5a44-4c3e-9f21-0b8e6d4a1c73",
- "project_name": "Fall campaigns",
- "category_id": "9d1f2b7c-5a44-4c3e-9f21-0b8e6d4a1c73",
- "category_name": "Fall campaigns"
}
}Asynchronously generate multiple formats of an image, video, GIF, HTML5 banner, or PDF in a single API call. Ideal for bulk banner production, multi-channel marketing campaigns, and programmatic ad creative generation at scale.
Specify one or more format IDs to generate, or omit the list to generate all
formats defined in the design. Provide a callback_url to receive a webhook
notification when generation is complete, or poll
GET /generation-request/{generationRequestId} for status.
You can find available formats and elements by calling GET /designs/{designId}.
| designId required | string <uuid> Unique identifier (UUID) of the design |
object (AsyncElements) Same as | |
| template_format_names | Array of strings Format IDs you would like to generate. The array can be empty or not provided if you want to generate all formats of this design. |
| callback_url | string <uri> The url that will be called once generation of your images is done. We will do a |
| image_file_type | string Enum: "png" "jpeg" "webp" "avif" "gif" "pdf" "html5" "mp4" "auto" Output file type. Depends on the design type (static/animated/printer). Defaults to the design's configured type when omitted. |
| file_compression_level | integer [ 1 .. 100 ] Percentage of compression applied. |
object HTML5 output options (animated designs only) | |
object GIF output options (animated designs only) | |
object Video output options (animated designs only) | |
object Print output options (printer/printer_multipage designs only) | |
| original_visual_id | string <uuid> UUID of the original visual this generation is based on. |
{- "elements": {
- "element-name1": {
- "background_color": "#FF0000"
}, - "element-name2": {
- "background_color": "#FF0000"
}
}, - "template_format_names": [
- "facebook-feed",
- "instagram-post",
- "iab-medium"
], - "image_file_type": "png",
- "file_compression_level": 1,
- "html5": {
- "page_title": "Abyssale page title example",
- "ad_network": "default",
- "include_backup_image": true,
- "repeat": -1
}, - "gif": {
- "max_fps": 2,
- "repeat": -1
}, - "video": {
- "fps": 2
}, - "print": {
- "color_profile": "b4bc069b-fba6-4b13-aec1-3db2eeb7dbd7",
- "display_crop_marks": true
}, - "original_visual_id": "efce0750-ba21-49c4-be6f-4e6125cdc6bb"
}{- "generation_request_id": "06399fcd-0c21-47da-bd9b-1e653e0453e8"
}Asynchronously generate a multi-page print-ready PDF from a printer_multipage design.
Each page is defined by a pages dictionary entry, allowing different element
overrides per page — ideal for brochures, catalogs, and variable data printing.
Provide a callback_url to receive a webhook when the PDF is ready,
or poll GET /generation-request/{generationRequestId} for status.
You can find available formats and elements by calling GET /designs/{designId}.
| designId required | string <uuid> Unique identifier (UUID) of the design |
required | object (Pages) A |
| callback_url | string <uri> The url that will be called once generation of your PDF is done. We will do a |
object Print output options | |
| original_visual_id | string <uuid> UUID of the original visual this generation is based on. |
{- "pages": {
- "page_1": {
- "root": {
- "background_color": "#FF0000"
}
}, - "page_2": {
- "root": {
- "background_color": "#FF0000"
}
}
}, - "print": {
- "color_profile": "b4bc069b-fba6-4b13-aec1-3db2eeb7dbd7",
- "display_crop_marks": true
}, - "original_visual_id": "efce0750-ba21-49c4-be6f-4e6125cdc6bb"
}{- "generation_request_id": "06399fcd-0c21-47da-bd9b-1e653e0453e8"
}Poll the status of an asynchronous generation request. Use this endpoint to track the progress of a batch generation operation started with the async generate endpoint. The result is available for 7 days after generation.
| generationRequestId required | string <uuid> Unique identifier (UUID) of the generation request |
{- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "is_finalized": true,
- "banners": [
- {
- "id": "64238d01-d402-474b-8c2d-fbc957e9d290",
- "version": 1,
- "sharing_id": "5fcec999-2bfb-4dd7-ba38-2d9e16c49149",
- "file": {
- "type": "jpeg",
- "filename": "996739f4-b563-428a-a6e8-ec3cb8bd03d4.jpeg"
}, - "format": {
- "id": "facebook-post",
- "width": 1200,
- "height": 1200
}, - "template": {
- "id": "64238d01-d402-474b-8c2d-fbc957e9d290",
- "name": "Ad campaign fall 2025",
- "type": "static",
- "created_at": 1649942114,
- "updated_at": 1649942114,
- "project_id": "9d1f2b7c-5a44-4c3e-9f21-0b8e6d4a1c73",
- "project_name": "Fall campaigns",
- "category_id": "9d1f2b7c-5a44-4c3e-9f21-0b8e6d4a1c73",
- "category_name": "Fall campaigns"
}
}
], - "errors": [
- {
- "template_format_name": "string",
- "reason": "string"
}
]
}Retrieve the fonts available in your Abyssale workspace. Fonts can be applied to text and button elements during image generation. Two types are supported: Google Fonts (free, always available) and custom fonts uploaded to your workspace.
Reference a font by its ID when overriding the font property in a generation request.
Retrieve all fonts available in your Abyssale workspace, including Google Fonts
and any custom fonts you have uploaded. Use a font's id to override the font
on text or button elements in a generation request.
| type | string Enum: "google" "custom" Filter fonts by type. Omit to return both Google and custom fonts. |
[- {
- "id": "61568e7c-33c5-11ea-9877-92672c1b8195",
- "name": "Ubuntu Mono",
- "available_weights": [
- 100
], - "type": "google"
}
]Export a collection of previously generated banners as a single ZIP archive. Useful for downloading bulk assets for offline use, sharing with creative teams, or archiving a campaign's outputs. The export is processed asynchronously — provide a callback URL to receive a webhook notification when the archive is ready.
An asynchronous banner export has completed. The ZIP archive containing all
requested banners is available at the provided archive_url.
| export_id | string <uuid> ID of the export |
| archive_url | string <uri> The zip public URL |
| requested_at | number Timestamp at which the export has been requested |
| generated_at | number Timestamp at which the export has been done |
{- "export_id": "38cb7df3-1160-4824-8531-2bacde2b6517",
- "requested_at": 1642150000,
- "generated_at": 1642150002
}Asynchronously package a set of previously generated banners into a single ZIP
archive for download. Provide a callback_url to receive a webhook notification
when the export is ready, containing the archive URL.
| ids required | Array of strings <uuid> [ items <uuid > ] An array of string that corresponds to the banners IDs you would like to export. |
| callback_url | string <uri> The url that will be called once the generation of your export is done. |
{- "ids": [
- "64238d01-d402-474b-8c2d-fbc957e9d290"
],
}{- "export_id": "64238d01-d402-474b-8c2d-fbc957e9d290"
}Retrieve metadata and download URLs for generated files (images, videos, GIFs, HTML5 banners, and PDFs). Listen to webhook events to be notified in real time when a file or batch generation completes.
A new file (image, video, GIF, HTML5 banner, or PDF) has been generated and is ready to download.
| id required | string <uuid> |
| version | integer Version number of the generated file. |
| sharing_id | string <uuid> Identifier used for sharing this generated file. |
required | object (File) |
object (Format) | |
object (Design) |
{- "id": "64238d01-d402-474b-8c2d-fbc957e9d290",
- "version": 1,
- "sharing_id": "5fcec999-2bfb-4dd7-ba38-2d9e16c49149",
- "file": {
- "type": "jpeg",
- "filename": "996739f4-b563-428a-a6e8-ec3cb8bd03d4.jpeg"
}, - "format": {
- "id": "facebook-post",
- "width": 1200,
- "height": 1200
}, - "template": {
- "id": "64238d01-d402-474b-8c2d-fbc957e9d290",
- "name": "Ad campaign fall 2025",
- "type": "static",
- "created_at": 1649942114,
- "updated_at": 1649942114,
- "project_id": "9d1f2b7c-5a44-4c3e-9f21-0b8e6d4a1c73",
- "project_name": "Fall campaigns",
- "category_id": "9d1f2b7c-5a44-4c3e-9f21-0b8e6d4a1c73",
- "category_name": "Fall campaigns"
}
}An asynchronous batch generation request has completed. Contains the full list
of generated files and any per-format errors. Triggered by
POST /async/banner-builder/{designId}/generate.
| id required | string <uuid> Unique identifier (UUID) of the batch generation |
required | Array of objects (Banner) List of generated images |
Array of objects If any errors occur, they will be listed under that property |
{- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "banners": [
- {
- "id": "64238d01-d402-474b-8c2d-fbc957e9d290",
- "version": 1,
- "sharing_id": "5fcec999-2bfb-4dd7-ba38-2d9e16c49149",
- "file": {
- "type": "jpeg",
- "filename": "996739f4-b563-428a-a6e8-ec3cb8bd03d4.jpeg"
}, - "format": {
- "id": "facebook-post",
- "width": 1200,
- "height": 1200
}, - "template": {
- "id": "64238d01-d402-474b-8c2d-fbc957e9d290",
- "name": "Ad campaign fall 2025",
- "type": "static",
- "created_at": 1649942114,
- "updated_at": 1649942114,
- "project_id": "9d1f2b7c-5a44-4c3e-9f21-0b8e6d4a1c73",
- "project_name": "Fall campaigns",
- "category_id": "9d1f2b7c-5a44-4c3e-9f21-0b8e6d4a1c73",
- "category_name": "Fall campaigns"
}
}
], - "errors": [
- {
- "template_format_name": "string",
- "reason": "string"
}
]
}Retrieve the metadata and download URLs for a previously generated file (image, video, GIF, HTML5 banner, or PDF). Returns both the S3 storage URL and a CDN-hosted URL suitable for embedding directly in websites or emails.
| bannerId required | string <uuid> Unique identifier (UUID) of the file |
{- "id": "64238d01-d402-474b-8c2d-fbc957e9d290",
- "version": 1,
- "sharing_id": "5fcec999-2bfb-4dd7-ba38-2d9e16c49149",
- "file": {
- "type": "jpeg",
- "filename": "996739f4-b563-428a-a6e8-ec3cb8bd03d4.jpeg"
}, - "format": {
- "id": "facebook-post",
- "width": 1200,
- "height": 1200
}, - "template": {
- "id": "64238d01-d402-474b-8c2d-fbc957e9d290",
- "name": "Ad campaign fall 2025",
- "type": "static",
- "created_at": 1649942114,
- "updated_at": 1649942114,
- "project_id": "9d1f2b7c-5a44-4c3e-9f21-0b8e6d4a1c73",
- "project_name": "Fall campaigns",
- "category_id": "9d1f2b7c-5a44-4c3e-9f21-0b8e6d4a1c73",
- "category_name": "Fall campaigns"
}
}Projects let you organize your designs into logical groups — for example by campaign, client, or content type. All designs must belong to a project to be accessible via the API. Use projects to scope design retrieval and keep large design libraries manageable.
Retrieve all projects in your workspace. Projects group designs into logical collections. Only designs that belong to a project are accessible via the API.
[- {
- "id": "9d00e9fe-9bd5-4471-acad-baa702a867e9",
- "name": "Animation Tests",
- "created_at_ts": 1654763057
}, - {
- "id": "bdf9ab44-f5fd-47ad-881d-a45906901233",
- "name": "HTML5 Tests",
- "created_at_ts": 1663767647
}
]Create a new project to organize your designs. Assign designs to this project in the Abyssale editor to make them accessible via the API.
| name required | string [ 2 .. 100 ] characters Name of the project |
{- "name": "Summer Campaign 2024"
}{- "id": "cb2c4add-4867-11f0-96f2-0a00d9eb8f78",
- "name": "Summer Campaign 2024",
- "created_at_ts": 1749827107
}Workspace templates are master designs shared across your organization. Duplicate them into any project to create editable copies — useful for spinning up campaign variants, onboarding new clients, or maintaining brand-consistent starting points. The duplication is processed asynchronously; poll the status endpoint to track progress.
Retrieve the organisation-level master designs shared across your workspace.
A workspace template lives in a category (category_id), not a project — it
belongs to the organisation until you duplicate it into a project with
POST /workspace-templates/{companyTemplateId}/use. Designs, which do live in a
project, are listed by GET /designs; the two listings never overlap.
Items have the same shape as a design, except the grouping fields are named
category_id / category_name.
| category_id | string <uuid> Unique identifier (UUID) of a workspace category. List the available ids with
|
| type | string Enum: "static" "animated" "printer" "printer_multipage" Filter workspace templates by one of these types static, animated, printer, printer_multipage |
[- {
- "id": "64238d01-d402-474b-8c2d-fbc957e9d290",
- "name": "Brand master",
- "type": "static",
- "created_at": 1649942114,
- "updated_at": 1649942114,
- "category_id": "1c7a9e35-0b62-4d18-8f4a-2e5c7b90d146",
- "category_name": "Brand assets"
}
]The categories that group your workspace templates. Use an id from here as the
category_id filter on GET /workspace-templates.
This is the workspace-template counterpart of GET /projects, which groups designs.
[- {
- "id": "1c7a9e35-0b62-4d18-8f4a-2e5c7b90d146",
- "name": "Brand assets",
- "color": "#4F46E5",
- "icon": "star",
- "created_at": 1649942114
}
]Copy a shared workspace template into one of your projects, creating an editable
design instance. The duplication is asynchronous — use the returned
duplication_request_id to poll for completion via
GET /design-duplication-requests/{duplicateRequestId}.
| companyTemplateId required | string <uuid> Example: 0c967bd0-4137-4690-ad70-249aa021c68b Unique identifier (UUID) of the workspace template to duplicate |
| project_id required | string <uuid> Target project ID where the template will be duplicated |
| name | string [ 2 .. 100 ] characters Optional custom name for the duplicated template |
{- "project_id": "d59adee9-4867-11f0-96f2-0a00d9eb8f78"
}{- "duplication_request_id": "40c32a4e-4869-11f0-96f2-0a00d9eb8f78"
}Poll the status of a template duplication request. Use this endpoint to track the progress of an asynchronous duplication operation.
| duplicateRequestId required | string <uuid> Example: 40c32a4e-4869-11f0-96f2-0a00d9eb8f78 Unique identifier (UUID) of the duplication request |
{- "request_id": "40c32a4e-4869-11f0-96f2-0a00d9eb8f78",
- "status": "COMPLETED",
- "created_at_ts": 1749827734,
- "completed_at_ts": 1749827736,
- "errored_at_ts": null,
- "target_project": {
- "id": "d59adee9-4867-11f0-96f2-0a00d9eb8f78",
- "name": "HTML5 Tests",
- "created_at_ts": 1749827125
}, - "designs": [
- {
- "original_design_id": "0c967bd0-4137-4690-ad70-249aa021c68b",
- "target_design_id": "afb1a61a-6c50-4bc3-a49b-3381822d4e81",
- "target_design_name": "New Design name"
}
]
}Alpha. This API is in Alpha. The contract may change without notice and imports created with it are not guaranteed to remain compatible. It is not covered by deprecation policy.
This applies to every operation in this section.
Import a design from a JSON structure. The flow is three steps:
/templates/import/json — Submit your template structure. Receive an import ID, a links block, and a flat uploads[] array of presigned S3 POSTs (one per image/logo layer whose src is a filename).fields./templates/import/json/{importId} — Validate the import. The edge API performs a pre-flight HEAD on every expected upload, then queues the import for backend processing. Use the links.dashboard URL to track progress in the platform.Dry-run validation: add ?validate_only=true to the POST to run the full schema plus every payload-only cross-field validator without creating an import or generating presigned URLs. Returns 204 No Content on success and the structured error contract on failure. Exactly three checks are excluded because they require I/O rather than the payload alone: unknown_font and project_not_found (database lookups) and unreachable_src (a network HEAD). A 204 therefore means the payload is structurally valid; it does not guarantee the referenced fonts, URLs and project exist.
Sync short-circuit: when every src is a public URL or data URI, the POST returns the import already in QUEUED and uploads[] is empty — no PUT needed.
Use GET /templates/import/json/{importId} to poll the import status at any time. The response includes next_check_after_ms (suggested polling cadence), structured warnings[] (non-fatal processing decisions), and a structured error object when status is FAILED.
Thumbnails are backend-generated — the payload carries no preview image field (preview_image is rejected as unknown_field). After the design is built, the backend renders every format and copies the first rendered visual to the design thumbnail; the import stays in PROCESSING while previews render and reaches DONE once the thumbnail exists.
Errors use a unified, machine-readable shape across all template-import endpoints:
{
"errors": [
{ "path": "layers[2].properties.font_size", "code": "out_of_range",
"message": "font_size must be between 2 and 1000",
"expected": { "min": 2, "max": 1000 }, "received": 1500 }
]
}
Round-trip helper: GET /designs/{designUuid}/as-import?as=v1 returns an existing static, printer or animated design in the exact shape POST /templates/import/json accepts — useful as an agent few-shot or for round-trip regression tests.
Printer designs (v2): type: "printer" produces single-page print PDF designs. The payload is authored in physical units: each printer format declares a required unit (mm | in) and all geometry in that format (format dims, layer layout, geometric properties, bleed_size/safe_size) is a float in that unit; font_size/min_font_size are floats in pt (0.48–240). Colors are stored as cmyk(c,m,y,k) / cmyka(c,m,y,k[,a]) tokens (integers 0–100, no %), but a solid hex value is accepted and converted to cmyk with a color_converted warning; a cmyk token on a static/animated import is likewise converted to hex. Conversion uses no ICC profile and is approximate — author cmyk(...) directly when the exact print color matters. Gradients on printer stay rejected with unsupported_for_type, because converting one would collapse it to a flat fill. Format sizes and layer layouts must additionally stay within the platform's pixel limits when converted at the fixed 300 DPI (violations → out_of_range; individual geometric properties carry no upper bound on printer; 300 DPI is only this layout bridge — the stored per-format render DPI is computed by the backend, capped at 300, and surfaced read-only on GET /designs/{designId}). Printer layer assets uploaded via presigned URL may be up to 500 MB (20 MB for static); inline data URIs are capped at 2 MB decoded on every design type — the overall request body is capped at 20 MB; TIFF layer sources are accepted on printer and printer_multipage — print originals are stored untouched (ICC profile preserved) and a display proxy is generated automatically for TIFF or >20 MB inputs.
Animated designs (v3): type: "animated" produces animated (video) designs. The payload is px-authored like static (integer geometry, hex colors) plus a required top-level animation object: duration (float seconds, 1–60 — the timeline length) and optional screenshot_at_s (0 ≤ value ≤ duration, default = duration — the moment the HTML5 backup screenshot is taken). Each layer may carry an optional animation object: start_at_s (default 0), end_at_s (default = duration; must be ≤ duration and > start_at_s), and tweens — { type: slide | fade | scale | rotate | audioFade | typeWriting, keyframes: [...] }. Keyframe items are deliberately loose (raw objects — several keyframe schema generations coexist); the only requirement is that every keyframe carries a numeric data.time. Tween ids are generated by the backend and are not payload fields. Two new layer types exist on animated only: video (src mp4/webm + muted) and audio (src mp3/wav + muted + max_volume 0–1; muted: true renders silent). Every animated format must fit within a 1920×1200 bounding box in either orientation (max side ≤ 1920 and min side ≤ 1200 — violations → out_of_range). Layer assets uploaded via presigned URL are capped per layer type on animated — 100 MB video, 25 MB audio, 20 MB image/logo; inline data URIs stay capped at 2 MB decoded. animation on static/printer, video/audio layers off-animated, and group auto_layout/direction/placement/gap on animated are rejected (unsupported_for_type / unknown_enum_value).
Multipage print designs (v6): type: "printer_multipage" produces a multi-page print PDF. All pages of a multipage design have the same dimensions, so the print setup is declared once at the top level: required unit (mm | in), width, height, plus optional bleed_size/safe_size (floats in unit) and an optional global dpi (integer 10–300). Instead of formats + top-level layers, the payload carries a top-level ordered pages array (1–30 pages; the backend names them page_1 … page_N in array order). A page object carries only background_color (required) and its own layers array (1–80 layers). A page is single-format, so its layer layout/properties may be flat or keyed under default; any other key on a page, including the print settings and name, is rejected with unknown_field. Every per-layer rule inherits type: "printer" unchanged (cmyk color storage with hex converted and gradients rejected, physical-unit geometry, pt font sizes 0.48–240, TIFF sources, 500 MB presigned / 2 MB data-URI / 1 MB qrcode-icon caps, shadow/text_shadow/pattern_name/pattern_color/overlay_* accepted and dropped with a property_dropped_for_type warning, gradients rejected). There are no shared layers and no layer ordering field — an element repeated across pages is included in each page. dpi is one global optional field (10–300) capped at the document's single area-based ceiling; omitted, the backend computes that ceiling as the default for every page. GET /designs/{designId}/as-import?as=v1 emits the same top-level-print-setup + pages[] shape and multipage designs round-trip. GET /designs/{designId} returns pages[] + elements_per_page for multipage designs (no formats), and GET /designs/{designId}/formats/page_N reads a single page.
Company API keys only. User API keys are not accepted on these routes.
Alpha. This API is in Alpha. The contract may change without notice and imports created with it are not guaranteed to remain compatible. It is not covered by deprecation policy.
List this company's JSON template imports, newest first.
Use this to recover an import whose id you no longer have. Because the flow is
POST → upload → PUT, a client that loses the id between those steps leaves behind an
import it cannot otherwise resume or inspect; listing is how you find it again.
Rows are summaries. The unbounded warnings[] array and the error object are served by
GET /templates/import/json/{importId} — a row carries warning_count instead, and a
links.status pointing at that endpoint.
status filters on the effective status, matching what the item endpoint reports: an
import whose presigned upload window has closed counts as FAILED, not
WAITING_FOR_VALIDATION, even though the stored row is untouched.
Lists JSON imports exclusively — imports created through other channels are not returned.
No cursor. Results are capped by limit; narrow with created_after /
created_before to reach imports beyond the cap.
| status | string Enum: "WAITING_FOR_VALIDATION" "QUEUED" "PROCESSING" "DONE" "FAILED" Return only imports whose effective status matches. |
| created_after | integer Unix timestamp (seconds). Return imports created at or after this moment. |
| created_before | integer Unix timestamp (seconds). Return imports created at or before this moment. |
| limit | integer [ 1 .. 200 ] Default: 50 Maximum number of imports to return. |
[- {
- "id": "64238d01-d402-474b-8c2d-fbc957e9d290",
- "name": "Autumn card",
- "status": "WAITING_FOR_VALIDATION",
- "created_at_ts": 1649942114,
- "validated_at_ts": 1649942200,
- "warning_count": 3,
- "result": {
- "kind": "template",
- "uuid": "095be615-a8ad-4c33-8e9c-c7612fbf6c9f",
- "project_uuid": "1de37675-f868-4726-bb1d-b08d89f463f9"
}
}
]Alpha. This API is in Alpha. The contract may change without notice and imports created with it are not guaranteed to remain compatible. It is not covered by deprecation policy.
Submit a template structure as JSON. Returns an import ID, a links block, and a flat
uploads[] array of presigned S3 POSTs for any image/logo src filenames. When every
src is a public URL or data URI, the import is returned already in QUEUED with an
empty uploads[] — no PUT call is needed.
The payload carries no preview image: design thumbnails are generated by the backend
after import (every format is rendered and the first visual becomes the thumbnail; the
import reports PROCESSING while previews render).
Dry run: pass ?validate_only=true to validate the payload without creating an
import. Runs the full schema and all payload-only structural validators (conflicting
filename sources, data-URI validity and decoded-size caps, TIFF type constraints,
video/audio extensions, animated keyframes, qrcode icons). Excluded: unknown_font,
project_not_found (database lookups) and unreachable_src (network HEAD).
Returns 204 No Content on success.
Company API keys only. User API keys receive 401 Unauthorized.
| validate_only | boolean Default: false When |
| strict | boolean Default: false When |
| name required | string [ 1 .. 100 ] characters |
| type required | string Enum: "static" "printer" "animated" "printer_multipage" Design type. |
object What the import produces. Defaults to | |
Array of objects (TemplateImportJsonFormat) [ 1 .. 20 ] items List of formats (1-20). Required on | |
Array of objects (TemplateImportJsonLayer) [ 1 .. 80 ] items Required on | |
| unit | string Enum: "mm" "in"
|
| width | number > 0
|
| height | number > 0
|
| bleed_size | number >= 0
|
| safe_size | number >= 0
|
| dpi | integer [ 10 .. 300 ]
|
Array of objects (TemplateImportJsonPage) [ 1 .. 30 ] items
| |
object Animated designs only — required when |
{- "name": "My YouTube Template",
- "type": "static",
- "target": {
- "kind": "template",
- "project_uuid": "1de37675-f868-4726-bb1d-b08d89f463f9"
}, - "formats": [
- {
- "name": "youtube-thumbnail",
- "width": 1280,
- "height": 720,
- "background_color": "#ffffff",
- "unit": "mm",
- "bleed_size": 0,
- "safe_size": 0
}
], - "layers": [
- {
- "name": "title",
- "type": "image",
- "hidden": false,
- "locked": false,
- "layer_ids": [
- "headline",
- "subtitle"
], - "layout": {
- "youtube-thumbnail": {
- "x": 57,
- "y": 67,
- "width": 715,
- "height": 383
}
}, - "properties": {
- "default": {
- "payload": "Hello World",
- "color": "#ffffff",
- "font_size": 108
}, - "youtube-thumbnail": {
- "font_size": 80
}
}, - "animation": {
- "start_at_s": 0,
- "end_at_s": 0,
- "tweens": [
- {
- "type": "fade",
- "keyframes": [
- {
- "attr": "opacity",
- "data": {
- "type": "start",
- "time": 0
}
}, - {
- "attr": "opacity",
- "data": {
- "type": "end",
- "time": 1.5
}
}
]
}
]
}
}
], - "unit": "mm",
- "width": 210,
- "height": 297,
- "bleed_size": 0,
- "safe_size": 0,
- "dpi": 300,
- "pages": [
- {
- "background_color": "cmyk(0,0,0,0)",
- "layers": [
- {
- "name": "title",
- "type": "image",
- "hidden": false,
- "locked": false,
- "layer_ids": [
- "headline",
- "subtitle"
], - "layout": {
- "youtube-thumbnail": {
- "x": 57,
- "y": 67,
- "width": 715,
- "height": 383
}
}, - "properties": {
- "default": {
- "payload": "Hello World",
- "color": "#ffffff",
- "font_size": 108
}, - "youtube-thumbnail": {
- "font_size": 80
}
}, - "animation": {
- "start_at_s": 0,
- "end_at_s": 0,
- "tweens": [
- {
- "type": "fade",
- "keyframes": [
- {
- "attr": "opacity",
- "data": {
- "type": "start",
- "time": 0
}
}, - {
- "attr": "opacity",
- "data": {
- "type": "end",
- "time": 1.5
}
}
]
}
]
}
}
]
}
], - "animation": {
- "duration": 8,
- "screenshot_at_s": 8
}
}{- "id": "64238d01-d402-474b-8c2d-fbc957e9d290",
- "status": "WAITING_FOR_VALIDATION",
- "uploads": [
- {
- "target": "layer",
- "name": "hero",
- "fields": {
- "property1": "string",
- "property2": "string"
}, - "max_bytes": 20000000,
- "expires_at_ts": 1749831334
}
]
}Alpha. This API is in Alpha. The contract may change without notice and imports created with it are not guaranteed to remain compatible. It is not covered by deprecation policy.
Poll the status of a JSON template import. Scoped to the caller's company.
Returns next_check_after_ms (suggested delay before the next poll, null on terminal states),
a stable append-only warnings[] array, and a structured error object when status is FAILED.
Expired upload windows. An import that reaches WAITING_FOR_VALIDATION and is never
validated before its presigned upload window closes is reported as terminal:
status: "FAILED", next_check_after_ms: null, no validate link, and
error: {path: "uploads", code: "missing_assets", ...}. This is derived at read time from
the import's creation timestamp — the GET performs no write, so the stored row keeps its
original state. A poller should therefore always drive off terminal states rather than
waiting indefinitely.
| importId required | string <uuid> Import UUID returned by the POST endpoint. |
{- "id": "64238d01-d402-474b-8c2d-fbc957e9d290",
- "name": "Holiday Campaign",
- "status": "PROCESSING",
- "created_at_ts": 1234567890,
- "validated_at_ts": null,
- "next_check_after_ms": 2000,
- "warnings": [
- {
- "path": "layers[2].properties.font_size",
- "layer": "headline",
- "code": "out_of_range",
- "message": "font_size must be between 2 and 1000",
- "expected": null,
- "received": null
}
], - "error": {
- "path": "layers[2].properties.font_size",
- "layer": "headline",
- "code": "out_of_range",
- "message": "font_size must be between 2 and 1000",
- "expected": null,
- "received": null
}, - "result": {
- "kind": "design",
- "uuid": "095be615-a8ad-4c33-8e9c-c7612fbf6c9f",
- "project_uuid": "1de37675-f868-4726-bb1d-b08d89f463f9"
}
}Alpha. This API is in Alpha. The contract may change without notice and imports created with it are not guaranteed to remain compatible. It is not covered by deprecation policy.
Call this after every entry of uploads[] has been POSTed to S3. The edge API performs a
pre-flight HEAD on every expected key. If any are missing, it returns
422 missing_assets with the list of unfulfilled targets.
On success, the import status transitions to QUEUED and backend processing picks it up.
PUT is idempotent on QUEUED rows. PUT on a PROCESSING, DONE, or FAILED row returns
409 template_import_already_processed.
| importId required | string <uuid> Import UUID returned by the POST endpoint. |
{- "id": "64238d01-d402-474b-8c2d-fbc957e9d290",
- "status": "QUEUED",
- "next_check_after_ms": 2000,
}Alpha. This API is in Alpha. The contract may change without notice and imports created with it are not guaranteed to remain compatible. It is not covered by deprecation policy.
Return an existing static, printer, animated or printer_multipage design serialized into
the exact shape that POST /templates/import/json accepts. Useful as a few-shot example
for AI agents or as a round-trip regression test (as-import → POST → as-import, diff
should be empty).
The returned src values are Abyssale-hosted https:// URLs — agents do not need to
re-upload assets. The response always validates against the import schema before being
returned; designs using features outside the import schema yield 400 not_round_trippable
with structured details.
Printer designs: the payload is emitted verbatim in the physical-unit space the
import contract is authored in — geometry as floats in each format's unit (mm | in),
font sizes in pt (no DPI conversion). cmyk color tokens are normalised to the canonical
cmyk()/cmyka() grammar, and a legacy RGB-authored (hex) solid color is converted to
cmyk with a color_converted warning rather than failing the export; only a gradient on a
printer design still fails with not_round_trippable. Star geometry exceeding the v1
pixel-equivalence caps is clipped with a warning (donor fidelity — import no longer
enforces those caps).
bleed_size/safe_size round-trip (read from the stored print settings and emitted on
the exported formats), as do rectangle-shape strokes, untouched print image originals
(the exported src prefers the original over any display proxy), and qrcode center
icons (emitted as data URIs; the error-correction level is backend-chosen and never
emitted). dpi round-trips as well: it is an import field (int 10–300, per format on
printer, capped by the format's area-based ceiling), so the stored value is emitted and
re-import reuses it rather than recomputing. A structured
print_settings_not_recoverable warning is emitted when the print settings cannot be
read at all.
Animated designs: the export carries the full animation model — top-level
animation (duration from the stored timeline length, screenshot_at_s when stored)
and per-layer animation (start_at_s/end_at_s timing plus tweens with keyframes
passed through verbatim; stored tween ids are dropped — re-import regenerates them).
video/audio layers export their hosted source URL as src plus muted/max_volume.
Legacy designs with no stored timeline duration derive it from element timing and emit a
structured animation_duration_derived warning; designs whose formats exceed the
1920×1200 either-orientation bounding box (legacy data only) fail with
not_round_trippable. Values outside the import bounds (duration, timing) are clipped
with warnings.
Multipage print designs (printer_multipage): the export emits the document-wide
print setup at the top level (unit/width/height, plus bleed_size/safe_size/dpi
when recoverable) and a pages[] array whose entries carry only background_color and
layers — each page transformed via the printer path (geometry verbatim in the
physical-unit space, colors normalised to cmyk, no DPI conversion). An image/logo layer whose slot
was never filled is emitted with no src and keeps its layout, on every design type —
src is optional on import so an empty slot round-trips. A legacy design whose stored pages disagree on a print
setting cannot be expressed in the uniform shape: the first page's value is emitted with a
print_settings_not_recoverable warning at width/height/unit, bleed_size, safe_size
or dpi (a divergent dpi is omitted entirely so the backend recomputes it). A stored
dpi above the document's ceiling is clamped to the ceiling with the same warning rather
than failing the export. An export left with no layers at all yields
not_round_trippable.
| designUuid required | string <uuid> UUID of an existing design. |
| as | string Default: "v1" Value: "v1" Export-contract version. Pin to |
{- "payload": {
- "name": "My YouTube Template",
- "type": "static",
- "target": {
- "kind": "template",
- "project_uuid": "1de37675-f868-4726-bb1d-b08d89f463f9"
}, - "formats": [
- {
- "name": "youtube-thumbnail",
- "width": 1280,
- "height": 720,
- "background_color": "#ffffff",
- "unit": "mm",
- "bleed_size": 0,
- "safe_size": 0
}
], - "layers": [
- {
- "name": "title",
- "type": "image",
- "hidden": false,
- "locked": false,
- "layer_ids": [
- "headline",
- "subtitle"
], - "layout": {
- "youtube-thumbnail": {
- "x": 57,
- "y": 67,
- "width": 715,
- "height": 383
}
}, - "properties": {
- "default": {
- "payload": "Hello World",
- "color": "#ffffff",
- "font_size": 108
}, - "youtube-thumbnail": {
- "font_size": 80
}
}, - "animation": {
- "start_at_s": 0,
- "end_at_s": 0,
- "tweens": [
- {
- "type": "fade",
- "keyframes": [
- {
- "attr": "opacity",
- "data": {
- "type": "start",
- "time": 0
}
}, - {
- "attr": "opacity",
- "data": {
- "type": "end",
- "time": 1.5
}
}
]
}
]
}
}
], - "unit": "mm",
- "width": 210,
- "height": 297,
- "bleed_size": 0,
- "safe_size": 0,
- "dpi": 300,
- "pages": [
- {
- "background_color": "cmyk(0,0,0,0)",
- "layers": [
- {
- "name": "title",
- "type": "image",
- "hidden": false,
- "locked": false,
- "layer_ids": [
- "headline",
- "subtitle"
], - "layout": {
- "youtube-thumbnail": {
- "x": 57,
- "y": 67,
- "width": 715,
- "height": 383
}
}, - "properties": {
- "default": {
- "payload": "Hello World",
- "color": "#ffffff",
- "font_size": 108
}, - "youtube-thumbnail": {
- "font_size": 80
}
}, - "animation": {
- "start_at_s": 0,
- "end_at_s": 0,
- "tweens": [
- {
- "type": "fade",
- "keyframes": [
- {
- "attr": "opacity",
- "data": {
- "type": "start",
- "time": 0
}
}, - {
- "attr": "opacity",
- "data": {
- "type": "end",
- "time": 1.5
}
}
]
}
]
}
}
]
}
], - "animation": {
- "duration": 8,
- "screenshot_at_s": 8
}
}, - "warnings": [
- {
- "path": "layers[2].properties.font_size",
- "layer": "headline",
- "code": "out_of_range",
- "message": "font_size must be between 2 and 1000",
- "expected": null,
- "received": null
}
], - "version": "v1"
}Dynamic images are live image URLs that render on-the-fly based on query parameters. Once created for a design, the URL can be embedded anywhere — emails, websites, social posts — and customized with different text, colors, or images per use case, without additional API calls. Ideal for personalization at scale.
Creates a dynamic image URL for a given design. Only one dynamic image is allowed per design. Subsequent calls return the existing dynamic image. Use enable_rate_limit in the request body to enable API rate limiting for this image & enable_production_mode to enable production mode (default is test mode).
| designId required | string Unique identifier of the design |
| enable_rate_limit | boolean Default: false Enable API rate limiting for this dynamic image |
| enable_production_mode | boolean Default: false Enable Production mode for this dynamic image |
{- "enable_rate_limit": true,
- "enable_production_mode": true
}{- "id": "a12b3c4d-5e6f-7890-abcd-ef1234567890",
- "design_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
- "formats": [
- {
- "id": "one-format",
- "uid": "11111111-2222-3333-4444-555555555555",
- "width": 3333,
- "unit": "px",
- "height": 666,
}, - {
- "id": "another-format",
- "uid": "66666666-7777-8888-9999-000000000000",
- "width": 1920,
- "unit": "px",
- "height": 1080,
}
]
}