Export a design as an import payload
Alpha
The contract may change without notice, and imports created with it are not guaranteed to remain compatible. Not covered by the deprecation policy.
Sample request
curl -X GET "https://api.abyssale.com/designs/$DESIGN_ID/as-import" \
-H "x-api-key: $ABYSSALE_API_KEY"Example 200 response
{
"payload": {
"name": "My YouTube Template",
"type": "static",
"target": {
"kind": "design",
"project_uuid": "00000000-0000-0000-0000-000000000000"
},
"formats": [
{
"name": "youtube-thumbnail",
"width": 1280,
"height": 720,
"background_color": "#ffffff",
"bleed_size": 0,
"safe_size": 0,
"dpi": 300
}
],
"layers": [
{
"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
}
}
}
]
},
"warnings": [
{
"message": "string",
"code": "color_converted",
"path": "layers[2].properties.color",
"layer": "headline"
}
],
"version": "v2026-08-21"
}Example 400 error response
{
"id": "out_of_range",
"message": "formats[0].width: Must be greater than or equal to 1 and less than or equal to 5000.",
"errors": [
{
"path": "formats[0].width",
"code": "out_of_range",
"message": "Must be greater than or equal to 1 and less than or equal to 5000.",
"expected": {
"min": 1,
"max": 5000
},
"received": 99999999
}
]
}Return an existing design — any type, printer_multipage included — serialized into the exact shape POST /designs/import/json accepts. Useful as a worked example for an agent, or as a round-trip regression test (as-import → POST → as-import should diff empty).
Behaviour, limits and caveats
src values are Abyssale-hosted https:// URLs, so no asset needs re-uploading. The response is validated against the import schema before it is returned: a design holding anything the schema cannot express answers 400 not_round_trippable with structured details.
Stored state the contract has no field for is dropped and named in warnings[], never folded into a field that means something else — a re-import reproduces the design minus what was reported, never different artwork.
Values outside the import bounds are clipped and warned about rather than failing the export. What cannot degrade fails instead: a gradient on a print design, a printer_multipage whose pages disagree on their unit (each page's geometry is expressed in its own unit, so one ruler cannot describe them), an animated format outside the 1920×1200 box, and an export left with no layers.
Rate limit: shares the write budget with POST /designs/import/json — 60 requests/minute and 1 200/hour across both, so a round trip costs two calls and 1 200/hour is 600 export–edit–import cycles. See Rate limits.
Path parameters
designId string (uuid) required
Unique identifier (UUID) of the design. A value that is not a UUID does not match the route and answers 404 with id: endpoint_not_found.
Response 200
Envelope containing the import payload. payload is ready to be POSTed to /designs/import/json (for target.kind=design re-imports, add a target object — the payload never contains one). warnings[] describes values that were clipped, converted or dropped to stay within the export contract.
payload DesignImportRequest always
Import payload. static / printer / animated designs carry formats + top-level layers (both required). printer_multipage designs instead carry a top-level pages array (and no formats/layers) — see the pages and type fields.
Show child attributes
name string always
Length 1–100 characters.
type string always
Design type. One of "static", "printer", "animated", "printer_multipage".
More about type
Design type. static = single image; printer = single-page print PDF; animated = animated/video design; printer_multipage = multi-page print PDF. Printer payloads are authored in physical units — the design declares unit (mm | in) once at the top level, geometry is floats in that unit, font sizes are pt, and colors are stored as cmyk (a solid hex is converted with a color_converted warning; gradients are rejected). Animated payloads are px-authored like static and require a top-level animation object; they may use video/audio layers and per-layer animation timing/tweens.
Muting is spelled differently on each of the three surfaces, and the type changes too. Importing takes muted (a boolean, on the video/audio layer properties); reading a design returns it as the attributes video_muted / audio_muted (booleans); overriding it at generation time takes audio_muted as a number 0–1, where true/false answer 400. Convert deliberately when moving a value between them. Multipage payloads (printer_multipage) drop formats/layers, declare the print setup once at the top level (unit/width/height required, bleed_size/safe_size/dpi optional), and carry a top-level ordered pages array (1–30 pages); each page is a self-contained print page (own background_color + layers, inheriting the document-wide print setup) that inherits every per-layer printer rule. No shared layers exist — a repeated element is included per page.
target object
Where the import lands.
More about target
Where the import lands. An import creates a design — the thing you can generate from — so this defaults to { "kind": "design" } and project_uuid is required: a design lives in a project, and one without a project is not returned by GET /designs. Omitting target entirely therefore fails with missing_required at target.project_uuid.
Pass kind: workspace_template for the other variant: a reusable blueprint in the workspace library, which has no project and must be duplicated into one before you can generate from it. Only designs are addressable via GET /designs/{uuid}/as-import.
Show child attributes
kind string always
Required WHENEVER target is present. The design default applies only to an entirely absent target; sending {"project_uuid": "..."} with no kind is missing_required at target.kind. One of "design", "workspace_template".
project_uuid string (uuid)
Required for kind=design; forbidden for kind=workspace_template.
formats array of DesignImportFormat
List of formats (1-20). 1–20 items.
More about formats
List of formats (1-20). Required on static/printer/animated; omit on printer_multipage (use pages instead). A count outside 1-20 is rejected with out_of_range at formats. background_color is required on each format. There is no preview-image field — thumbnails are rendered for you (a preview_image key is rejected as unknown_field).
Show child attributes
name string always
Format name, 1–100 characters, matching ^[a-z0-9][a-z0-9_-]*$ — lowercase letters, digits, - and _, starting with a letter or digit. Anything else (spaces, capitals) is invalid_payload. Must be unique within formats (duplicate_format_name) and must not be a platform-reserved name (reserved_format_name). Length 1–100 characters.
width number always
The upper bound depends on the design type, so the schema declares none. Minimum 0. Greater than true.
More about width
The upper bound depends on the design type, so the schema declares none. static: width in pixels, integer 1–5000. printer: a float in the design's unit (mm | in), no fixed upper bound of its own — its pixel equivalent at 300 DPI must stay within 1–20000, else out_of_range. On animated: integer pixels, and the format must additionally fit within a 1920×1200 bounding box in either orientation (max(width, height) ≤ 1920 and min(width, height) ≤ 1200 — out_of_range otherwise).
height number always
The upper bound depends on the design type, so the schema declares none. Minimum 0. Greater than true.
More about height
The upper bound depends on the design type, so the schema declares none. static: height in pixels, integer 1–5000. printer: a float in the design's unit (mm | in), no fixed upper bound of its own — its pixel equivalent at 300 DPI must stay within 1–20000, else out_of_range. On animated: integer pixels, and the format must additionally fit within a 1920×1200 bounding box in either orientation (max(width, height) ≤ 1920 and min(width, height) ≤ 1200 — out_of_range otherwise).
background_color string always
On static/animated: hex (#RRGGBB / #RRGGBBAA) or a gradient; a cmyk token is converted to hex with a color_converted warning.
More about background_color
On static/animated: hex (#RRGGBB / #RRGGBBAA) or a gradient; a cmyk token is converted to hex with a color_converted warning. On type: "printer": a cmyk(c,m,y,k) / cmyka(c,m,y,k[,a]) token (integers 0–100) is what gets stored — a solid hex value is accepted and converted to cmyk with the same warning, while a gradient is rejected with unsupported_for_type.
bleed_size number
Printer only. Bleed size as a float in the design's unit; presence enables bleed on the created design. Forbidden on static (unsupported_for_type). Minimum 0.
safe_size number
Printer only. Safe-zone size as a float in the design's unit; presence enables the safe zone on the created design. Forbidden on static (unsupported_for_type). Minimum 0.
dpi integer
Printer only, optional. Range 10–300.
More about dpi
Printer only, optional. Render DPI for this format. Beyond the 10–300 field range, the value is capped by an area-based ceiling computed from the format size, so a large format rejects a high DPI with out_of_range; omitted, the backend computes the ceiling itself. Forbidden on static/animated (unsupported_for_type). On printer_multipage dpi is a single document-level value instead — a page carries no print settings of its own.
layers array of DesignImportLayer
Required on static/printer/animated; omit on printer_multipage (each page carries its own flat layers). 1-80 layers - an empty array is rejected with out_of_range at layers (expected: {min: 1, max: 80}), since required enforces key presence and not non-emptiness. 1–80 items.
More about layers
Required on static/printer/animated; omit on printer_multipage (each page carries its own flat layers). 1-80 layers - an empty array is rejected with out_of_range at layers (expected: {min: 1, max: 80}), since required enforces key presence and not non-emptiness. Layers defined once at the top level. Each layer contains layout and properties keyed by format name. A "default" key may be used in either dict — it is merged under each per-format entry before validation, so overrides only need to specify the fields that differ.
Show child attributes
name string always
Layer name, unique within the design (duplicate_layer_name). On printer_multipage uniqueness is scoped to the page, so two pages may reuse a name. Length 1–40 characters.
type string always
video and audio are accepted on type: "animated" designs only — on static/printer the enum simply doesn't include them (unknown_enum_value). The order above is the contractual order surfaced in an unknown_enum_value problem's expected list. One of "image", "logo", "text", "shape", "group", "button", "rating", "qrcode", "illustration", "video", "audio".
hidden boolean | object
Hide the layer. Defaults to false.
More about hidden
Hide the layer. Either a single boolean applying to every format, or an object keyed by format name when the layer is hidden in some formats and visible in others — the editor allows that, and collapsing it to one boolean would remove the layer from a format where it is meant to show.
A multi-format design MAY still send the flat form: "hidden everywhere" is a complete answer, unlike a flat layout. A missing format key means false; a default key covers the formats not named.
locked boolean | object
Lock the layer against editing. Same flat-or-format-keyed shape as hidden. Defaults to false.
layer_ids array of string
group layers only — required there, rejected anywhere else. Omitting it on a group is missing_required; sending it on any other layer type is unsupported_for_type.
More about layer_ids
group layers only — required there, rejected anywhere else. Omitting it on a group is missing_required; sending it on any other layer type is unsupported_for_type. Names of the layers this group contains. Declared here beside layout/properties rather than inside the group's property object, because a group's membership is one list for the whole design — the same children in every format, so per-format membership is unrepresentable by design. Each entry must name a layer declared in the same format, otherwise conditional_dependency_missing.
layout DesignImportLayoutBox | object always
Keyed by format name.
More about layout
Keyed by format name. Each value is { x, y, width, height }. A "default" key is allowed and will be merged under each per-format entry. Single-format designs may instead pass the { x, y, width, height } object directly (flat form, no format key). The same applies to properties.
On static designs all four values are integer pixels (bounds below). On printer designs they are floats in the design's unit (mm | in); each value's pixel equivalent at the fixed 300 DPI must stay within the same bounds (out_of_range otherwise).
properties DesignImportStaticLayerProperties | DesignImportPrinterLayerProperties | DesignImportAnimatedLayerProperties | object always
The layer's property set.
More about properties
The layer's property set. Either flat ({ "payload": "Hi", ... }) or keyed by format name ({ "youtube-thumbnail": { ... } }, plus an optional "default" key merged under every per-format entry). A single-format design may use either shape; a multi-format design must key, and must cover every format. The two are told apart by key name: a top-level key that is a declared property of the layer's type means the flat form. layout follows exactly the same rule.
Which property object is valid depends on the layer's type and on the design type. The three anyOf branches below are the generated, per-design-type unions — DesignImportStaticLayerProperties, DesignImportPrinterLayerProperties and DesignImportAnimatedLayerProperties — each fanning out to one schema per layer type. They are generated from the schemas that validate the request, so their fields, bounds, enums and defaults are exact. The fourth branch is the format-keyed envelope, whose values are those same objects.
A generic JSON-Schema validator cannot enforce the pairing, so do not trust a green result here. The branch is selected by the layer's own type, and OpenAPI 3.0 has no way to tie an anyOf to a sibling field. A property set that is legal for some layer type therefore validates for every layer type — alignment on a text layer passes offline validation and then answers 400 unknown_field, because alignment belongs to button/image/logo/illustration/group and a text layer takes text_align + vertical_align. Check a payload against the layer type you are actually sending, or against the API with ?validate_only=true.
Printer designs (type: "printer" / printer_multipage): every *_color field, stroke.color included, is a cmyk(c,m,y,k) / cmyka(c,m,y,k[,a]) token (integers 0–100, no %). A solid hex is accepted and converted, reported as color_converted; inline markup inside a text payload is exempt and stays hex. Conversion is approximate — no ICC profile — so author cmyk(...) directly when the exact print colour matters. Gradients are rejected (unsupported_for_type). Geometric values (stroke.width, radius, padding, gap, star_dimension, star_margin) are floats in the design's unit with no upper bound; only the format size and the layer layout are checked against the pixel bounds at 300 DPI (out_of_range). font_size/min_font_size are floats in pt, 0.48–240.
shadow, text_shadow, pattern_name, pattern_color and overlay are accepted and dropped — validated in full, then removed, one property_dropped_for_type warning per occurrence naming the layer path and the property. overlay is one nested object, so it drops as a unit and reports a single warning. Because they are still validated, a gradient in pattern_color is invalid_payload. filter_name stays allowed.
Animated designs (type: "animated"): px/hex like static (a cmyk token is converted to hex with a color_converted warning). Group auto_layout, direction, alignment and gap are rejected (unsupported_for_type). A video src is a filename or URL ending .mp4/.webm or a data:video/mp4|data:video/webm URI; an audio src is .mp3/.wav or data:audio/mpeg|data:audio/wav. On both, an extension is checked only when present: an extensionless https:// URL is accepted and the worker probes the content type. A media extension in an image/logo src is rejected (unsupported_for_type).
Asset fields: src (and qrcode icon_src) accepts a filename — which returns a presigned S3 POST in uploads[] — an https:// URL, or a data URI decoded and uploaded server-side at POST time (≤ 2 MB decoded on every design type). A qrcode's centre icon is icon_src and accepts the same three forms as src, with a data URI capped at 1 MB decoded rather than 2 MB. There is no icon property. The QR error-correction level is chosen for you (Q with an icon, M without) and is not a payload field.
Conditional dependencies the flat schemas cannot express: mask_name: "rounded_corners" requires mask_properties.radius; filter_name: "duotone" requires filter_properties.name; each entry of a group's layer-level layer_ids must name a layer declared in the same format. Violations are reported as conditional_dependency_missing.
filter_properties.name must additionally be one of the named duotone presets — blue_green, blue_orange, deep_green_light_green, BrightRed_light_yellow, brown_pale_green, brown_beige, deep_blue_green, deep_blue_red, deep_purple_orange, deep_purple_beige, deep_purple_pale_green, deep_blue_pale_green. An unlisted name is rejected with unknown_enum_value.
Groups carry their per-format geometry like every other layer type, but their MEMBERSHIP is not per-format: layer_ids sits on the layer itself and names the same children in every format. A group may also carry the common layer-level hidden/locked attributes: the platform keeps a group consistent with its members automatically — setting them on import propagates the flag down to the members (per format), and on reads (GET /designs/{designId}/formats/{formatSpecifier}, as-import) a group's hidden/locked are computed from its members (all members locked ⇒ group locked; all hidden ⇒ group hidden).
animation object
Animated designs only (unsupported_for_type on static/printer). Per-layer timeline timing and tweens — format-global (not keyed by format name). Layers without it span the whole timeline.
unit string
Print designs only (printer and printer_multipage) — required. Physical authoring unit for everything in the document: format/page size, bleed and safe sizes, and every layer's geometry in every format. One of "mm", "in".
More about unit
Print designs only (printer and printer_multipage) — required. Physical authoring unit for everything in the document: format/page size, bleed and safe sizes, and every layer's geometry in every format. Declared exactly once, because a print design has one ruler — it does not change between its formats. Missing → missing_required at unit; present on static/animated → unsupported_for_type. A format carrying its own unit is rejected with unknown_field.
width number
printer_multipage designs only — required. Page width as a float in unit, shared by every page. Its pixel equivalent at 300 DPI must stay within 1–20000 (out_of_range at width). Minimum 0. Greater than true.
height number
printer_multipage designs only — required. Page height as a float in unit, shared by every page. Same 1–20000 px-equivalence bound, reported at height. Minimum 0. Greater than true.
bleed_size number
printer_multipage designs only. Optional bleed size as a float in unit; presence enables bleed on every page. Minimum 0.
safe_size number
printer_multipage designs only. Optional safe-zone size as a float in unit; presence enables the safe zone on every page. Minimum 0.
dpi integer
printer_multipage designs only. Optional global render DPI for the whole document. Range 10–300.
More about dpi
printer_multipage designs only. Optional global render DPI for the whole document. Because the document has a single page size there is exactly one area-based ceiling min(300, floor(72*sqrt(225000000/(w_pt*h_pt)))); a higher value is rejected with out_of_range at dpi (carrying expected: {max: <ceiling>} and received). Omitted ⇒ the backend computes that same ceiling as the default, identically for every page. On printer designs dpi is a per-format field instead.
pages array of DesignImportPage
printer_multipage designs only (replaces formats + top-level layers; forbidden on other types). 1–30 items.
More about pages
printer_multipage designs only (replaces formats + top-level layers; forbidden on other types). Ordered array of 1–30 self-contained print pages — array order is the page order (the backend names them page_1 … page_N). A count outside 1–30 → out_of_range at pages. All pages share one size, so the print setup (unit/width/height/bleed_size/safe_size/dpi) is declared once at the top level; a page object carries only background_color and a flat layers array (one format per page).
Show child attributes
background_color string always
A cmyk(c,m,y,k) / cmyka(c,m,y,k[,a]) token (integers 0-100) - required.
More about background_color
A cmyk(c,m,y,k) / cmyka(c,m,y,k[,a]) token (integers 0-100) - required. A solid hex value is accepted and converted to cmyk with a color_converted warning; a gradient is rejected with unsupported_for_type — same as type: "printer". This is the one setting that stays per page, because a cover legitimately differs from an inside page.
layers array of DesignImportLayer always
This page's layers, authored in the document-level unit. 1–80 items.
More about layers
This page's layers, authored in the document-level unit. A page is single-format, so each layer's layout/properties may be given flat or keyed under default — any other key is rejected with unknown_format_key. Either way the stored structure is flat. Same layer shapes and per-layer rules as type: "printer". An empty array is rejected with out_of_range at pages[i].layers (expected: {min: 1, max: 80}).
animation object
Animated designs only — required when type is animated, rejected with unsupported_for_type on static/printer. The design's timeline.
Show child attributes
duration number always
Timeline length in seconds (1–60). The authoritative animation duration. Range 1–60.
screenshot_at_s number
The moment (seconds, 0 ≤ value ≤ duration — out_of_range beyond) at which the HTML5 backup screenshot is taken. Defaults to duration when omitted. Minimum 0.
warnings array of Warning always
Values that were clipped, converted or dropped so the payload stays valid against the contract (e.g. color_converted, property_not_representable, print_settings_not_recoverable). An entry with no code is informational and carries no stable machine identity — branch only on entries that have one.
Show child attributes
message string always
Human-readable explanation.
code string
Stable, machine-readable code, when the warning has one. Absent on purely informational entries.
path string
Where in the payload the warning applies, same syntax as Problem.path.
layer string
Name of the layer the entry belongs to, when it was raised while transforming a layer.
More about layer
Name of the layer the entry belongs to, when it was raised while transforming a layer. An index in path identifies a position in the emitted array, which is not the name the caller sees in the editor — group on this rather than parsing path.
version string always
The API version that produced this response, named by release date (vYYYY-MM-DD). Pattern ^v\d{4}-\d{2}-\d{2}$.
More about version
The API version that produced this response, named by release date (vYYYY-MM-DD). Stamped as a top-level field on JSON object bodies, success and error alike, so a client can always tell which contract answered. There is no version-selection parameter — a single version is maintained at a time.
Two kinds of body are not stamped. Array bodies (the listings) carry no envelope. And a body that already has a version key of its own is left alone — which in practice means Banner, whose version is the generated file's integer counter. So GET /banners/{bannerId} and the synchronous generate are the two responses that do not tell you which contract answered.
The value changes when a new version is released. Match the vYYYY-MM-DD shape rather than pinning today's literal, or your client breaks on the next release.
Errors
Every failure uses the shared error envelope — {id, message, errors?}. Branch on id, never on message. See Errors.
| Status | When |
|---|---|
400 | Design contains features outside the import schema (not_round_trippable). |
401 | Unauthorized — missing or invalid API key. |
404 | Design not found or does not belong to the caller's company. id: template_not_found — the same code every other design lookup returns. |
429 | Too Many Requests. |
500 | Internal Server Error — an unexpected error occurred on our side. |
