Sign Customiser

Reserve a staging target for one file, before attaching it to an option image, a font, a backboard shape or the storefront product image.

Overview

Declares a file the caller intends to upload — its purpose, filename, content type and byte size — and returns a presigned target to PUT the bytes to, the headers that must accompany them, and an upload id. Nothing in the store changes until attach_customiser_asset binds the uploaded object to a slot; an upload that is never attached expires and is pruned. The declared purpose fixes the allowed content types and the maximum size, so a mismatch is caught before the bytes are sent. It requires the customisers:write scope and an idempotency_key, and supports dry_run.

Permission

Requires thecustomisers:writepermission. A connection without it answersinsufficient_scopenaming the permission to approve.

Annotations

readOnlyHintfalse
destructiveHintfalse
idempotentHinttrue
openWorldHintfalse

Prerequisites

  • The file purpose, its filename, its content type and its exact byte size.
  • A merchant has connected this store and approved the customisers:write scope.

Side effects

  • Creates one staged upload record and mints a presigned target that expires.
  • Nothing in the store configuration changes until attach_customiser_asset runs.
  • Claims the idempotency_key for at least 24 hours.

Arguments

purposestringrequired

What the file is for. The purpose fixes the accepted content types and the byte cap, and the slot it can later be attached to. Expected to grow: tolerate unknown values.

One of: font_ttf, option_image, option_texture_image, backboard_shape_svg, label_image

filenamestringrequired

The original filename. Its extension is checked against the purpose; the stored object name is chosen by the server.

content_typestringrequired

The file media type, such as font/ttf, image/png or image/svg+xml. Both this and the filename extension must match the purpose.

byte_sizeintegerrequired

The exact size of the bytes that will be sent. The staged object is refused at attach time if what arrives is a different size.

idempotency_keystring

A client-generated key unique to this logical write, such as a UUID. Required: repeating the call with the same key and the same arguments replays the original result instead of writing twice, and the same key with different arguments is an idempotency_key_conflict. A dry run claims no key.

dry_runboolean

When true, the declaration is checked against the purpose limits is checked and nothing is written: the result is the same verdict the apply would have reached, and a problem the apply would have raised comes back as the same tool error. Defaults to false.

Result

The staged upload record and the presigned target to PUT the bytes to, with the headers the PUT must carry. The target is returned once and cannot be re-read. A dry run returns the create verdict and mints nothing.

request_idstringalways present
storeobjectalways present
store.idintegeralways present
store.namestringalways present
uploadobject

The staged upload. Its id is what attach_customiser_asset takes. Absent on a dry run.

upload.idstringalways present
upload.purposestring

One of: font_ttf, option_image, option_texture_image, backboard_shape_svg, label_image

upload.filenamestring or null
upload.content_typestring or null
upload.byte_sizeinteger or null
upload.statusstring

One of: pending, uploaded, attached, rejected, expired

upload.verification_failure_codestring or null
upload.expires_atstring or null
upload.attached_atstring or null
upload.created_atstring or null
upload_targetobject

Where to PUT the bytes, and the headers the PUT must carry. Returned only by this tool, only once, and never by get_upload.

upload_target.methodstringalways present
upload_target.urlstringalways present
upload_target.headersobject
upload_target.expires_atstring or null
idempotent_replayboolean

True when this call replayed an earlier declaration carrying the same idempotency_key rather than minting a second upload.

dry_runboolean

Present and true only when dry_run was requested.

validboolean

Present only on a dry run, and always true: a failing dry run returns a tool error instead.

verdictobject

Present only on a dry run: the create verdict.

Error cases

insufficient_scope

The connection was approved without customisers:write.

missing_idempotency_key

Send idempotency_key. It is required for this write.

unsupported_value

The content type or the filename extension is not one this purpose accepts; allowed_values lists the ones it does.

upload_size_exceeded

byte_size is above the purpose maximum, which the problem reports as max_byte_size.

Stage a TrueType font file

tools/call
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "create_upload",
    "arguments": {
      "purpose": "font_ttf",
      "filename": "Signature.ttf",
      "content_type": "font/ttf",
      "byte_size": 184320,
      "idempotency_key": "c3d4e5f6-7081-4b9c-8d0e-1f2a3b4c5d6e"
    }
  }
}
result
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "structuredContent": {
      "request_id": "req_01jz9x2k7c8f3m5n6p7q8r9s0t",
      "store": {
        "id": 12,
        "name": "Demo Signs"
      },
      "upload": {
        "id": "upl_9f2c4b7ad13e6058ba41cd",
        "purpose": "font_ttf",
        "filename": "Signature.ttf",
        "content_type": "font/ttf",
        "byte_size": 184320,
        "status": "pending",
        "verification_failure_code": null,
        "expires_at": "2026-07-23T00:00:00Z",
        "attached_at": null,
        "created_at": "2026-07-22T00:00:00Z"
      },
      "upload_target": {
        "method": "PUT",
        "url": "https://uploads.example.invalid/staged-upload-target",
        "headers": [],
        "expires_at": "2026-07-22T00:10:00Z"
      },
      "idempotent_replay": false
    }
  }
}

A content type the purpose does not accept (error)

tools/call
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "create_upload",
    "arguments": {
      "purpose": "font_ttf",
      "filename": "Signature.otf",
      "content_type": "font/otf",
      "byte_size": 184320,
      "idempotency_key": "c3d4e5f6-7081-4b9c-8d0e-1f2a3b4c5d6e"
    }
  }
}
result
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "isError": true,
    "structuredContent": {
      "error": {
        "code": "unsupported_value",
        "title": "Unsupported value",
        "detail": "The font_ttf purpose does not accept the content type font/otf.",
        "recovery": "Send one of the values in allowed_values for the field named by pointer or parameter.",
        "pointer": "/content_type",
        "allowed_values": [
          "font/ttf"
        ]
      }
    }
  }
}