Customiser backboards
Attach a backboard shape
Takes the SVG of a staged upload and makes it this backboard's shape,
returning the backboard with geometry.has_custom_shape true and
geometry.status pending. Requires customisers:write and an
Idempotency-Key.
Overview
Create the upload first with POST/api/v3/uploads using purpose
backboard_shape_svg, send the bytes to the presigned target it returns,
then send that upload's id here. The upload is single use: attaching it a
second time answers 409, and a replacement shape needs a new upload.
A shape is the one uploaded file this API stores inside its own resource
rather than as a file. It is sanitised and written into the backboard, is
never published as a URL, and the staged object is deleted rather than
kept. geometry is how you read it back.
The bytes are verified here, not at create: the object must exist, be
within the purpose's cap, be well-formed XML with an svg element, carry
no script elements, event-handler attributes or javascript: references,
and still draw something once sanitised. A file that fails answers
invalid_svg and leaves the upload rejected, and attaching it again
replays that code — so fix the file and create a new upload rather than
retrying.
Attaching a shape queues server-side geometry measurement, which is why
the response reports geometry.status pending. Poll the backboard until
it reaches ready; failed carries a geometry.failure_reason. A
backboard that has measured is what backboard_type custom_shape and
cutout need, so this is the call that makes those types usable: attach
the shape, then set the type in the ordinary backboard write.
Sending this to a backboard that already has a shape replaces it and
discards the previous measurement. Re-attaching a document that sanitises
to the shape already measured is the one exception: there is nothing new
to measure, so the existing ready result stands and the response says so
rather than reporting pending.
The attach clears the customiser's cached storefront configuration and
runs the advisory language sync. It never regenerates Shopify products,
product images, or the product cache.
Authorisation
Bearer token required. Include it in theAuthorization header.
Required scope: customisers:write
Path parameters
The customiser id.
Example: 1
The backboard id.
Example: 11
Request body
Request body example
{
"upload_id": "upl_9f2c4b7ad13e6058ba41cd"
}curl https://web.signcustomiser.com/api/v3/customisers/1/backboards/11/shape \
--request PUT \
--header 'Accept: application/json' \
--header 'Authorization: Bearer YOUR_API_TOKEN' \
--header 'Content-Type: application/json' \
--data '{
"upload_id": "upl_9f2c4b7ad13e6058ba41cd"
}'{
"data": {
"object": "backboard",
"id": 11,
"customiser_id": 42,
"name": "Cloud Panel",
"description": null,
"backboard_type": "board",
"letter_type_ids": [],
"outline_size": null,
"allow_multilayer": false,
"custom_background_enabled": false,
"show_custom_background": false,
"base_price": 0,
"currency": "USD",
"price_multiplier": 1,
"price_multiplier_target": "base_letter_price",
"price_per_sq_cm": null,
"sort_order": 1,
"image_url": null,
"button_image_url": null,
"geometry": {
"has_custom_shape": true,
"status": "pending",
"failure_reason": null,
"measured_at": null
},
"created_at": "2026-08-13T00:00:00Z",
"updated_at": "2026-08-13T00:05:00Z"
},
"links": {
"self": "https://web.signcustomiser.com/api/v3/customisers/42/backboards/11/shape",
"documentation": "https://www.signcustomiser.com/help/api/"
},
"meta": {
"api_version": "v3",
"request_id": "req_01jz9x2k7c8f3m5n6p7q8r9s0t"
}
}{
"type": "https://www.signcustomiser.com/help/api/problems/missing_idempotency_key",
"title": "Missing idempotency key",
"status": 400,
"code": "missing_idempotency_key",
"detail": "Provide an Idempotency-Key header for this write.",
"request_id": "req_01jz9x2k7c8f3m5n6p7q8r9s0t"
}{
"type": "https://www.signcustomiser.com/help/api/problems/missing_api_key",
"title": "Missing API key",
"status": 401,
"code": "missing_api_key",
"detail": "Provide a store API key as a bearer token.",
"request_id": "req_01jz9x2k7c8f3m5n6p7q8r9s0t"
}{
"type": "https://www.signcustomiser.com/help/api/problems/insufficient_scope",
"title": "Insufficient scope",
"status": 403,
"code": "insufficient_scope",
"detail": "This operation requires the customisers:write scope.",
"required_scopes": [
"customisers:write"
],
"granted_scopes": [
"customisers:read"
],
"request_id": "req_01jz9x2k7c8f3m5n6p7q8r9s0t"
}{
"type": "https://www.signcustomiser.com/help/api/problems/resource_not_found",
"title": "Resource not found",
"status": 404,
"code": "resource_not_found",
"detail": "The requested resource does not exist or does not belong to the authenticated store.",
"request_id": "req_01jz9x2k7c8f3m5n6p7q8r9s0t"
}{
"type": "https://www.signcustomiser.com/help/api/problems/upload_already_attached",
"title": "Upload already attached",
"status": 409,
"code": "upload_already_attached",
"detail": "This upload has already been attached to a file slot. Each upload is single use: create a new upload for each assignment.",
"request_id": "req_01jz9x2k7c8f3m5n6p7q8r9s0t"
}{
"type": "https://www.signcustomiser.com/help/api/problems/invalid_svg",
"title": "Invalid SVG",
"status": 422,
"code": "invalid_svg",
"detail": "The shape must not contain script elements for security reasons.",
"errors": [
{
"pointer": "/upload_id",
"code": "invalid_svg",
"detail": "The shape must not contain script elements for security reasons."
}
],
"request_id": "req_01jz9x2k7c8f3m5n6p7q8r9s0t"
}{
"type": "https://www.signcustomiser.com/help/api/problems/rate_limited",
"title": "Too many requests",
"status": 429,
"code": "rate_limited",
"detail": "You have exceeded the request limit for this API key.",
"retry_after": 37,
"request_id": "req_01jz9x2k7c8f3m5n6p7q8r9s0t"
}