{
  "openapi": "3.1.0",
  "info": {
    "title": "Scaylr mockup render API",
    "version": "2026-11-11",
    "summary": "One design in, mockup JPEGs out, on Scaylr templates.",
    "description": "Render a design on Scaylr library templates, your own uploaded PSDs (\"My templates\") and your \"Mockup from a photo\" templates, with the same engine as the Scaylr studio. Human-readable reference: https://api.scaylr.io/docs. Get a key at https://scaylr.io/api-dashboard.",
    "contact": {
      "name": "Scaylr",
      "email": "info@scaylr.io",
      "url": "https://api.scaylr.io/docs"
    },
    "termsOfService": "https://scaylr.io/terms.html#api"
  },
  "servers": [
    {
      "url": "https://api.scaylr.io"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Templates",
      "description": "Library, own (mine:) and photo templates, and collections."
    },
    {
      "name": "Jobs",
      "description": "Start render jobs and read their results."
    },
    {
      "name": "Account",
      "description": "Usage and health."
    }
  ],
  "paths": {
    "/api/batch/templates": {
      "get": {
        "tags": [
          "Templates"
        ],
        "operationId": "listTemplates",
        "summary": "List the templates this key may use",
        "parameters": [
          {
            "name": "category",
            "in": "query",
            "description": "e.g. tshirt, hoodie, mug, tumbler, tote, ornament, apparel, poster; `mine`; `photo` or `photo-<kind>`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "kind",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "library",
                "mine",
                "photo"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The templates.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TemplateList"
                }
              }
            }
          },
          "401": {
            "description": "No key, a wrong key or a revoked key (`unauthorized`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "unauthorized": {
                    "value": {
                      "error": {
                        "code": "unauthorized",
                        "message": "Send a valid API key: Authorization: Bearer sbk_..."
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "A per-key limit was hit: `rate_limited` (120 requests/minute, with Retry-After; or 60 jobs/hour), `daily_limit` (2,000 renders/24 h) or `queue_full` (400 renders waiting).",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait (sent with the per-minute request limit).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our side (`internal`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/batch/templates/{id}": {
      "get": {
        "tags": [
          "Templates"
        ],
        "operationId": "getTemplate",
        "summary": "One template, with its size and smart objects",
        "description": "Opens the PSD (a few seconds the first time, then cached). If the renderer cannot open it, the answer is 200 with `error` and `unsupported: true` instead of the size.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "A template id: `lib:<slug>`, `mine:<uuid>` or `photo:<uuid>` (URL-encode it if needed).",
            "schema": {
              "type": "string"
            },
            "example": "lib:white-mug-kitchen-counter"
          }
        ],
        "responses": {
          "200": {
            "description": "The template.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TemplateDetail"
                }
              }
            }
          },
          "401": {
            "description": "No key, a wrong key or a revoked key (`unauthorized`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "unauthorized": {
                    "value": {
                      "error": {
                        "code": "unauthorized",
                        "message": "Send a valid API key: Authorization: Bearer sbk_..."
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Unknown path, template or job (`not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "not_found": {
                    "value": {
                      "error": {
                        "code": "not_found",
                        "message": "No such job for this key."
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "A per-key limit was hit: `rate_limited` (120 requests/minute, with Retry-After; or 60 jobs/hour), `daily_limit` (2,000 renders/24 h) or `queue_full` (400 renders waiting).",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait (sent with the per-minute request limit).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our side (`internal`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/batch/templates/{id}/thumb": {
      "get": {
        "tags": [
          "Templates"
        ],
        "operationId": "getTemplateThumb",
        "summary": "The template's thumbnail (JPEG)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "A template id: `lib:<slug>`, `mine:<uuid>` or `photo:<uuid>` (URL-encode it if needed).",
            "schema": {
              "type": "string"
            },
            "example": "lib:white-mug-kitchen-counter"
          }
        ],
        "responses": {
          "200": {
            "description": "The JPEG (own and photo templates).",
            "content": {
              "image/jpeg": {
                "schema": {
                  "type": "string",
                  "contentMediaType": "image/jpeg"
                }
              }
            }
          },
          "302": {
            "description": "Library templates: a redirect to the public thumbnail.",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string",
                  "format": "uri"
                }
              }
            }
          },
          "401": {
            "description": "No key, a wrong key or a revoked key (`unauthorized`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "unauthorized": {
                    "value": {
                      "error": {
                        "code": "unauthorized",
                        "message": "Send a valid API key: Authorization: Bearer sbk_..."
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Unknown path, template or job (`not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "not_found": {
                    "value": {
                      "error": {
                        "code": "not_found",
                        "message": "No such job for this key."
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "A per-key limit was hit: `rate_limited` (120 requests/minute, with Retry-After; or 60 jobs/hour), `daily_limit` (2,000 renders/24 h) or `queue_full` (400 renders waiting).",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait (sent with the per-minute request limit).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our side (`internal`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/batch/collections": {
      "get": {
        "tags": [
          "Templates"
        ],
        "operationId": "listCollections",
        "summary": "The account's studio collections",
        "responses": {
          "200": {
            "description": "The collections.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CollectionList"
                }
              }
            }
          },
          "401": {
            "description": "No key, a wrong key or a revoked key (`unauthorized`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "unauthorized": {
                    "value": {
                      "error": {
                        "code": "unauthorized",
                        "message": "Send a valid API key: Authorization: Bearer sbk_..."
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "A per-key limit was hit: `rate_limited` (120 requests/minute, with Retry-After; or 60 jobs/hour), `daily_limit` (2,000 renders/24 h) or `queue_full` (400 renders waiting).",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait (sent with the per-minute request limit).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our side (`internal`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/batch/render": {
      "post": {
        "tags": [
          "Jobs"
        ],
        "operationId": "startRender",
        "summary": "Start a render job: one design, up to 40 templates",
        "description": "Upload the design (multipart/form-data: file field `design` + text field `request` with the JSON) or give `design_url` (application/json). Answers 202 with the job.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RenderRequestJson"
              },
              "example": {
                "design_url": "https://example.com/designs/1042.png",
                "templates": [
                  "lib:white-tshirt-flat-lay-oak-floor",
                  {
                    "id": "lib:white-mug-kitchen-counter",
                    "mug": "wrap"
                  },
                  "uc_93352493-1b4"
                ],
                "options": {
                  "max_side": 2000,
                  "quality": 88
                },
                "reference": "order-1042",
                "callback_url": "https://example.com/hooks/scaylr"
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "design",
                  "request"
                ],
                "properties": {
                  "design": {
                    "type": "string",
                    "contentMediaType": "application/octet-stream",
                    "description": "The design: PNG or JPEG, at most 30 MB."
                  },
                  "request": {
                    "type": "string",
                    "contentMediaType": "application/json",
                    "description": "JSON text of a RenderRequest (without design_url)."
                  }
                }
              },
              "encoding": {
                "design": {
                  "contentType": "image/png, image/jpeg"
                }
              }
            }
          }
        },
        "callbacks": {
          "jobFinished": {
            "{$request.body#/callback_url}": {
              "post": {
                "summary": "Sent when the job finishes (done, partial or failed)",
                "description": "The body is the same JSON as GET /api/batch/jobs/{id}. Answer 2xx within 10 s; otherwise retried up to 3 times (after ~1 min, ~5 min, ~30 min). Check `Scaylr-Signature: t=<unix seconds>,v1=<hex>`: v1 = HMAC-SHA256 of `<t>.<raw body>`, keyed with the lowercase hex SHA-256 of your full API key.",
                "parameters": [
                  {
                    "name": "Scaylr-Signature",
                    "in": "header",
                    "required": true,
                    "schema": {
                      "type": "string"
                    },
                    "example": "t=1793116232,v1=5f2b..."
                  }
                ],
                "requestBody": {
                  "required": true,
                  "content": {
                    "application/json": {
                      "schema": {
                        "$ref": "#/components/schemas/Job"
                      }
                    }
                  }
                },
                "responses": {
                  "2XX": {
                    "description": "Received."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "The job was created.",
            "headers": {
              "Location": {
                "description": "/api/batch/jobs/<id>",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JobCreated"
                }
              }
            }
          },
          "400": {
            "description": "Bad request. `code` is one of bad_request, bad_design, bad_options, bad_templates, too_many_templates.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "bad_templates": {
                    "value": {
                      "error": {
                        "code": "bad_templates",
                        "message": "templates[1]: unknown template \"lib:white-mugg\""
                      }
                    }
                  },
                  "too_many_templates": {
                    "value": {
                      "error": {
                        "code": "too_many_templates",
                        "message": "At most 40 templates per job."
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No key, a wrong key or a revoked key (`unauthorized`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "unauthorized": {
                    "value": {
                      "error": {
                        "code": "unauthorized",
                        "message": "Send a valid API key: Authorization: Bearer sbk_..."
                      }
                    }
                  }
                }
              }
            }
          },
          "402": {
            "description": "Not enough renders left this month (`monthly_limit`). Buy credits or upgrade at https://scaylr.io/api-dashboard.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "monthly_limit": {
                    "value": {
                      "error": {
                        "code": "monthly_limit",
                        "message": "This job needs 12 renders but the account has 3 left this month ..."
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "The account for this key no longer exists (`forbidden`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "The design or the upload is larger than 30 MB (`too_large`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Content-Type is neither application/json nor multipart/form-data (code `bad_request`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "A per-key limit was hit: `rate_limited` (120 requests/minute, with Retry-After; or 60 jobs/hour), `daily_limit` (2,000 renders/24 h) or `queue_full` (400 renders waiting).",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait (sent with the per-minute request limit).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our side (`internal`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/batch/jobs/{id}": {
      "get": {
        "tags": [
          "Jobs"
        ],
        "operationId": "getJob",
        "summary": "A job's status and its image links",
        "description": "Only the key that started the job can read it. Poll every ~5 s, or use callback_url.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The job.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "401": {
            "description": "No key, a wrong key or a revoked key (`unauthorized`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "unauthorized": {
                    "value": {
                      "error": {
                        "code": "unauthorized",
                        "message": "Send a valid API key: Authorization: Bearer sbk_..."
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Unknown path, template or job (`not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "not_found": {
                    "value": {
                      "error": {
                        "code": "not_found",
                        "message": "No such job for this key."
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "A per-key limit was hit: `rate_limited` (120 requests/minute, with Retry-After; or 60 jobs/hour), `daily_limit` (2,000 renders/24 h) or `queue_full` (400 renders waiting).",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait (sent with the per-minute request limit).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our side (`internal`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/batch/usage": {
      "get": {
        "tags": [
          "Account"
        ],
        "operationId": "getUsage",
        "summary": "This month's renders, plan and credits",
        "responses": {
          "200": {
            "description": "Usage.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Usage"
                }
              }
            }
          },
          "401": {
            "description": "No key, a wrong key or a revoked key (`unauthorized`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "unauthorized": {
                    "value": {
                      "error": {
                        "code": "unauthorized",
                        "message": "Send a valid API key: Authorization: Bearer sbk_..."
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "The account for this key no longer exists (`forbidden`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "A per-key limit was hit: `rate_limited` (120 requests/minute, with Retry-After; or 60 jobs/hour), `daily_limit` (2,000 renders/24 h) or `queue_full` (400 renders waiting).",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait (sent with the per-minute request limit).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our side (`internal`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/batch/health": {
      "get": {
        "tags": [
          "Account"
        ],
        "operationId": "health",
        "summary": "Is the API up? (no key needed)",
        "security": [],
        "responses": {
          "200": {
            "description": "Up.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "version": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "ok": true,
                  "version": "2026-11-11"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "sbk_<id>_<secret>",
        "description": "Create a key at https://scaylr.io/api-dashboard."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "bad_request",
                  "bad_design",
                  "bad_options",
                  "bad_templates",
                  "too_many_templates",
                  "unauthorized",
                  "monthly_limit",
                  "forbidden",
                  "not_found",
                  "too_large",
                  "rate_limited",
                  "daily_limit",
                  "queue_full",
                  "internal"
                ]
              },
              "message": {
                "type": "string"
              }
            }
          }
        }
      },
      "Template": {
        "type": "object",
        "required": [
          "id",
          "kind",
          "name",
          "category",
          "available"
        ],
        "properties": {
          "id": {
            "type": "string",
            "examples": [
              "lib:white-mug-kitchen-counter",
              "mine:bb33d7f2-9f56-4418-96ae-1e6eebff7f8f",
              "photo:2b1c0e9a-0000-0000-0000-000000000000"
            ]
          },
          "kind": {
            "type": "string",
            "enum": [
              "library",
              "mine",
              "photo"
            ]
          },
          "name": {
            "type": "string"
          },
          "category": {
            "type": "string"
          },
          "thumb": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "smart_objects": {
            "type": "integer",
            "description": "Library templates only."
          },
          "smart_object_ratio": {
            "type": [
              "number",
              "string",
              "null"
            ],
            "description": "Library templates only."
          },
          "psd_bytes": {
            "type": [
              "integer",
              "null"
            ]
          },
          "pro": {
            "type": "boolean",
            "description": "Library templates only: a Pro library template (needs a paid API plan or pay as you go)."
          },
          "updated_at": {
            "type": "integer",
            "description": "Own and photo templates: Unix seconds."
          },
          "available": {
            "type": "boolean",
            "description": "Whether this key can render it right now."
          },
          "width": {
            "type": "integer",
            "description": "Once the template has been opened at least once."
          },
          "height": {
            "type": "integer"
          }
        }
      },
      "TemplateList": {
        "type": "object",
        "properties": {
          "count": {
            "type": "integer"
          },
          "categories": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            }
          },
          "templates": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Template"
            }
          }
        }
      },
      "SmartObjectSlot": {
        "type": "object",
        "properties": {
          "index": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "width": {
            "type": "integer"
          },
          "height": {
            "type": "integer"
          },
          "hidden": {
            "type": "boolean"
          },
          "mug": {
            "type": "object",
            "description": "Mug and tumbler templates.",
            "properties": {
              "angle": {
                "type": "number"
              },
              "circ": {
                "type": "number"
              }
            }
          }
        }
      },
      "TemplateDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Template"
          },
          {
            "type": "object",
            "properties": {
              "smart_object_slots": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/SmartObjectSlot"
                }
              },
              "error": {
                "type": "string",
                "description": "Only when the renderer could not open the PSD."
              },
              "unsupported": {
                "type": "boolean",
                "description": "true when the PSD uses a feature the renderer does not support yet."
              }
            }
          }
        ]
      },
      "Collection": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "examples": [
              "uc_93352493-1b4"
            ]
          },
          "name": {
            "type": "string"
          },
          "created_at": {
            "type": "integer"
          },
          "count": {
            "type": "integer"
          },
          "templates": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "CollectionList": {
        "type": "object",
        "properties": {
          "count": {
            "type": "integer"
          },
          "collections": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Collection"
            }
          }
        }
      },
      "RenderOptions": {
        "type": "object",
        "description": "Unknown options are rejected (400).",
        "properties": {
          "fit": {
            "type": "string",
            "enum": [
              "fill",
              "fit",
              "stretch"
            ],
            "default": "fill"
          },
          "scale": {
            "type": "number",
            "minimum": 0.1,
            "maximum": 4,
            "default": 1
          },
          "offset_x": {
            "type": "number",
            "minimum": -1,
            "maximum": 1,
            "default": 0
          },
          "offset_y": {
            "type": "number",
            "minimum": -1,
            "maximum": 1,
            "default": 0
          },
          "mug": {
            "type": "string",
            "enum": [
              "auto",
              "wrap",
              "side"
            ],
            "default": "auto"
          },
          "slots": {
            "oneOf": [
              {
                "const": "all"
              },
              {
                "type": "array",
                "minItems": 1,
                "items": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 49
                }
              }
            ],
            "default": "all"
          },
          "max_side": {
            "type": "number",
            "minimum": 400,
            "maximum": 2048,
            "default": 2048,
            "description": "Sandbox: at most 1024."
          },
          "quality": {
            "type": "number",
            "minimum": 50,
            "maximum": 95,
            "default": 88
          }
        }
      },
      "TemplateEntry": {
        "oneOf": [
          {
            "type": "string",
            "description": "A template id (lib:, mine:, photo:) or a collection id (uc_… or collection:uc_…)."
          },
          {
            "allOf": [
              {
                "type": "object",
                "required": [
                  "id"
                ],
                "properties": {
                  "id": {
                    "type": "string"
                  }
                }
              },
              {
                "$ref": "#/components/schemas/RenderOptions"
              }
            ],
            "description": "A template or collection with its own options."
          }
        ]
      },
      "RenderRequest": {
        "type": "object",
        "required": [
          "templates"
        ],
        "properties": {
          "templates": {
            "type": "array",
            "minItems": 1,
            "maxItems": 40,
            "items": {
              "$ref": "#/components/schemas/TemplateEntry"
            },
            "description": "At most 40 after collections are expanded."
          },
          "options": {
            "$ref": "#/components/schemas/RenderOptions"
          },
          "reference": {
            "type": "string",
            "maxLength": 200
          },
          "callback_url": {
            "type": "string",
            "format": "uri",
            "pattern": "^https://",
            "description": "POSTed the finished job. See the jobFinished callback."
          }
        }
      },
      "RenderRequestJson": {
        "allOf": [
          {
            "$ref": "#/components/schemas/RenderRequest"
          },
          {
            "type": "object",
            "required": [
              "design_url"
            ],
            "properties": {
              "design_url": {
                "type": "string",
                "format": "uri",
                "pattern": "^https://",
                "description": "An https link to a PNG or JPEG."
              }
            }
          }
        ]
      },
      "Item": {
        "type": "object",
        "required": [
          "index",
          "template",
          "status"
        ],
        "properties": {
          "index": {
            "type": "integer"
          },
          "template": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "done",
              "failed"
            ]
          },
          "options": {
            "type": "object"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "When done. Deleted 30 days after the job was created."
          },
          "width": {
            "type": "integer"
          },
          "height": {
            "type": "integer"
          },
          "mug": {
            "type": "string",
            "enum": [
              "wrap",
              "side"
            ]
          },
          "ms": {
            "type": "integer",
            "description": "Render time."
          },
          "error": {
            "type": "string",
            "description": "When failed, e.g. \"template not supported by the renderer: …\"."
          }
        }
      },
      "Job": {
        "type": "object",
        "required": [
          "id",
          "status",
          "counts",
          "items"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "done",
              "partial",
              "failed"
            ]
          },
          "created_at": {
            "type": "integer"
          },
          "started_at": {
            "type": [
              "integer",
              "null"
            ]
          },
          "finished_at": {
            "type": [
              "integer",
              "null"
            ]
          },
          "results_expire_at": {
            "type": "integer",
            "description": "Unix seconds; result images and the job record are deleted then (30 days)."
          },
          "design": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "png",
                  "jpeg"
                ]
              },
              "width": {
                "type": "integer"
              },
              "height": {
                "type": "integer"
              },
              "bytes": {
                "type": "integer"
              },
              "reference": {
                "type": "string"
              }
            }
          },
          "counts": {
            "type": "object",
            "properties": {
              "total": {
                "type": "integer"
              },
              "done": {
                "type": "integer"
              },
              "failed": {
                "type": "integer"
              },
              "queued": {
                "type": "integer"
              },
              "running": {
                "type": "integer"
              }
            }
          },
          "renders_ahead_in_queue": {
            "type": "integer"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Item"
            }
          }
        }
      },
      "JobCreated": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Job"
          },
          {
            "type": "object",
            "properties": {
              "poll": {
                "type": "string",
                "examples": [
                  "/api/batch/jobs/2165802a-1039-4984-a2e7-ede2b4b3c6e8"
                ]
              },
              "skipped": {
                "type": "array",
                "description": "Templates from a collection that could not be used.",
                "items": {
                  "type": "object",
                  "properties": {
                    "template": {
                      "type": "string"
                    },
                    "reason": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        ]
      },
      "Usage": {
        "type": "object",
        "properties": {
          "plan": {
            "type": "string",
            "enum": [
              "sandbox",
              "payg",
              "starter",
              "growth",
              "scale",
              "custom",
              "internal"
            ]
          },
          "plan_name": {
            "type": "string"
          },
          "period": {
            "type": "object",
            "properties": {
              "start": {
                "type": "integer"
              },
              "end": {
                "type": "integer"
              }
            }
          },
          "included": {
            "type": [
              "integer",
              "null"
            ]
          },
          "used": {
            "type": "integer"
          },
          "credits": {
            "type": "integer"
          },
          "waiting": {
            "type": "integer"
          },
          "remaining": {
            "type": [
              "integer",
              "null"
            ]
          },
          "watermark": {
            "type": "boolean"
          },
          "key": {
            "type": "string"
          },
          "days": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "day": {
                  "type": "string",
                  "format": "date"
                },
                "renders": {
                  "type": "integer"
                },
                "failed": {
                  "type": "integer"
                },
                "from_credits": {
                  "type": "integer"
                }
              }
            }
          }
        }
      }
    }
  }
}