{
  "openapi": "3.1.0",
  "info": {
    "title": "3DAPI API",
    "version": "2.0.0",
    "description": "The 3DAPI API lets sellers manage everything they do on the platform, and lets integrators build on top of it.\n\n## Conventions\n\n- **Ids** are opaque strings with a type prefix, for example `prd_` for products and `var_` for variants. Store and compare them; never parse them.\n- **Money** is `{ \"amount\": 5000, \"currency\": \"usd\" }`: `amount` is an integer in the smallest currency unit (cents), so `5000` is $50.00. There are no decimal amounts anywhere in the API.\n- **Field names** are snake_case. Every object has an `object` field naming its type. Timestamps are ISO 8601 in UTC.\n- **Missing values** are `null`; fields are never left out. The only exception is a field that appears only when requested with `expand[]`, which says so.\n- **Lists** return `{ \"object\": \"list\", \"data\": [...], \"has_more\": true, \"next_cursor\": \"...\" }`. Pass `next_cursor` back as `cursor` for the next page.\n- **Expanding**: related objects are references, `{ \"id\", \"object\", \"name\" }`. Add `expand[]=<path>` (repeatable, up to 3 levels, for example `expand[]=variants.contents.design`) to receive the full object instead, identical to what its own endpoint returns. An expanded object needs the read scope its own endpoint needs: `variants.contents.extra` needs `extras:read`, `variants.contents.stock` needs `stocks:read`, and a design or attachment needs the read scope of the product or stock it belongs to. Without that scope the reference stays as it is; it is not an error. Each `expand[]` parameter lists the scopes its paths need.\n- **Cursors** are signed: pass `next_cursor` back exactly as received. An edited cursor is refused with `invalid_cursor`.\n- **Request bodies** are JSON (`Content-Type: application/json`, UTF-8) and at most 1 MiB (1,048,576 bytes). Another content type is refused with `unsupported_media_type` (415), a larger body with `payload_too_large` (413), and bytes that are not UTF-8 or not JSON with `invalid_json` (400). Files never travel in a body: send them through `POST /v2/uploads`. The NUL character (U+0000) is refused in any text field or query parameter with `validation_failed`.\n- **Id lengths**: every id is exactly 37 characters (a 3-letter prefix, `_` and 33 letters or digits), so `VARCHAR(37)` holds any of them. Upload ids (`upl_`) are the exception: they are signed and grow with the file name, about 330 characters for a short name, up to 840 for a 255-character ASCII name and never more than 2,600. They expire after 24 hours, so there is rarely a reason to store them; if you do, use a text column.\n- **Errors** always have the same body: `{ \"status_code\", \"error\", \"code\", \"message\", \"details\" }`. Branch on `code`, which is stable; `message` explains how to fix the problem.\n- **Rate limits**: every authenticated request counts toward 500 per minute per API key (per user for signed-in dashboard sessions). Operations that create quotes, uploads and orders, or move money, also count toward 60 per minute. Public sign-up, catalog and shipping endpoints are limited per IP address. Responses carry `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset`; past a limit the answer is 429 `rate_limited` with a `Retry-After` header. See Rate limits below.\n\n## Rate limits\n\nLimits are counted in fixed windows. Each operation lists the limits it counts toward in `x-ratelimit` (`limit` requests per `window_seconds`, `per` API key or user, or per IP address); these are the defaults.\n\n| Applies to | Limit | Counted per |\n| --- | --- | --- |\n| Every authenticated request | 500 per minute | API key, or user for dashboard sessions |\n| Creating quotes, uploads and orders, cancelling orders, top-ups, card setup and withdrawals (on top of the above) | 60 per minute | API key, or user |\n| Integrator sign-up (`create_integrator_account`) | 10 per hour | IP address |\n| Resending the confirmation email (`resend_integrator_confirmation`) | 5 per hour | IP address |\n| Catalog and shipping reads (materials, categories, shipping rates and quotes) | 120 per minute | IP address |\n\n`get_health` and the OpenAPI document are not limited.\n\nEvery limited response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset` (Unix seconds) for the limit closest to being reached. Past a limit the API answers 429 with code `rate_limited`, a `Retry-After` header in seconds, and `details` with `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`. A refused request does not count. A request replayed with the same `Idempotency-Key` counts like any other request.\n\n## Authentication\n\nSend your API key in the `x-api-key` header. Create keys in your 3DAPI dashboard. Endpoints marked public need no key.\n\nScopes are read and write per resource:\n\n- `products:read`: read products, variants, designs and attachments.\n- `products:write`: create and change products, variants and designs.\n- `orders:read`: read orders and import issues.\n- `orders:write`: place and cancel orders.\n- `orders:read_customer`: see buyer personal data on orders.\n- `stocks:read`: read stocks and their batches.\n- `stocks:write`: create and change stocks, restock rules and purchases.\n- `extras:read`: read extras.\n- `extras:write`: create and change extras.\n- `qc:read`: read quality control tickets.\n- `qc:write`: answer and open quality control tickets.\n- `wallet:read`: read the wallet and transactions.\n- `webhooks:read`: read webhook endpoints, events and deliveries.\n- `quotes:read`: read quotes and approval requests (integrators).\n- `quotes:write`: create quotes and approval requests (integrators).\n\n`orders:read_customer` adds buyer personal data (customer, full shipping address, notes) to orders and to order events; without it those fields are null and the address keeps only the country. Keys created before per-resource scopes carry a legacy permission: `full` has every scope above, `order` has `orders:read` and `orders:write`, `pricing` has none in this API. `GET /v2/me` returns both the stored `scopes` and the expanded `effective_scopes`.\n\nEach operation lists the API key scopes it accepts in `x-required-scopes`: any one of them is enough, and a key with full access has them all. An empty list means no scope is needed (public operations, and operations every valid key may call). Signed-in dashboard sessions are not scoped.\n\nSome operations are for one kind of account only, which each description says: designers (sellers on 3DAPI) or integrators (companies building on the API, see the Quotes and Integrator onboarding sections). `x-audience` on each operation lists the kinds of account it is for: `designer`, `integrator`, or both. Integrator accounts and their keys belong to this API only: the previous API (paths under `/v1`) refuses them by design, apart from `/v1/me`. Use the `/v2` operations for quotes, orders and everything else."
  },
  "servers": [
    {
      "url": "https://public-api.dapi.digital",
      "description": "Production"
    }
  ],
  "security": [
    {
      "apiKey": []
    },
    {
      "session": []
    }
  ],
  "tags": [
    {
      "name": "Products",
      "description": "What you sell and its variants."
    },
    {
      "name": "Orders",
      "description": "Orders for your products, from every channel."
    },
    {
      "name": "Integrator orders",
      "description": "Orders integrators place for their own customers, paid from the wallet or through a hosted checkout. Integrators only."
    },
    {
      "name": "Designs",
      "description": "Printable model files."
    },
    {
      "name": "Stocks",
      "description": "Ready-made units bought in batches."
    },
    {
      "name": "Extras",
      "description": "Bought-in parts shipped with products (designers) or with order lines (integrators)."
    },
    {
      "name": "Attachments",
      "description": "Production documents."
    },
    {
      "name": "Quality control",
      "description": "Quality checks, partner questions and issue reports on orders."
    },
    {
      "name": "Wallet",
      "description": "Your balance and money history. Integrators also manage cards, top-ups and withdrawals here."
    },
    {
      "name": "Webhooks",
      "description": "Events sent to your endpoints, and the events log.\n\n**Verifying a delivery.** Every delivery is a POST with three headers:\n\n- `X-Dapi-Webhook-Signature`: `sha256=<hex>`, the HMAC-SHA256 of `<X-Dapi-Webhook-Timestamp>.<raw body>` keyed with the endpoint secret (`whsec_...`, shown when the endpoint is created or its secret rotated).\n- `X-Dapi-Webhook-Timestamp`: the Unix time in seconds when this attempt was signed. Every retry is signed again with a new timestamp.\n- `X-Dapi-Event-Id`: the event id (`evt_...`), the same on every retry of one event.\n\nTo verify: compute the HMAC over the raw bytes you received (before parsing the JSON), compare it to the header in constant time, and refuse the request when the timestamp is more than 5 minutes away from your clock (replay tolerance). Answer with any 2xx within 10 seconds; failed deliveries are retried with backoff for 24 hours, so deduplicate on the event id."
    },
    {
      "name": "Uploads",
      "description": "Sending files: photos, design files and documents."
    },
    {
      "name": "Shipping",
      "description": "Shipping zones and prices."
    },
    {
      "name": "Materials",
      "description": "The material catalog."
    },
    {
      "name": "Categories",
      "description": "Product categories and their safety requirements."
    },
    {
      "name": "Quotes",
      "description": "Prices for printing model files. Integrators only."
    },
    {
      "name": "Integrator onboarding",
      "description": "Signing up as an integrator and getting the account approved."
    },
    {
      "name": "Integrator account",
      "description": "Your integrator account: names, sign-in email and password, and your EU responsible person. Integrators only, from a signed-in dashboard session."
    },
    {
      "name": "Analytics",
      "description": "Figures about your account over time: quote volume, value and reviews. Integrators only."
    },
    {
      "name": "Logs",
      "description": "The requests you made in the last 30 days, with bodies and the webhooks they caused. Integrators only."
    },
    {
      "name": "API keys",
      "description": "Keys for calling the API from your servers. Integrators manage them here."
    },
    {
      "name": "Identity",
      "description": "Who the credentials belong to."
    },
    {
      "name": "Health",
      "description": "Service status."
    }
  ],
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "description": "API key created in the 3DAPI dashboard."
      },
      "session": {
        "type": "http",
        "scheme": "bearer",
        "description": "Session token of a signed-in API dashboard user."
      }
    },
    "schemas": {
      "Order": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^ord_[0-9A-Za-z]+$",
            "description": "Unique identifier of the order, prefixed with `ord_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "order"
            ],
            "description": "Names the type of this object. Always `order`."
          },
          "number": {
            "type": "string",
            "description": "Human-readable order number, the one your customers and 3DAPI support use.",
            "example": "PV-SCN-1701A372"
          },
          "channel": {
            "$ref": "#/components/schemas/OrderChannel"
          },
          "status": {
            "$ref": "#/components/schemas/OrderStatus"
          },
          "customer": {
            "$ref": "#/components/schemas/OrderCustomer"
          },
          "shipping_address": {
            "$ref": "#/components/schemas/OrderShippingAddress"
          },
          "charges": {
            "$ref": "#/components/schemas/OrderCharges"
          },
          "earnings": {
            "$ref": "#/components/schemas/OrderEarnings"
          },
          "shipping": {
            "$ref": "#/components/schemas/OrderShipping"
          },
          "external": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "channel_order_id": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The order id in Shopify or ShipStation."
              },
              "channel_total": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "amount": {
                    "type": "integer",
                    "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                    "example": 5000
                  },
                  "currency": {
                    "type": "string",
                    "pattern": "^[a-z]{3}$",
                    "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                    "example": "usd"
                  }
                },
                "required": [
                  "amount",
                  "currency"
                ],
                "description": "What the customer paid in that channel, when known."
              }
            },
            "required": [
              "channel_order_id",
              "channel_total"
            ],
            "description": "The original order in the sales channel, for Shopify and ShipStation orders. Null otherwise."
          },
          "customer_notes": {
            "type": [
              "string",
              "null"
            ],
            "description": "Notes the customer left at checkout. Null without the `orders:read_customer` scope, since buyers often write personal details here."
          },
          "qc": {
            "type": "object",
            "properties": {
              "open_tickets": {
                "type": "integer",
                "minimum": 0,
                "description": "QC tickets on this order that are not approved, closed or cancelled."
              },
              "blocks_shipping": {
                "type": "boolean",
                "description": "True when an open quality check holds the order back from shipping."
              }
            },
            "required": [
              "open_tickets",
              "blocks_shipping"
            ],
            "description": "Quality control on this order. List QC tickets with `order` for the details."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Point in time, ISO 8601 in UTC.",
            "example": "2026-04-21T07:34:06Z"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "Point in time, ISO 8601 in UTC.",
            "example": "2026-04-21T07:34:06Z"
          },
          "item_count": {
            "type": "integer",
            "minimum": 0,
            "description": "Units ordered across all lines.",
            "example": 1
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OrderItem"
            },
            "description": "The order lines."
          },
          "refunds": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OrderRefund"
            },
            "description": "Completed refunds, oldest first. Empty when none."
          }
        },
        "required": [
          "id",
          "object",
          "number",
          "channel",
          "status",
          "customer",
          "shipping_address",
          "charges",
          "earnings",
          "shipping",
          "external",
          "customer_notes",
          "qc",
          "created_at",
          "updated_at",
          "item_count",
          "items",
          "refunds"
        ],
        "description": "An order for your products, from any channel: what was ordered, where it ships, its status, what was charged and what you earn."
      },
      "OrderChannel": {
        "type": "string",
        "enum": [
          "storefront",
          "shopify",
          "shipstation",
          "api",
          "manual",
          "replacement"
        ],
        "description": "Where the order came from:\n- `storefront`: a buyer checked out on your 3DAPI store and paid by card.\n- `shopify`: imported from your Shopify store.\n- `shipstation`: imported from ShipStation.\n- `api`: created through the API.\n- `manual`: placed by you from the dashboard, charged at cost.\n- `replacement`: a reprint after a quality issue; `charges.paid_by` says who paid for it."
      },
      "OrderStatus": {
        "type": "string",
        "enum": [
          "awaiting_payment",
          "paid",
          "assigned",
          "in_production",
          "shipped",
          "completed",
          "on_hold",
          "rejected",
          "returned",
          "cancelled"
        ],
        "description": "Where the order is:\n- `awaiting_payment`: you still have to pay for it.\n- `paid`: paid, waiting to be assigned to a production partner.\n- `assigned`: a production partner has it; production has not started.\n- `in_production`: being made.\n- `shipped`: on its way to the customer. `shipping` has the tracking details.\n- `completed`: delivered and settled.\n- `on_hold`: there is not enough stock to fulfil it.\n- `rejected`: the production partner refused it.\n- `returned`: sent back after shipping.\n- `cancelled`: cancelled, usually after a full refund."
      },
      "OrderCustomer": {
        "type": [
          "object",
          "null"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "Customer full name."
          },
          "email": {
            "type": [
              "string",
              "null"
            ],
            "description": "Customer email."
          },
          "phone": {
            "type": [
              "string",
              "null"
            ],
            "description": "Customer phone."
          },
          "company": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "name": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Company name."
              },
              "tax_id": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "VAT or tax number the buyer entered."
              },
              "tax_id_validated": {
                "type": [
                  "boolean",
                  "null"
                ],
                "description": "True when VIES confirmed the EU VAT number, so the sale was zero-rated. False when it did not. Null when not applicable."
              }
            },
            "required": [
              "name",
              "tax_id",
              "tax_id_validated"
            ],
            "description": "Set when the buyer bought as a business."
          }
        },
        "required": [
          "name",
          "email",
          "phone",
          "company"
        ],
        "description": "Who ordered. Personal data: null unless the API key has the `orders:read_customer` scope."
      },
      "OrderShippingAddress": {
        "type": "object",
        "properties": {
          "formatted": {
            "type": [
              "string",
              "null"
            ],
            "description": "The full address as one line. Null without the `orders:read_customer` scope.",
            "example": "Str. Test 1, Cluj, RO"
          },
          "city": {
            "type": [
              "string",
              "null"
            ],
            "description": "City. Null without the `orders:read_customer` scope."
          },
          "country": {
            "type": [
              "string",
              "null"
            ],
            "description": "Country name. Always present when known.",
            "example": "Romania"
          },
          "country_code": {
            "type": [
              "string",
              "null"
            ],
            "description": "ISO 3166-1 alpha-2 country code. Always present when known.",
            "example": "RO"
          }
        },
        "required": [
          "formatted",
          "city",
          "country",
          "country_code"
        ],
        "description": "Where the order ships. The country is always shown because it decides shipping and VAT; the rest is personal data."
      },
      "OrderCharges": {
        "type": "object",
        "properties": {
          "paid_by": {
            "type": "string",
            "enum": [
              "buyer",
              "you",
              "dapi"
            ],
            "description": "`buyer` when a customer paid 3DAPI (storefront orders), `you` when you paid for the order (manual, imported and API orders, and replacements you were responsible for), `dapi` when 3DAPI covers it (a replacement for a production partner mistake; you were not charged, and the charges show what the replacement cost). `earnings` is null unless `buyer`."
          },
          "items": {
            "type": "object",
            "properties": {
              "amount": {
                "type": "integer",
                "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                "example": 5000
              },
              "currency": {
                "type": "string",
                "pattern": "^[a-z]{3}$",
                "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                "example": "usd"
              }
            },
            "required": [
              "amount",
              "currency"
            ],
            "description": "Items total."
          },
          "shipping": {
            "type": "object",
            "properties": {
              "amount": {
                "type": "integer",
                "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                "example": 5000
              },
              "currency": {
                "type": "string",
                "pattern": "^[a-z]{3}$",
                "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                "example": "usd"
              }
            },
            "required": [
              "amount",
              "currency"
            ],
            "description": "Shipping charged."
          },
          "vat": {
            "type": "object",
            "properties": {
              "amount": {
                "type": "object",
                "properties": {
                  "amount": {
                    "type": "integer",
                    "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                    "example": 5000
                  },
                  "currency": {
                    "type": "string",
                    "pattern": "^[a-z]{3}$",
                    "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                    "example": "usd"
                  }
                },
                "required": [
                  "amount",
                  "currency"
                ],
                "description": "VAT charged."
              },
              "rate": {
                "type": "number",
                "minimum": 0,
                "maximum": 1,
                "description": "VAT rate as a fraction, for example 0.21. 0 when none.",
                "example": 0.21
              },
              "included": {
                "type": "boolean",
                "description": "True when VAT is carved out of the prices; false when it was added on top."
              },
              "country": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Country whose rate applied, ISO alpha-2. Null when none."
              }
            },
            "required": [
              "amount",
              "rate",
              "included",
              "country"
            ],
            "description": "VAT on the order. Zero outside the EU and UK."
          },
          "sample_credit": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "amount": {
                "type": "integer",
                "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                "example": 5000
              },
              "currency": {
                "type": "string",
                "pattern": "^[a-z]{3}$",
                "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                "example": "usd"
              }
            },
            "required": [
              "amount",
              "currency"
            ],
            "description": "Sample credit that covered part of the charge. Null when none was used."
          },
          "total": {
            "type": "object",
            "properties": {
              "amount": {
                "type": "integer",
                "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                "example": 5000
              },
              "currency": {
                "type": "string",
                "pattern": "^[a-z]{3}$",
                "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                "example": "usd"
              }
            },
            "required": [
              "amount",
              "currency"
            ],
            "description": "The full charge, VAT included, before any sample credit."
          }
        },
        "required": [
          "paid_by",
          "items",
          "shipping",
          "vat",
          "sample_credit",
          "total"
        ],
        "description": "What the payer was charged for the order."
      },
      "OrderEarnings": {
        "type": [
          "object",
          "null"
        ],
        "properties": {
          "manufacturing": {
            "type": "object",
            "properties": {
              "amount": {
                "type": "integer",
                "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                "example": 5000
              },
              "currency": {
                "type": "string",
                "pattern": "^[a-z]{3}$",
                "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                "example": "usd"
              }
            },
            "required": [
              "amount",
              "currency"
            ],
            "description": "Paid to the production partner for this order. 0 on an order fulfilled from prepaid stock: that manufacturing was paid when you bought the stock batch."
          },
          "platform_fee": {
            "type": "object",
            "properties": {
              "amount": {
                "type": "integer",
                "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                "example": 5000
              },
              "currency": {
                "type": "string",
                "pattern": "^[a-z]{3}$",
                "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                "example": "usd"
              }
            },
            "required": [
              "amount",
              "currency"
            ],
            "description": "3DAPI commission, at your plan rate."
          },
          "payment_processing": {
            "type": "object",
            "properties": {
              "amount": {
                "type": "integer",
                "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                "example": 5000
              },
              "currency": {
                "type": "string",
                "pattern": "^[a-z]{3}$",
                "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                "example": "usd"
              }
            },
            "required": [
              "amount",
              "currency"
            ],
            "description": "Card processing fee taken from your earnings on the buyer payment. Kept by the card processor even when the order is refunded. 0 on older orders whose payout was made without deducting it."
          },
          "refunded": {
            "type": "object",
            "properties": {
              "amount": {
                "type": "integer",
                "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                "example": 5000
              },
              "currency": {
                "type": "string",
                "pattern": "^[a-z]{3}$",
                "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                "example": "usd"
              }
            },
            "required": [
              "amount",
              "currency"
            ],
            "description": "What completed refunds took off your earnings."
          },
          "net": {
            "type": "object",
            "properties": {
              "amount": {
                "type": "integer",
                "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                "example": 5000
              },
              "currency": {
                "type": "string",
                "pattern": "^[a-z]{3}$",
                "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                "example": "usd"
              }
            },
            "required": [
              "amount",
              "currency"
            ],
            "description": "What you keep. Never rounded up to 0: minimum prices include the card fee, so it is not negative on sales made at or above them, but a price set before that rule can show a negative amount here."
          }
        },
        "required": [
          "manufacturing",
          "platform_fee",
          "payment_processing",
          "refunded",
          "net"
        ],
        "description": "What you earn from a buyer-paid order and what was taken off it. Null when you paid for the order yourself."
      },
      "OrderShipping": {
        "type": "object",
        "properties": {
          "tier": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "small",
              "medium",
              "large",
              null
            ],
            "description": "Shipping tier the order was priced at, frozen at placement: `small`, `medium` or `large`, the weight bands a variant's `shipping.tier` describes. Null when it was priced at the flat rate."
          },
          "carrier": {
            "type": [
              "string",
              "null"
            ],
            "description": "Carrier, once shipped.",
            "example": "DHL"
          },
          "tracking_number": {
            "type": [
              "string",
              "null"
            ],
            "description": "Tracking number, once shipped."
          },
          "shipped_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the order shipped. Null before shipping, and for some orders that shipped before this was recorded.",
            "example": "2026-04-21T07:34:06Z"
          }
        },
        "required": [
          "tier",
          "carrier",
          "tracking_number",
          "shipped_at"
        ],
        "description": "Shipping details."
      },
      "OrderItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^itm_[0-9A-Za-z]+$",
            "description": "Unique identifier of the order item, prefixed with `itm_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "itm_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "order_item"
            ],
            "description": "Names the type of this object. Always `order_item`."
          },
          "product": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "id": {
                "type": "string",
                "pattern": "^prd_[0-9A-Za-z]+$",
                "description": "Unique identifier of the product, prefixed with `prd_`. Treat it as an opaque string: store it and compare it, never parse it.",
                "example": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
              },
              "object": {
                "type": "string",
                "enum": [
                  "product"
                ],
                "description": "Names the type of this object. Always `product`."
              },
              "name": {
                "type": "string",
                "description": "Display name of the product."
              }
            },
            "required": [
              "id",
              "object",
              "name"
            ],
            "description": "The product, as named when ordered."
          },
          "variant": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "pattern": "^var_[0-9A-Za-z]+$",
                "description": "Unique identifier of the variant, prefixed with `var_`. Treat it as an opaque string: store it and compare it, never parse it.",
                "example": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
              },
              "object": {
                "type": "string",
                "enum": [
                  "variant"
                ],
                "description": "Names the type of this object. Always `variant`."
              },
              "name": {
                "type": "string",
                "description": "Display name of the variant."
              }
            },
            "required": [
              "id",
              "object",
              "name"
            ],
            "description": "The variant ordered, as named when ordered."
          },
          "sku": {
            "type": [
              "string",
              "null"
            ],
            "description": "The variant SKU at the time of the order.",
            "example": "CLICK-BLUE"
          },
          "quantity": {
            "type": "integer",
            "minimum": 1,
            "description": "Units ordered on this line.",
            "example": 1
          },
          "unit_price": {
            "type": "object",
            "properties": {
              "amount": {
                "type": "integer",
                "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                "example": 5000
              },
              "currency": {
                "type": "string",
                "pattern": "^[a-z]{3}$",
                "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                "example": "usd"
              }
            },
            "required": [
              "amount",
              "currency"
            ],
            "description": "What the payer paid per unit: the sales price when a buyer paid, manufacturing plus platform fee when you paid."
          },
          "total": {
            "type": "object",
            "properties": {
              "amount": {
                "type": "integer",
                "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                "example": 5000
              },
              "currency": {
                "type": "string",
                "pattern": "^[a-z]{3}$",
                "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                "example": "usd"
              }
            },
            "required": [
              "amount",
              "currency"
            ],
            "description": "`unit_price` times `quantity`."
          },
          "stock_used": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "batch": {
                  "$ref": "#/components/schemas/StockBatchReference"
                },
                "units": {
                  "type": "integer",
                  "minimum": 1,
                  "description": "Units this line took from that batch."
                }
              },
              "required": [
                "batch",
                "units"
              ]
            },
            "description": "The stock batches this line took units from, and how many. Empty when nothing came from stock."
          },
          "fulfilled_from_stock": {
            "type": "boolean",
            "description": "True when at least part of this line ships from your ready-made stock instead of being printed. Its manufacturing was paid when you bought the stock batch, so it is not charged again on this order; an order fulfilled from prepaid stock shows `earnings.manufacturing` as 0."
          }
        },
        "required": [
          "id",
          "object",
          "product",
          "variant",
          "sku",
          "quantity",
          "unit_price",
          "total",
          "stock_used",
          "fulfilled_from_stock"
        ],
        "description": "One line of an order. Names and prices are frozen when the order is placed, so later product edits do not change it."
      },
      "StockBatchReference": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^bat_[0-9A-Za-z]+$",
            "description": "Unique identifier of the stock batch, prefixed with `bat_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "bat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "stock_batch"
            ],
            "description": "Names the type of this object. Always `stock_batch`."
          },
          "name": {
            "type": "string",
            "description": "Display name of the stock batch."
          }
        },
        "required": [
          "id",
          "object",
          "name"
        ],
        "description": "Reference to a stock batch. Pass the matching `expand[]` path to receive the full stock batch instead."
      },
      "OrderRefund": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^rfd_[0-9A-Za-z]+$",
            "description": "Unique identifier of the refund, prefixed with `rfd_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "rfd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "amount": {
            "type": "object",
            "properties": {
              "amount": {
                "type": "integer",
                "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                "example": 5000
              },
              "currency": {
                "type": "string",
                "pattern": "^[a-z]{3}$",
                "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                "example": "usd"
              }
            },
            "required": [
              "amount",
              "currency"
            ],
            "description": "Total refunded, VAT included."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Point in time, ISO 8601 in UTC.",
            "example": "2026-04-21T07:34:06Z"
          }
        },
        "required": [
          "id",
          "amount",
          "created_at"
        ],
        "description": "A completed refund."
      },
      "IntegratorOrder": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^ord_[0-9A-Za-z]+$",
            "description": "Unique identifier of the order, prefixed with `ord_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "order"
            ],
            "description": "Names the type of this object. Always `order`."
          },
          "number": {
            "type": "string",
            "description": "Human-readable order number, the one your customers and 3DAPI support use.",
            "example": "PV-SCN-1701A372"
          },
          "channel": {
            "type": "string",
            "enum": [
              "integrator_checkout",
              "integrator_wallet"
            ],
            "description": "Which money flow the order follows."
          },
          "status": {
            "$ref": "#/components/schemas/OrderStatus"
          },
          "customer": {
            "$ref": "#/components/schemas/OrderCustomer"
          },
          "shipping_address": {
            "$ref": "#/components/schemas/OrderShippingAddress"
          },
          "charges": {
            "$ref": "#/components/schemas/OrderCharges"
          },
          "earnings": {
            "$ref": "#/components/schemas/OrderEarnings"
          },
          "shipping": {
            "$ref": "#/components/schemas/OrderShipping"
          },
          "customer_notes": {
            "type": [
              "string",
              "null"
            ],
            "description": "Notes the customer left at checkout. Null without the `orders:read_customer` scope, since buyers often write personal details here."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Point in time, ISO 8601 in UTC.",
            "example": "2026-04-21T07:34:06Z"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "Point in time, ISO 8601 in UTC.",
            "example": "2026-04-21T07:34:06Z"
          },
          "mode": {
            "type": "string",
            "enum": [
              "checkout",
              "wallet"
            ],
            "description": "`wallet`: you pay the production price plus shipping from your wallet (a shortfall is charged to your saved card). `checkout`: your customer pays the sales price you set through a hosted 3DAPI checkout and you earn the difference."
          },
          "checkout": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "object": {
                "type": "string",
                "enum": [
                  "checkout_link"
                ],
                "description": "Names the type of this object. Always `checkout_link`."
              },
              "url": {
                "type": "string",
                "format": "uri",
                "description": "Send your customer here to pay."
              },
              "status": {
                "type": "string",
                "enum": [
                  "open",
                  "completed",
                  "expired",
                  "cancelled"
                ],
                "description": "`open` until paid or closed; `completed` once the buyer paid; `expired` when the link timed out unpaid, which cancels the order (`checkout.expired`); `cancelled` when you cancelled the unpaid order (`order.cancelled`)."
              },
              "expires_at": {
                "type": "string",
                "format": "date-time",
                "description": "Point in time, ISO 8601 in UTC.",
                "example": "2026-04-21T07:34:06Z"
              }
            },
            "required": [
              "object",
              "url",
              "status",
              "expires_at"
            ],
            "description": "The hosted checkout link in `checkout` mode, null in `wallet` mode."
          },
          "item_count": {
            "type": "integer",
            "minimum": 0,
            "description": "Units ordered across all lines."
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/IntegratorOrderItem"
            },
            "description": "The order lines. In lists only present with `expand[]=items`."
          },
          "refunds": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "pattern": "^rfd_[0-9A-Za-z]+$",
                  "description": "Unique identifier of the refund, prefixed with `rfd_`. Treat it as an opaque string: store it and compare it, never parse it.",
                  "example": "rfd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                },
                "amount": {
                  "type": "object",
                  "properties": {
                    "amount": {
                      "type": "integer",
                      "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                      "example": 5000
                    },
                    "currency": {
                      "type": "string",
                      "pattern": "^[a-z]{3}$",
                      "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                      "example": "usd"
                    }
                  },
                  "required": [
                    "amount",
                    "currency"
                  ],
                  "description": "Total refunded, VAT included."
                },
                "created_at": {
                  "type": "string",
                  "format": "date-time",
                  "description": "Point in time, ISO 8601 in UTC.",
                  "example": "2026-04-21T07:34:06Z"
                }
              },
              "required": [
                "id",
                "amount",
                "created_at"
              ]
            },
            "description": "Completed refunds, oldest first, in lists too. Empty when none."
          },
          "reference": {
            "type": [
              "string",
              "null"
            ],
            "description": "Your own order id, as you sent it.",
            "example": "SHOP-1042"
          }
        },
        "required": [
          "id",
          "object",
          "number",
          "channel",
          "status",
          "customer",
          "shipping_address",
          "charges",
          "earnings",
          "shipping",
          "customer_notes",
          "created_at",
          "updated_at",
          "mode",
          "checkout",
          "item_count",
          "refunds",
          "reference"
        ],
        "description": "An order for prints of your quotes: what was ordered, where it ships, its status, what was charged and, in `checkout` mode, what you earn."
      },
      "IntegratorOrderItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^itm_[0-9A-Za-z]+$",
            "description": "Unique identifier of the order item, prefixed with `itm_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "itm_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "order_item"
            ],
            "description": "Names the type of this object. Always `order_item`."
          },
          "quote": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "pattern": "^qte_[0-9A-Za-z]+$",
                "description": "Unique identifier of the quote, prefixed with `qte_`. Treat it as an opaque string: store it and compare it, never parse it.",
                "example": "qte_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
              },
              "object": {
                "type": "string",
                "enum": [
                  "quote"
                ],
                "description": "Names the type of this object. Always `quote`."
              },
              "name": {
                "type": "string",
                "description": "Display name of the quote."
              }
            },
            "required": [
              "id",
              "object",
              "name"
            ],
            "description": "The quote this line prints, as named when ordered."
          },
          "materials": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CatalogMaterialReference"
            },
            "description": "The materials loaded, in slot order. `material_slots` says which slot each one goes in."
          },
          "material_slots": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MaterialSlot"
            },
            "description": "Which material goes in which extruder slot, by slot. Slots the file does not use are left out."
          },
          "quantity": {
            "type": "integer",
            "minimum": 1,
            "description": "Copies of the quote printed on this line.",
            "example": 10
          },
          "unit_price": {
            "type": "object",
            "properties": {
              "amount": {
                "type": "integer",
                "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                "example": 5000
              },
              "currency": {
                "type": "string",
                "pattern": "^[a-z]{3}$",
                "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                "example": "usd"
              }
            },
            "required": [
              "amount",
              "currency"
            ],
            "description": "What the payer paid per unit: the production price in `wallet` mode, your sales price in `checkout` mode."
          },
          "production_unit_price": {
            "type": "object",
            "properties": {
              "amount": {
                "type": "integer",
                "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                "example": 5000
              },
              "currency": {
                "type": "string",
                "pattern": "^[a-z]{3}$",
                "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                "example": "usd"
              }
            },
            "required": [
              "amount",
              "currency"
            ],
            "description": "The quote price per unit at this quantity (it falls as a plate fills), plus the price of the extras shipped with each unit: what you pay in `wallet` mode, your cost in `checkout` mode. It already includes the platform fee: `earnings.manufacturing` plus `earnings.platform_fee` equal this price times the quantity, summed over the lines."
          },
          "extras": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "extra": {
                  "$ref": "#/components/schemas/ExtraReference"
                },
                "quantity_per_unit": {
                  "type": "integer",
                  "minimum": 1,
                  "description": "How many of this extra ship with each printed unit of the line.",
                  "example": 2
                }
              },
              "required": [
                "extra",
                "quantity_per_unit"
              ]
            },
            "description": "Extras shipped with every unit of this line, as ordered. Empty when none."
          },
          "total": {
            "type": "object",
            "properties": {
              "amount": {
                "type": "integer",
                "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                "example": 5000
              },
              "currency": {
                "type": "string",
                "pattern": "^[a-z]{3}$",
                "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                "example": "usd"
              }
            },
            "required": [
              "amount",
              "currency"
            ],
            "description": "`unit_price` times `quantity`."
          }
        },
        "required": [
          "id",
          "object",
          "quote",
          "materials",
          "material_slots",
          "quantity",
          "unit_price",
          "production_unit_price",
          "extras",
          "total"
        ],
        "description": "One line of an order."
      },
      "CatalogMaterialReference": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^mat_[0-9A-Za-z]+$",
            "description": "Unique identifier of the material, prefixed with `mat_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "material"
            ],
            "description": "Names the type of this object. Always `material`."
          },
          "name": {
            "type": "string",
            "description": "Display name of the material."
          }
        },
        "required": [
          "id",
          "object",
          "name"
        ],
        "description": "Reference to a catalog material (`mat_` id). Read the full material with GET /v2/materials."
      },
      "MaterialSlot": {
        "type": "object",
        "properties": {
          "slot": {
            "type": "integer",
            "minimum": 1,
            "maximum": 16,
            "description": "Extruder slot (filament number) in the 3MF, from 1, as `suggested_materials[].slot` on the quote lists them.",
            "example": 2
          },
          "material": {
            "$ref": "#/components/schemas/CatalogMaterialReference"
          }
        },
        "required": [
          "slot",
          "material"
        ],
        "description": "A catalog material loaded into one extruder slot."
      },
      "ExtraReference": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^xtr_[0-9A-Za-z]+$",
            "description": "Unique identifier of the extra, prefixed with `xtr_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "xtr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "extra"
            ],
            "description": "Names the type of this object. Always `extra`."
          },
          "name": {
            "type": "string",
            "description": "Display name of the extra."
          }
        },
        "required": [
          "id",
          "object",
          "name"
        ],
        "description": "Reference to a extra. Pass the matching `expand[]` path to receive the full extra instead."
      },
      "QcTicket": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^qct_[0-9A-Za-z]+$",
            "description": "Unique identifier of the qc ticket, prefixed with `qct_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "qct_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "qc_ticket"
            ],
            "description": "Names the type of this object. Always `qc_ticket`."
          },
          "kind": {
            "$ref": "#/components/schemas/QcTicketKind"
          },
          "reason": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "onboarding",
              "spot_check",
              "capability",
              "suspension",
              null
            ],
            "description": "Why a quality check was raised. Null for questions and issue reports."
          },
          "status": {
            "$ref": "#/components/schemas/QcTicketStatus"
          },
          "needs_your_action": {
            "type": "boolean",
            "description": "True when the next step is yours: photos are waiting for your review, or a question is waiting for your answer."
          },
          "blocks_shipping": {
            "type": "boolean",
            "description": "True when the order cannot ship until this ticket is approved. Questions and issue reports never block."
          },
          "order": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "pattern": "^ord_[0-9A-Za-z]+$",
                "description": "Unique identifier of the order, prefixed with `ord_`. Treat it as an opaque string: store it and compare it, never parse it.",
                "example": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
              },
              "object": {
                "type": "string",
                "enum": [
                  "order"
                ],
                "description": "Names the type of this object. Always `order`."
              },
              "name": {
                "type": "string",
                "description": "Display name of the order."
              }
            },
            "required": [
              "id",
              "object",
              "name"
            ],
            "description": "The order this ticket is about."
          },
          "round": {
            "type": "integer",
            "minimum": 1,
            "description": "Current round of photos."
          },
          "replacement": {
            "$ref": "#/components/schemas/QcReplacement"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Point in time, ISO 8601 in UTC.",
            "example": "2026-04-21T07:34:06Z"
          },
          "submitted_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the production partner last submitted photos.",
            "example": "2026-04-21T07:34:06Z"
          },
          "resolved_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the ticket was approved, closed or cancelled.",
            "example": "2026-04-21T07:34:06Z"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/QcItem"
            },
            "description": "The products on the ticket. A conversation has one container item for its messages."
          }
        },
        "required": [
          "id",
          "object",
          "kind",
          "reason",
          "status",
          "needs_your_action",
          "blocks_shipping",
          "order",
          "round",
          "replacement",
          "created_at",
          "submitted_at",
          "resolved_at",
          "items"
        ],
        "description": "A quality check, a question from the production partner, or an issue you reported on an order."
      },
      "QcTicketKind": {
        "type": "string",
        "enum": [
          "quality_check",
          "question",
          "issue_report"
        ],
        "description": "What the ticket is, which also says who opened it:\n- `quality_check`: 3DAPI asked the production partner for photos of what it made, before shipping. You accept or refuse each product. Opened by 3DAPI, automatically or by our team.\n- `question`: the production partner needs an answer from you before it prints. Opened by the production partner.\n- `issue_report`: you reported a defect after delivery and asked for a replacement. Opened by you; 3DAPI decides the replacement, and that decision closes the report."
      },
      "QcTicketStatus": {
        "type": "string",
        "enum": [
          "awaiting_photos",
          "awaiting_your_review",
          "refusal_under_review",
          "reprinting",
          "approved",
          "awaiting_answer",
          "answered",
          "closed",
          "cancelled"
        ],
        "description": "Where the ticket is. Quality checks:\n- `awaiting_photos`: the production partner owes photos.\n- `awaiting_your_review`: photos are in; accept or refuse each product.\n- `refusal_under_review`: you refused a product and 3DAPI is ruling on it.\n- `reprinting`: a refusal was upheld and the partner is remaking the product.\n- `approved`: every product was accepted; the order can ship.\n\nQuestions and issue reports:\n- `awaiting_answer`: waiting for a reply: from you on a question, from 3DAPI on an issue report.\n- `answered`: you answered the question; the production partner has the next word.\n- `closed`: settled. A question is closed by the production partner who asked it; an issue report closes when 3DAPI decides the replacement.\n\nAny kind:\n- `cancelled`: the ticket no longer applies, for example after the order moved to another partner."
      },
      "QcReplacement": {
        "type": [
          "object",
          "null"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "variant": {
                  "$ref": "#/components/schemas/VariantReference"
                },
                "quantity": {
                  "type": "integer",
                  "minimum": 1,
                  "description": "Units of that variant you asked to have replaced."
                }
              },
              "required": [
                "variant",
                "quantity"
              ]
            },
            "description": "What you asked to have replaced."
          },
          "decision": {
            "type": "string",
            "enum": [
              "pending",
              "reprint",
              "handled_by_support",
              "declined"
            ],
            "description": "`pending` until 3DAPI rules, then `reprint` (a replacement order is made), `handled_by_support` (resolved another way, see the conversation) or `declined`."
          },
          "paid_by": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "you",
              "production_partner",
              null
            ],
            "description": "Who pays for the reprint. Null until decided."
          },
          "order": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "id": {
                "type": "string",
                "pattern": "^ord_[0-9A-Za-z]+$",
                "description": "Unique identifier of the order, prefixed with `ord_`. Treat it as an opaque string: store it and compare it, never parse it.",
                "example": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
              },
              "object": {
                "type": "string",
                "enum": [
                  "order"
                ],
                "description": "Names the type of this object. Always `order`."
              },
              "name": {
                "type": "string",
                "description": "Display name of the order."
              }
            },
            "required": [
              "id",
              "object",
              "name"
            ],
            "description": "The replacement order, once one was made."
          }
        },
        "required": [
          "items",
          "decision",
          "paid_by",
          "order"
        ],
        "description": "Only on issue reports: the replacement you asked for and what was decided."
      },
      "VariantReference": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^var_[0-9A-Za-z]+$",
            "description": "Unique identifier of the variant, prefixed with `var_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "variant"
            ],
            "description": "Names the type of this object. Always `variant`."
          },
          "name": {
            "type": "string",
            "description": "Display name of the variant."
          }
        },
        "required": [
          "id",
          "object",
          "name"
        ],
        "description": "Reference to a variant. Pass the matching `expand[]` path to receive the full variant instead."
      },
      "QcItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^qci_[0-9A-Za-z]+$",
            "description": "Unique identifier of the qc item, prefixed with `qci_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "qci_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "qc_item"
            ],
            "description": "Names the type of this object. Always `qc_item`."
          },
          "title": {
            "type": [
              "string",
              "null"
            ],
            "description": "What is being checked, usually the variant name.",
            "example": "Clicker Blue"
          },
          "product": {
            "$ref": "#/components/schemas/ProductReference"
          },
          "variant": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "id": {
                "type": "string",
                "pattern": "^var_[0-9A-Za-z]+$",
                "description": "Unique identifier of the variant, prefixed with `var_`. Treat it as an opaque string: store it and compare it, never parse it.",
                "example": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
              },
              "object": {
                "type": "string",
                "enum": [
                  "variant"
                ],
                "description": "Names the type of this object. Always `variant`."
              },
              "name": {
                "type": "string",
                "description": "Display name of the variant."
              }
            },
            "required": [
              "id",
              "object",
              "name"
            ],
            "description": "The variant being checked. Null when the item is not linked to a variant."
          },
          "status": {
            "type": "string",
            "enum": [
              "awaiting_photos",
              "in_review",
              "refusal_under_review",
              "accepted"
            ],
            "description": "Where this product stands: photos owed, photos in and waiting for a verdict, your refusal being ruled on by 3DAPI, or accepted."
          },
          "round": {
            "type": "integer",
            "minimum": 1,
            "description": "Which round of photos this is. A new round starts after a refusal is upheld and the product is remade."
          },
          "photos": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "pattern": "^qcp_[0-9A-Za-z]+$",
                  "description": "Identifier of the photo, to point at it in a refusal.",
                  "example": "qcp_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                },
                "url": {
                  "type": "string",
                  "format": "uri",
                  "description": "Signed URL to the photo. Valid for 15 minutes; get the ticket again for fresh ones."
                },
                "round": {
                  "type": "integer",
                  "minimum": 1,
                  "description": "The round of photos this one belongs to."
                },
                "uploaded_at": {
                  "type": "string",
                  "format": "date-time",
                  "description": "Point in time, ISO 8601 in UTC.",
                  "example": "2026-04-21T07:34:06Z"
                }
              },
              "required": [
                "id",
                "url",
                "round",
                "uploaded_at"
              ]
            },
            "description": "Photos the production partner uploaded, every round."
          },
          "history": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "action": {
                  "type": "string",
                  "enum": [
                    "submitted",
                    "accepted",
                    "refused",
                    "refusal_upheld",
                    "refusal_overturned",
                    "asked",
                    "answered",
                    "closed"
                  ],
                  "description": "What happened: photos submitted, product accepted or refused, a refusal upheld or overturned by 3DAPI, or a message asked, answered or closed."
                },
                "by": {
                  "$ref": "#/components/schemas/QcActor"
                },
                "round": {
                  "type": "integer",
                  "minimum": 1,
                  "description": "The round of photos this happened in."
                },
                "note": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "The message or reason given, when any."
                },
                "at": {
                  "type": "string",
                  "format": "date-time",
                  "description": "Point in time, ISO 8601 in UTC.",
                  "example": "2026-04-21T07:34:06Z"
                }
              },
              "required": [
                "action",
                "by",
                "round",
                "note",
                "at"
              ]
            },
            "description": "Everything that happened to this product, oldest first."
          }
        },
        "required": [
          "id",
          "object",
          "title",
          "product",
          "variant",
          "status",
          "round",
          "photos",
          "history"
        ],
        "description": "One product on a ticket, with its photos and the decisions on it."
      },
      "ProductReference": {
        "type": [
          "object",
          "null"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^prd_[0-9A-Za-z]+$",
            "description": "Unique identifier of the product, prefixed with `prd_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "product"
            ],
            "description": "Names the type of this object. Always `product`."
          },
          "name": {
            "type": "string",
            "description": "Display name of the product."
          }
        },
        "required": [
          "id",
          "object",
          "name"
        ],
        "description": "Reference to a product. Pass the matching `expand[]` path to receive the full product instead."
      },
      "QcActor": {
        "type": "string",
        "enum": [
          "you",
          "production_partner",
          "dapi"
        ],
        "description": "Who did it: you, the production partner or the 3DAPI team."
      },
      "Product": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^prd_[0-9A-Za-z]+$",
            "description": "Unique identifier of the product, prefixed with `prd_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "product"
            ],
            "description": "Names the type of this object. Always `product`."
          },
          "name": {
            "type": "string",
            "description": "Display name.",
            "example": "PR Tracker"
          },
          "slug": {
            "type": "string",
            "description": "URL-safe name used in the storefront address.",
            "example": "pr-tracker"
          },
          "category": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/CategoryReference"
              },
              {
                "$ref": "#/components/schemas/Category"
              },
              {
                "type": "null"
              }
            ],
            "description": "The product category, which decides its safety requirements. Null when none is selected."
          },
          "review": {
            "$ref": "#/components/schemas/ProductReview"
          },
          "publishing": {
            "$ref": "#/components/schemas/ProductPublishing"
          },
          "variant_count": {
            "type": "integer",
            "minimum": 0,
            "description": "Number of variants.",
            "example": 2
          },
          "price_range": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "min": {
                "type": "object",
                "properties": {
                  "amount": {
                    "type": "integer",
                    "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                    "example": 5000
                  },
                  "currency": {
                    "type": "string",
                    "pattern": "^[a-z]{3}$",
                    "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                    "example": "usd"
                  }
                },
                "required": [
                  "amount",
                  "currency"
                ],
                "description": "Lowest variant price."
              },
              "max": {
                "type": "object",
                "properties": {
                  "amount": {
                    "type": "integer",
                    "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                    "example": 5000
                  },
                  "currency": {
                    "type": "string",
                    "pattern": "^[a-z]{3}$",
                    "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                    "example": "usd"
                  }
                },
                "required": [
                  "amount",
                  "currency"
                ],
                "description": "Highest variant price."
              }
            },
            "required": [
              "min",
              "max"
            ],
            "description": "Lowest and highest variant price. Null without variants."
          },
          "images": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Image"
            },
            "description": "Product photos."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Point in time, ISO 8601 in UTC.",
            "example": "2026-04-21T07:34:06Z"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "Point in time, ISO 8601 in UTC.",
            "example": "2026-04-21T07:34:06Z"
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "description": "Product description shown on the storefront."
          },
          "options": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string",
                  "description": "Option name.",
                  "example": "Color"
                },
                "values": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  },
                  "description": "Possible values, in display order.",
                  "example": [
                    "Black",
                    "Blue"
                  ]
                }
              },
              "required": [
                "name",
                "values"
              ]
            },
            "description": "The choices a buyer makes, in display order. Each variant has one value per option."
          },
          "variants": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Variant"
            },
            "description": "Every variant."
          },
          "attachments": {
            "type": "array",
            "items": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/AttachmentReference"
                },
                {
                  "$ref": "#/components/schemas/Attachment"
                }
              ],
              "description": "A attachment reference by default. With `expand[]=attachments` it is the full attachment, identical to what its own GET returns, when your API key has the read scope that GET needs; otherwise it stays a reference."
            },
            "description": "Production documents sent to the production partner with every order."
          },
          "storefront": {
            "type": "object",
            "properties": {
              "url": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "uri",
                "description": "Public product page. Null while your store is not public."
              },
              "show_compliance_labels": {
                "type": "boolean",
                "description": "Whether the category safety labels (warnings, age restriction, risk level, CE mark) are shown on the product page."
              },
              "disclaimers": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "title": {
                      "type": "string",
                      "description": "Heading of the disclaimer.",
                      "example": "Production safety"
                    },
                    "text": {
                      "type": "string",
                      "description": "The disclaimer text.",
                      "example": "Keep away from heat."
                    }
                  },
                  "required": [
                    "title",
                    "text"
                  ]
                },
                "description": "Your disclaimers shown on the product page."
              }
            },
            "required": [
              "url",
              "show_compliance_labels",
              "disclaimers"
            ],
            "description": "How the product appears on your storefront."
          },
          "custom_label_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "Your custom label image, printed by the production partner and shipped with the product. Null when none."
          },
          "compliance": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ProductComplianceSummary"
              },
              {
                "$ref": "#/components/schemas/ProductCompliance"
              }
            ],
            "description": "Safety documents at a glance. With `expand[]=compliance` it is the full view, identical to what the product compliance endpoint returns."
          }
        },
        "required": [
          "id",
          "object",
          "name",
          "slug",
          "category",
          "review",
          "publishing",
          "variant_count",
          "price_range",
          "images",
          "created_at",
          "updated_at",
          "description",
          "options",
          "variants",
          "attachments",
          "storefront",
          "custom_label_url",
          "compliance"
        ],
        "description": "Something you sell: its variants, what they are made of, their prices and margins, and its storefront and review state."
      },
      "CategoryReference": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^cat_[0-9A-Za-z]+$",
            "description": "Unique identifier of the category, prefixed with `cat_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "cat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "category"
            ],
            "description": "Names the type of this object. Always `category`."
          },
          "name": {
            "type": "string",
            "description": "Display name of the category."
          }
        },
        "required": [
          "id",
          "object",
          "name"
        ],
        "description": "Reference to a category. Pass the matching `expand[]` path to receive the full category instead."
      },
      "Category": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^cat_[0-9A-Za-z]+$",
            "description": "Unique identifier of the category, prefixed with `cat_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "cat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "category"
            ],
            "description": "Names the type of this object. Always `category`."
          },
          "name": {
            "type": "string",
            "description": "Display name.",
            "example": "Action Figures & Dolls"
          },
          "slug": {
            "type": "string",
            "description": "URL-safe name, unique across categories.",
            "example": "toys-and-games--action-figures-and-dolls"
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "description": "Longer explanation of what belongs here, when provided."
          },
          "parent": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "id": {
                "type": "string",
                "pattern": "^cat_[0-9A-Za-z]+$",
                "description": "Unique identifier of the category, prefixed with `cat_`. Treat it as an opaque string: store it and compare it, never parse it.",
                "example": "cat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
              },
              "object": {
                "type": "string",
                "enum": [
                  "category"
                ],
                "description": "Names the type of this object. Always `category`."
              },
              "name": {
                "type": "string",
                "description": "Display name of the category."
              }
            },
            "required": [
              "id",
              "object",
              "name"
            ],
            "description": "The top-level category this one belongs to. Null for a top-level category. Products may use either level."
          },
          "compliance": {
            "$ref": "#/components/schemas/CategoryCompliance"
          }
        },
        "required": [
          "id",
          "object",
          "name",
          "slug",
          "description",
          "parent",
          "compliance"
        ],
        "description": "A product category. Categories form a two-level tree and decide which safety requirements a product must meet."
      },
      "CategoryCompliance": {
        "type": "object",
        "properties": {
          "risk_level": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "low",
              "medium",
              "high",
              "critical",
              null
            ],
            "description": "GPSR risk level of products in this category. Null when none is set."
          },
          "ce_marking_required": {
            "type": "boolean",
            "description": "Whether products in this category need CE marking."
          },
          "age_restriction": {
            "type": [
              "string",
              "null"
            ],
            "description": "Free-text age restriction, for example `0-36 months` or `18+`. Null when none applies."
          },
          "safety_warnings_required": {
            "type": "boolean",
            "description": "Whether the product page must show safety warnings for this category."
          },
          "safety_flags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Typical hazards for the category, in snake_case, for example `choking_hazard`.",
            "example": [
              "choking_hazard",
              "age_restriction"
            ]
          },
          "mandatory_warnings": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Exact warning sentences that are shown on the product page of every product in this category."
          },
          "required_documents": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RequiredDocument"
            },
            "description": "Documents a product in this category must provide. Whether a missing document currently blocks publishing is reported on the product, in `publishing.blockers`."
          }
        },
        "required": [
          "risk_level",
          "ce_marking_required",
          "age_restriction",
          "safety_warnings_required",
          "safety_flags",
          "mandatory_warnings",
          "required_documents"
        ],
        "description": "EU/UK product safety (GPSR) requirements for products in this category, with everything inherited from the parent category already applied."
      },
      "RequiredDocument": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "description": "Stable identifier of the requirement. Use it to say which requirement an uploaded compliance document satisfies.",
            "example": "material-toxicity-assessment"
          },
          "name": {
            "type": "string",
            "description": "Name of the document, as shown to sellers.",
            "example": "Material Toxicity Assessment"
          }
        },
        "required": [
          "key",
          "name"
        ],
        "description": "A document a product in this category must provide."
      },
      "ProductReview": {
        "type": "object",
        "properties": {
          "status": {
            "$ref": "#/components/schemas/ReviewStatus"
          },
          "reason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why it was rejected. Null unless `status` is `rejected`."
          }
        },
        "required": [
          "status",
          "reason"
        ],
        "description": "Review of the product as a whole: approved once every design and extra in it is approved, rejected if any is rejected."
      },
      "ReviewStatus": {
        "type": "string",
        "enum": [
          "pending",
          "approved",
          "rejected"
        ],
        "description": "Where 3DAPI stands on reviewing this object:\n- `pending`: waiting for review by 3DAPI. Nothing to do on your side.\n- `approved`: reviewed and accepted.\n- `rejected`: reviewed and refused. `reason` says why; fix it and save again to resubmit."
      },
      "ProductPublishing": {
        "type": "object",
        "properties": {
          "is_public": {
            "type": "boolean",
            "description": "Whether the product is listed on your storefront. A public product can only be ordered once `review.status` is `approved`."
          },
          "unpublished_by": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "seller",
              "platform",
              null
            ],
            "description": "Who made a private product private. `platform` means 3DAPI unpublished it, for example after a plan downgrade put you over your public product limit; upgrading can restore it. Null while public."
          },
          "can_publish": {
            "type": "boolean",
            "description": "True when nothing blocks making the product public."
          },
          "blockers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PublishBlocker"
            },
            "description": "Everything that currently blocks publishing, in the order to fix them. Empty when `can_publish` is true."
          }
        },
        "required": [
          "is_public",
          "unpublished_by",
          "can_publish",
          "blockers"
        ],
        "description": "Storefront visibility."
      },
      "PublishBlocker": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "enum": [
              "onboarding_incomplete",
              "not_registered_business",
              "public_store_disabled",
              "no_design",
              "files_not_approved",
              "category_required",
              "compliance_documents_missing"
            ],
            "description": "Why the product cannot be made public:\n- `onboarding_incomplete`: store onboarding (billing and store details) is not finished.\n- `not_registered_business`: products can only be sold by a registered business; update your billing details.\n- `public_store_disabled`: your public store is turned off in your store settings.\n- `no_design`: the product has no design file.\n- `files_not_approved`: 3DAPI has not approved the design files yet.\n- `category_required`: the product needs a category.\n- `compliance_documents_missing`: a document required by the category is missing or not approved."
          },
          "message": {
            "type": "string",
            "description": "What to do about it, ready to show to a person.",
            "example": "Select a product category before publishing."
          }
        },
        "required": [
          "code",
          "message"
        ]
      },
      "Image": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Public image URL."
          },
          "primary": {
            "type": "boolean",
            "description": "True for the main image, shown first. At most one."
          }
        },
        "required": [
          "url",
          "primary"
        ],
        "description": "A product or stock photo."
      },
      "Variant": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^var_[0-9A-Za-z]+$",
            "description": "Unique identifier of the variant, prefixed with `var_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "variant"
            ],
            "description": "Names the type of this object. Always `variant`."
          },
          "sku": {
            "type": "string",
            "description": "Your SKU code, unique across your active variants.",
            "example": "TRACKER-BLUE"
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Display name of the variant.",
            "example": "PR Tracker Blue"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "inactive"
            ],
            "description": "`active` variants can be ordered; `inactive` ones are kept but not sold."
          },
          "options": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string",
                  "description": "Option name, matching one in the product `options`.",
                  "example": "Color"
                },
                "value": {
                  "type": "string",
                  "description": "The value this variant has for that option.",
                  "example": "Blue"
                }
              },
              "required": [
                "name",
                "value"
              ]
            },
            "description": "Option values, in the product option order."
          },
          "category": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/CategoryReference"
              },
              {
                "$ref": "#/components/schemas/Category"
              },
              {
                "type": "null"
              }
            ],
            "description": "A category that overrides the product category for this variant only. Null when the variant uses the product category."
          },
          "pricing": {
            "type": "object",
            "properties": {
              "price": {
                "type": "object",
                "properties": {
                  "amount": {
                    "type": "integer",
                    "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                    "example": 5000
                  },
                  "currency": {
                    "type": "string",
                    "pattern": "^[a-z]{3}$",
                    "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                    "example": "usd"
                  }
                },
                "required": [
                  "amount",
                  "currency"
                ],
                "description": "What the buyer pays."
              },
              "prices_include_vat": {
                "type": "boolean",
                "description": "Whether `price` already includes VAT. Resolved from the variant, then the product, then your store setting."
              },
              "costs": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "manufacturing": {
                    "type": "object",
                    "properties": {
                      "amount": {
                        "type": "integer",
                        "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                        "example": 5000
                      },
                      "currency": {
                        "type": "string",
                        "pattern": "^[a-z]{3}$",
                        "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                        "example": "usd"
                      }
                    },
                    "required": [
                      "amount",
                      "currency"
                    ],
                    "description": "Printing and production, extras excluded."
                  },
                  "extra_parts": {
                    "type": "object",
                    "properties": {
                      "amount": {
                        "type": "integer",
                        "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                        "example": 5000
                      },
                      "currency": {
                        "type": "string",
                        "pattern": "^[a-z]{3}$",
                        "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                        "example": "usd"
                      }
                    },
                    "required": [
                      "amount",
                      "currency"
                    ],
                    "description": "The extras in the variant."
                  },
                  "platform_fee": {
                    "type": "object",
                    "properties": {
                      "amount": {
                        "type": "integer",
                        "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                        "example": 5000
                      },
                      "currency": {
                        "type": "string",
                        "pattern": "^[a-z]{3}$",
                        "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                        "example": "usd"
                      }
                    },
                    "required": [
                      "amount",
                      "currency"
                    ],
                    "description": "3DAPI commission, at your plan rate."
                  }
                },
                "required": [
                  "manufacturing",
                  "extra_parts",
                  "platform_fee"
                ],
                "description": "What it costs to fulfil one unit. Null until 3DAPI has priced every part of the variant."
              },
              "earnings": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "amount": {
                    "type": "integer",
                    "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                    "example": 5000
                  },
                  "currency": {
                    "type": "string",
                    "pattern": "^[a-z]{3}$",
                    "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                    "example": "usd"
                  }
                },
                "required": [
                  "amount",
                  "currency"
                ],
                "description": "What you earn per unit sold: `price` minus every cost. Null until the variant is priced."
              }
            },
            "required": [
              "price",
              "prices_include_vat",
              "costs",
              "earnings"
            ],
            "description": "Price and margin for one unit."
          },
          "shipping": {
            "type": "object",
            "properties": {
              "tier": {
                "type": [
                  "string",
                  "null"
                ],
                "enum": [
                  "small",
                  "medium",
                  "large",
                  null
                ],
                "description": "Shipping tier: `small` up to 600 g, `medium` 600 g to 2 kg, `large` above 2 kg. Null until every design in the variant has been measured."
              },
              "determined_by": {
                "type": [
                  "string",
                  "null"
                ],
                "enum": [
                  "weight",
                  "volume",
                  null
                ],
                "description": "Whether weight or size put the variant in its tier. Carriers charge for whichever is greater, so a bulky, light print can ship in a bigger tier. Null when `tier` is null."
              },
              "chargeable_weight_grams": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "The weight the tier is based on: the greater of actual and volumetric weight. Null when `tier` is null.",
                "example": 820
              },
              "extra_small": {
                "type": "boolean",
                "description": "True when the variant is light enough (under 100 g) that extra units in the same order ship without the additional-item charge."
              }
            },
            "required": [
              "tier",
              "determined_by",
              "chargeable_weight_grams",
              "extra_small"
            ],
            "description": "How one unit is charged for shipping. Prices per tier and zone come from the shipping rates."
          },
          "contents": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/VariantContentItem"
            },
            "description": "What each unit of the variant is made of."
          },
          "image": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "url": {
                "type": "string",
                "format": "uri",
                "description": "Public image URL."
              }
            },
            "required": [
              "url"
            ],
            "description": "Image specific to this variant, if any."
          }
        },
        "required": [
          "id",
          "object",
          "sku",
          "name",
          "status",
          "options",
          "category",
          "pricing",
          "shipping",
          "contents",
          "image"
        ],
        "description": "An orderable version of a product with its own SKU, price and contents, for example one color."
      },
      "VariantContentItem": {
        "oneOf": [
          {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "design"
                ],
                "description": "A printed part."
              },
              "quantity": {
                "type": "integer",
                "minimum": 1,
                "description": "Copies of the design in one unit of the variant."
              },
              "design": {
                "anyOf": [
                  {
                    "$ref": "#/components/schemas/DesignReference"
                  },
                  {
                    "$ref": "#/components/schemas/Design"
                  }
                ],
                "description": "A design reference by default. With `expand[]=variants.contents.design` it is the full design, identical to what its own GET returns, when your API key has the read scope that GET needs; otherwise it stays a reference."
              },
              "materials": {
                "type": "array",
                "items": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/MaterialReference"
                    },
                    {
                      "$ref": "#/components/schemas/Material"
                    }
                  ],
                  "description": "A material reference by default. With `expand[]=variants.contents.materials` it is the full material."
                },
                "description": "Materials this variant prints the design in: one for a single-material design, one per color slot for a multicolor design."
              }
            },
            "required": [
              "type",
              "quantity",
              "design",
              "materials"
            ]
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "stock"
                ],
                "description": "Ready-made units taken from a stock."
              },
              "quantity": {
                "type": "integer",
                "minimum": 1,
                "description": "Stock units used by one unit of the variant."
              },
              "consumes_stock": {
                "type": "boolean",
                "description": "True when the variant ships from this stock. False when the stock is linked but not used for fulfilment."
              },
              "stock": {
                "anyOf": [
                  {
                    "$ref": "#/components/schemas/StockReference"
                  },
                  {
                    "$ref": "#/components/schemas/Stock"
                  }
                ],
                "description": "A stock reference by default. With `expand[]=variants.contents.stock` it is the full stock, identical to what its own GET returns, when your API key has the read scope that GET needs; otherwise it stays a reference."
              }
            },
            "required": [
              "type",
              "quantity",
              "consumes_stock",
              "stock"
            ]
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "extra"
                ],
                "description": "A bought-in part."
              },
              "quantity": {
                "type": "integer",
                "minimum": 1,
                "description": "Units of the extra in one unit of the variant."
              },
              "unit_price": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "amount": {
                    "type": "integer",
                    "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                    "example": 5000
                  },
                  "currency": {
                    "type": "string",
                    "pattern": "^[a-z]{3}$",
                    "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                    "example": "usd"
                  }
                },
                "required": [
                  "amount",
                  "currency"
                ],
                "description": "What one unit of the extra adds to the variant cost. Null until the extra is priced."
              },
              "extra": {
                "anyOf": [
                  {
                    "$ref": "#/components/schemas/ExtraReference"
                  },
                  {
                    "$ref": "#/components/schemas/Extra"
                  }
                ],
                "description": "A extra reference by default. With `expand[]=variants.contents.extra` it is the full extra, identical to what its own GET returns, when your API key has the read scope that GET needs; otherwise it stays a reference."
              }
            },
            "required": [
              "type",
              "quantity",
              "unit_price",
              "extra"
            ]
          }
        ],
        "description": "One thing that goes into each unit of the variant. `type` says which fields are set."
      },
      "DesignReference": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^dsg_[0-9A-Za-z]+$",
            "description": "Unique identifier of the design, prefixed with `dsg_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "dsg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "design"
            ],
            "description": "Names the type of this object. Always `design`."
          },
          "name": {
            "type": "string",
            "description": "Display name of the design."
          }
        },
        "required": [
          "id",
          "object",
          "name"
        ],
        "description": "Reference to a design. Pass the matching `expand[]` path to receive the full design instead."
      },
      "Design": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^dsg_[0-9A-Za-z]+$",
            "description": "Unique identifier of the design, prefixed with `dsg_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "dsg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "design"
            ],
            "description": "Names the type of this object. Always `design`."
          },
          "name": {
            "type": "string",
            "description": "Display name, usually the name of the uploaded file.",
            "example": "tracker.3mf"
          },
          "owner": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ProductReference"
              },
              {
                "$ref": "#/components/schemas/StockReference"
              }
            ],
            "description": "The product or stock this design belongs to. A design always belongs to exactly one of them; check `object`."
          },
          "review": {
            "$ref": "#/components/schemas/Review"
          },
          "files": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DesignFile"
            },
            "description": "The model files of this design. Usually exactly one."
          },
          "materials": {
            "$ref": "#/components/schemas/DesignMaterials"
          },
          "manufacturing": {
            "type": "object",
            "properties": {
              "colors": {
                "type": "integer",
                "minimum": 1,
                "maximum": 8,
                "description": "How many colors the print uses at once, 1 to 8."
              },
              "requires_ams": {
                "type": "boolean",
                "description": "Whether printing needs an automatic material system (several filaments loaded at once)."
              },
              "assembly_required": {
                "type": "boolean",
                "description": "Whether the printed parts must be assembled before shipping."
              },
              "farm_instructions": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Notes for the production partner, for example assembly steps."
              },
              "extra_parts_note": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Free-text description of extra parts, kept for reference. The extras that are priced and shipped are the ones in each variant `contents`."
              }
            },
            "required": [
              "colors",
              "requires_ams",
              "assembly_required",
              "farm_instructions",
              "extra_parts_note"
            ],
            "description": "How the design is produced."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Point in time, ISO 8601 in UTC.",
            "example": "2026-04-21T07:34:06Z"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "Point in time, ISO 8601 in UTC.",
            "example": "2026-04-21T07:34:06Z"
          }
        },
        "required": [
          "id",
          "object",
          "name",
          "owner",
          "review",
          "files",
          "materials",
          "manufacturing",
          "created_at",
          "updated_at"
        ],
        "description": "A printable model: its files, the materials it can be printed in and how it is produced. Every design is reviewed by 3DAPI before it can be manufactured."
      },
      "StockReference": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^stk_[0-9A-Za-z]+$",
            "description": "Unique identifier of the stock, prefixed with `stk_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "stk_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "stock"
            ],
            "description": "Names the type of this object. Always `stock`."
          },
          "name": {
            "type": "string",
            "description": "Display name of the stock."
          }
        },
        "required": [
          "id",
          "object",
          "name"
        ],
        "description": "Reference to a stock. Pass the matching `expand[]` path to receive the full stock instead."
      },
      "Review": {
        "type": "object",
        "properties": {
          "status": {
            "$ref": "#/components/schemas/ReviewStatus"
          },
          "reason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why it was rejected. Null unless `status` is `rejected`."
          },
          "reviewed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the latest review decision was made. Null while pending.",
            "example": "2026-04-21T07:34:06Z"
          }
        },
        "required": [
          "status",
          "reason",
          "reviewed_at"
        ],
        "description": "Review by 3DAPI. Anything that will be manufactured is reviewed before it can be sold or produced."
      },
      "DesignFile": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "File name.",
            "example": "tracker.3mf"
          },
          "format": {
            "type": "string",
            "enum": [
              "3mf",
              "stl"
            ],
            "description": "File format."
          },
          "size_bytes": {
            "type": [
              "integer",
              "null"
            ],
            "description": "File size in bytes. Null only when the size cannot be read from storage.",
            "example": 2758723
          },
          "download_url": {
            "type": "string",
            "format": "uri",
            "description": "Signed URL to download the file. Valid for 15 minutes; fetch the design again for a fresh one."
          },
          "id": {
            "type": "string",
            "pattern": "^dfl_[0-9A-Za-z]+$",
            "description": "Unique identifier of the design file, prefixed with `dfl_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "dfl_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "design_file"
            ],
            "description": "Names the type of this object. Always `design_file`."
          },
          "uploaded_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the file was uploaded.",
            "example": "2026-04-21T07:34:06Z"
          },
          "production_version": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "name": {
                "type": "string",
                "description": "File name.",
                "example": "tracker.3mf"
              },
              "format": {
                "type": "string",
                "enum": [
                  "3mf",
                  "stl"
                ],
                "description": "File format."
              },
              "size_bytes": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "File size in bytes. Null only when the size cannot be read from storage.",
                "example": 2758723
              },
              "download_url": {
                "type": "string",
                "format": "uri",
                "description": "Signed URL to download the file. Valid for 15 minutes; fetch the design again for a fresh one."
              },
              "prepared_at": {
                "type": "string",
                "format": "date-time",
                "description": "When 3DAPI prepared this version.",
                "example": "2026-04-21T07:34:06Z"
              }
            },
            "required": [
              "name",
              "format",
              "size_bytes",
              "download_url",
              "prepared_at"
            ],
            "description": "The copy 3DAPI adapted to the production printer (printer, process and filament settings written in). This is the file that gets printed. Null when your upload is printed as it is."
          }
        },
        "required": [
          "name",
          "format",
          "size_bytes",
          "download_url",
          "id",
          "object",
          "uploaded_at",
          "production_version"
        ],
        "description": "A printable model file. The top-level fields describe your upload, exactly as you sent it."
      },
      "DesignMaterials": {
        "oneOf": [
          {
            "$ref": "#/components/schemas/SingleMaterialConfig"
          },
          {
            "$ref": "#/components/schemas/MulticolorMaterialConfig"
          }
        ],
        "discriminator": {
          "propertyName": "mode",
          "mapping": {
            "single": "#/components/schemas/SingleMaterialConfig",
            "multicolor": "#/components/schemas/MulticolorMaterialConfig"
          }
        },
        "description": "Which materials the design may be printed in. `mode` tells the two shapes apart."
      },
      "SingleMaterialConfig": {
        "type": "object",
        "properties": {
          "mode": {
            "type": "string",
            "enum": [
              "single"
            ],
            "description": "The whole design prints in one material."
          },
          "allowed": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MaterialReference"
            },
            "description": "Materials buyers or variants may choose from. Each variant picks one."
          }
        },
        "required": [
          "mode",
          "allowed"
        ]
      },
      "MaterialReference": {
        "type": "object",
        "properties": {
          "id": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^mat_[0-9A-Za-z]+$",
            "description": "Unique identifier of a catalog material, prefixed with `mat_`. Null for a custom material.",
            "example": "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "material"
            ],
            "description": "Names the type of this object. Always `material`."
          },
          "name": {
            "type": "string",
            "description": "Display name, for example `PLA Black`. For custom materials, exactly what the seller typed.",
            "example": "PLA Black"
          },
          "custom": {
            "type": "boolean",
            "description": "True for a seller-specified material that is not in the catalog. Custom materials need manual review before production."
          }
        },
        "required": [
          "id",
          "object",
          "name",
          "custom"
        ],
        "description": "Reference to a material. Catalog materials can be expanded to the full material; custom materials have no id and nothing more to expand."
      },
      "MulticolorMaterialConfig": {
        "type": "object",
        "properties": {
          "mode": {
            "type": "string",
            "enum": [
              "multicolor"
            ],
            "description": "The design prints in several materials at once."
          },
          "combinations": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "description": "Identifier of the combination, unique within the design."
                },
                "name": {
                  "type": "string",
                  "description": "Display name listing the materials in slot order.",
                  "example": "PETG Yellow + PETG Orange"
                },
                "slots": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/MaterialReference"
                  },
                  "description": "One material per color slot, in the order the file uses them."
                }
              },
              "required": [
                "id",
                "name",
                "slots"
              ]
            },
            "description": "Saved material combinations a variant can use."
          }
        },
        "required": [
          "mode",
          "combinations"
        ]
      },
      "Material": {
        "type": "object",
        "properties": {
          "id": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^mat_[0-9A-Za-z]+$",
            "description": "Unique identifier of a catalog material, prefixed with `mat_`. Null for a custom material.",
            "example": "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "material"
            ],
            "description": "Names the type of this object. Always `material`."
          },
          "name": {
            "type": "string",
            "description": "Display name, for example `PLA Black`. For custom materials, exactly what the seller typed.",
            "example": "PLA Black"
          },
          "custom": {
            "type": "boolean",
            "description": "True for a seller-specified material that is not in the catalog. Custom materials need manual review before production."
          },
          "technology": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "fdm",
              null
            ],
            "description": "Printing technology. `fdm` is filament printing, the only one offered today. Null for custom materials."
          },
          "color": {
            "type": [
              "string",
              "null"
            ],
            "description": "Color name as listed in the catalog, for example `Black`. Null for custom materials.",
            "example": "Black"
          },
          "color_hex": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^#[0-9A-F]{6}$",
            "description": "Reference colour of the filament as `#RRGGBB`, the colour 3DAPI matches the colours in your 3MF files against (see `suggested_materials` on a quote). Null for custom materials and catalog materials without a reference colour.",
            "example": "#000000"
          },
          "filament_type": {
            "type": [
              "string",
              "null"
            ],
            "description": "Plastic family in lowercase, for example `pla` or `petg`. Null for custom materials and when unknown.",
            "example": "pla"
          }
        },
        "required": [
          "id",
          "object",
          "name",
          "custom",
          "technology",
          "color",
          "color_hex",
          "filament_type"
        ],
        "description": "A material a design can be printed in, either from the 3DAPI catalog or custom."
      },
      "Stock": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^stk_[0-9A-Za-z]+$",
            "description": "Unique identifier of the stock, prefixed with `stk_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "stk_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "stock"
            ],
            "description": "Names the type of this object. Always `stock`."
          },
          "name": {
            "type": "string",
            "description": "Display name.",
            "example": "Clicker 50 pack"
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "description": "Longer description, when provided."
          },
          "review": {
            "$ref": "#/components/schemas/Review"
          },
          "units_per_batch": {
            "type": "integer",
            "minimum": 0,
            "description": "How many units one purchased batch contains.",
            "example": 50
          },
          "batch_price": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "amount": {
                "type": "integer",
                "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                "example": 5000
              },
              "currency": {
                "type": "string",
                "pattern": "^[a-z]{3}$",
                "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                "example": "usd"
              }
            },
            "required": [
              "amount",
              "currency"
            ],
            "description": "What you pay for one batch, the same in every region. Set by 3DAPI when the stock is approved; null until then."
          },
          "unit_price": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "amount": {
                "type": "integer",
                "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                "example": 5000
              },
              "currency": {
                "type": "string",
                "pattern": "^[a-z]{3}$",
                "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                "example": "usd"
              }
            },
            "required": [
              "amount",
              "currency"
            ],
            "description": "What one unit costs you: `batch_price` divided by `units_per_batch`. Null until the stock is priced."
          },
          "contents": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StockContentItem"
            },
            "description": "What every unit of the stock is made of."
          },
          "inventory": {
            "type": "object",
            "properties": {
              "available": {
                "type": "integer",
                "minimum": 0,
                "description": "Units made and ready to ship.",
                "example": 32
              },
              "in_production": {
                "type": "integer",
                "minimum": 0,
                "description": "Units paid for and not yet made.",
                "example": 18
              },
              "by_region": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "region": {
                      "$ref": "#/components/schemas/RegionReference"
                    },
                    "available": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Units made and ready to ship.",
                      "example": 32
                    },
                    "in_production": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Units paid for and not yet made.",
                      "example": 18
                    }
                  },
                  "required": [
                    "region",
                    "available",
                    "in_production"
                  ]
                },
                "description": "The same counts per production region. Orders are filled from a region that ships to the buyer."
              }
            },
            "required": [
              "available",
              "in_production",
              "by_region"
            ],
            "description": "Units across every batch you bought."
          },
          "restock": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "region": {
                  "$ref": "#/components/schemas/RegionReference"
                },
                "mode": {
                  "type": "string",
                  "enum": [
                    "off",
                    "notify",
                    "auto_buy"
                  ],
                  "description": "What happens when this region reaches `when_available_below`:\n- `off`: nothing.\n- `notify`: you get a low-stock email.\n- `auto_buy`: batches for `reorder_units` are bought from your wallet, when auto-reorder is enabled on your account and the balance covers them; otherwise you get the email."
                },
                "when_available_below": {
                  "type": [
                    "integer",
                    "null"
                  ],
                  "minimum": 1,
                  "description": "Threshold in units. The rule is checked each time an order draws units from this stock (when the order is assigned for production), not continuously. It counts the units paid for minus the units used in this region, across every batch and including units still in production (so not `available`), and triggers when that count is at or below this threshold. Null when `mode` is `off`.",
                  "example": 10
                },
                "reorder_units": {
                  "type": [
                    "integer",
                    "null"
                  ],
                  "minimum": 1,
                  "description": "Units to buy per automatic purchase, returned as set. Purchases round up to whole batches: an automatic purchase buys `reorder_units` divided by `units_per_batch`, rounded up (at least one batch), at `batch_price` each, and the batch it creates holds exactly those batches times `units_per_batch` units. A multiple of `units_per_batch` buys exactly what you ask for. Only set when `mode` is `auto_buy`.",
                  "example": 50
                }
              },
              "required": [
                "region",
                "mode",
                "when_available_below",
                "reorder_units"
              ]
            },
            "description": "Restock rule for each region the stock can be produced in. The regions are chosen by 3DAPI at approval."
          },
          "images": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Image"
            },
            "description": "Photos of the stock."
          },
          "attachments": {
            "type": "array",
            "items": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/AttachmentReference"
                },
                {
                  "$ref": "#/components/schemas/Attachment"
                }
              ],
              "description": "A attachment reference by default. With `expand[]=attachments` it is the full attachment, identical to what its own GET returns, when your API key has the read scope that GET needs; otherwise it stays a reference."
            },
            "description": "Production documents for this stock."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Point in time, ISO 8601 in UTC.",
            "example": "2026-04-21T07:34:06Z"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "Point in time, ISO 8601 in UTC.",
            "example": "2026-04-21T07:34:06Z"
          }
        },
        "required": [
          "id",
          "object",
          "name",
          "description",
          "review",
          "units_per_batch",
          "batch_price",
          "unit_price",
          "contents",
          "inventory",
          "restock",
          "images",
          "attachments",
          "created_at",
          "updated_at"
        ],
        "description": "Ready-made units you buy in batches ahead of time and that are kept in storage until orders use them. A variant can ship from a stock instead of being printed on demand."
      },
      "StockContentItem": {
        "oneOf": [
          {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "design"
                ],
                "description": "A printed part."
              },
              "design": {
                "anyOf": [
                  {
                    "$ref": "#/components/schemas/DesignReference"
                  },
                  {
                    "$ref": "#/components/schemas/Design"
                  }
                ],
                "description": "A design reference by default. With `expand[]=contents.design` it is the full design, identical to what its own GET returns, when your API key has the read scope that GET needs; otherwise it stays a reference."
              }
            },
            "required": [
              "type",
              "design"
            ]
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "extra"
                ],
                "description": "A bought-in part."
              },
              "extra": {
                "anyOf": [
                  {
                    "$ref": "#/components/schemas/ExtraReference"
                  },
                  {
                    "$ref": "#/components/schemas/Extra"
                  }
                ],
                "description": "A extra reference by default. With `expand[]=contents.extra` it is the full extra, identical to what its own GET returns, when your API key has the read scope that GET needs; otherwise it stays a reference."
              }
            },
            "required": [
              "type",
              "extra"
            ]
          }
        ],
        "description": "One thing that goes into every unit of the stock. `type` says which field is set. Each item counts once per unit."
      },
      "Extra": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^xtr_[0-9A-Za-z]+$",
            "description": "Unique identifier of the extra, prefixed with `xtr_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "xtr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "extra"
            ],
            "description": "Names the type of this object. Always `extra`."
          },
          "name": {
            "type": "string",
            "description": "Display name of the part.",
            "example": "Magnet 10mm"
          },
          "description": {
            "type": "string",
            "description": "What the part is, as written by the seller.",
            "example": "Neodymium magnet, 10mm diameter."
          },
          "review": {
            "$ref": "#/components/schemas/Review"
          },
          "price": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "amount": {
                "type": "integer",
                "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                "example": 5000
              },
              "currency": {
                "type": "string",
                "pattern": "^[a-z]{3}$",
                "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                "example": "usd"
              }
            },
            "required": [
              "amount",
              "currency"
            ],
            "description": "What one unit adds to a variant cost, under `costs.extra_parts` (designers), or to the production price of each printed unit of an order line that carries it (integrators, `items[].extras`). Set by 3DAPI when the extra is approved. While an edited extra is back in review this stays the last approved price. Null until the extra is approved, and null again after a rejection: rejecting clears the price even if the extra was approved before."
          },
          "purchase_links": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "url": {
                  "type": "string",
                  "maxLength": 2048,
                  "format": "uri",
                  "description": "Where the part can be bought: an http:// or https:// URL. Any other link is refused with `validation_failed`."
                },
                "label": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Short label for the link, for example `Datasheet`."
                }
              },
              "required": [
                "url",
                "label"
              ]
            },
            "description": "Where the production partner can buy the part. At least one."
          },
          "images": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "url": {
                  "type": "string",
                  "format": "uri",
                  "description": "Public image URL."
                },
                "name": {
                  "type": "string",
                  "description": "Original file name."
                }
              },
              "required": [
                "url",
                "name"
              ]
            },
            "description": "Photos of the part. At least one."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Point in time, ISO 8601 in UTC.",
            "example": "2026-04-21T07:34:06Z"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the extra last changed: created, edited, or approved or rejected by 3DAPI. Extras last changed before this was recorded show the latest of their creation and review decision instead.",
            "example": "2026-04-21T07:34:06Z"
          }
        },
        "required": [
          "id",
          "object",
          "name",
          "description",
          "review",
          "price",
          "purchase_links",
          "images",
          "created_at",
          "updated_at"
        ],
        "description": "A bought-in part that ships with a product, for example a magnet, screw or battery. Extras belong to the account (designer or integrator) that created them and are reviewed by 3DAPI before use."
      },
      "RegionReference": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^reg_[0-9A-Za-z]+$",
            "description": "Unique identifier of the region, prefixed with `reg_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "reg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "region"
            ],
            "description": "Names the type of this object. Always `region`."
          },
          "name": {
            "type": "string",
            "description": "Display name of the region."
          }
        },
        "required": [
          "id",
          "object",
          "name"
        ],
        "description": "Reference to a region. Pass the matching `expand[]` path to receive the full region instead."
      },
      "AttachmentReference": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^att_[0-9A-Za-z]+$",
            "description": "Unique identifier of the attachment, prefixed with `att_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "att_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "attachment"
            ],
            "description": "Names the type of this object. Always `attachment`."
          },
          "name": {
            "type": "string",
            "description": "Display name of the attachment."
          }
        },
        "required": [
          "id",
          "object",
          "name"
        ],
        "description": "Reference to a attachment. Pass the matching `expand[]` path to receive the full attachment instead."
      },
      "Attachment": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^att_[0-9A-Za-z]+$",
            "description": "Unique identifier of the attachment, prefixed with `att_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "att_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "attachment"
            ],
            "description": "Names the type of this object. Always `attachment`."
          },
          "name": {
            "type": "string",
            "description": "File name.",
            "example": "assembly-guide.pdf"
          },
          "format": {
            "type": "string",
            "description": "File extension, lowercase, for example `pdf` or `mp4`.",
            "example": "pdf"
          },
          "size_bytes": {
            "type": "integer",
            "description": "File size in bytes.",
            "example": 482113
          },
          "download_url": {
            "type": "string",
            "format": "uri",
            "description": "Signed URL to download the file. Valid for 15 minutes; fetch the attachment again for a fresh one."
          },
          "owner": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ProductReference"
              },
              {
                "$ref": "#/components/schemas/StockReference"
              }
            ],
            "description": "The product or stock this file belongs to."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Point in time, ISO 8601 in UTC.",
            "example": "2026-04-21T07:34:06Z"
          }
        },
        "required": [
          "id",
          "object",
          "name",
          "format",
          "size_bytes",
          "download_url",
          "owner",
          "created_at"
        ],
        "description": "A production document, such as assembly instructions or a guide. It is sent to the production partner with every order of its product or stock and is never shown to buyers."
      },
      "ProductComplianceSummary": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "product_compliance"
            ],
            "description": "Names the type of this object. Always `product_compliance`."
          },
          "enforced": {
            "type": "boolean",
            "description": "Whether missing documents block publishing this product right now. Only EU/UK sellers are held to it, and only while document enforcement is switched on by 3DAPI; when true, the product `publishing.blockers` include `compliance_documents_missing` until `complete` is true."
          },
          "complete": {
            "type": "boolean",
            "description": "True when every requirement is approved."
          },
          "missing": {
            "type": "integer",
            "minimum": 0,
            "description": "Requirements without an approved document.",
            "example": 1
          }
        },
        "required": [
          "object",
          "enforced",
          "complete",
          "missing"
        ],
        "description": "Compliance at a glance. Pass `expand[]=compliance` for every requirement and document."
      },
      "ProductCompliance": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "product_compliance"
            ],
            "description": "Names the type of this object. Always `product_compliance`."
          },
          "product": {
            "$ref": "#/components/schemas/ProductReference"
          },
          "enforced": {
            "type": "boolean",
            "description": "Whether missing documents block publishing this product right now. Only EU/UK sellers are held to it, and only while document enforcement is switched on by 3DAPI; when true, the product `publishing.blockers` include `compliance_documents_missing` until `complete` is true."
          },
          "complete": {
            "type": "boolean",
            "description": "True when every requirement is approved."
          },
          "requirements": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ComplianceRequirement"
            },
            "description": "Every document the product categories require, in category order."
          },
          "other_documents": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ComplianceDocument"
            },
            "description": "Documents uploaded for requirements that no longer apply, for example after a category change. Kept so nothing you uploaded disappears."
          }
        },
        "required": [
          "object",
          "product",
          "enforced",
          "complete",
          "requirements",
          "other_documents"
        ],
        "description": "The EU/UK product safety documents a product needs, what you uploaded for each, and whether they are approved."
      },
      "ComplianceRequirement": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "description": "Stable identifier of the requirement, as listed on the category.",
            "example": "material-toxicity-assessment"
          },
          "name": {
            "type": "string",
            "description": "Name of the required document, as the category lists it.",
            "example": "Material Toxicity Assessment"
          },
          "status": {
            "$ref": "#/components/schemas/ComplianceRequirementStatus"
          },
          "required_by": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CategoryReference"
            },
            "description": "The categories that ask for this document: the product category and any variant category overrides."
          },
          "documents": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ComplianceDocument"
            },
            "description": "Documents uploaded for this requirement, newest first."
          }
        },
        "required": [
          "key",
          "name",
          "status",
          "required_by",
          "documents"
        ]
      },
      "ComplianceRequirementStatus": {
        "type": "string",
        "enum": [
          "approved",
          "pending_review",
          "rejected",
          "missing"
        ],
        "description": "Where a requirement stands:\n- `approved`: an approved document covers it.\n- `pending_review`: a document is uploaded and waiting for review by 3DAPI.\n- `rejected`: every document uploaded for it was rejected; see each `review.reason`.\n- `missing`: nothing is uploaded for it yet."
      },
      "ComplianceDocument": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^cdc_[0-9A-Za-z]+$",
            "description": "Unique identifier of the compliance document, prefixed with `cdc_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "cdc_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "compliance_document"
            ],
            "description": "Names the type of this object. Always `compliance_document`."
          },
          "name": {
            "type": "string",
            "description": "File name.",
            "example": "toxicity-report.pdf"
          },
          "format": {
            "type": [
              "string",
              "null"
            ],
            "description": "File extension, lowercase.",
            "example": "pdf"
          },
          "size_bytes": {
            "type": [
              "integer",
              "null"
            ],
            "description": "File size in bytes."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Signed URL to download the document. Valid for 15 minutes; get the compliance view again for a fresh one."
          },
          "review": {
            "$ref": "#/components/schemas/Review"
          },
          "uploaded_at": {
            "type": "string",
            "format": "date-time",
            "description": "Point in time, ISO 8601 in UTC.",
            "example": "2026-04-21T07:34:06Z"
          }
        },
        "required": [
          "id",
          "object",
          "name",
          "format",
          "size_bytes",
          "url",
          "review",
          "uploaded_at"
        ],
        "description": "A safety document you uploaded for a product, and its review."
      },
      "StockBatch": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^bat_[0-9A-Za-z]+$",
            "description": "Unique identifier of the stock batch, prefixed with `bat_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "bat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "stock_batch"
            ],
            "description": "Names the type of this object. Always `stock_batch`."
          },
          "stock": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "pattern": "^stk_[0-9A-Za-z]+$",
                "description": "Unique identifier of the stock, prefixed with `stk_`. Treat it as an opaque string: store it and compare it, never parse it.",
                "example": "stk_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
              },
              "object": {
                "type": "string",
                "enum": [
                  "stock"
                ],
                "description": "Names the type of this object. Always `stock`."
              },
              "name": {
                "type": "string",
                "description": "Display name of the stock."
              }
            },
            "required": [
              "id",
              "object",
              "name"
            ],
            "description": "The stock this batch is of."
          },
          "region": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "pattern": "^reg_[0-9A-Za-z]+$",
                "description": "Unique identifier of the region, prefixed with `reg_`. Treat it as an opaque string: store it and compare it, never parse it.",
                "example": "reg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
              },
              "object": {
                "type": "string",
                "enum": [
                  "region"
                ],
                "description": "Names the type of this object. Always `region`."
              },
              "name": {
                "type": "string",
                "description": "Display name of the region."
              }
            },
            "required": [
              "id",
              "object",
              "name"
            ],
            "description": "Production region holding the units. Orders use units from a region that ships to the buyer."
          },
          "status": {
            "$ref": "#/components/schemas/StockBatchStatus"
          },
          "batches": {
            "type": "integer",
            "minimum": 1,
            "description": "How many batches were bought in this purchase line, at least one. Each holds the stock `units_per_batch`. An `auto_reorder` purchase rounds `reorder_units` up to whole batches.",
            "example": 2
          },
          "units": {
            "type": "object",
            "properties": {
              "purchased": {
                "type": "integer",
                "minimum": 0,
                "description": "Units bought in this purchase line: `batches` times units per batch.",
                "example": 100
              },
              "made": {
                "type": "integer",
                "minimum": 0,
                "description": "Units the production partner has made so far.",
                "example": 60
              },
              "used": {
                "type": "integer",
                "minimum": 0,
                "description": "Units orders have used.",
                "example": 20
              },
              "available": {
                "type": "integer",
                "minimum": 0,
                "description": "Units ready to ship: `made` minus `used`.",
                "example": 40
              }
            },
            "required": [
              "purchased",
              "made",
              "used",
              "available"
            ],
            "description": "Unit counts for this batch."
          },
          "price_paid": {
            "type": "object",
            "properties": {
              "amount": {
                "type": "integer",
                "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                "example": 5000
              },
              "currency": {
                "type": "string",
                "pattern": "^[a-z]{3}$",
                "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                "example": "usd"
              }
            },
            "required": [
              "amount",
              "currency"
            ],
            "description": "What you paid for this purchase line, all batches included."
          },
          "unit_price": {
            "type": "object",
            "properties": {
              "amount": {
                "type": "integer",
                "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                "example": 5000
              },
              "currency": {
                "type": "string",
                "pattern": "^[a-z]{3}$",
                "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                "example": "usd"
              }
            },
            "required": [
              "amount",
              "currency"
            ],
            "description": "What one unit cost you."
          },
          "purchase_reference": {
            "type": [
              "string",
              "null"
            ],
            "description": "Reference of the checkout this batch was bought in, shared by every batch bought together and shown on the wallet charge.",
            "example": "ST-8KD2X"
          },
          "origin": {
            "type": "string",
            "enum": [
              "manual",
              "auto_reorder"
            ],
            "description": "How the batch was bought: `manual` by you (dashboard or API), `auto_reorder` automatically from your wallet when a region reached its restock threshold."
          },
          "purchased_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the batch was bought.",
            "example": "2026-04-21T07:34:06Z"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When every unit was made. Null until then.",
            "example": "2026-04-21T07:34:06Z"
          }
        },
        "required": [
          "id",
          "object",
          "stock",
          "region",
          "status",
          "batches",
          "units",
          "price_paid",
          "unit_price",
          "purchase_reference",
          "origin",
          "purchased_at",
          "completed_at"
        ],
        "description": "One purchase of a stock in one region: how many units were bought, how many are made and how many orders have used."
      },
      "StockBatchStatus": {
        "type": "string",
        "enum": [
          "queued",
          "in_production",
          "completed",
          "used_up"
        ],
        "description": "Where the batch is:\n- `queued`: paid for; production has not started.\n- `in_production`: the production partner is making the units. Units already made can be used by orders.\n- `completed`: every unit has been made.\n- `used_up`: orders have used every unit."
      },
      "Quote": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^qte_[0-9A-Za-z]+$",
            "description": "Unique identifier of the quote, prefixed with `qte_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "qte_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "quote"
            ],
            "description": "Names the type of this object. Always `quote`."
          },
          "name": {
            "type": "string",
            "description": "Display name of the quote. The `name` you sent when creating it, otherwise the uploaded file name.",
            "example": "Phone stand v2"
          },
          "file": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string",
                "description": "Name of the uploaded file.",
                "example": "phone-stand-v2.3mf"
              },
              "size_bytes": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "Size of the uploaded file in bytes. Null for old quotes created before sizes were recorded.",
                "example": 2758723
              },
              "download_url": {
                "type": "string",
                "format": "uri",
                "description": "Signed link to download the uploaded file. It expires after 15 minutes; fetch the quote again for a fresh one."
              },
              "production_version": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "File name.",
                    "example": "phone-stand-v2.3mf"
                  },
                  "format": {
                    "type": "string",
                    "enum": [
                      "3mf"
                    ],
                    "description": "File format. Always `3mf`: a mesh upload (STL, OBJ, STEP) becomes a 3MF project."
                  },
                  "size_bytes": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "description": "File size in bytes. Null when it was not recorded.",
                    "example": 2913004
                  },
                  "download_url": {
                    "type": "string",
                    "format": "uri",
                    "description": "Signed link to download the file. It expires after 15 minutes; fetch the quote again for a fresh one."
                  },
                  "prepared_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When 3DAPI prepared this version.",
                    "example": "2026-04-21T07:34:06Z"
                  }
                },
                "required": [
                  "name",
                  "format",
                  "size_bytes",
                  "download_url",
                  "prepared_at"
                ],
                "description": "The copy 3DAPI adapted to the production printer (printer, process and filament settings written in), the same as a design file's `production_version`. This is the file that gets printed, and the one `price` is for.\n\nNull when your upload is printed as it is: while the quote is `processing` and the file is still being checked, when the check found nothing to change, and when the check could not be done (the quote is then priced on your upload, which is also what gets printed). It never changes once the quote is `ready`."
              }
            },
            "required": [
              "name",
              "size_bytes",
              "download_url",
              "production_version"
            ],
            "description": "The model file you uploaded, exactly as you sent it, and the production version 3DAPI prepared from it."
          },
          "status": {
            "$ref": "#/components/schemas/QuoteStatus"
          },
          "price": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "amount": {
                "type": "integer",
                "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                "example": 5000
              },
              "currency": {
                "type": "string",
                "pattern": "^[a-z]{3}$",
                "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                "example": "usd"
              }
            },
            "required": [
              "amount",
              "currency"
            ],
            "description": "Price to print one unit of the model, in usd: the price of the file that gets printed, which is `file.production_version` when there is one, otherwise your upload. Null until `status` is `ready`, and on failed quotes."
          },
          "failure_reason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why slicing failed, written to be shown to a person, usually with how to fix the file. For example, a binary STL the slicer cannot read (small models exported with zero normals are mistaken for text STL files) says to export it again with normals or as .3mf. Null unless `status` is `failed`.",
            "example": null
          },
          "suggested_materials": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "$ref": "#/components/schemas/SuggestedMaterial"
            },
            "description": "One suggested catalog material per extruder slot the file prints with, in slot order, read from the colours and filament types in your file. Only slots something prints with are listed, so the numbers can have gaps (a project using filaments 2 and 4 lists slots 2 and 4).\n\nOrder with `use_suggested_materials: true` on the order line to print with exactly these, or send `materials` as `[{ slot, material }]` for the same slots to choose your own.\n\nNull until `status` is `ready`, on failed quotes, and on quotes created before suggestions existed."
          },
          "approval": {
            "type": "object",
            "properties": {
              "status": {
                "$ref": "#/components/schemas/QuoteApprovalStatus"
              },
              "reason": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Why 3DAPI rejected the file. Null unless `status` is `rejected`."
              }
            },
            "required": [
              "status",
              "reason"
            ],
            "description": "The file in your account approval. Approval only matters until your account is approved; afterwards new quotes stay `not_submitted`."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Point in time, ISO 8601 in UTC.",
            "example": "2026-04-21T07:34:06Z"
          },
          "finished_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When slicing finished, successfully or not. Null while `processing`.",
            "example": "2026-04-21T07:34:06Z"
          }
        },
        "required": [
          "id",
          "object",
          "name",
          "file",
          "status",
          "price",
          "failure_reason",
          "suggested_materials",
          "approval",
          "created_at",
          "finished_at"
        ],
        "description": "A price for printing one model file. Integrators upload model files to get quotes; until the account is approved, 10 approved quotes are also what 3DAPI reviews to approve the account (see `get_integrator_onboarding`)."
      },
      "QuoteStatus": {
        "type": "string",
        "enum": [
          "processing",
          "ready",
          "failed"
        ],
        "description": "Where the quote stands:\n- `processing`: the file is being sliced to measure material and print time. Usually takes one to a few minutes.\n- `ready`: slicing finished and `price` is set.\n- `failed`: the file could not be sliced, for example a broken or empty model. `failure_reason` says why. Upload a corrected file as a new quote."
      },
      "SuggestedMaterial": {
        "type": "object",
        "properties": {
          "slot": {
            "type": "integer",
            "minimum": 1,
            "description": "Extruder slot (filament number in the slicer, starting at 1) that prints with this material. The print farm loads the material into this slot.",
            "example": 1
          },
          "file_color": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^#[0-9A-F]{6}$",
            "description": "Colour your file gives this slot, as `#RRGGBB`. Null when the file has no colour for it (STL, OBJ and STEP files never do).",
            "example": "#C12E1F"
          },
          "file_filament_type": {
            "type": [
              "string",
              "null"
            ],
            "description": "Filament type your file names for this slot, as written in the file (for example `PLA` or `PETG-HF`). Null when it names none. The suggestion stays within this plastic when the catalog has it, otherwise it is PLA.",
            "example": "PLA"
          },
          "material": {
            "$ref": "#/components/schemas/MaterialReference"
          },
          "match": {
            "$ref": "#/components/schemas/SuggestedMaterialMatch"
          }
        },
        "required": [
          "slot",
          "file_color",
          "file_filament_type",
          "material",
          "match"
        ],
        "description": "The catalog material 3DAPI suggests for one extruder slot of the file."
      },
      "SuggestedMaterialMatch": {
        "type": "string",
        "enum": [
          "exact",
          "close",
          "approximate"
        ],
        "description": "How close the suggested catalog material is to the colour in your file.\n- `exact`: indistinguishable to the eye.\n- `close`: the same colour, a shade apart.\n- `approximate`: the nearest the catalog has, visibly different; also used when the file gives no colour. Check it before ordering."
      },
      "QuoteApprovalStatus": {
        "type": "string",
        "enum": [
          "not_submitted",
          "in_review",
          "approved",
          "rejected"
        ],
        "description": "Where the file stands in your account approval (onboarding):\n- `not_submitted`: not part of any approval request yet.\n- `in_review`: part of the approval request 3DAPI is reviewing.\n- `approved`: approved by 3DAPI; it counts toward the 10 approved files your account needs and stays approved.\n- `rejected`: refused by 3DAPI; `reason` says why. It cannot be submitted again: upload a corrected file as a new quote and submit that instead."
      },
      "ApprovalRequest": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^apr_[0-9A-Za-z]+$",
            "description": "Unique identifier of the approval request, prefixed with `apr_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "apr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "approval_request"
            ],
            "description": "Names the type of this object. Always `approval_request`."
          },
          "status": {
            "$ref": "#/components/schemas/ApprovalRequestStatus"
          },
          "quotes": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "quote": {
                  "$ref": "#/components/schemas/QuoteReference"
                },
                "decision": {
                  "$ref": "#/components/schemas/QuoteDecision"
                },
                "reason": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Why the file was rejected, written by the reviewer. Null unless `decision` is `rejected`.",
                  "example": null
                }
              },
              "required": [
                "quote",
                "decision",
                "reason"
              ]
            },
            "description": "The quotes submitted in this request, with the decision on each file."
          },
          "submitted_at": {
            "type": "string",
            "format": "date-time",
            "description": "Point in time, ISO 8601 in UTC.",
            "example": "2026-04-21T07:34:06Z"
          },
          "reviewed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the review was completed. Null while `in_review`.",
            "example": "2026-04-21T07:34:06Z"
          }
        },
        "required": [
          "id",
          "object",
          "status",
          "quotes",
          "submitted_at",
          "reviewed_at"
        ],
        "description": "A set of your quotes sent to 3DAPI to approve your integrator account. 3DAPI approves or rejects each file separately; the account is approved once 10 files are approved."
      },
      "ApprovalRequestStatus": {
        "type": "string",
        "enum": [
          "in_review",
          "reviewed"
        ],
        "description": "- `in_review`: 3DAPI is reviewing the files. You cannot upload or send another request until it is reviewed.\n- `reviewed`: every file has a decision. If all were approved and you reached the required number, your account is approved; otherwise replace the rejected files and send a new request."
      },
      "QuoteReference": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^qte_[0-9A-Za-z]+$",
            "description": "Unique identifier of the quote, prefixed with `qte_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "qte_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "quote"
            ],
            "description": "Names the type of this object. Always `quote`."
          },
          "name": {
            "type": "string",
            "description": "Display name of the quote."
          }
        },
        "required": [
          "id",
          "object",
          "name"
        ],
        "description": "Reference to a quote. Pass the matching `expand[]` path to receive the full quote instead."
      },
      "QuoteDecision": {
        "type": "string",
        "enum": [
          "pending",
          "approved",
          "rejected"
        ],
        "description": "- `pending`: not decided yet.\n- `approved`: the file is approved and counts toward your account approval.\n- `rejected`: the file is refused; `reason` says why. Upload a corrected file and include the new quote in your next request."
      },
      "Health": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "ok",
              "degraded"
            ],
            "description": "`ok` when every dependency is reachable, `degraded` otherwise."
          },
          "database": {
            "type": "string",
            "enum": [
              "up",
              "down"
            ],
            "description": "Whether the database answered."
          },
          "checked_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the check ran.",
            "example": "2026-04-21T07:34:06Z"
          }
        },
        "required": [
          "status",
          "database",
          "checked_at"
        ],
        "description": "Service health report."
      },
      "Me": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "actor"
            ],
            "description": "Names the type of this object. Always `actor`."
          },
          "user_id": {
            "type": "string",
            "format": "uuid",
            "description": "Id of the account the credentials belong to."
          },
          "kind": {
            "type": "string",
            "enum": [
              "designer",
              "integrator"
            ],
            "description": "- `designer`: a seller on 3DAPI, using the API for their own store.\n- `integrator`: a company building on the API through the API dashboard."
          },
          "authenticated_with": {
            "type": "string",
            "enum": [
              "session",
              "api_key",
              "oauth"
            ],
            "description": "`api_key` for the `x-api-key` header, `session` for a signed-in API dashboard user, `oauth` for an app the user connected and approved, such as Claude or ChatGPT."
          },
          "scopes": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string",
              "enum": [
                "pricing",
                "order",
                "full",
                "products:read",
                "products:write",
                "orders:read",
                "orders:write",
                "orders:read_customer",
                "stocks:read",
                "stocks:write",
                "extras:read",
                "extras:write",
                "qc:read",
                "qc:write",
                "wallet:read",
                "webhooks:read",
                "quotes:read",
                "quotes:write"
              ]
            },
            "description": "The scopes stored on the API key, as chosen when it was created: per-resource scopes such as `products:read`, or a legacy permission (`full`, `order` or `pricing`). For a connected app, the scopes the user approved. See `effective_scopes` for what they allow. Null for sessions, which are not scoped."
          },
          "effective_scopes": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string",
              "enum": [
                "products:read",
                "products:write",
                "orders:read",
                "orders:write",
                "orders:read_customer",
                "stocks:read",
                "stocks:write",
                "extras:read",
                "extras:write",
                "qc:read",
                "qc:write",
                "wallet:read",
                "webhooks:read",
                "quotes:read",
                "quotes:write"
              ]
            },
            "description": "What the API key may actually do, as the per-resource scopes operations list in `x-required-scopes`: `scopes` with legacy permissions expanded. `full` gives every scope (customer data included), `order` gives `orders:read` and `orders:write`, `pricing` gives none in this API. Null for sessions, which are not scoped.",
            "example": [
              "orders:read",
              "orders:write"
            ]
          },
          "approval": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "pending",
              "approved",
              "rejected",
              null
            ],
            "description": "Integrators only, null for designers. A `pending` integrator can explore the API; going live needs `approved`."
          }
        },
        "required": [
          "object",
          "user_id",
          "kind",
          "authenticated_with",
          "scopes",
          "effective_scopes",
          "approval"
        ],
        "description": "Who the current credentials belong to."
      },
      "Error": {
        "type": "object",
        "properties": {
          "status_code": {
            "type": "integer",
            "description": "HTTP status code, repeated in the body.",
            "example": 404
          },
          "error": {
            "type": "string",
            "description": "Short label matching the HTTP status.",
            "example": "Not Found"
          },
          "code": {
            "type": "string",
            "description": "Stable machine-readable error code in snake_case. Branch on this, never on `message`.",
            "example": "product_not_found"
          },
          "message": {
            "type": "string",
            "description": "What went wrong and how to fix it, written to be shown to a person.",
            "example": "Product prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu was not found."
          },
          "details": {
            "type": "object",
            "additionalProperties": {},
            "description": "Extra structured context when it helps, for example the list of invalid fields. When a refusal is fixed in the 3DAPI dashboard (billing, store, compliance or plan), `fix_url` links to the page that fixes it."
          }
        },
        "required": [
          "status_code",
          "error",
          "code",
          "message"
        ],
        "description": "Every error response has this shape."
      },
      "IntegratorAccount": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^itg_[0-9A-Za-z]+$",
            "description": "Unique identifier of the integrator account, prefixed with `itg_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "itg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "integrator_account"
            ],
            "description": "Names the type of this object. Always `integrator_account`."
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "The email address to sign in with."
          },
          "company_name": {
            "type": "string",
            "description": "Name of the company."
          },
          "email_confirmed": {
            "type": "boolean",
            "description": "Whether the email address is confirmed. A new account starts `false`: open the link in the confirmation email before signing in. Signing in is refused until then.",
            "example": false
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Point in time, ISO 8601 in UTC.",
            "example": "2026-04-21T07:34:06Z"
          }
        },
        "required": [
          "id",
          "object",
          "email",
          "company_name",
          "email_confirmed",
          "created_at"
        ],
        "description": "A new integrator account. It starts in onboarding: upload files for quotes and request approval (see `get_integrator_onboarding`)."
      },
      "CreateIntegratorAccountRequest": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string",
            "maxLength": 254,
            "format": "email",
            "description": "Email address to sign in with. 3DAPI sends account and approval emails here.",
            "example": "dev@acme.example"
          },
          "password": {
            "type": "string",
            "minLength": 8,
            "maxLength": 72,
            "description": "Password to sign in with, 8 to 72 characters.",
            "example": "correct-horse-battery"
          },
          "company_name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "description": "Name of the company building the integration.",
            "example": "Acme Prints"
          }
        },
        "required": [
          "email",
          "password",
          "company_name"
        ]
      },
      "ResendIntegratorConfirmationRequest": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string",
            "maxLength": 254,
            "format": "email",
            "description": "The email address the account was created with.",
            "example": "dev@acme.example"
          }
        },
        "required": [
          "email"
        ]
      },
      "IntegratorAccountSettings": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^itg_[0-9A-Za-z]+$",
            "description": "Unique identifier of the integrator account, prefixed with `itg_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "itg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "integrator_account_settings"
            ],
            "description": "Names the type of this object. Always `integrator_account_settings`."
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "The email address you sign in with. Account emails go here.",
            "example": "dev@acme.example"
          },
          "pending_email": {
            "type": [
              "string",
              "null"
            ],
            "format": "email",
            "description": "A new sign-in address waiting for its confirmation link (see `request_integrator_email_change`), or null. You keep signing in with `email` until it is confirmed.",
            "example": null
          },
          "company_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Your company as 3DAPI shows it, for example to buyers of your checkout orders. Your legal name for invoices is in your billing details.",
            "example": "Acme Prints"
          },
          "contact_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "The person 3DAPI contacts about the account.",
            "example": "Ada Lovelace"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the account was created.",
            "example": "2026-04-21T07:34:06Z"
          }
        },
        "required": [
          "id",
          "object",
          "email",
          "pending_email",
          "company_name",
          "contact_name",
          "created_at"
        ],
        "description": "Your integrator account settings. Billing details (legal name, address, VAT number) are separate: `get_billing_details`."
      },
      "UpdateIntegratorAccountSettingsRequest": {
        "type": "object",
        "properties": {
          "company_name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "description": "Your company name, shown in the dashboard and to 3DAPI staff. Omit to keep it.",
            "example": "Acme Prints"
          },
          "contact_name": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 200,
            "description": "Null or an empty string clears it.",
            "example": "Ada Lovelace"
          }
        }
      },
      "ChangeIntegratorPasswordRequest": {
        "type": "object",
        "properties": {
          "current_password": {
            "type": "string",
            "minLength": 1,
            "maxLength": 72,
            "description": "The password you sign in with now."
          },
          "new_password": {
            "type": "string",
            "minLength": 8,
            "maxLength": 72,
            "description": "The new password, 8 to 72 characters."
          }
        },
        "required": [
          "current_password",
          "new_password"
        ]
      },
      "IntegratorEmailChange": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "email_change"
            ],
            "description": "Names the type of this object. Always `email_change`."
          },
          "new_email": {
            "type": "string",
            "format": "email",
            "description": "The address the account will use once the change is confirmed.",
            "example": "ops@acme.example"
          },
          "confirm_current_email": {
            "type": "boolean",
            "description": "Whether your current address also got a link to open. When `true`, the change happens once both links are opened; when `false`, once the new address opens its link."
          }
        },
        "required": [
          "object",
          "new_email",
          "confirm_current_email"
        ],
        "description": "An email change waiting for confirmation."
      },
      "RequestIntegratorEmailChangeRequest": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string",
            "maxLength": 254,
            "format": "email",
            "description": "The address to sign in with from now on.",
            "example": "ops@acme.example"
          }
        },
        "required": [
          "email"
        ]
      },
      "EuResponsiblePersonStatus": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "eu_responsible_person_status"
            ],
            "description": "Names the type of this object. Always `eu_responsible_person_status`."
          },
          "required": {
            "type": "boolean",
            "description": "Whether checkout orders shipping to the EU or UK need an active EU responsible person. `true` when your billing country is outside the EU and UK, or not saved yet; `false` for EU and UK businesses, which answer for their products themselves."
          },
          "billing_country": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^[A-Z]{2}$",
            "description": "The country of your billing details, which decides `required`. Null until you save billing details.",
            "example": "DE"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "missing"
            ],
            "description": "- `active`: an EU responsible person is on file.\n- `missing`: none is on file. When `required` is `true`, checkout orders to EU or UK addresses are refused (`compliance_blocked`, requirement `eu_responsible_person_missing`)."
          },
          "active": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "id": {
                "type": "string",
                "pattern": "^erp_[0-9A-Za-z]+$",
                "description": "Unique identifier of the eu responsible person, prefixed with `erp_`. Treat it as an opaque string: store it and compare it, never parse it.",
                "example": "erp_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
              },
              "object": {
                "type": "string",
                "enum": [
                  "eu_responsible_person"
                ],
                "description": "Names the type of this object. Always `eu_responsible_person`."
              },
              "company_name": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The responsible person, a company established in the EU or UK.",
                "example": "EU Rep GmbH"
              },
              "email": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Where authorities and buyers can reach it.",
                "example": "contact@eurep.example"
              },
              "address": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Its postal address.",
                "example": "Hauptstr. 1, 10115 Berlin"
              },
              "country": {
                "type": [
                  "string",
                  "null"
                ],
                "pattern": "^[A-Z]{2}$",
                "description": "ISO 3166-1 alpha-2 country code: an EU member state or `GB` (United Kingdom).",
                "example": "DE"
              },
              "active": {
                "type": "boolean",
                "description": "Whether it is the current responsible person. At most one is active; the others are history."
              },
              "created_at": {
                "type": "string",
                "format": "date-time",
                "description": "Point in time, ISO 8601 in UTC.",
                "example": "2026-04-21T07:34:06Z"
              },
              "disabled_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time",
                "description": "When it stopped being the responsible person (replaced or removed). Null while active.",
                "example": "2026-04-21T07:34:06Z"
              }
            },
            "required": [
              "id",
              "object",
              "company_name",
              "email",
              "address",
              "country",
              "active",
              "created_at",
              "disabled_at"
            ],
            "description": "The current responsible person, or null."
          },
          "previous": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EuResponsiblePerson"
            },
            "description": "Earlier responsible persons, newest first."
          }
        },
        "required": [
          "object",
          "required",
          "billing_country",
          "status",
          "active",
          "previous"
        ],
        "description": "Your EU responsible person under the EU General Product Safety Regulation (GPSR), and whether you need one."
      },
      "EuResponsiblePerson": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^erp_[0-9A-Za-z]+$",
            "description": "Unique identifier of the eu responsible person, prefixed with `erp_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "erp_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "eu_responsible_person"
            ],
            "description": "Names the type of this object. Always `eu_responsible_person`."
          },
          "company_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "The responsible person, a company established in the EU or UK.",
            "example": "EU Rep GmbH"
          },
          "email": {
            "type": [
              "string",
              "null"
            ],
            "description": "Where authorities and buyers can reach it.",
            "example": "contact@eurep.example"
          },
          "address": {
            "type": [
              "string",
              "null"
            ],
            "description": "Its postal address.",
            "example": "Hauptstr. 1, 10115 Berlin"
          },
          "country": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^[A-Z]{2}$",
            "description": "ISO 3166-1 alpha-2 country code: an EU member state or `GB` (United Kingdom).",
            "example": "DE"
          },
          "active": {
            "type": "boolean",
            "description": "Whether it is the current responsible person. At most one is active; the others are history."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Point in time, ISO 8601 in UTC.",
            "example": "2026-04-21T07:34:06Z"
          },
          "disabled_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When it stopped being the responsible person (replaced or removed). Null while active.",
            "example": "2026-04-21T07:34:06Z"
          }
        },
        "required": [
          "id",
          "object",
          "company_name",
          "email",
          "address",
          "country",
          "active",
          "created_at",
          "disabled_at"
        ],
        "description": "A GPSR EU responsible person record. Records are never edited: replacing one disables it and adds a new one."
      },
      "SetEuResponsiblePersonRequest": {
        "type": "object",
        "properties": {
          "company_name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "description": "Legal name of the EU responsible person, a company established in the EU.",
            "example": "EU Rep GmbH"
          },
          "email": {
            "type": "string",
            "maxLength": 254,
            "format": "email",
            "description": "Email where authorities can reach the EU responsible person.",
            "example": "contact@eurep.example"
          },
          "address": {
            "type": "string",
            "minLength": 1,
            "maxLength": 500,
            "description": "Street, number, postal code and city.",
            "example": "Hauptstr. 1, 10115 Berlin"
          },
          "country": {
            "type": "string",
            "minLength": 2,
            "maxLength": 2,
            "description": "ISO 3166-1 alpha-2 code of an EU member state, or `GB`. Any other country is refused.",
            "example": "DE"
          }
        },
        "required": [
          "company_name",
          "email",
          "address",
          "country"
        ]
      },
      "EuResponsiblePersonDeleted": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^erp_[0-9A-Za-z]+$",
            "description": "Unique identifier of the eu responsible person, prefixed with `erp_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "erp_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "eu_responsible_person"
            ],
            "description": "Names the type of this object. Always `eu_responsible_person`."
          },
          "deleted": {
            "type": "boolean",
            "enum": [
              true
            ],
            "description": "Always true: the EU responsible person is removed."
          }
        },
        "required": [
          "id",
          "object",
          "deleted"
        ]
      },
      "CreatedApiKey": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^key_[0-9A-Za-z]+$",
            "description": "Unique identifier of the api key, prefixed with `key_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "key_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "api_key"
            ],
            "description": "Names the type of this object. Always `api_key`."
          },
          "name": {
            "type": "string",
            "description": "Your label for the key, for example where it is used.",
            "example": "Production server"
          },
          "preview": {
            "type": "string",
            "description": "The first characters of the key, to recognise it. Never enough to authenticate with.",
            "example": "sk_3f9a1c2b7..."
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "What the key may do. Integrator keys carry `quotes:read`, `quotes:write`, `orders:read`, `orders:write`, `orders:read_customer`, `extras:read`, `extras:write`, `wallet:read`, `webhooks:read`.",
            "example": [
              "quotes:read",
              "quotes:write"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Point in time, ISO 8601 in UTC.",
            "example": "2026-04-21T07:34:06Z"
          },
          "last_used_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the key last authenticated a request, to the minute. Null if it was never used.",
            "example": "2026-04-21T07:34:06Z"
          },
          "secret": {
            "type": "string",
            "description": "The full key. Shown only in this response: store it securely now, it cannot be retrieved later. If it is lost, revoke the key and create a new one.",
            "example": "sk_3f9a1c2b7d...e41"
          }
        },
        "required": [
          "id",
          "object",
          "name",
          "preview",
          "scopes",
          "created_at",
          "last_used_at",
          "secret"
        ],
        "description": "A new API key, with its secret shown this one time."
      },
      "CreateApiKeyRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 50,
            "description": "A label for the key, 1 to 50 characters.",
            "example": "Production server"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string",
              "minLength": 1
            },
            "minItems": 1,
            "description": "What the key may do. Allowed for integrators: `quotes:read`, `quotes:write`, `orders:read`, `orders:write`, `orders:read_customer`, `extras:read`, `extras:write`, `wallet:read`, `webhooks:read`.",
            "example": [
              "quotes:read",
              "quotes:write"
            ]
          }
        },
        "required": [
          "name",
          "scopes"
        ]
      },
      "ApiKeyList": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ],
            "description": "Names the type of this object. Always `list`."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiKey"
            },
            "description": "The page of results."
          },
          "has_more": {
            "type": "boolean",
            "description": "True when more results exist after this page. Fetch them by passing `next_cursor` as `cursor`."
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque cursor for the next page, or null on the last page. Pass it back unchanged as `cursor`.",
            "example": "eyJjIjoiMjAyNi0wNC0yMVQwNzozNDowNloiLCJpIjoiN0txMiJ9"
          }
        },
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ]
      },
      "ApiKey": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^key_[0-9A-Za-z]+$",
            "description": "Unique identifier of the api key, prefixed with `key_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "key_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "api_key"
            ],
            "description": "Names the type of this object. Always `api_key`."
          },
          "name": {
            "type": "string",
            "description": "Your label for the key, for example where it is used.",
            "example": "Production server"
          },
          "preview": {
            "type": "string",
            "description": "The first characters of the key, to recognise it. Never enough to authenticate with.",
            "example": "sk_3f9a1c2b7..."
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "What the key may do. Integrator keys carry `quotes:read`, `quotes:write`, `orders:read`, `orders:write`, `orders:read_customer`, `extras:read`, `extras:write`, `wallet:read`, `webhooks:read`.",
            "example": [
              "quotes:read",
              "quotes:write"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Point in time, ISO 8601 in UTC.",
            "example": "2026-04-21T07:34:06Z"
          },
          "last_used_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the key last authenticated a request, to the minute. Null if it was never used.",
            "example": "2026-04-21T07:34:06Z"
          }
        },
        "required": [
          "id",
          "object",
          "name",
          "preview",
          "scopes",
          "created_at",
          "last_used_at"
        ],
        "description": "A secret key for calling the API from your servers, sent in the `x-api-key` header. Its secret is shown once, when it is created."
      },
      "ApiKeyDeleted": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^key_[0-9A-Za-z]+$",
            "description": "Unique identifier of the api key, prefixed with `key_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "key_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "api_key"
            ],
            "description": "Names the type of this object. Always `api_key`."
          },
          "deleted": {
            "type": "boolean",
            "enum": [
              true
            ],
            "description": "Always true: the key is revoked and every request made with it is refused."
          }
        },
        "required": [
          "id",
          "object",
          "deleted"
        ]
      },
      "CreateQuoteRequest": {
        "type": "object",
        "properties": {
          "file": {
            "type": "object",
            "properties": {
              "upload": {
                "type": "string",
                "pattern": "^upl_[0-9A-Za-z]+$",
                "description": "An upload id from `POST /v2/uploads`, used before it expires. The file is checked when used: one that is not what its name and type say is refused with 422 `invalid_file` (the expected type in `details.expected`) and must be uploaded again.",
                "example": "upl_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
              }
            },
            "required": [
              "upload"
            ],
            "description": "The model to quote, as `{ \"upload\": \"upl_...\" }` from `POST /v2/uploads` with purpose `quote_file`."
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "description": "Display name for the quote. Defaults to the uploaded file name.",
            "example": "Phone stand v2"
          }
        },
        "required": [
          "file"
        ]
      },
      "QuoteList": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ],
            "description": "Names the type of this object. Always `list`."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Quote"
            },
            "description": "The page of results."
          },
          "has_more": {
            "type": "boolean",
            "description": "True when more results exist after this page. Fetch them by passing `next_cursor` as `cursor`."
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque cursor for the next page, or null on the last page. Pass it back unchanged as `cursor`.",
            "example": "eyJjIjoiMjAyNi0wNC0yMVQwNzozNDowNloiLCJpIjoiN0txMiJ9"
          }
        },
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ]
      },
      "IntegratorOnboarding": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "integrator_onboarding"
            ],
            "description": "Names the type of this object. Always `integrator_onboarding`."
          },
          "status": {
            "$ref": "#/components/schemas/OnboardingStatus"
          },
          "approved_files": {
            "type": "integer",
            "minimum": 0,
            "description": "How many of your files 3DAPI has approved so far.",
            "example": 7
          },
          "required_files": {
            "type": "integer",
            "description": "How many approved files your account needs to be approved. Always 10 today.",
            "example": 10
          },
          "uploads": {
            "type": "object",
            "properties": {
              "used": {
                "type": "integer",
                "minimum": 0,
                "description": "Quotes created in the current round that count toward the allowance: processing and ready ones. A quote that ends `failed` does not count.",
                "example": 4
              },
              "limit": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "Quotes allowed in the current round (25). Null once your account is approved: quotes are then unlimited.",
                "example": 25
              }
            },
            "required": [
              "used",
              "limit"
            ],
            "description": "Your upload allowance. Before approval each round allows 25 quotes; a review that rejects files starts a new round."
          },
          "files_to_submit": {
            "type": "integer",
            "minimum": 0,
            "description": "How many ready quotes your next approval request must contain: the required number at first, then the number of files the last review rejected. 0 while a request is in review and once approved.",
            "example": 3
          },
          "current_request": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "id": {
                "type": "string",
                "pattern": "^apr_[0-9A-Za-z]+$",
                "description": "Unique identifier of the approval request, prefixed with `apr_`. Treat it as an opaque string: store it and compare it, never parse it.",
                "example": "apr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
              },
              "object": {
                "type": "string",
                "enum": [
                  "approval_request"
                ],
                "description": "Names the type of this object. Always `approval_request`."
              },
              "status": {
                "$ref": "#/components/schemas/ApprovalRequestStatus"
              },
              "quotes": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "quote": {
                      "$ref": "#/components/schemas/QuoteReference"
                    },
                    "decision": {
                      "$ref": "#/components/schemas/QuoteDecision"
                    },
                    "reason": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Why the file was rejected, written by the reviewer. Null unless `decision` is `rejected`.",
                      "example": null
                    }
                  },
                  "required": [
                    "quote",
                    "decision",
                    "reason"
                  ]
                },
                "description": "The quotes submitted in this request, with the decision on each file."
              },
              "submitted_at": {
                "type": "string",
                "format": "date-time",
                "description": "Point in time, ISO 8601 in UTC.",
                "example": "2026-04-21T07:34:06Z"
              },
              "reviewed_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time",
                "description": "When the review was completed. Null while `in_review`.",
                "example": "2026-04-21T07:34:06Z"
              }
            },
            "required": [
              "id",
              "object",
              "status",
              "quotes",
              "submitted_at",
              "reviewed_at"
            ],
            "description": "Your latest approval request, in review or reviewed. Null before your first request."
          },
          "order_blockers": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "billing_details_missing"
              ]
            },
            "description": "Account setup still missing before you can place orders, apart from approval. `billing_details_missing`: add your business billing details in the dashboard (Finance > Wallet); until then orders are refused with `compliance_blocked`. Empty when nothing is missing. Selling to EU or UK customers from outside the EU and UK also needs an EU responsible person, checked per order.",
            "example": []
          }
        },
        "required": [
          "object",
          "status",
          "approved_files",
          "required_files",
          "uploads",
          "files_to_submit",
          "current_request",
          "order_blockers"
        ],
        "description": "Where your integrator account stands in onboarding: what you have done and what to do next to get approved."
      },
      "OnboardingStatus": {
        "type": "string",
        "enum": [
          "collecting",
          "in_review",
          "changes_requested",
          "approved"
        ],
        "description": "- `collecting`: upload files to get quotes, then send an approval request with `files_to_submit` ready quotes.\n- `in_review`: an approval request is being reviewed. Uploads are paused until it is reviewed.\n- `changes_requested`: the last review rejected some files. Replace them with new quotes and send another request with `files_to_submit` quotes; you have a fresh upload allowance.\n- `approved`: your account is approved. Quotes are unlimited and you can place orders."
      },
      "CreateApprovalRequestRequest": {
        "type": "object",
        "properties": {
          "quotes": {
            "type": "array",
            "items": {
              "type": "string",
              "pattern": "^qte_[0-9A-Za-z]+$",
              "description": "Unique identifier of the quote, prefixed with `qte_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "qte_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "minItems": 1,
            "maxItems": 10,
            "description": "The quotes to submit, exactly `files_to_submit` of them, each `ready` and `not_submitted`. Omit it to submit your latest eligible quotes automatically."
          }
        }
      },
      "ApprovalRequestList": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ],
            "description": "Names the type of this object. Always `list`."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApprovalRequest"
            },
            "description": "The page of results."
          },
          "has_more": {
            "type": "boolean",
            "description": "True when more results exist after this page. Fetch them by passing `next_cursor` as `cursor`."
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque cursor for the next page, or null on the last page. Pass it back unchanged as `cursor`.",
            "example": "eyJjIjoiMjAyNi0wNC0yMVQwNzozNDowNloiLCJpIjoiN0txMiJ9"
          }
        },
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ]
      },
      "IntegratorAnalytics": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "integrator_analytics"
            ],
            "description": "Names the type of this object. Always `integrator_analytics`."
          },
          "period": {
            "type": "object",
            "properties": {
              "from": {
                "type": "string",
                "format": "date-time",
                "description": "Start of the period, included.",
                "example": "2026-04-21T07:34:06Z"
              },
              "to": {
                "type": "string",
                "format": "date-time",
                "description": "End of the period, excluded.",
                "example": "2026-04-21T07:34:06Z"
              },
              "granularity": {
                "$ref": "#/components/schemas/AnalyticsGranularity"
              },
              "time_zone": {
                "type": "string",
                "enum": [
                  "UTC"
                ],
                "description": "Buckets and dates are in UTC."
              }
            },
            "required": [
              "from",
              "to",
              "granularity",
              "time_zone"
            ],
            "description": "The window this answer covers."
          },
          "previous_period": {
            "type": "object",
            "properties": {
              "from": {
                "type": "string",
                "format": "date-time",
                "description": "Point in time, ISO 8601 in UTC.",
                "example": "2026-04-21T07:34:06Z"
              },
              "to": {
                "type": "string",
                "format": "date-time",
                "description": "Point in time, ISO 8601 in UTC.",
                "example": "2026-04-21T07:34:06Z"
              }
            },
            "required": [
              "from",
              "to"
            ],
            "description": "The window of the same length just before `period`, which `quotes.previous` covers. Compare the two for change."
          },
          "filters": {
            "type": "object",
            "properties": {
              "status": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/QuoteStatus"
                },
                "description": "Quote states the figures are limited to; empty means all."
              },
              "api_key": {
                "type": [
                  "string",
                  "null"
                ],
                "pattern": "^key_[0-9A-Za-z]+$",
                "description": "Only quotes created with this key; null means every quote.",
                "example": "key_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
              }
            },
            "required": [
              "status",
              "api_key"
            ],
            "description": "The filters applied, as understood."
          },
          "quotes": {
            "type": "object",
            "properties": {
              "totals": {
                "type": "object",
                "properties": {
                  "created": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "Quotes created in the period.",
                    "example": 42
                  },
                  "processing": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "Of those, still being sliced.",
                    "example": 42
                  },
                  "ready": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "Of those, priced.",
                    "example": 42
                  },
                  "failed": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "Of those, failed.",
                    "example": 42
                  },
                  "success_rate": {
                    "type": [
                      "number",
                      "null"
                    ],
                    "minimum": 0,
                    "maximum": 1,
                    "description": "Share of finished quotes that came out priced: `ready / (ready + failed)`, from 0 to 1. Null when no quote finished.",
                    "example": 0.94
                  },
                  "quoted_value": {
                    "type": "object",
                    "properties": {
                      "amount": {
                        "type": "integer",
                        "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                        "example": 5000
                      },
                      "currency": {
                        "type": "string",
                        "pattern": "^[a-z]{3}$",
                        "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                        "example": "usd"
                      }
                    },
                    "required": [
                      "amount",
                      "currency"
                    ],
                    "description": "Sum of the unit prices of the priced quotes: what one copy of each would cost."
                  },
                  "average_value": {
                    "type": [
                      "object",
                      "null"
                    ],
                    "properties": {
                      "amount": {
                        "type": "integer",
                        "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                        "example": 5000
                      },
                      "currency": {
                        "type": "string",
                        "pattern": "^[a-z]{3}$",
                        "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                        "example": "usd"
                      }
                    },
                    "required": [
                      "amount",
                      "currency"
                    ],
                    "description": "Mean unit price of the priced quotes. Null without any."
                  },
                  "median_slicing_seconds": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": 0,
                    "description": "Median time from the start of slicing to a price, in seconds. Null without any priced quote.",
                    "example": 74
                  }
                },
                "required": [
                  "created",
                  "processing",
                  "ready",
                  "failed",
                  "success_rate",
                  "quoted_value",
                  "average_value",
                  "median_slicing_seconds"
                ],
                "description": "For `period`."
              },
              "previous": {
                "type": "object",
                "properties": {
                  "created": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "Quotes created in the period.",
                    "example": 42
                  },
                  "processing": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "Of those, still being sliced.",
                    "example": 42
                  },
                  "ready": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "Of those, priced.",
                    "example": 42
                  },
                  "failed": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "Of those, failed.",
                    "example": 42
                  },
                  "success_rate": {
                    "type": [
                      "number",
                      "null"
                    ],
                    "minimum": 0,
                    "maximum": 1,
                    "description": "Share of finished quotes that came out priced: `ready / (ready + failed)`, from 0 to 1. Null when no quote finished.",
                    "example": 0.94
                  },
                  "quoted_value": {
                    "type": "object",
                    "properties": {
                      "amount": {
                        "type": "integer",
                        "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                        "example": 5000
                      },
                      "currency": {
                        "type": "string",
                        "pattern": "^[a-z]{3}$",
                        "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                        "example": "usd"
                      }
                    },
                    "required": [
                      "amount",
                      "currency"
                    ],
                    "description": "Sum of the unit prices of the priced quotes: what one copy of each would cost."
                  },
                  "average_value": {
                    "type": [
                      "object",
                      "null"
                    ],
                    "properties": {
                      "amount": {
                        "type": "integer",
                        "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                        "example": 5000
                      },
                      "currency": {
                        "type": "string",
                        "pattern": "^[a-z]{3}$",
                        "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                        "example": "usd"
                      }
                    },
                    "required": [
                      "amount",
                      "currency"
                    ],
                    "description": "Mean unit price of the priced quotes. Null without any."
                  },
                  "median_slicing_seconds": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": 0,
                    "description": "Median time from the start of slicing to a price, in seconds. Null without any priced quote.",
                    "example": 74
                  }
                },
                "required": [
                  "created",
                  "processing",
                  "ready",
                  "failed",
                  "success_rate",
                  "quoted_value",
                  "average_value",
                  "median_slicing_seconds"
                ],
                "description": "For `previous_period`, with the same filters."
              },
              "series": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "start": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Start of the bucket.",
                      "example": "2026-04-21T07:34:06Z"
                    },
                    "created": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Quotes created in the bucket.",
                      "example": 42
                    },
                    "ready": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Of those, priced.",
                      "example": 42
                    },
                    "failed": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Of those, failed.",
                      "example": 42
                    },
                    "quoted_value": {
                      "type": "object",
                      "properties": {
                        "amount": {
                          "type": "integer",
                          "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                          "example": 5000
                        },
                        "currency": {
                          "type": "string",
                          "pattern": "^[a-z]{3}$",
                          "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                          "example": "usd"
                        }
                      },
                      "required": [
                        "amount",
                        "currency"
                      ],
                      "description": "Sum of the unit prices priced in the bucket."
                    }
                  },
                  "required": [
                    "start",
                    "created",
                    "ready",
                    "failed",
                    "quoted_value"
                  ]
                },
                "description": "One entry per bucket of `period`, oldest first, including empty buckets. The first and last buckets can be partial."
              },
              "value_distribution": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "min": {
                      "type": "object",
                      "properties": {
                        "amount": {
                          "type": "integer",
                          "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                          "example": 5000
                        },
                        "currency": {
                          "type": "string",
                          "pattern": "^[a-z]{3}$",
                          "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                          "example": "usd"
                        }
                      },
                      "required": [
                        "amount",
                        "currency"
                      ],
                      "description": "Lower bound, included."
                    },
                    "max": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "properties": {
                        "amount": {
                          "type": "integer",
                          "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                          "example": 5000
                        },
                        "currency": {
                          "type": "string",
                          "pattern": "^[a-z]{3}$",
                          "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                          "example": "usd"
                        }
                      },
                      "required": [
                        "amount",
                        "currency"
                      ],
                      "description": "Upper bound, excluded. Null for the last range."
                    },
                    "count": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Priced quotes whose unit price falls in the range.",
                      "example": 42
                    }
                  },
                  "required": [
                    "min",
                    "max",
                    "count"
                  ]
                },
                "description": "How the unit prices of priced quotes spread over fixed ranges: under $5, $5 to $10, $10 to $25, $25 to $50, $50 to $100, $100 to $250, $250 and up."
              },
              "file_types": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "type": {
                      "type": "string",
                      "description": "File extension in lowercase without the dot, or `other` when the file name has none.",
                      "example": "stl"
                    },
                    "created": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Quotes of this file type.",
                      "example": 42
                    },
                    "ready": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Of those, priced.",
                      "example": 42
                    }
                  },
                  "required": [
                    "type",
                    "created",
                    "ready"
                  ]
                },
                "description": "Quotes by file type, most quoted first."
              },
              "top_files": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string",
                      "description": "File name as uploaded.",
                      "example": "phone-stand.stl"
                    },
                    "created": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Quotes of a file with this name.",
                      "example": 42
                    },
                    "ready": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Of those, priced.",
                      "example": 42
                    },
                    "latest_price": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "properties": {
                        "amount": {
                          "type": "integer",
                          "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                          "example": 5000
                        },
                        "currency": {
                          "type": "string",
                          "pattern": "^[a-z]{3}$",
                          "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                          "example": "usd"
                        }
                      },
                      "required": [
                        "amount",
                        "currency"
                      ],
                      "description": "Unit price of the most recent priced quote of this file. Null when none was priced."
                    },
                    "last_quoted_at": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Point in time, ISO 8601 in UTC.",
                      "example": "2026-04-21T07:34:06Z"
                    }
                  },
                  "required": [
                    "name",
                    "created",
                    "ready",
                    "latest_price",
                    "last_quoted_at"
                  ]
                },
                "description": "The 10 file names quoted most often in the period."
              },
              "sources": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "api_key": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "properties": {
                        "id": {
                          "type": "string",
                          "pattern": "^key_[0-9A-Za-z]+$",
                          "description": "Unique identifier of the api key, prefixed with `key_`. Treat it as an opaque string: store it and compare it, never parse it.",
                          "example": "key_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                        },
                        "object": {
                          "type": "string",
                          "enum": [
                            "api_key"
                          ],
                          "description": "Names the type of this object. Always `api_key`."
                        },
                        "name": {
                          "type": "string",
                          "description": "The key's label, or \"Deleted key\" when it no longer exists."
                        }
                      },
                      "required": [
                        "id",
                        "object",
                        "name"
                      ],
                      "description": "The key the quotes were created with; null for quotes made from the dashboard."
                    },
                    "created": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Quotes from this source.",
                      "example": 42
                    },
                    "ready": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Of those, priced.",
                      "example": 42
                    }
                  },
                  "required": [
                    "api_key",
                    "created",
                    "ready"
                  ]
                },
                "description": "Quotes by API key, most first."
              }
            },
            "required": [
              "totals",
              "previous",
              "series",
              "value_distribution",
              "file_types",
              "top_files",
              "sources"
            ],
            "description": "Your quotes in the period, with the filters."
          },
          "api_usage": {
            "type": "object",
            "properties": {
              "totals": {
                "type": "object",
                "properties": {
                  "calls": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "Calls made with your API keys.",
                    "example": 42
                  },
                  "client_errors": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "Of those, answered with a 4xx status.",
                    "example": 42
                  },
                  "server_errors": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "Of those, answered with a 5xx status.",
                    "example": 42
                  },
                  "error_rate": {
                    "type": [
                      "number",
                      "null"
                    ],
                    "minimum": 0,
                    "maximum": 1,
                    "description": "Share of calls answered with an error (4xx or 5xx), from 0 to 1. Null without calls.",
                    "example": 0.012
                  },
                  "p50_ms": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": 0,
                    "description": "Median time to answer, in milliseconds. Null without calls.",
                    "example": 120
                  },
                  "p95_ms": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": 0,
                    "description": "95th percentile time to answer, in milliseconds. Null without calls.",
                    "example": 120
                  }
                },
                "required": [
                  "calls",
                  "client_errors",
                  "server_errors",
                  "error_rate",
                  "p50_ms",
                  "p95_ms"
                ],
                "description": "For `period`."
              },
              "previous": {
                "type": "object",
                "properties": {
                  "calls": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "Calls made with your API keys.",
                    "example": 42
                  },
                  "client_errors": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "Of those, answered with a 4xx status.",
                    "example": 42
                  },
                  "server_errors": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "Of those, answered with a 5xx status.",
                    "example": 42
                  },
                  "error_rate": {
                    "type": [
                      "number",
                      "null"
                    ],
                    "minimum": 0,
                    "maximum": 1,
                    "description": "Share of calls answered with an error (4xx or 5xx), from 0 to 1. Null without calls.",
                    "example": 0.012
                  },
                  "p50_ms": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": 0,
                    "description": "Median time to answer, in milliseconds. Null without calls.",
                    "example": 120
                  },
                  "p95_ms": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": 0,
                    "description": "95th percentile time to answer, in milliseconds. Null without calls.",
                    "example": 120
                  }
                },
                "required": [
                  "calls",
                  "client_errors",
                  "server_errors",
                  "error_rate",
                  "p50_ms",
                  "p95_ms"
                ],
                "description": "For `previous_period`."
              },
              "series": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "start": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Point in time, ISO 8601 in UTC.",
                      "example": "2026-04-21T07:34:06Z"
                    },
                    "calls": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Calls in the bucket.",
                      "example": 42
                    },
                    "errors": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Of those, answered with a 4xx or 5xx status.",
                      "example": 42
                    },
                    "p95_ms": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "minimum": 0,
                      "description": "95th percentile time to answer in the bucket.",
                      "example": 120
                    }
                  },
                  "required": [
                    "start",
                    "calls",
                    "errors",
                    "p95_ms"
                  ]
                },
                "description": "One entry per bucket of `period`, oldest first."
              },
              "endpoints": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "method": {
                      "type": "string",
                      "description": "HTTP method, uppercase.",
                      "example": "POST"
                    },
                    "route": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "The operation's path template; null for calls to paths no operation answers.",
                      "example": "/v2/quotes"
                    },
                    "calls": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Calls to this operation.",
                      "example": 42
                    },
                    "errors": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Of those, answered with an error.",
                      "example": 42
                    },
                    "p95_ms": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "minimum": 0,
                      "description": "95th percentile time to answer.",
                      "example": 120
                    }
                  },
                  "required": [
                    "method",
                    "route",
                    "calls",
                    "errors",
                    "p95_ms"
                  ]
                },
                "description": "The 10 operations you called most."
              },
              "sources": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "api_key": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string",
                          "pattern": "^key_[0-9A-Za-z]+$",
                          "description": "Unique identifier of the api key, prefixed with `key_`. Treat it as an opaque string: store it and compare it, never parse it.",
                          "example": "key_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                        },
                        "object": {
                          "type": "string",
                          "enum": [
                            "api_key"
                          ],
                          "description": "Names the type of this object. Always `api_key`."
                        },
                        "name": {
                          "type": "string",
                          "description": "The key's label, or \"Deleted key\" when it no longer exists."
                        }
                      },
                      "required": [
                        "id",
                        "object",
                        "name"
                      ],
                      "description": "The API key the calls were made with."
                    },
                    "calls": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Calls made with this key.",
                      "example": 42
                    },
                    "errors": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Of those, answered with an error.",
                      "example": 42
                    }
                  },
                  "required": [
                    "api_key",
                    "calls",
                    "errors"
                  ]
                },
                "description": "Calls by API key, most first."
              }
            },
            "required": [
              "totals",
              "previous",
              "series",
              "endpoints",
              "sources"
            ],
            "description": "Calls made with your API keys in the period, from the request log (kept 30 days). Dashboard traffic is not counted. The `api_key` filter applies; `status` does not."
          },
          "reviews": {
            "type": "object",
            "properties": {
              "requests_sent": {
                "type": "integer",
                "minimum": 0,
                "description": "Approval requests you sent.",
                "example": 42
              },
              "files_in_review": {
                "type": "integer",
                "minimum": 0,
                "description": "Files waiting for a decision.",
                "example": 42
              },
              "files_approved": {
                "type": "integer",
                "minimum": 0,
                "description": "Files approved.",
                "example": 42
              },
              "files_rejected": {
                "type": "integer",
                "minimum": 0,
                "description": "Files rejected.",
                "example": 42
              },
              "rejection_reasons": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "reason": {
                      "type": "string",
                      "description": "The reviewer's reason, as written.",
                      "example": "Walls under 0.8 mm will break in print."
                    },
                    "count": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Files rejected with this reason.",
                      "example": 42
                    }
                  },
                  "required": [
                    "reason",
                    "count"
                  ]
                },
                "description": "The 5 most common rejection reasons."
              }
            },
            "required": [
              "requests_sent",
              "files_in_review",
              "files_approved",
              "files_rejected",
              "rejection_reasons"
            ],
            "description": "Approval reviews of your files since the account began. Not limited by the period or the filters."
          },
          "orders": {
            "type": "object",
            "properties": {
              "totals": {
                "type": "object",
                "properties": {
                  "created": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "Orders placed in the period, in any status.",
                    "example": 42
                  },
                  "paid": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "Of those, paid: every `wallet` order, and `checkout` orders whose customer completed the checkout.",
                    "example": 42
                  },
                  "cancelled": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "Of those, now `cancelled`.",
                    "example": 42
                  },
                  "wallet": {
                    "type": "object",
                    "properties": {
                      "orders": {
                        "type": "integer",
                        "minimum": 0,
                        "description": "Paid `wallet` orders.",
                        "example": 42
                      },
                      "charged": {
                        "type": "object",
                        "properties": {
                          "amount": {
                            "type": "integer",
                            "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                            "example": 5000
                          },
                          "currency": {
                            "type": "string",
                            "pattern": "^[a-z]{3}$",
                            "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                            "example": "usd"
                          }
                        },
                        "required": [
                          "amount",
                          "currency"
                        ],
                        "description": "Sum of their `charges.total`: what you paid for production and shipping (VAT included when charged), as get_integrator_order shows it."
                      },
                      "refunded": {
                        "type": "object",
                        "properties": {
                          "amount": {
                            "type": "integer",
                            "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                            "example": 5000
                          },
                          "currency": {
                            "type": "string",
                            "pattern": "^[a-z]{3}$",
                            "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                            "example": "usd"
                          }
                        },
                        "required": [
                          "amount",
                          "currency"
                        ],
                        "description": "Sum of their completed `refunds`, back to your wallet (cancellations included)."
                      },
                      "spend": {
                        "type": "object",
                        "properties": {
                          "amount": {
                            "type": "integer",
                            "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                            "example": 5000
                          },
                          "currency": {
                            "type": "string",
                            "pattern": "^[a-z]{3}$",
                            "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                            "example": "usd"
                          }
                        },
                        "required": [
                          "amount",
                          "currency"
                        ],
                        "description": "`charged` less `refunded`: your net production spend."
                      },
                      "average_order_value": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "properties": {
                          "amount": {
                            "type": "integer",
                            "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                            "example": 5000
                          },
                          "currency": {
                            "type": "string",
                            "pattern": "^[a-z]{3}$",
                            "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                            "example": "usd"
                          }
                        },
                        "required": [
                          "amount",
                          "currency"
                        ],
                        "description": "`charged` divided by `orders`. Null without orders."
                      }
                    },
                    "required": [
                      "orders",
                      "charged",
                      "refunded",
                      "spend",
                      "average_order_value"
                    ],
                    "description": "Orders you paid from your wallet at the production price."
                  },
                  "checkout": {
                    "type": "object",
                    "properties": {
                      "orders": {
                        "type": "integer",
                        "minimum": 0,
                        "description": "Paid `checkout` orders.",
                        "example": 42
                      },
                      "charged": {
                        "type": "object",
                        "properties": {
                          "amount": {
                            "type": "integer",
                            "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                            "example": 5000
                          },
                          "currency": {
                            "type": "string",
                            "pattern": "^[a-z]{3}$",
                            "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                            "example": "usd"
                          }
                        },
                        "required": [
                          "amount",
                          "currency"
                        ],
                        "description": "Sum of their `charges.total`: what your customers paid, VAT and shipping included, as get_integrator_order shows it."
                      },
                      "refunded": {
                        "type": "object",
                        "properties": {
                          "amount": {
                            "type": "integer",
                            "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                            "example": 5000
                          },
                          "currency": {
                            "type": "string",
                            "pattern": "^[a-z]{3}$",
                            "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                            "example": "usd"
                          }
                        },
                        "required": [
                          "amount",
                          "currency"
                        ],
                        "description": "Sum of their completed `refunds`, back to your customers' cards."
                      },
                      "sales": {
                        "type": "object",
                        "properties": {
                          "amount": {
                            "type": "integer",
                            "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                            "example": 5000
                          },
                          "currency": {
                            "type": "string",
                            "pattern": "^[a-z]{3}$",
                            "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                            "example": "usd"
                          }
                        },
                        "required": [
                          "amount",
                          "currency"
                        ],
                        "description": "`charged` less `refunded`: your net checkout sales."
                      },
                      "earnings": {
                        "type": "object",
                        "properties": {
                          "amount": {
                            "type": "integer",
                            "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                            "example": 5000
                          },
                          "currency": {
                            "type": "string",
                            "pattern": "^[a-z]{3}$",
                            "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                            "example": "usd"
                          }
                        },
                        "required": [
                          "amount",
                          "currency"
                        ],
                        "description": "Sum of their `earnings.net`: what you earn after the production price, the card fee and refunds. Credited to your wallet when each order completes."
                      },
                      "average_order_value": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "properties": {
                          "amount": {
                            "type": "integer",
                            "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                            "example": 5000
                          },
                          "currency": {
                            "type": "string",
                            "pattern": "^[a-z]{3}$",
                            "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                            "example": "usd"
                          }
                        },
                        "required": [
                          "amount",
                          "currency"
                        ],
                        "description": "`charged` divided by `orders`. Null without orders."
                      }
                    },
                    "required": [
                      "orders",
                      "charged",
                      "refunded",
                      "sales",
                      "earnings",
                      "average_order_value"
                    ],
                    "description": "Orders your customers paid through a 3DAPI checkout link at your sales price. An unpaid link (open or expired) moves no money and is not counted here."
                  }
                },
                "required": [
                  "created",
                  "paid",
                  "cancelled",
                  "wallet",
                  "checkout"
                ],
                "description": "For `period`."
              },
              "previous": {
                "type": "object",
                "properties": {
                  "created": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "Orders placed in the period, in any status.",
                    "example": 42
                  },
                  "paid": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "Of those, paid: every `wallet` order, and `checkout` orders whose customer completed the checkout.",
                    "example": 42
                  },
                  "cancelled": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "Of those, now `cancelled`.",
                    "example": 42
                  },
                  "wallet": {
                    "type": "object",
                    "properties": {
                      "orders": {
                        "type": "integer",
                        "minimum": 0,
                        "description": "Paid `wallet` orders.",
                        "example": 42
                      },
                      "charged": {
                        "type": "object",
                        "properties": {
                          "amount": {
                            "type": "integer",
                            "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                            "example": 5000
                          },
                          "currency": {
                            "type": "string",
                            "pattern": "^[a-z]{3}$",
                            "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                            "example": "usd"
                          }
                        },
                        "required": [
                          "amount",
                          "currency"
                        ],
                        "description": "Sum of their `charges.total`: what you paid for production and shipping (VAT included when charged), as get_integrator_order shows it."
                      },
                      "refunded": {
                        "type": "object",
                        "properties": {
                          "amount": {
                            "type": "integer",
                            "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                            "example": 5000
                          },
                          "currency": {
                            "type": "string",
                            "pattern": "^[a-z]{3}$",
                            "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                            "example": "usd"
                          }
                        },
                        "required": [
                          "amount",
                          "currency"
                        ],
                        "description": "Sum of their completed `refunds`, back to your wallet (cancellations included)."
                      },
                      "spend": {
                        "type": "object",
                        "properties": {
                          "amount": {
                            "type": "integer",
                            "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                            "example": 5000
                          },
                          "currency": {
                            "type": "string",
                            "pattern": "^[a-z]{3}$",
                            "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                            "example": "usd"
                          }
                        },
                        "required": [
                          "amount",
                          "currency"
                        ],
                        "description": "`charged` less `refunded`: your net production spend."
                      },
                      "average_order_value": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "properties": {
                          "amount": {
                            "type": "integer",
                            "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                            "example": 5000
                          },
                          "currency": {
                            "type": "string",
                            "pattern": "^[a-z]{3}$",
                            "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                            "example": "usd"
                          }
                        },
                        "required": [
                          "amount",
                          "currency"
                        ],
                        "description": "`charged` divided by `orders`. Null without orders."
                      }
                    },
                    "required": [
                      "orders",
                      "charged",
                      "refunded",
                      "spend",
                      "average_order_value"
                    ],
                    "description": "Orders you paid from your wallet at the production price."
                  },
                  "checkout": {
                    "type": "object",
                    "properties": {
                      "orders": {
                        "type": "integer",
                        "minimum": 0,
                        "description": "Paid `checkout` orders.",
                        "example": 42
                      },
                      "charged": {
                        "type": "object",
                        "properties": {
                          "amount": {
                            "type": "integer",
                            "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                            "example": 5000
                          },
                          "currency": {
                            "type": "string",
                            "pattern": "^[a-z]{3}$",
                            "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                            "example": "usd"
                          }
                        },
                        "required": [
                          "amount",
                          "currency"
                        ],
                        "description": "Sum of their `charges.total`: what your customers paid, VAT and shipping included, as get_integrator_order shows it."
                      },
                      "refunded": {
                        "type": "object",
                        "properties": {
                          "amount": {
                            "type": "integer",
                            "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                            "example": 5000
                          },
                          "currency": {
                            "type": "string",
                            "pattern": "^[a-z]{3}$",
                            "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                            "example": "usd"
                          }
                        },
                        "required": [
                          "amount",
                          "currency"
                        ],
                        "description": "Sum of their completed `refunds`, back to your customers' cards."
                      },
                      "sales": {
                        "type": "object",
                        "properties": {
                          "amount": {
                            "type": "integer",
                            "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                            "example": 5000
                          },
                          "currency": {
                            "type": "string",
                            "pattern": "^[a-z]{3}$",
                            "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                            "example": "usd"
                          }
                        },
                        "required": [
                          "amount",
                          "currency"
                        ],
                        "description": "`charged` less `refunded`: your net checkout sales."
                      },
                      "earnings": {
                        "type": "object",
                        "properties": {
                          "amount": {
                            "type": "integer",
                            "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                            "example": 5000
                          },
                          "currency": {
                            "type": "string",
                            "pattern": "^[a-z]{3}$",
                            "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                            "example": "usd"
                          }
                        },
                        "required": [
                          "amount",
                          "currency"
                        ],
                        "description": "Sum of their `earnings.net`: what you earn after the production price, the card fee and refunds. Credited to your wallet when each order completes."
                      },
                      "average_order_value": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "properties": {
                          "amount": {
                            "type": "integer",
                            "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                            "example": 5000
                          },
                          "currency": {
                            "type": "string",
                            "pattern": "^[a-z]{3}$",
                            "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                            "example": "usd"
                          }
                        },
                        "required": [
                          "amount",
                          "currency"
                        ],
                        "description": "`charged` divided by `orders`. Null without orders."
                      }
                    },
                    "required": [
                      "orders",
                      "charged",
                      "refunded",
                      "sales",
                      "earnings",
                      "average_order_value"
                    ],
                    "description": "Orders your customers paid through a 3DAPI checkout link at your sales price. An unpaid link (open or expired) moves no money and is not counted here."
                  }
                },
                "required": [
                  "created",
                  "paid",
                  "cancelled",
                  "wallet",
                  "checkout"
                ],
                "description": "For `previous_period`."
              },
              "series": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "start": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Start of the bucket.",
                      "example": "2026-04-21T07:34:06Z"
                    },
                    "created": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Orders placed in the bucket, in any status.",
                      "example": 42
                    },
                    "wallet": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Of those, `wallet` orders.",
                      "example": 42
                    },
                    "checkout": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Of those, `checkout` orders.",
                      "example": 42
                    },
                    "wallet_spend": {
                      "type": "object",
                      "properties": {
                        "amount": {
                          "type": "integer",
                          "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                          "example": 5000
                        },
                        "currency": {
                          "type": "string",
                          "pattern": "^[a-z]{3}$",
                          "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                          "example": "usd"
                        }
                      },
                      "required": [
                        "amount",
                        "currency"
                      ],
                      "description": "Net spend (`charged` less `refunded`) of the paid `wallet` orders placed in the bucket."
                    },
                    "checkout_sales": {
                      "type": "object",
                      "properties": {
                        "amount": {
                          "type": "integer",
                          "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                          "example": 5000
                        },
                        "currency": {
                          "type": "string",
                          "pattern": "^[a-z]{3}$",
                          "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                          "example": "usd"
                        }
                      },
                      "required": [
                        "amount",
                        "currency"
                      ],
                      "description": "Net sales (`charged` less `refunded`) of the paid `checkout` orders placed in the bucket."
                    }
                  },
                  "required": [
                    "start",
                    "created",
                    "wallet",
                    "checkout",
                    "wallet_spend",
                    "checkout_sales"
                  ]
                },
                "description": "One entry per bucket of `period`, oldest first, including empty buckets."
              },
              "by_status": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "$ref": "#/components/schemas/OrderStatus"
                    },
                    "count": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Orders placed in the period now in this status.",
                      "example": 42
                    }
                  },
                  "required": [
                    "status",
                    "count"
                  ]
                },
                "description": "Orders placed in the period by their current status, in lifecycle order. Statuses without orders are left out."
              },
              "by_mode": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "mode": {
                      "type": "string",
                      "enum": [
                        "checkout",
                        "wallet"
                      ],
                      "description": "`wallet`: you pay the production price plus shipping from your wallet (a shortfall is charged to your saved card). `checkout`: your customer pays the sales price you set through a hosted 3DAPI checkout and you earn the difference."
                    },
                    "created": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Orders placed in the period in this mode.",
                      "example": 42
                    },
                    "paid": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Of those, paid.",
                      "example": 42
                    }
                  },
                  "required": [
                    "mode",
                    "created",
                    "paid"
                  ]
                },
                "description": "Orders placed in the period by mode."
              },
              "top_quotes": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "quote": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string",
                          "pattern": "^qte_[0-9A-Za-z]+$",
                          "description": "Unique identifier of the quote, prefixed with `qte_`. Treat it as an opaque string: store it and compare it, never parse it.",
                          "example": "qte_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                        },
                        "object": {
                          "type": "string",
                          "enum": [
                            "quote"
                          ],
                          "description": "Names the type of this object. Always `quote`."
                        },
                        "name": {
                          "type": "string",
                          "description": "The quote name.",
                          "example": "phone-stand.stl"
                        }
                      },
                      "required": [
                        "id",
                        "object",
                        "name"
                      ],
                      "description": "The quote ordered."
                    },
                    "orders": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Paid orders with a line from this quote.",
                      "example": 42
                    },
                    "units": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Units of this quote on those orders.",
                      "example": 42
                    }
                  },
                  "required": [
                    "quote",
                    "orders",
                    "units"
                  ]
                },
                "description": "The 10 quotes with the most units on paid orders placed in the period."
              }
            },
            "required": [
              "totals",
              "previous",
              "series",
              "by_status",
              "by_mode",
              "top_quotes"
            ],
            "description": "Your orders placed in the period, both modes. Amounts are the ones get_integrator_order shows for each order (`charges.total`, completed `refunds`, `earnings.net`), added up; a cancelled order keeps what it was charged and its refund. The `status` and `api_key` filters do not apply."
          }
        },
        "required": [
          "object",
          "period",
          "previous_period",
          "filters",
          "quotes",
          "api_usage",
          "reviews",
          "orders"
        ],
        "description": "Figures about your account for a period: quote volume, quoted value, slicing time, API usage, approval reviews and orders."
      },
      "AnalyticsGranularity": {
        "type": "string",
        "enum": [
          "day",
          "week",
          "month"
        ],
        "description": "Bucket size of the time series: `day` (UTC calendar day), `week` (ISO week starting Monday, UTC) or `month` (UTC calendar month)."
      },
      "ApiRequestList": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ],
            "description": "Names the type of this object. Always `list`."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiRequest"
            },
            "description": "The page of results."
          },
          "has_more": {
            "type": "boolean",
            "description": "True when more results exist after this page. Fetch them by passing `next_cursor` as `cursor`."
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque cursor for the next page, or null on the last page. Pass it back unchanged as `cursor`.",
            "example": "eyJjIjoiMjAyNi0wNC0yMVQwNzozNDowNloiLCJpIjoiN0txMiJ9"
          }
        },
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ]
      },
      "ApiRequest": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^req_[0-9A-Za-z]{24}$",
            "description": "Unique identifier of the request, prefixed with `req_`. Every response carries it in the `Request-Id` header, and error bodies in `details.request_id`.",
            "example": "req_7Kq2Xb9LmN4pR8sT1vW3yZ5a"
          },
          "object": {
            "type": "string",
            "enum": [
              "api_request"
            ],
            "description": "Names the type of this object. Always `api_request`."
          },
          "method": {
            "type": "string",
            "description": "HTTP method, uppercase.",
            "example": "POST"
          },
          "route": {
            "type": [
              "string",
              "null"
            ],
            "description": "The operation's path template, for grouping (`/v2/quotes/:id`). Null when no operation matched, for example an unknown path.",
            "example": "/v2/quotes"
          },
          "path": {
            "type": "string",
            "description": "The path as called, without the query string.",
            "example": "/v2/quotes"
          },
          "status_code": {
            "type": "integer",
            "description": "HTTP status code the API answered with.",
            "example": 201
          },
          "duration_ms": {
            "type": "integer",
            "minimum": 0,
            "description": "Time the API took to answer, in milliseconds.",
            "example": 184
          },
          "credential": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "api_key",
              "session",
              "oauth",
              null
            ],
            "description": "How the call was authenticated: an API key, a signed-in dashboard session, or an app the user connected (OAuth, such as Claude or ChatGPT). Null when it was refused before authentication."
          },
          "api_key": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "id": {
                "type": "string",
                "pattern": "^key_[0-9A-Za-z]+$",
                "description": "Unique identifier of the api key, prefixed with `key_`. Treat it as an opaque string: store it and compare it, never parse it.",
                "example": "key_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
              },
              "object": {
                "type": "string",
                "enum": [
                  "api_key"
                ],
                "description": "Names the type of this object. Always `api_key`."
              },
              "name": {
                "type": "string",
                "description": "The key's label, or \"Deleted key\" when it no longer exists."
              }
            },
            "required": [
              "id",
              "object",
              "name"
            ],
            "description": "The key used, when one was."
          },
          "ip": {
            "type": [
              "string",
              "null"
            ],
            "description": "IP address the request came from: the first address in `X-Forwarded-For`, or the connecting address when that header is absent. Null when neither could be read.",
            "example": "203.0.113.7"
          },
          "user_agent": {
            "type": [
              "string",
              "null"
            ],
            "description": "The `User-Agent` header sent. Null when there was none.",
            "example": "node-fetch/1.0"
          },
          "idempotency_key": {
            "type": [
              "string",
              "null"
            ],
            "description": "The `Idempotency-Key` header sent, if any."
          },
          "error_code": {
            "type": [
              "string",
              "null"
            ],
            "description": "The `code` of an error answer; null on success.",
            "example": null
          },
          "request_bytes": {
            "type": "integer",
            "minimum": 0,
            "description": "Size of the request body in bytes, as received (chunked bodies included). 0 when there was none.",
            "example": 96
          },
          "response_bytes": {
            "type": "integer",
            "minimum": 0,
            "description": "Size of the response body in bytes. 0 when there was none.",
            "example": 512
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the request arrived.",
            "example": "2026-04-21T07:34:06Z"
          }
        },
        "required": [
          "id",
          "object",
          "method",
          "route",
          "path",
          "status_code",
          "duration_ms",
          "credential",
          "api_key",
          "ip",
          "user_agent",
          "idempotency_key",
          "error_code",
          "request_bytes",
          "response_bytes",
          "created_at"
        ],
        "description": "A request you made to the API, as its log keeps it for 30 days. Bodies come with `get_api_request`."
      },
      "ApiRequestDetail": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^req_[0-9A-Za-z]{24}$",
            "description": "Unique identifier of the request, prefixed with `req_`. Every response carries it in the `Request-Id` header, and error bodies in `details.request_id`.",
            "example": "req_7Kq2Xb9LmN4pR8sT1vW3yZ5a"
          },
          "object": {
            "type": "string",
            "enum": [
              "api_request"
            ],
            "description": "Names the type of this object. Always `api_request`."
          },
          "method": {
            "type": "string",
            "description": "HTTP method, uppercase.",
            "example": "POST"
          },
          "route": {
            "type": [
              "string",
              "null"
            ],
            "description": "The operation's path template, for grouping (`/v2/quotes/:id`). Null when no operation matched, for example an unknown path.",
            "example": "/v2/quotes"
          },
          "path": {
            "type": "string",
            "description": "The path as called, without the query string.",
            "example": "/v2/quotes"
          },
          "status_code": {
            "type": "integer",
            "description": "HTTP status code the API answered with.",
            "example": 201
          },
          "duration_ms": {
            "type": "integer",
            "minimum": 0,
            "description": "Time the API took to answer, in milliseconds.",
            "example": 184
          },
          "credential": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "api_key",
              "session",
              "oauth",
              null
            ],
            "description": "How the call was authenticated: an API key, a signed-in dashboard session, or an app the user connected (OAuth, such as Claude or ChatGPT). Null when it was refused before authentication."
          },
          "api_key": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "id": {
                "type": "string",
                "pattern": "^key_[0-9A-Za-z]+$",
                "description": "Unique identifier of the api key, prefixed with `key_`. Treat it as an opaque string: store it and compare it, never parse it.",
                "example": "key_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
              },
              "object": {
                "type": "string",
                "enum": [
                  "api_key"
                ],
                "description": "Names the type of this object. Always `api_key`."
              },
              "name": {
                "type": "string",
                "description": "The key's label, or \"Deleted key\" when it no longer exists."
              }
            },
            "required": [
              "id",
              "object",
              "name"
            ],
            "description": "The key used, when one was."
          },
          "ip": {
            "type": [
              "string",
              "null"
            ],
            "description": "IP address the request came from: the first address in `X-Forwarded-For`, or the connecting address when that header is absent. Null when neither could be read.",
            "example": "203.0.113.7"
          },
          "user_agent": {
            "type": [
              "string",
              "null"
            ],
            "description": "The `User-Agent` header sent. Null when there was none.",
            "example": "node-fetch/1.0"
          },
          "idempotency_key": {
            "type": [
              "string",
              "null"
            ],
            "description": "The `Idempotency-Key` header sent, if any."
          },
          "error_code": {
            "type": [
              "string",
              "null"
            ],
            "description": "The `code` of an error answer; null on success.",
            "example": null
          },
          "request_bytes": {
            "type": "integer",
            "minimum": 0,
            "description": "Size of the request body in bytes, as received (chunked bodies included). 0 when there was none.",
            "example": 96
          },
          "response_bytes": {
            "type": "integer",
            "minimum": 0,
            "description": "Size of the response body in bytes. 0 when there was none.",
            "example": 512
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the request arrived.",
            "example": "2026-04-21T07:34:06Z"
          },
          "request_body": {
            "description": "The body you sent, with credentials, secrets and buyer personal data removed. JSON when it parsed, text otherwise. Null when there was none, or it is no longer available."
          },
          "response_body": {
            "description": "The body the API answered, redacted the same way. Null when there was none, or it is no longer available."
          },
          "bodies_truncated": {
            "type": "boolean",
            "description": "True when a body was larger than 256 KB and only its start was kept (then as text)."
          },
          "events": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "pattern": "^evt_[0-9A-Za-z]+$",
                  "description": "Unique identifier of the event, prefixed with `evt_`. Treat it as an opaque string: store it and compare it, never parse it.",
                  "example": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                },
                "object": {
                  "type": "string",
                  "enum": [
                    "event"
                  ],
                  "description": "Names the type of this object. Always `event`."
                },
                "type": {
                  "type": "string",
                  "description": "The event type.",
                  "example": "order.created"
                },
                "created_at": {
                  "type": "string",
                  "format": "date-time",
                  "description": "Point in time, ISO 8601 in UTC.",
                  "example": "2026-04-21T07:34:06Z"
                },
                "deliveries": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "string",
                        "pattern": "^whd_[0-9A-Za-z]+$",
                        "description": "Unique identifier of the webhook delivery, prefixed with `whd_`. Treat it as an opaque string: store it and compare it, never parse it.",
                        "example": "whd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                      },
                      "object": {
                        "type": "string",
                        "enum": [
                          "webhook_delivery"
                        ],
                        "description": "Names the type of this object. Always `webhook_delivery`."
                      },
                      "endpoint": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "pattern": "^whe_[0-9A-Za-z]+$",
                            "description": "Unique identifier of the webhook endpoint, prefixed with `whe_`. Treat it as an opaque string: store it and compare it, never parse it.",
                            "example": "whe_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                          },
                          "object": {
                            "type": "string",
                            "enum": [
                              "webhook_endpoint"
                            ],
                            "description": "Names the type of this object. Always `webhook_endpoint`."
                          },
                          "url": {
                            "type": "string",
                            "description": "The URL the event was sent to.",
                            "example": "https://example.com/webhooks/dapi"
                          }
                        },
                        "required": [
                          "id",
                          "object",
                          "url"
                        ],
                        "description": "The webhook endpoint this delivery went to."
                      },
                      "status": {
                        "$ref": "#/components/schemas/WebhookDeliveryStatus"
                      },
                      "attempts": {
                        "type": "array",
                        "items": {
                          "$ref": "#/components/schemas/WebhookDeliveryAttempt"
                        },
                        "description": "Every try so far, first try first. Empty until the first try is made."
                      }
                    },
                    "required": [
                      "id",
                      "object",
                      "endpoint",
                      "status",
                      "attempts"
                    ]
                  },
                  "description": "Where the event was sent, with every try."
                }
              },
              "required": [
                "id",
                "object",
                "type",
                "created_at",
                "deliveries"
              ]
            },
            "description": "Webhook events this request caused, with their deliveries to your endpoints."
          }
        },
        "required": [
          "id",
          "object",
          "method",
          "route",
          "path",
          "status_code",
          "duration_ms",
          "credential",
          "api_key",
          "ip",
          "user_agent",
          "idempotency_key",
          "error_code",
          "request_bytes",
          "response_bytes",
          "created_at",
          "bodies_truncated",
          "events"
        ],
        "description": "One logged request with its bodies and what it caused."
      },
      "WebhookDeliveryStatus": {
        "type": "string",
        "enum": [
          "pending",
          "succeeded",
          "failed",
          "skipped"
        ],
        "description": "- `pending`: being sent, or waiting for a retry (retries run for 24 hours).\n- `succeeded`: your endpoint answered with a 2xx.\n- `failed`: every try in the 24 hours failed.\n- `skipped`: not sent because the endpoint was disabled when its turn came. Nothing was posted, so there are no attempts."
      },
      "WebhookDeliveryAttempt": {
        "type": "object",
        "properties": {
          "attempt": {
            "type": "integer",
            "minimum": 1,
            "description": "1 for the first try."
          },
          "started_at": {
            "type": "string",
            "format": "date-time",
            "description": "Point in time, ISO 8601 in UTC.",
            "example": "2026-04-21T07:34:06Z"
          },
          "duration_ms": {
            "type": "integer",
            "minimum": 0,
            "description": "How long your endpoint took to answer or fail, in milliseconds. 0 when the request was not sent because the URL was refused.",
            "example": 312
          },
          "status_code": {
            "type": [
              "integer",
              "null"
            ],
            "description": "What your endpoint answered; null when it did not answer (timeout, connection refused)."
          },
          "succeeded": {
            "type": "boolean",
            "description": "True when your endpoint answered with a 2xx status."
          },
          "error": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why the attempt failed: `HTTP <status>` for a non-2xx answer, or `Timed out after <n>s`, `Could not connect` or `Target not allowed` when there was no answer. Null when it succeeded.",
            "example": "HTTP 500"
          },
          "response_body": {
            "type": [
              "string",
              "null"
            ],
            "description": "The first 4 KB of your endpoint's answer, with secrets and personal data removed."
          }
        },
        "required": [
          "attempt",
          "started_at",
          "duration_ms",
          "status_code",
          "succeeded",
          "error",
          "response_body"
        ]
      },
      "ProductList": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ],
            "description": "Names the type of this object. Always `list`."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProductSummary"
            },
            "description": "The page of results."
          },
          "has_more": {
            "type": "boolean",
            "description": "True when more results exist after this page. Fetch them by passing `next_cursor` as `cursor`."
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque cursor for the next page, or null on the last page. Pass it back unchanged as `cursor`.",
            "example": "eyJjIjoiMjAyNi0wNC0yMVQwNzozNDowNloiLCJpIjoiN0txMiJ9"
          }
        },
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ]
      },
      "ProductSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^prd_[0-9A-Za-z]+$",
            "description": "Unique identifier of the product, prefixed with `prd_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "product"
            ],
            "description": "Names the type of this object. Always `product`."
          },
          "name": {
            "type": "string",
            "description": "Display name.",
            "example": "PR Tracker"
          },
          "slug": {
            "type": "string",
            "description": "URL-safe name used in the storefront address.",
            "example": "pr-tracker"
          },
          "category": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/CategoryReference"
              },
              {
                "$ref": "#/components/schemas/Category"
              },
              {
                "type": "null"
              }
            ],
            "description": "The product category, which decides its safety requirements. Null when none is selected."
          },
          "review": {
            "$ref": "#/components/schemas/ProductReview"
          },
          "publishing": {
            "$ref": "#/components/schemas/ProductPublishing"
          },
          "variant_count": {
            "type": "integer",
            "minimum": 0,
            "description": "Number of variants.",
            "example": 2
          },
          "price_range": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "min": {
                "type": "object",
                "properties": {
                  "amount": {
                    "type": "integer",
                    "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                    "example": 5000
                  },
                  "currency": {
                    "type": "string",
                    "pattern": "^[a-z]{3}$",
                    "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                    "example": "usd"
                  }
                },
                "required": [
                  "amount",
                  "currency"
                ],
                "description": "Lowest variant price."
              },
              "max": {
                "type": "object",
                "properties": {
                  "amount": {
                    "type": "integer",
                    "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                    "example": 5000
                  },
                  "currency": {
                    "type": "string",
                    "pattern": "^[a-z]{3}$",
                    "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                    "example": "usd"
                  }
                },
                "required": [
                  "amount",
                  "currency"
                ],
                "description": "Highest variant price."
              }
            },
            "required": [
              "min",
              "max"
            ],
            "description": "Lowest and highest variant price. Null without variants."
          },
          "images": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Image"
            },
            "description": "Product photos."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Point in time, ISO 8601 in UTC.",
            "example": "2026-04-21T07:34:06Z"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "Point in time, ISO 8601 in UTC.",
            "example": "2026-04-21T07:34:06Z"
          },
          "variants": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Variant"
            },
            "description": "Only present with `expand[]=variants`."
          }
        },
        "required": [
          "id",
          "object",
          "name",
          "slug",
          "category",
          "review",
          "publishing",
          "variant_count",
          "price_range",
          "images",
          "created_at",
          "updated_at"
        ],
        "description": "A product as listed. Fetch the product for its description, options, variants and storefront details."
      },
      "CreateProductRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "description": "Display name, shown on the storefront.",
            "example": "PR Tracker"
          },
          "slug": {
            "type": "string",
            "maxLength": 120,
            "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
            "description": "URL-safe name for the storefront address, unique across your products. Generated from the name when omitted.",
            "example": "pr-tracker"
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 20000,
            "description": "Product description shown on the storefront. Null for none."
          },
          "category": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^cat_[0-9A-Za-z]+$",
            "description": "The product category, which decides its safety requirements.",
            "example": "cat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "prices_include_vat": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "For EU and UK buyers: `true` prices include VAT, `false` VAT is added on top, `null` inherits your store setting."
          },
          "options": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 100,
                  "description": "Option name.",
                  "example": "Color"
                },
                "values": {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 100
                  },
                  "minItems": 1,
                  "maxItems": 100,
                  "description": "Possible values, in display order. Removing a value a variant still uses is refused (`option_in_use`).",
                  "example": [
                    "Black",
                    "Blue"
                  ]
                }
              },
              "required": [
                "name",
                "values"
              ]
            },
            "maxItems": 10,
            "description": "The choices buyers make, in display order."
          },
          "disclaimers": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "title": {
                  "type": "string",
                  "maxLength": 200,
                  "description": "Heading of the disclaimer.",
                  "example": "Production safety"
                },
                "text": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 5000,
                  "description": "The disclaimer text. An entry whose text is blank once trimmed is dropped.",
                  "example": "Keep away from heat."
                }
              },
              "required": [
                "title",
                "text"
              ]
            },
            "maxItems": 20,
            "description": "Your disclaimers shown on the product page."
          },
          "show_compliance_labels": {
            "type": "boolean",
            "description": "Whether the category safety labels (warnings, age restriction, risk level, CE mark) are shown on the product page. `true` when omitted on create."
          },
          "images": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ImageInput"
            },
            "maxItems": 20,
            "description": "Product photos, in display order. The list replaces the current images: entries not sent are deleted."
          },
          "custom_label": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/UploadReference"
              },
              {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "description": "The URL of the label the product already has."
                  }
                },
                "required": [
                  "url"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "A label the production partner prints and ships with the product: new (`upload`, purpose `product_image`) or kept (`url`). Null removes it."
          },
          "attachments": {
            "type": "array",
            "items": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/UploadReference"
                },
                {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "pattern": "^att_[0-9A-Za-z]+$",
                      "description": "An attachment to keep.",
                      "example": "att_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                    }
                  },
                  "required": [
                    "id"
                  ]
                }
              ]
            },
            "maxItems": 20,
            "description": "Production documents for the farm: new (`upload`, purpose `attachment`) or kept (`id`)."
          },
          "manufacturing": {
            "type": "object",
            "properties": {
              "assembly_required": {
                "type": "boolean",
                "description": "Whether the printed parts must be assembled before shipping. Turning it on or off sends every design back to review."
              },
              "farm_instructions": {
                "type": [
                  "string",
                  "null"
                ],
                "maxLength": 5000,
                "description": "Notes for the production partner, for example assembly steps. Omitted keeps the current notes; null or blank clears them."
              }
            },
            "required": [
              "assembly_required"
            ],
            "description": "How the product is produced. Applies to every design."
          },
          "designs": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "key": {
                  "type": "string",
                  "pattern": "^[a-zA-Z0-9_-]{1,50}$",
                  "description": "Your name for this design within the request, for variants to reference it.",
                  "example": "body"
                },
                "file": {
                  "type": "object",
                  "properties": {
                    "upload": {
                      "type": "string",
                      "pattern": "^upl_[0-9A-Za-z]+$",
                      "description": "An upload id from `POST /v2/uploads`, used before it expires. The file is checked when used: one that is not what its name and type say is refused with 422 `invalid_file` (the expected type in `details.expected`) and must be uploaded again.",
                      "example": "upl_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                    }
                  },
                  "required": [
                    "upload"
                  ],
                  "description": "The model file, uploaded first with purpose `design_file`."
                },
                "name": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "minLength": 1,
                  "maxLength": 200,
                  "description": "Display name. The file name when omitted."
                },
                "materials": {
                  "$ref": "#/components/schemas/DesignMaterialsInput"
                }
              },
              "required": [
                "key",
                "file",
                "materials"
              ]
            },
            "minItems": 1,
            "maxItems": 50,
            "description": "The model files of the product."
          },
          "variants": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CreateVariantRequest"
            },
            "maxItems": 200,
            "description": "Variants to create with the product. Up to 200 per product."
          }
        },
        "required": [
          "name",
          "designs"
        ]
      },
      "ImageInput": {
        "anyOf": [
          {
            "type": "object",
            "properties": {
              "upload": {
                "type": "string",
                "pattern": "^upl_[0-9A-Za-z]+$",
                "description": "An upload id from `POST /v2/uploads`, used before it expires. The file is checked when used: one that is not what its name and type say is refused with 422 `invalid_file` (the expected type in `details.expected`) and must be uploaded again.",
                "example": "upl_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
              },
              "primary": {
                "type": "boolean",
                "description": "Makes this the main image. At most one entry may be marked. Without a marked entry, a kept main image stays main; otherwise one is chosen for you."
              }
            },
            "required": [
              "upload"
            ]
          },
          {
            "type": "object",
            "properties": {
              "url": {
                "type": "string",
                "format": "uri",
                "description": "The URL of an image this product or stock already has, to keep it."
              },
              "primary": {
                "type": "boolean",
                "description": "Makes this the main image. At most one entry may be marked. Without a marked entry, a kept main image stays main; otherwise one is chosen for you."
              }
            },
            "required": [
              "url"
            ]
          }
        ],
        "description": "A new image (`{ \"upload\": \"upl_...\" }`, purpose `product_image`) or one to keep (`{ \"url\": \"...\" }`). At most one `primary`. Without one marked, the image that was main stays main if you keep it; otherwise a product takes the first image in the list, and a stock takes the first new photo, or its newest photo when none is new."
      },
      "UploadReference": {
        "type": "object",
        "properties": {
          "upload": {
            "type": "string",
            "pattern": "^upl_[0-9A-Za-z]+$",
            "description": "An upload id from `POST /v2/uploads`, used before it expires. The file is checked when used: one that is not what its name and type say is refused with 422 `invalid_file` (the expected type in `details.expected`) and must be uploaded again.",
            "example": "upl_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          }
        },
        "required": [
          "upload"
        ]
      },
      "DesignMaterialsInput": {
        "oneOf": [
          {
            "type": "object",
            "properties": {
              "mode": {
                "type": "string",
                "enum": [
                  "single"
                ],
                "description": "`single`: the design prints in one material, and each variant picks one from `allowed`."
              },
              "allowed": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/MaterialInput"
                },
                "minItems": 1,
                "maxItems": 100,
                "description": "Materials variants may print this design in; each variant picks one."
              }
            },
            "required": [
              "mode",
              "allowed"
            ]
          },
          {
            "type": "object",
            "properties": {
              "mode": {
                "type": "string",
                "enum": [
                  "multicolor"
                ],
                "description": "`multicolor`: the design prints in several color slots, and each variant picks one of the `combinations`."
              },
              "combinations": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "maxLength": 200,
                      "description": "Display name. Built from the slot materials when omitted."
                    },
                    "slots": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MaterialInput"
                      },
                      "minItems": 2,
                      "maxItems": 8,
                      "description": "One material per color slot, in the order the file uses them."
                    }
                  },
                  "required": [
                    "slots"
                  ]
                },
                "minItems": 1,
                "maxItems": 100,
                "description": "Color combinations variants may print this design in. Every combination has the same number of slots; each variant picks one by sending its slot materials in order."
              }
            },
            "required": [
              "mode",
              "combinations"
            ]
          }
        ]
      },
      "MaterialInput": {
        "anyOf": [
          {
            "type": "string",
            "pattern": "^mat_[0-9A-Za-z]+$",
            "description": "Unique identifier of the material, prefixed with `mat_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          {
            "$ref": "#/components/schemas/CustomMaterialInput"
          }
        ],
        "description": "A catalog material id (`mat_...`) or `{ \"custom\": \"<name>\" }`."
      },
      "CustomMaterialInput": {
        "type": "object",
        "properties": {
          "custom": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100,
            "description": "A filament not in the catalog, by name. Reviewed by 3DAPI.",
            "example": "PETG Matte"
          }
        },
        "required": [
          "custom"
        ]
      },
      "CreateVariantRequest": {
        "type": "object",
        "properties": {
          "sku": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100,
            "description": "Your SKU code, unique across your active variants.",
            "example": "TRACKER-BLUE"
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 200,
            "description": "Display name of the variant. Null for none.",
            "example": "PR Tracker Blue"
          },
          "options": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string",
                  "minLength": 1,
                  "description": "An option name of the product.",
                  "example": "Color"
                },
                "value": {
                  "type": "string",
                  "minLength": 1,
                  "description": "One of the values of that option.",
                  "example": "Blue"
                }
              },
              "required": [
                "name",
                "value"
              ]
            },
            "default": [],
            "description": "One value per product option, using the product option names and values."
          },
          "price": {
            "type": "object",
            "properties": {
              "amount": {
                "type": "integer",
                "maximum": 10000000,
                "description": "Amount in cents, at most 10000000 ($100,000.00). 5000 means $50.00.",
                "example": 5000
              },
              "currency": {
                "type": "string",
                "pattern": "^[a-z]{3}$",
                "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                "example": "usd"
              }
            },
            "required": [
              "amount",
              "currency"
            ],
            "description": "What the buyer pays, at most $100,000.00. Not below the minimum, which covers production, a platform fee of up to 10% and the card fee at its highest rate (2.9% + $0.30, the buyer is not known yet): minimum = (manufacturing + extra parts + $0.30) / (0.90 - 0.029), rounded up to the cent, or no minimum while nothing has a production price yet. A lower price is refused with `price_below_minimum`, with the minimum in `details.minimum`. Checked when the price or the contents change, so variants saved before this rule keep their price until edited."
          },
          "prices_include_vat": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "For EU and UK buyers: `true` the price includes VAT, `false` VAT is added on top, `null` inherits from the product, then your store setting."
          },
          "category": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^cat_[0-9A-Za-z]+$",
            "description": "Overrides the product category for this variant. Null uses the product category.",
            "example": "cat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "contents": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/VariantContentInput"
            },
            "minItems": 1,
            "maxItems": 50,
            "description": "What each unit is made of: at least one design or one stock."
          },
          "image": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/UploadReference"
              },
              {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "description": "The URL of one of the product images, to use as the variant image."
                  }
                },
                "required": [
                  "url"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "A variant image, new (`upload`) or kept (`url`). Null for none."
          }
        },
        "required": [
          "sku",
          "price",
          "contents"
        ]
      },
      "VariantContentInput": {
        "oneOf": [
          {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "design"
                ],
                "description": "`design`: a design of this product, printed for the variant."
              },
              "design": {
                "type": "string",
                "minLength": 1,
                "description": "A design id (`dsg_...`), or, inside `create_product`, the `key` of a design created in the same request."
              },
              "quantity": {
                "type": "integer",
                "minimum": 1,
                "maximum": 100,
                "description": "Copies of the design in one unit of the variant."
              },
              "materials": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/MaterialInput"
                },
                "minItems": 1,
                "maxItems": 8,
                "description": "One material for a single-material design, or one per color slot for a multicolor design, in slot order. Each must be allowed by the design."
              }
            },
            "required": [
              "type",
              "design",
              "quantity",
              "materials"
            ]
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "extra"
                ],
                "description": "`extra`: one of your extras, packed with the variant."
              },
              "extra": {
                "type": "string",
                "pattern": "^xtr_[0-9A-Za-z]+$",
                "description": "One of your extras. An extra still pending review is accepted and sends the product designs back to review; a rejected one is refused (`extra_not_approved`).",
                "example": "xtr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
              },
              "quantity": {
                "type": "integer",
                "minimum": 1,
                "maximum": 100,
                "description": "Units of the extra in one unit of the variant."
              }
            },
            "required": [
              "type",
              "extra",
              "quantity"
            ]
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "stock"
                ],
                "description": "`stock`: one of your approved stocks."
              },
              "stock": {
                "type": "string",
                "pattern": "^stk_[0-9A-Za-z]+$",
                "description": "Unique identifier of the stock, prefixed with `stk_`. Treat it as an opaque string: store it and compare it, never parse it.",
                "example": "stk_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
              },
              "quantity": {
                "type": "integer",
                "minimum": 1,
                "maximum": 100,
                "description": "Stock units used by one unit of the variant."
              },
              "consumes_stock": {
                "type": "boolean",
                "default": true,
                "description": "Ship this variant from the stock."
              }
            },
            "required": [
              "type",
              "stock",
              "quantity"
            ]
          }
        ]
      },
      "UpdateProductRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "description": "Display name, shown on the storefront.",
            "example": "PR Tracker"
          },
          "slug": {
            "type": "string",
            "maxLength": 120,
            "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
            "description": "URL-safe name for the storefront address, unique across your products. Generated from the name when omitted.",
            "example": "pr-tracker"
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 20000,
            "description": "Product description shown on the storefront. Null for none."
          },
          "category": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^cat_[0-9A-Za-z]+$",
            "description": "The product category, which decides its safety requirements.",
            "example": "cat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "prices_include_vat": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "For EU and UK buyers: `true` prices include VAT, `false` VAT is added on top, `null` inherits your store setting."
          },
          "options": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 100,
                  "description": "Option name.",
                  "example": "Color"
                },
                "values": {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 100
                  },
                  "minItems": 1,
                  "maxItems": 100,
                  "description": "Possible values, in display order. Removing a value a variant still uses is refused (`option_in_use`).",
                  "example": [
                    "Black",
                    "Blue"
                  ]
                }
              },
              "required": [
                "name",
                "values"
              ]
            },
            "maxItems": 10,
            "description": "The choices buyers make, in display order."
          },
          "disclaimers": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "title": {
                  "type": "string",
                  "maxLength": 200,
                  "description": "Heading of the disclaimer.",
                  "example": "Production safety"
                },
                "text": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 5000,
                  "description": "The disclaimer text. An entry whose text is blank once trimmed is dropped.",
                  "example": "Keep away from heat."
                }
              },
              "required": [
                "title",
                "text"
              ]
            },
            "maxItems": 20,
            "description": "Your disclaimers shown on the product page."
          },
          "show_compliance_labels": {
            "type": "boolean",
            "description": "Whether the category safety labels (warnings, age restriction, risk level, CE mark) are shown on the product page. `true` when omitted on create."
          },
          "images": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ImageInput"
            },
            "maxItems": 20,
            "description": "Product photos, in display order. The list replaces the current images: entries not sent are deleted."
          },
          "custom_label": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/UploadReference"
              },
              {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "description": "The URL of the label the product already has."
                  }
                },
                "required": [
                  "url"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "A label the production partner prints and ships with the product: new (`upload`, purpose `product_image`) or kept (`url`). Null removes it."
          },
          "attachments": {
            "type": "array",
            "items": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/UploadReference"
                },
                {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "pattern": "^att_[0-9A-Za-z]+$",
                      "description": "An attachment to keep.",
                      "example": "att_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                    }
                  },
                  "required": [
                    "id"
                  ]
                }
              ]
            },
            "maxItems": 20,
            "description": "Production documents for the farm: new (`upload`, purpose `attachment`) or kept (`id`)."
          },
          "manufacturing": {
            "type": "object",
            "properties": {
              "assembly_required": {
                "type": "boolean",
                "description": "Whether the printed parts must be assembled before shipping. Turning it on or off sends every design back to review."
              },
              "farm_instructions": {
                "type": [
                  "string",
                  "null"
                ],
                "maxLength": 5000,
                "description": "Notes for the production partner, for example assembly steps. Omitted keeps the current notes; null or blank clears them."
              }
            },
            "required": [
              "assembly_required"
            ],
            "description": "How the product is produced. Applies to every design."
          }
        },
        "description": "Only the fields you send change. Arrays replace the whole list. Designs and variants have their own operations."
      },
      "AddDesignRequest": {
        "type": "object",
        "properties": {
          "file": {
            "type": "object",
            "properties": {
              "upload": {
                "type": "string",
                "pattern": "^upl_[0-9A-Za-z]+$",
                "description": "An upload id from `POST /v2/uploads`, used before it expires. The file is checked when used: one that is not what its name and type say is refused with 422 `invalid_file` (the expected type in `details.expected`) and must be uploaded again.",
                "example": "upl_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
              }
            },
            "required": [
              "upload"
            ],
            "description": "The model file, uploaded first with purpose `design_file`."
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 1,
            "maxLength": 200,
            "description": "Display name. The file name when omitted."
          },
          "materials": {
            "$ref": "#/components/schemas/DesignMaterialsInput"
          }
        },
        "required": [
          "file",
          "materials"
        ]
      },
      "UpdateDesignRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 1,
            "maxLength": 200,
            "description": "Display name. The file name when omitted."
          },
          "materials": {
            "$ref": "#/components/schemas/DesignMaterialsInput"
          }
        }
      },
      "UpdateVariantRequest": {
        "type": "object",
        "properties": {
          "sku": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100,
            "description": "Your SKU code, unique across your active variants.",
            "example": "TRACKER-BLUE"
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 200,
            "description": "Display name of the variant. Null for none.",
            "example": "PR Tracker Blue"
          },
          "options": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string",
                  "minLength": 1,
                  "description": "An option name of the product.",
                  "example": "Color"
                },
                "value": {
                  "type": "string",
                  "minLength": 1,
                  "description": "One of the values of that option.",
                  "example": "Blue"
                }
              },
              "required": [
                "name",
                "value"
              ]
            },
            "description": "One value per product option, using the product option names and values."
          },
          "price": {
            "type": "object",
            "properties": {
              "amount": {
                "type": "integer",
                "maximum": 10000000,
                "description": "Amount in cents, at most 10000000 ($100,000.00). 5000 means $50.00.",
                "example": 5000
              },
              "currency": {
                "type": "string",
                "pattern": "^[a-z]{3}$",
                "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                "example": "usd"
              }
            },
            "required": [
              "amount",
              "currency"
            ],
            "description": "What the buyer pays, at most $100,000.00. Not below the minimum, which covers production, a platform fee of up to 10% and the card fee at its highest rate (2.9% + $0.30, the buyer is not known yet): minimum = (manufacturing + extra parts + $0.30) / (0.90 - 0.029), rounded up to the cent, or no minimum while nothing has a production price yet. A lower price is refused with `price_below_minimum`, with the minimum in `details.minimum`. Checked when the price or the contents change, so variants saved before this rule keep their price until edited."
          },
          "prices_include_vat": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "For EU and UK buyers: `true` the price includes VAT, `false` VAT is added on top, `null` inherits from the product, then your store setting."
          },
          "category": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^cat_[0-9A-Za-z]+$",
            "description": "Overrides the product category for this variant. Null uses the product category.",
            "example": "cat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "contents": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/VariantContentInput"
            },
            "minItems": 1,
            "maxItems": 50,
            "description": "What each unit is made of: at least one design or one stock."
          },
          "image": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/UploadReference"
              },
              {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "description": "The URL of one of the product images, to use as the variant image."
                  }
                },
                "required": [
                  "url"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "A variant image, new (`upload`) or kept (`url`). Null for none."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "inactive"
            ],
            "description": "`inactive` keeps the variant but stops selling it."
          }
        },
        "description": "Only the fields you send change. Arrays replace the whole list."
      },
      "UploadComplianceDocumentRequest": {
        "type": "object",
        "properties": {
          "requirement": {
            "type": "string",
            "minLength": 1,
            "description": "The `key` of the requirement this document covers, from the product compliance view or the category.",
            "example": "material-toxicity-assessment"
          },
          "file": {
            "type": "object",
            "properties": {
              "upload": {
                "type": "string",
                "pattern": "^upl_[0-9A-Za-z]+$",
                "description": "An upload id from `POST /v2/uploads`, used before it expires. The file is checked when used: one that is not what its name and type say is refused with 422 `invalid_file` (the expected type in `details.expected`) and must be uploaded again.",
                "example": "upl_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
              }
            },
            "required": [
              "upload"
            ],
            "description": "The document, uploaded first with purpose `compliance_document`."
          }
        },
        "required": [
          "requirement",
          "file"
        ]
      },
      "CreateOrderRequest": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "variant": {
                  "type": "string",
                  "pattern": "^var_[0-9A-Za-z]+$",
                  "description": "One of your variants. Its product must be approved.",
                  "example": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                },
                "quantity": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 1000,
                  "description": "Units of this variant to produce and ship, 1 to 1000.",
                  "example": 2
                }
              },
              "required": [
                "variant",
                "quantity"
              ]
            },
            "minItems": 1,
            "maxItems": 100,
            "description": "What to produce and ship: 1 to 100 lines of 1 to 1000 units each."
          },
          "customer": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string",
                "minLength": 1,
                "maxLength": 200,
                "description": "Full name of the person receiving the order.",
                "example": "Ana Popescu"
              },
              "email": {
                "type": "string",
                "format": "email",
                "description": "Email of the person receiving the order.",
                "example": "ana@example.com"
              },
              "phone": {
                "type": [
                  "string",
                  "null"
                ],
                "maxLength": 40,
                "description": "Phone number with country prefix, for the carrier.",
                "example": "+40712345678"
              }
            },
            "required": [
              "name",
              "email"
            ],
            "description": "Who receives the order."
          },
          "shipping_address": {
            "type": "object",
            "properties": {
              "line1": {
                "type": "string",
                "minLength": 1,
                "maxLength": 200,
                "description": "Street and number.",
                "example": "Str. Test 1"
              },
              "line2": {
                "type": [
                  "string",
                  "null"
                ],
                "maxLength": 200,
                "description": "Apartment, suite, floor or building. Omit or send null when there is none."
              },
              "city": {
                "type": "string",
                "minLength": 1,
                "maxLength": 100,
                "description": "City or town.",
                "example": "Cluj-Napoca"
              },
              "state": {
                "type": [
                  "string",
                  "null"
                ],
                "maxLength": 100,
                "description": "State, province, county or region, where the address uses one. Omit or send null otherwise."
              },
              "postal_code": {
                "type": [
                  "string",
                  "null"
                ],
                "maxLength": 20,
                "description": "Postal or ZIP code. Omit or send null where the country has none.",
                "example": "400001"
              },
              "country_code": {
                "type": "string",
                "pattern": "^[A-Z]{2}$",
                "description": "ISO 3166-1 alpha-2, uppercase. A code that is not a real country is refused with `validation_failed`.",
                "example": "RO"
              }
            },
            "required": [
              "line1",
              "city",
              "country_code"
            ],
            "description": "Where to ship. Stored as one formatted line plus the country, which the order returns as `shipping_address.formatted`."
          },
          "reference": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^[a-zA-Z0-9_-]{1,100}$",
            "description": "Your own order id, for matching. Letters, digits, `-` and `_`. Returned as `external.channel_order_id` and searchable.",
            "example": "SHOP-10023"
          },
          "customer_notes": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 2000,
            "description": "Free-text notes for the order, up to 2000 characters. Returned as `customer_notes`, which is only shown to keys with the `orders:read_customer` scope."
          },
          "discount_code": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 100,
            "description": "A discount code from 3DAPI."
          },
          "use_sample_credit": {
            "type": "boolean",
            "default": false,
            "description": "Spend your sample credit on this order first. How much applies is decided by 3DAPI and shown in `charges.sample_credit`."
          }
        },
        "required": [
          "items",
          "customer",
          "shipping_address"
        ]
      },
      "OrderList": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ],
            "description": "Names the type of this object. Always `list`."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OrderSummary"
            },
            "description": "The page of results."
          },
          "has_more": {
            "type": "boolean",
            "description": "True when more results exist after this page. Fetch them by passing `next_cursor` as `cursor`."
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque cursor for the next page, or null on the last page. Pass it back unchanged as `cursor`.",
            "example": "eyJjIjoiMjAyNi0wNC0yMVQwNzozNDowNloiLCJpIjoiN0txMiJ9"
          }
        },
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ]
      },
      "OrderSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^ord_[0-9A-Za-z]+$",
            "description": "Unique identifier of the order, prefixed with `ord_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "order"
            ],
            "description": "Names the type of this object. Always `order`."
          },
          "number": {
            "type": "string",
            "description": "Human-readable order number, the one your customers and 3DAPI support use.",
            "example": "PV-SCN-1701A372"
          },
          "channel": {
            "$ref": "#/components/schemas/OrderChannel"
          },
          "status": {
            "$ref": "#/components/schemas/OrderStatus"
          },
          "customer": {
            "$ref": "#/components/schemas/OrderCustomer"
          },
          "shipping_address": {
            "$ref": "#/components/schemas/OrderShippingAddress"
          },
          "charges": {
            "$ref": "#/components/schemas/OrderCharges"
          },
          "earnings": {
            "$ref": "#/components/schemas/OrderEarnings"
          },
          "shipping": {
            "$ref": "#/components/schemas/OrderShipping"
          },
          "external": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "channel_order_id": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The order id in Shopify or ShipStation."
              },
              "channel_total": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "amount": {
                    "type": "integer",
                    "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                    "example": 5000
                  },
                  "currency": {
                    "type": "string",
                    "pattern": "^[a-z]{3}$",
                    "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                    "example": "usd"
                  }
                },
                "required": [
                  "amount",
                  "currency"
                ],
                "description": "What the customer paid in that channel, when known."
              }
            },
            "required": [
              "channel_order_id",
              "channel_total"
            ],
            "description": "The original order in the sales channel, for Shopify and ShipStation orders. Null otherwise."
          },
          "customer_notes": {
            "type": [
              "string",
              "null"
            ],
            "description": "Notes the customer left at checkout. Null without the `orders:read_customer` scope, since buyers often write personal details here."
          },
          "qc": {
            "type": "object",
            "properties": {
              "open_tickets": {
                "type": "integer",
                "minimum": 0,
                "description": "QC tickets on this order that are not approved, closed or cancelled."
              },
              "blocks_shipping": {
                "type": "boolean",
                "description": "True when an open quality check holds the order back from shipping."
              }
            },
            "required": [
              "open_tickets",
              "blocks_shipping"
            ],
            "description": "Quality control on this order. List QC tickets with `order` for the details."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Point in time, ISO 8601 in UTC.",
            "example": "2026-04-21T07:34:06Z"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "Point in time, ISO 8601 in UTC.",
            "example": "2026-04-21T07:34:06Z"
          },
          "item_count": {
            "type": "integer",
            "minimum": 0,
            "description": "Units ordered across all lines.",
            "example": 1
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OrderItem"
            },
            "description": "Only present with `expand[]=items`."
          }
        },
        "required": [
          "id",
          "object",
          "number",
          "channel",
          "status",
          "customer",
          "shipping_address",
          "charges",
          "earnings",
          "shipping",
          "external",
          "customer_notes",
          "qc",
          "created_at",
          "updated_at",
          "item_count"
        ],
        "description": "An order as listed. Get the order for its items and refunds."
      },
      "CreateIntegratorOrderRequest": {
        "type": "object",
        "properties": {
          "mode": {
            "type": "string",
            "enum": [
              "checkout",
              "wallet"
            ],
            "description": "`wallet`: you pay the production price plus shipping from your wallet (a shortfall is charged to your saved card). `checkout`: your customer pays the sales price you set through a hosted 3DAPI checkout and you earn the difference."
          },
          "items": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "quote": {
                  "type": "string",
                  "pattern": "^qte_[0-9A-Za-z]+$",
                  "description": "One of your quotes with status `ready`.",
                  "example": "qte_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                },
                "quantity": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 10000,
                  "description": "Copies of this quote to print and ship, 1 to 10000 per line (an order has up to 100 lines). The production price per unit depends on it. Designer orders (create_order) allow 1 to 1000 units per line instead, also with up to 100 lines.",
                  "example": 10
                },
                "materials": {
                  "anyOf": [
                    {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "pattern": "^mat_[0-9A-Za-z]+$",
                        "description": "Unique identifier of the material, prefixed with `mat_`. Treat it as an opaque string: store it and compare it, never parse it.",
                        "example": "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                      },
                      "minItems": 1,
                      "maxItems": 16
                    },
                    {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "slot": {
                            "type": "integer",
                            "minimum": 1,
                            "maximum": 16,
                            "description": "Extruder slot (filament number) in the 3MF, from 1, as `suggested_materials[].slot` on the quote lists them.",
                            "example": 2
                          },
                          "material": {
                            "type": "string",
                            "pattern": "^mat_[0-9A-Za-z]+$",
                            "description": "A catalog material (list_materials).",
                            "example": "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                          }
                        },
                        "required": [
                          "slot",
                          "material"
                        ]
                      },
                      "minItems": 1,
                      "maxItems": 16
                    }
                  ],
                  "description": "Which catalog material (list_materials) goes in which extruder slot of the quote's file. Either a list of `{ slot, material }`, one for each slot in the quote's `suggested_materials` and no others, or a plain list of material ids for slots 1, 2, 3... in order, which only works when the quote uses slots from 1 without gaps. Slots that do not match are refused with `material_slots_mismatch` (`details.expected_slots`). Send this or `use_suggested_materials`, not both. Quotes without `suggested_materials` (created before suggestions) take materials for slots 1 to n, n no more than the quote prints with (`material_not_allowed`, `details.slots`)."
                },
                "use_suggested_materials": {
                  "type": "boolean",
                  "enum": [
                    true
                  ],
                  "description": "Print with the quote's `suggested_materials`: the catalog material closest to each filament colour in your file, in the slot it uses. Send this or `materials`, not both. Refused with `suggestions_unavailable` when the quote has no suggestions."
                },
                "unit_sales_price": {
                  "type": [
                    "object",
                    "null"
                  ],
                  "properties": {
                    "amount": {
                      "type": "integer",
                      "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                      "example": 5000
                    },
                    "currency": {
                      "type": "string",
                      "pattern": "^[a-z]{3}$",
                      "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                      "example": "usd"
                    }
                  },
                  "required": [
                    "amount",
                    "currency"
                  ],
                  "description": "Required in `checkout` mode, forbidden in `wallet` mode: what your customer pays per unit, net of VAT, in `usd` (another currency is refused with `currency_not_supported`). Not below the minimum, which covers the production unit price (the quote price plus the extras on this line) and this unit's share of the card fee checkout takes, so a sale at the minimum never loses money. With k = fee percent / 100 x (1 + the VAT rate the buyer pays on top) and the fee of the destination (EU 1.5% + $0.30, UK 2.5% + $0.30, elsewhere 2.9% + $0.30): minimum = (production_unit_price + (k x shipping + $0.30) / units in the order) / (1 - k), rounded up to the cent. A lower price is refused with `sales_price_below_production`, with `details.minimum`, `details.production_unit_price` and `details.formula`."
                },
                "extras": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "extra": {
                        "type": "string",
                        "pattern": "^xtr_[0-9A-Za-z]+$",
                        "description": "One of your extras, approved by 3DAPI (list_extras).",
                        "example": "xtr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                      },
                      "quantity_per_unit": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 100,
                        "description": "How many of the extra ship with each unit.",
                        "example": 2
                      }
                    },
                    "required": [
                      "extra",
                      "quantity_per_unit"
                    ]
                  },
                  "maxItems": 20,
                  "description": "Bought-in parts shipped with every unit of this line, for example magnets or screws. Each extra's approved `price` times `quantity_per_unit` is added to the production price of every unit: you pay it in `wallet` mode, and in `checkout` mode it raises the minimum `unit_sales_price` and comes out of your earnings. They also count toward the parcel's shipping weight, as on designer orders. The production partner gets them with the order."
                }
              },
              "required": [
                "quote",
                "quantity"
              ]
            },
            "minItems": 1,
            "maxItems": 100,
            "description": "What to print and ship, 1 to 100 lines."
          },
          "shipping_address": {
            "type": "object",
            "properties": {
              "line1": {
                "type": "string",
                "minLength": 1,
                "maxLength": 200,
                "description": "Street and number.",
                "example": "Hauptstrasse 12"
              },
              "line2": {
                "type": [
                  "string",
                  "null"
                ],
                "maxLength": 200,
                "description": "Apartment, suite, floor or building. Omit or send null when there is none."
              },
              "city": {
                "type": "string",
                "minLength": 1,
                "maxLength": 100,
                "description": "City or town.",
                "example": "Berlin"
              },
              "state": {
                "type": [
                  "string",
                  "null"
                ],
                "maxLength": 100,
                "description": "State, province, county or region, where the address uses one. Omit or send null otherwise."
              },
              "postal_code": {
                "type": [
                  "string",
                  "null"
                ],
                "maxLength": 20,
                "description": "Postal or ZIP code. Omit or send null where the country has none.",
                "example": "10115"
              },
              "country_code": {
                "type": "string",
                "pattern": "^[A-Z]{2}$",
                "description": "ISO 3166-1 alpha-2, uppercase. A code that is not a real country is refused with `validation_failed`.",
                "example": "DE"
              }
            },
            "required": [
              "line1",
              "city",
              "country_code"
            ],
            "description": "Where to ship, in both modes. In `checkout` mode it also decides the buyer's VAT (EU and UK consumers pay their country's rate on top) and the payment account."
          },
          "customer": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string",
                "minLength": 1,
                "maxLength": 200,
                "description": "Full name of the person receiving the order.",
                "example": "Jane Buyer"
              },
              "email": {
                "type": "string",
                "format": "email",
                "description": "Email of the person receiving the order.",
                "example": "jane@example.com"
              },
              "phone": {
                "type": [
                  "string",
                  "null"
                ],
                "maxLength": 40,
                "description": "Phone number with country prefix. Omit or send null when you have none.",
                "example": "+4930123456"
              },
              "company": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200,
                    "description": "The business name, printed on the invoice.",
                    "example": "Muster GmbH"
                  },
                  "tax_id": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 50,
                    "description": "The business VAT number. For an EU shipping country it is checked with VIES when the order is created: a confirmed number pays no VAT (reverse charge), a number VIES rejects is refused (`vat_number_invalid`), and when VIES cannot be reached VAT is charged. Numbers for other countries, the UK included, are not checked and VAT follows the normal rules.",
                    "example": "DE123456789"
                  }
                },
                "required": [
                  "name"
                ],
                "description": "`checkout` mode only: set when your customer buys as a business. Returned as `customer.company`, with `tax_id_validated` once VIES has checked the number."
              }
            },
            "required": [
              "name",
              "email"
            ],
            "description": "Who receives the order. In `checkout` mode this is your customer, who gets the order confirmation and shipping emails from 3DAPI naming you as the seller."
          },
          "checkout": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "success_url": {
                "type": "string",
                "maxLength": 2048,
                "format": "uri",
                "description": "Where the hosted page sends your customer after paying. Must be https; plain http is accepted only for localhost and 127.0.0.1. Anything else is refused with `validation_failed`."
              },
              "cancel_url": {
                "type": "string",
                "maxLength": 2048,
                "format": "uri",
                "description": "Where the hosted page sends your customer if they leave without paying. Same rules as `success_url`."
              }
            },
            "required": [
              "success_url",
              "cancel_url"
            ],
            "description": "Required in `checkout` mode, forbidden in `wallet` mode."
          },
          "reference": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^[a-zA-Z0-9_-]{1,100}$",
            "description": "Your own order id, for matching.",
            "example": "SHOP-1042"
          },
          "customer_notes": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 2000,
            "description": "Free-text notes for the order, up to 2000 characters. Returned as `customer_notes`, which is only shown to keys with the `orders:read_customer` scope."
          }
        },
        "required": [
          "mode",
          "items",
          "shipping_address",
          "customer"
        ]
      },
      "ShippingEstimate": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "shipping_estimate"
            ],
            "description": "Names the type of this object. Always `shipping_estimate`."
          },
          "country_code": {
            "type": "string",
            "pattern": "^[A-Z]{2}$",
            "description": "Destination country, as sent.",
            "example": "DE"
          },
          "zone": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "code": {
                "type": "string",
                "description": "Stable identifier of the zone, for example `eu-core`. Zones are platform configuration, so they have a readable code instead of an opaque id.",
                "example": "eu-core"
              },
              "name": {
                "type": "string",
                "description": "Display name of the zone.",
                "example": "EU Core"
              }
            },
            "required": [
              "code",
              "name"
            ],
            "description": "The zone that serves the country. Null when the order cannot be weighed yet and the flat rate applies."
          },
          "tier": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "small",
              "medium",
              "large",
              null
            ],
            "description": "Tier of the heaviest item in the parcel, which the parcel is priced at. Null with the flat rate."
          },
          "additional_items": {
            "type": "integer",
            "minimum": 0,
            "description": "Items charged on top of the heaviest one. Extra-small items ship free and are not counted.",
            "example": 3
          },
          "total": {
            "type": "object",
            "properties": {
              "amount": {
                "type": "integer",
                "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                "example": 5000
              },
              "currency": {
                "type": "string",
                "pattern": "^[a-z]{3}$",
                "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                "example": "usd"
              }
            },
            "required": [
              "amount",
              "currency"
            ],
            "description": "What the order would be charged for shipping, without VAT. In `checkout` mode buyers in the EU or UK pay VAT on top."
          }
        },
        "required": [
          "object",
          "country_code",
          "zone",
          "tier",
          "additional_items",
          "total"
        ],
        "description": "The shipping `create_integrator_order` would charge for these lines and this destination."
      },
      "EstimateIntegratorOrderShippingRequest": {
        "type": "object",
        "properties": {
          "country": {
            "type": "string",
            "pattern": "^[A-Z]{2}$",
            "description": "Destination country, ISO 3166-1 alpha-2, uppercase: the `shipping_address.country_code` you would order with. A code that is not a real country is refused with `validation_failed`.",
            "example": "DE"
          },
          "items": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "quote": {
                  "type": "string",
                  "pattern": "^qte_[0-9A-Za-z]+$",
                  "description": "One of your quotes with status `ready`.",
                  "example": "qte_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                },
                "quantity": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 10000,
                  "description": "Copies of this quote to print and ship, 1 to 10000 per line (an order has up to 100 lines). The production price per unit depends on it. Designer orders (create_order) allow 1 to 1000 units per line instead, also with up to 100 lines.",
                  "example": 10
                },
                "extras": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "extra": {
                        "type": "string",
                        "pattern": "^xtr_[0-9A-Za-z]+$",
                        "description": "One of your extras, approved by 3DAPI (list_extras).",
                        "example": "xtr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                      },
                      "quantity_per_unit": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 100,
                        "description": "How many of the extra ship with each unit.",
                        "example": 2
                      }
                    },
                    "required": [
                      "extra",
                      "quantity_per_unit"
                    ]
                  },
                  "maxItems": 20,
                  "description": "The extras you would ship with every unit of this line, as on `create_integrator_order`. They count toward the shipping weight."
                }
              },
              "required": [
                "quote",
                "quantity"
              ]
            },
            "minItems": 1,
            "maxItems": 100,
            "description": "The lines of the order you would place, as `create_integrator_order` takes them. Materials and prices do not change shipping, so they are not sent."
          }
        },
        "required": [
          "country",
          "items"
        ]
      },
      "IntegratorOrderList": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ],
            "description": "Names the type of this object. Always `list`."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/IntegratorOrder"
            },
            "description": "The page of results."
          },
          "has_more": {
            "type": "boolean",
            "description": "True when more results exist after this page. Fetch them by passing `next_cursor` as `cursor`."
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque cursor for the next page, or null on the last page. Pass it back unchanged as `cursor`.",
            "example": "eyJjIjoiMjAyNi0wNC0yMVQwNzozNDowNloiLCJpIjoiN0txMiJ9"
          }
        },
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ]
      },
      "ImportIssueList": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ],
            "description": "Names the type of this object. Always `list`."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ImportIssue"
            },
            "description": "The page of results."
          },
          "has_more": {
            "type": "boolean",
            "description": "True when more results exist after this page. Fetch them by passing `next_cursor` as `cursor`."
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque cursor for the next page, or null on the last page. Pass it back unchanged as `cursor`.",
            "example": "eyJjIjoiMjAyNi0wNC0yMVQwNzozNDowNloiLCJpIjoiN0txMiJ9"
          }
        },
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ]
      },
      "ImportIssue": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^imp_[0-9A-Za-z]+$",
            "description": "Unique identifier of the import issue, prefixed with `imp_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "imp_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "import_issue"
            ],
            "description": "Names the type of this object. Always `import_issue`."
          },
          "channel": {
            "$ref": "#/components/schemas/ImportChannel"
          },
          "channel_order_id": {
            "type": "string",
            "description": "The order id in the sales channel.",
            "example": "193554871"
          },
          "channel_order_number": {
            "type": [
              "string",
              "null"
            ],
            "description": "The order number your customer sees in the sales channel.",
            "example": "3002"
          },
          "ordered_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the customer placed the order in the sales channel.",
            "example": "2026-04-21T07:34:06Z"
          },
          "reason": {
            "$ref": "#/components/schemas/ImportIssueReason"
          },
          "message": {
            "type": "string",
            "description": "What went wrong and how to fix it, ready to show to a person."
          },
          "unmatched_items": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "sku": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "The SKU as it arrived from the channel.",
                  "example": "PR-TRACKERV2-465KG"
                },
                "name": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "The item name in the channel."
                },
                "quantity": {
                  "type": "integer",
                  "minimum": 1,
                  "description": "Units of this item in the channel order."
                }
              },
              "required": [
                "sku",
                "name",
                "quantity"
              ]
            },
            "description": "The items that matched none of your variants. Filled for `unknown_sku` only; empty for the other reasons."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the import failed.",
            "example": "2026-04-21T07:34:06Z"
          }
        },
        "required": [
          "id",
          "object",
          "channel",
          "channel_order_id",
          "channel_order_number",
          "ordered_at",
          "reason",
          "message",
          "unmatched_items",
          "created_at"
        ],
        "description": "An order from a connected sales channel that could not be imported, and why. Retry or dismiss it from the dashboard; once the order imports, the issue disappears from this list."
      },
      "ImportChannel": {
        "type": "string",
        "enum": [
          "shipstation"
        ],
        "description": "The sales channel the order came from. Only `shipstation` today; `shopify` joins when the Shopify integration ships."
      },
      "ImportIssueReason": {
        "type": "string",
        "enum": [
          "unknown_sku",
          "no_items",
          "variant_not_found",
          "multiple_sellers"
        ],
        "description": "Why the order could not be imported:\n- `unknown_sku`: at least one item has a SKU that matches none of your variants. Give a variant that SKU, then import the order again.\n- `no_items`: the order arrived with no items to make, or only lines such as shipping or discounts. Add the products in the sales channel, then import it again.\n- `variant_not_found`: an item's SKU is linked to a variant that no longer exists. Link the SKU to a current variant, then import the order again.\n- `multiple_sellers`: the order's SKUs are linked to products of more than one seller, and an order is made for one seller only. Link every SKU to your own variants or split the order, then import it again."
      },
      "ReviewQcTicketRequest": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "oneOf": [
                {
                  "type": "object",
                  "properties": {
                    "item": {
                      "type": "string",
                      "pattern": "^qci_[0-9A-Za-z]+$",
                      "description": "Unique identifier of the qc item, prefixed with `qci_`. Treat it as an opaque string: store it and compare it, never parse it.",
                      "example": "qci_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                    },
                    "verdict": {
                      "type": "string",
                      "enum": [
                        "accept"
                      ],
                      "description": "`accept`: the product is fine as photographed."
                    },
                    "note": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "maxLength": 2000,
                      "description": "Optional note for the production partner, kept in the history."
                    }
                  },
                  "required": [
                    "item",
                    "verdict"
                  ]
                },
                {
                  "type": "object",
                  "properties": {
                    "item": {
                      "type": "string",
                      "pattern": "^qci_[0-9A-Za-z]+$",
                      "description": "Unique identifier of the qc item, prefixed with `qci_`. Treat it as an opaque string: store it and compare it, never parse it.",
                      "example": "qci_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                    },
                    "verdict": {
                      "type": "string",
                      "enum": [
                        "refuse"
                      ],
                      "description": "`refuse`: the product is not acceptable. 3DAPI rules on the refusal."
                    },
                    "reason": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 4000,
                      "description": "What is wrong and what to change. Required: 3DAPI rules on it and the partner reprints from it."
                    },
                    "photos": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "pattern": "^qcp_[0-9A-Za-z]+$",
                        "description": "Unique identifier of the qc photo, prefixed with `qcp_`. Treat it as an opaque string: store it and compare it, never parse it.",
                        "example": "qcp_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                      },
                      "maxItems": 50,
                      "default": [],
                      "description": "The photos that show the problem: `qcp_` ids of this product's photos from its current round."
                    }
                  },
                  "required": [
                    "item",
                    "verdict",
                    "reason"
                  ]
                }
              ]
            },
            "minItems": 1,
            "maxItems": 100,
            "description": "One verdict per product you are ruling on."
          }
        },
        "required": [
          "items"
        ]
      },
      "SendQcMessageRequest": {
        "type": "object",
        "properties": {
          "body": {
            "type": "string",
            "minLength": 1,
            "maxLength": 5000,
            "description": "Your answer to the production partner, up to 5000 characters. Kept in the item history as `answered`."
          },
          "photos": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UploadReference"
            },
            "maxItems": 10,
            "description": "Photos to attach, uploaded first with purpose `qc_photo`."
          }
        },
        "required": [
          "body"
        ]
      },
      "CreateQcTicketRequest": {
        "type": "object",
        "properties": {
          "order": {
            "type": "string",
            "pattern": "^ord_[0-9A-Za-z]+$",
            "description": "The delivered order the issue is about. It must be `completed`.",
            "example": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "items": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "order_item": {
                  "type": "string",
                  "pattern": "^itm_[0-9A-Za-z]+$",
                  "description": "Unique identifier of the order item, prefixed with `itm_`. Treat it as an opaque string: store it and compare it, never parse it.",
                  "example": "itm_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                },
                "quantity": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 1000,
                  "description": "Units to replace, at most the quantity ordered on that item (more is refused with `quantity_exceeds_ordered`)."
                }
              },
              "required": [
                "order_item",
                "quantity"
              ]
            },
            "minItems": 1,
            "maxItems": 50,
            "description": "What came back wrong and needs replacing."
          },
          "message": {
            "type": "string",
            "minLength": 1,
            "maxLength": 5000,
            "description": "What happened, for 3DAPI to rule on."
          },
          "photos": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UploadReference"
            },
            "maxItems": 10,
            "description": "Photos to attach, uploaded first with purpose `qc_photo`."
          }
        },
        "required": [
          "order",
          "items",
          "message"
        ]
      },
      "QcTicketList": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ],
            "description": "Names the type of this object. Always `list`."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/QcTicketSummary"
            },
            "description": "The page of results."
          },
          "has_more": {
            "type": "boolean",
            "description": "True when more results exist after this page. Fetch them by passing `next_cursor` as `cursor`."
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque cursor for the next page, or null on the last page. Pass it back unchanged as `cursor`.",
            "example": "eyJjIjoiMjAyNi0wNC0yMVQwNzozNDowNloiLCJpIjoiN0txMiJ9"
          }
        },
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ]
      },
      "QcTicketSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^qct_[0-9A-Za-z]+$",
            "description": "Unique identifier of the qc ticket, prefixed with `qct_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "qct_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "qc_ticket"
            ],
            "description": "Names the type of this object. Always `qc_ticket`."
          },
          "kind": {
            "$ref": "#/components/schemas/QcTicketKind"
          },
          "reason": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "onboarding",
              "spot_check",
              "capability",
              "suspension",
              null
            ],
            "description": "Why a quality check was raised. Null for questions and issue reports."
          },
          "status": {
            "$ref": "#/components/schemas/QcTicketStatus"
          },
          "needs_your_action": {
            "type": "boolean",
            "description": "True when the next step is yours: photos are waiting for your review, or a question is waiting for your answer."
          },
          "blocks_shipping": {
            "type": "boolean",
            "description": "True when the order cannot ship until this ticket is approved. Questions and issue reports never block."
          },
          "order": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "pattern": "^ord_[0-9A-Za-z]+$",
                "description": "Unique identifier of the order, prefixed with `ord_`. Treat it as an opaque string: store it and compare it, never parse it.",
                "example": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
              },
              "object": {
                "type": "string",
                "enum": [
                  "order"
                ],
                "description": "Names the type of this object. Always `order`."
              },
              "name": {
                "type": "string",
                "description": "Display name of the order."
              }
            },
            "required": [
              "id",
              "object",
              "name"
            ],
            "description": "The order this ticket is about."
          },
          "round": {
            "type": "integer",
            "minimum": 1,
            "description": "Current round of photos."
          },
          "replacement": {
            "$ref": "#/components/schemas/QcReplacement"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Point in time, ISO 8601 in UTC.",
            "example": "2026-04-21T07:34:06Z"
          },
          "submitted_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the production partner last submitted photos.",
            "example": "2026-04-21T07:34:06Z"
          },
          "resolved_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the ticket was approved, closed or cancelled.",
            "example": "2026-04-21T07:34:06Z"
          }
        },
        "required": [
          "id",
          "object",
          "kind",
          "reason",
          "status",
          "needs_your_action",
          "blocks_shipping",
          "order",
          "round",
          "replacement",
          "created_at",
          "submitted_at",
          "resolved_at"
        ],
        "description": "A QC ticket as listed. Get the ticket for its products and photos."
      },
      "StockList": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ],
            "description": "Names the type of this object. Always `list`."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StockSummary"
            },
            "description": "The page of results."
          },
          "has_more": {
            "type": "boolean",
            "description": "True when more results exist after this page. Fetch them by passing `next_cursor` as `cursor`."
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque cursor for the next page, or null on the last page. Pass it back unchanged as `cursor`.",
            "example": "eyJjIjoiMjAyNi0wNC0yMVQwNzozNDowNloiLCJpIjoiN0txMiJ9"
          }
        },
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ]
      },
      "StockSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^stk_[0-9A-Za-z]+$",
            "description": "Unique identifier of the stock, prefixed with `stk_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "stk_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "stock"
            ],
            "description": "Names the type of this object. Always `stock`."
          },
          "name": {
            "type": "string",
            "description": "Display name.",
            "example": "Clicker 50 pack"
          },
          "review": {
            "$ref": "#/components/schemas/Review"
          },
          "units_per_batch": {
            "type": "integer",
            "minimum": 0,
            "description": "How many units one purchased batch contains.",
            "example": 50
          },
          "batch_price": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "amount": {
                "type": "integer",
                "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                "example": 5000
              },
              "currency": {
                "type": "string",
                "pattern": "^[a-z]{3}$",
                "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                "example": "usd"
              }
            },
            "required": [
              "amount",
              "currency"
            ],
            "description": "What you pay for one batch, the same in every region. Set by 3DAPI when the stock is approved; null until then."
          },
          "unit_price": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "amount": {
                "type": "integer",
                "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                "example": 5000
              },
              "currency": {
                "type": "string",
                "pattern": "^[a-z]{3}$",
                "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                "example": "usd"
              }
            },
            "required": [
              "amount",
              "currency"
            ],
            "description": "What one unit costs you: `batch_price` divided by `units_per_batch`. Null until the stock is priced."
          },
          "images": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Image"
            },
            "description": "Photos of the stock."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Point in time, ISO 8601 in UTC.",
            "example": "2026-04-21T07:34:06Z"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "Point in time, ISO 8601 in UTC.",
            "example": "2026-04-21T07:34:06Z"
          },
          "inventory": {
            "type": "object",
            "properties": {
              "available": {
                "type": "integer",
                "minimum": 0,
                "description": "Units made and ready to ship, across regions."
              },
              "in_production": {
                "type": "integer",
                "minimum": 0,
                "description": "Units paid for and not yet made, across regions."
              }
            },
            "required": [
              "available",
              "in_production"
            ],
            "description": "Units across every batch you bought."
          }
        },
        "required": [
          "id",
          "object",
          "name",
          "review",
          "units_per_batch",
          "batch_price",
          "unit_price",
          "images",
          "created_at",
          "updated_at",
          "inventory"
        ],
        "description": "A stock as listed. Get the stock for its contents, inventory per region and restock rules."
      },
      "StockBatchList": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ],
            "description": "Names the type of this object. Always `list`."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StockBatch"
            },
            "description": "The page of results."
          },
          "has_more": {
            "type": "boolean",
            "description": "True when more results exist after this page. Fetch them by passing `next_cursor` as `cursor`."
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque cursor for the next page, or null on the last page. Pass it back unchanged as `cursor`.",
            "example": "eyJjIjoiMjAyNi0wNC0yMVQwNzozNDowNloiLCJpIjoiN0txMiJ9"
          }
        },
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ]
      },
      "StockBatchUsageList": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ],
            "description": "Names the type of this object. Always `list`."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StockBatchUsage"
            },
            "description": "The page of results."
          },
          "has_more": {
            "type": "boolean",
            "description": "True when more results exist after this page. Fetch them by passing `next_cursor` as `cursor`."
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque cursor for the next page, or null on the last page. Pass it back unchanged as `cursor`.",
            "example": "eyJjIjoiMjAyNi0wNC0yMVQwNzozNDowNloiLCJpIjoiN0txMiJ9"
          }
        },
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ]
      },
      "StockBatchUsage": {
        "type": "object",
        "properties": {
          "order": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "pattern": "^ord_[0-9A-Za-z]+$",
                "description": "Unique identifier of the order, prefixed with `ord_`. Treat it as an opaque string: store it and compare it, never parse it.",
                "example": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
              },
              "object": {
                "type": "string",
                "enum": [
                  "order"
                ],
                "description": "Names the type of this object. Always `order`."
              },
              "name": {
                "type": "string",
                "description": "Display name of the order."
              }
            },
            "required": [
              "id",
              "object",
              "name"
            ],
            "description": "The order that used the units."
          },
          "variant": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "pattern": "^var_[0-9A-Za-z]+$",
                "description": "Unique identifier of the variant, prefixed with `var_`. Treat it as an opaque string: store it and compare it, never parse it.",
                "example": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
              },
              "object": {
                "type": "string",
                "enum": [
                  "variant"
                ],
                "description": "Names the type of this object. Always `variant`."
              },
              "name": {
                "type": "string",
                "description": "Display name of the variant."
              }
            },
            "required": [
              "id",
              "object",
              "name"
            ],
            "description": "The variant ordered."
          },
          "units": {
            "type": "integer",
            "minimum": 1,
            "description": "Units this order line took from the batch.",
            "example": 2
          },
          "used_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the order was placed.",
            "example": "2026-04-21T07:34:06Z"
          }
        },
        "required": [
          "order",
          "variant",
          "units",
          "used_at"
        ],
        "description": "Units an order line took from a batch."
      },
      "Region": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^reg_[0-9A-Za-z]+$",
            "description": "Unique identifier of the region, prefixed with `reg_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "reg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "region"
            ],
            "description": "Names the type of this object. Always `region`."
          },
          "name": {
            "type": "string",
            "description": "Display name, as set by the production partner.",
            "example": "Western Europe"
          },
          "countries": {
            "type": "array",
            "items": {
              "type": "string",
              "pattern": "^[A-Z]{2}$",
              "description": "ISO 3166-1 alpha-2 country code."
            },
            "description": "Countries orders can ship to from this region.",
            "example": [
              "DE",
              "FR",
              "NL"
            ]
          }
        },
        "required": [
          "id",
          "object",
          "name",
          "countries"
        ],
        "description": "A production region your stock is held in, and the countries it ships to. Orders use stock from a region that ships to the buyer."
      },
      "CreateStockRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "description": "Display name.",
            "example": "Clicker 50 pack"
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 5000,
            "description": "Longer description. Null for none.",
            "example": "Pre-assembled clickers, bagged."
          },
          "units_per_batch": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100000,
            "description": "How many units one batch contains.",
            "example": 50
          },
          "extras": {
            "type": "array",
            "items": {
              "type": "string",
              "pattern": "^xtr_[0-9A-Za-z]+$",
              "description": "Unique identifier of the extra, prefixed with `xtr_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "xtr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "maxItems": 50,
            "description": "Your extras packed into every unit. Each counts once per unit. An extra still pending review is accepted and is reviewed with the stock; a rejected one is refused (`extra_not_approved`)."
          },
          "manufacturing": {
            "type": "object",
            "properties": {
              "assembly_required": {
                "type": "boolean",
                "description": "Whether the printed parts must be assembled before shipping. `false` when `manufacturing` is omitted on create."
              },
              "farm_instructions": {
                "type": [
                  "string",
                  "null"
                ],
                "maxLength": 5000,
                "description": "Notes for the production partner, for example packing steps. Omitted keeps the current notes; null or blank clears them. A change sends the stock back to review."
              }
            },
            "required": [
              "assembly_required"
            ],
            "description": "How the stock is produced. Applies to every design."
          },
          "images": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ImageInput"
            },
            "maxItems": 20,
            "description": "Stock photos, in display order. On create, every entry is a new `upload`. On update the list replaces the current images: entries not sent are deleted, and kept ones use the `url` the stock returns."
          },
          "attachments": {
            "type": "array",
            "items": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/UploadReference"
                },
                {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "pattern": "^att_[0-9A-Za-z]+$",
                      "description": "An attachment of this stock to keep.",
                      "example": "att_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                    }
                  },
                  "required": [
                    "id"
                  ]
                }
              ]
            },
            "maxItems": 20,
            "description": "Production documents for the farm: new (`upload`, purpose `attachment`) or kept (`id`, update only). On update the list replaces the current attachments."
          },
          "designs": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "file": {
                  "type": "object",
                  "properties": {
                    "upload": {
                      "type": "string",
                      "pattern": "^upl_[0-9A-Za-z]+$",
                      "description": "An upload id from `POST /v2/uploads`, used before it expires. The file is checked when used: one that is not what its name and type say is refused with 422 `invalid_file` (the expected type in `details.expected`) and must be uploaded again.",
                      "example": "upl_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                    }
                  },
                  "required": [
                    "upload"
                  ],
                  "description": "The model file, uploaded with purpose `design_file`."
                },
                "name": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "minLength": 1,
                  "maxLength": 200,
                  "description": "Display name. The file name when omitted."
                },
                "materials": {
                  "$ref": "#/components/schemas/DesignMaterialsInput"
                }
              },
              "required": [
                "file",
                "materials"
              ]
            },
            "minItems": 1,
            "maxItems": 50,
            "description": "The model files of the stock. Add more later with `add_stock_design`, which sends the stock back to review."
          }
        },
        "required": [
          "name",
          "units_per_batch",
          "designs"
        ]
      },
      "UpdateStockRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "description": "Display name.",
            "example": "Clicker 50 pack"
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 5000,
            "description": "Longer description. Null for none.",
            "example": "Pre-assembled clickers, bagged."
          },
          "units_per_batch": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100000,
            "description": "How many units one batch contains.",
            "example": 50
          },
          "extras": {
            "type": "array",
            "items": {
              "type": "string",
              "pattern": "^xtr_[0-9A-Za-z]+$",
              "description": "Unique identifier of the extra, prefixed with `xtr_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "xtr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "maxItems": 50,
            "description": "Your extras packed into every unit. Each counts once per unit. An extra still pending review is accepted and is reviewed with the stock; a rejected one is refused (`extra_not_approved`)."
          },
          "manufacturing": {
            "type": "object",
            "properties": {
              "assembly_required": {
                "type": "boolean",
                "description": "Whether the printed parts must be assembled before shipping. `false` when `manufacturing` is omitted on create."
              },
              "farm_instructions": {
                "type": [
                  "string",
                  "null"
                ],
                "maxLength": 5000,
                "description": "Notes for the production partner, for example packing steps. Omitted keeps the current notes; null or blank clears them. A change sends the stock back to review."
              }
            },
            "required": [
              "assembly_required"
            ],
            "description": "How the stock is produced. Applies to every design."
          },
          "images": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ImageInput"
            },
            "maxItems": 20,
            "description": "Stock photos, in display order. On create, every entry is a new `upload`. On update the list replaces the current images: entries not sent are deleted, and kept ones use the `url` the stock returns."
          },
          "attachments": {
            "type": "array",
            "items": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/UploadReference"
                },
                {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "pattern": "^att_[0-9A-Za-z]+$",
                      "description": "An attachment of this stock to keep.",
                      "example": "att_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                    }
                  },
                  "required": [
                    "id"
                  ]
                }
              ]
            },
            "maxItems": 20,
            "description": "Production documents for the farm: new (`upload`, purpose `attachment`) or kept (`id`, update only). On update the list replaces the current attachments."
          }
        },
        "description": "Only the fields you send change. Arrays replace the whole list."
      },
      "StockPurchase": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "stock_purchase"
            ],
            "description": "Names the type of this object. Always `stock_purchase`."
          },
          "purchase_reference": {
            "type": "string",
            "description": "Reference shared by every batch bought in this checkout.",
            "example": "ST-8KD2X"
          },
          "total": {
            "type": "object",
            "properties": {
              "amount": {
                "type": "integer",
                "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                "example": 5000
              },
              "currency": {
                "type": "string",
                "pattern": "^[a-z]{3}$",
                "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                "example": "usd"
              }
            },
            "required": [
              "amount",
              "currency"
            ],
            "description": "What was taken from your wallet."
          },
          "batches": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StockBatch"
            },
            "description": "The batches bought, one per item."
          }
        },
        "required": [
          "object",
          "purchase_reference",
          "total",
          "batches"
        ],
        "description": "One checkout of stock batches."
      },
      "CreateStockPurchaseRequest": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "stock": {
                  "type": "string",
                  "pattern": "^stk_[0-9A-Za-z]+$",
                  "description": "An approved stock of yours.",
                  "example": "stk_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                },
                "region": {
                  "type": "string",
                  "pattern": "^reg_[0-9A-Za-z]+$",
                  "description": "One of the stock regions, from the stock `restock` list.",
                  "example": "reg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                },
                "batches": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 1000,
                  "description": "How many batches to buy for this region, each of `units_per_batch` units at the stock `batch_price`.",
                  "example": 2
                }
              },
              "required": [
                "stock",
                "region",
                "batches"
              ]
            },
            "minItems": 1,
            "maxItems": 50,
            "description": "What to buy. Each stock and region pair at most once."
          }
        },
        "required": [
          "items"
        ]
      },
      "SetRestockRequest": {
        "type": "object",
        "properties": {
          "mode": {
            "type": "string",
            "enum": [
              "off",
              "notify",
              "auto_buy"
            ],
            "description": "`off` does nothing, `notify` emails you when the region runs low, `auto_buy` buys `reorder_units` from your wallet (when auto-reorder is on in your wallet settings; otherwise it emails)."
          },
          "when_available_below": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "maximum": 100000,
            "description": "Threshold in units. The rule is checked each time an order draws units from this stock (when the order is assigned for production), not continuously. It counts the units paid for minus the units used in this region, across every batch and including units still in production (so not `available`), and triggers when that count is at or below this threshold. Required unless `mode` is `off`.",
            "example": 10
          },
          "reorder_units": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "maximum": 100000,
            "description": "Units to buy per automatic purchase, returned as set. Purchases round up to whole batches: an automatic purchase buys `reorder_units` divided by `units_per_batch`, rounded up (at least one batch), at `batch_price` each, and the batch it creates holds exactly those batches times `units_per_batch` units. A multiple of `units_per_batch` buys exactly what you ask for. Required for `auto_buy`.",
            "example": 50
          }
        },
        "required": [
          "mode"
        ]
      },
      "CreateExtraRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "description": "Display name of the part.",
            "example": "Magnet 10mm"
          },
          "description": {
            "type": "string",
            "minLength": 1,
            "maxLength": 5000,
            "description": "What the part is, for the production partner and our review.",
            "example": "Neodymium magnet, 10mm diameter."
          },
          "purchase_links": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "url": {
                  "type": "string",
                  "maxLength": 2048,
                  "format": "uri",
                  "description": "Where the part can be bought: an http:// or https:// URL. Any other link is refused with `validation_failed`."
                },
                "label": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "maxLength": 80,
                  "description": "Short label for the link, for example `Datasheet`."
                }
              },
              "required": [
                "url"
              ]
            },
            "minItems": 1,
            "maxItems": 20,
            "description": "Where the production partner can buy the part. At least one."
          },
          "images": {
            "type": "array",
            "items": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/UploadReference"
                },
                {
                  "$ref": "#/components/schemas/ExistingImageReference"
                }
              ],
              "description": "A new photo, as `{ \"upload\": \"upl_...\" }` with purpose `extra_image`, or a photo the extra already has, as `{ \"url\": \"...\" }`."
            },
            "minItems": 1,
            "maxItems": 10,
            "description": "Photos of the part, at least one. Replaces the whole list on update."
          }
        },
        "required": [
          "name",
          "description",
          "purchase_links",
          "images"
        ]
      },
      "ExistingImageReference": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "description": "The URL of a photo the extra already has, to keep it."
          }
        },
        "required": [
          "url"
        ]
      },
      "UpdateExtraRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "description": "Display name of the part.",
            "example": "Magnet 10mm"
          },
          "description": {
            "type": "string",
            "minLength": 1,
            "maxLength": 5000,
            "description": "What the part is, for the production partner and our review.",
            "example": "Neodymium magnet, 10mm diameter."
          },
          "purchase_links": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "url": {
                  "type": "string",
                  "maxLength": 2048,
                  "format": "uri",
                  "description": "Where the part can be bought: an http:// or https:// URL. Any other link is refused with `validation_failed`."
                },
                "label": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "maxLength": 80,
                  "description": "Short label for the link, for example `Datasheet`."
                }
              },
              "required": [
                "url"
              ]
            },
            "minItems": 1,
            "maxItems": 20,
            "description": "Where the production partner can buy the part. At least one."
          },
          "images": {
            "type": "array",
            "items": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/UploadReference"
                },
                {
                  "$ref": "#/components/schemas/ExistingImageReference"
                }
              ],
              "description": "A new photo, as `{ \"upload\": \"upl_...\" }` with purpose `extra_image`, or a photo the extra already has, as `{ \"url\": \"...\" }`."
            },
            "minItems": 1,
            "maxItems": 10,
            "description": "Photos of the part, at least one. Replaces the whole list on update."
          }
        },
        "description": "Only the fields you send change. Arrays replace the whole list."
      },
      "ExtraList": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ],
            "description": "Names the type of this object. Always `list`."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Extra"
            },
            "description": "The page of results."
          },
          "has_more": {
            "type": "boolean",
            "description": "True when more results exist after this page. Fetch them by passing `next_cursor` as `cursor`."
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque cursor for the next page, or null on the last page. Pass it back unchanged as `cursor`.",
            "example": "eyJjIjoiMjAyNi0wNC0yMVQwNzozNDowNloiLCJpIjoiN0txMiJ9"
          }
        },
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ]
      },
      "Wallet": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "wallet"
            ],
            "description": "Names the type of this object. Always `wallet`."
          },
          "balance": {
            "type": "object",
            "properties": {
              "amount": {
                "type": "integer",
                "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                "example": 5000
              },
              "currency": {
                "type": "string",
                "pattern": "^[a-z]{3}$",
                "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                "example": "usd"
              }
            },
            "required": [
              "amount",
              "currency"
            ],
            "description": "What you can spend: manual, imported and API orders and stock purchases are paid from it."
          },
          "sample_credit": {
            "type": "object",
            "properties": {
              "amount": {
                "type": "integer",
                "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                "example": 5000
              },
              "currency": {
                "type": "string",
                "pattern": "^[a-z]{3}$",
                "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                "example": "usd"
              }
            },
            "required": [
              "amount",
              "currency"
            ],
            "description": "Credit for sample orders. Spent before the balance on eligible orders; cannot be withdrawn. Always 0 for integrator accounts."
          },
          "pending_withdrawals": {
            "type": "object",
            "properties": {
              "amount": {
                "type": "integer",
                "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                "example": 5000
              },
              "currency": {
                "type": "string",
                "pattern": "^[a-z]{3}$",
                "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                "example": "usd"
              }
            },
            "required": [
              "amount",
              "currency"
            ],
            "description": "Withdrawals you requested that are not paid out yet. Already taken off `balance`."
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the balance last changed.",
            "example": "2026-04-21T07:34:06Z"
          }
        },
        "required": [
          "object",
          "balance",
          "sample_credit",
          "pending_withdrawals",
          "updated_at"
        ],
        "description": "Your 3DAPI balance. Top-ups, withdrawals and wallet settings are managed in the dashboard."
      },
      "TransactionList": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ],
            "description": "Names the type of this object. Always `list`."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Transaction"
            },
            "description": "The page of results."
          },
          "has_more": {
            "type": "boolean",
            "description": "True when more results exist after this page. Fetch them by passing `next_cursor` as `cursor`."
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque cursor for the next page, or null on the last page. Pass it back unchanged as `cursor`.",
            "example": "eyJjIjoiMjAyNi0wNC0yMVQwNzozNDowNloiLCJpIjoiN0txMiJ9"
          }
        },
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ]
      },
      "Transaction": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^txn_[0-9A-Za-z]+$",
            "description": "Unique identifier of the transaction, prefixed with `txn_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "txn_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "transaction"
            ],
            "description": "Names the type of this object. Always `transaction`."
          },
          "direction": {
            "$ref": "#/components/schemas/TransactionDirection"
          },
          "category": {
            "$ref": "#/components/schemas/TransactionCategory"
          },
          "adjustment_reason": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "support_credit",
              "support_debit",
              "shipping_discount",
              "warehousing_charge",
              "replacement_order_charge",
              null
            ],
            "description": "Why the adjustment happened, when `category` is `adjustment`; null for every other category."
          },
          "paid_with": {
            "$ref": "#/components/schemas/TransactionPaidWith"
          },
          "status": {
            "$ref": "#/components/schemas/TransactionStatus"
          },
          "amount": {
            "type": "object",
            "properties": {
              "amount": {
                "type": "integer",
                "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                "example": 5000
              },
              "currency": {
                "type": "string",
                "pattern": "^[a-z]{3}$",
                "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                "example": "usd"
              }
            },
            "required": [
              "amount",
              "currency"
            ],
            "description": "Amount moved, VAT included. Always positive. On a `top_up` this is what your card was charged; your balance grows by the net amount (`amount` minus `vat.amount`), so top-up amounts do not add up to your balance."
          },
          "vat": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "amount": {
                "type": "object",
                "properties": {
                  "amount": {
                    "type": "integer",
                    "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                    "example": 5000
                  },
                  "currency": {
                    "type": "string",
                    "pattern": "^[a-z]{3}$",
                    "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                    "example": "usd"
                  }
                },
                "required": [
                  "amount",
                  "currency"
                ],
                "description": "An amount of money. Amounts are integers in the smallest currency unit, so they never need decimal parsing."
              },
              "rate": {
                "type": "number",
                "minimum": 0,
                "maximum": 1,
                "description": "VAT rate as a fraction, for example 0.21."
              }
            },
            "required": [
              "amount",
              "rate"
            ],
            "description": "VAT included in `amount`, when any was charged."
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "description": "What happened, ready to show to a person.",
            "example": "ShipStation order SSG2M9S9"
          },
          "orders": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OrderReference"
            },
            "description": "The orders this transaction paid for or came from."
          },
          "invoice_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "Invoice or receipt for card charges and top-ups, when one exists."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Point in time, ISO 8601 in UTC.",
            "example": "2026-04-21T07:34:06Z"
          }
        },
        "required": [
          "id",
          "object",
          "direction",
          "category",
          "adjustment_reason",
          "paid_with",
          "status",
          "amount",
          "vat",
          "description",
          "orders",
          "invoice_url",
          "created_at"
        ],
        "description": "One movement of your money for orders, stock or the wallet."
      },
      "TransactionDirection": {
        "type": "string",
        "enum": [
          "credit",
          "debit"
        ],
        "description": "`credit` added money for you, `debit` took it. Amounts are always positive; this gives the sign."
      },
      "TransactionCategory": {
        "type": "string",
        "enum": [
          "order_payment",
          "stock_purchase",
          "top_up",
          "earnings",
          "refund",
          "withdrawal",
          "adjustment"
        ],
        "description": "What the money was for:\n- `order_payment`: you paid for a manual, imported or API order, from the wallet or by card.\n- `stock_purchase`: you bought stock batches, including automatic reorders.\n- `top_up`: you added money to the wallet by card, manually, automatically or to cover a shortfall.\n- `earnings`: your earnings from a completed storefront order or, for integrators, a completed checkout order. A `debit` when the order's sales did not cover manufacturing, the platform fee and the card fee: the difference is charged to your wallet at completion.\n- `refund`: an order refund returned to your wallet.\n- `withdrawal`: money you withdrew from the wallet.\n- `adjustment`: a credit or debit that is not one of the above. `adjustment_reason` says which kind, and `description` explains it in words."
      },
      "TransactionPaidWith": {
        "type": "string",
        "enum": [
          "wallet",
          "card"
        ],
        "description": "`wallet` when the money moved through your balance, `card` when a card was charged directly (no-profit orders paid at checkout). Filter on `wallet` to reconcile with the balance."
      },
      "TransactionStatus": {
        "type": "string",
        "enum": [
          "pending",
          "completed"
        ],
        "description": "`pending` only for withdrawals requested and not paid out yet; everything else is `completed`."
      },
      "OrderReference": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^ord_[0-9A-Za-z]+$",
            "description": "Unique identifier of the order, prefixed with `ord_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "order"
            ],
            "description": "Names the type of this object. Always `order`."
          },
          "name": {
            "type": "string",
            "description": "Display name of the order."
          }
        },
        "required": [
          "id",
          "object",
          "name"
        ],
        "description": "Reference to a order. Pass the matching `expand[]` path to receive the full order instead."
      },
      "TransactionSummary": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "transaction_summary"
            ],
            "description": "Names the type of this object. Always `transaction_summary`."
          },
          "created_after": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Start of the period applied: as requested, or 30 days before the request when neither created_after nor created_before was given. Null for no lower bound.",
            "example": "2026-04-21T07:34:06Z"
          },
          "created_before": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "End of the period, as requested. Null for no upper bound.",
            "example": "2026-04-21T07:34:06Z"
          },
          "credits": {
            "type": "object",
            "properties": {
              "amount": {
                "type": "integer",
                "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                "example": 5000
              },
              "currency": {
                "type": "string",
                "pattern": "^[a-z]{3}$",
                "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                "example": "usd"
              }
            },
            "required": [
              "amount",
              "currency"
            ],
            "description": "Total credited in the period."
          },
          "debits": {
            "type": "object",
            "properties": {
              "amount": {
                "type": "integer",
                "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                "example": 5000
              },
              "currency": {
                "type": "string",
                "pattern": "^[a-z]{3}$",
                "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                "example": "usd"
              }
            },
            "required": [
              "amount",
              "currency"
            ],
            "description": "Total debited in the period."
          },
          "net": {
            "type": "object",
            "properties": {
              "amount": {
                "type": "integer",
                "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                "example": 5000
              },
              "currency": {
                "type": "string",
                "pattern": "^[a-z]{3}$",
                "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                "example": "usd"
              }
            },
            "required": [
              "amount",
              "currency"
            ],
            "description": "`credits` minus `debits`. Negative when you spent more than came in."
          },
          "by_category": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "category": {
                  "$ref": "#/components/schemas/TransactionCategory"
                },
                "direction": {
                  "$ref": "#/components/schemas/TransactionDirection"
                },
                "count": {
                  "type": "integer",
                  "minimum": 0,
                  "description": "How many completed transactions of this category and direction the period holds.",
                  "example": 31
                },
                "total": {
                  "type": "object",
                  "properties": {
                    "amount": {
                      "type": "integer",
                      "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                      "example": 5000
                    },
                    "currency": {
                      "type": "string",
                      "pattern": "^[a-z]{3}$",
                      "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                      "example": "usd"
                    }
                  },
                  "required": [
                    "amount",
                    "currency"
                  ],
                  "description": "An amount of money. Amounts are integers in the smallest currency unit, so they never need decimal parsing."
                }
              },
              "required": [
                "category",
                "direction",
                "count",
                "total"
              ]
            },
            "description": "Totals per category and direction. Categories with no transactions are left out."
          }
        },
        "required": [
          "object",
          "created_after",
          "created_before",
          "credits",
          "debits",
          "net",
          "by_category"
        ],
        "description": "Totals of your transactions over a period, completed ones only."
      },
      "BillingDetails": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "billing_details"
            ],
            "description": "Names the type of this object. Always `billing_details`."
          },
          "legal_name": {
            "type": "string",
            "description": "Your company’s registered legal name, as it appears on invoices.",
            "example": "Acme Prints Ltd"
          },
          "country": {
            "type": "string",
            "minLength": 2,
            "maxLength": 2,
            "description": "Country of registration, ISO 3166-1 alpha-2. It decides the VAT on top-ups and which of 3DAPI’s payment accounts you pay.",
            "example": "DE"
          },
          "address_line1": {
            "type": "string",
            "description": "First line of the registered address.",
            "example": "Hauptstrasse 12"
          },
          "address_line2": {
            "type": [
              "string",
              "null"
            ],
            "description": "Second line of the address. Null when there is none.",
            "example": null
          },
          "city": {
            "type": "string",
            "description": "City of the registered address.",
            "example": "Berlin"
          },
          "postal_code": {
            "type": "string",
            "description": "Postal or ZIP code of the registered address.",
            "example": "10115"
          },
          "vat_number": {
            "type": [
              "string",
              "null"
            ],
            "description": "Your VAT number (EU) or VAT or tax number (UK), or null.",
            "example": "DE123456789"
          },
          "vat_status": {
            "$ref": "#/components/schemas/VatStatus"
          },
          "required_tax_form": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "w9",
              "w8ben_e",
              null
            ],
            "description": "The IRS form your country needs with every save: `w9` for US businesses, `w8ben_e` for businesses outside the US, EU and UK. Null otherwise."
          },
          "tax_form": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "kind": {
                "$ref": "#/components/schemas/TaxFormKind"
              },
              "submitted_at": {
                "type": "string",
                "format": "date-time",
                "description": "Point in time, ISO 8601 in UTC.",
                "example": "2026-04-21T07:34:06Z"
              }
            },
            "required": [
              "kind",
              "submitted_at"
            ],
            "description": "The latest tax form on file. Its tax ids are never returned."
          },
          "email": {
            "type": [
              "string",
              "null"
            ],
            "description": "Where invoices are addressed: your account email.",
            "example": "billing@acme.example"
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Point in time, ISO 8601 in UTC.",
            "example": "2026-04-21T07:34:06Z"
          }
        },
        "required": [
          "object",
          "legal_name",
          "country",
          "address_line1",
          "address_line2",
          "city",
          "postal_code",
          "vat_number",
          "vat_status",
          "required_tax_form",
          "tax_form",
          "email",
          "updated_at"
        ],
        "description": "Who invoices are made out to. Needed before your first top-up: the country and VAT number decide the VAT added to top-ups."
      },
      "VatStatus": {
        "type": "string",
        "enum": [
          "none",
          "validated",
          "in_review",
          "invalid",
          "unverified",
          "not_checked"
        ],
        "description": "What 3DAPI knows about `vat_number`:\n- `none`: no number given.\n- `validated`: an EU VAT number VIES confirmed, or 3DAPI approved after a manual check. Top-ups carry no VAT (reverse charge).\n- `in_review`: VIES did not confirm the number, so 3DAPI is checking it by hand. Top-ups are charged VAT until it is approved.\n- `invalid`: 3DAPI rejected the number after checking it. Top-ups are charged VAT; check the number and save again.\n- `unverified`: VIES could not confirm the number and no manual check is open. Top-ups are charged VAT.\n- `not_checked`: a number from outside the EU, which today means a UK VAT or tax number, kept for your invoices. VAT follows your country."
      },
      "TaxFormKind": {
        "type": "string",
        "enum": [
          "w9",
          "w8ben_e"
        ],
        "description": "`w9`: IRS Form W-9 (US). `w8ben_e`: IRS Form W-8BEN-E (foreign entity)."
      },
      "SaveBillingDetailsRequest": {
        "type": "object",
        "properties": {
          "legal_name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "description": "Your company’s registered legal name, as it should appear on invoices."
          },
          "country": {
            "type": "string",
            "pattern": "^[A-Za-z]{2}$",
            "description": "ISO 3166-1 alpha-2."
          },
          "address_line1": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "description": "First line of the registered address."
          },
          "address_line2": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 200,
            "description": "Second line of the address. Omit or null for none."
          },
          "city": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "description": "City of the registered address."
          },
          "postal_code": {
            "type": "string",
            "minLength": 1,
            "maxLength": 20,
            "description": "Postal or ZIP code of the registered address."
          },
          "vat_number": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 50,
            "description": "EU VAT number (checked with VIES when you save; one VIES does not confirm goes to a manual check), or a UK VAT or tax number. Refused for US businesses and businesses outside the US, EU and UK, whose tax ids go on the W-9 or W-8BEN-E instead (`validation_failed`). Omit or null for none."
          },
          "w9": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "name": {
                "type": "string",
                "minLength": 1,
                "maxLength": 200,
                "description": "Your name as shown on your federal income tax return."
              },
              "business_name": {
                "type": [
                  "string",
                  "null"
                ],
                "maxLength": 200,
                "description": "Business name or disregarded entity name, when it differs from `name`. Omit or null for none."
              },
              "federal_tax_classification": {
                "type": "string",
                "enum": [
                  "individual_sole_proprietor",
                  "c_corporation",
                  "s_corporation",
                  "partnership",
                  "trust_estate",
                  "llc",
                  "other"
                ],
                "description": "Your federal tax classification, as ticked on the form:\n- `individual_sole_proprietor`: individual, sole proprietor or single-member LLC.\n- `c_corporation`: C corporation.\n- `s_corporation`: S corporation.\n- `partnership`: partnership.\n- `trust_estate`: trust or estate.\n- `llc`: limited liability company; give its tax treatment in `llc_tax_classification`.\n- `other`: describe it in `other_classification`."
              },
              "llc_tax_classification": {
                "type": [
                  "string",
                  "null"
                ],
                "enum": [
                  "C",
                  "S",
                  "P",
                  null
                ],
                "description": "Only when `federal_tax_classification` is `llc`: how the LLC is taxed. `C`: C corporation, `S`: S corporation, `P`: partnership. Ignored for other classifications."
              },
              "other_classification": {
                "type": [
                  "string",
                  "null"
                ],
                "maxLength": 200,
                "description": "Only when `federal_tax_classification` is `other`: the classification, in your words. Ignored for other classifications."
              },
              "exempt_payee_code": {
                "type": [
                  "string",
                  "null"
                ],
                "maxLength": 50,
                "description": "Exempt payee code from the form’s exemptions box, if any. Omit or null for none."
              },
              "fatca_reporting_code": {
                "type": [
                  "string",
                  "null"
                ],
                "maxLength": 50,
                "description": "Exemption from FATCA reporting code, if any. Omit or null for none."
              },
              "address": {
                "type": "string",
                "minLength": 1,
                "maxLength": 500,
                "description": "Street address: number, street, and apartment or suite."
              },
              "city": {
                "type": "string",
                "minLength": 1,
                "maxLength": 200,
                "description": "City of `address`."
              },
              "state": {
                "type": "string",
                "minLength": 1,
                "maxLength": 100,
                "description": "State of `address`.",
                "example": "CA"
              },
              "zip_code": {
                "type": "string",
                "minLength": 1,
                "maxLength": 20,
                "description": "ZIP code of `address`.",
                "example": "94105"
              },
              "account_numbers": {
                "type": [
                  "string",
                  "null"
                ],
                "maxLength": 200,
                "description": "Account number(s) to list on the form, if any. Omit or null for none."
              },
              "tin": {
                "type": "string",
                "maxLength": 20,
                "description": "Your taxpayer identification number, of the kind in `tin_type`: exactly 9 digits. Dashes and spaces are allowed and removed before it is stored. Never returned by the API.",
                "example": "12-3456789"
              },
              "tin_type": {
                "type": "string",
                "enum": [
                  "ssn",
                  "ein"
                ],
                "description": "Which number `tin` is. `ssn`: Social Security Number. `ein`: Employer Identification Number."
              },
              "signature_name": {
                "type": "string",
                "minLength": 1,
                "maxLength": 200,
                "description": "Your full name, typed as your signature. The IP address and user agent of the request are recorded with the form."
              },
              "certified": {
                "type": "boolean",
                "enum": [
                  true
                ],
                "description": "You certify the form under penalties of perjury, as on the IRS form."
              }
            },
            "required": [
              "name",
              "federal_tax_classification",
              "address",
              "city",
              "state",
              "zip_code",
              "tin",
              "tin_type",
              "signature_name",
              "certified"
            ],
            "description": "Required for a US business, on every save."
          },
          "w8ben_e": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "name": {
                "type": "string",
                "minLength": 1,
                "maxLength": 200,
                "description": "Name of the organization that is the beneficial owner of the income."
              },
              "country_of_incorporation": {
                "type": "string",
                "minLength": 2,
                "maxLength": 2,
                "description": "Country where the organization is incorporated or organized, ISO 3166-1 alpha-2.",
                "example": "CA"
              },
              "disregarded_entity_name": {
                "type": [
                  "string",
                  "null"
                ],
                "maxLength": 200,
                "description": "Name of the disregarded entity, if one receives the payment. Omit or null for none."
              },
              "chapter3_status": {
                "type": "string",
                "enum": [
                  "corporation",
                  "disregarded_entity",
                  "partnership",
                  "simple_trust",
                  "grantor_trust",
                  "complex_trust",
                  "estate",
                  "government",
                  "central_bank",
                  "tax_exempt_org",
                  "private_foundation",
                  "international_org",
                  "other"
                ],
                "description": "Chapter 3 status (entity type), as on the dashboard form. `central_bank` is a central bank of issue.",
                "example": "corporation"
              },
              "chapter4_status": {
                "type": "string",
                "enum": [
                  "active_nffe",
                  "passive_nffe",
                  "participating_ffi",
                  "reporting_model1_ffi",
                  "reporting_model2_ffi",
                  "nonparticipating_ffi",
                  "exempt_beneficial_owner",
                  "certified_deemed_compliant_ffi",
                  "other"
                ],
                "description": "Chapter 4 (FATCA) status, as on the dashboard form. With `other`, describe it in `chapter4_status_other`.",
                "example": "active_nffe"
              },
              "chapter4_status_other": {
                "type": [
                  "string",
                  "null"
                ],
                "maxLength": 200,
                "description": "Only when `chapter4_status` is `other`: the FATCA status, in your words. Ignored otherwise."
              },
              "residence_address": {
                "type": "string",
                "minLength": 1,
                "maxLength": 500,
                "description": "Permanent residence address of the organization: street and number."
              },
              "residence_city": {
                "type": "string",
                "minLength": 1,
                "maxLength": 200,
                "description": "City of `residence_address`."
              },
              "residence_state": {
                "type": [
                  "string",
                  "null"
                ],
                "maxLength": 100,
                "description": "State or province of `residence_address`, if any. Omit or null for none."
              },
              "residence_postal_code": {
                "type": "string",
                "minLength": 1,
                "maxLength": 20,
                "description": "Postal code of `residence_address`."
              },
              "residence_country": {
                "type": "string",
                "minLength": 2,
                "maxLength": 2,
                "description": "Country of `residence_address`, ISO 3166-1 alpha-2.",
                "example": "CA"
              },
              "us_tin": {
                "type": [
                  "string",
                  "null"
                ],
                "maxLength": 20,
                "description": "US taxpayer identification number, if the form requires one for you. Omit or null for none. Never returned by the API."
              },
              "giin": {
                "type": [
                  "string",
                  "null"
                ],
                "maxLength": 30,
                "description": "Global Intermediary Identification Number (GIIN), if you have one. Omit or null for none."
              },
              "foreign_tin": {
                "type": [
                  "string",
                  "null"
                ],
                "maxLength": 30,
                "description": "Tax identifying number in your country of residence, if any. Omit or null for none. Never returned by the API."
              },
              "reference_number": {
                "type": [
                  "string",
                  "null"
                ],
                "maxLength": 100,
                "description": "Reference number(s) to show on the form, if any. Omit or null for none."
              },
              "treaty_country": {
                "type": [
                  "string",
                  "null"
                ],
                "maxLength": 2,
                "description": "Country, ISO 3166-1 alpha-2, whose income tax treaty with the US you claim benefits under. Omit or null when you claim none."
              },
              "signature_name": {
                "type": "string",
                "minLength": 1,
                "maxLength": 200,
                "description": "Full name of the person signing, typed as the signature. The IP address and user agent of the request are recorded with the form."
              },
              "capacity": {
                "type": [
                  "string",
                  "null"
                ],
                "maxLength": 200,
                "description": "Capacity in which the signer acts for the organization, if given.",
                "example": "Director"
              },
              "certified": {
                "type": "boolean",
                "enum": [
                  true
                ],
                "description": "You certify the form under penalties of perjury, as on the IRS form."
              }
            },
            "required": [
              "name",
              "country_of_incorporation",
              "chapter3_status",
              "chapter4_status",
              "residence_address",
              "residence_city",
              "residence_postal_code",
              "residence_country",
              "signature_name",
              "certified"
            ],
            "description": "Required for a business outside the US, EU and UK, on every save."
          }
        },
        "required": [
          "legal_name",
          "country",
          "address_line1",
          "city",
          "postal_code"
        ]
      },
      "CardList": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ],
            "description": "Names the type of this object. Always `list`."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Card"
            },
            "description": "The page of results."
          },
          "has_more": {
            "type": "boolean",
            "description": "True when more results exist after this page. Fetch them by passing `next_cursor` as `cursor`."
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque cursor for the next page, or null on the last page. Pass it back unchanged as `cursor`.",
            "example": "eyJjIjoiMjAyNi0wNC0yMVQwNzozNDowNloiLCJpIjoiN0txMiJ9"
          }
        },
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ]
      },
      "Card": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^crd_[0-9A-Za-z]+$",
            "description": "Unique identifier of the card, prefixed with `crd_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "crd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "card"
            ],
            "description": "Names the type of this object. Always `card`."
          },
          "brand": {
            "type": [
              "string",
              "null"
            ],
            "description": "Card brand as Stripe reports it, lowercase. Null when it is not known.",
            "example": "visa"
          },
          "last4": {
            "type": [
              "string",
              "null"
            ],
            "description": "Last 4 digits of the card number. Null when it is not known.",
            "example": "4242"
          },
          "exp_month": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Expiry month, 1 to 12. Null when it is not known.",
            "example": 12
          },
          "exp_year": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Expiry year, four digits. Null when it is not known.",
            "example": 2030
          },
          "is_default": {
            "type": "boolean",
            "description": "The card top-ups and auto top up charge. Exactly one of your cards is the default."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Point in time, ISO 8601 in UTC.",
            "example": "2026-04-21T07:34:06Z"
          }
        },
        "required": [
          "id",
          "object",
          "brand",
          "last4",
          "exp_month",
          "exp_year",
          "is_default",
          "created_at"
        ],
        "description": "A payment card saved on your wallet through Stripe. Card numbers never reach 3DAPI."
      },
      "CheckoutSession": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "checkout_session"
            ],
            "description": "Names the type of this object. Always `checkout_session`."
          },
          "session_id": {
            "type": "string",
            "description": "Stripe’s id for the page. For a card setup, pass it to `complete_card_setup` after the redirect back.",
            "example": "cs_test_a1b2c3"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "The Stripe-hosted page to send the browser to.",
            "example": "https://checkout.stripe.com/c/pay/cs_test_a1b2c3"
          }
        },
        "required": [
          "object",
          "session_id",
          "url"
        ],
        "description": "A Stripe-hosted page. After it, Stripe returns the browser to your dashboard wallet page."
      },
      "CardDeleted": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^crd_[0-9A-Za-z]+$",
            "description": "Unique identifier of the card, prefixed with `crd_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "crd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "card"
            ],
            "description": "Names the type of this object. Always `card`."
          },
          "deleted": {
            "type": "boolean",
            "enum": [
              true
            ],
            "description": "Always true: the card was removed."
          }
        },
        "required": [
          "id",
          "object",
          "deleted"
        ]
      },
      "StartTopUpRequest": {
        "type": "object",
        "properties": {
          "amount": {
            "type": "object",
            "properties": {
              "amount": {
                "type": "integer",
                "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                "example": 5000
              },
              "currency": {
                "type": "string",
                "pattern": "^[a-z]{3}$",
                "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                "example": "usd"
              }
            },
            "required": [
              "amount",
              "currency"
            ],
            "description": "What lands in your wallet, $1.00 to $100,000.00. When VAT applies it is added on top and shown on the Stripe page."
          }
        },
        "required": [
          "amount"
        ]
      },
      "WalletSettings": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "wallet_settings"
            ],
            "description": "Names the type of this object. Always `wallet_settings`."
          },
          "auto_top_up": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean",
                "description": "When on, your default card is charged `amount` (plus any VAT) when an order needs more than your balance."
              },
              "amount": {
                "type": "object",
                "properties": {
                  "amount": {
                    "type": "integer",
                    "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                    "example": 5000
                  },
                  "currency": {
                    "type": "string",
                    "pattern": "^[a-z]{3}$",
                    "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                    "example": "usd"
                  }
                },
                "required": [
                  "amount",
                  "currency"
                ],
                "description": "How much each automatic top-up adds."
              }
            },
            "required": [
              "enabled",
              "amount"
            ],
            "description": "Auto top up: charges your default card when an order needs more than your balance."
          }
        },
        "required": [
          "object",
          "auto_top_up"
        ],
        "description": "How your wallet refills itself."
      },
      "UpdateWalletSettingsRequest": {
        "type": "object",
        "properties": {
          "auto_top_up": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean",
                "description": "True turns auto top up on, false turns it off."
              },
              "amount": {
                "type": "object",
                "properties": {
                  "amount": {
                    "type": "integer",
                    "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                    "example": 5000
                  },
                  "currency": {
                    "type": "string",
                    "pattern": "^[a-z]{3}$",
                    "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                    "example": "usd"
                  }
                },
                "required": [
                  "amount",
                  "currency"
                ],
                "description": "Omit to keep the current amount."
              }
            },
            "required": [
              "enabled"
            ],
            "description": "Auto top up: charges your default card when an order needs more than your balance."
          }
        },
        "required": [
          "auto_top_up"
        ]
      },
      "WithdrawalList": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ],
            "description": "Names the type of this object. Always `list`."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Withdrawal"
            },
            "description": "The page of results."
          },
          "has_more": {
            "type": "boolean",
            "description": "True when more results exist after this page. Fetch them by passing `next_cursor` as `cursor`."
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque cursor for the next page, or null on the last page. Pass it back unchanged as `cursor`.",
            "example": "eyJjIjoiMjAyNi0wNC0yMVQwNzozNDowNloiLCJpIjoiN0txMiJ9"
          }
        },
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ]
      },
      "Withdrawal": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^wdr_[0-9A-Za-z]+$",
            "description": "Unique identifier of the withdrawal, prefixed with `wdr_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "wdr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "withdrawal"
            ],
            "description": "Names the type of this object. Always `withdrawal`."
          },
          "amount": {
            "type": "object",
            "properties": {
              "amount": {
                "type": "integer",
                "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                "example": 5000
              },
              "currency": {
                "type": "string",
                "pattern": "^[a-z]{3}$",
                "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                "example": "usd"
              }
            },
            "required": [
              "amount",
              "currency"
            ],
            "description": "An amount of money. Amounts are integers in the smallest currency unit, so they never need decimal parsing."
          },
          "status": {
            "$ref": "#/components/schemas/WithdrawalStatus"
          },
          "payment_link": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "The payment link you gave, when you gave one."
          },
          "invoice_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "A short-lived link to the invoice you attached, when you attached one."
          },
          "note": {
            "type": [
              "string",
              "null"
            ],
            "description": "What 3DAPI wrote when paying or rejecting it."
          },
          "processed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Point in time, ISO 8601 in UTC.",
            "example": "2026-04-21T07:34:06Z"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Point in time, ISO 8601 in UTC.",
            "example": "2026-04-21T07:34:06Z"
          }
        },
        "required": [
          "id",
          "object",
          "amount",
          "status",
          "payment_link",
          "invoice_url",
          "note",
          "processed_at",
          "created_at"
        ],
        "description": "A request to pay out part of your wallet balance."
      },
      "WithdrawalStatus": {
        "type": "string",
        "enum": [
          "pending",
          "completed",
          "cancelled",
          "rejected"
        ],
        "description": "- `pending`: waiting for 3DAPI to pay it out. The amount is already off your balance.\n- `completed`: paid out to your payout details.\n- `cancelled`: you cancelled it; the amount went back to your balance.\n- `rejected`: 3DAPI refused it (`note` says why); the amount went back to your balance."
      },
      "WithdrawalEligibility": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "withdrawal_eligibility"
            ],
            "description": "Names the type of this object. Always `withdrawal_eligibility`."
          },
          "can_withdraw_now": {
            "type": "boolean",
            "description": "True when no request is pending and the cooldown has ended. It does not check your balance."
          },
          "cooldown_days": {
            "type": "integer",
            "description": "Days between paid-out requests: 14 for integrators.",
            "example": 14
          },
          "has_pending": {
            "type": "boolean",
            "description": "A pending request holds the slot until it is processed or cancelled."
          },
          "next_available_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the cooldown after your last paid-out request ends; null when you are not in one.",
            "example": "2026-04-21T07:34:06Z"
          }
        },
        "required": [
          "object",
          "can_withdraw_now",
          "cooldown_days",
          "has_pending",
          "next_available_at"
        ],
        "description": "Whether you can request a withdrawal now, and if not, why."
      },
      "CreateWithdrawalRequest": {
        "type": "object",
        "properties": {
          "amount": {
            "type": "object",
            "properties": {
              "amount": {
                "type": "integer",
                "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                "example": 5000
              },
              "currency": {
                "type": "string",
                "pattern": "^[a-z]{3}$",
                "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                "example": "usd"
              }
            },
            "required": [
              "amount",
              "currency"
            ],
            "description": "How much to withdraw. Your whole balance can be withdrawn."
          },
          "payment_link": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 2000,
            "format": "uri",
            "description": "A payment link for this payout, shown to 3DAPI staff with the request. Send it, `invoice`, or both. Omit or null for none."
          },
          "invoice": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "upload": {
                "type": "string",
                "minLength": 1,
                "description": "An upload id with purpose `withdrawal_invoice`: a PDF invoice in USD."
              }
            },
            "required": [
              "upload"
            ],
            "description": "Your invoice for the amount. Send it, `payment_link`, or both. Omit or null for none."
          }
        },
        "required": [
          "amount"
        ],
        "description": "At least one of `invoice` and `payment_link` is required."
      },
      "PayoutDetails": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "payout_details"
            ],
            "description": "Names the type of this object. Always `payout_details`."
          },
          "bank_name": {
            "type": "string",
            "description": "Name of the bank holding the account.",
            "example": "Deutsche Bank"
          },
          "account_holder_name": {
            "type": "string",
            "description": "Name the account is held in.",
            "example": "Acme Prints Ltd"
          },
          "account_number_last4": {
            "type": "string",
            "description": "The last 4 characters of the account number or IBAN. The full number is never returned; send it again in full to change it.",
            "example": "3000"
          },
          "routing_number": {
            "type": "string",
            "description": "Routing number, sort code or SWIFT/BIC.",
            "example": "COBADEFFXXX"
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Point in time, ISO 8601 in UTC.",
            "example": "2026-04-21T07:34:06Z"
          }
        },
        "required": [
          "object",
          "bank_name",
          "account_holder_name",
          "account_number_last4",
          "routing_number",
          "updated_at"
        ],
        "description": "The bank account withdrawals are paid to."
      },
      "SavePayoutDetailsRequest": {
        "type": "object",
        "properties": {
          "bank_name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "description": "Name of the bank holding the account."
          },
          "account_holder_name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "description": "Name the account is held in, as the bank has it."
          },
          "account_number": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100,
            "description": "The full account number or IBAN."
          },
          "routing_number": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100,
            "description": "Routing number, sort code or SWIFT/BIC of the bank."
          }
        },
        "required": [
          "bank_name",
          "account_holder_name",
          "account_number",
          "routing_number"
        ]
      },
      "EventList": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ],
            "description": "Names the type of this object. Always `list`."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Event"
            },
            "description": "The page of results."
          },
          "has_more": {
            "type": "boolean",
            "description": "True when more results exist after this page. Fetch them by passing `next_cursor` as `cursor`."
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque cursor for the next page, or null on the last page. Pass it back unchanged as `cursor`.",
            "example": "eyJjIjoiMjAyNi0wNC0yMVQwNzozNDowNloiLCJpIjoiN0txMiJ9"
          }
        },
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ]
      },
      "Event": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^evt_[0-9A-Za-z]+$",
            "description": "Unique identifier of the event, prefixed with `evt_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "event"
            ],
            "description": "Names the type of this object. Always `event`."
          },
          "type": {
            "$ref": "#/components/schemas/EventType"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Point in time, ISO 8601 in UTC.",
            "example": "2026-04-21T07:34:06Z"
          },
          "data": {
            "type": "object",
            "properties": {
              "object": {
                "type": "object",
                "additionalProperties": {},
                "description": "The object the event is about, as its GET endpoint returns it."
              },
              "previous": {
                "type": [
                  "object",
                  "null"
                ],
                "additionalProperties": {},
                "description": "The fields that changed, with their old values."
              },
              "region": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "pattern": "^reg_[0-9A-Za-z]+$",
                    "description": "Unique identifier of the region, prefixed with `reg_`. Treat it as an opaque string: store it and compare it, never parse it.",
                    "example": "reg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                  },
                  "object": {
                    "type": "string",
                    "enum": [
                      "region"
                    ],
                    "description": "Names the type of this object. Always `region`."
                  },
                  "name": {
                    "type": "string",
                    "description": "Display name of the region."
                  }
                },
                "required": [
                  "id",
                  "object",
                  "name"
                ],
                "description": "The region the event is about: set on `stock.low`, null on every other event."
              }
            },
            "required": [
              "object",
              "previous",
              "region"
            ],
            "description": "The object the event is about, as it is now, and what changed. Each event type documents its exact shape in the `webhooks` section."
          }
        },
        "required": [
          "id",
          "object",
          "type",
          "created_at",
          "data"
        ],
        "description": "Something that happened in your account. Sent to your webhook endpoints and kept in the events log for 30 days."
      },
      "EventType": {
        "type": "string",
        "enum": [
          "order.created",
          "order.paid",
          "order.assigned",
          "order.in_production",
          "order.shipped",
          "order.completed",
          "order.on_hold",
          "order.rejected",
          "order.returned",
          "order.cancelled",
          "checkout.expired",
          "qc_ticket.needs_your_action",
          "qc_ticket.resolved",
          "product.approved",
          "product.rejected",
          "stock_batch.purchased",
          "stock_batch.completed",
          "stock.low",
          "quote.ready",
          "quote.failed",
          "quote.approved",
          "quote.rejected",
          "approval_request.approved",
          "approval_request.changes_requested"
        ],
        "description": "What happened. See the webhooks section for each event and its payload."
      },
      "WebhookEndpointList": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ],
            "description": "Names the type of this object. Always `list`."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookEndpoint"
            },
            "description": "The page of results."
          },
          "has_more": {
            "type": "boolean",
            "description": "True when more results exist after this page. Fetch them by passing `next_cursor` as `cursor`."
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque cursor for the next page, or null on the last page. Pass it back unchanged as `cursor`.",
            "example": "eyJjIjoiMjAyNi0wNC0yMVQwNzozNDowNloiLCJpIjoiN0txMiJ9"
          }
        },
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ]
      },
      "WebhookEndpoint": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^whe_[0-9A-Za-z]+$",
            "description": "Unique identifier of the webhook endpoint, prefixed with `whe_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "whe_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "webhook_endpoint"
            ],
            "description": "Names the type of this object. Always `webhook_endpoint`."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Where events are posted."
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EventType"
            },
            "description": "The event types this endpoint receives."
          },
          "payload_version": {
            "type": "string",
            "enum": [
              "v1",
              "v2"
            ],
            "description": "`v2` sends the event envelope documented here. `v1` keeps the payload of the previous API, for integrations built on it."
          },
          "status": {
            "type": "string",
            "enum": [
              "enabled",
              "disabled"
            ],
            "description": "Disabled endpoints receive nothing."
          },
          "last_delivery": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "succeeded": {
                "type": "boolean",
                "description": "True when your endpoint answered with a 2xx status. False when it answered anything else or could not be reached."
              },
              "status_code": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "HTTP status your endpoint answered. Null when it could not be reached, and always null for the legacy endpoint (`payload_version: v1`, the webhook URL set before endpoints existed), which records only whether its latest delivery succeeded."
              },
              "at": {
                "type": "string",
                "format": "date-time",
                "description": "Point in time, ISO 8601 in UTC.",
                "example": "2026-04-21T07:34:06Z"
              }
            },
            "required": [
              "succeeded",
              "status_code",
              "at"
            ],
            "description": "How the latest delivery went. Null before the first one."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Point in time, ISO 8601 in UTC.",
            "example": "2026-04-21T07:34:06Z"
          }
        },
        "required": [
          "id",
          "object",
          "url",
          "events",
          "payload_version",
          "status",
          "last_delivery",
          "created_at"
        ],
        "description": "A URL that receives the events it subscribes to. Endpoints and their secrets are managed in the dashboard; the secret is never returned by the API."
      },
      "WebhookEndpointWithSecret": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^whe_[0-9A-Za-z]+$",
            "description": "Unique identifier of the webhook endpoint, prefixed with `whe_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "whe_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "webhook_endpoint"
            ],
            "description": "Names the type of this object. Always `webhook_endpoint`."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Where events are posted."
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EventType"
            },
            "description": "The event types this endpoint receives."
          },
          "payload_version": {
            "type": "string",
            "enum": [
              "v1",
              "v2"
            ],
            "description": "`v2` sends the event envelope documented here. `v1` keeps the payload of the previous API, for integrations built on it."
          },
          "status": {
            "type": "string",
            "enum": [
              "enabled",
              "disabled"
            ],
            "description": "Disabled endpoints receive nothing."
          },
          "last_delivery": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "succeeded": {
                "type": "boolean",
                "description": "True when your endpoint answered with a 2xx status. False when it answered anything else or could not be reached."
              },
              "status_code": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "HTTP status your endpoint answered. Null when it could not be reached, and always null for the legacy endpoint (`payload_version: v1`, the webhook URL set before endpoints existed), which records only whether its latest delivery succeeded."
              },
              "at": {
                "type": "string",
                "format": "date-time",
                "description": "Point in time, ISO 8601 in UTC.",
                "example": "2026-04-21T07:34:06Z"
              }
            },
            "required": [
              "succeeded",
              "status_code",
              "at"
            ],
            "description": "How the latest delivery went. Null before the first one."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Point in time, ISO 8601 in UTC.",
            "example": "2026-04-21T07:34:06Z"
          },
          "secret": {
            "type": "string",
            "description": "The signing secret of this endpoint (`whsec_...`). Shown only in this response: store it to verify `X-Dapi-Webhook-Signature`. Rotate it to get a new one.",
            "example": "whsec_3f1c9a2b7d4e5f60718293a4b5c6d7e8f9012a3b4c5d6e7f8091a2b3c4d5e6f7"
          }
        },
        "required": [
          "id",
          "object",
          "url",
          "events",
          "payload_version",
          "status",
          "last_delivery",
          "created_at",
          "secret"
        ],
        "description": "A webhook endpoint with its signing secret, returned once."
      },
      "CreateWebhookEndpointRequest": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "maxLength": 2048,
            "format": "uri",
            "description": "Where events are posted. A public `https://` URL; private, local and reserved addresses are refused, and redirects are not followed.",
            "example": "https://example.com/webhooks/dapi"
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/IntegratorEventType"
            },
            "minItems": 1,
            "description": "The event types to send to this endpoint. At least one.",
            "example": [
              "quote.ready",
              "quote.failed",
              "order.shipped"
            ]
          },
          "enabled": {
            "type": "boolean",
            "default": true,
            "description": "Whether the endpoint receives events. Disabled endpoints count toward the endpoint limit too."
          }
        },
        "required": [
          "url",
          "events"
        ]
      },
      "IntegratorEventType": {
        "type": "string",
        "enum": [
          "order.created",
          "order.paid",
          "order.assigned",
          "order.in_production",
          "order.shipped",
          "order.completed",
          "order.on_hold",
          "order.rejected",
          "order.returned",
          "order.cancelled",
          "checkout.expired",
          "quote.ready",
          "quote.failed",
          "quote.approved",
          "quote.rejected",
          "approval_request.approved",
          "approval_request.changes_requested"
        ],
        "description": "An event type an integrator endpoint can receive: order events (`order.*`, `checkout.expired`), quote events (`quote.*`) and onboarding events (`approval_request.*`). See the webhooks section for each payload."
      },
      "UpdateWebhookEndpointRequest": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "maxLength": 2048,
            "format": "uri",
            "description": "Where events are posted. A public `https://` URL; private, local and reserved addresses are refused, and redirects are not followed.",
            "example": "https://example.com/webhooks/dapi"
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/IntegratorEventType"
            },
            "minItems": 1,
            "description": "Replaces the subscribed event types."
          },
          "enabled": {
            "type": "boolean",
            "description": "Turns delivery on or off. Deliveries due while an endpoint is disabled are skipped (`skipped`), not sent later."
          }
        }
      },
      "WebhookEndpointDeleted": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^whe_[0-9A-Za-z]+$",
            "description": "Unique identifier of the webhook endpoint, prefixed with `whe_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "whe_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "webhook_endpoint"
            ],
            "description": "Names the type of this object. Always `webhook_endpoint`."
          },
          "deleted": {
            "type": "boolean",
            "enum": [
              true
            ],
            "description": "Always true: the endpoint is gone."
          }
        },
        "required": [
          "id",
          "object",
          "deleted"
        ]
      },
      "WebhookTestResult": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "webhook_test"
            ],
            "description": "Names the type of this object. Always `webhook_test`."
          },
          "ok": {
            "type": "boolean",
            "description": "True when your endpoint answered with a 2xx in time."
          },
          "message": {
            "type": "string",
            "description": "What happened, for a person."
          }
        },
        "required": [
          "object",
          "ok",
          "message"
        ]
      },
      "WebhookDeliveryList": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ],
            "description": "Names the type of this object. Always `list`."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookDelivery"
            },
            "description": "The page of results."
          },
          "has_more": {
            "type": "boolean",
            "description": "True when more results exist after this page. Fetch them by passing `next_cursor` as `cursor`."
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque cursor for the next page, or null on the last page. Pass it back unchanged as `cursor`.",
            "example": "eyJjIjoiMjAyNi0wNC0yMVQwNzozNDowNloiLCJpIjoiN0txMiJ9"
          }
        },
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ]
      },
      "WebhookDelivery": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^whd_[0-9A-Za-z]+$",
            "description": "Unique identifier of the webhook delivery, prefixed with `whd_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "whd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "webhook_delivery"
            ],
            "description": "Names the type of this object. Always `webhook_delivery`."
          },
          "endpoint": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "pattern": "^whe_[0-9A-Za-z]+$",
                "description": "Unique identifier of the webhook endpoint, prefixed with `whe_`. Treat it as an opaque string: store it and compare it, never parse it.",
                "example": "whe_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
              },
              "object": {
                "type": "string",
                "enum": [
                  "webhook_endpoint"
                ],
                "description": "Names the type of this object. Always `webhook_endpoint`."
              },
              "url": {
                "type": "string",
                "description": "The URL the event was sent to.",
                "example": "https://example.com/webhooks/dapi"
              }
            },
            "required": [
              "id",
              "object",
              "url"
            ],
            "description": "The webhook endpoint this delivery went to."
          },
          "event": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "id": {
                "type": "string",
                "pattern": "^evt_[0-9A-Za-z]+$",
                "description": "Unique identifier of the event, prefixed with `evt_`. Treat it as an opaque string: store it and compare it, never parse it.",
                "example": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
              },
              "object": {
                "type": "string",
                "enum": [
                  "event"
                ],
                "description": "Names the type of this object. Always `event`."
              },
              "type": {
                "type": "string",
                "description": "The event type.",
                "example": "quote.ready"
              }
            },
            "required": [
              "id",
              "object",
              "type"
            ],
            "description": "The event delivered. Null for deliveries of the previous API payload (`payload_version: v1`)."
          },
          "status": {
            "$ref": "#/components/schemas/WebhookDeliveryStatus"
          },
          "skipped_reason": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "endpoint_disabled",
              null
            ],
            "description": "Why a `skipped` delivery was not sent: `endpoint_disabled`, the endpoint was disabled when its turn came. Null otherwise."
          },
          "next_attempt_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the next try is due, while `pending`. Null otherwise.",
            "example": "2026-04-21T07:34:06Z"
          },
          "attempts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookDeliveryAttempt"
            },
            "description": "Every try, oldest first: when it started, how long it took and what your endpoint answered."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Point in time, ISO 8601 in UTC.",
            "example": "2026-04-21T07:34:06Z"
          }
        },
        "required": [
          "id",
          "object",
          "endpoint",
          "event",
          "status",
          "skipped_reason",
          "next_attempt_at",
          "attempts",
          "created_at"
        ],
        "description": "One event sent to one endpoint, with every try."
      },
      "EventDeliveries": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "event_deliveries"
            ],
            "description": "Names the type of this object. Always `event_deliveries`."
          },
          "event": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "pattern": "^evt_[0-9A-Za-z]+$",
                "description": "Unique identifier of the event, prefixed with `evt_`. Treat it as an opaque string: store it and compare it, never parse it.",
                "example": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
              },
              "object": {
                "type": "string",
                "enum": [
                  "event"
                ],
                "description": "Names the type of this object. Always `event`."
              },
              "type": {
                "type": "string",
                "description": "The event type.",
                "example": "quote.ready"
              },
              "created_at": {
                "type": "string",
                "format": "date-time",
                "description": "Point in time, ISO 8601 in UTC.",
                "example": "2026-04-21T07:34:06Z"
              }
            },
            "required": [
              "id",
              "object",
              "type",
              "created_at"
            ],
            "description": "The event these deliveries carried."
          },
          "request": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "id": {
                "type": "string",
                "description": "The `Request-Id` of the request, as on your Logs page.",
                "example": "req_7Kq2Xb9LmN4pR8sT1vW3yZ5a"
              },
              "object": {
                "type": "string",
                "enum": [
                  "api_request"
                ],
                "description": "Names the type of this object. Always `api_request`."
              }
            },
            "required": [
              "id",
              "object"
            ],
            "description": "The API request that caused the event (open it with get_api_request). Null when something else caused it, such as the slicer or a review."
          },
          "deliveries": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookDelivery"
            },
            "description": "Where the event was sent, oldest first: one delivery per subscribed endpoint, plus one per resend."
          }
        },
        "required": [
          "object",
          "event",
          "request",
          "deliveries"
        ],
        "description": "Where one event was sent and what caused it."
      },
      "ResendEventRequest": {
        "type": "object",
        "properties": {
          "endpoint": {
            "type": "string",
            "pattern": "^whe_[0-9A-Za-z]+$",
            "description": "The endpoint to send the event to.",
            "example": "whe_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          }
        },
        "required": [
          "endpoint"
        ]
      },
      "Upload": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^upl_[0-9A-Za-z]+$",
            "description": "Unique identifier of the upload, prefixed with `upl_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "example": "upl_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
          },
          "object": {
            "type": "string",
            "enum": [
              "upload"
            ],
            "description": "Names the type of this object. Always `upload`."
          },
          "purpose": {
            "$ref": "#/components/schemas/UploadPurpose"
          },
          "upload_url": {
            "type": "string",
            "format": "uri",
            "description": "Send the file bytes here, with `method` and `headers` exactly as given."
          },
          "method": {
            "type": "string",
            "enum": [
              "PUT"
            ],
            "description": "The HTTP method for sending the file to `upload_url`. Always `PUT`."
          },
          "headers": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Headers the upload request must carry.",
            "example": {
              "Content-Type": "image/png"
            }
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "After this the upload URL stops working and the id can no longer be referenced. Uploads not used by then are deleted.",
            "example": "2026-04-21T07:34:06Z"
          }
        },
        "required": [
          "id",
          "object",
          "purpose",
          "upload_url",
          "method",
          "headers",
          "expires_at"
        ],
        "description": "A slot to upload one file. PUT the bytes to `upload_url`, then reference the id in a write, for example `\"images\": [{ \"upload\": \"upl_...\" }]`."
      },
      "UploadPurpose": {
        "type": "string",
        "enum": [
          "extra_image",
          "product_image",
          "design_file",
          "attachment",
          "qc_photo",
          "compliance_document",
          "quote_file",
          "withdrawal_invoice"
        ],
        "description": "What the file is for. Each purpose accepts its own types and size:\n- `extra_image`: a photo of an extra part; image/png, image/jpeg, image/webp; file names ending in .png, .jpg, .jpeg, .jfif, .webp (or no extension); up to 25,000,000 bytes (25 MB).\n- `product_image`: a product or variant photo; image/png, image/jpeg, image/webp; file names ending in .png, .jpg, .jpeg, .jfif, .webp (or no extension); up to 25,000,000 bytes (25 MB).\n- `design_file`: a printable model (.3mf, .stl or .obj); model/3mf, model/stl, model/obj, application/octet-stream; file names ending in .3mf, .stl, .obj; up to 150,000,000 bytes (150 MB).\n- `attachment`: a production document for the farm (PDF, image or short video); application/pdf, image/png, image/jpeg, video/mp4; file names ending in .pdf, .png, .jpg, .jpeg, .jfif, .mp4, .m4v (or no extension); up to 25,000,000 bytes (25 MB).\n- `qc_photo`: a photo for a QC message or issue report; image/png, image/jpeg, image/webp; file names ending in .png, .jpg, .jpeg, .jfif, .webp (or no extension); up to 25,000,000 bytes (25 MB).\n- `compliance_document`: a product safety document (PDF or image); application/pdf, image/png, image/jpeg; file names ending in .pdf, .png, .jpg, .jpeg, .jfif (or no extension); up to 25,000,000 bytes (25 MB).\n- `quote_file`: a model to quote (.stl, .3mf, .obj, .step or .stp); integrators only; model/stl, model/3mf, model/obj, model/step, application/step, application/sla, application/vnd.ms-pki.stl, application/octet-stream; file names ending in .stl, .3mf, .obj, .step, .stp; up to 262,144,000 bytes (250 MiB).\n- `withdrawal_invoice`: an invoice (PDF, in USD) for a withdrawal request; integrators only; application/pdf; file names ending in .pdf; up to 10,485,760 bytes (10 MiB).\nThe extension must match `content_type`. When a write uses the upload, its contents are checked against that type (the first bytes of an image, PDF or video; the header and structure of a model), and a file that is not what it claims is refused with 422 `invalid_file`."
      },
      "CreateUploadRequest": {
        "type": "object",
        "properties": {
          "purpose": {
            "$ref": "#/components/schemas/UploadPurpose"
          },
          "file_name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255,
            "description": "The file name, kept for display. Its extension must be one the purpose accepts and match `content_type`; model files and invoices need one.",
            "example": "magnet.png"
          },
          "content_type": {
            "type": "string",
            "minLength": 1,
            "description": "MIME type of the file. Must be allowed for the purpose.",
            "example": "image/png"
          },
          "size_bytes": {
            "type": "integer",
            "minimum": 1,
            "description": "Exact size of the file in bytes. Must be within the purpose limit (see `purpose`; 1 MB is 1,000,000 bytes and 1 MiB is 1,048,576 bytes).",
            "example": 182344
          }
        },
        "required": [
          "purpose",
          "file_name",
          "content_type",
          "size_bytes"
        ]
      },
      "ShippingRateCard": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "shipping_rate_card"
            ],
            "description": "Names the type of this object. Always `shipping_rate_card`."
          },
          "currency": {
            "type": "string",
            "enum": [
              "usd"
            ],
            "description": "Currency of every price on the card."
          },
          "tiers": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "tier": {
                  "$ref": "#/components/schemas/ShippingTier"
                },
                "max_weight_grams": {
                  "type": [
                    "integer",
                    "null"
                  ],
                  "description": "Heaviest chargeable weight in the tier. Null for the top tier."
                }
              },
              "required": [
                "tier",
                "max_weight_grams"
              ]
            },
            "description": "Tier limits, lightest first."
          },
          "extra_small_max_weight_grams": {
            "type": "integer",
            "description": "Items under this weight do not add the per-additional-item charge. Extra small is a flag on an item, not a tier you can quote: such an item is priced as `small` when it is the heaviest in the parcel, and is left out of `additional_items` otherwise.",
            "example": 100
          },
          "zones": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ShippingZone"
            },
            "description": "Every active zone, or only the matching one with `country`."
          }
        },
        "required": [
          "object",
          "currency",
          "tiers",
          "extra_small_max_weight_grams",
          "zones"
        ],
        "description": "What buyers pay for shipping. An order ships for the `base_price` of the tier of its heaviest single item (never the summed weight), plus `per_additional_item` for every other item. Items under `extra_small_max_weight_grams` ride along without the additional charge, unless they come with extra parts. Example: three small items and one medium item to EU Core cost the medium base price plus three additional items. When a zone has no price of its own for a tier, checkout charges that tier at the zone medium price (per additional item charge included), or, when the zone prices no medium tier either, a flat default for the whole parcel with no per additional item charge. The prices here already show that fallback, so they are always what checkout charges."
      },
      "ShippingTier": {
        "type": "string",
        "enum": [
          "small",
          "medium",
          "large"
        ],
        "description": "Weight class of an item, by the greater of its actual and volumetric weight: `small` up to 600 g, `medium` up to 2 kg, `large` above."
      },
      "ShippingZone": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "description": "Stable identifier of the zone, for example `eu-core`. Zones are platform configuration, so they have a readable code instead of an opaque id.",
            "example": "eu-core"
          },
          "object": {
            "type": "string",
            "enum": [
              "shipping_zone"
            ],
            "description": "Names the type of this object. Always `shipping_zone`."
          },
          "name": {
            "type": "string",
            "description": "Display name.",
            "example": "EU Core"
          },
          "countries": {
            "type": "array",
            "items": {
              "type": "string",
              "pattern": "^[A-Z]{2}$",
              "description": "ISO 3166-1 alpha-2 country code, uppercase. A code that is not a real country is refused with `validation_failed`.",
              "example": "DE"
            },
            "description": "Countries this zone ships to. Empty for the rest-of-world zone, which covers every country not listed elsewhere."
          },
          "rest_of_world": {
            "type": "boolean",
            "description": "True for the catch-all zone."
          },
          "base_price": {
            "type": "object",
            "properties": {
              "small": {
                "type": "object",
                "properties": {
                  "amount": {
                    "type": "integer",
                    "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                    "example": 5000
                  },
                  "currency": {
                    "type": "string",
                    "pattern": "^[a-z]{3}$",
                    "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                    "example": "usd"
                  }
                },
                "required": [
                  "amount",
                  "currency"
                ],
                "description": "An amount of money. Amounts are integers in the smallest currency unit, so they never need decimal parsing."
              },
              "medium": {
                "type": "object",
                "properties": {
                  "amount": {
                    "type": "integer",
                    "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                    "example": 5000
                  },
                  "currency": {
                    "type": "string",
                    "pattern": "^[a-z]{3}$",
                    "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                    "example": "usd"
                  }
                },
                "required": [
                  "amount",
                  "currency"
                ],
                "description": "An amount of money. Amounts are integers in the smallest currency unit, so they never need decimal parsing."
              },
              "large": {
                "type": "object",
                "properties": {
                  "amount": {
                    "type": "integer",
                    "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                    "example": 5000
                  },
                  "currency": {
                    "type": "string",
                    "pattern": "^[a-z]{3}$",
                    "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                    "example": "usd"
                  }
                },
                "required": [
                  "amount",
                  "currency"
                ],
                "description": "An amount of money. Amounts are integers in the smallest currency unit, so they never need decimal parsing."
              }
            },
            "required": [
              "small",
              "medium",
              "large"
            ],
            "description": "What an order costs to ship to this zone, by the tier of its heaviest item. A tier the zone does not price shows the fallback checkout charges: the medium price, else the flat default (which then ignores `per_additional_item`)."
          },
          "per_additional_item": {
            "type": "object",
            "properties": {
              "amount": {
                "type": "integer",
                "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                "example": 5000
              },
              "currency": {
                "type": "string",
                "pattern": "^[a-z]{3}$",
                "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                "example": "usd"
              }
            },
            "required": [
              "amount",
              "currency"
            ],
            "description": "Added for every item after the heaviest one."
          }
        },
        "required": [
          "code",
          "object",
          "name",
          "countries",
          "rest_of_world",
          "base_price",
          "per_additional_item"
        ],
        "description": "A group of destination countries sharing one set of prices."
      },
      "ShippingQuote": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "shipping_quote"
            ],
            "description": "Names the type of this object. Always `shipping_quote`."
          },
          "zone": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "description": "Stable identifier of the zone, for example `eu-core`. Zones are platform configuration, so they have a readable code instead of an opaque id.",
                "example": "eu-core"
              },
              "name": {
                "type": "string",
                "description": "Display name of the zone.",
                "example": "EU Core"
              }
            },
            "required": [
              "code",
              "name"
            ],
            "description": "The zone that serves the country."
          },
          "country_code": {
            "type": "string",
            "pattern": "^[A-Z]{2}$",
            "description": "ISO 3166-1 alpha-2 country code, uppercase. A code that is not a real country is refused with `validation_failed`.",
            "example": "DE"
          },
          "tier": {
            "$ref": "#/components/schemas/ShippingTier"
          },
          "base_price": {
            "type": "object",
            "properties": {
              "amount": {
                "type": "integer",
                "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                "example": 5000
              },
              "currency": {
                "type": "string",
                "pattern": "^[a-z]{3}$",
                "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                "example": "usd"
              }
            },
            "required": [
              "amount",
              "currency"
            ],
            "description": "Base price of the tier in this zone. When the zone does not price the tier, the fallback checkout charges: the zone medium price, else the flat default."
          },
          "additional_items": {
            "type": "integer",
            "minimum": 0,
            "description": "Items charged on top of the heaviest one.",
            "example": 3
          },
          "per_additional_item": {
            "type": "object",
            "properties": {
              "amount": {
                "type": "integer",
                "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                "example": 5000
              },
              "currency": {
                "type": "string",
                "pattern": "^[a-z]{3}$",
                "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                "example": "usd"
              }
            },
            "required": [
              "amount",
              "currency"
            ],
            "description": "Charge for each additional item in this zone."
          },
          "total": {
            "type": "object",
            "properties": {
              "amount": {
                "type": "integer",
                "description": "Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.",
                "example": 5000
              },
              "currency": {
                "type": "string",
                "pattern": "^[a-z]{3}$",
                "description": "Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.",
                "example": "usd"
              }
            },
            "required": [
              "amount",
              "currency"
            ],
            "description": "What the shipping costs: `base_price` plus `per_additional_item` times `additional_items`."
          }
        },
        "required": [
          "object",
          "zone",
          "country_code",
          "tier",
          "base_price",
          "additional_items",
          "per_additional_item",
          "total"
        ],
        "description": "The shipping price for one parcel to one country."
      },
      "MaterialList": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ],
            "description": "Names the type of this object. Always `list`."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Material"
            },
            "description": "The page of results."
          },
          "has_more": {
            "type": "boolean",
            "description": "True when more results exist after this page. Fetch them by passing `next_cursor` as `cursor`."
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque cursor for the next page, or null on the last page. Pass it back unchanged as `cursor`.",
            "example": "eyJjIjoiMjAyNi0wNC0yMVQwNzozNDowNloiLCJpIjoiN0txMiJ9"
          }
        },
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ]
      },
      "CategoryList": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ],
            "description": "Names the type of this object. Always `list`."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Category"
            },
            "description": "The page of results."
          },
          "has_more": {
            "type": "boolean",
            "description": "True when more results exist after this page. Fetch them by passing `next_cursor` as `cursor`."
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque cursor for the next page, or null on the last page. Pass it back unchanged as `cursor`.",
            "example": "eyJjIjoiMjAyNi0wNC0yMVQwNzozNDowNloiLCJpIjoiN0txMiJ9"
          }
        },
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ]
      }
    },
    "parameters": {},
    "headers": {
      "X-RateLimit-Limit": {
        "description": "Requests allowed in the current window by the limit closest to being reached.",
        "schema": {
          "type": "integer",
          "example": 500
        }
      },
      "X-RateLimit-Remaining": {
        "description": "Requests left in the current window. 0 on a 429.",
        "schema": {
          "type": "integer",
          "example": 499
        }
      },
      "X-RateLimit-Reset": {
        "description": "When the current window ends, in Unix seconds (UTC).",
        "schema": {
          "type": "integer",
          "example": 1793448060
        }
      },
      "Retry-After": {
        "description": "Seconds to wait before retrying. Sent with 429 only.",
        "schema": {
          "type": "integer",
          "example": 42
        }
      }
    }
  },
  "paths": {
    "/v2/health": {
      "get": {
        "operationId": "get_health",
        "x-required-scopes": [],
        "x-audience": [
          "designer",
          "integrator"
        ],
        "tags": [
          "Health"
        ],
        "summary": "Check service health",
        "description": "Reports whether the API and its database are up. Needs no credentials. Use it for uptime monitoring, not for business logic.",
        "security": [],
        "responses": {
          "200": {
            "description": "Everything is reachable.",
            "content": {
              "application/json": {
                "example": {
                  "status": "ok",
                  "database": "up",
                  "checked_at": "2026-10-14T09:30:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Health"
                }
              }
            }
          },
          "503": {
            "description": "A dependency is down; `status` is `degraded`.",
            "content": {
              "application/json": {
                "example": {
                  "status": "degraded",
                  "database": "down",
                  "checked_at": "2026-10-14T09:30:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Health"
                }
              }
            }
          }
        }
      }
    },
    "/v2/me": {
      "get": {
        "operationId": "get_me",
        "x-required-scopes": [],
        "x-audience": [
          "designer",
          "integrator"
        ],
        "tags": [
          "Identity"
        ],
        "summary": "Identify the current credentials",
        "description": "Returns who the credentials belong to and what they may do. Call it first to check that an API key works and which scopes it carries.",
        "responses": {
          "200": {
            "description": "The credentials are valid.",
            "content": {
              "application/json": {
                "example": {
                  "object": "actor",
                  "user_id": "44444444-4444-4444-8444-444444444444",
                  "kind": "designer",
                  "authenticated_with": "api_key",
                  "scopes": [
                    "order"
                  ],
                  "effective_scopes": [
                    "orders:read",
                    "orders:write"
                  ],
                  "approval": null
                },
                "schema": {
                  "$ref": "#/components/schemas/Me"
                }
              }
            }
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The account may no longer use the API (`account_blacklisted`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "account_blacklisted"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/integrator/accounts": {
      "post": {
        "operationId": "create_integrator_account",
        "x-required-scopes": [],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Integrator onboarding"
        ],
        "summary": "Sign up as an integrator",
        "description": "Creates an integrator account: a company building on the 3DAPI API. No credentials are needed and none are returned.\n\nWe email a confirmation link to the address. Open it to confirm the email (it leads to the API dashboard); only then can you sign in with the email and password and use the session or an API key for every other call. The link expires; ask for a new one with `resend_integrator_confirmation`.\n\nAn integrator account can never also be a seller account on 3DAPI, so use an email address that has no seller account.\n\nLimited to a few sign-ups per IP address per hour.",
        "security": [],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "email": "dev@acme.example",
                "password": "correct-horse-battery",
                "company_name": "Acme Prints"
              },
              "schema": {
                "$ref": "#/components/schemas/CreateIntegratorAccountRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The account was created and the confirmation email sent. `email_confirmed` is `false`.",
            "content": {
              "application/json": {
                "example": {
                  "id": "itg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "integrator_account",
                  "email": "dev@acme.example",
                  "company_name": "Acme Prints",
                  "email_confirmed": false,
                  "created_at": "2026-10-20T09:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/IntegratorAccount"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid (`validation_failed`), or the password is too weak for the sign-in service (`weak_password`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "weak_password"
            ]
          },
          "409": {
            "description": "An account with this email already exists (`email_taken`). Sign in instead, or use another email.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "email_taken"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 10 per hour per IP address. Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          },
          "503": {
            "description": "Sign-up is not available right now, or could not be finished (`signup_unavailable`). Nothing is kept in either case, so the same email can be used again. Try again later or contact support@dapi.digital.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "signup_unavailable"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "signup",
            "limit": 10,
            "window_seconds": 3600,
            "per": "ip"
          }
        ]
      }
    },
    "/v2/integrator/accounts/confirmation": {
      "post": {
        "operationId": "resend_integrator_confirmation",
        "x-required-scopes": [],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Integrator onboarding"
        ],
        "summary": "Resend the confirmation email",
        "description": "Sends a new confirmation link to an integrator account whose email is not confirmed yet, for example when the first link expired or never arrived. No credentials are needed.\n\nThe answer is always 202, whether or not such an account exists or is already confirmed, so this operation never reveals which emails have accounts. Nothing is sent when there is no unconfirmed integrator account for the address.\n\nLimited to a few requests per IP address per hour.",
        "security": [],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "email": "dev@acme.example"
              },
              "schema": {
                "$ref": "#/components/schemas/ResendIntegratorConfirmationRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted. If an unconfirmed integrator account uses this email, a new confirmation link is on its way. No body."
          },
          "400": {
            "description": "The request is invalid (`validation_failed`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 5 per hour per IP address. Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          },
          "503": {
            "description": "Sending confirmations is not available right now (`signup_unavailable`). Try again later or contact support@dapi.digital.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "signup_unavailable"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "confirmation_resend",
            "limit": 5,
            "window_seconds": 3600,
            "per": "ip"
          }
        ]
      }
    },
    "/v2/integrator/account": {
      "get": {
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "get_integrator_account",
        "x-required-scopes": [],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Integrator account"
        ],
        "summary": "Get your account settings",
        "description": "Returns your account id, sign-in email (and a new one waiting for confirmation), company and contact name, and when the account was created.\n\nIntegrators only, and only with a signed-in dashboard session (`credential_not_allowed` with an API key).",
        "responses": {
          "200": {
            "description": "Your account settings.",
            "content": {
              "application/json": {
                "example": {
                  "id": "itg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "integrator_account_settings",
                  "email": "dev@acme.example",
                  "pending_email": null,
                  "company_name": "Acme Prints",
                  "contact_name": "Ada Lovelace",
                  "created_at": "2026-10-20T09:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/IntegratorAccountSettings"
                }
              }
            }
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      },
      "patch": {
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "update_integrator_account",
        "x-required-scopes": [],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Integrator account"
        ],
        "summary": "Update your company or contact name",
        "description": "Changes only the fields sent. Billing details and the sign-in email have their own operations.\n\nIntegrators only, and only with a signed-in dashboard session (`credential_not_allowed` with an API key).",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "contact_name": "Ada Lovelace"
              },
              "schema": {
                "$ref": "#/components/schemas/UpdateIntegratorAccountSettingsRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Saved. Your account settings.",
            "content": {
              "application/json": {
                "example": {
                  "id": "itg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "integrator_account_settings",
                  "email": "dev@acme.example",
                  "pending_email": null,
                  "company_name": "Acme Prints",
                  "contact_name": "Ada Lovelace",
                  "created_at": "2026-10-20T09:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/IntegratorAccountSettings"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/integrator/account/password": {
      "post": {
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "change_integrator_password",
        "x-required-scopes": [],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Integrator account"
        ],
        "summary": "Change your password",
        "description": "Sets a new sign-in password after checking the current one, and emails your sign-in address that it changed. Sessions already open stay signed in.\n\nIntegrators only, and only with a signed-in dashboard session (`credential_not_allowed` with an API key).",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "current_password": "correct-horse-battery",
                "new_password": "staple-violet-orbit"
              },
              "schema": {
                "$ref": "#/components/schemas/ChangeIntegratorPasswordRequest"
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "The password was changed."
          },
          "400": {
            "description": "The request is invalid (`validation_failed`), the new password is the current one (`same_password`), or the sign-in service refused it as too weak (`weak_password`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "same_password",
              "weak_password"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "422": {
            "description": "The current password is not correct (`current_password_incorrect`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "current_password_incorrect"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          },
          "503": {
            "description": "Sign-in details cannot be changed right now (`account_service_unavailable`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "account_service_unavailable"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/integrator/account/email-change": {
      "post": {
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "request_integrator_email_change",
        "x-required-scopes": [],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Integrator account"
        ],
        "summary": "Change your sign-in email",
        "description": "Emails a confirmation link to the new address, and to your current address when the sign-in service asks both to confirm (`confirm_current_email`). Nothing changes until the links are opened; until then the account signs in with its current email and shows the new one as `pending_email`. Asking again replaces the earlier links.\n\nIntegrators only, and only with a signed-in dashboard session (`credential_not_allowed` with an API key).",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "email": "ops@acme.example"
              },
              "schema": {
                "$ref": "#/components/schemas/RequestIntegratorEmailChangeRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "The confirmation email(s) were sent.",
            "content": {
              "application/json": {
                "example": {
                  "object": "email_change",
                  "new_email": "ops@acme.example",
                  "confirm_current_email": true
                },
                "schema": {
                  "$ref": "#/components/schemas/IntegratorEmailChange"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid (`validation_failed`), or the address is the one you already sign in with (`email_unchanged`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "email_unchanged"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "409": {
            "description": "Another account uses this address (`email_taken`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "email_taken"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          },
          "503": {
            "description": "Sign-in details cannot be changed right now (`account_service_unavailable`), or the confirmation email could not be sent (`email_not_sent`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "account_service_unavailable",
              "email_not_sent"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/integrator/eu-responsible-person": {
      "get": {
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "get_eu_responsible_person",
        "x-required-scopes": [],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Integrator account"
        ],
        "summary": "Get your EU responsible person",
        "description": "Returns your EU responsible person, earlier ones, and whether you need one. Under the GPSR, products sold to buyers in the EU or UK need a responsible person established there. A business billed outside the EU and UK needs one before its checkout orders can ship to EU or UK addresses; orders to other countries, and wallet orders, do not.\n\nIntegrators only, and only with a signed-in dashboard session (`credential_not_allowed` with an API key).",
        "responses": {
          "200": {
            "description": "Your EU responsible person status.",
            "content": {
              "application/json": {
                "example": {
                  "object": "eu_responsible_person_status",
                  "required": true,
                  "billing_country": "US",
                  "status": "active",
                  "active": {
                    "id": "erp_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "eu_responsible_person",
                    "company_name": "EU Rep GmbH",
                    "email": "contact@eurep.example",
                    "address": "Hauptstr. 1, 10115 Berlin",
                    "country": "DE",
                    "active": true,
                    "created_at": "2026-10-20T09:00:00Z",
                    "disabled_at": null
                  },
                  "previous": []
                },
                "schema": {
                  "$ref": "#/components/schemas/EuResponsiblePersonStatus"
                }
              }
            }
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      },
      "put": {
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "set_eu_responsible_person",
        "x-required-scopes": [],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Integrator account"
        ],
        "summary": "Add or replace your EU responsible person",
        "description": "Makes this company your active EU responsible person. A current one is disabled and kept in `previous`; records are never edited. Takes effect at once for new orders.\n\nIntegrators only, and only with a signed-in dashboard session (`credential_not_allowed` with an API key).",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "company_name": "EU Rep GmbH",
                "email": "contact@eurep.example",
                "address": "Hauptstr. 1, 10115 Berlin",
                "country": "DE"
              },
              "schema": {
                "$ref": "#/components/schemas/SetEuResponsiblePersonRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Saved. Your EU responsible person status.",
            "content": {
              "application/json": {
                "example": {
                  "object": "eu_responsible_person_status",
                  "required": true,
                  "billing_country": "US",
                  "status": "active",
                  "active": {
                    "id": "erp_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "eu_responsible_person",
                    "company_name": "EU Rep GmbH",
                    "email": "contact@eurep.example",
                    "address": "Hauptstr. 1, 10115 Berlin",
                    "country": "DE",
                    "active": true,
                    "created_at": "2026-10-20T09:00:00Z",
                    "disabled_at": null
                  },
                  "previous": []
                },
                "schema": {
                  "$ref": "#/components/schemas/EuResponsiblePersonStatus"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "422": {
            "description": "The country is not an EU member state or the United Kingdom (`country_not_eu_or_uk`). Nothing was saved.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "country_not_eu_or_uk"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      },
      "delete": {
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "remove_eu_responsible_person",
        "x-required-scopes": [],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Integrator account"
        ],
        "summary": "Remove your EU responsible person",
        "description": "Disables your active EU responsible person. It stays in `previous` and cannot be re-enabled; add it again to use it again. While you need one, checkout orders to EU or UK addresses are refused from this moment.\n\nIntegrators only, and only with a signed-in dashboard session (`credential_not_allowed` with an API key).",
        "responses": {
          "200": {
            "description": "Removed.",
            "content": {
              "application/json": {
                "example": {
                  "id": "erp_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "eu_responsible_person",
                  "deleted": true
                },
                "schema": {
                  "$ref": "#/components/schemas/EuResponsiblePersonDeleted"
                }
              }
            }
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have no active EU responsible person (`eu_responsible_person_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "eu_responsible_person_not_found"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/api-keys": {
      "post": {
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "create_api_key",
        "x-required-scopes": [],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "API keys"
        ],
        "summary": "Create an API key",
        "description": "Creates a secret key for calling the API from your servers. The response is the only time the full key (`secret`) is shown; store it securely.\n\nKeys work before your account is approved, with the same onboarding limits as the dashboard. Up to 10 active keys per account; revoke one to make room.\n\nIntegrators only, and only with a signed-in dashboard session: an API key cannot manage keys (`credential_not_allowed`).",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "name": "Production server",
                "scopes": [
                  "quotes:read",
                  "quotes:write"
                ]
              },
              "schema": {
                "$ref": "#/components/schemas/CreateApiKeyRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The key, with its secret shown once.",
            "content": {
              "application/json": {
                "example": {
                  "id": "key_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "api_key",
                  "name": "Production server",
                  "preview": "sk_3f9a1c2b7...",
                  "scopes": [
                    "quotes:read",
                    "quotes:write"
                  ],
                  "created_at": "2026-10-20T09:30:00Z",
                  "last_used_at": null,
                  "secret": "sk_3f9a1c2b7d5e6f708192a3b4c5d6e7f8091a2b3c4d5e6f708192a3b4c5d6e41"
                },
                "schema": {
                  "$ref": "#/components/schemas/CreatedApiKey"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid (`validation_failed`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "Called with an API key instead of a session (`credential_not_allowed`), or not an integrator account (`actor_not_allowed`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "credential_not_allowed",
              "actor_not_allowed"
            ]
          },
          "409": {
            "description": "You already have 10 active keys (`key_limit_reached`). Revoke one first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "key_limit_reached"
            ]
          },
          "422": {
            "description": "A scope is unknown or not allowed for your account (`scope_not_allowed`, `details.scopes`). Allowed: `quotes:read`, `quotes:write`, `orders:read`, `orders:write`, `orders:read_customer`, `extras:read`, `extras:write`, `wallet:read`, `webhooks:read`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "scope_not_allowed"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      },
      "get": {
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "list_api_keys",
        "x-required-scopes": [],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "API keys"
        ],
        "summary": "List API keys",
        "description": "Lists your active API keys, newest first, without their secrets. Revoked keys are not listed. Integrators only, and only with a signed-in dashboard session: an API key cannot manage keys (`credential_not_allowed`).",
        "responses": {
          "200": {
            "description": "Your active keys, all on one page.",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "id": "key_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "api_key",
                      "name": "Production server",
                      "preview": "sk_3f9a1c2b7...",
                      "scopes": [
                        "quotes:read",
                        "quotes:write"
                      ],
                      "created_at": "2026-10-20T09:30:00Z",
                      "last_used_at": null
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyList"
                }
              }
            }
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/api-keys/{id}": {
      "delete": {
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "revoke_api_key",
        "x-required-scopes": [],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "API keys"
        ],
        "summary": "Revoke an API key",
        "description": "Revokes a key at once: the next request made with it is refused with `invalid_api_key`. This cannot be undone. Integrators only, and only with a signed-in dashboard session: an API key cannot manage keys (`credential_not_allowed`).",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^key_[0-9A-Za-z]+$",
              "description": "Unique identifier of the api key, prefixed with `key_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "key_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the api key, prefixed with `key_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The key was revoked.",
            "content": {
              "application/json": {
                "example": {
                  "id": "key_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "api_key",
                  "deleted": true
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyDeleted"
                }
              }
            }
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have no active key with this id (`api_key_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "api_key_not_found"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/quotes": {
      "post": {
        "operationId": "create_quote",
        "x-required-scopes": [
          "quotes:write"
        ],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Quotes"
        ],
        "summary": "Quote a model file",
        "description": "Starts pricing one model file and returns the quote with `status: processing`. Poll `get_quote` until it is `ready` (with `price`) or `failed`.\n\nWhile it is processing, 3DAPI first checks the file against its production printers and, when needed, prepares a production version with the right printer, process and filament settings (`file.production_version`). The price is for that version, since it is the file that gets printed; your upload stays downloadable as it is.\n\nUpload the file first: `POST /v2/uploads` with purpose `quote_file`, PUT the bytes, then send the upload id here.\n\nLimits before your account is approved: up to 25 quotes per round, and none while an approval request is in review. After a review that rejected files you get a fresh round of 25. Once approved, quotes are unlimited. A quote counts from the moment it is created, while it is processing and once it is ready; a quote that ends `failed` is given back to your allowance. `get_integrator_onboarding` shows how many you have used.\n\nIntegrators only; designers are refused with `actor_not_allowed`.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Any unique string, for example a UUID. Sending the same request again with the same key within 24 hours returns the first response without doing anything twice. Reusing a key with a different body is refused with `idempotency_key_reused`. A refused request (4xx) frees the key so you can correct and resend it; a server error (5xx) is kept and returned again, because the write may already have happened: check the resource before retrying with a new key."
            },
            "required": false,
            "description": "Any unique string, for example a UUID. Sending the same request again with the same key within 24 hours returns the first response without doing anything twice. Reusing a key with a different body is refused with `idempotency_key_reused`. A refused request (4xx) frees the key so you can correct and resend it; a server error (5xx) is kept and returned again, because the write may already have happened: check the resource before retrying with a new key.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "file": {
                  "upload": "upl_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                },
                "name": "Phone stand v2"
              },
              "schema": {
                "$ref": "#/components/schemas/CreateQuoteRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The quote, being processed.",
            "content": {
              "application/json": {
                "example": {
                  "id": "qte_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "quote",
                  "name": "Phone stand v2",
                  "file": {
                    "name": "phone-stand-v2.3mf",
                    "size_bytes": 2758723,
                    "download_url": "https://files.example.com/public-api/quotes/7f3a/input/phone-stand-v2.3mf?signature=abc",
                    "production_version": null
                  },
                  "status": "processing",
                  "suggested_materials": null,
                  "price": null,
                  "failure_reason": null,
                  "approval": {
                    "status": "not_submitted",
                    "reason": null
                  },
                  "created_at": "2026-10-20T09:12:00Z",
                  "finished_at": null
                },
                "schema": {
                  "$ref": "#/components/schemas/Quote"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid (`validation_failed`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "409": {
            "description": "Your account cannot quote right now: you used the 25 uploads of this round, so request approval next (`upload_limit_reached`), or an approval request is in review and uploads resume once it is reviewed (`approval_in_review`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "upload_limit_reached",
              "approval_in_review"
            ]
          },
          "422": {
            "description": "The upload is unknown, expired or of the wrong purpose (`unknown_upload`, `upload_expired`, `upload_purpose_mismatch`), its file was never sent to `upload_url` (`upload_missing`), or the upload id was already used by another quote (`upload_already_used`; every upload id works once).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "unknown_upload",
              "upload_expired",
              "upload_purpose_mismatch",
              "upload_missing",
              "upload_already_used"
            ]
          },
          "429": {
            "description": "The daily quote limit for accounts not yet approved is reached (`daily_quote_limit_reached`). It resets at midnight UTC. Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user), plus 60 per minute per API key (or signed-in user) for quote, upload, order and payment operations. Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "daily_quote_limit_reached",
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          },
          {
            "policy": "expensive_write",
            "limit": 60,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      },
      "get": {
        "operationId": "list_quotes",
        "x-required-scopes": [
          "quotes:read"
        ],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Quotes"
        ],
        "summary": "List quotes",
        "description": "Lists your quotes, newest first, with their price and approval status. Use the filters to find quotes to submit for approval (`status=ready&approval_status=not_submitted`). Integrators only; designers are refused with `actor_not_allowed`.",
        "parameters": [
          {
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/QuoteStatus"
              },
              "description": "Only quotes in these states. Repeat for several."
            },
            "required": false,
            "description": "Only quotes in these states. Repeat for several.",
            "style": "form",
            "explode": true,
            "name": "status",
            "in": "query"
          },
          {
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/QuoteApprovalStatus"
              },
              "description": "Only quotes in these approval states, for example `approval_status=not_submitted&status=ready` for the quotes you can still submit for approval."
            },
            "required": false,
            "description": "Only quotes in these approval states, for example `approval_status=not_submitted&status=ready` for the quotes you can still submit for approval.",
            "style": "form",
            "explode": true,
            "name": "approval_status",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time",
              "description": "Only quotes created at or after this time (ISO 8601).",
              "example": "2026-10-01T00:00:00Z"
            },
            "required": false,
            "description": "Only quotes created at or after this time (ISO 8601).",
            "name": "created_after",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time",
              "description": "Only quotes created before this time (ISO 8601).",
              "example": "2026-11-01T00:00:00Z"
            },
            "required": false,
            "description": "Only quotes created before this time (ISO 8601).",
            "name": "created_before",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "description": "How many results per page, from 1 to 100.",
              "example": 20
            },
            "required": false,
            "description": "How many results per page, from 1 to 100.",
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Where to continue from: the `next_cursor` of the previous page. Omit for the first page."
            },
            "required": false,
            "description": "Where to continue from: the `next_cursor` of the previous page. Omit for the first page.",
            "name": "cursor",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of quotes.",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "id": "qte_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "quote",
                      "name": "Phone stand v2",
                      "file": {
                        "name": "phone-stand-v2.3mf",
                        "size_bytes": 2758723,
                        "download_url": "https://files.example.com/public-api/quotes/7f3a/input/phone-stand-v2.3mf?signature=abc",
                        "production_version": {
                          "name": "phone-stand-v2.3mf",
                          "format": "3mf",
                          "size_bytes": 2913004,
                          "download_url": "https://files.example.com/public-api/quotes/7f3a/production/phone-stand-v2.3mf?signature=def",
                          "prepared_at": "2026-10-20T09:12:09Z"
                        }
                      },
                      "status": "ready",
                      "suggested_materials": [
                        {
                          "slot": 1,
                          "file_color": "#C12E1F",
                          "file_filament_type": "PLA",
                          "material": {
                            "id": "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                            "object": "material",
                            "name": "PLA Red",
                            "custom": false
                          },
                          "match": "exact"
                        },
                        {
                          "slot": 3,
                          "file_color": "#0B2A88",
                          "file_filament_type": null,
                          "material": {
                            "id": "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                            "object": "material",
                            "name": "PLA Blue",
                            "custom": false
                          },
                          "match": "close"
                        }
                      ],
                      "price": {
                        "amount": 1245,
                        "currency": "usd"
                      },
                      "failure_reason": null,
                      "approval": {
                        "status": "not_submitted",
                        "reason": null
                      },
                      "created_at": "2026-10-20T09:12:00Z",
                      "finished_at": "2026-10-20T09:13:41Z"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/QuoteList"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/quotes/{id}": {
      "get": {
        "operationId": "get_quote",
        "x-required-scopes": [
          "quotes:read"
        ],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Quotes"
        ],
        "summary": "Get a quote",
        "description": "Returns one of your quotes. Poll it after `create_quote` until `status` is `ready` or `failed`; every few seconds is enough. Integrators only; designers are refused with `actor_not_allowed`.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^qte_[0-9A-Za-z]+$",
              "description": "Unique identifier of the quote, prefixed with `qte_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "qte_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the quote, prefixed with `qte_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The quote.",
            "content": {
              "application/json": {
                "example": {
                  "id": "qte_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "quote",
                  "name": "Phone stand v2",
                  "file": {
                    "name": "phone-stand-v2.3mf",
                    "size_bytes": 2758723,
                    "download_url": "https://files.example.com/public-api/quotes/7f3a/input/phone-stand-v2.3mf?signature=abc",
                    "production_version": {
                      "name": "phone-stand-v2.3mf",
                      "format": "3mf",
                      "size_bytes": 2913004,
                      "download_url": "https://files.example.com/public-api/quotes/7f3a/production/phone-stand-v2.3mf?signature=def",
                      "prepared_at": "2026-10-20T09:12:09Z"
                    }
                  },
                  "status": "ready",
                  "suggested_materials": [
                    {
                      "slot": 1,
                      "file_color": "#C12E1F",
                      "file_filament_type": "PLA",
                      "material": {
                        "id": "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "material",
                        "name": "PLA Red",
                        "custom": false
                      },
                      "match": "exact"
                    },
                    {
                      "slot": 3,
                      "file_color": "#0B2A88",
                      "file_filament_type": null,
                      "material": {
                        "id": "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "material",
                        "name": "PLA Blue",
                        "custom": false
                      },
                      "match": "close"
                    }
                  ],
                  "price": {
                    "amount": 1245,
                    "currency": "usd"
                  },
                  "failure_reason": null,
                  "approval": {
                    "status": "not_submitted",
                    "reason": null
                  },
                  "created_at": "2026-10-20T09:12:00Z",
                  "finished_at": "2026-10-20T09:13:41Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Quote"
                }
              }
            }
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have no quote with this id (`quote_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "quote_not_found"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/integrator/onboarding": {
      "get": {
        "operationId": "get_integrator_onboarding",
        "x-required-scopes": [
          "quotes:read"
        ],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Integrator onboarding"
        ],
        "summary": "Get your onboarding status",
        "description": "Returns where your integrator account stands: approved files so far, your upload allowance, how many quotes the next approval request needs and the latest request with its per-file decisions. Use it to decide the next step: upload more files, send a request, wait for the review or replace rejected files. Integrators only; designers are refused with `actor_not_allowed`.",
        "responses": {
          "200": {
            "description": "Your onboarding status.",
            "content": {
              "application/json": {
                "example": {
                  "object": "integrator_onboarding",
                  "status": "changes_requested",
                  "approved_files": 9,
                  "required_files": 10,
                  "uploads": {
                    "used": 0,
                    "limit": 25
                  },
                  "files_to_submit": 1,
                  "current_request": {
                    "id": "apr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "approval_request",
                    "status": "reviewed",
                    "quotes": [
                      {
                        "quote": {
                          "id": "qte_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "quote",
                          "name": "Phone stand v2"
                        },
                        "decision": "rejected",
                        "reason": "The model has open edges; close the mesh and upload it again."
                      }
                    ],
                    "submitted_at": "2026-10-20T10:00:00Z",
                    "reviewed_at": "2026-10-21T08:30:00Z"
                  },
                  "order_blockers": [
                    "billing_details_missing"
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/IntegratorOnboarding"
                }
              }
            }
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/integrator/approval-requests": {
      "post": {
        "operationId": "create_approval_request",
        "x-required-scopes": [
          "quotes:write"
        ],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Integrator onboarding"
        ],
        "summary": "Request account approval",
        "description": "Sends ready quotes to 3DAPI for review. Your account is approved once 10 files are approved.\n\nSend `quotes` to choose the files, exactly `files_to_submit` of them (see `get_integrator_onboarding`), each `ready` and never submitted. Omit `quotes` to submit your latest eligible quotes automatically.\n\nThe first request needs 10 quotes. Approved files stay approved, so after a review with rejections the next request needs only as many new quotes as were rejected. Rejected quotes cannot be submitted again: upload corrected files first.\n\nWhile the request is in review you cannot create quotes or send another request. You get an email when the review is done.\n\nIntegrators only; designers are refused with `actor_not_allowed`.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Any unique string, for example a UUID. Sending the same request again with the same key within 24 hours returns the first response without doing anything twice. Reusing a key with a different body is refused with `idempotency_key_reused`. A refused request (4xx) frees the key so you can correct and resend it; a server error (5xx) is kept and returned again, because the write may already have happened: check the resource before retrying with a new key."
            },
            "required": false,
            "description": "Any unique string, for example a UUID. Sending the same request again with the same key within 24 hours returns the first response without doing anything twice. Reusing a key with a different body is refused with `idempotency_key_reused`. A refused request (4xx) frees the key so you can correct and resend it; a server error (5xx) is kept and returned again, because the write may already have happened: check the resource before retrying with a new key.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "automatic": {
                  "summary": "Submit the latest eligible quotes",
                  "value": {}
                },
                "chosen": {
                  "summary": "Submit chosen quotes",
                  "value": {
                    "quotes": [
                      "qte_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                    ]
                  }
                }
              },
              "schema": {
                "$ref": "#/components/schemas/CreateApprovalRequestRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The request, in review.",
            "content": {
              "application/json": {
                "example": {
                  "id": "apr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "approval_request",
                  "status": "in_review",
                  "quotes": [
                    {
                      "quote": {
                        "id": "qte_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "quote",
                        "name": "Phone stand v2"
                      },
                      "decision": "pending",
                      "reason": null
                    }
                  ],
                  "submitted_at": "2026-10-20T10:00:00Z",
                  "reviewed_at": null
                },
                "schema": {
                  "$ref": "#/components/schemas/ApprovalRequest"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid (`validation_failed`), for example `quotes` does not hold exactly `files_to_submit` ids.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "409": {
            "description": "Your account is already approved (`already_approved`), or a request is already in review (`approval_in_review`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "already_approved",
              "approval_in_review"
            ]
          },
          "422": {
            "description": "Without `quotes`, you have fewer eligible quotes than `files_to_submit` (`not_enough_ready_quotes`, `details.required` and `details.available`). With `quotes`, one of them is unknown, listed twice, still being priced, failed, already submitted, approved or rejected (`quote_not_eligible`, `details.quote` and `details.reason`: `not_found`, `duplicate`, `not_ready`, `failed`, `in_review`, `approved` or `rejected`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "not_enough_ready_quotes",
              "quote_not_eligible",
              "not_found",
              "not_ready",
              "in_review"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      },
      "get": {
        "operationId": "list_approval_requests",
        "x-required-scopes": [
          "quotes:read"
        ],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Integrator onboarding"
        ],
        "summary": "List approval requests",
        "description": "Lists your approval requests, newest first, with the decision on each file. Integrators only; designers are refused with `actor_not_allowed`.",
        "parameters": [
          {
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/ApprovalRequestStatus"
              },
              "description": "Only requests in these states."
            },
            "required": false,
            "description": "Only requests in these states.",
            "style": "form",
            "explode": true,
            "name": "status",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "description": "How many results per page, from 1 to 100.",
              "example": 20
            },
            "required": false,
            "description": "How many results per page, from 1 to 100.",
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Where to continue from: the `next_cursor` of the previous page. Omit for the first page."
            },
            "required": false,
            "description": "Where to continue from: the `next_cursor` of the previous page. Omit for the first page.",
            "name": "cursor",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of approval requests.",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "id": "apr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "approval_request",
                      "status": "reviewed",
                      "quotes": [
                        {
                          "quote": {
                            "id": "qte_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                            "object": "quote",
                            "name": "Phone stand v2"
                          },
                          "decision": "rejected",
                          "reason": "The model has open edges; close the mesh and upload it again."
                        }
                      ],
                      "submitted_at": "2026-10-20T10:00:00Z",
                      "reviewed_at": "2026-10-21T08:30:00Z"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/ApprovalRequestList"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/integrator/approval-requests/{id}": {
      "get": {
        "operationId": "get_approval_request",
        "x-required-scopes": [
          "quotes:read"
        ],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Integrator onboarding"
        ],
        "summary": "Get an approval request",
        "description": "Returns one of your approval requests with the decision and reason for each file. Integrators only; designers are refused with `actor_not_allowed`.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^apr_[0-9A-Za-z]+$",
              "description": "Unique identifier of the approval request, prefixed with `apr_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "apr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the approval request, prefixed with `apr_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The approval request.",
            "content": {
              "application/json": {
                "example": {
                  "id": "apr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "approval_request",
                  "status": "reviewed",
                  "quotes": [
                    {
                      "quote": {
                        "id": "qte_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "quote",
                        "name": "Phone stand v2"
                      },
                      "decision": "rejected",
                      "reason": "The model has open edges; close the mesh and upload it again."
                    }
                  ],
                  "submitted_at": "2026-10-20T10:00:00Z",
                  "reviewed_at": "2026-10-21T08:30:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/ApprovalRequest"
                }
              }
            }
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have no approval request with this id (`approval_request_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "approval_request_not_found"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/integrator/analytics": {
      "get": {
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "get_integrator_analytics",
        "x-required-scopes": [],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Analytics"
        ],
        "summary": "Get your analytics",
        "description": "Figures about your quotes and API calls for a period: how many quotes you created and how they ended, their total and average unit price, how long slicing took, a time series, the spread of prices, file types, your most quoted files and which API keys created them, plus the outcome of your approval reviews.\n\nThe `orders` section covers the orders you placed in the period: counts over time, by status and by mode, your wallet spend on production, your checkout sales and earnings, average order value and the quotes ordered most. Its amounts are the per-order figures of get_integrator_order, added up.\n\nEvery total comes with the same figure for the equal period just before, for comparison. Periods are up to 731 days and 366 buckets; all dates are UTC.\n\nIntegrators only, and only with a signed-in dashboard session: an API key cannot read analytics (`credential_not_allowed`).",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "date-time",
              "description": "Start of the period, included. Defaults to 30 days before `to`.",
              "example": "2026-09-22T00:00:00Z"
            },
            "required": false,
            "description": "Start of the period, included. Defaults to 30 days before `to`.",
            "name": "from",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time",
              "description": "End of the period, excluded. Defaults to the end of today (UTC).",
              "example": "2026-10-22T00:00:00Z"
            },
            "required": false,
            "description": "End of the period, excluded. Defaults to the end of today (UTC).",
            "name": "to",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "day",
                "week",
                "month"
              ],
              "description": "Bucket size of `quotes.series`. Defaults to `day` up to 62 days, `week` up to 184 days, `month` beyond."
            },
            "required": false,
            "description": "Bucket size of `quotes.series`. Defaults to `day` up to 62 days, `week` up to 184 days, `month` beyond.",
            "name": "granularity",
            "in": "query"
          },
          {
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/QuoteStatus"
              },
              "description": "Only quotes in these states. Repeat for several."
            },
            "required": false,
            "description": "Only quotes in these states. Repeat for several.",
            "name": "status",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "pattern": "^key_[0-9A-Za-z]+$",
              "description": "Only quotes created with this API key.",
              "example": "key_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": false,
            "description": "Only quotes created with this API key.",
            "name": "api_key",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Your analytics for the period.",
            "content": {
              "application/json": {
                "example": {
                  "object": "integrator_analytics",
                  "period": {
                    "from": "2026-09-22T00:00:00Z",
                    "to": "2026-10-22T00:00:00Z",
                    "granularity": "day",
                    "time_zone": "UTC"
                  },
                  "previous_period": {
                    "from": "2026-08-23T00:00:00Z",
                    "to": "2026-09-22T00:00:00Z"
                  },
                  "filters": {
                    "status": [],
                    "api_key": null
                  },
                  "quotes": {
                    "totals": {
                      "created": 128,
                      "processing": 2,
                      "ready": 118,
                      "failed": 8,
                      "success_rate": 0.9365,
                      "quoted_value": {
                        "amount": 162480,
                        "currency": "usd"
                      },
                      "average_value": {
                        "amount": 1377,
                        "currency": "usd"
                      },
                      "median_slicing_seconds": 74
                    },
                    "previous": {
                      "created": 97,
                      "processing": 0,
                      "ready": 89,
                      "failed": 8,
                      "success_rate": 0.9175,
                      "quoted_value": {
                        "amount": 118300,
                        "currency": "usd"
                      },
                      "average_value": {
                        "amount": 1329,
                        "currency": "usd"
                      },
                      "median_slicing_seconds": 81
                    },
                    "series": [
                      {
                        "start": "2026-10-21T00:00:00Z",
                        "created": 6,
                        "ready": 5,
                        "failed": 1,
                        "quoted_value": {
                          "amount": 7240,
                          "currency": "usd"
                        }
                      }
                    ],
                    "value_distribution": [
                      {
                        "min": {
                          "amount": 0,
                          "currency": "usd"
                        },
                        "max": {
                          "amount": 500,
                          "currency": "usd"
                        },
                        "count": 12
                      },
                      {
                        "min": {
                          "amount": 25000,
                          "currency": "usd"
                        },
                        "max": null,
                        "count": 1
                      }
                    ],
                    "file_types": [
                      {
                        "type": "stl",
                        "created": 96,
                        "ready": 90
                      }
                    ],
                    "top_files": [
                      {
                        "name": "phone-stand.stl",
                        "created": 9,
                        "ready": 9,
                        "latest_price": {
                          "amount": 1240,
                          "currency": "usd"
                        },
                        "last_quoted_at": "2026-10-21T14:02:11Z"
                      }
                    ],
                    "sources": [
                      {
                        "api_key": {
                          "id": "key_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "api_key",
                          "name": "Production server"
                        },
                        "created": 110,
                        "ready": 103
                      },
                      {
                        "api_key": null,
                        "created": 18,
                        "ready": 15
                      }
                    ]
                  },
                  "api_usage": {
                    "totals": {
                      "calls": 1840,
                      "client_errors": 21,
                      "server_errors": 1,
                      "error_rate": 0.012,
                      "p50_ms": 96,
                      "p95_ms": 412
                    },
                    "previous": {
                      "calls": 1302,
                      "client_errors": 30,
                      "server_errors": 0,
                      "error_rate": 0.023,
                      "p50_ms": 104,
                      "p95_ms": 455
                    },
                    "series": [
                      {
                        "start": "2026-10-21T00:00:00Z",
                        "calls": 74,
                        "errors": 1,
                        "p95_ms": 380
                      }
                    ],
                    "endpoints": [
                      {
                        "method": "GET",
                        "route": "/v2/quotes/:id",
                        "calls": 1520,
                        "errors": 4,
                        "p95_ms": 160
                      }
                    ],
                    "sources": [
                      {
                        "api_key": {
                          "id": "key_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "api_key",
                          "name": "Production server"
                        },
                        "calls": 1840,
                        "errors": 22
                      }
                    ]
                  },
                  "reviews": {
                    "requests_sent": 2,
                    "files_in_review": 0,
                    "files_approved": 7,
                    "files_rejected": 3,
                    "rejection_reasons": [
                      {
                        "reason": "Walls under 0.8 mm will break in print.",
                        "count": 2
                      }
                    ]
                  },
                  "orders": {
                    "totals": {
                      "created": 14,
                      "paid": 13,
                      "cancelled": 1,
                      "wallet": {
                        "orders": 8,
                        "charged": {
                          "amount": 46400,
                          "currency": "usd"
                        },
                        "refunded": {
                          "amount": 5800,
                          "currency": "usd"
                        },
                        "spend": {
                          "amount": 40600,
                          "currency": "usd"
                        },
                        "average_order_value": {
                          "amount": 5800,
                          "currency": "usd"
                        }
                      },
                      "checkout": {
                        "orders": 5,
                        "charged": {
                          "amount": 42300,
                          "currency": "usd"
                        },
                        "refunded": {
                          "amount": 0,
                          "currency": "usd"
                        },
                        "sales": {
                          "amount": 42300,
                          "currency": "usd"
                        },
                        "earnings": {
                          "amount": 13240,
                          "currency": "usd"
                        },
                        "average_order_value": {
                          "amount": 8460,
                          "currency": "usd"
                        }
                      }
                    },
                    "previous": {
                      "created": 9,
                      "paid": 8,
                      "cancelled": 0,
                      "wallet": {
                        "orders": 5,
                        "charged": {
                          "amount": 30150,
                          "currency": "usd"
                        },
                        "refunded": {
                          "amount": 0,
                          "currency": "usd"
                        },
                        "spend": {
                          "amount": 30150,
                          "currency": "usd"
                        },
                        "average_order_value": {
                          "amount": 6030,
                          "currency": "usd"
                        }
                      },
                      "checkout": {
                        "orders": 3,
                        "charged": {
                          "amount": 19800,
                          "currency": "usd"
                        },
                        "refunded": {
                          "amount": 0,
                          "currency": "usd"
                        },
                        "sales": {
                          "amount": 19800,
                          "currency": "usd"
                        },
                        "earnings": {
                          "amount": 6120,
                          "currency": "usd"
                        },
                        "average_order_value": {
                          "amount": 6600,
                          "currency": "usd"
                        }
                      }
                    },
                    "series": [
                      {
                        "start": "2026-10-21T00:00:00Z",
                        "created": 2,
                        "wallet": 1,
                        "checkout": 1,
                        "wallet_spend": {
                          "amount": 5800,
                          "currency": "usd"
                        },
                        "checkout_sales": {
                          "amount": 8460,
                          "currency": "usd"
                        }
                      }
                    ],
                    "by_status": [
                      {
                        "status": "in_production",
                        "count": 3
                      },
                      {
                        "status": "shipped",
                        "count": 4
                      },
                      {
                        "status": "completed",
                        "count": 6
                      },
                      {
                        "status": "cancelled",
                        "count": 1
                      }
                    ],
                    "by_mode": [
                      {
                        "mode": "wallet",
                        "created": 8,
                        "paid": 8
                      },
                      {
                        "mode": "checkout",
                        "created": 6,
                        "paid": 5
                      }
                    ],
                    "top_quotes": [
                      {
                        "quote": {
                          "id": "qte_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "quote",
                          "name": "phone-stand.stl"
                        },
                        "orders": 7,
                        "units": 42
                      }
                    ]
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/IntegratorAnalytics"
                }
              }
            }
          },
          "400": {
            "description": "A filter is invalid (`validation_failed`), `from` is not before `to` or the period is too long (`invalid_period`), or the granularity gives too many buckets (`too_many_buckets`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_period",
              "too_many_buckets"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "Called with an API key instead of a session (`credential_not_allowed`), or not an integrator account (`actor_not_allowed`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "credential_not_allowed",
              "actor_not_allowed"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/api-requests": {
      "get": {
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "list_api_requests",
        "x-required-scopes": [],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Logs"
        ],
        "summary": "List your API requests",
        "description": "Lists the requests you made to the API in the last 30 days, newest first: method, path, status, time taken and the key used. Filter by time, status class, method, operation, key or request id.\n\nIntegrators only, and only with a signed-in dashboard session (`credential_not_allowed` with an API key).",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "date-time",
              "description": "Only requests made at or after this time."
            },
            "required": false,
            "description": "Only requests made at or after this time.",
            "name": "created_after",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time",
              "description": "Only requests made before this time."
            },
            "required": false,
            "description": "Only requests made before this time.",
            "name": "created_before",
            "in": "query"
          },
          {
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "2xx",
                  "3xx",
                  "4xx",
                  "5xx"
                ]
              },
              "description": "Only these status classes (`2xx`, `4xx`, ...). Repeat for several."
            },
            "required": false,
            "description": "Only these status classes (`2xx`, `4xx`, ...). Repeat for several.",
            "style": "form",
            "explode": true,
            "name": "status_class",
            "in": "query"
          },
          {
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "GET",
                  "POST",
                  "PUT",
                  "PATCH",
                  "DELETE"
                ]
              },
              "description": "Only these HTTP methods. Repeat for several."
            },
            "required": false,
            "description": "Only these HTTP methods. Repeat for several.",
            "style": "form",
            "explode": true,
            "name": "method",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "maxLength": 200,
              "description": "Only this operation, by its path template (`/v2/quotes/:id`)."
            },
            "required": false,
            "description": "Only this operation, by its path template (`/v2/quotes/:id`).",
            "name": "route",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "pattern": "^key_[0-9A-Za-z]+$",
              "description": "Only requests made with this API key.",
              "example": "key_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": false,
            "description": "Only requests made with this API key.",
            "name": "api_key",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "pattern": "^req_[0-9A-Za-z]{24}$",
              "description": "Find one request by its `Request-Id`.",
              "example": "req_7Kq2Xb9LmN4pR8sT1vW3yZ5a"
            },
            "required": false,
            "description": "Find one request by its `Request-Id`.",
            "name": "request_id",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "description": "How many results per page, from 1 to 100.",
              "example": 20
            },
            "required": false,
            "description": "How many results per page, from 1 to 100.",
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Where to continue from: the `next_cursor` of the previous page. Omit for the first page."
            },
            "required": false,
            "description": "Where to continue from: the `next_cursor` of the previous page. Omit for the first page.",
            "name": "cursor",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of requests.",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "id": "req_7Kq2Xb9LmN4pR8sT1vW3yZ5a",
                      "object": "api_request",
                      "method": "POST",
                      "route": "/v2/quotes",
                      "path": "/v2/quotes",
                      "status_code": 201,
                      "duration_ms": 184,
                      "credential": "api_key",
                      "api_key": {
                        "id": "key_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "api_key",
                        "name": "Production server"
                      },
                      "ip": "203.0.113.7",
                      "user_agent": "node-fetch/1.0",
                      "idempotency_key": "0f8c2d1e-5a6b-4c3d-9e8f-1a2b3c4d5e6f",
                      "error_code": null,
                      "request_bytes": 96,
                      "response_bytes": 512,
                      "created_at": "2026-10-21T14:02:11Z"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiRequestList"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/api-requests/{id}": {
      "get": {
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "get_api_request",
        "x-required-scopes": [],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Logs"
        ],
        "summary": "Get an API request",
        "description": "Returns one logged request with the bodies sent and answered (credentials, secrets and buyer personal data removed), the webhook events it caused and every delivery attempt to your endpoints.\n\nIntegrators only, and only with a signed-in dashboard session (`credential_not_allowed` with an API key).",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^req_[0-9A-Za-z]{24}$",
              "description": "Unique identifier of the request, prefixed with `req_`. Every response carries it in the `Request-Id` header, and error bodies in `details.request_id`.",
              "example": "req_7Kq2Xb9LmN4pR8sT1vW3yZ5a"
            },
            "required": true,
            "description": "Unique identifier of the request, prefixed with `req_`. Every response carries it in the `Request-Id` header, and error bodies in `details.request_id`.",
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The request.",
            "content": {
              "application/json": {
                "example": {
                  "id": "req_7Kq2Xb9LmN4pR8sT1vW3yZ5a",
                  "object": "api_request",
                  "method": "POST",
                  "route": "/v2/quotes",
                  "path": "/v2/quotes",
                  "status_code": 201,
                  "duration_ms": 184,
                  "credential": "api_key",
                  "api_key": {
                    "id": "key_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "api_key",
                    "name": "Production server"
                  },
                  "ip": "203.0.113.7",
                  "user_agent": "node-fetch/1.0",
                  "idempotency_key": "0f8c2d1e-5a6b-4c3d-9e8f-1a2b3c4d5e6f",
                  "error_code": null,
                  "request_bytes": 96,
                  "response_bytes": 512,
                  "created_at": "2026-10-21T14:02:11Z",
                  "request_body": {
                    "name": "Phone stand",
                    "file": {
                      "upload": "upl_3fZq9a[redacted]"
                    }
                  },
                  "response_body": {
                    "id": "qte_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "quote",
                    "status": "processing"
                  },
                  "bodies_truncated": false,
                  "events": []
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiRequestDetail"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have no logged request with this id (`api_request_not_found`). Requests are kept for 30 days.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "api_request_not_found"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/products": {
      "get": {
        "operationId": "list_products",
        "x-required-scopes": [
          "products:read"
        ],
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Products"
        ],
        "summary": "List products",
        "description": "Lists your products, newest first by default. Items leave out variants and storefront details to stay small; get the product for everything, or pass `expand[]=variants`. Designers only.",
        "parameters": [
          {
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/ReviewStatus"
              },
              "description": "Only products in these review states. Repeat for several, for example `review_status=pending&review_status=rejected`."
            },
            "required": false,
            "description": "Only products in these review states. Repeat for several, for example `review_status=pending&review_status=rejected`.",
            "style": "form",
            "explode": true,
            "name": "review_status",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ],
              "description": "Only public (`true`) or private (`false`) products."
            },
            "required": false,
            "description": "Only public (`true`) or private (`false`) products.",
            "name": "is_public",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "pattern": "^cat_[0-9A-Za-z]+$",
              "description": "Only products in this category.",
              "example": "cat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": false,
            "description": "Only products in this category.",
            "name": "category",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Only products whose name contains this text, ignoring case."
            },
            "required": false,
            "description": "Only products whose name contains this text, ignoring case.",
            "name": "search",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time",
              "description": "Only products changed after this time. Use it to sync changes since your last poll.",
              "example": "2026-04-21T07:34:06Z"
            },
            "required": false,
            "description": "Only products changed after this time. Use it to sync changes since your last poll.",
            "name": "updated_after",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "-created_at",
                "created_at",
                "-updated_at",
                "name",
                "position"
              ],
              "default": "-created_at",
              "description": "Order of results. A leading `-` means descending. `position` is your own order from the dashboard. `name` sorts alphabetically in a language-aware collation, which compares letters before spaces and case (`test slicer` comes after `testing warnings`); do not expect byte order."
            },
            "required": false,
            "description": "Order of results. A leading `-` means descending. `position` is your own order from the dashboard. `name` sorts alphabetically in a language-aware collation, which compares letters before spaces and case (`test slicer` comes after `testing warnings`); do not expect byte order.",
            "name": "sort",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "description": "How many results per page, from 1 to 100.",
              "example": 20
            },
            "required": false,
            "description": "How many results per page, from 1 to 100.",
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Where to continue from: the `next_cursor` of the previous page. Omit for the first page."
            },
            "required": false,
            "description": "Where to continue from: the `next_cursor` of the previous page. Omit for the first page.",
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "variants",
                  "category",
                  "variants.category",
                  "variants.contents.design",
                  "variants.contents.stock",
                  "variants.contents.extra",
                  "variants.contents.materials"
                ]
              },
              "description": "Replace references with full objects. Repeat the parameter for several paths, for example `expand[]=variants`. Unknown paths are rejected with `invalid_expand`. An expanded object from another resource needs the read scope of that resource on your API key; without it the reference `{ id, object, name }` is returned instead, with no error. Here `variants.contents.extra` needs `extras:read`, `variants.contents.stock` needs `stocks:read`."
            },
            "required": false,
            "description": "Replace references with full objects. Repeat the parameter for several paths, for example `expand[]=variants`. Unknown paths are rejected with `invalid_expand`. An expanded object from another resource needs the read scope of that resource on your API key; without it the reference `{ id, object, name }` is returned instead, with no error. Here `variants.contents.extra` needs `extras:read`, `variants.contents.stock` needs `stocks:read`.",
            "style": "form",
            "explode": true,
            "name": "expand[]",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of products.",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "id": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "product",
                      "name": "PR Tracker",
                      "slug": "pr-tracker",
                      "category": {
                        "id": "cat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "category",
                        "name": "Desk accessories"
                      },
                      "review": {
                        "status": "approved",
                        "reason": null
                      },
                      "publishing": {
                        "is_public": true,
                        "unpublished_by": null,
                        "can_publish": true,
                        "blockers": []
                      },
                      "variant_count": 1,
                      "price_range": {
                        "min": {
                          "amount": 5000,
                          "currency": "usd"
                        },
                        "max": {
                          "amount": 5000,
                          "currency": "usd"
                        }
                      },
                      "images": [
                        {
                          "url": "https://cdn.example.com/tracker.png",
                          "primary": true
                        }
                      ],
                      "created_at": "2026-04-21T07:34:06Z",
                      "updated_at": "2026-10-02T11:12:40Z"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/ProductList"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      },
      "post": {
        "operationId": "create_product",
        "x-required-scopes": [
          "products:write"
        ],
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Products"
        ],
        "summary": "Create a product",
        "description": "Creates a product with its design files, and optionally its images, attachments and variants, in one request. It is private and its designs are pending review by 3DAPI; publish it once it is approved. Upload files first (purposes `design_file`, `product_image`, `attachment`). Counts toward your plan product limit. Designers only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Any unique string, for example a UUID. Sending the same request again with the same key within 24 hours returns the first response without doing anything twice. Reusing a key with a different body is refused with `idempotency_key_reused`. A refused request (4xx) frees the key so you can correct and resend it; a server error (5xx) is kept and returned again, because the write may already have happened: check the resource before retrying with a new key."
            },
            "required": false,
            "description": "Any unique string, for example a UUID. Sending the same request again with the same key within 24 hours returns the first response without doing anything twice. Reusing a key with a different body is refused with `idempotency_key_reused`. A refused request (4xx) frees the key so you can correct and resend it; a server error (5xx) is kept and returned again, because the write may already have happened: check the resource before retrying with a new key.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "name": "PR Tracker",
                "description": "A desk tracker for your personal records.",
                "category": "cat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                "options": [
                  {
                    "name": "Color",
                    "values": [
                      "Black",
                      "Blue"
                    ]
                  }
                ],
                "images": [
                  {
                    "upload": "upl_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "primary": true
                  }
                ],
                "manufacturing": {
                  "assembly_required": true,
                  "farm_instructions": null
                },
                "designs": [
                  {
                    "key": "body",
                    "file": {
                      "upload": "upl_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                    },
                    "name": "Tracker body",
                    "materials": {
                      "mode": "single",
                      "allowed": [
                        "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                      ]
                    }
                  }
                ],
                "variants": [
                  {
                    "sku": "TRACKER-BLUE",
                    "name": "PR Tracker Blue",
                    "options": [
                      {
                        "name": "Color",
                        "value": "Blue"
                      }
                    ],
                    "price": {
                      "amount": 5000,
                      "currency": "usd"
                    },
                    "contents": [
                      {
                        "type": "design",
                        "design": "body",
                        "quantity": 1,
                        "materials": [
                          "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                        ]
                      }
                    ]
                  }
                ]
              },
              "schema": {
                "$ref": "#/components/schemas/CreateProductRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The new product, private and pending review.",
            "content": {
              "application/json": {
                "example": {
                  "id": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "product",
                  "name": "PR Tracker",
                  "slug": "pr-tracker",
                  "category": {
                    "id": "cat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "category",
                    "name": "Desk accessories"
                  },
                  "review": {
                    "status": "pending",
                    "reason": null
                  },
                  "publishing": {
                    "is_public": false,
                    "unpublished_by": "seller",
                    "can_publish": false,
                    "blockers": [
                      {
                        "code": "files_not_approved",
                        "message": "The design files must be approved by 3DAPI before the product can be made public."
                      }
                    ]
                  },
                  "variant_count": 1,
                  "price_range": {
                    "min": {
                      "amount": 5000,
                      "currency": "usd"
                    },
                    "max": {
                      "amount": 5000,
                      "currency": "usd"
                    }
                  },
                  "images": [
                    {
                      "url": "https://cdn.example.com/tracker.png",
                      "primary": true
                    }
                  ],
                  "created_at": "2026-04-21T07:34:06Z",
                  "updated_at": "2026-10-02T11:12:40Z",
                  "description": "A desk tracker for your personal records.",
                  "options": [
                    {
                      "name": "Color",
                      "values": [
                        "Black",
                        "Blue"
                      ]
                    }
                  ],
                  "variants": [
                    {
                      "id": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "variant",
                      "sku": "TRACKER-BLUE",
                      "name": "PR Tracker Blue",
                      "status": "active",
                      "options": [
                        {
                          "name": "Color",
                          "value": "Blue"
                        }
                      ],
                      "category": null,
                      "pricing": {
                        "price": {
                          "amount": 5000,
                          "currency": "usd"
                        },
                        "prices_include_vat": true,
                        "costs": {
                          "manufacturing": {
                            "amount": 332,
                            "currency": "usd"
                          },
                          "extra_parts": {
                            "amount": 40,
                            "currency": "usd"
                          },
                          "platform_fee": {
                            "amount": 250,
                            "currency": "usd"
                          }
                        },
                        "earnings": {
                          "amount": 4378,
                          "currency": "usd"
                        }
                      },
                      "shipping": {
                        "tier": "medium",
                        "determined_by": "volume",
                        "chargeable_weight_grams": 820,
                        "extra_small": false
                      },
                      "contents": [
                        {
                          "type": "design",
                          "quantity": 1,
                          "design": {
                            "id": "dsg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                            "object": "design",
                            "name": "tracker.3mf"
                          },
                          "materials": [
                            {
                              "id": "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                              "object": "material",
                              "name": "PLA Blue",
                              "custom": false
                            }
                          ]
                        },
                        {
                          "type": "extra",
                          "quantity": 4,
                          "unit_price": {
                            "amount": 10,
                            "currency": "usd"
                          },
                          "extra": {
                            "id": "xtr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                            "object": "extra",
                            "name": "M3 screw"
                          }
                        }
                      ],
                      "image": null
                    }
                  ],
                  "attachments": [
                    {
                      "id": "att_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "attachment",
                      "name": "assembly-guide.pdf"
                    }
                  ],
                  "storefront": {
                    "url": "https://dapi.digital/store/acme/pr-tracker",
                    "show_compliance_labels": true,
                    "disclaimers": [
                      {
                        "title": "Production safety",
                        "text": "Keep away from heat."
                      }
                    ]
                  },
                  "custom_label_url": null,
                  "compliance": {
                    "object": "product_compliance",
                    "enforced": false,
                    "complete": false,
                    "missing": 1
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Product"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "Your plan product limit is reached (`plan_limit_reached`); `details.fix_url` points at upgrading.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "plan_limit_reached"
            ]
          },
          "409": {
            "description": "The slug or a SKU is already used (`slug_taken`, `sku_already_used`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "slug_taken",
              "sku_already_used"
            ]
          },
          "422": {
            "description": "A variant breaks a product rule: `option_mismatch`, `material_not_allowed`, `material_not_found`, `extra_not_found`, `extra_not_approved` (a rejected extra), `stock_not_found`, `stock_not_approved`, `category_not_found`, `variant_needs_design_or_stock`, `price_below_minimum` (minimum in `details.minimum`), `currency_not_supported`, `unknown_design_key`, `duplicate_design_key`, `design_not_in_product`, `too_many_variants`; a design names a combination with the wrong slot count (`combination_slots_mismatch`); an image or attachment to keep is not the product (`image_not_found`, `attachment_not_found`, `custom_label_not_found`, `too_many_primary_images`); or an upload cannot be used: `unknown_upload`, `upload_expired`, `upload_purpose_mismatch`, `upload_missing` (an upload id whose file was never sent), `upload_already_used` (an upload id already used once; each works once).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "option_mismatch",
              "material_not_allowed",
              "material_not_found",
              "extra_not_found",
              "extra_not_approved",
              "stock_not_found",
              "stock_not_approved",
              "category_not_found",
              "variant_needs_design_or_stock",
              "price_below_minimum",
              "currency_not_supported",
              "unknown_design_key",
              "duplicate_design_key",
              "design_not_in_product",
              "too_many_variants",
              "combination_slots_mismatch",
              "image_not_found",
              "attachment_not_found",
              "custom_label_not_found",
              "too_many_primary_images",
              "unknown_upload",
              "upload_expired",
              "upload_purpose_mismatch",
              "upload_missing",
              "upload_already_used"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/products/{id}": {
      "get": {
        "operationId": "get_product",
        "x-required-scopes": [
          "products:read"
        ],
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Products"
        ],
        "summary": "Get a product",
        "description": "Returns one product with every variant, its contents, pricing and margins, shipping tier, and what blocks publishing. Related designs, stocks, extras, materials, categories and attachments are references; expand them with `expand[]`. Designers only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^prd_[0-9A-Za-z]+$",
              "description": "Unique identifier of the product, prefixed with `prd_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the product, prefixed with `prd_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "category",
                  "variants.category",
                  "variants.contents.design",
                  "variants.contents.stock",
                  "variants.contents.extra",
                  "variants.contents.materials",
                  "attachments",
                  "compliance"
                ]
              },
              "description": "Replace references with full objects. Repeat the parameter for several paths, for example `expand[]=category`. Unknown paths are rejected with `invalid_expand`. An expanded object from another resource needs the read scope of that resource on your API key; without it the reference `{ id, object, name }` is returned instead, with no error. Here `variants.contents.extra` needs `extras:read`, `variants.contents.stock` needs `stocks:read`."
            },
            "required": false,
            "description": "Replace references with full objects. Repeat the parameter for several paths, for example `expand[]=category`. Unknown paths are rejected with `invalid_expand`. An expanded object from another resource needs the read scope of that resource on your API key; without it the reference `{ id, object, name }` is returned instead, with no error. Here `variants.contents.extra` needs `extras:read`, `variants.contents.stock` needs `stocks:read`.",
            "style": "form",
            "explode": true,
            "name": "expand[]",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "The product.",
            "content": {
              "application/json": {
                "example": {
                  "id": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "product",
                  "name": "PR Tracker",
                  "slug": "pr-tracker",
                  "category": {
                    "id": "cat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "category",
                    "name": "Desk accessories"
                  },
                  "review": {
                    "status": "approved",
                    "reason": null
                  },
                  "publishing": {
                    "is_public": true,
                    "unpublished_by": null,
                    "can_publish": true,
                    "blockers": []
                  },
                  "variant_count": 1,
                  "price_range": {
                    "min": {
                      "amount": 5000,
                      "currency": "usd"
                    },
                    "max": {
                      "amount": 5000,
                      "currency": "usd"
                    }
                  },
                  "images": [
                    {
                      "url": "https://cdn.example.com/tracker.png",
                      "primary": true
                    }
                  ],
                  "created_at": "2026-04-21T07:34:06Z",
                  "updated_at": "2026-10-02T11:12:40Z",
                  "description": "A desk tracker for your personal records.",
                  "options": [
                    {
                      "name": "Color",
                      "values": [
                        "Black",
                        "Blue"
                      ]
                    }
                  ],
                  "variants": [
                    {
                      "id": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "variant",
                      "sku": "TRACKER-BLUE",
                      "name": "PR Tracker Blue",
                      "status": "active",
                      "options": [
                        {
                          "name": "Color",
                          "value": "Blue"
                        }
                      ],
                      "category": null,
                      "pricing": {
                        "price": {
                          "amount": 5000,
                          "currency": "usd"
                        },
                        "prices_include_vat": true,
                        "costs": {
                          "manufacturing": {
                            "amount": 332,
                            "currency": "usd"
                          },
                          "extra_parts": {
                            "amount": 40,
                            "currency": "usd"
                          },
                          "platform_fee": {
                            "amount": 250,
                            "currency": "usd"
                          }
                        },
                        "earnings": {
                          "amount": 4378,
                          "currency": "usd"
                        }
                      },
                      "shipping": {
                        "tier": "medium",
                        "determined_by": "volume",
                        "chargeable_weight_grams": 820,
                        "extra_small": false
                      },
                      "contents": [
                        {
                          "type": "design",
                          "quantity": 1,
                          "design": {
                            "id": "dsg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                            "object": "design",
                            "name": "tracker.3mf"
                          },
                          "materials": [
                            {
                              "id": "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                              "object": "material",
                              "name": "PLA Blue",
                              "custom": false
                            }
                          ]
                        },
                        {
                          "type": "extra",
                          "quantity": 4,
                          "unit_price": {
                            "amount": 10,
                            "currency": "usd"
                          },
                          "extra": {
                            "id": "xtr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                            "object": "extra",
                            "name": "M3 screw"
                          }
                        }
                      ],
                      "image": null
                    }
                  ],
                  "attachments": [
                    {
                      "id": "att_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "attachment",
                      "name": "assembly-guide.pdf"
                    }
                  ],
                  "storefront": {
                    "url": "https://dapi.digital/store/acme/pr-tracker",
                    "show_compliance_labels": true,
                    "disclaimers": [
                      {
                        "title": "Production safety",
                        "text": "Keep away from heat."
                      }
                    ]
                  },
                  "custom_label_url": null,
                  "compliance": {
                    "object": "product_compliance",
                    "enforced": false,
                    "complete": false,
                    "missing": 1
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Product"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have no product with this id (`product_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "product_not_found"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      },
      "patch": {
        "operationId": "update_product",
        "x-required-scopes": [
          "products:write"
        ],
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Products"
        ],
        "summary": "Update a product",
        "description": "Changes the product fields you send. Sends designs back to review by 3DAPI: a new design file; turning assembly on or off (every design); a newly added extra that is not approved yet (every design); and, per design, a change to its custom materials, single or multicolor mode, color count or multi-material (AMS) requirement. Nothing else does. Designers only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^prd_[0-9A-Za-z]+$",
              "description": "Unique identifier of the product, prefixed with `prd_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the product, prefixed with `prd_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "name": "PR Tracker v2",
                "category": "cat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                "manufacturing": {
                  "assembly_required": false,
                  "farm_instructions": "Print the lid with the logo facing up."
                }
              },
              "schema": {
                "$ref": "#/components/schemas/UpdateProductRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated product.",
            "content": {
              "application/json": {
                "example": {
                  "id": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "product",
                  "name": "PR Tracker",
                  "slug": "pr-tracker",
                  "category": {
                    "id": "cat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "category",
                    "name": "Desk accessories"
                  },
                  "review": {
                    "status": "approved",
                    "reason": null
                  },
                  "publishing": {
                    "is_public": true,
                    "unpublished_by": null,
                    "can_publish": true,
                    "blockers": []
                  },
                  "variant_count": 1,
                  "price_range": {
                    "min": {
                      "amount": 5000,
                      "currency": "usd"
                    },
                    "max": {
                      "amount": 5000,
                      "currency": "usd"
                    }
                  },
                  "images": [
                    {
                      "url": "https://cdn.example.com/tracker.png",
                      "primary": true
                    }
                  ],
                  "created_at": "2026-04-21T07:34:06Z",
                  "updated_at": "2026-10-02T11:12:40Z",
                  "description": "A desk tracker for your personal records.",
                  "options": [
                    {
                      "name": "Color",
                      "values": [
                        "Black",
                        "Blue"
                      ]
                    }
                  ],
                  "variants": [
                    {
                      "id": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "variant",
                      "sku": "TRACKER-BLUE",
                      "name": "PR Tracker Blue",
                      "status": "active",
                      "options": [
                        {
                          "name": "Color",
                          "value": "Blue"
                        }
                      ],
                      "category": null,
                      "pricing": {
                        "price": {
                          "amount": 5000,
                          "currency": "usd"
                        },
                        "prices_include_vat": true,
                        "costs": {
                          "manufacturing": {
                            "amount": 332,
                            "currency": "usd"
                          },
                          "extra_parts": {
                            "amount": 40,
                            "currency": "usd"
                          },
                          "platform_fee": {
                            "amount": 250,
                            "currency": "usd"
                          }
                        },
                        "earnings": {
                          "amount": 4378,
                          "currency": "usd"
                        }
                      },
                      "shipping": {
                        "tier": "medium",
                        "determined_by": "volume",
                        "chargeable_weight_grams": 820,
                        "extra_small": false
                      },
                      "contents": [
                        {
                          "type": "design",
                          "quantity": 1,
                          "design": {
                            "id": "dsg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                            "object": "design",
                            "name": "tracker.3mf"
                          },
                          "materials": [
                            {
                              "id": "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                              "object": "material",
                              "name": "PLA Blue",
                              "custom": false
                            }
                          ]
                        },
                        {
                          "type": "extra",
                          "quantity": 4,
                          "unit_price": {
                            "amount": 10,
                            "currency": "usd"
                          },
                          "extra": {
                            "id": "xtr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                            "object": "extra",
                            "name": "M3 screw"
                          }
                        }
                      ],
                      "image": null
                    }
                  ],
                  "attachments": [
                    {
                      "id": "att_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "attachment",
                      "name": "assembly-guide.pdf"
                    }
                  ],
                  "storefront": {
                    "url": "https://dapi.digital/store/acme/pr-tracker",
                    "show_compliance_labels": true,
                    "disclaimers": [
                      {
                        "title": "Production safety",
                        "text": "Keep away from heat."
                      }
                    ]
                  },
                  "custom_label_url": null,
                  "compliance": {
                    "object": "product_compliance",
                    "enforced": false,
                    "complete": false,
                    "missing": 1
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Product"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have no product with this id (`product_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "product_not_found"
            ]
          },
          "409": {
            "description": "The slug is already used (`slug_taken`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "slug_taken"
            ]
          },
          "422": {
            "description": "Removing an option value a variant still uses (`option_in_use`); `category_not_found`; an image or attachment to keep is not the product (`image_not_found`, `attachment_not_found`, `custom_label_not_found`, `too_many_primary_images`); or an upload cannot be used: `unknown_upload`, `upload_expired`, `upload_purpose_mismatch`, `upload_missing` (an upload id whose file was never sent), `upload_already_used` (an upload id already used once; each works once).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "option_in_use",
              "category_not_found",
              "image_not_found",
              "attachment_not_found",
              "custom_label_not_found",
              "too_many_primary_images",
              "unknown_upload",
              "upload_expired",
              "upload_purpose_mismatch",
              "upload_missing",
              "upload_already_used"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      },
      "delete": {
        "operationId": "delete_product",
        "x-required-scopes": [
          "products:write"
        ],
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Products"
        ],
        "summary": "Delete a product",
        "description": "Deletes a product, its designs, images and unused variants, as the dashboard does. Variants that orders reference are kept (marked deleted), and orders keep their own frozen copy of the product, so order history is never lost. Cannot be undone. Designers only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^prd_[0-9A-Za-z]+$",
              "description": "Unique identifier of the product, prefixed with `prd_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the product, prefixed with `prd_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The product was deleted.",
            "content": {
              "application/json": {
                "example": {
                  "id": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "product",
                  "deleted": true
                },
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "The id of the deleted product."
                    },
                    "object": {
                      "type": "string",
                      "enum": [
                        "product"
                      ],
                      "description": "Always `product`."
                    },
                    "deleted": {
                      "type": "boolean",
                      "enum": [
                        true
                      ],
                      "description": "Always `true`: the deletion succeeded."
                    }
                  },
                  "required": [
                    "id",
                    "object",
                    "deleted"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have no product with this id (`product_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "product_not_found"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/products/{id}/publish": {
      "post": {
        "operationId": "publish_product",
        "x-required-scopes": [
          "products:write"
        ],
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Products"
        ],
        "summary": "Publish a product",
        "description": "Makes the product public on your storefront. Refused while anything in `publishing.blockers` remains, which includes an inactive store: you must be registered as a business and have your public store turned on. Counts toward your plan limit of public products. Designers only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^prd_[0-9A-Za-z]+$",
              "description": "Unique identifier of the product, prefixed with `prd_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the product, prefixed with `prd_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Any unique string, for example a UUID. Sending the same request again with the same key within 24 hours returns the first response without doing anything twice. Reusing a key with a different body is refused with `idempotency_key_reused`. A refused request (4xx) frees the key so you can correct and resend it; a server error (5xx) is kept and returned again, because the write may already have happened: check the resource before retrying with a new key."
            },
            "required": false,
            "description": "Any unique string, for example a UUID. Sending the same request again with the same key within 24 hours returns the first response without doing anything twice. Reusing a key with a different body is refused with `idempotency_key_reused`. A refused request (4xx) frees the key so you can correct and resend it; a server error (5xx) is kept and returned again, because the write may already have happened: check the resource before retrying with a new key.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "responses": {
          "200": {
            "description": "The product, now public.",
            "content": {
              "application/json": {
                "example": {
                  "id": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "product",
                  "name": "PR Tracker",
                  "slug": "pr-tracker",
                  "category": {
                    "id": "cat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "category",
                    "name": "Desk accessories"
                  },
                  "review": {
                    "status": "approved",
                    "reason": null
                  },
                  "publishing": {
                    "is_public": true,
                    "unpublished_by": null,
                    "can_publish": true,
                    "blockers": []
                  },
                  "variant_count": 1,
                  "price_range": {
                    "min": {
                      "amount": 5000,
                      "currency": "usd"
                    },
                    "max": {
                      "amount": 5000,
                      "currency": "usd"
                    }
                  },
                  "images": [
                    {
                      "url": "https://cdn.example.com/tracker.png",
                      "primary": true
                    }
                  ],
                  "created_at": "2026-04-21T07:34:06Z",
                  "updated_at": "2026-10-02T11:12:40Z",
                  "description": "A desk tracker for your personal records.",
                  "options": [
                    {
                      "name": "Color",
                      "values": [
                        "Black",
                        "Blue"
                      ]
                    }
                  ],
                  "variants": [
                    {
                      "id": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "variant",
                      "sku": "TRACKER-BLUE",
                      "name": "PR Tracker Blue",
                      "status": "active",
                      "options": [
                        {
                          "name": "Color",
                          "value": "Blue"
                        }
                      ],
                      "category": null,
                      "pricing": {
                        "price": {
                          "amount": 5000,
                          "currency": "usd"
                        },
                        "prices_include_vat": true,
                        "costs": {
                          "manufacturing": {
                            "amount": 332,
                            "currency": "usd"
                          },
                          "extra_parts": {
                            "amount": 40,
                            "currency": "usd"
                          },
                          "platform_fee": {
                            "amount": 250,
                            "currency": "usd"
                          }
                        },
                        "earnings": {
                          "amount": 4378,
                          "currency": "usd"
                        }
                      },
                      "shipping": {
                        "tier": "medium",
                        "determined_by": "volume",
                        "chargeable_weight_grams": 820,
                        "extra_small": false
                      },
                      "contents": [
                        {
                          "type": "design",
                          "quantity": 1,
                          "design": {
                            "id": "dsg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                            "object": "design",
                            "name": "tracker.3mf"
                          },
                          "materials": [
                            {
                              "id": "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                              "object": "material",
                              "name": "PLA Blue",
                              "custom": false
                            }
                          ]
                        },
                        {
                          "type": "extra",
                          "quantity": 4,
                          "unit_price": {
                            "amount": 10,
                            "currency": "usd"
                          },
                          "extra": {
                            "id": "xtr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                            "object": "extra",
                            "name": "M3 screw"
                          }
                        }
                      ],
                      "image": null
                    }
                  ],
                  "attachments": [
                    {
                      "id": "att_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "attachment",
                      "name": "assembly-guide.pdf"
                    }
                  ],
                  "storefront": {
                    "url": "https://dapi.digital/store/acme/pr-tracker",
                    "show_compliance_labels": true,
                    "disclaimers": [
                      {
                        "title": "Production safety",
                        "text": "Keep away from heat."
                      }
                    ]
                  },
                  "custom_label_url": null,
                  "compliance": {
                    "object": "product_compliance",
                    "enforced": false,
                    "complete": false,
                    "missing": 1
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Product"
                }
              }
            }
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "Your plan limit of public products is reached (`plan_limit_reached`); `details.fix_url` points at upgrading.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "plan_limit_reached"
            ]
          },
          "404": {
            "description": "You have no product with this id (`product_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "product_not_found"
            ]
          },
          "409": {
            "description": "Something blocks publishing (`cannot_publish`); `details.blockers` lists every blocker with its code and what to do, the same list as `publishing.blockers`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "cannot_publish"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/products/{id}/unpublish": {
      "post": {
        "operationId": "unpublish_product",
        "x-required-scopes": [
          "products:write"
        ],
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Products"
        ],
        "summary": "Unpublish a product",
        "description": "Takes the product off your storefront. Always allowed. Designers only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^prd_[0-9A-Za-z]+$",
              "description": "Unique identifier of the product, prefixed with `prd_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the product, prefixed with `prd_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Any unique string, for example a UUID. Sending the same request again with the same key within 24 hours returns the first response without doing anything twice. Reusing a key with a different body is refused with `idempotency_key_reused`. A refused request (4xx) frees the key so you can correct and resend it; a server error (5xx) is kept and returned again, because the write may already have happened: check the resource before retrying with a new key."
            },
            "required": false,
            "description": "Any unique string, for example a UUID. Sending the same request again with the same key within 24 hours returns the first response without doing anything twice. Reusing a key with a different body is refused with `idempotency_key_reused`. A refused request (4xx) frees the key so you can correct and resend it; a server error (5xx) is kept and returned again, because the write may already have happened: check the resource before retrying with a new key.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "responses": {
          "200": {
            "description": "The product, now private.",
            "content": {
              "application/json": {
                "example": {
                  "id": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "product",
                  "name": "PR Tracker",
                  "slug": "pr-tracker",
                  "category": {
                    "id": "cat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "category",
                    "name": "Desk accessories"
                  },
                  "review": {
                    "status": "approved",
                    "reason": null
                  },
                  "publishing": {
                    "is_public": false,
                    "unpublished_by": "seller",
                    "can_publish": true,
                    "blockers": []
                  },
                  "variant_count": 1,
                  "price_range": {
                    "min": {
                      "amount": 5000,
                      "currency": "usd"
                    },
                    "max": {
                      "amount": 5000,
                      "currency": "usd"
                    }
                  },
                  "images": [
                    {
                      "url": "https://cdn.example.com/tracker.png",
                      "primary": true
                    }
                  ],
                  "created_at": "2026-04-21T07:34:06Z",
                  "updated_at": "2026-10-02T11:12:40Z",
                  "description": "A desk tracker for your personal records.",
                  "options": [
                    {
                      "name": "Color",
                      "values": [
                        "Black",
                        "Blue"
                      ]
                    }
                  ],
                  "variants": [
                    {
                      "id": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "variant",
                      "sku": "TRACKER-BLUE",
                      "name": "PR Tracker Blue",
                      "status": "active",
                      "options": [
                        {
                          "name": "Color",
                          "value": "Blue"
                        }
                      ],
                      "category": null,
                      "pricing": {
                        "price": {
                          "amount": 5000,
                          "currency": "usd"
                        },
                        "prices_include_vat": true,
                        "costs": {
                          "manufacturing": {
                            "amount": 332,
                            "currency": "usd"
                          },
                          "extra_parts": {
                            "amount": 40,
                            "currency": "usd"
                          },
                          "platform_fee": {
                            "amount": 250,
                            "currency": "usd"
                          }
                        },
                        "earnings": {
                          "amount": 4378,
                          "currency": "usd"
                        }
                      },
                      "shipping": {
                        "tier": "medium",
                        "determined_by": "volume",
                        "chargeable_weight_grams": 820,
                        "extra_small": false
                      },
                      "contents": [
                        {
                          "type": "design",
                          "quantity": 1,
                          "design": {
                            "id": "dsg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                            "object": "design",
                            "name": "tracker.3mf"
                          },
                          "materials": [
                            {
                              "id": "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                              "object": "material",
                              "name": "PLA Blue",
                              "custom": false
                            }
                          ]
                        },
                        {
                          "type": "extra",
                          "quantity": 4,
                          "unit_price": {
                            "amount": 10,
                            "currency": "usd"
                          },
                          "extra": {
                            "id": "xtr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                            "object": "extra",
                            "name": "M3 screw"
                          }
                        }
                      ],
                      "image": null
                    }
                  ],
                  "attachments": [
                    {
                      "id": "att_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "attachment",
                      "name": "assembly-guide.pdf"
                    }
                  ],
                  "storefront": {
                    "url": "https://dapi.digital/store/acme/pr-tracker",
                    "show_compliance_labels": true,
                    "disclaimers": [
                      {
                        "title": "Production safety",
                        "text": "Keep away from heat."
                      }
                    ]
                  },
                  "custom_label_url": null,
                  "compliance": {
                    "object": "product_compliance",
                    "enforced": false,
                    "complete": false,
                    "missing": 1
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Product"
                }
              }
            }
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have no product with this id (`product_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "product_not_found"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/products/{id}/designs": {
      "post": {
        "operationId": "add_design",
        "x-required-scopes": [
          "products:write"
        ],
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Designs"
        ],
        "summary": "Add a design to a product",
        "description": "Adds a model file to a product, with the materials it may be printed in. The new design is pending review by 3DAPI, so the product is too until it is approved. Designers only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^prd_[0-9A-Za-z]+$",
              "description": "Unique identifier of the product, prefixed with `prd_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the product, prefixed with `prd_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Any unique string, for example a UUID. Sending the same request again with the same key within 24 hours returns the first response without doing anything twice. Reusing a key with a different body is refused with `idempotency_key_reused`. A refused request (4xx) frees the key so you can correct and resend it; a server error (5xx) is kept and returned again, because the write may already have happened: check the resource before retrying with a new key."
            },
            "required": false,
            "description": "Any unique string, for example a UUID. Sending the same request again with the same key within 24 hours returns the first response without doing anything twice. Reusing a key with a different body is refused with `idempotency_key_reused`. A refused request (4xx) frees the key so you can correct and resend it; a server error (5xx) is kept and returned again, because the write may already have happened: check the resource before retrying with a new key.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "file": {
                  "upload": "upl_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                },
                "name": "Tracker lid",
                "materials": {
                  "mode": "single",
                  "allowed": [
                    "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                  ]
                }
              },
              "schema": {
                "$ref": "#/components/schemas/AddDesignRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The new design, pending review.",
            "content": {
              "application/json": {
                "example": {
                  "id": "dsg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "design",
                  "name": "tracker.3mf",
                  "owner": {
                    "id": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "product",
                    "name": "PR Tracker"
                  },
                  "review": {
                    "status": "pending",
                    "reason": null,
                    "reviewed_at": null
                  },
                  "files": [
                    {
                      "id": "dfl_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "design_file",
                      "name": "tracker.3mf",
                      "format": "3mf",
                      "size_bytes": 2758723,
                      "download_url": "https://files.example.com/tracker.3mf?signature=abc",
                      "uploaded_at": "2026-04-21T07:34:06Z",
                      "production_version": {
                        "name": "farm-settings-tracker.3mf",
                        "format": "3mf",
                        "size_bytes": 2761090,
                        "download_url": "https://files.example.com/farm-settings-tracker.3mf?signature=def",
                        "prepared_at": "2026-04-21T07:36:40Z"
                      }
                    }
                  ],
                  "materials": {
                    "mode": "single",
                    "allowed": [
                      {
                        "id": "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "material",
                        "name": "PLA Black",
                        "custom": false
                      },
                      {
                        "id": null,
                        "object": "material",
                        "name": "PETG Matte",
                        "custom": true
                      }
                    ]
                  },
                  "manufacturing": {
                    "colors": 1,
                    "requires_ams": false,
                    "assembly_required": true,
                    "farm_instructions": null,
                    "extra_parts_note": "Two M2 screws"
                  },
                  "created_at": "2026-04-21T07:34:06Z",
                  "updated_at": "2026-04-21T09:10:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Design"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have no product with this id (`product_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "product_not_found"
            ]
          },
          "422": {
            "description": "`material_not_found`, `combination_slots_mismatch`, or the file upload cannot be used: `unknown_upload`, `upload_expired`, `upload_purpose_mismatch`, `upload_missing`, `upload_already_used`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "material_not_found",
              "combination_slots_mismatch",
              "unknown_upload",
              "upload_expired",
              "upload_purpose_mismatch",
              "upload_missing",
              "upload_already_used"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/designs/{id}": {
      "patch": {
        "operationId": "update_design",
        "x-required-scopes": [
          "products:write"
        ],
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Designs"
        ],
        "summary": "Update a design",
        "description": "Changes a design name or its allowed materials. Sends designs back to review by 3DAPI: a new design file; turning assembly on or off (every design); a newly added extra that is not approved yet (every design); and, per design, a change to its custom materials, single or multicolor mode, color count or multi-material (AMS) requirement. Nothing else does. Designers only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^dsg_[0-9A-Za-z]+$",
              "description": "Unique identifier of the design, prefixed with `dsg_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "dsg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the design, prefixed with `dsg_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "materials": {
                  "mode": "single",
                  "allowed": [
                    "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    {
                      "custom": "Silk white PLA"
                    }
                  ]
                }
              },
              "schema": {
                "$ref": "#/components/schemas/UpdateDesignRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated design.",
            "content": {
              "application/json": {
                "example": {
                  "id": "dsg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "design",
                  "name": "tracker.3mf",
                  "owner": {
                    "id": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "product",
                    "name": "PR Tracker"
                  },
                  "review": {
                    "status": "approved",
                    "reason": null,
                    "reviewed_at": "2026-04-21T09:10:00Z"
                  },
                  "files": [
                    {
                      "id": "dfl_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "design_file",
                      "name": "tracker.3mf",
                      "format": "3mf",
                      "size_bytes": 2758723,
                      "download_url": "https://files.example.com/tracker.3mf?signature=abc",
                      "uploaded_at": "2026-04-21T07:34:06Z",
                      "production_version": {
                        "name": "farm-settings-tracker.3mf",
                        "format": "3mf",
                        "size_bytes": 2761090,
                        "download_url": "https://files.example.com/farm-settings-tracker.3mf?signature=def",
                        "prepared_at": "2026-04-21T07:36:40Z"
                      }
                    }
                  ],
                  "materials": {
                    "mode": "single",
                    "allowed": [
                      {
                        "id": "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "material",
                        "name": "PLA Black",
                        "custom": false
                      },
                      {
                        "id": null,
                        "object": "material",
                        "name": "PETG Matte",
                        "custom": true
                      }
                    ]
                  },
                  "manufacturing": {
                    "colors": 1,
                    "requires_ams": false,
                    "assembly_required": true,
                    "farm_instructions": null,
                    "extra_parts_note": "Two M2 screws"
                  },
                  "created_at": "2026-04-21T07:34:06Z",
                  "updated_at": "2026-04-21T09:10:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Design"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have no design with this id (`design_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "design_not_found"
            ]
          },
          "409": {
            "description": "The design belongs to a stock (`design_belongs_to_stock`); stock designs are changed from the dashboard for now.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "design_belongs_to_stock"
            ]
          },
          "422": {
            "description": "A material a variant still uses was removed (`material_in_use`), `material_not_found` or `combination_slots_mismatch`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "material_in_use",
              "material_not_found",
              "combination_slots_mismatch"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      },
      "delete": {
        "operationId": "delete_design",
        "x-required-scopes": [
          "products:write"
        ],
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Designs"
        ],
        "summary": "Delete a design",
        "description": "Removes a design from its product. Not possible while a variant uses it: change or delete those variants first. Designers only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^dsg_[0-9A-Za-z]+$",
              "description": "Unique identifier of the design, prefixed with `dsg_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "dsg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the design, prefixed with `dsg_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The design was deleted.",
            "content": {
              "application/json": {
                "example": {
                  "id": "dsg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "design",
                  "deleted": true
                },
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "The id of the deleted design."
                    },
                    "object": {
                      "type": "string",
                      "enum": [
                        "design"
                      ],
                      "description": "Always `design`."
                    },
                    "deleted": {
                      "type": "boolean",
                      "enum": [
                        true
                      ],
                      "description": "Always `true`: the deletion succeeded."
                    }
                  },
                  "required": [
                    "id",
                    "object",
                    "deleted"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have no design with this id (`design_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "design_not_found"
            ]
          },
          "409": {
            "description": "A variant still uses this design (`design_in_use`), or the design belongs to a stock (`design_belongs_to_stock`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "design_in_use",
              "design_belongs_to_stock"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      },
      "get": {
        "operationId": "get_design",
        "x-required-scopes": [
          "products:read",
          "stocks:read"
        ],
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Designs"
        ],
        "summary": "Get a design",
        "description": "Returns one of your designs: its files with download links, allowed materials, production settings and review status. Designers only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^dsg_[0-9A-Za-z]+$",
              "description": "Unique identifier of the design, prefixed with `dsg_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "dsg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the design, prefixed with `dsg_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The design.",
            "content": {
              "application/json": {
                "example": {
                  "id": "dsg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "design",
                  "name": "tracker.3mf",
                  "owner": {
                    "id": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "product",
                    "name": "PR Tracker"
                  },
                  "review": {
                    "status": "approved",
                    "reason": null,
                    "reviewed_at": "2026-04-21T09:10:00Z"
                  },
                  "files": [
                    {
                      "id": "dfl_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "design_file",
                      "name": "tracker.3mf",
                      "format": "3mf",
                      "size_bytes": 2758723,
                      "download_url": "https://files.example.com/tracker.3mf?signature=abc",
                      "uploaded_at": "2026-04-21T07:34:06Z",
                      "production_version": {
                        "name": "farm-settings-tracker.3mf",
                        "format": "3mf",
                        "size_bytes": 2761090,
                        "download_url": "https://files.example.com/farm-settings-tracker.3mf?signature=def",
                        "prepared_at": "2026-04-21T07:36:40Z"
                      }
                    }
                  ],
                  "materials": {
                    "mode": "single",
                    "allowed": [
                      {
                        "id": "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "material",
                        "name": "PLA Black",
                        "custom": false
                      },
                      {
                        "id": null,
                        "object": "material",
                        "name": "PETG Matte",
                        "custom": true
                      }
                    ]
                  },
                  "manufacturing": {
                    "colors": 1,
                    "requires_ams": false,
                    "assembly_required": true,
                    "farm_instructions": null,
                    "extra_parts_note": "Two M2 screws"
                  },
                  "created_at": "2026-04-21T07:34:06Z",
                  "updated_at": "2026-04-21T09:10:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Design"
                }
              }
            }
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have no design with this id (`design_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "design_not_found"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/products/{id}/variants": {
      "post": {
        "operationId": "create_variant",
        "x-required-scopes": [
          "products:write"
        ],
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Products"
        ],
        "summary": "Add a variant",
        "description": "Adds an orderable variant to a product: its SKU, option values, price and what it is made of. A newly added extra that is not approved yet sends the product designs back to review. Up to 200 variants per product. Designers only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^prd_[0-9A-Za-z]+$",
              "description": "Unique identifier of the product, prefixed with `prd_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the product, prefixed with `prd_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Any unique string, for example a UUID. Sending the same request again with the same key within 24 hours returns the first response without doing anything twice. Reusing a key with a different body is refused with `idempotency_key_reused`. A refused request (4xx) frees the key so you can correct and resend it; a server error (5xx) is kept and returned again, because the write may already have happened: check the resource before retrying with a new key."
            },
            "required": false,
            "description": "Any unique string, for example a UUID. Sending the same request again with the same key within 24 hours returns the first response without doing anything twice. Reusing a key with a different body is refused with `idempotency_key_reused`. A refused request (4xx) frees the key so you can correct and resend it; a server error (5xx) is kept and returned again, because the write may already have happened: check the resource before retrying with a new key.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "sku": "TRACKER-BLACK",
                "name": "PR Tracker Black",
                "options": [
                  {
                    "name": "Color",
                    "value": "Black"
                  }
                ],
                "price": {
                  "amount": 5000,
                  "currency": "usd"
                },
                "contents": [
                  {
                    "type": "design",
                    "design": "dsg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "quantity": 1,
                    "materials": [
                      "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                    ]
                  },
                  {
                    "type": "extra",
                    "extra": "xtr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "quantity": 4
                  }
                ]
              },
              "schema": {
                "$ref": "#/components/schemas/CreateVariantRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The new variant.",
            "content": {
              "application/json": {
                "example": {
                  "id": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "variant",
                  "sku": "TRACKER-BLUE",
                  "name": "PR Tracker Blue",
                  "status": "active",
                  "options": [
                    {
                      "name": "Color",
                      "value": "Blue"
                    }
                  ],
                  "category": null,
                  "pricing": {
                    "price": {
                      "amount": 5000,
                      "currency": "usd"
                    },
                    "prices_include_vat": true,
                    "costs": {
                      "manufacturing": {
                        "amount": 332,
                        "currency": "usd"
                      },
                      "extra_parts": {
                        "amount": 40,
                        "currency": "usd"
                      },
                      "platform_fee": {
                        "amount": 250,
                        "currency": "usd"
                      }
                    },
                    "earnings": {
                      "amount": 4378,
                      "currency": "usd"
                    }
                  },
                  "shipping": {
                    "tier": "medium",
                    "determined_by": "volume",
                    "chargeable_weight_grams": 820,
                    "extra_small": false
                  },
                  "contents": [
                    {
                      "type": "design",
                      "quantity": 1,
                      "design": {
                        "id": "dsg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "design",
                        "name": "tracker.3mf"
                      },
                      "materials": [
                        {
                          "id": "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "material",
                          "name": "PLA Blue",
                          "custom": false
                        }
                      ]
                    },
                    {
                      "type": "extra",
                      "quantity": 4,
                      "unit_price": {
                        "amount": 10,
                        "currency": "usd"
                      },
                      "extra": {
                        "id": "xtr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "extra",
                        "name": "M3 screw"
                      }
                    }
                  ],
                  "image": null
                },
                "schema": {
                  "$ref": "#/components/schemas/Variant"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have no product with this id (`product_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "product_not_found"
            ]
          },
          "409": {
            "description": "The SKU is already used (`sku_already_used`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "sku_already_used"
            ]
          },
          "422": {
            "description": "`option_mismatch`, `material_not_allowed`, `material_not_found`, `extra_not_found`, `extra_not_approved` (a rejected extra), `stock_not_found`, `stock_not_approved`, `category_not_found`, `variant_needs_design_or_stock`, `price_below_minimum` (minimum in `details.minimum`), `currency_not_supported`, `design_not_in_product` or `too_many_variants`; or the image upload cannot be used: `unknown_upload`, `upload_expired`, `upload_purpose_mismatch`, `upload_missing` (an upload id whose file was never sent), `upload_already_used` (an upload id already used once; each works once).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "option_mismatch",
              "material_not_allowed",
              "material_not_found",
              "extra_not_found",
              "extra_not_approved",
              "stock_not_found",
              "stock_not_approved",
              "category_not_found",
              "variant_needs_design_or_stock",
              "price_below_minimum",
              "currency_not_supported",
              "design_not_in_product",
              "too_many_variants",
              "unknown_upload",
              "upload_expired",
              "upload_purpose_mismatch",
              "upload_missing",
              "upload_already_used"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/variants/{id}": {
      "patch": {
        "operationId": "update_variant",
        "x-required-scopes": [
          "products:write"
        ],
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Products"
        ],
        "summary": "Update a variant",
        "description": "Changes the variant fields you send. Set `status: inactive` to stop selling it without deleting it. Designers only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^var_[0-9A-Za-z]+$",
              "description": "Unique identifier of the variant, prefixed with `var_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the variant, prefixed with `var_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "price": {
                  "amount": 5500,
                  "currency": "usd"
                },
                "status": "active"
              },
              "schema": {
                "$ref": "#/components/schemas/UpdateVariantRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated variant.",
            "content": {
              "application/json": {
                "example": {
                  "id": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "variant",
                  "sku": "TRACKER-BLUE",
                  "name": "PR Tracker Blue",
                  "status": "active",
                  "options": [
                    {
                      "name": "Color",
                      "value": "Blue"
                    }
                  ],
                  "category": null,
                  "pricing": {
                    "price": {
                      "amount": 5000,
                      "currency": "usd"
                    },
                    "prices_include_vat": true,
                    "costs": {
                      "manufacturing": {
                        "amount": 332,
                        "currency": "usd"
                      },
                      "extra_parts": {
                        "amount": 40,
                        "currency": "usd"
                      },
                      "platform_fee": {
                        "amount": 250,
                        "currency": "usd"
                      }
                    },
                    "earnings": {
                      "amount": 4378,
                      "currency": "usd"
                    }
                  },
                  "shipping": {
                    "tier": "medium",
                    "determined_by": "volume",
                    "chargeable_weight_grams": 820,
                    "extra_small": false
                  },
                  "contents": [
                    {
                      "type": "design",
                      "quantity": 1,
                      "design": {
                        "id": "dsg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "design",
                        "name": "tracker.3mf"
                      },
                      "materials": [
                        {
                          "id": "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "material",
                          "name": "PLA Blue",
                          "custom": false
                        }
                      ]
                    },
                    {
                      "type": "extra",
                      "quantity": 4,
                      "unit_price": {
                        "amount": 10,
                        "currency": "usd"
                      },
                      "extra": {
                        "id": "xtr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "extra",
                        "name": "M3 screw"
                      }
                    }
                  ],
                  "image": null
                },
                "schema": {
                  "$ref": "#/components/schemas/Variant"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have no variant with this id (`variant_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "variant_not_found"
            ]
          },
          "409": {
            "description": "The SKU is already used (`sku_already_used`), or the variant was imported from a sales channel that owns its SKU and price (`variant_managed_by_channel`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "sku_already_used",
              "variant_managed_by_channel"
            ]
          },
          "422": {
            "description": "`option_mismatch`, `material_not_allowed`, `material_not_found`, `extra_not_found`, `extra_not_approved` (a rejected extra), `stock_not_found`, `stock_not_approved`, `category_not_found`, `variant_needs_design_or_stock`, `price_below_minimum` (minimum in `details.minimum`), `currency_not_supported` or `design_not_in_product`. The price floor is only checked when the price or the contents change.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "option_mismatch",
              "material_not_allowed",
              "material_not_found",
              "extra_not_found",
              "extra_not_approved",
              "stock_not_found",
              "stock_not_approved",
              "category_not_found",
              "variant_needs_design_or_stock",
              "price_below_minimum",
              "currency_not_supported",
              "design_not_in_product"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      },
      "delete": {
        "operationId": "delete_variant",
        "x-required-scopes": [
          "products:write"
        ],
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Products"
        ],
        "summary": "Delete a variant",
        "description": "Removes a variant, as the dashboard does: kept and marked deleted when orders reference it (so order history stays intact), removed otherwise. Either way it stops being sold and no longer appears on the product. Designers only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^var_[0-9A-Za-z]+$",
              "description": "Unique identifier of the variant, prefixed with `var_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the variant, prefixed with `var_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The variant was deleted.",
            "content": {
              "application/json": {
                "example": {
                  "id": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "variant",
                  "deleted": true
                },
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "The id of the deleted variant."
                    },
                    "object": {
                      "type": "string",
                      "enum": [
                        "variant"
                      ],
                      "description": "Always `variant`."
                    },
                    "deleted": {
                      "type": "boolean",
                      "enum": [
                        true
                      ],
                      "description": "Always `true`: the deletion succeeded."
                    }
                  },
                  "required": [
                    "id",
                    "object",
                    "deleted"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have no variant with this id (`variant_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "variant_not_found"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/products/{id}/compliance/documents": {
      "post": {
        "operationId": "upload_compliance_document",
        "x-required-scopes": [
          "products:write"
        ],
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Products"
        ],
        "summary": "Upload a compliance document",
        "description": "Adds a document for one requirement of the product. It is pending review by 3DAPI, and the requirement shows `pending_review` until then. To replace a document, upload the new one and delete the old one. Returns the whole compliance view. Designers only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^prd_[0-9A-Za-z]+$",
              "description": "Unique identifier of the product, prefixed with `prd_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the product, prefixed with `prd_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Any unique string, for example a UUID. Sending the same request again with the same key within 24 hours returns the first response without doing anything twice. Reusing a key with a different body is refused with `idempotency_key_reused`. A refused request (4xx) frees the key so you can correct and resend it; a server error (5xx) is kept and returned again, because the write may already have happened: check the resource before retrying with a new key."
            },
            "required": false,
            "description": "Any unique string, for example a UUID. Sending the same request again with the same key within 24 hours returns the first response without doing anything twice. Reusing a key with a different body is refused with `idempotency_key_reused`. A refused request (4xx) frees the key so you can correct and resend it; a server error (5xx) is kept and returned again, because the write may already have happened: check the resource before retrying with a new key.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "requirement": "material-toxicity-assessment",
                "file": {
                  "upload": "upl_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                }
              },
              "schema": {
                "$ref": "#/components/schemas/UploadComplianceDocumentRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The compliance view with the new document.",
            "content": {
              "application/json": {
                "example": {
                  "object": "product_compliance",
                  "product": {
                    "id": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "product",
                    "name": "Action Figure"
                  },
                  "enforced": false,
                  "complete": false,
                  "requirements": [
                    {
                      "key": "material-toxicity-assessment",
                      "name": "Material Toxicity Assessment",
                      "status": "approved",
                      "required_by": [
                        {
                          "id": "cat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "category",
                          "name": "Action Figures & Dolls"
                        }
                      ],
                      "documents": [
                        {
                          "id": "cdc_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "compliance_document",
                          "name": "toxicity-report.pdf",
                          "format": "pdf",
                          "size_bytes": 182344,
                          "url": "https://files.example.com/compliance/toxicity-report.pdf?signature=abc",
                          "review": {
                            "status": "approved",
                            "reason": null,
                            "reviewed_at": "2026-10-02T10:00:00Z"
                          },
                          "uploaded_at": "2026-10-01T08:00:00Z"
                        }
                      ]
                    },
                    {
                      "key": "technical-file-and-risk-assessment",
                      "name": "Technical File & Risk Assessment",
                      "status": "missing",
                      "required_by": [
                        {
                          "id": "cat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "category",
                          "name": "Action Figures & Dolls"
                        }
                      ],
                      "documents": []
                    }
                  ],
                  "other_documents": []
                },
                "schema": {
                  "$ref": "#/components/schemas/ProductCompliance"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have no product with this id (`product_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "product_not_found"
            ]
          },
          "422": {
            "description": "The requirement does not apply to this product (`unknown_requirement`); `details.requirements` lists the valid keys.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "unknown_requirement"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/compliance-documents/{id}": {
      "delete": {
        "operationId": "delete_compliance_document",
        "x-required-scopes": [
          "products:write"
        ],
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Products"
        ],
        "summary": "Delete a compliance document",
        "description": "Removes a document you uploaded. Returns the compliance view of its product, so you see which requirements it leaves missing. Designers only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^cdc_[0-9A-Za-z]+$",
              "description": "Unique identifier of the compliance document, prefixed with `cdc_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "cdc_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the compliance document, prefixed with `cdc_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The compliance view without the document.",
            "content": {
              "application/json": {
                "example": {
                  "object": "product_compliance",
                  "product": {
                    "id": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "product",
                    "name": "Action Figure"
                  },
                  "enforced": false,
                  "complete": false,
                  "requirements": [
                    {
                      "key": "material-toxicity-assessment",
                      "name": "Material Toxicity Assessment",
                      "status": "approved",
                      "required_by": [
                        {
                          "id": "cat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "category",
                          "name": "Action Figures & Dolls"
                        }
                      ],
                      "documents": [
                        {
                          "id": "cdc_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "compliance_document",
                          "name": "toxicity-report.pdf",
                          "format": "pdf",
                          "size_bytes": 182344,
                          "url": "https://files.example.com/compliance/toxicity-report.pdf?signature=abc",
                          "review": {
                            "status": "approved",
                            "reason": null,
                            "reviewed_at": "2026-10-02T10:00:00Z"
                          },
                          "uploaded_at": "2026-10-01T08:00:00Z"
                        }
                      ]
                    },
                    {
                      "key": "technical-file-and-risk-assessment",
                      "name": "Technical File & Risk Assessment",
                      "status": "missing",
                      "required_by": [
                        {
                          "id": "cat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "category",
                          "name": "Action Figures & Dolls"
                        }
                      ],
                      "documents": []
                    }
                  ],
                  "other_documents": []
                },
                "schema": {
                  "$ref": "#/components/schemas/ProductCompliance"
                }
              }
            }
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have no compliance document with this id (`compliance_document_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "compliance_document_not_found"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/products/{id}/compliance": {
      "get": {
        "operationId": "get_product_compliance",
        "x-required-scopes": [
          "products:read"
        ],
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Products"
        ],
        "summary": "Get product compliance",
        "description": "Returns every safety document the product categories require, the documents you uploaded for each and their review status. Use it to see what is still missing before publishing to EU/UK buyers. Designers only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^prd_[0-9A-Za-z]+$",
              "description": "Unique identifier of the product, prefixed with `prd_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the product, prefixed with `prd_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The compliance view.",
            "content": {
              "application/json": {
                "example": {
                  "object": "product_compliance",
                  "product": {
                    "id": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "product",
                    "name": "Action Figure"
                  },
                  "enforced": false,
                  "complete": false,
                  "requirements": [
                    {
                      "key": "material-toxicity-assessment",
                      "name": "Material Toxicity Assessment",
                      "status": "approved",
                      "required_by": [
                        {
                          "id": "cat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "category",
                          "name": "Action Figures & Dolls"
                        }
                      ],
                      "documents": [
                        {
                          "id": "cdc_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "compliance_document",
                          "name": "toxicity-report.pdf",
                          "format": "pdf",
                          "size_bytes": 182344,
                          "url": "https://files.example.com/compliance/toxicity-report.pdf?signature=abc",
                          "review": {
                            "status": "approved",
                            "reason": null,
                            "reviewed_at": "2026-10-02T10:00:00Z"
                          },
                          "uploaded_at": "2026-10-01T08:00:00Z"
                        }
                      ]
                    },
                    {
                      "key": "technical-file-and-risk-assessment",
                      "name": "Technical File & Risk Assessment",
                      "status": "missing",
                      "required_by": [
                        {
                          "id": "cat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "category",
                          "name": "Action Figures & Dolls"
                        }
                      ],
                      "documents": []
                    }
                  ],
                  "other_documents": []
                },
                "schema": {
                  "$ref": "#/components/schemas/ProductCompliance"
                }
              }
            }
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have no product with this id (`product_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "product_not_found"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/orders": {
      "post": {
        "operationId": "create_order",
        "x-required-scopes": [
          "orders:write"
        ],
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Orders"
        ],
        "summary": "Create an order",
        "description": "Orders your own variants for a customer, for example a sale on your own website. You pay at cost (production price, shipping and VAT) from your wallet; if the balance is short, the difference is charged to your saved card. The order comes back paid, numbered `2AP...`, with `channel: api`, and goes to production. If payment fails, no order is created. Designers only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Any unique string, for example a UUID. Sending the same request again with the same key within 24 hours returns the first response without doing anything twice. Reusing a key with a different body is refused with `idempotency_key_reused`. A refused request (4xx) frees the key so you can correct and resend it; a server error (5xx) is kept and returned again, because the write may already have happened: check the resource before retrying with a new key."
            },
            "required": false,
            "description": "Any unique string, for example a UUID. Sending the same request again with the same key within 24 hours returns the first response without doing anything twice. Reusing a key with a different body is refused with `idempotency_key_reused`. A refused request (4xx) frees the key so you can correct and resend it; a server error (5xx) is kept and returned again, because the write may already have happened: check the resource before retrying with a new key.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "items": [
                  {
                    "variant": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "quantity": 2
                  }
                ],
                "customer": {
                  "name": "Ana Popescu",
                  "email": "ana@example.com",
                  "phone": "+40712345678"
                },
                "shipping_address": {
                  "line1": "Str. Test 1",
                  "city": "Cluj-Napoca",
                  "postal_code": "400001",
                  "country_code": "RO"
                },
                "reference": "SHOP-10023"
              },
              "schema": {
                "$ref": "#/components/schemas/CreateOrderRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The order, paid.",
            "content": {
              "application/json": {
                "example": {
                  "id": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "order",
                  "number": "2AP7KD2XQ",
                  "channel": "api",
                  "status": "paid",
                  "customer": {
                    "name": "Ana Popescu",
                    "email": "ana@example.com",
                    "phone": null,
                    "company": null
                  },
                  "shipping_address": {
                    "formatted": "Str. Test 1, Cluj, RO",
                    "city": "Cluj",
                    "country": "Romania",
                    "country_code": "RO"
                  },
                  "charges": {
                    "paid_by": "you",
                    "items": {
                      "amount": 1800,
                      "currency": "usd"
                    },
                    "shipping": {
                      "amount": 600,
                      "currency": "usd"
                    },
                    "vat": {
                      "amount": {
                        "amount": 0,
                        "currency": "usd"
                      },
                      "rate": 0,
                      "included": false,
                      "country": null
                    },
                    "sample_credit": null,
                    "total": {
                      "amount": 2400,
                      "currency": "usd"
                    }
                  },
                  "earnings": null,
                  "shipping": {
                    "tier": "small",
                    "carrier": null,
                    "tracking_number": null,
                    "shipped_at": null
                  },
                  "external": {
                    "channel_order_id": "SHOP-10023",
                    "channel_total": null
                  },
                  "customer_notes": null,
                  "qc": {
                    "open_tickets": 1,
                    "blocks_shipping": true
                  },
                  "created_at": "2026-10-09T08:39:29Z",
                  "updated_at": "2026-10-09T08:40:02Z",
                  "item_count": 1,
                  "items": [
                    {
                      "id": "itm_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "order_item",
                      "product": {
                        "id": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "product",
                        "name": "Clicker"
                      },
                      "variant": {
                        "id": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "variant",
                        "name": "Clicker Blue"
                      },
                      "sku": "CLICK-BLUE",
                      "quantity": 1,
                      "unit_price": {
                        "amount": 1800,
                        "currency": "usd"
                      },
                      "total": {
                        "amount": 1800,
                        "currency": "usd"
                      },
                      "stock_used": [],
                      "fulfilled_from_stock": false
                    }
                  ],
                  "refunds": []
                },
                "schema": {
                  "$ref": "#/components/schemas/Order"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid (`validation_failed`), or a discount code is unknown (`invalid_discount_code`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_discount_code"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "402": {
            "description": "Payment failed: no saved card to cover the shortfall, or the card was declined (`payment_failed`). No order was created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "payment_failed"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "422": {
            "description": "A variant is not orderable: unknown, inactive, or its product is not approved (`variant_not_orderable`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "variant_not_orderable"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user), plus 60 per minute per API key (or signed-in user) for quote, upload, order and payment operations. Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          },
          {
            "policy": "expensive_write",
            "limit": 60,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      },
      "get": {
        "operationId": "list_orders",
        "x-required-scopes": [
          "orders:read"
        ],
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Orders"
        ],
        "summary": "List orders",
        "description": "Lists orders for your products from every channel, newest first. Items and refunds are left out to stay small; get the order for them, or pass `expand[]=items`. Customer details need the `orders:read_customer` scope. Designers only.",
        "parameters": [
          {
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/OrderStatus"
              },
              "description": "Only orders in these states. Repeat for several, for example `status=paid&status=assigned`."
            },
            "required": false,
            "description": "Only orders in these states. Repeat for several, for example `status=paid&status=assigned`.",
            "style": "form",
            "explode": true,
            "name": "status",
            "in": "query"
          },
          {
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/OrderChannel"
              },
              "description": "Only orders from these channels."
            },
            "required": false,
            "description": "Only orders from these channels.",
            "style": "form",
            "explode": true,
            "name": "channel",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Matches the order number, or the customer name or email when the key has `orders:read_customer`."
            },
            "required": false,
            "description": "Matches the order number, or the customer name or email when the key has `orders:read_customer`.",
            "name": "search",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time",
              "description": "Only orders placed after this time.",
              "example": "2026-04-21T07:34:06Z"
            },
            "required": false,
            "description": "Only orders placed after this time.",
            "name": "created_after",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time",
              "description": "Only orders placed before this time.",
              "example": "2026-04-21T07:34:06Z"
            },
            "required": false,
            "description": "Only orders placed before this time.",
            "name": "created_before",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time",
              "description": "Only orders changed after this time. Use it to sync status changes since your last poll.",
              "example": "2026-04-21T07:34:06Z"
            },
            "required": false,
            "description": "Only orders changed after this time. Use it to sync status changes since your last poll.",
            "name": "updated_after",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "-created_at",
                "created_at",
                "-updated_at"
              ],
              "default": "-created_at",
              "description": "Order of results. A leading `-` means descending."
            },
            "required": false,
            "description": "Order of results. A leading `-` means descending.",
            "name": "sort",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "description": "How many results per page, from 1 to 100.",
              "example": 20
            },
            "required": false,
            "description": "How many results per page, from 1 to 100.",
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Where to continue from: the `next_cursor` of the previous page. Omit for the first page."
            },
            "required": false,
            "description": "Where to continue from: the `next_cursor` of the previous page. Omit for the first page.",
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "items"
                ]
              },
              "description": "Replace references with full objects. Repeat the parameter for several paths, for example `expand[]=items`. Unknown paths are rejected with `invalid_expand`."
            },
            "required": false,
            "description": "Replace references with full objects. Repeat the parameter for several paths, for example `expand[]=items`. Unknown paths are rejected with `invalid_expand`.",
            "style": "form",
            "explode": true,
            "name": "expand[]",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of orders.",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "id": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "order",
                      "number": "PV-SCN-1701A372",
                      "channel": "storefront",
                      "status": "in_production",
                      "customer": {
                        "name": "Ana Popescu",
                        "email": "ana@example.com",
                        "phone": null,
                        "company": null
                      },
                      "shipping_address": {
                        "formatted": "Str. Test 1, Cluj, RO",
                        "city": "Cluj",
                        "country": "Romania",
                        "country_code": "RO"
                      },
                      "charges": {
                        "paid_by": "buyer",
                        "items": {
                          "amount": 1800,
                          "currency": "usd"
                        },
                        "shipping": {
                          "amount": 600,
                          "currency": "usd"
                        },
                        "vat": {
                          "amount": {
                            "amount": 0,
                            "currency": "usd"
                          },
                          "rate": 0,
                          "included": false,
                          "country": null
                        },
                        "sample_credit": null,
                        "total": {
                          "amount": 2400,
                          "currency": "usd"
                        }
                      },
                      "earnings": {
                        "manufacturing": {
                          "amount": 900,
                          "currency": "usd"
                        },
                        "platform_fee": {
                          "amount": 300,
                          "currency": "usd"
                        },
                        "payment_processing": {
                          "amount": 102,
                          "currency": "usd"
                        },
                        "refunded": {
                          "amount": 0,
                          "currency": "usd"
                        },
                        "net": {
                          "amount": 498,
                          "currency": "usd"
                        }
                      },
                      "shipping": {
                        "tier": "small",
                        "carrier": null,
                        "tracking_number": null,
                        "shipped_at": null
                      },
                      "external": null,
                      "customer_notes": null,
                      "qc": {
                        "open_tickets": 1,
                        "blocks_shipping": true
                      },
                      "created_at": "2026-10-09T08:39:29Z",
                      "updated_at": "2026-10-09T08:40:02Z",
                      "item_count": 1
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/OrderList"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/orders/{id}/cancel": {
      "post": {
        "operationId": "cancel_order",
        "x-required-scopes": [
          "orders:write"
        ],
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Orders"
        ],
        "summary": "Cancel an order",
        "description": "Cancels one of your orders while it is still `paid` (no production partner has started) and refunds it in full to where it was paid from. Wallet-paid orders come back `cancelled` at once; card-paid orders stay `paid` until the card refund completes, then become `cancelled` and `order.cancelled` is sent. Designers only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^ord_[0-9A-Za-z]+$",
              "description": "Unique identifier of the order, prefixed with `ord_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the order, prefixed with `ord_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Any unique string, for example a UUID. Sending the same request again with the same key within 24 hours returns the first response without doing anything twice. Reusing a key with a different body is refused with `idempotency_key_reused`. A refused request (4xx) frees the key so you can correct and resend it; a server error (5xx) is kept and returned again, because the write may already have happened: check the resource before retrying with a new key."
            },
            "required": false,
            "description": "Any unique string, for example a UUID. Sending the same request again with the same key within 24 hours returns the first response without doing anything twice. Reusing a key with a different body is refused with `idempotency_key_reused`. A refused request (4xx) frees the key so you can correct and resend it; a server error (5xx) is kept and returned again, because the write may already have happened: check the resource before retrying with a new key.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "responses": {
          "200": {
            "description": "The order, cancelled or with its refund on the way.",
            "content": {
              "application/json": {
                "example": {
                  "id": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "order",
                  "number": "PV-SCN-1701A372",
                  "channel": "storefront",
                  "status": "cancelled",
                  "customer": {
                    "name": "Ana Popescu",
                    "email": "ana@example.com",
                    "phone": null,
                    "company": null
                  },
                  "shipping_address": {
                    "formatted": "Str. Test 1, Cluj, RO",
                    "city": "Cluj",
                    "country": "Romania",
                    "country_code": "RO"
                  },
                  "charges": {
                    "paid_by": "buyer",
                    "items": {
                      "amount": 1800,
                      "currency": "usd"
                    },
                    "shipping": {
                      "amount": 600,
                      "currency": "usd"
                    },
                    "vat": {
                      "amount": {
                        "amount": 0,
                        "currency": "usd"
                      },
                      "rate": 0,
                      "included": false,
                      "country": null
                    },
                    "sample_credit": null,
                    "total": {
                      "amount": 2400,
                      "currency": "usd"
                    }
                  },
                  "earnings": {
                    "manufacturing": {
                      "amount": 900,
                      "currency": "usd"
                    },
                    "platform_fee": {
                      "amount": 300,
                      "currency": "usd"
                    },
                    "payment_processing": {
                      "amount": 102,
                      "currency": "usd"
                    },
                    "refunded": {
                      "amount": 0,
                      "currency": "usd"
                    },
                    "net": {
                      "amount": 498,
                      "currency": "usd"
                    }
                  },
                  "shipping": {
                    "tier": "small",
                    "carrier": null,
                    "tracking_number": null,
                    "shipped_at": null
                  },
                  "external": null,
                  "customer_notes": null,
                  "qc": {
                    "open_tickets": 1,
                    "blocks_shipping": true
                  },
                  "created_at": "2026-10-09T08:39:29Z",
                  "updated_at": "2026-10-09T08:40:02Z",
                  "item_count": 1,
                  "items": [
                    {
                      "id": "itm_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "order_item",
                      "product": {
                        "id": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "product",
                        "name": "Clicker"
                      },
                      "variant": {
                        "id": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "variant",
                        "name": "Clicker Blue"
                      },
                      "sku": "CLICK-BLUE",
                      "quantity": 1,
                      "unit_price": {
                        "amount": 1800,
                        "currency": "usd"
                      },
                      "total": {
                        "amount": 1800,
                        "currency": "usd"
                      },
                      "stock_used": [],
                      "fulfilled_from_stock": false
                    }
                  ],
                  "refunds": []
                },
                "schema": {
                  "$ref": "#/components/schemas/Order"
                }
              }
            }
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have no order with this id (`order_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "order_not_found"
            ]
          },
          "409": {
            "description": "The order cannot be cancelled here (`order_not_cancellable`): it is past `paid` (contact support), a refund on it is already on the way, it is not an order you paid for yourself (API and manual orders only), it was created through API v1 (cancel those from the dashboard or contact support), it was paid by card and card refunds are unavailable, or the card refund was refused. The message says which; the order and your money are unchanged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "order_not_cancellable"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user), plus 60 per minute per API key (or signed-in user) for quote, upload, order and payment operations. Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          },
          {
            "policy": "expensive_write",
            "limit": 60,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/orders/{id}": {
      "get": {
        "operationId": "get_order",
        "x-required-scopes": [
          "orders:read"
        ],
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Orders"
        ],
        "summary": "Get an order",
        "description": "Returns one order with its items, charges, earnings, shipping and refunds. Customer details need the `orders:read_customer` scope. Designers only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^ord_[0-9A-Za-z]+$",
              "description": "Unique identifier of the order, prefixed with `ord_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the order, prefixed with `ord_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The order.",
            "content": {
              "application/json": {
                "example": {
                  "id": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "order",
                  "number": "PV-SCN-1701A372",
                  "channel": "storefront",
                  "status": "in_production",
                  "customer": {
                    "name": "Ana Popescu",
                    "email": "ana@example.com",
                    "phone": null,
                    "company": null
                  },
                  "shipping_address": {
                    "formatted": "Str. Test 1, Cluj, RO",
                    "city": "Cluj",
                    "country": "Romania",
                    "country_code": "RO"
                  },
                  "charges": {
                    "paid_by": "buyer",
                    "items": {
                      "amount": 1800,
                      "currency": "usd"
                    },
                    "shipping": {
                      "amount": 600,
                      "currency": "usd"
                    },
                    "vat": {
                      "amount": {
                        "amount": 0,
                        "currency": "usd"
                      },
                      "rate": 0,
                      "included": false,
                      "country": null
                    },
                    "sample_credit": null,
                    "total": {
                      "amount": 2400,
                      "currency": "usd"
                    }
                  },
                  "earnings": {
                    "manufacturing": {
                      "amount": 900,
                      "currency": "usd"
                    },
                    "platform_fee": {
                      "amount": 300,
                      "currency": "usd"
                    },
                    "payment_processing": {
                      "amount": 102,
                      "currency": "usd"
                    },
                    "refunded": {
                      "amount": 0,
                      "currency": "usd"
                    },
                    "net": {
                      "amount": 498,
                      "currency": "usd"
                    }
                  },
                  "shipping": {
                    "tier": "small",
                    "carrier": null,
                    "tracking_number": null,
                    "shipped_at": null
                  },
                  "external": null,
                  "customer_notes": null,
                  "qc": {
                    "open_tickets": 1,
                    "blocks_shipping": true
                  },
                  "created_at": "2026-10-09T08:39:29Z",
                  "updated_at": "2026-10-09T08:40:02Z",
                  "item_count": 1,
                  "items": [
                    {
                      "id": "itm_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "order_item",
                      "product": {
                        "id": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "product",
                        "name": "Clicker"
                      },
                      "variant": {
                        "id": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "variant",
                        "name": "Clicker Blue"
                      },
                      "sku": "CLICK-BLUE",
                      "quantity": 1,
                      "unit_price": {
                        "amount": 1800,
                        "currency": "usd"
                      },
                      "total": {
                        "amount": 1800,
                        "currency": "usd"
                      },
                      "stock_used": [],
                      "fulfilled_from_stock": false
                    }
                  ],
                  "refunds": []
                },
                "schema": {
                  "$ref": "#/components/schemas/Order"
                }
              }
            }
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have no order with this id (`order_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "order_not_found"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/integrator/orders": {
      "post": {
        "operationId": "create_integrator_order",
        "x-required-scopes": [
          "orders:write"
        ],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Integrator orders"
        ],
        "summary": "Order prints of your quotes",
        "description": "Orders prints of your own `ready` quotes once your account is approved. In `wallet` mode you pay the production price plus shipping from your wallet; a shortfall is charged to your saved card with VAT on the top-up, as for every wallet top-up. The order comes back paid, numbered `2AP...`, and goes to production. If payment fails, no order is created. In `checkout` mode your customer pays your `unit_sales_price` plus shipping, and VAT when they ship to the EU or UK, on a hosted 3DAPI checkout: the order comes back `awaiting_payment`, numbered `2AC...`, with `checkout.url` to send them to, and `order.created` is sent. The link stays open 24 hours; unpaid, the order is then cancelled and `checkout.expired` is sent. Cancelling it yourself before it is paid sends `order.cancelled` instead. Once the order is completed you earn the sales price less the production price and the card fee, credited to your wallet. A line may carry approved extras (`items[].extras`): their price per unit is added to the line's production price in both modes. Integrators only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Any unique string, for example a UUID. Sending the same request again with the same key within 24 hours returns the first response without doing anything twice. Reusing a key with a different body is refused with `idempotency_key_reused`. A refused request (4xx) frees the key so you can correct and resend it; a server error (5xx) is kept and returned again, because the write may already have happened: check the resource before retrying with a new key."
            },
            "required": false,
            "description": "Any unique string, for example a UUID. Sending the same request again with the same key within 24 hours returns the first response without doing anything twice. Reusing a key with a different body is refused with `idempotency_key_reused`. A refused request (4xx) frees the key so you can correct and resend it; a server error (5xx) is kept and returned again, because the write may already have happened: check the resource before retrying with a new key.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "wallet": {
                  "summary": "You pay the production price from your wallet",
                  "value": {
                    "mode": "wallet",
                    "items": [
                      {
                        "quote": "qte_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "quantity": 10,
                        "materials": [
                          "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                        ]
                      }
                    ],
                    "shipping_address": {
                      "line1": "Hauptstrasse 12",
                      "city": "Berlin",
                      "postal_code": "10115",
                      "country_code": "DE"
                    },
                    "customer": {
                      "name": "Jane Buyer",
                      "email": "jane@example.com"
                    },
                    "reference": "SHOP-1042"
                  }
                },
                "checkout": {
                  "summary": "Your customer pays your price on a hosted checkout",
                  "value": {
                    "mode": "checkout",
                    "items": [
                      {
                        "quote": "qte_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "quantity": 1,
                        "use_suggested_materials": true,
                        "unit_sales_price": {
                          "amount": 2900,
                          "currency": "usd"
                        }
                      }
                    ],
                    "shipping_address": {
                      "line1": "Hauptstrasse 12",
                      "city": "Berlin",
                      "postal_code": "10115",
                      "country_code": "DE"
                    },
                    "customer": {
                      "name": "Jane Buyer",
                      "email": "jane@example.com"
                    },
                    "checkout": {
                      "success_url": "https://shop.example/thanks",
                      "cancel_url": "https://shop.example/cart"
                    },
                    "reference": "SHOP-1043"
                  }
                }
              },
              "schema": {
                "$ref": "#/components/schemas/CreateIntegratorOrderRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The order: paid in `wallet` mode, `awaiting_payment` with its checkout link in `checkout` mode.",
            "content": {
              "application/json": {
                "example": {
                  "id": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "order",
                  "number": "2AP7K2M9X",
                  "channel": "integrator_wallet",
                  "status": "paid",
                  "customer": {
                    "name": "Ana Popescu",
                    "email": "ana@example.com",
                    "phone": null,
                    "company": null
                  },
                  "shipping_address": {
                    "formatted": "Str. Test 1, Cluj, RO",
                    "city": "Cluj",
                    "country": "Romania",
                    "country_code": "RO"
                  },
                  "charges": {
                    "paid_by": "you",
                    "items": {
                      "amount": 5000,
                      "currency": "usd"
                    },
                    "shipping": {
                      "amount": 800,
                      "currency": "usd"
                    },
                    "vat": {
                      "amount": {
                        "amount": 0,
                        "currency": "usd"
                      },
                      "rate": 0,
                      "included": false,
                      "country": null
                    },
                    "sample_credit": null,
                    "total": {
                      "amount": 5800,
                      "currency": "usd"
                    }
                  },
                  "earnings": null,
                  "shipping": {
                    "tier": "small",
                    "carrier": null,
                    "tracking_number": null,
                    "shipped_at": null
                  },
                  "customer_notes": null,
                  "created_at": "2026-10-09T08:39:29Z",
                  "updated_at": "2026-10-09T08:40:02Z",
                  "item_count": 10,
                  "mode": "wallet",
                  "checkout": null,
                  "items": [
                    {
                      "id": "itm_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "order_item",
                      "quote": {
                        "id": "qte_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "quote",
                        "name": "phone-stand.stl"
                      },
                      "materials": [
                        {
                          "id": "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "material",
                          "name": "PLA Black"
                        }
                      ],
                      "material_slots": [
                        {
                          "slot": 1,
                          "material": {
                            "id": "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                            "object": "material",
                            "name": "PLA Black"
                          }
                        }
                      ],
                      "quantity": 10,
                      "unit_price": {
                        "amount": 500,
                        "currency": "usd"
                      },
                      "production_unit_price": {
                        "amount": 500,
                        "currency": "usd"
                      },
                      "extras": [],
                      "total": {
                        "amount": 5000,
                        "currency": "usd"
                      }
                    }
                  ],
                  "refunds": [],
                  "reference": "SHOP-1042"
                },
                "schema": {
                  "$ref": "#/components/schemas/IntegratorOrder"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid (`validation_failed`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "402": {
            "description": "Payment failed: the card was declined (`payment_failed`), or the wallet is short and there is no saved card (`insufficient_balance`). No order was created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "payment_failed",
              "insufficient_balance"
            ]
          },
          "403": {
            "description": "Your account cannot order yet: add your business billing details, or, to sell to EU or UK customers from outside the EU and UK, an active EU responsible person (`compliance_blocked`, `details.requirements`; `details.fix_url` links to the dashboard page that fixes it).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "compliance_blocked"
            ]
          },
          "404": {
            "description": "A quote is not one of yours (`quote_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "quote_not_found"
            ]
          },
          "409": {
            "description": "Production is paused (`production_unavailable`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "production_unavailable"
            ]
          },
          "422": {
            "description": "A quote is not ready (`quote_not_ready`) or was rejected in review (`quote_rejected`), an extra is not one of yours (`extra_not_found`) or not approved (`extra_not_approved`), your account is not approved yet (`quote_not_approved`), a material is unknown or there are more materials than the quote prints with (`material_not_allowed`), the slots sent are not the slots the quote's file uses (`material_slots_mismatch`, `details.expected_slots`), the suggested materials were asked for on a quote that has none (`suggestions_unavailable`), a sales price is not in usd (`currency_not_supported`) or is below the minimum (`sales_price_below_production`, `details.minimum`), we do not ship to the country (`shipping_destination_not_served`), or VIES did not confirm the business buyer's EU VAT number (`vat_number_invalid`, `details.tax_id` and `details.country_code`; nothing is created). The request is checked in full before the account compliance gate (403), so these come first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "quote_not_ready",
              "quote_rejected",
              "extra_not_found",
              "extra_not_approved",
              "quote_not_approved",
              "material_not_allowed",
              "material_slots_mismatch",
              "suggestions_unavailable",
              "currency_not_supported",
              "sales_price_below_production",
              "shipping_destination_not_served",
              "vat_number_invalid"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user), plus 60 per minute per API key (or signed-in user) for quote, upload, order and payment operations. Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          },
          "503": {
            "description": "Card payments are not available right now (`card_payments_unavailable`); no order was created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "card_payments_unavailable"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          },
          {
            "policy": "expensive_write",
            "limit": 60,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      },
      "get": {
        "operationId": "list_integrator_orders",
        "x-required-scopes": [
          "orders:read"
        ],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Integrator orders"
        ],
        "summary": "List your orders",
        "description": "Your orders, newest first, without their lines unless `expand[]=items`. Integrators only.",
        "parameters": [
          {
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/OrderStatus"
              },
              "description": "Only orders in these statuses (repeatable)."
            },
            "required": false,
            "description": "Only orders in these statuses (repeatable).",
            "name": "status",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "checkout",
                "wallet"
              ],
              "description": "`wallet`: you pay the production price plus shipping from your wallet (a shortfall is charged to your saved card). `checkout`: your customer pays the sales price you set through a hosted 3DAPI checkout and you earn the difference."
            },
            "required": false,
            "description": "`wallet`: you pay the production price plus shipping from your wallet (a shortfall is charged to your saved card). `checkout`: your customer pays the sales price you set through a hosted 3DAPI checkout and you earn the difference.",
            "name": "mode",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time",
              "description": "Only orders placed after this time.",
              "example": "2026-04-21T07:34:06Z"
            },
            "required": false,
            "description": "Only orders placed after this time.",
            "name": "created_after",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time",
              "description": "Only orders placed before this time.",
              "example": "2026-04-21T07:34:06Z"
            },
            "required": false,
            "description": "Only orders placed before this time.",
            "name": "created_before",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "maxLength": 100,
              "description": "Exact match on your own order id."
            },
            "required": false,
            "description": "Exact match on your own order id.",
            "name": "reference",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "pattern": "^qte_[0-9A-Za-z]+$",
              "description": "Only orders with a line from this quote.",
              "example": "qte_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": false,
            "description": "Only orders with a line from this quote.",
            "name": "quote",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "maxLength": 100,
              "description": "Part of the order `number` or of your `reference`, case-insensitive. For example `7K2M` finds `2AP7K2M9X`, and `1042` finds `SHOP-1042`.",
              "example": "1042"
            },
            "required": false,
            "description": "Part of the order `number` or of your `reference`, case-insensitive. For example `7K2M` finds `2AP7K2M9X`, and `1042` finds `SHOP-1042`.",
            "name": "search",
            "in": "query"
          },
          {
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "items"
                ]
              },
              "description": "`items` adds the order lines."
            },
            "required": false,
            "description": "`items` adds the order lines.",
            "name": "expand[]",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "description": "How many results per page, from 1 to 100.",
              "example": 20
            },
            "required": false,
            "description": "How many results per page, from 1 to 100.",
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Where to continue from: the `next_cursor` of the previous page. Omit for the first page."
            },
            "required": false,
            "description": "Where to continue from: the `next_cursor` of the previous page. Omit for the first page.",
            "name": "cursor",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of orders.",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    [
                      {
                        "id": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "order",
                        "number": "2AP7K2M9X",
                        "channel": "integrator_wallet",
                        "status": "paid",
                        "customer": {
                          "name": "Ana Popescu",
                          "email": "ana@example.com",
                          "phone": null,
                          "company": null
                        },
                        "shipping_address": {
                          "formatted": "Str. Test 1, Cluj, RO",
                          "city": "Cluj",
                          "country": "Romania",
                          "country_code": "RO"
                        },
                        "charges": {
                          "paid_by": "you",
                          "items": {
                            "amount": 5000,
                            "currency": "usd"
                          },
                          "shipping": {
                            "amount": 800,
                            "currency": "usd"
                          },
                          "vat": {
                            "amount": {
                              "amount": 0,
                              "currency": "usd"
                            },
                            "rate": 0,
                            "included": false,
                            "country": null
                          },
                          "sample_credit": null,
                          "total": {
                            "amount": 5800,
                            "currency": "usd"
                          }
                        },
                        "earnings": null,
                        "shipping": {
                          "tier": "small",
                          "carrier": null,
                          "tracking_number": null,
                          "shipped_at": null
                        },
                        "customer_notes": null,
                        "created_at": "2026-10-09T08:39:29Z",
                        "updated_at": "2026-10-09T08:40:02Z",
                        "item_count": 10,
                        "mode": "wallet",
                        "checkout": null,
                        "refunds": [],
                        "reference": "SHOP-1042"
                      }
                    ]
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/IntegratorOrderList"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/integrator/orders/shipping-estimate": {
      "post": {
        "operationId": "estimate_integrator_order_shipping",
        "x-required-scopes": [
          "orders:read"
        ],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Integrator orders"
        ],
        "summary": "Estimate the shipping of an order",
        "description": "What `create_integrator_order` would charge for shipping these lines to this country, computed by the same code the order is charged with: the parcel takes the tier of its heaviest item, every other item (extra-small ones excluded) adds the zone's per-item price, and extras count toward the weight. Nothing is created, and your account does not need to be approved yet.\n\nThe total is without VAT: in `checkout` mode, buyers in the EU or UK pay VAT on top. It can change if the rate card changes before you order; the order charges the rate card at the time it is placed.\n\nSend the same `items` (quote, quantity, extras) and the destination you would order with. Integrators only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Any unique string, for example a UUID. Sending the same request again with the same key within 24 hours returns the first response without doing anything twice. Reusing a key with a different body is refused with `idempotency_key_reused`. A refused request (4xx) frees the key so you can correct and resend it; a server error (5xx) is kept and returned again, because the write may already have happened: check the resource before retrying with a new key."
            },
            "required": false,
            "description": "Any unique string, for example a UUID. Sending the same request again with the same key within 24 hours returns the first response without doing anything twice. Reusing a key with a different body is refused with `idempotency_key_reused`. A refused request (4xx) frees the key so you can correct and resend it; a server error (5xx) is kept and returned again, because the write may already have happened: check the resource before retrying with a new key.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "country": "DE",
                "items": [
                  {
                    "quote": "qte_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "quantity": 2
                  },
                  {
                    "quote": "qte_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "quantity": 2,
                    "extras": [
                      {
                        "extra": "xtr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "quantity_per_unit": 1
                      }
                    ]
                  }
                ]
              },
              "schema": {
                "$ref": "#/components/schemas/EstimateIntegratorOrderShippingRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The shipping the order would be charged.",
            "content": {
              "application/json": {
                "example": {
                  "object": "shipping_estimate",
                  "country_code": "DE",
                  "zone": {
                    "code": "eu-core",
                    "name": "EU Core"
                  },
                  "tier": "medium",
                  "additional_items": 3,
                  "total": {
                    "amount": 1099,
                    "currency": "usd"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ShippingEstimate"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid (`validation_failed`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "A quote is not one of yours (`quote_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "quote_not_found"
            ]
          },
          "422": {
            "description": "A quote is not ready (`quote_not_ready`) or was rejected in review (`quote_rejected`), an extra is not one of yours (`extra_not_found`) or not approved (`extra_not_approved`), or we do not ship to the country (`shipping_destination_not_served`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "quote_not_ready",
              "quote_rejected",
              "extra_not_found",
              "extra_not_approved",
              "shipping_destination_not_served"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/integrator/orders/{id}": {
      "get": {
        "operationId": "get_integrator_order",
        "x-required-scopes": [
          "orders:read"
        ],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Integrator orders"
        ],
        "summary": "Get an order",
        "description": "One of your orders, with its lines and refunds. Integrators only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^ord_[0-9A-Za-z]+$",
              "description": "Unique identifier of the order, prefixed with `ord_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the order, prefixed with `ord_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The order.",
            "content": {
              "application/json": {
                "example": {
                  "id": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "order",
                  "number": "2AP7K2M9X",
                  "channel": "integrator_wallet",
                  "status": "paid",
                  "customer": {
                    "name": "Ana Popescu",
                    "email": "ana@example.com",
                    "phone": null,
                    "company": null
                  },
                  "shipping_address": {
                    "formatted": "Str. Test 1, Cluj, RO",
                    "city": "Cluj",
                    "country": "Romania",
                    "country_code": "RO"
                  },
                  "charges": {
                    "paid_by": "you",
                    "items": {
                      "amount": 5000,
                      "currency": "usd"
                    },
                    "shipping": {
                      "amount": 800,
                      "currency": "usd"
                    },
                    "vat": {
                      "amount": {
                        "amount": 0,
                        "currency": "usd"
                      },
                      "rate": 0,
                      "included": false,
                      "country": null
                    },
                    "sample_credit": null,
                    "total": {
                      "amount": 5800,
                      "currency": "usd"
                    }
                  },
                  "earnings": null,
                  "shipping": {
                    "tier": "small",
                    "carrier": null,
                    "tracking_number": null,
                    "shipped_at": null
                  },
                  "customer_notes": null,
                  "created_at": "2026-10-09T08:39:29Z",
                  "updated_at": "2026-10-09T08:40:02Z",
                  "item_count": 10,
                  "mode": "wallet",
                  "checkout": null,
                  "items": [
                    {
                      "id": "itm_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "order_item",
                      "quote": {
                        "id": "qte_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "quote",
                        "name": "phone-stand.stl"
                      },
                      "materials": [
                        {
                          "id": "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "material",
                          "name": "PLA Black"
                        }
                      ],
                      "material_slots": [
                        {
                          "slot": 1,
                          "material": {
                            "id": "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                            "object": "material",
                            "name": "PLA Black"
                          }
                        }
                      ],
                      "quantity": 10,
                      "unit_price": {
                        "amount": 500,
                        "currency": "usd"
                      },
                      "production_unit_price": {
                        "amount": 500,
                        "currency": "usd"
                      },
                      "extras": [],
                      "total": {
                        "amount": 5000,
                        "currency": "usd"
                      }
                    }
                  ],
                  "refunds": [],
                  "reference": "SHOP-1042"
                },
                "schema": {
                  "$ref": "#/components/schemas/IntegratorOrder"
                }
              }
            }
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have no order with this id (`order_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "order_not_found"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/integrator/orders/{id}/cancel": {
      "post": {
        "operationId": "cancel_integrator_order",
        "x-required-scopes": [
          "orders:write"
        ],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Integrator orders"
        ],
        "summary": "Cancel an order",
        "description": "Cancels an order. An unpaid `checkout` order (`awaiting_payment`) has its link closed and is cancelled. A `paid` order a production partner has not started is refunded in full: to your wallet for `wallet` orders, to your customer's card for `checkout` orders. Integrators only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^ord_[0-9A-Za-z]+$",
              "description": "Unique identifier of the order, prefixed with `ord_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the order, prefixed with `ord_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Any unique string, for example a UUID. Sending the same request again with the same key within 24 hours returns the first response without doing anything twice. Reusing a key with a different body is refused with `idempotency_key_reused`. A refused request (4xx) frees the key so you can correct and resend it; a server error (5xx) is kept and returned again, because the write may already have happened: check the resource before retrying with a new key."
            },
            "required": false,
            "description": "Any unique string, for example a UUID. Sending the same request again with the same key within 24 hours returns the first response without doing anything twice. Reusing a key with a different body is refused with `idempotency_key_reused`. A refused request (4xx) frees the key so you can correct and resend it; a server error (5xx) is kept and returned again, because the write may already have happened: check the resource before retrying with a new key.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "responses": {
          "200": {
            "description": "The order, cancelled.",
            "content": {
              "application/json": {
                "example": {
                  "id": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "order",
                  "number": "2AP7K2M9X",
                  "channel": "integrator_wallet",
                  "status": "cancelled",
                  "customer": {
                    "name": "Ana Popescu",
                    "email": "ana@example.com",
                    "phone": null,
                    "company": null
                  },
                  "shipping_address": {
                    "formatted": "Str. Test 1, Cluj, RO",
                    "city": "Cluj",
                    "country": "Romania",
                    "country_code": "RO"
                  },
                  "charges": {
                    "paid_by": "you",
                    "items": {
                      "amount": 5000,
                      "currency": "usd"
                    },
                    "shipping": {
                      "amount": 800,
                      "currency": "usd"
                    },
                    "vat": {
                      "amount": {
                        "amount": 0,
                        "currency": "usd"
                      },
                      "rate": 0,
                      "included": false,
                      "country": null
                    },
                    "sample_credit": null,
                    "total": {
                      "amount": 5800,
                      "currency": "usd"
                    }
                  },
                  "earnings": null,
                  "shipping": {
                    "tier": "small",
                    "carrier": null,
                    "tracking_number": null,
                    "shipped_at": null
                  },
                  "customer_notes": null,
                  "created_at": "2026-10-09T08:39:29Z",
                  "updated_at": "2026-10-09T08:40:02Z",
                  "item_count": 10,
                  "mode": "wallet",
                  "checkout": null,
                  "items": [
                    {
                      "id": "itm_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "order_item",
                      "quote": {
                        "id": "qte_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "quote",
                        "name": "phone-stand.stl"
                      },
                      "materials": [
                        {
                          "id": "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "material",
                          "name": "PLA Black"
                        }
                      ],
                      "material_slots": [
                        {
                          "slot": 1,
                          "material": {
                            "id": "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                            "object": "material",
                            "name": "PLA Black"
                          }
                        }
                      ],
                      "quantity": 10,
                      "unit_price": {
                        "amount": 500,
                        "currency": "usd"
                      },
                      "production_unit_price": {
                        "amount": 500,
                        "currency": "usd"
                      },
                      "extras": [],
                      "total": {
                        "amount": 5000,
                        "currency": "usd"
                      }
                    }
                  ],
                  "refunds": [],
                  "reference": "SHOP-1042"
                },
                "schema": {
                  "$ref": "#/components/schemas/IntegratorOrder"
                }
              }
            }
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have no order with this id (`order_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "order_not_found"
            ]
          },
          "409": {
            "description": "The order cannot be cancelled (`order_not_cancellable`): it is already cancelled, or past `paid` (a production partner started it; contact support). Unpaid checkout orders (`awaiting_payment`) and `paid` orders can be cancelled; the message says which case applies.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "order_not_cancellable",
              "awaiting_payment"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user), plus 60 per minute per API key (or signed-in user) for quote, upload, order and payment operations. Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          },
          {
            "policy": "expensive_write",
            "limit": 60,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/import-issues": {
      "get": {
        "operationId": "list_import_issues",
        "x-required-scopes": [
          "orders:read"
        ],
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Orders"
        ],
        "summary": "List import issues",
        "description": "Lists orders from your connected sales channels that could not be imported, newest first, with what to fix. Filter on `sku` to find every order one missing SKU mapping blocks. Designers only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "enum": [
                "shipstation"
              ],
              "description": "Only this channel."
            },
            "required": false,
            "description": "Only this channel.",
            "name": "channel",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "unknown_sku",
                "no_items",
                "variant_not_found",
                "multiple_sellers"
              ],
              "description": "Only this reason."
            },
            "required": false,
            "description": "Only this reason.",
            "name": "reason",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Only issues with an unmatched item of exactly this SKU. Use it to find every order one missing mapping is blocking."
            },
            "required": false,
            "description": "Only issues with an unmatched item of exactly this SKU. Use it to find every order one missing mapping is blocking.",
            "name": "sku",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time",
              "description": "Only orders placed after this time.",
              "example": "2026-04-21T07:34:06Z"
            },
            "required": false,
            "description": "Only orders placed after this time.",
            "name": "ordered_after",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time",
              "description": "Only orders placed before this time.",
              "example": "2026-04-21T07:34:06Z"
            },
            "required": false,
            "description": "Only orders placed before this time.",
            "name": "ordered_before",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "-ordered_at",
                "ordered_at",
                "-created_at"
              ],
              "default": "-ordered_at",
              "description": "Order of results. A leading `-` means newest first."
            },
            "required": false,
            "description": "Order of results. A leading `-` means newest first.",
            "name": "sort",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "description": "How many results per page, from 1 to 100.",
              "example": 20
            },
            "required": false,
            "description": "How many results per page, from 1 to 100.",
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Where to continue from: the `next_cursor` of the previous page. Omit for the first page."
            },
            "required": false,
            "description": "Where to continue from: the `next_cursor` of the previous page. Omit for the first page.",
            "name": "cursor",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of import issues.",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "id": "imp_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "import_issue",
                      "channel": "shipstation",
                      "channel_order_id": "193554871",
                      "channel_order_number": "3002",
                      "ordered_at": "2026-10-07T14:20:54Z",
                      "reason": "unknown_sku",
                      "message": "1 item has a SKU that matches none of your variants. Give the variant this SKU, then import the order again.",
                      "unmatched_items": [
                        {
                          "sku": "PR-TRACKERV2-465KG",
                          "name": "PR Tracker V2 - 465 Kilograms (kg) Plates",
                          "quantity": 1
                        }
                      ],
                      "created_at": "2026-10-07T14:21:10Z"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/ImportIssueList"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/import-issues/{id}": {
      "get": {
        "operationId": "get_import_issue",
        "x-required-scopes": [
          "orders:read"
        ],
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Orders"
        ],
        "summary": "Get an import issue",
        "description": "Returns one order that could not be imported. Designers only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^imp_[0-9A-Za-z]+$",
              "description": "Unique identifier of the import issue, prefixed with `imp_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "imp_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the import issue, prefixed with `imp_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The import issue.",
            "content": {
              "application/json": {
                "example": {
                  "id": "imp_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "import_issue",
                  "channel": "shipstation",
                  "channel_order_id": "193554871",
                  "channel_order_number": "3002",
                  "ordered_at": "2026-10-07T14:20:54Z",
                  "reason": "unknown_sku",
                  "message": "1 item has a SKU that matches none of your variants. Give the variant this SKU, then import the order again.",
                  "unmatched_items": [
                    {
                      "sku": "PR-TRACKERV2-465KG",
                      "name": "PR Tracker V2 - 465 Kilograms (kg) Plates",
                      "quantity": 1
                    }
                  ],
                  "created_at": "2026-10-07T14:21:10Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/ImportIssue"
                }
              }
            }
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have no import issue with this id (`import_issue_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "import_issue_not_found"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/qc-tickets/{id}/review": {
      "post": {
        "operationId": "review_qc_ticket",
        "x-required-scopes": [
          "qc:write"
        ],
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Quality control"
        ],
        "summary": "Review a quality check",
        "description": "Accepts or refuses the products on a quality check, all in one request: either every verdict is recorded or none is. Rule on any product whose status is `in_review` (the ticket shows `needs_your_action: true`); this works even while another product on the ticket is already `refusal_under_review`. Accepting every product approves the ticket and lets the order ship. A refusal goes to 3DAPI, which rules on it (`refusal_under_review`); if upheld, the partner reprints the product and sends new photos. Same effect as reviewing in the dashboard. Designers only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^qct_[0-9A-Za-z]+$",
              "description": "Unique identifier of the qc ticket, prefixed with `qct_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "qct_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the qc ticket, prefixed with `qct_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Any unique string, for example a UUID. Sending the same request again with the same key within 24 hours returns the first response without doing anything twice. Reusing a key with a different body is refused with `idempotency_key_reused`. A refused request (4xx) frees the key so you can correct and resend it; a server error (5xx) is kept and returned again, because the write may already have happened: check the resource before retrying with a new key."
            },
            "required": false,
            "description": "Any unique string, for example a UUID. Sending the same request again with the same key within 24 hours returns the first response without doing anything twice. Reusing a key with a different body is refused with `idempotency_key_reused`. A refused request (4xx) frees the key so you can correct and resend it; a server error (5xx) is kept and returned again, because the write may already have happened: check the resource before retrying with a new key.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "items": [
                  {
                    "item": "qci_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "verdict": "accept"
                  },
                  {
                    "item": "qci_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "verdict": "refuse",
                    "reason": "Layer lines on the front face. Please reprint slower.",
                    "photos": [
                      "qcp_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                    ]
                  }
                ]
              },
              "schema": {
                "$ref": "#/components/schemas/ReviewQcTicketRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The ticket after your review.",
            "content": {
              "application/json": {
                "example": {
                  "id": "qct_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "qc_ticket",
                  "kind": "quality_check",
                  "reason": "spot_check",
                  "status": "awaiting_your_review",
                  "needs_your_action": true,
                  "blocks_shipping": true,
                  "order": {
                    "id": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "order",
                    "name": "PV-SCN-1701A372"
                  },
                  "round": 1,
                  "replacement": null,
                  "created_at": "2026-10-09T15:20:00Z",
                  "submitted_at": "2026-10-10T09:01:00Z",
                  "resolved_at": null,
                  "items": [
                    {
                      "id": "qci_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "qc_item",
                      "title": "Clicker Blue",
                      "product": {
                        "id": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "product",
                        "name": "Clicker"
                      },
                      "variant": {
                        "id": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "variant",
                        "name": "Clicker Blue"
                      },
                      "status": "in_review",
                      "round": 1,
                      "photos": [
                        {
                          "id": "qcp_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "url": "https://files.example.com/qc/clicker-front.jpg?signature=abc",
                          "round": 1,
                          "uploaded_at": "2026-10-10T09:00:00Z"
                        }
                      ],
                      "history": [
                        {
                          "action": "submitted",
                          "by": "production_partner",
                          "round": 1,
                          "note": null,
                          "at": "2026-10-10T09:01:00Z"
                        }
                      ]
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/QcTicket"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have no QC ticket with this id (`qc_ticket_not_found`), or a product is not on it (`qc_item_not_found`, `details.field` names the entry).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "qc_ticket_not_found",
              "qc_item_not_found"
            ]
          },
          "409": {
            "description": "The ticket is not a quality check (`not_a_quality_check`), or it is closed or a product is not in review (`not_awaiting_review`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "not_a_quality_check",
              "not_awaiting_review"
            ]
          },
          "422": {
            "description": "A product is listed twice (`duplicate_item`), or a refusal points at a photo that is not one of that product's current photos (`photo_not_on_item`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "duplicate_item",
              "photo_not_on_item"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/qc-tickets/{id}/messages": {
      "post": {
        "operationId": "send_qc_message",
        "x-required-scopes": [
          "qc:write"
        ],
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Quality control"
        ],
        "summary": "Reply on a QC conversation",
        "description": "Answers a question from the production partner (`kind: question`). Turns alternate: you can post only when the partner spoke last (the ticket is `awaiting_answer`). Photos are uploaded first with purpose `qc_photo`. Issue reports take no messages: 3DAPI answers one by deciding the replacement. Designers only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^qct_[0-9A-Za-z]+$",
              "description": "Unique identifier of the qc ticket, prefixed with `qct_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "qct_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the qc ticket, prefixed with `qct_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Any unique string, for example a UUID. Sending the same request again with the same key within 24 hours returns the first response without doing anything twice. Reusing a key with a different body is refused with `idempotency_key_reused`. A refused request (4xx) frees the key so you can correct and resend it; a server error (5xx) is kept and returned again, because the write may already have happened: check the resource before retrying with a new key."
            },
            "required": false,
            "description": "Any unique string, for example a UUID. Sending the same request again with the same key within 24 hours returns the first response without doing anything twice. Reusing a key with a different body is refused with `idempotency_key_reused`. A refused request (4xx) frees the key so you can correct and resend it; a server error (5xx) is kept and returned again, because the write may already have happened: check the resource before retrying with a new key.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "body": "Yes, print it in matte black. The photo shows where the logo goes.",
                "photos": [
                  {
                    "upload": "upl_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                  }
                ]
              },
              "schema": {
                "$ref": "#/components/schemas/SendQcMessageRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The ticket with your message.",
            "content": {
              "application/json": {
                "example": {
                  "id": "qct_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "qc_ticket",
                  "kind": "quality_check",
                  "reason": "spot_check",
                  "status": "awaiting_your_review",
                  "needs_your_action": true,
                  "blocks_shipping": true,
                  "order": {
                    "id": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "order",
                    "name": "PV-SCN-1701A372"
                  },
                  "round": 1,
                  "replacement": null,
                  "created_at": "2026-10-09T15:20:00Z",
                  "submitted_at": "2026-10-10T09:01:00Z",
                  "resolved_at": null,
                  "items": [
                    {
                      "id": "qci_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "qc_item",
                      "title": "Clicker Blue",
                      "product": {
                        "id": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "product",
                        "name": "Clicker"
                      },
                      "variant": {
                        "id": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "variant",
                        "name": "Clicker Blue"
                      },
                      "status": "in_review",
                      "round": 1,
                      "photos": [
                        {
                          "id": "qcp_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "url": "https://files.example.com/qc/clicker-front.jpg?signature=abc",
                          "round": 1,
                          "uploaded_at": "2026-10-10T09:00:00Z"
                        }
                      ],
                      "history": [
                        {
                          "action": "submitted",
                          "by": "production_partner",
                          "round": 1,
                          "note": null,
                          "at": "2026-10-10T09:01:00Z"
                        }
                      ]
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/QcTicket"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have no QC ticket with this id (`qc_ticket_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "qc_ticket_not_found"
            ]
          },
          "409": {
            "description": "It is not your turn (`not_your_turn`), the ticket is not a question (`not_a_conversation`), or the question is closed (`ticket_closed`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "not_your_turn",
              "not_a_conversation",
              "ticket_closed"
            ]
          },
          "422": {
            "description": "An upload is unknown, expired or of the wrong purpose (`unknown_upload`, `upload_expired`, `upload_purpose_mismatch`, `upload_missing` when the file was never sent, `upload_already_used` when the upload id was already used once).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "unknown_upload",
              "upload_expired",
              "upload_purpose_mismatch",
              "upload_missing",
              "upload_already_used"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/qc-tickets": {
      "post": {
        "operationId": "create_qc_ticket",
        "x-required-scopes": [
          "qc:write"
        ],
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Quality control"
        ],
        "summary": "Report an issue with a delivered order",
        "description": "Opens an issue report on a completed order: what came back wrong, how many, and photos (uploaded first with purpose `qc_photo`). 3DAPI reviews it and decides on a replacement and who pays for it (`replacement`); that decision closes the report. The production partner is not part of the report. It never holds an order. Designers only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Any unique string, for example a UUID. Sending the same request again with the same key within 24 hours returns the first response without doing anything twice. Reusing a key with a different body is refused with `idempotency_key_reused`. A refused request (4xx) frees the key so you can correct and resend it; a server error (5xx) is kept and returned again, because the write may already have happened: check the resource before retrying with a new key."
            },
            "required": false,
            "description": "Any unique string, for example a UUID. Sending the same request again with the same key within 24 hours returns the first response without doing anything twice. Reusing a key with a different body is refused with `idempotency_key_reused`. A refused request (4xx) frees the key so you can correct and resend it; a server error (5xx) is kept and returned again, because the write may already have happened: check the resource before retrying with a new key.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "order": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                "items": [
                  {
                    "order_item": "itm_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "quantity": 1
                  }
                ],
                "message": "The lid arrived cracked along the hinge.",
                "photos": [
                  {
                    "upload": "upl_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                  }
                ]
              },
              "schema": {
                "$ref": "#/components/schemas/CreateQcTicketRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The new issue report.",
            "content": {
              "application/json": {
                "example": {
                  "id": "qct_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "qc_ticket",
                  "kind": "issue_report",
                  "reason": null,
                  "status": "awaiting_answer",
                  "needs_your_action": false,
                  "blocks_shipping": false,
                  "order": {
                    "id": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "order",
                    "name": "PV-SCN-1701A372"
                  },
                  "round": 1,
                  "replacement": {
                    "items": [
                      {
                        "variant": {
                          "id": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "variant",
                          "name": "Clicker Blue"
                        },
                        "quantity": 1
                      }
                    ],
                    "decision": "pending",
                    "paid_by": null,
                    "order": null
                  },
                  "created_at": "2026-10-09T15:20:00Z",
                  "submitted_at": "2026-10-10T09:01:00Z",
                  "resolved_at": null,
                  "items": [
                    {
                      "id": "qci_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "qc_item",
                      "title": "Clicker Blue",
                      "product": {
                        "id": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "product",
                        "name": "Clicker"
                      },
                      "variant": {
                        "id": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "variant",
                        "name": "Clicker Blue"
                      },
                      "status": "in_review",
                      "round": 1,
                      "photos": [
                        {
                          "id": "qcp_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "url": "https://files.example.com/qc/clicker-front.jpg?signature=abc",
                          "round": 1,
                          "uploaded_at": "2026-10-10T09:00:00Z"
                        }
                      ],
                      "history": [
                        {
                          "action": "submitted",
                          "by": "production_partner",
                          "round": 1,
                          "note": null,
                          "at": "2026-10-10T09:01:00Z"
                        }
                      ]
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/QcTicket"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have no such order (`order_not_found`), or an item is not on it (`order_item_not_found`, `details.field` names the entry).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "order_not_found",
              "order_item_not_found"
            ]
          },
          "409": {
            "description": "Issues can only be reported on completed orders (`order_not_eligible`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "order_not_eligible"
            ]
          },
          "422": {
            "description": "An item is listed twice (`duplicate_item`), asks for more units than were ordered (`quantity_exceeds_ordered`, `details.ordered`), or an upload is unknown, expired or of the wrong purpose (`unknown_upload`, `upload_expired`, `upload_purpose_mismatch`, `upload_missing` when the file was never sent, `upload_already_used` when the upload id was already used once).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "duplicate_item",
              "quantity_exceeds_ordered",
              "unknown_upload",
              "upload_expired",
              "upload_purpose_mismatch",
              "upload_missing",
              "upload_already_used"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      },
      "get": {
        "operationId": "list_qc_tickets",
        "x-required-scopes": [
          "qc:read"
        ],
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Quality control"
        ],
        "summary": "List QC tickets",
        "description": "Lists quality checks, questions from production partners and issues you reported, newest first. Filter on `needs_your_action=true` to find what is waiting on you, or on `blocks_shipping=true` to see what holds orders back. Designers only.",
        "parameters": [
          {
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/QcTicketKind"
              },
              "description": "Only these kinds."
            },
            "required": false,
            "description": "Only these kinds.",
            "style": "form",
            "explode": true,
            "name": "kind",
            "in": "query"
          },
          {
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/QcTicketStatus"
              },
              "description": "Only tickets in these states."
            },
            "required": false,
            "description": "Only tickets in these states.",
            "style": "form",
            "explode": true,
            "name": "status",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ],
              "description": "Only tickets that are (or are not) waiting on you."
            },
            "required": false,
            "description": "Only tickets that are (or are not) waiting on you.",
            "name": "needs_your_action",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ],
              "description": "Only tickets that are (or are not) holding an order back."
            },
            "required": false,
            "description": "Only tickets that are (or are not) holding an order back.",
            "name": "blocks_shipping",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "pattern": "^ord_[0-9A-Za-z]+$",
              "description": "Only tickets on this order.",
              "example": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": false,
            "description": "Only tickets on this order.",
            "name": "order",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time",
              "description": "Only tickets opened after this time.",
              "example": "2026-04-21T07:34:06Z"
            },
            "required": false,
            "description": "Only tickets opened after this time.",
            "name": "created_after",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "-created_at",
                "created_at"
              ],
              "default": "-created_at",
              "description": "Order of results. A leading `-` means newest first."
            },
            "required": false,
            "description": "Order of results. A leading `-` means newest first.",
            "name": "sort",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "description": "How many results per page, from 1 to 100.",
              "example": 20
            },
            "required": false,
            "description": "How many results per page, from 1 to 100.",
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Where to continue from: the `next_cursor` of the previous page. Omit for the first page."
            },
            "required": false,
            "description": "Where to continue from: the `next_cursor` of the previous page. Omit for the first page.",
            "name": "cursor",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of tickets.",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "id": "qct_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "qc_ticket",
                      "kind": "quality_check",
                      "reason": "spot_check",
                      "status": "awaiting_your_review",
                      "needs_your_action": true,
                      "blocks_shipping": true,
                      "order": {
                        "id": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "order",
                        "name": "PV-SCN-1701A372"
                      },
                      "round": 1,
                      "replacement": null,
                      "created_at": "2026-10-09T15:20:00Z",
                      "submitted_at": "2026-10-10T09:01:00Z",
                      "resolved_at": null
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/QcTicketList"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/qc-tickets/{id}": {
      "get": {
        "operationId": "get_qc_ticket",
        "x-required-scopes": [
          "qc:read"
        ],
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Quality control"
        ],
        "summary": "Get a QC ticket",
        "description": "Returns one ticket with every product on it, its photos and the history of decisions and messages. Designers only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^qct_[0-9A-Za-z]+$",
              "description": "Unique identifier of the qc ticket, prefixed with `qct_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "qct_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the qc ticket, prefixed with `qct_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The ticket.",
            "content": {
              "application/json": {
                "example": {
                  "id": "qct_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "qc_ticket",
                  "kind": "quality_check",
                  "reason": "spot_check",
                  "status": "awaiting_your_review",
                  "needs_your_action": true,
                  "blocks_shipping": true,
                  "order": {
                    "id": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "order",
                    "name": "PV-SCN-1701A372"
                  },
                  "round": 1,
                  "replacement": null,
                  "created_at": "2026-10-09T15:20:00Z",
                  "submitted_at": "2026-10-10T09:01:00Z",
                  "resolved_at": null,
                  "items": [
                    {
                      "id": "qci_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "qc_item",
                      "title": "Clicker Blue",
                      "product": {
                        "id": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "product",
                        "name": "Clicker"
                      },
                      "variant": {
                        "id": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "variant",
                        "name": "Clicker Blue"
                      },
                      "status": "in_review",
                      "round": 1,
                      "photos": [
                        {
                          "id": "qcp_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "url": "https://files.example.com/qc/clicker-front.jpg?signature=abc",
                          "round": 1,
                          "uploaded_at": "2026-10-10T09:00:00Z"
                        }
                      ],
                      "history": [
                        {
                          "action": "submitted",
                          "by": "production_partner",
                          "round": 1,
                          "note": null,
                          "at": "2026-10-10T09:01:00Z"
                        }
                      ]
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/QcTicket"
                }
              }
            }
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have no QC ticket with this id (`qc_ticket_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "qc_ticket_not_found"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/stocks": {
      "get": {
        "operationId": "list_stocks",
        "x-required-scopes": [
          "stocks:read"
        ],
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Stocks"
        ],
        "summary": "List stocks",
        "description": "Lists your stocks, newest first, with how many units are available and in production. Filter on `in_stock=true` to see what can ship now. Designers only.",
        "parameters": [
          {
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/ReviewStatus"
              },
              "description": "Only stocks in these review states."
            },
            "required": false,
            "description": "Only stocks in these review states.",
            "style": "form",
            "explode": true,
            "name": "review_status",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ],
              "description": "Only stocks with (or without) units available to ship."
            },
            "required": false,
            "description": "Only stocks with (or without) units available to ship.",
            "name": "in_stock",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Only stocks whose name contains this text, ignoring case."
            },
            "required": false,
            "description": "Only stocks whose name contains this text, ignoring case.",
            "name": "search",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "-created_at",
                "created_at",
                "name"
              ],
              "default": "-created_at",
              "description": "Order of results. A leading `-` means descending. `name` sorts alphabetically in a language-aware collation, which compares letters before spaces and case (`test slicer` comes after `testing warnings`); do not expect byte order."
            },
            "required": false,
            "description": "Order of results. A leading `-` means descending. `name` sorts alphabetically in a language-aware collation, which compares letters before spaces and case (`test slicer` comes after `testing warnings`); do not expect byte order.",
            "name": "sort",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "description": "How many results per page, from 1 to 100.",
              "example": 20
            },
            "required": false,
            "description": "How many results per page, from 1 to 100.",
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Where to continue from: the `next_cursor` of the previous page. Omit for the first page."
            },
            "required": false,
            "description": "Where to continue from: the `next_cursor` of the previous page. Omit for the first page.",
            "name": "cursor",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of stocks.",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "id": "stk_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "stock",
                      "name": "Clicker 50 pack",
                      "review": {
                        "status": "approved",
                        "reason": null,
                        "reviewed_at": "2026-05-02T10:00:00Z"
                      },
                      "units_per_batch": 50,
                      "batch_price": {
                        "amount": 30000,
                        "currency": "usd"
                      },
                      "unit_price": {
                        "amount": 600,
                        "currency": "usd"
                      },
                      "inventory": {
                        "available": 32,
                        "in_production": 18
                      },
                      "images": [
                        {
                          "url": "https://cdn.example.com/clicker.png",
                          "primary": true
                        }
                      ],
                      "created_at": "2026-04-30T12:00:00Z",
                      "updated_at": "2026-05-02T10:00:00Z"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/StockList"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      },
      "post": {
        "operationId": "create_stock",
        "x-required-scopes": [
          "stocks:write"
        ],
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Stocks"
        ],
        "summary": "Create a stock",
        "description": "Creates a stock (ready-made units you buy in batches) and submits it for review. It comes back pending, without a price or regions: 3DAPI sets the batch price and the production regions when approving it. Upload design files first with purpose `design_file`. Your plan must include prepaid stock. Designers only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Any unique string, for example a UUID. Sending the same request again with the same key within 24 hours returns the first response without doing anything twice. Reusing a key with a different body is refused with `idempotency_key_reused`. A refused request (4xx) frees the key so you can correct and resend it; a server error (5xx) is kept and returned again, because the write may already have happened: check the resource before retrying with a new key."
            },
            "required": false,
            "description": "Any unique string, for example a UUID. Sending the same request again with the same key within 24 hours returns the first response without doing anything twice. Reusing a key with a different body is refused with `idempotency_key_reused`. A refused request (4xx) frees the key so you can correct and resend it; a server error (5xx) is kept and returned again, because the write may already have happened: check the resource before retrying with a new key.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "name": "Clicker 50 pack",
                "units_per_batch": 50,
                "designs": [
                  {
                    "file": {
                      "upload": "upl_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                    },
                    "name": "Clicker",
                    "materials": {
                      "mode": "single",
                      "allowed": [
                        "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                      ]
                    }
                  }
                ],
                "extras": [
                  "xtr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                ],
                "manufacturing": {
                  "assembly_required": true,
                  "farm_instructions": "Bag in tens."
                }
              },
              "schema": {
                "$ref": "#/components/schemas/CreateStockRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The new stock, pending review.",
            "content": {
              "application/json": {
                "example": {
                  "id": "stk_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "stock",
                  "name": "Clicker 50 pack",
                  "description": "Pre-assembled clickers, bagged.",
                  "review": {
                    "status": "pending",
                    "reason": null,
                    "reviewed_at": null
                  },
                  "units_per_batch": 50,
                  "batch_price": null,
                  "unit_price": null,
                  "contents": [
                    {
                      "type": "design",
                      "design": {
                        "id": "dsg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "design",
                        "name": "clicker.3mf"
                      }
                    },
                    {
                      "type": "extra",
                      "extra": {
                        "id": "xtr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "extra",
                        "name": "M3 screw"
                      }
                    }
                  ],
                  "inventory": {
                    "available": 0,
                    "in_production": 0,
                    "by_region": []
                  },
                  "restock": [],
                  "images": [
                    {
                      "url": "https://cdn.example.com/clicker.png",
                      "primary": true
                    }
                  ],
                  "attachments": [
                    {
                      "id": "att_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "attachment",
                      "name": "packing-guide.pdf"
                    }
                  ],
                  "created_at": "2026-04-30T12:00:00Z",
                  "updated_at": "2026-05-02T10:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Stock"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "Your plan does not include prepaid stock, which stocks are part of (`plan_limit_reached`); `details.fix_url` points at upgrading.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "plan_limit_reached"
            ]
          },
          "422": {
            "description": "An extra id is not one of yours (`extra_not_found`) or names an extra 3DAPI rejected (`extra_not_approved`, its name in `details.extra_name`), a material id is not in the catalog (`material_not_found`), multicolor combinations have different slot counts (`slot_count_mismatch`), more than one image is primary (`too_many_primary_images`), an image or attachment is sent as one to keep although a new stock has none yet (`unknown_image`, `unknown_attachment`; send `{ \"upload\": \"upl_...\" }`), or an upload id is unknown, expired or for another purpose (`unknown_upload`, `upload_expired`, `upload_purpose_mismatch`, `upload_missing` when the file was never sent, `upload_already_used` when the upload id was already used once).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "extra_not_found",
              "extra_not_approved",
              "material_not_found",
              "slot_count_mismatch",
              "too_many_primary_images",
              "unknown_image",
              "unknown_attachment",
              "unknown_upload",
              "upload_expired",
              "upload_purpose_mismatch",
              "upload_missing",
              "upload_already_used"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/stocks/{id}": {
      "get": {
        "operationId": "get_stock",
        "x-required-scopes": [
          "stocks:read"
        ],
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Stocks"
        ],
        "summary": "Get a stock",
        "description": "Returns one of your stocks: its price, contents, inventory per region and restock rules. Designers only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^stk_[0-9A-Za-z]+$",
              "description": "Unique identifier of the stock, prefixed with `stk_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "stk_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the stock, prefixed with `stk_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "contents.design",
                  "contents.extra",
                  "attachments"
                ]
              },
              "description": "Replace references with full objects. Repeat the parameter for several paths, for example `expand[]=contents.design`. Unknown paths are rejected with `invalid_expand`. An expanded object from another resource needs the read scope of that resource on your API key; without it the reference `{ id, object, name }` is returned instead, with no error. Here `contents.extra` needs `extras:read`."
            },
            "required": false,
            "description": "Replace references with full objects. Repeat the parameter for several paths, for example `expand[]=contents.design`. Unknown paths are rejected with `invalid_expand`. An expanded object from another resource needs the read scope of that resource on your API key; without it the reference `{ id, object, name }` is returned instead, with no error. Here `contents.extra` needs `extras:read`.",
            "style": "form",
            "explode": true,
            "name": "expand[]",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "The stock.",
            "content": {
              "application/json": {
                "example": {
                  "id": "stk_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "stock",
                  "name": "Clicker 50 pack",
                  "description": "Pre-assembled clickers, bagged.",
                  "review": {
                    "status": "approved",
                    "reason": null,
                    "reviewed_at": "2026-05-02T10:00:00Z"
                  },
                  "units_per_batch": 50,
                  "batch_price": {
                    "amount": 30000,
                    "currency": "usd"
                  },
                  "unit_price": {
                    "amount": 600,
                    "currency": "usd"
                  },
                  "contents": [
                    {
                      "type": "design",
                      "design": {
                        "id": "dsg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "design",
                        "name": "clicker.3mf"
                      }
                    },
                    {
                      "type": "extra",
                      "extra": {
                        "id": "xtr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "extra",
                        "name": "M3 screw"
                      }
                    }
                  ],
                  "inventory": {
                    "available": 32,
                    "in_production": 18,
                    "by_region": [
                      {
                        "region": {
                          "id": "reg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "region",
                          "name": "Western Europe"
                        },
                        "available": 32,
                        "in_production": 18
                      }
                    ]
                  },
                  "restock": [
                    {
                      "region": {
                        "id": "reg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "region",
                        "name": "Western Europe"
                      },
                      "mode": "auto_buy",
                      "when_available_below": 10,
                      "reorder_units": 50
                    }
                  ],
                  "images": [
                    {
                      "url": "https://cdn.example.com/clicker.png",
                      "primary": true
                    }
                  ],
                  "attachments": [
                    {
                      "id": "att_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "attachment",
                      "name": "packing-guide.pdf"
                    }
                  ],
                  "created_at": "2026-04-30T12:00:00Z",
                  "updated_at": "2026-05-02T10:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Stock"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have no stock with this id (`stock_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "stock_not_found"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      },
      "patch": {
        "operationId": "update_stock",
        "x-required-scopes": [
          "stocks:write"
        ],
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Stocks"
        ],
        "summary": "Update a stock",
        "description": "Changes the fields you send. Changing `units_per_batch` or the farm instructions sends the stock back to review; it cannot be bought until approved again. Designers only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^stk_[0-9A-Za-z]+$",
              "description": "Unique identifier of the stock, prefixed with `stk_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "stk_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the stock, prefixed with `stk_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "units_per_batch": 50,
                "manufacturing": {
                  "assembly_required": true,
                  "farm_instructions": "Pack each unit in its own bag."
                }
              },
              "schema": {
                "$ref": "#/components/schemas/UpdateStockRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated stock.",
            "content": {
              "application/json": {
                "example": {
                  "id": "stk_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "stock",
                  "name": "Clicker 50 pack",
                  "description": "Pre-assembled clickers, bagged.",
                  "review": {
                    "status": "approved",
                    "reason": null,
                    "reviewed_at": "2026-05-02T10:00:00Z"
                  },
                  "units_per_batch": 50,
                  "batch_price": {
                    "amount": 30000,
                    "currency": "usd"
                  },
                  "unit_price": {
                    "amount": 600,
                    "currency": "usd"
                  },
                  "contents": [
                    {
                      "type": "design",
                      "design": {
                        "id": "dsg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "design",
                        "name": "clicker.3mf"
                      }
                    },
                    {
                      "type": "extra",
                      "extra": {
                        "id": "xtr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "extra",
                        "name": "M3 screw"
                      }
                    }
                  ],
                  "inventory": {
                    "available": 32,
                    "in_production": 18,
                    "by_region": [
                      {
                        "region": {
                          "id": "reg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "region",
                          "name": "Western Europe"
                        },
                        "available": 32,
                        "in_production": 18
                      }
                    ]
                  },
                  "restock": [
                    {
                      "region": {
                        "id": "reg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "region",
                        "name": "Western Europe"
                      },
                      "mode": "auto_buy",
                      "when_available_below": 10,
                      "reorder_units": 50
                    }
                  ],
                  "images": [
                    {
                      "url": "https://cdn.example.com/clicker.png",
                      "primary": true
                    }
                  ],
                  "attachments": [
                    {
                      "id": "att_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "attachment",
                      "name": "packing-guide.pdf"
                    }
                  ],
                  "created_at": "2026-04-30T12:00:00Z",
                  "updated_at": "2026-05-02T10:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Stock"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have no stock with this id (`stock_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "stock_not_found"
            ]
          },
          "422": {
            "description": "An extra id is not one of yours (`extra_not_found`) or names an extra 3DAPI rejected (`extra_not_approved`, its name in `details.extra_name`), an image `url` or attachment `id` to keep is not one of the stock's (`unknown_image`, `unknown_attachment`), more than one image is primary (`too_many_primary_images`), or an upload id is unknown, expired or for another purpose.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "extra_not_found",
              "extra_not_approved",
              "unknown_image",
              "unknown_attachment",
              "too_many_primary_images"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/stock-batches": {
      "get": {
        "operationId": "list_all_stock_batches",
        "x-required-scopes": [
          "stocks:read"
        ],
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Stocks"
        ],
        "summary": "List batches across stocks",
        "description": "Lists every batch you bought, of any stock, newest first. Use it to see everything in production at once, or every batch bought in one checkout with `purchase_reference`. Designers only.",
        "parameters": [
          {
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/StockBatchStatus"
              },
              "description": "Only batches in these states. Repeat for several, for example `status=queued&status=in_production`."
            },
            "required": false,
            "description": "Only batches in these states. Repeat for several, for example `status=queued&status=in_production`.",
            "style": "form",
            "explode": true,
            "name": "status",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "pattern": "^reg_[0-9A-Za-z]+$",
              "description": "Only batches held in this region.",
              "example": "reg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": false,
            "description": "Only batches held in this region.",
            "name": "region",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "-purchased_at",
                "purchased_at"
              ],
              "default": "-purchased_at",
              "description": "Order of results. A leading `-` means newest first."
            },
            "required": false,
            "description": "Order of results. A leading `-` means newest first.",
            "name": "sort",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "description": "How many results per page, from 1 to 100.",
              "example": 20
            },
            "required": false,
            "description": "How many results per page, from 1 to 100.",
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Where to continue from: the `next_cursor` of the previous page. Omit for the first page."
            },
            "required": false,
            "description": "Where to continue from: the `next_cursor` of the previous page. Omit for the first page.",
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "pattern": "^stk_[0-9A-Za-z]+$",
              "description": "Only batches of this stock.",
              "example": "stk_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": false,
            "description": "Only batches of this stock.",
            "name": "stock",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time",
              "description": "Only batches bought after this time.",
              "example": "2026-04-21T07:34:06Z"
            },
            "required": false,
            "description": "Only batches bought after this time.",
            "name": "purchased_after",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time",
              "description": "Only batches bought before this time.",
              "example": "2026-04-21T07:34:06Z"
            },
            "required": false,
            "description": "Only batches bought before this time.",
            "name": "purchased_before",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "manual",
                "auto_reorder"
              ],
              "description": "Only batches you bought (`manual`) or auto-reorder bought."
            },
            "required": false,
            "description": "Only batches you bought (`manual`) or auto-reorder bought.",
            "name": "origin",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Only batches bought in this checkout, for example `ST-8KD2X`."
            },
            "required": false,
            "description": "Only batches bought in this checkout, for example `ST-8KD2X`.",
            "name": "purchase_reference",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of batches.",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "id": "bat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "stock_batch",
                      "stock": {
                        "id": "stk_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "stock",
                        "name": "Clicker 50 pack"
                      },
                      "region": {
                        "id": "reg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "region",
                        "name": "Western Europe"
                      },
                      "status": "in_production",
                      "batches": 2,
                      "units": {
                        "purchased": 100,
                        "made": 60,
                        "used": 20,
                        "available": 40
                      },
                      "price_paid": {
                        "amount": 60000,
                        "currency": "usd"
                      },
                      "unit_price": {
                        "amount": 600,
                        "currency": "usd"
                      },
                      "purchase_reference": "ST-8KD2X",
                      "origin": "manual",
                      "purchased_at": "2026-09-30T10:00:00Z",
                      "completed_at": null
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/StockBatchList"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/stocks/{id}/batches/{batch_id}/usage": {
      "get": {
        "operationId": "list_stock_batch_usage",
        "x-required-scopes": [
          "stocks:read"
        ],
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Stocks"
        ],
        "summary": "List what used a batch",
        "description": "Lists the orders that took units from one batch, newest first. Designers only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^stk_[0-9A-Za-z]+$",
              "description": "Unique identifier of the stock, prefixed with `stk_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "stk_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the stock, prefixed with `stk_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "pattern": "^bat_[0-9A-Za-z]+$",
              "description": "Unique identifier of the stock batch, prefixed with `bat_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "bat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the stock batch, prefixed with `bat_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "batch_id",
            "in": "path"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "description": "How many results per page, from 1 to 100.",
              "example": 20
            },
            "required": false,
            "description": "How many results per page, from 1 to 100.",
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Where to continue from: the `next_cursor` of the previous page. Omit for the first page."
            },
            "required": false,
            "description": "Where to continue from: the `next_cursor` of the previous page. Omit for the first page.",
            "name": "cursor",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of usage lines.",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "order": {
                        "id": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "order",
                        "name": "SSG2M9S9"
                      },
                      "variant": {
                        "id": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "variant",
                        "name": "Clicker Blue"
                      },
                      "units": 2,
                      "used_at": "2026-10-11T08:00:00Z"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/StockBatchUsageList"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have no such stock, or the batch is not one of its batches (`stock_not_found`, `stock_batch_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "stock_not_found",
              "stock_batch_not_found"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/stocks/{id}/batches": {
      "get": {
        "operationId": "list_stock_batches",
        "x-required-scopes": [
          "stocks:read"
        ],
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Stocks"
        ],
        "summary": "List the batches of a stock",
        "description": "Lists every batch you bought of one stock, newest first, with how many units are made, used and still available. Use it to follow production and see where your inventory is. Designers only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^stk_[0-9A-Za-z]+$",
              "description": "Unique identifier of the stock, prefixed with `stk_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "stk_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the stock, prefixed with `stk_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/StockBatchStatus"
              },
              "description": "Only batches in these states. Repeat for several, for example `status=queued&status=in_production`."
            },
            "required": false,
            "description": "Only batches in these states. Repeat for several, for example `status=queued&status=in_production`.",
            "style": "form",
            "explode": true,
            "name": "status",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "pattern": "^reg_[0-9A-Za-z]+$",
              "description": "Only batches held in this region.",
              "example": "reg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": false,
            "description": "Only batches held in this region.",
            "name": "region",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "-purchased_at",
                "purchased_at"
              ],
              "default": "-purchased_at",
              "description": "Order of results. A leading `-` means newest first."
            },
            "required": false,
            "description": "Order of results. A leading `-` means newest first.",
            "name": "sort",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "description": "How many results per page, from 1 to 100.",
              "example": 20
            },
            "required": false,
            "description": "How many results per page, from 1 to 100.",
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Where to continue from: the `next_cursor` of the previous page. Omit for the first page."
            },
            "required": false,
            "description": "Where to continue from: the `next_cursor` of the previous page. Omit for the first page.",
            "name": "cursor",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of batches.",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "id": "bat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "stock_batch",
                      "stock": {
                        "id": "stk_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "stock",
                        "name": "Clicker 50 pack"
                      },
                      "region": {
                        "id": "reg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "region",
                        "name": "Western Europe"
                      },
                      "status": "in_production",
                      "batches": 2,
                      "units": {
                        "purchased": 100,
                        "made": 60,
                        "used": 20,
                        "available": 40
                      },
                      "price_paid": {
                        "amount": 60000,
                        "currency": "usd"
                      },
                      "unit_price": {
                        "amount": 600,
                        "currency": "usd"
                      },
                      "purchase_reference": "ST-8KD2X",
                      "origin": "manual",
                      "purchased_at": "2026-09-30T10:00:00Z",
                      "completed_at": null
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/StockBatchList"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have no stock with this id (`stock_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "stock_not_found"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/stocks/{id}/batches/{batch_id}": {
      "get": {
        "operationId": "get_stock_batch",
        "x-required-scopes": [
          "stocks:read"
        ],
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Stocks"
        ],
        "summary": "Get a stock batch",
        "description": "Returns one batch of a stock with its production progress. Designers only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^stk_[0-9A-Za-z]+$",
              "description": "Unique identifier of the stock, prefixed with `stk_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "stk_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the stock, prefixed with `stk_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "pattern": "^bat_[0-9A-Za-z]+$",
              "description": "Unique identifier of the stock batch, prefixed with `bat_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "bat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the stock batch, prefixed with `bat_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "batch_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The batch.",
            "content": {
              "application/json": {
                "example": {
                  "id": "bat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "stock_batch",
                  "stock": {
                    "id": "stk_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "stock",
                    "name": "Clicker 50 pack"
                  },
                  "region": {
                    "id": "reg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "region",
                    "name": "Western Europe"
                  },
                  "status": "in_production",
                  "batches": 2,
                  "units": {
                    "purchased": 100,
                    "made": 60,
                    "used": 20,
                    "available": 40
                  },
                  "price_paid": {
                    "amount": 60000,
                    "currency": "usd"
                  },
                  "unit_price": {
                    "amount": 600,
                    "currency": "usd"
                  },
                  "purchase_reference": "ST-8KD2X",
                  "origin": "manual",
                  "purchased_at": "2026-09-30T10:00:00Z",
                  "completed_at": null
                },
                "schema": {
                  "$ref": "#/components/schemas/StockBatch"
                }
              }
            }
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have no such stock, or the batch is not one of its batches (`stock_not_found`, `stock_batch_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "stock_not_found",
              "stock_batch_not_found"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/regions/{id}": {
      "get": {
        "operationId": "get_region",
        "x-required-scopes": [
          "stocks:read"
        ],
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Stocks"
        ],
        "summary": "Get a production region",
        "description": "Returns a region one of your stocks is produced in, with the countries it ships to. Only regions of your own stocks are available. Designers only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^reg_[0-9A-Za-z]+$",
              "description": "Unique identifier of the region, prefixed with `reg_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "reg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the region, prefixed with `reg_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The region.",
            "content": {
              "application/json": {
                "example": {
                  "id": "reg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "region",
                  "name": "Western Europe",
                  "countries": [
                    "DE",
                    "FR",
                    "NL"
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/Region"
                }
              }
            }
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "None of your stocks is produced in this region (`region_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "region_not_found"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/stocks/{id}/designs": {
      "post": {
        "operationId": "add_stock_design",
        "x-required-scopes": [
          "stocks:write"
        ],
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Stocks"
        ],
        "summary": "Add a design to a stock",
        "description": "Adds a model file to a stock. Sends the stock back to review. Edit or remove designs with the design operations. Designers only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^stk_[0-9A-Za-z]+$",
              "description": "Unique identifier of the stock, prefixed with `stk_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "stk_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the stock, prefixed with `stk_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Any unique string, for example a UUID. Sending the same request again with the same key within 24 hours returns the first response without doing anything twice. Reusing a key with a different body is refused with `idempotency_key_reused`. A refused request (4xx) frees the key so you can correct and resend it; a server error (5xx) is kept and returned again, because the write may already have happened: check the resource before retrying with a new key."
            },
            "required": false,
            "description": "Any unique string, for example a UUID. Sending the same request again with the same key within 24 hours returns the first response without doing anything twice. Reusing a key with a different body is refused with `idempotency_key_reused`. A refused request (4xx) frees the key so you can correct and resend it; a server error (5xx) is kept and returned again, because the write may already have happened: check the resource before retrying with a new key.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "file": {
                  "upload": "upl_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                },
                "name": "Tracker lid",
                "materials": {
                  "mode": "single",
                  "allowed": [
                    "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                  ]
                }
              },
              "schema": {
                "$ref": "#/components/schemas/AddDesignRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The new design, pending review.",
            "content": {
              "application/json": {
                "example": {
                  "id": "dsg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "design",
                  "name": "tracker.3mf",
                  "owner": {
                    "id": "stk_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "stock",
                    "name": "Clicker 50 pack"
                  },
                  "review": {
                    "status": "pending",
                    "reason": null,
                    "reviewed_at": null
                  },
                  "files": [
                    {
                      "id": "dfl_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "design_file",
                      "name": "tracker.3mf",
                      "format": "3mf",
                      "size_bytes": 2758723,
                      "download_url": "https://files.example.com/tracker.3mf?signature=abc",
                      "uploaded_at": "2026-04-21T07:34:06Z",
                      "production_version": {
                        "name": "farm-settings-tracker.3mf",
                        "format": "3mf",
                        "size_bytes": 2761090,
                        "download_url": "https://files.example.com/farm-settings-tracker.3mf?signature=def",
                        "prepared_at": "2026-04-21T07:36:40Z"
                      }
                    }
                  ],
                  "materials": {
                    "mode": "single",
                    "allowed": [
                      {
                        "id": "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "material",
                        "name": "PLA Black",
                        "custom": false
                      },
                      {
                        "id": null,
                        "object": "material",
                        "name": "PETG Matte",
                        "custom": true
                      }
                    ]
                  },
                  "manufacturing": {
                    "colors": 1,
                    "requires_ams": false,
                    "assembly_required": true,
                    "farm_instructions": null,
                    "extra_parts_note": "Two M2 screws"
                  },
                  "created_at": "2026-04-21T07:34:06Z",
                  "updated_at": "2026-04-21T09:10:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Design"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have no stock with this id (`stock_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "stock_not_found"
            ]
          },
          "422": {
            "description": "A material id is not in the catalog (`material_not_found`), multicolor combinations have different slot counts (`slot_count_mismatch`), or the upload id is unknown, expired or for another purpose.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "material_not_found",
              "slot_count_mismatch"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/stock-purchases": {
      "post": {
        "operationId": "create_stock_purchase",
        "x-required-scopes": [
          "stocks:write"
        ],
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Stocks"
        ],
        "summary": "Buy stock batches",
        "description": "Buys batches of one or more approved stocks in one checkout, paid from your wallet. There is no card top-up: if the balance is short, nothing is bought. Each item becomes a batch that the production partner of its region makes. Auto-reorder buys from the same wallet: when a region reaches its restock threshold and auto-reorder is on in your wallet settings, batches are bought automatically (`origin: auto_reorder`), so keep enough balance for both. Your plan must include prepaid stock. Designers only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Any unique string, for example a UUID. Sending the same request again with the same key within 24 hours returns the first response without doing anything twice. Reusing a key with a different body is refused with `idempotency_key_reused`. A refused request (4xx) frees the key so you can correct and resend it; a server error (5xx) is kept and returned again, because the write may already have happened: check the resource before retrying with a new key."
            },
            "required": false,
            "description": "Any unique string, for example a UUID. Sending the same request again with the same key within 24 hours returns the first response without doing anything twice. Reusing a key with a different body is refused with `idempotency_key_reused`. A refused request (4xx) frees the key so you can correct and resend it; a server error (5xx) is kept and returned again, because the write may already have happened: check the resource before retrying with a new key.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "items": [
                  {
                    "stock": "stk_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "region": "reg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "batches": 2
                  }
                ]
              },
              "schema": {
                "$ref": "#/components/schemas/CreateStockPurchaseRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The purchase and the batches bought.",
            "content": {
              "application/json": {
                "example": {
                  "object": "stock_purchase",
                  "purchase_reference": "ST-8KD2X",
                  "total": {
                    "amount": 60000,
                    "currency": "usd"
                  },
                  "batches": [
                    {
                      "id": "bat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "stock_batch",
                      "stock": {
                        "id": "stk_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "stock",
                        "name": "Clicker 50 pack"
                      },
                      "region": {
                        "id": "reg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "region",
                        "name": "Western Europe"
                      },
                      "status": "queued",
                      "batches": 2,
                      "units": {
                        "purchased": 100,
                        "made": 60,
                        "used": 20,
                        "available": 40
                      },
                      "price_paid": {
                        "amount": 60000,
                        "currency": "usd"
                      },
                      "unit_price": {
                        "amount": 600,
                        "currency": "usd"
                      },
                      "purchase_reference": "ST-8KD2X",
                      "origin": "manual",
                      "purchased_at": "2026-09-30T10:00:00Z",
                      "completed_at": null
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/StockPurchase"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "402": {
            "description": "Your wallet balance does not cover the purchase (`insufficient_balance`); `details.required` is the amount needed and `details.balance` what the wallet holds. Nothing was bought.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "insufficient_balance"
            ]
          },
          "403": {
            "description": "Your plan does not include prepaid stock (`plan_limit_reached`); `details.fix_url` points at upgrading.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "plan_limit_reached"
            ]
          },
          "409": {
            "description": "A stock is not approved and priced (`stock_not_approved`); `details.items` lists the positions concerned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "stock_not_approved"
            ]
          },
          "422": {
            "description": "A stock is not one of yours (`stock_not_found`), a region is not one of the stock regions (`region_not_available`), or a stock and region pair appears twice (`duplicate_item`); `details.items` lists the positions concerned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "stock_not_found",
              "region_not_available",
              "duplicate_item"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user), plus 60 per minute per API key (or signed-in user) for quote, upload, order and payment operations. Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          },
          {
            "policy": "expensive_write",
            "limit": 60,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/stocks/{id}/restock/{region_id}": {
      "put": {
        "operationId": "set_stock_restock",
        "x-required-scopes": [
          "stocks:write"
        ],
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Stocks"
        ],
        "summary": "Set a restock rule",
        "description": "Sets what happens when one region runs low (see `when_available_below` for exactly what is counted). Auto-reorder buys from the same wallet: when a region reaches its restock threshold and auto-reorder is on in your wallet settings, batches are bought automatically (`origin: auto_reorder`), so keep enough balance for both. Returns the stock with its updated `restock` list. Designers only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^stk_[0-9A-Za-z]+$",
              "description": "Unique identifier of the stock, prefixed with `stk_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "stk_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the stock, prefixed with `stk_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "pattern": "^reg_[0-9A-Za-z]+$",
              "description": "Unique identifier of the region, prefixed with `reg_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "reg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the region, prefixed with `reg_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "region_id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "mode": "auto_buy",
                "when_available_below": 10,
                "reorder_units": 50
              },
              "schema": {
                "$ref": "#/components/schemas/SetRestockRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The stock with the new rule.",
            "content": {
              "application/json": {
                "example": {
                  "id": "stk_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "stock",
                  "name": "Clicker 50 pack",
                  "description": "Pre-assembled clickers, bagged.",
                  "review": {
                    "status": "approved",
                    "reason": null,
                    "reviewed_at": "2026-05-02T10:00:00Z"
                  },
                  "units_per_batch": 50,
                  "batch_price": {
                    "amount": 30000,
                    "currency": "usd"
                  },
                  "unit_price": {
                    "amount": 600,
                    "currency": "usd"
                  },
                  "contents": [
                    {
                      "type": "design",
                      "design": {
                        "id": "dsg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "design",
                        "name": "clicker.3mf"
                      }
                    },
                    {
                      "type": "extra",
                      "extra": {
                        "id": "xtr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "extra",
                        "name": "M3 screw"
                      }
                    }
                  ],
                  "inventory": {
                    "available": 32,
                    "in_production": 18,
                    "by_region": [
                      {
                        "region": {
                          "id": "reg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "region",
                          "name": "Western Europe"
                        },
                        "available": 32,
                        "in_production": 18
                      }
                    ]
                  },
                  "restock": [
                    {
                      "region": {
                        "id": "reg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "region",
                        "name": "Western Europe"
                      },
                      "mode": "auto_buy",
                      "when_available_below": 10,
                      "reorder_units": 50
                    }
                  ],
                  "images": [
                    {
                      "url": "https://cdn.example.com/clicker.png",
                      "primary": true
                    }
                  ],
                  "attachments": [
                    {
                      "id": "att_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "attachment",
                      "name": "packing-guide.pdf"
                    }
                  ],
                  "created_at": "2026-04-30T12:00:00Z",
                  "updated_at": "2026-05-02T10:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Stock"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid, including a field the mode needs that is missing or one that does not apply to it (`validation_failed`, `details.errors`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "`auto_buy` buys stock, so your plan must include prepaid stock (`plan_limit_reached`); `details.fix_url` points at upgrading.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "plan_limit_reached"
            ]
          },
          "404": {
            "description": "You have no stock with this id (`stock_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "stock_not_found"
            ]
          },
          "422": {
            "description": "The region is not one of the stock regions (`region_not_available`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "region_not_available"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/extras": {
      "post": {
        "operationId": "create_extra",
        "x-required-scopes": [
          "extras:write"
        ],
        "x-audience": [
          "designer",
          "integrator"
        ],
        "tags": [
          "Extras"
        ],
        "summary": "Create an extra part",
        "description": "Adds an extra part and submits it for review by 3DAPI. It comes back with `review.status: pending` and no price; we set the price when we approve it, and only approved extras can be used: in variants (designers) or on order lines (integrators, `items[].extras` of create_integrator_order). Upload the photos first with purpose `extra_image`. For designers and integrators.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Any unique string, for example a UUID. Sending the same request again with the same key within 24 hours returns the first response without doing anything twice. Reusing a key with a different body is refused with `idempotency_key_reused`. A refused request (4xx) frees the key so you can correct and resend it; a server error (5xx) is kept and returned again, because the write may already have happened: check the resource before retrying with a new key."
            },
            "required": false,
            "description": "Any unique string, for example a UUID. Sending the same request again with the same key within 24 hours returns the first response without doing anything twice. Reusing a key with a different body is refused with `idempotency_key_reused`. A refused request (4xx) frees the key so you can correct and resend it; a server error (5xx) is kept and returned again, because the write may already have happened: check the resource before retrying with a new key.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "name": "Magnet 10mm",
                "description": "Neodymium magnet, 10mm diameter.",
                "purchase_links": [
                  {
                    "url": "https://example.com/magnet-10mm",
                    "label": "Store"
                  }
                ],
                "images": [
                  {
                    "upload": "upl_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                  }
                ]
              },
              "schema": {
                "$ref": "#/components/schemas/CreateExtraRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The extra, pending review.",
            "content": {
              "application/json": {
                "example": {
                  "id": "xtr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "extra",
                  "name": "Magnet 10mm",
                  "description": "Neodymium magnet, 10mm diameter.",
                  "review": {
                    "status": "pending",
                    "reason": null,
                    "reviewed_at": null
                  },
                  "price": null,
                  "purchase_links": [
                    {
                      "url": "https://example.com/magnet-10mm",
                      "label": "Datasheet"
                    }
                  ],
                  "images": [
                    {
                      "url": "https://cdn.example.com/magnet.png",
                      "name": "magnet.png"
                    }
                  ],
                  "created_at": "2026-07-22T09:56:30Z",
                  "updated_at": "2026-07-22T10:05:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Extra"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid (`validation_failed`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "422": {
            "description": "An upload is unknown, expired or of the wrong purpose (`unknown_upload`, `upload_expired`, `upload_purpose_mismatch`, `upload_missing` when the file was never sent, `upload_already_used` when the upload id was already used once), an image `url` is given (a new extra has no photos to keep, `unknown_image`), or the same upload is listed twice (`duplicate_image`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "unknown_upload",
              "upload_expired",
              "upload_purpose_mismatch",
              "upload_missing",
              "upload_already_used",
              "unknown_image",
              "duplicate_image"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      },
      "get": {
        "operationId": "list_extras",
        "x-required-scopes": [
          "extras:read"
        ],
        "x-audience": [
          "designer",
          "integrator"
        ],
        "tags": [
          "Extras"
        ],
        "summary": "List extra parts",
        "description": "Lists your extra parts, newest first, with their review status and price. For designers and integrators.",
        "parameters": [
          {
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/ReviewStatus"
              },
              "description": "Only extras in these review states."
            },
            "required": false,
            "description": "Only extras in these review states.",
            "style": "form",
            "explode": true,
            "name": "review_status",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Only extras whose name contains this text, ignoring case."
            },
            "required": false,
            "description": "Only extras whose name contains this text, ignoring case.",
            "name": "search",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "-created_at",
                "created_at",
                "name"
              ],
              "default": "-created_at",
              "description": "Order of results. A leading `-` means descending. `name` sorts alphabetically in a language-aware collation, which compares letters before spaces and case (`test slicer` comes after `testing warnings`); do not expect byte order."
            },
            "required": false,
            "description": "Order of results. A leading `-` means descending. `name` sorts alphabetically in a language-aware collation, which compares letters before spaces and case (`test slicer` comes after `testing warnings`); do not expect byte order.",
            "name": "sort",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "description": "How many results per page, from 1 to 100.",
              "example": 20
            },
            "required": false,
            "description": "How many results per page, from 1 to 100.",
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Where to continue from: the `next_cursor` of the previous page. Omit for the first page."
            },
            "required": false,
            "description": "Where to continue from: the `next_cursor` of the previous page. Omit for the first page.",
            "name": "cursor",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of extras.",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "id": "xtr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "extra",
                      "name": "Magnet 10mm",
                      "description": "Neodymium magnet, 10mm diameter.",
                      "review": {
                        "status": "approved",
                        "reason": null,
                        "reviewed_at": "2026-07-22T10:05:00Z"
                      },
                      "price": {
                        "amount": 50,
                        "currency": "usd"
                      },
                      "purchase_links": [
                        {
                          "url": "https://example.com/magnet-10mm",
                          "label": "Datasheet"
                        }
                      ],
                      "images": [
                        {
                          "url": "https://cdn.example.com/magnet.png",
                          "name": "magnet.png"
                        }
                      ],
                      "created_at": "2026-07-22T09:56:30Z",
                      "updated_at": "2026-07-22T10:05:00Z"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/ExtraList"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/extras/{id}": {
      "patch": {
        "operationId": "update_extra",
        "x-required-scopes": [
          "extras:write"
        ],
        "x-audience": [
          "designer",
          "integrator"
        ],
        "tags": [
          "Extras"
        ],
        "summary": "Update an extra part",
        "description": "Changes the fields you send. Sending any field sends the extra back to review by 3DAPI (`review.status: pending`), even if the value is unchanged; a body with no fields changes nothing. Until it is approved again the extra keeps its last approved price. For designers, every product using it goes back to review too (`pending`), which takes it off your store until 3DAPI approves the extra again, exactly as an edit in the dashboard does. For integrators, new orders cannot use it until it is approved again; orders already placed keep it. For designers and integrators.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^xtr_[0-9A-Za-z]+$",
              "description": "Unique identifier of the extra, prefixed with `xtr_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "xtr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the extra, prefixed with `xtr_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "description": "Neodymium magnet, 10mm diameter, N52 grade.",
                "images": [
                  {
                    "url": "https://cdn.example.com/magnet.png"
                  },
                  {
                    "upload": "upl_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                  }
                ]
              },
              "schema": {
                "$ref": "#/components/schemas/UpdateExtraRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The extra, pending review.",
            "content": {
              "application/json": {
                "example": {
                  "id": "xtr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "extra",
                  "name": "Magnet 10mm",
                  "description": "Neodymium magnet, 10mm diameter.",
                  "review": {
                    "status": "pending",
                    "reason": null,
                    "reviewed_at": null
                  },
                  "price": {
                    "amount": 50,
                    "currency": "usd"
                  },
                  "purchase_links": [
                    {
                      "url": "https://example.com/magnet-10mm",
                      "label": "Datasheet"
                    }
                  ],
                  "images": [
                    {
                      "url": "https://cdn.example.com/magnet.png",
                      "name": "magnet.png"
                    }
                  ],
                  "created_at": "2026-07-22T09:56:30Z",
                  "updated_at": "2026-07-22T10:05:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Extra"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid (`validation_failed`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have no extra with this id (`extra_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "extra_not_found"
            ]
          },
          "422": {
            "description": "An upload is unknown, expired or of the wrong purpose (`unknown_upload`, `upload_expired`, `upload_purpose_mismatch`, `upload_missing` when the file was never sent, `upload_already_used` when the upload id was already used once), an image `url` is not one of the photos this extra has (`unknown_image`), or a photo is listed twice (`duplicate_image`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "unknown_upload",
              "upload_expired",
              "upload_purpose_mismatch",
              "upload_missing",
              "upload_already_used",
              "unknown_image",
              "duplicate_image"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      },
      "get": {
        "operationId": "get_extra",
        "x-required-scopes": [
          "extras:read"
        ],
        "x-audience": [
          "designer",
          "integrator"
        ],
        "tags": [
          "Extras"
        ],
        "summary": "Get an extra part",
        "description": "Returns one of your extra parts with its review status and price. For designers and integrators.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^xtr_[0-9A-Za-z]+$",
              "description": "Unique identifier of the extra, prefixed with `xtr_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "xtr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the extra, prefixed with `xtr_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The extra.",
            "content": {
              "application/json": {
                "example": {
                  "id": "xtr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "extra",
                  "name": "Magnet 10mm",
                  "description": "Neodymium magnet, 10mm diameter.",
                  "review": {
                    "status": "approved",
                    "reason": null,
                    "reviewed_at": "2026-07-22T10:05:00Z"
                  },
                  "price": {
                    "amount": 50,
                    "currency": "usd"
                  },
                  "purchase_links": [
                    {
                      "url": "https://example.com/magnet-10mm",
                      "label": "Datasheet"
                    }
                  ],
                  "images": [
                    {
                      "url": "https://cdn.example.com/magnet.png",
                      "name": "magnet.png"
                    }
                  ],
                  "created_at": "2026-07-22T09:56:30Z",
                  "updated_at": "2026-07-22T10:05:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Extra"
                }
              }
            }
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have no extra with this id (`extra_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "extra_not_found"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/attachments/{id}": {
      "get": {
        "operationId": "get_attachment",
        "x-required-scopes": [
          "products:read",
          "stocks:read"
        ],
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Attachments"
        ],
        "summary": "Get an attachment",
        "description": "Returns one production document with a download link. Designers only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^att_[0-9A-Za-z]+$",
              "description": "Unique identifier of the attachment, prefixed with `att_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "att_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the attachment, prefixed with `att_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The attachment.",
            "content": {
              "application/json": {
                "example": {
                  "id": "att_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "attachment",
                  "name": "assembly-guide.pdf",
                  "format": "pdf",
                  "size_bytes": 482113,
                  "download_url": "https://files.example.com/assembly-guide.pdf?signature=abc",
                  "owner": {
                    "id": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "product",
                    "name": "PR Tracker"
                  },
                  "created_at": "2026-09-30T14:21:07Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Attachment"
                }
              }
            }
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have no attachment with this id (`attachment_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "attachment_not_found"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/wallet": {
      "get": {
        "operationId": "get_wallet",
        "x-required-scopes": [
          "wallet:read"
        ],
        "x-audience": [
          "designer",
          "integrator"
        ],
        "tags": [
          "Wallet"
        ],
        "summary": "Get your wallet balance",
        "description": "Returns your balance, sample credit and pending withdrawals. Check it before creating orders that are paid from the wallet. Designers only.",
        "responses": {
          "200": {
            "description": "The wallet.",
            "content": {
              "application/json": {
                "example": {
                  "object": "wallet",
                  "balance": {
                    "amount": 24310,
                    "currency": "usd"
                  },
                  "sample_credit": {
                    "amount": 2000,
                    "currency": "usd"
                  },
                  "pending_withdrawals": {
                    "amount": 0,
                    "currency": "usd"
                  },
                  "updated_at": "2026-10-14T08:02:11Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Wallet"
                }
              }
            }
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/transactions": {
      "get": {
        "operationId": "list_transactions",
        "x-required-scopes": [
          "wallet:read"
        ],
        "x-audience": [
          "designer",
          "integrator"
        ],
        "tags": [
          "Wallet"
        ],
        "summary": "List transactions",
        "description": "Lists every movement of your money for orders, stock and the wallet, newest first: wallet payments and top-ups, card-paid orders, earnings, refunds and withdrawals. Plan subscription charges are not included. Designers only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "enum": [
                "credit",
                "debit"
              ],
              "description": "Only credits or only debits."
            },
            "required": false,
            "description": "Only credits or only debits.",
            "name": "direction",
            "in": "query"
          },
          {
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/TransactionCategory"
              },
              "description": "Only these categories. Repeat for several, for example `category=order_payment&category=stock_purchase`."
            },
            "required": false,
            "description": "Only these categories. Repeat for several, for example `category=order_payment&category=stock_purchase`.",
            "style": "form",
            "explode": true,
            "name": "category",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "wallet",
                "card"
              ],
              "description": "Only wallet movements or only card charges."
            },
            "required": false,
            "description": "Only wallet movements or only card charges.",
            "name": "paid_with",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "completed"
              ],
              "description": "Only pending or only completed transactions."
            },
            "required": false,
            "description": "Only pending or only completed transactions.",
            "name": "status",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time",
              "description": "Only transactions after this time. When neither created_after nor created_before is given, defaults to 30 days before the request. Any explicit range is honoured as given, with no maximum length.",
              "example": "2026-04-21T07:34:06Z"
            },
            "required": false,
            "description": "Only transactions after this time. When neither created_after nor created_before is given, defaults to 30 days before the request. Any explicit range is honoured as given, with no maximum length.",
            "name": "created_after",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time",
              "description": "Only transactions before this time. Giving it on its own turns off the 30 day default, so there is no lower bound.",
              "example": "2026-04-21T07:34:06Z"
            },
            "required": false,
            "description": "Only transactions before this time. Giving it on its own turns off the 30 day default, so there is no lower bound.",
            "name": "created_before",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ],
              "minimum": 0,
              "description": "Only amounts of at least this many cents."
            },
            "required": false,
            "description": "Only amounts of at least this many cents.",
            "name": "amount_min",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ],
              "minimum": 0,
              "description": "Only amounts of at most this many cents."
            },
            "required": false,
            "description": "Only amounts of at most this many cents.",
            "name": "amount_max",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Only transactions for this order: an `ord_` id or an order number."
            },
            "required": false,
            "description": "Only transactions for this order: an `ord_` id or an order number.",
            "name": "order",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Matches the description and order numbers."
            },
            "required": false,
            "description": "Matches the description and order numbers.",
            "name": "search",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "-created_at",
                "created_at",
                "-amount",
                "amount"
              ],
              "default": "-created_at",
              "description": "Order of results. A leading `-` means descending."
            },
            "required": false,
            "description": "Order of results. A leading `-` means descending.",
            "name": "sort",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "description": "How many results per page, from 1 to 100.",
              "example": 20
            },
            "required": false,
            "description": "How many results per page, from 1 to 100.",
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Where to continue from: the `next_cursor` of the previous page. Omit for the first page."
            },
            "required": false,
            "description": "Where to continue from: the `next_cursor` of the previous page. Omit for the first page.",
            "name": "cursor",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of transactions.",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "id": "txn_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "transaction",
                      "direction": "debit",
                      "category": "order_payment",
                      "adjustment_reason": null,
                      "paid_with": "wallet",
                      "status": "completed",
                      "amount": {
                        "amount": 5299,
                        "currency": "usd"
                      },
                      "vat": null,
                      "description": "ShipStation order SSG2M9S9",
                      "orders": [
                        {
                          "id": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "order",
                          "name": "SSG2M9S9"
                        }
                      ],
                      "invoice_url": null,
                      "created_at": "2026-10-12T14:03:55Z"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/TransactionList"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/transactions/summary": {
      "get": {
        "operationId": "get_transaction_summary",
        "x-required-scopes": [
          "wallet:read"
        ],
        "x-audience": [
          "designer",
          "integrator"
        ],
        "tags": [
          "Wallet"
        ],
        "summary": "Summarize transactions",
        "description": "Totals your completed transactions over a period, per category and direction. Use it for monthly reporting instead of paging through every transaction. Designers only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "date-time",
              "description": "Only transactions after this time. When neither created_after nor created_before is given, defaults to 30 days before the request. Any explicit range is honoured as given, with no maximum length.",
              "example": "2026-04-21T07:34:06Z"
            },
            "required": false,
            "description": "Only transactions after this time. When neither created_after nor created_before is given, defaults to 30 days before the request. Any explicit range is honoured as given, with no maximum length.",
            "name": "created_after",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time",
              "description": "Only transactions before this time. Giving it on its own turns off the 30 day default, so there is no lower bound.",
              "example": "2026-04-21T07:34:06Z"
            },
            "required": false,
            "description": "Only transactions before this time. Giving it on its own turns off the 30 day default, so there is no lower bound.",
            "name": "created_before",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "wallet",
                "card"
              ],
              "description": "Only wallet movements or only card charges."
            },
            "required": false,
            "description": "Only wallet movements or only card charges.",
            "name": "paid_with",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "The totals.",
            "content": {
              "application/json": {
                "example": {
                  "object": "transaction_summary",
                  "created_after": "2026-09-01T00:00:00Z",
                  "created_before": "2026-10-01T00:00:00Z",
                  "credits": {
                    "amount": 50000,
                    "currency": "usd"
                  },
                  "debits": {
                    "amount": 41230,
                    "currency": "usd"
                  },
                  "net": {
                    "amount": 8770,
                    "currency": "usd"
                  },
                  "by_category": [
                    {
                      "category": "top_up",
                      "direction": "credit",
                      "count": 2,
                      "total": {
                        "amount": 50000,
                        "currency": "usd"
                      }
                    },
                    {
                      "category": "order_payment",
                      "direction": "debit",
                      "count": 31,
                      "total": {
                        "amount": 29230,
                        "currency": "usd"
                      }
                    },
                    {
                      "category": "stock_purchase",
                      "direction": "debit",
                      "count": 2,
                      "total": {
                        "amount": 12000,
                        "currency": "usd"
                      }
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/TransactionSummary"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/billing-details": {
      "get": {
        "security": [
          {
            "session": []
          }
        ],
        "x-required-scopes": [],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Wallet"
        ],
        "operationId": "get_billing_details",
        "summary": "Get your billing details",
        "description": "Who your invoices are made out to. Integrators only, from a signed-in dashboard session: an API key cannot manage money (`credential_not_allowed`).",
        "responses": {
          "200": {
            "description": "Your billing details.",
            "content": {
              "application/json": {
                "example": {
                  "object": "billing_details",
                  "legal_name": "Acme Prints Ltd",
                  "country": "DE",
                  "address_line1": "Hauptstrasse 12",
                  "address_line2": null,
                  "city": "Berlin",
                  "postal_code": "10115",
                  "vat_number": "DE123456789",
                  "vat_status": "validated",
                  "required_tax_form": null,
                  "tax_form": null,
                  "email": "billing@acme.example",
                  "updated_at": "2026-10-21T09:30:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/BillingDetails"
                }
              }
            }
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have not added billing details yet (`billing_details_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "billing_details_not_found"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      },
      "put": {
        "security": [
          {
            "session": []
          }
        ],
        "x-required-scopes": [],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Wallet"
        ],
        "operationId": "save_billing_details",
        "summary": "Save your billing details",
        "description": "Replaces your billing details. Required before adding a card or topping up.\n\nAn EU VAT number is checked with VIES: a confirmed number means top-ups carry no VAT (reverse charge). Outside the EU, VAT follows your country, as for every account.\n\nIntegrators only, from a signed-in dashboard session: an API key cannot manage money (`credential_not_allowed`).",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "legal_name": "Acme Prints Ltd",
                "country": "DE",
                "address_line1": "Hauptstrasse 12",
                "city": "Berlin",
                "postal_code": "10115",
                "vat_number": "DE123456789"
              },
              "schema": {
                "$ref": "#/components/schemas/SaveBillingDetailsRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The saved details, with the VAT check result.",
            "content": {
              "application/json": {
                "example": {
                  "object": "billing_details",
                  "legal_name": "Acme Prints Ltd",
                  "country": "DE",
                  "address_line1": "Hauptstrasse 12",
                  "address_line2": null,
                  "city": "Berlin",
                  "postal_code": "10115",
                  "vat_number": "DE123456789",
                  "vat_status": "validated",
                  "required_tax_form": null,
                  "tax_form": null,
                  "email": "billing@acme.example",
                  "updated_at": "2026-10-21T09:30:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/BillingDetails"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid (`validation_failed`, `details.errors` per field): for example a W-9 `tin` without exactly 9 digits, `llc_tax_classification` or `other_classification` missing for that classification, `chapter4_status_other` missing when `chapter4_status` is `other`, or a `vat_number` from a US business or a business outside the US, EU and UK.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "vat_number"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "422": {
            "description": "A US business without `w9`, or a business outside the US, EU and UK without `w8ben_e` (`tax_form_required`, `details.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "tax_form_required"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/cards": {
      "get": {
        "security": [
          {
            "session": []
          }
        ],
        "x-required-scopes": [],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Wallet"
        ],
        "operationId": "list_cards",
        "summary": "List saved cards",
        "description": "Your saved cards, the default first. Integrators only, from a signed-in dashboard session: an API key cannot manage money (`credential_not_allowed`).",
        "responses": {
          "200": {
            "description": "Your cards, all on one page.",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "id": "crd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "card",
                      "brand": "visa",
                      "last4": "4242",
                      "exp_month": 12,
                      "exp_year": 2030,
                      "is_default": true,
                      "created_at": "2026-10-21T09:30:00Z"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/CardList"
                }
              }
            }
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/cards/setup-sessions": {
      "post": {
        "security": [
          {
            "session": []
          }
        ],
        "x-required-scopes": [],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Wallet"
        ],
        "operationId": "start_card_setup",
        "summary": "Start adding a card",
        "description": "Opens a Stripe page that saves a card. Stripe sends the browser back to the wallet page with `session_id`; call `complete_card_setup` with it. The new card becomes the default. Integrators only, from a signed-in dashboard session: an API key cannot manage money (`credential_not_allowed`).",
        "responses": {
          "201": {
            "description": "The Stripe page to open.",
            "content": {
              "application/json": {
                "example": {
                  "object": "checkout_session",
                  "session_id": "cs_test_a1b2c3",
                  "url": "https://checkout.stripe.com/c/pay/cs_test_a1b2c3"
                },
                "schema": {
                  "$ref": "#/components/schemas/CheckoutSession"
                }
              }
            }
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "409": {
            "description": "Add billing details first (`billing_details_required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "billing_details_required"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user), plus 60 per minute per API key (or signed-in user) for quote, upload, order and payment operations. Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          },
          "503": {
            "description": "Card payments are not available on this deployment (`card_payments_unavailable`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "card_payments_unavailable"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          },
          {
            "policy": "expensive_write",
            "limit": 60,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/cards/setup-sessions/{session_id}/complete": {
      "post": {
        "security": [
          {
            "session": []
          }
        ],
        "x-required-scopes": [],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Wallet"
        ],
        "operationId": "complete_card_setup",
        "summary": "Finish adding a card",
        "description": "Saves the card from a finished setup page right away, without waiting for Stripe's notification. Safe to call more than once. Integrators only, from a signed-in dashboard session: an API key cannot manage money (`credential_not_allowed`).",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "required": true,
            "name": "session_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Your cards, the new one included.",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "id": "crd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "card",
                      "brand": "visa",
                      "last4": "4242",
                      "exp_month": 12,
                      "exp_year": 2030,
                      "is_default": true,
                      "created_at": "2026-10-21T09:30:00Z"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/CardList"
                }
              }
            }
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "No card setup with this id was started from your account (`card_setup_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "card_setup_not_found"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          },
          "503": {
            "description": "Card payments are not available right now (`card_payments_unavailable`). Try again later.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "card_payments_unavailable"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/cards/{id}/make-default": {
      "post": {
        "security": [
          {
            "session": []
          }
        ],
        "x-required-scopes": [],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Wallet"
        ],
        "operationId": "make_card_default",
        "summary": "Make a card the default",
        "description": "The default card pays for top-ups and auto top up. Integrators only, from a signed-in dashboard session: an API key cannot manage money (`credential_not_allowed`).",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^crd_[0-9A-Za-z]+$",
              "description": "Unique identifier of the card, prefixed with `crd_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "crd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the card, prefixed with `crd_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The card, now the default.",
            "content": {
              "application/json": {
                "example": {
                  "id": "crd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "card",
                  "brand": "visa",
                  "last4": "4242",
                  "exp_month": 12,
                  "exp_year": 2030,
                  "is_default": true,
                  "created_at": "2026-10-21T09:30:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Card"
                }
              }
            }
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "No such card (`card_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "card_not_found"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/cards/{id}": {
      "delete": {
        "security": [
          {
            "session": []
          }
        ],
        "x-required-scopes": [],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Wallet"
        ],
        "operationId": "remove_card",
        "summary": "Remove a card",
        "description": "Removes the card from your wallet and from Stripe. A removed default hands the default to your newest other card. Integrators only, from a signed-in dashboard session: an API key cannot manage money (`credential_not_allowed`).",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^crd_[0-9A-Za-z]+$",
              "description": "Unique identifier of the card, prefixed with `crd_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "crd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the card, prefixed with `crd_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The card was removed.",
            "content": {
              "application/json": {
                "example": {
                  "id": "crd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "card",
                  "deleted": true
                },
                "schema": {
                  "$ref": "#/components/schemas/CardDeleted"
                }
              }
            }
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "No such card (`card_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "card_not_found"
            ]
          },
          "409": {
            "description": "It is your only card and auto top up is on (`auto_top_up_needs_card`). Turn auto top up off first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "auto_top_up_needs_card"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          },
          "503": {
            "description": "Card payments are not available right now (`card_payments_unavailable`). Try again later. Stripe refused to remove the card (`card_removal_failed`); try again in a moment.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "card_payments_unavailable",
              "card_removal_failed"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/top-ups": {
      "post": {
        "security": [
          {
            "session": []
          }
        ],
        "x-required-scopes": [],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Wallet"
        ],
        "operationId": "start_top_up",
        "summary": "Top up your wallet",
        "description": "Opens a Stripe page that charges a card for `amount`, plus VAT when your billing details call for it (EU and UK businesses without a confirmed VAT number). Only `amount` lands in your wallet, once Stripe confirms the payment; a Stripe invoice is issued for the full charge.\n\nYou need billing details and a saved card first.\n\nIntegrators only, from a signed-in dashboard session: an API key cannot manage money (`credential_not_allowed`).",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "amount": {
                  "amount": 5000,
                  "currency": "usd"
                }
              },
              "schema": {
                "$ref": "#/components/schemas/StartTopUpRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The Stripe page to open.",
            "content": {
              "application/json": {
                "example": {
                  "object": "checkout_session",
                  "session_id": "cs_test_a1b2c3",
                  "url": "https://checkout.stripe.com/c/pay/cs_test_a1b2c3"
                },
                "schema": {
                  "$ref": "#/components/schemas/CheckoutSession"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "409": {
            "description": "Add billing details first (`billing_details_required`), or add a card (`no_saved_card`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "billing_details_required",
              "no_saved_card"
            ]
          },
          "422": {
            "description": "The amount is outside $1.00 to $100,000.00 (`amount_out_of_range`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "amount_out_of_range"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user), plus 60 per minute per API key (or signed-in user) for quote, upload, order and payment operations. Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          },
          "503": {
            "description": "Card payments are not available right now (`card_payments_unavailable`). Try again later.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "card_payments_unavailable"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          },
          {
            "policy": "expensive_write",
            "limit": 60,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/wallet/settings": {
      "get": {
        "security": [
          {
            "session": []
          }
        ],
        "x-required-scopes": [],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Wallet"
        ],
        "operationId": "get_wallet_settings",
        "summary": "Get wallet settings",
        "description": "Auto top up. Integrators only, from a signed-in dashboard session: an API key cannot manage money (`credential_not_allowed`).",
        "responses": {
          "200": {
            "description": "Your settings.",
            "content": {
              "application/json": {
                "example": {
                  "object": "wallet_settings",
                  "auto_top_up": {
                    "enabled": false,
                    "amount": {
                      "amount": 5000,
                      "currency": "usd"
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/WalletSettings"
                }
              }
            }
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      },
      "patch": {
        "security": [
          {
            "session": []
          }
        ],
        "x-required-scopes": [],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Wallet"
        ],
        "operationId": "update_wallet_settings",
        "summary": "Update wallet settings",
        "description": "Turns auto top up on or off and sets its amount. It charges your default card when an order needs more than your balance. Integrators only, from a signed-in dashboard session: an API key cannot manage money (`credential_not_allowed`).",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "auto_top_up": {
                  "enabled": true,
                  "amount": {
                    "amount": 5000,
                    "currency": "usd"
                  }
                }
              },
              "schema": {
                "$ref": "#/components/schemas/UpdateWalletSettingsRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Your settings.",
            "content": {
              "application/json": {
                "example": {
                  "object": "wallet_settings",
                  "auto_top_up": {
                    "enabled": false,
                    "amount": {
                      "amount": 5000,
                      "currency": "usd"
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/WalletSettings"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "422": {
            "description": "The auto top up amount is outside $1.00 to $100,000.00 (`amount_out_of_range`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "amount_out_of_range"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/withdrawals": {
      "get": {
        "security": [
          {
            "session": []
          }
        ],
        "x-required-scopes": [],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Wallet"
        ],
        "operationId": "list_withdrawals",
        "summary": "List withdrawals",
        "description": "Your withdrawal requests, newest first. Integrators only, from a signed-in dashboard session: an API key cannot manage money (`credential_not_allowed`).",
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "description": "How many results per page, from 1 to 100.",
              "example": 20
            },
            "required": false,
            "description": "How many results per page, from 1 to 100.",
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Where to continue from: the `next_cursor` of the previous page. Omit for the first page."
            },
            "required": false,
            "description": "Where to continue from: the `next_cursor` of the previous page. Omit for the first page.",
            "name": "cursor",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of requests.",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "id": "wdr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "withdrawal",
                      "amount": {
                        "amount": 25000,
                        "currency": "usd"
                      },
                      "status": "pending",
                      "payment_link": null,
                      "invoice_url": "https://files.dapi.digital/invoices/acme-1021.pdf?sig=abc",
                      "note": null,
                      "processed_at": null,
                      "created_at": "2026-10-21T09:30:00Z"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/WithdrawalList"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      },
      "post": {
        "security": [
          {
            "session": []
          }
        ],
        "x-required-scopes": [],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Wallet"
        ],
        "operationId": "create_withdrawal",
        "summary": "Request a withdrawal",
        "description": "Asks 3DAPI to pay out part of your balance to your payout details. The amount leaves your balance now and is held until the payout is made; cancelling or a rejection puts it back.\n\nAttach an invoice in USD (an upload with purpose `withdrawal_invoice`) or a payment link. One request every 14 days.\n\nIntegrators only, from a signed-in dashboard session: an API key cannot manage money (`credential_not_allowed`).",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "amount": {
                  "amount": 25000,
                  "currency": "usd"
                },
                "invoice": {
                  "upload": "upl_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                }
              },
              "schema": {
                "$ref": "#/components/schemas/CreateWithdrawalRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The pending request.",
            "content": {
              "application/json": {
                "example": {
                  "id": "wdr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "withdrawal",
                  "amount": {
                    "amount": 25000,
                    "currency": "usd"
                  },
                  "status": "pending",
                  "payment_link": null,
                  "invoice_url": "https://files.dapi.digital/invoices/acme-1021.pdf?sig=abc",
                  "note": null,
                  "processed_at": null,
                  "created_at": "2026-10-21T09:30:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Withdrawal"
                }
              }
            }
          },
          "400": {
            "description": "Neither an invoice nor a payment link (`invoice_or_payment_link_required`), or the request is invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "invoice_or_payment_link_required"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "409": {
            "description": "A request is pending (`withdrawal_pending`), or the cooldown has not ended (`withdrawal_cooldown`, `details.next_available_at`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "withdrawal_pending",
              "withdrawal_cooldown"
            ]
          },
          "422": {
            "description": "The amount is more than your balance (`insufficient_balance`), or the upload is unknown, expired or of another purpose.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "insufficient_balance"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user), plus 60 per minute per API key (or signed-in user) for quote, upload, order and payment operations. Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          },
          {
            "policy": "expensive_write",
            "limit": 60,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/withdrawals/eligibility": {
      "get": {
        "security": [
          {
            "session": []
          }
        ],
        "x-required-scopes": [],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Wallet"
        ],
        "operationId": "get_withdrawal_eligibility",
        "summary": "Check whether you can withdraw",
        "description": "One withdrawal every 14 days, counted from your last paid-out request; a pending request holds the slot. Integrators only, from a signed-in dashboard session: an API key cannot manage money (`credential_not_allowed`).",
        "responses": {
          "200": {
            "description": "Whether you can withdraw now.",
            "content": {
              "application/json": {
                "example": {
                  "object": "withdrawal_eligibility",
                  "can_withdraw_now": true,
                  "cooldown_days": 14,
                  "has_pending": false,
                  "next_available_at": null
                },
                "schema": {
                  "$ref": "#/components/schemas/WithdrawalEligibility"
                }
              }
            }
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/withdrawals/{id}/cancel": {
      "post": {
        "security": [
          {
            "session": []
          }
        ],
        "x-required-scopes": [],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Wallet"
        ],
        "operationId": "cancel_withdrawal",
        "summary": "Cancel a withdrawal",
        "description": "Cancels a pending request; its amount returns to your balance. Integrators only, from a signed-in dashboard session: an API key cannot manage money (`credential_not_allowed`).",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^wdr_[0-9A-Za-z]+$",
              "description": "Unique identifier of the withdrawal, prefixed with `wdr_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "wdr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the withdrawal, prefixed with `wdr_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The cancelled request.",
            "content": {
              "application/json": {
                "example": {
                  "id": "wdr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "withdrawal",
                  "amount": {
                    "amount": 25000,
                    "currency": "usd"
                  },
                  "status": "cancelled",
                  "payment_link": null,
                  "invoice_url": "https://files.dapi.digital/invoices/acme-1021.pdf?sig=abc",
                  "note": null,
                  "processed_at": "2026-10-21T10:00:00Z",
                  "created_at": "2026-10-21T09:30:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Withdrawal"
                }
              }
            }
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "No such request (`withdrawal_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "withdrawal_not_found"
            ]
          },
          "409": {
            "description": "It is no longer pending (`withdrawal_not_pending`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "withdrawal_not_pending"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/payout-details": {
      "get": {
        "security": [
          {
            "session": []
          }
        ],
        "x-required-scopes": [],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Wallet"
        ],
        "operationId": "get_payout_details",
        "summary": "Get payout details",
        "description": "The bank account withdrawals are paid to. Integrators only, from a signed-in dashboard session: an API key cannot manage money (`credential_not_allowed`).",
        "responses": {
          "200": {
            "description": "Your payout details.",
            "content": {
              "application/json": {
                "example": {
                  "object": "payout_details",
                  "bank_name": "Deutsche Bank",
                  "account_holder_name": "Acme Prints Ltd",
                  "account_number_last4": "3000",
                  "routing_number": "COBADEFFXXX",
                  "updated_at": "2026-10-21T09:30:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/PayoutDetails"
                }
              }
            }
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have not added payout details yet (`payout_details_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "payout_details_not_found"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      },
      "put": {
        "security": [
          {
            "session": []
          }
        ],
        "x-required-scopes": [],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Wallet"
        ],
        "operationId": "save_payout_details",
        "summary": "Save payout details",
        "description": "Replaces the bank account withdrawals are paid to. Bank transfer is the only payout method. Integrators only, from a signed-in dashboard session: an API key cannot manage money (`credential_not_allowed`).",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "bank_name": "Deutsche Bank",
                "account_holder_name": "Acme Prints Ltd",
                "account_number": "DE89370400440532013000",
                "routing_number": "COBADEFFXXX"
              },
              "schema": {
                "$ref": "#/components/schemas/SavePayoutDetailsRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The saved details.",
            "content": {
              "application/json": {
                "example": {
                  "object": "payout_details",
                  "bank_name": "Deutsche Bank",
                  "account_holder_name": "Acme Prints Ltd",
                  "account_number_last4": "3000",
                  "routing_number": "COBADEFFXXX",
                  "updated_at": "2026-10-21T09:30:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/PayoutDetails"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/events": {
      "get": {
        "operationId": "list_events",
        "x-required-scopes": [
          "webhooks:read"
        ],
        "x-audience": [
          "designer",
          "integrator"
        ],
        "tags": [
          "Webhooks"
        ],
        "summary": "List events",
        "description": "Lists the events of the last 30 days, newest first, whether or not they were delivered. Use it to catch up after downtime or to reconcile what your endpoint received. Integrators see their own event types only (orders, checkouts, quotes and onboarding); `get_event_deliveries` shows where each event was sent.",
        "parameters": [
          {
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/EventType"
              },
              "description": "Only these event types. Repeat for several."
            },
            "required": false,
            "description": "Only these event types. Repeat for several.",
            "style": "form",
            "explode": true,
            "name": "type",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Only events about this object, by its id, for example an `ord_` id."
            },
            "required": false,
            "description": "Only events about this object, by its id, for example an `ord_` id.",
            "name": "object",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time",
              "description": "Only events after this time.",
              "example": "2026-04-21T07:34:06Z"
            },
            "required": false,
            "description": "Only events after this time.",
            "name": "created_after",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time",
              "description": "Only events before this time.",
              "example": "2026-04-21T07:34:06Z"
            },
            "required": false,
            "description": "Only events before this time.",
            "name": "created_before",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "description": "How many results per page, from 1 to 100.",
              "example": 20
            },
            "required": false,
            "description": "How many results per page, from 1 to 100.",
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Where to continue from: the `next_cursor` of the previous page. Omit for the first page."
            },
            "required": false,
            "description": "Where to continue from: the `next_cursor` of the previous page. Omit for the first page.",
            "name": "cursor",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of events.",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "id": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "event",
                      "type": "order.shipped",
                      "created_at": "2026-10-14T10:02:00Z",
                      "data": {
                        "object": {
                          "id": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "order",
                          "number": "PV-SCN-1701A372",
                          "channel": "storefront",
                          "status": "shipped",
                          "customer": {
                            "name": "Ana Popescu",
                            "email": "ana@example.com",
                            "phone": null,
                            "company": null
                          },
                          "shipping_address": {
                            "formatted": "Str. Test 1, Cluj, RO",
                            "city": "Cluj",
                            "country": "Romania",
                            "country_code": "RO"
                          },
                          "charges": {
                            "paid_by": "buyer",
                            "items": {
                              "amount": 1800,
                              "currency": "usd"
                            },
                            "shipping": {
                              "amount": 600,
                              "currency": "usd"
                            },
                            "vat": {
                              "amount": {
                                "amount": 0,
                                "currency": "usd"
                              },
                              "rate": 0,
                              "included": false,
                              "country": null
                            },
                            "sample_credit": null,
                            "total": {
                              "amount": 2400,
                              "currency": "usd"
                            }
                          },
                          "earnings": {
                            "manufacturing": {
                              "amount": 900,
                              "currency": "usd"
                            },
                            "platform_fee": {
                              "amount": 300,
                              "currency": "usd"
                            },
                            "payment_processing": {
                              "amount": 102,
                              "currency": "usd"
                            },
                            "refunded": {
                              "amount": 0,
                              "currency": "usd"
                            },
                            "net": {
                              "amount": 498,
                              "currency": "usd"
                            }
                          },
                          "shipping": {
                            "tier": "small",
                            "carrier": null,
                            "tracking_number": null,
                            "shipped_at": null
                          },
                          "external": null,
                          "customer_notes": null,
                          "qc": {
                            "open_tickets": 1,
                            "blocks_shipping": true
                          },
                          "created_at": "2026-10-09T08:39:29Z",
                          "updated_at": "2026-10-09T08:40:02Z",
                          "item_count": 1,
                          "items": [
                            {
                              "id": "itm_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                              "object": "order_item",
                              "product": {
                                "id": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                                "object": "product",
                                "name": "Clicker"
                              },
                              "variant": {
                                "id": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                                "object": "variant",
                                "name": "Clicker Blue"
                              },
                              "sku": "CLICK-BLUE",
                              "quantity": 1,
                              "unit_price": {
                                "amount": 1800,
                                "currency": "usd"
                              },
                              "total": {
                                "amount": 1800,
                                "currency": "usd"
                              },
                              "stock_used": [],
                              "fulfilled_from_stock": false
                            }
                          ],
                          "refunds": []
                        },
                        "previous": {
                          "status": "paid"
                        },
                        "region": null
                      }
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/EventList"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/events/{id}": {
      "get": {
        "operationId": "get_event",
        "x-required-scopes": [
          "webhooks:read"
        ],
        "x-audience": [
          "designer",
          "integrator"
        ],
        "tags": [
          "Webhooks"
        ],
        "summary": "Get an event",
        "description": "Returns one event from the last 30 days, as it was sent. Integrators read their own event types only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^evt_[0-9A-Za-z]+$",
              "description": "Unique identifier of the event, prefixed with `evt_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the event, prefixed with `evt_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The event.",
            "content": {
              "application/json": {
                "example": {
                  "id": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "event",
                  "type": "order.shipped",
                  "created_at": "2026-10-14T10:02:00Z",
                  "data": {
                    "object": {
                      "id": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "order",
                      "number": "PV-SCN-1701A372",
                      "channel": "storefront",
                      "status": "shipped",
                      "customer": {
                        "name": "Ana Popescu",
                        "email": "ana@example.com",
                        "phone": null,
                        "company": null
                      },
                      "shipping_address": {
                        "formatted": "Str. Test 1, Cluj, RO",
                        "city": "Cluj",
                        "country": "Romania",
                        "country_code": "RO"
                      },
                      "charges": {
                        "paid_by": "buyer",
                        "items": {
                          "amount": 1800,
                          "currency": "usd"
                        },
                        "shipping": {
                          "amount": 600,
                          "currency": "usd"
                        },
                        "vat": {
                          "amount": {
                            "amount": 0,
                            "currency": "usd"
                          },
                          "rate": 0,
                          "included": false,
                          "country": null
                        },
                        "sample_credit": null,
                        "total": {
                          "amount": 2400,
                          "currency": "usd"
                        }
                      },
                      "earnings": {
                        "manufacturing": {
                          "amount": 900,
                          "currency": "usd"
                        },
                        "platform_fee": {
                          "amount": 300,
                          "currency": "usd"
                        },
                        "payment_processing": {
                          "amount": 102,
                          "currency": "usd"
                        },
                        "refunded": {
                          "amount": 0,
                          "currency": "usd"
                        },
                        "net": {
                          "amount": 498,
                          "currency": "usd"
                        }
                      },
                      "shipping": {
                        "tier": "small",
                        "carrier": null,
                        "tracking_number": null,
                        "shipped_at": null
                      },
                      "external": null,
                      "customer_notes": null,
                      "qc": {
                        "open_tickets": 1,
                        "blocks_shipping": true
                      },
                      "created_at": "2026-10-09T08:39:29Z",
                      "updated_at": "2026-10-09T08:40:02Z",
                      "item_count": 1,
                      "items": [
                        {
                          "id": "itm_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "order_item",
                          "product": {
                            "id": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                            "object": "product",
                            "name": "Clicker"
                          },
                          "variant": {
                            "id": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                            "object": "variant",
                            "name": "Clicker Blue"
                          },
                          "sku": "CLICK-BLUE",
                          "quantity": 1,
                          "unit_price": {
                            "amount": 1800,
                            "currency": "usd"
                          },
                          "total": {
                            "amount": 1800,
                            "currency": "usd"
                          },
                          "stock_used": [],
                          "fulfilled_from_stock": false
                        }
                      ],
                      "refunds": []
                    },
                    "previous": {
                      "status": "paid"
                    },
                    "region": null
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Event"
                }
              }
            }
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "No event with this id in the last 30 days (`event_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "event_not_found"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/webhook-endpoints": {
      "get": {
        "operationId": "list_webhook_endpoints",
        "x-required-scopes": [
          "webhooks:read"
        ],
        "x-audience": [
          "designer",
          "integrator"
        ],
        "tags": [
          "Webhooks"
        ],
        "summary": "List webhook endpoints",
        "description": "Lists your webhook endpoints, the events each receives and how its last delivery went. Designers create and edit endpoints in the dashboard; integrators in the dashboard too, through the session-only endpoint operations.",
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "description": "How many results per page, from 1 to 100.",
              "example": 20
            },
            "required": false,
            "description": "How many results per page, from 1 to 100.",
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Where to continue from: the `next_cursor` of the previous page. Omit for the first page."
            },
            "required": false,
            "description": "Where to continue from: the `next_cursor` of the previous page. Omit for the first page.",
            "name": "cursor",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of endpoints.",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "id": "whe_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "webhook_endpoint",
                      "url": "https://example.com/webhooks/dapi",
                      "events": [
                        "order.shipped",
                        "order.completed",
                        "qc_ticket.needs_your_action"
                      ],
                      "payload_version": "v2",
                      "status": "enabled",
                      "last_delivery": {
                        "succeeded": true,
                        "status_code": 200,
                        "at": "2026-10-14T10:02:01Z"
                      },
                      "created_at": "2026-10-01T12:00:00Z"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpointList"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      },
      "post": {
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "create_webhook_endpoint",
        "x-required-scopes": [],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Webhooks"
        ],
        "summary": "Create a webhook endpoint",
        "description": "Adds a URL that receives the events it subscribes to, signed with its own secret. The response is the only time the secret is shown.\n\nYou can have up to 10 endpoints, enabled or disabled, each with its own events.\n\nIntegrators only, and only with a signed-in dashboard session (`credential_not_allowed` with an API key).",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "url": "https://example.com/webhooks/dapi",
                "events": [
                  "quote.ready",
                  "quote.failed",
                  "order.shipped"
                ]
              },
              "schema": {
                "$ref": "#/components/schemas/CreateWebhookEndpointRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The endpoint, with its secret shown once.",
            "content": {
              "application/json": {
                "example": {
                  "id": "whe_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "webhook_endpoint",
                  "url": "https://example.com/webhooks/dapi",
                  "events": [
                    "quote.ready",
                    "quote.failed",
                    "order.shipped"
                  ],
                  "payload_version": "v2",
                  "status": "enabled",
                  "last_delivery": null,
                  "created_at": "2026-10-01T12:00:00Z",
                  "secret": "whsec_3f1c9a2b7d4e5f60718293a4b5c6d7e8f9012a3b4c5d6e7f8091a2b3c4d5e6f7"
                },
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpointWithSecret"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid, for example an event type integrators cannot receive (`validation_failed`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "409": {
            "description": "You already have 10 endpoints (`webhook_endpoint_limit_reached`). Delete one first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "webhook_endpoint_limit_reached"
            ]
          },
          "422": {
            "description": "The URL is not a public https address (`webhook_url_not_allowed`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "webhook_url_not_allowed"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/webhook-endpoints/{id}": {
      "patch": {
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "update_webhook_endpoint",
        "x-required-scopes": [],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Webhooks"
        ],
        "summary": "Update a webhook endpoint",
        "description": "Changes the URL, the events or whether the endpoint is enabled; fields you leave out stay as they are. The secret does not change. Integrators only, and only with a signed-in dashboard session (`credential_not_allowed` with an API key).",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^whe_[0-9A-Za-z]+$",
              "description": "Unique identifier of the webhook endpoint, prefixed with `whe_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "whe_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the webhook endpoint, prefixed with `whe_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "enabled": false
              },
              "schema": {
                "$ref": "#/components/schemas/UpdateWebhookEndpointRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The endpoint.",
            "content": {
              "application/json": {
                "example": {
                  "id": "whe_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "webhook_endpoint",
                  "url": "https://example.com/webhooks/dapi",
                  "events": [
                    "order.shipped",
                    "order.completed",
                    "qc_ticket.needs_your_action"
                  ],
                  "payload_version": "v2",
                  "status": "disabled",
                  "last_delivery": {
                    "succeeded": true,
                    "status_code": 200,
                    "at": "2026-10-14T10:02:01Z"
                  },
                  "created_at": "2026-10-01T12:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpoint"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have no dashboard endpoint with this id (`webhook_endpoint_not_found`). The previous API endpoint (`payload_version: v1`) is managed there.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "webhook_endpoint_not_found"
            ]
          },
          "422": {
            "description": "The URL is not a public https address (`webhook_url_not_allowed`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "webhook_url_not_allowed"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      },
      "delete": {
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "delete_webhook_endpoint",
        "x-required-scopes": [],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Webhooks"
        ],
        "summary": "Delete a webhook endpoint",
        "description": "Deletes the endpoint and its pending deliveries. Events stay in the events log. This cannot be undone. Integrators only, and only with a signed-in dashboard session (`credential_not_allowed` with an API key).",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^whe_[0-9A-Za-z]+$",
              "description": "Unique identifier of the webhook endpoint, prefixed with `whe_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "whe_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the webhook endpoint, prefixed with `whe_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The endpoint was deleted.",
            "content": {
              "application/json": {
                "example": {
                  "id": "whe_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "webhook_endpoint",
                  "deleted": true
                },
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpointDeleted"
                }
              }
            }
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have no dashboard endpoint with this id (`webhook_endpoint_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "webhook_endpoint_not_found"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/webhook-endpoints/{id}/rotate-secret": {
      "post": {
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "rotate_webhook_endpoint_secret",
        "x-required-scopes": [],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Webhooks"
        ],
        "summary": "Rotate an endpoint secret",
        "description": "Replaces the signing secret at once and returns the new one, shown only here. Deliveries from now on, retries included, are signed with it. Integrators only, and only with a signed-in dashboard session (`credential_not_allowed` with an API key).",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^whe_[0-9A-Za-z]+$",
              "description": "Unique identifier of the webhook endpoint, prefixed with `whe_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "whe_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the webhook endpoint, prefixed with `whe_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The endpoint, with its new secret shown once.",
            "content": {
              "application/json": {
                "example": {
                  "id": "whe_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "webhook_endpoint",
                  "url": "https://example.com/webhooks/dapi",
                  "events": [
                    "order.shipped",
                    "order.completed",
                    "qc_ticket.needs_your_action"
                  ],
                  "payload_version": "v2",
                  "status": "enabled",
                  "last_delivery": {
                    "succeeded": true,
                    "status_code": 200,
                    "at": "2026-10-14T10:02:01Z"
                  },
                  "created_at": "2026-10-01T12:00:00Z",
                  "secret": "whsec_9a8b7c6d5e4f30211203f4e5d6c7b8a99a8b7c6d5e4f30211203f4e5d6c7b8a9"
                },
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpointWithSecret"
                }
              }
            }
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have no dashboard endpoint with this id (`webhook_endpoint_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "webhook_endpoint_not_found"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/webhook-endpoints/{id}/test": {
      "post": {
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "send_test_webhook_event",
        "x-required-scopes": [],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Webhooks"
        ],
        "summary": "Send a test event",
        "description": "Posts a signed `webhook.test` event to the endpoint now, so you can check that it is reachable and verifies signatures. The body is an event envelope whose `data.object` names the endpoint (`{ id, object }`); its id starts with `evt_test` and it is not kept in the events log, sent again or retried. Disabled endpoints can be tested too.\n\nOnly success or failure is reported.\n\nIntegrators only, and only with a signed-in dashboard session (`credential_not_allowed` with an API key).",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^whe_[0-9A-Za-z]+$",
              "description": "Unique identifier of the webhook endpoint, prefixed with `whe_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "whe_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the webhook endpoint, prefixed with `whe_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "How the test went.",
            "content": {
              "application/json": {
                "example": {
                  "object": "webhook_test",
                  "ok": true,
                  "message": "Your endpoint acknowledged the test."
                },
                "schema": {
                  "$ref": "#/components/schemas/WebhookTestResult"
                }
              }
            }
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have no dashboard endpoint with this id (`webhook_endpoint_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "webhook_endpoint_not_found"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/webhook-endpoints/{id}/deliveries": {
      "get": {
        "operationId": "list_webhook_endpoint_deliveries",
        "x-required-scopes": [
          "webhooks:read"
        ],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Webhooks"
        ],
        "summary": "List deliveries to an endpoint",
        "description": "Lists what was sent to one endpoint, newest first, with every try and your endpoint's answers. Deliveries are kept 30 days. Integrators only; readable with a session or a key with `webhooks:read`.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^whe_[0-9A-Za-z]+$",
              "description": "Unique identifier of the webhook endpoint, prefixed with `whe_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "whe_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the webhook endpoint, prefixed with `whe_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "description": "How many results per page, from 1 to 100.",
              "example": 20
            },
            "required": false,
            "description": "How many results per page, from 1 to 100.",
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Where to continue from: the `next_cursor` of the previous page. Omit for the first page."
            },
            "required": false,
            "description": "Where to continue from: the `next_cursor` of the previous page. Omit for the first page.",
            "name": "cursor",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of deliveries.",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "id": "whd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "webhook_delivery",
                      "endpoint": {
                        "id": "whe_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "webhook_endpoint",
                        "url": "https://example.com/webhooks/dapi"
                      },
                      "event": {
                        "id": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "event",
                        "type": "quote.ready"
                      },
                      "status": "succeeded",
                      "skipped_reason": null,
                      "next_attempt_at": null,
                      "attempts": [
                        {
                          "attempt": 1,
                          "started_at": "2026-10-22T09:13:42Z",
                          "duration_ms": 184,
                          "status_code": 200,
                          "succeeded": true,
                          "error": null,
                          "response_body": "ok"
                        }
                      ],
                      "created_at": "2026-10-22T09:13:42Z"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/WebhookDeliveryList"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "You have no dashboard endpoint with this id (`webhook_endpoint_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "webhook_endpoint_not_found"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/events/{id}/deliveries": {
      "get": {
        "operationId": "get_event_deliveries",
        "x-required-scopes": [
          "webhooks:read"
        ],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Webhooks"
        ],
        "summary": "Get where an event was sent",
        "description": "Returns the deliveries of one event to your endpoints, with every try, and the API request that caused it. Use it to see why an endpoint did not receive an event. Integrators only; readable with a session or a key with `webhooks:read`.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^evt_[0-9A-Za-z]+$",
              "description": "Unique identifier of the event, prefixed with `evt_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the event, prefixed with `evt_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The event, its cause and its deliveries.",
            "content": {
              "application/json": {
                "example": {
                  "object": "event_deliveries",
                  "event": {
                    "id": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "event",
                    "type": "quote.ready",
                    "created_at": "2026-10-22T09:13:41Z"
                  },
                  "request": null,
                  "deliveries": [
                    {
                      "id": "whd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "webhook_delivery",
                      "endpoint": {
                        "id": "whe_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "webhook_endpoint",
                        "url": "https://example.com/webhooks/dapi"
                      },
                      "event": {
                        "id": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "event",
                        "type": "quote.ready"
                      },
                      "status": "succeeded",
                      "skipped_reason": null,
                      "next_attempt_at": null,
                      "attempts": [
                        {
                          "attempt": 1,
                          "started_at": "2026-10-22T09:13:42Z",
                          "duration_ms": 184,
                          "status_code": 200,
                          "succeeded": true,
                          "error": null,
                          "response_body": "ok"
                        }
                      ],
                      "created_at": "2026-10-22T09:13:42Z"
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/EventDeliveries"
                }
              }
            }
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "No event with this id in the last 30 days (`event_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "event_not_found"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/events/{id}/resend": {
      "post": {
        "security": [
          {
            "session": []
          }
        ],
        "operationId": "resend_event",
        "x-required-scopes": [],
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Webhooks"
        ],
        "summary": "Send an event again",
        "description": "Sends an event again to one of your endpoints, as a new delivery retried for 24 hours like any other. The body is the event as first sent, with the same event id, so a receiver that deduplicates on `X-Dapi-Event-Id` ignores it if it already processed the event. It is signed with the current secret.\n\nThe endpoint must be enabled and subscribed to the event type.\n\nIntegrators only, and only with a signed-in dashboard session (`credential_not_allowed` with an API key).",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^evt_[0-9A-Za-z]+$",
              "description": "Unique identifier of the event, prefixed with `evt_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the event, prefixed with `evt_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "endpoint": "whe_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
              },
              "schema": {
                "$ref": "#/components/schemas/ResendEventRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The new delivery, after its first try.",
            "content": {
              "application/json": {
                "example": {
                  "id": "whd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "webhook_delivery",
                  "endpoint": {
                    "id": "whe_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "webhook_endpoint",
                    "url": "https://example.com/webhooks/dapi"
                  },
                  "event": {
                    "id": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "event",
                    "type": "quote.ready"
                  },
                  "status": "succeeded",
                  "skipped_reason": null,
                  "next_attempt_at": null,
                  "attempts": [
                    {
                      "attempt": 1,
                      "started_at": "2026-10-22T09:13:42Z",
                      "duration_ms": 184,
                      "status_code": 200,
                      "succeeded": true,
                      "error": null,
                      "response_body": "ok"
                    }
                  ],
                  "created_at": "2026-10-22T09:13:42Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/WebhookDelivery"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_scope",
              "actor_not_allowed",
              "credential_not_allowed",
              "account_blacklisted"
            ]
          },
          "404": {
            "description": "No such event in the last 30 days (`event_not_found`) or no such endpoint (`webhook_endpoint_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "event_not_found",
              "webhook_endpoint_not_found"
            ]
          },
          "409": {
            "description": "The endpoint is disabled (`webhook_endpoint_disabled`) or does not receive this event type (`event_type_not_subscribed`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "webhook_endpoint_disabled",
              "event_type_not_subscribed"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/uploads": {
      "post": {
        "operationId": "create_upload",
        "x-audience": [
          "designer",
          "integrator"
        ],
        "x-required-scopes": [
          "products:write",
          "extras:write",
          "stocks:write",
          "qc:write",
          "quotes:write"
        ],
        "tags": [
          "Uploads"
        ],
        "summary": "Start a file upload",
        "description": "Reserves a slot for one file and returns where to send it. Files go straight to storage, never through the API: (1) call this, (2) PUT the bytes to `upload_url` with the given headers, (3) reference the returned id in the write that uses the file. A type, file name extension (missing, not accepted for the purpose, or not matching `content_type`) or size the purpose does not allow is refused here, before anything is uploaded. The contents are checked when a write uses the upload (422 `invalid_file`). Designers may use every purpose except `quote_file` and `withdrawal_invoice`; integrators may use `quote_file` and `withdrawal_invoice` (with the `quotes:write` scope) and `extra_image` (with the `extras:write` scope).",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Any unique string, for example a UUID. Sending the same request again with the same key within 24 hours returns the first response without doing anything twice. Reusing a key with a different body is refused with `idempotency_key_reused`. A refused request (4xx) frees the key so you can correct and resend it; a server error (5xx) is kept and returned again, because the write may already have happened: check the resource before retrying with a new key."
            },
            "required": false,
            "description": "Any unique string, for example a UUID. Sending the same request again with the same key within 24 hours returns the first response without doing anything twice. Reusing a key with a different body is refused with `idempotency_key_reused`. A refused request (4xx) frees the key so you can correct and resend it; a server error (5xx) is kept and returned again, because the write may already have happened: check the resource before retrying with a new key.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "design_file": {
                  "summary": "A design file, for a designer",
                  "value": {
                    "purpose": "design_file",
                    "file_name": "tracker.3mf",
                    "content_type": "model/3mf",
                    "size_bytes": 2758723
                  }
                },
                "quote_file": {
                  "summary": "A model to quote, for an integrator",
                  "value": {
                    "purpose": "quote_file",
                    "file_name": "phone-stand.stl",
                    "content_type": "model/stl",
                    "size_bytes": 1834221
                  }
                }
              },
              "schema": {
                "$ref": "#/components/schemas/CreateUploadRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The upload slot.",
            "content": {
              "application/json": {
                "example": {
                  "id": "upl_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "upload",
                  "purpose": "extra_image",
                  "upload_url": "https://files.example.com/temp/7f3a.png?signature=abc",
                  "method": "PUT",
                  "headers": {
                    "Content-Type": "image/png"
                  },
                  "expires_at": "2026-10-15T10:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Upload"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "401": {
            "description": "Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "missing_credentials",
              "invalid_api_key",
              "invalid_session",
              "ambiguous_credentials"
            ]
          },
          "403": {
            "description": "The purpose is not available to your kind of account (`purpose_not_allowed`), a scope is missing (`missing_scope`), or the purpose needs a signed-in dashboard session (`credential_not_allowed`: `withdrawal_invoice`, like withdrawals themselves).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "purpose_not_allowed",
              "missing_scope",
              "credential_not_allowed",
              "withdrawal_invoice"
            ]
          },
          "422": {
            "description": "The type, extension or size is not allowed for the purpose (`file_type_not_allowed`, `file_too_large`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "file_type_not_allowed",
              "file_too_large"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user), plus 60 per minute per API key (or signed-in user) for quote, upload, order and payment operations. Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "per_credential",
            "limit": 500,
            "window_seconds": 60,
            "per": "api_key_or_user"
          },
          {
            "policy": "expensive_write",
            "limit": 60,
            "window_seconds": 60,
            "per": "api_key_or_user"
          }
        ]
      }
    },
    "/v2/shipping/rates": {
      "get": {
        "operationId": "get_shipping_rates",
        "x-required-scopes": [],
        "x-audience": [
          "designer",
          "integrator"
        ],
        "tags": [
          "Shipping"
        ],
        "summary": "Get the shipping rate card",
        "description": "Returns every shipping zone with its prices per tier. Public: no credentials needed. An order ships for the `base_price` of the tier of its heaviest single item (never the summed weight), plus `per_additional_item` for every other item. Items under `extra_small_max_weight_grams` ride along without the additional charge, unless they come with extra parts. Example: three small items and one medium item to EU Core cost the medium base price plus three additional items. When a zone has no price of its own for a tier, checkout charges that tier at the zone medium price (per additional item charge included), or, when the zone prices no medium tier either, a flat default for the whole parcel with no per additional item charge. The prices here already show that fallback, so they are always what checkout charges. For a single price, use the shipping quote instead.",
        "security": [],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^[A-Z]{2}$",
              "description": "Only return the zone that ships to this country.",
              "example": "DE"
            },
            "required": false,
            "description": "Only return the zone that ships to this country.",
            "name": "country",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "The rate card.",
            "content": {
              "application/json": {
                "example": {
                  "object": "shipping_rate_card",
                  "currency": "usd",
                  "tiers": [
                    {
                      "tier": "small",
                      "max_weight_grams": 600
                    },
                    {
                      "tier": "medium",
                      "max_weight_grams": 2000
                    },
                    {
                      "tier": "large",
                      "max_weight_grams": null
                    }
                  ],
                  "extra_small_max_weight_grams": 100,
                  "zones": [
                    {
                      "code": "eu-core",
                      "object": "shipping_zone",
                      "name": "EU Core",
                      "countries": [
                        "DE",
                        "NL",
                        "BE",
                        "PL",
                        "RO"
                      ],
                      "rest_of_world": false,
                      "base_price": {
                        "small": {
                          "amount": 599,
                          "currency": "usd"
                        },
                        "medium": {
                          "amount": 799,
                          "currency": "usd"
                        },
                        "large": {
                          "amount": 1299,
                          "currency": "usd"
                        }
                      },
                      "per_additional_item": {
                        "amount": 100,
                        "currency": "usd"
                      }
                    },
                    {
                      "code": "rest-of-world",
                      "object": "shipping_zone",
                      "name": "Rest of World",
                      "countries": [],
                      "rest_of_world": true,
                      "base_price": {
                        "small": {
                          "amount": 1599,
                          "currency": "usd"
                        },
                        "medium": {
                          "amount": 1999,
                          "currency": "usd"
                        },
                        "large": {
                          "amount": 2999,
                          "currency": "usd"
                        }
                      },
                      "per_additional_item": {
                        "amount": 100,
                        "currency": "usd"
                      }
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/ShippingRateCard"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 120 per minute per IP address. Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "public_read",
            "limit": 120,
            "window_seconds": 60,
            "per": "ip"
          }
        ]
      }
    },
    "/v2/shipping/quote": {
      "get": {
        "operationId": "get_shipping_quote",
        "x-required-scopes": [],
        "x-audience": [
          "designer",
          "integrator"
        ],
        "tags": [
          "Shipping"
        ],
        "summary": "Get a shipping price",
        "description": "Returns what one parcel costs to ship to a country, given the tier of its heaviest item and how many other items travel with it. Public: no credentials needed. Computed exactly as checkout does, including its fallback for a tier the zone does not price (the zone medium price, else a flat default for the whole parcel with `per_additional_item` 0). Every real country is served (the rest-of-world zone covers countries no other zone lists); a code that is not a country is a 400.",
        "security": [],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^[A-Z]{2}$",
              "description": "Destination country.",
              "example": "DE"
            },
            "required": true,
            "description": "Destination country.",
            "name": "country",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "small",
                "medium",
                "large"
              ],
              "description": "Tier of the heaviest item in the parcel. There is no `extra_small` tier: an extra-small item is quoted as `small`."
            },
            "required": true,
            "description": "Tier of the heaviest item in the parcel. There is no `extra_small` tier: an extra-small item is quoted as `small`.",
            "name": "tier",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ],
              "minimum": 0,
              "maximum": 1000,
              "default": 0,
              "description": "How many other items ship in the same parcel, extra-small items excluded. 0 for a single item.",
              "example": 3
            },
            "required": false,
            "description": "How many other items ship in the same parcel, extra-small items excluded. 0 for a single item.",
            "name": "additional_items",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "The price.",
            "content": {
              "application/json": {
                "example": {
                  "object": "shipping_quote",
                  "zone": {
                    "code": "eu-core",
                    "name": "EU Core"
                  },
                  "country_code": "DE",
                  "tier": "medium",
                  "base_price": {
                    "amount": 799,
                    "currency": "usd"
                  },
                  "additional_items": 3,
                  "per_additional_item": {
                    "amount": 100,
                    "currency": "usd"
                  },
                  "total": {
                    "amount": 1099,
                    "currency": "usd"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ShippingQuote"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "404": {
            "description": "No active zone serves the country (`shipping_zone_not_found`). Only possible if 3DAPI has switched off the rest-of-world zone.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "shipping_zone_not_found"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 120 per minute per IP address. Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "public_read",
            "limit": 120,
            "window_seconds": 60,
            "per": "ip"
          }
        ]
      }
    },
    "/v2/materials": {
      "get": {
        "operationId": "list_materials",
        "x-required-scopes": [],
        "x-audience": [
          "designer",
          "integrator"
        ],
        "tags": [
          "Materials"
        ],
        "summary": "List catalog materials",
        "description": "Lists every material in the 3DAPI catalog, the materials a design can be printed in. Public: no credentials needed. Use the ids when configuring which materials a design allows.",
        "security": [],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "description": "How many results per page, from 1 to 100.",
              "example": 20
            },
            "required": false,
            "description": "How many results per page, from 1 to 100.",
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Where to continue from: the `next_cursor` of the previous page. Omit for the first page."
            },
            "required": false,
            "description": "Where to continue from: the `next_cursor` of the previous page. Omit for the first page.",
            "name": "cursor",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of catalog materials.",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "id": "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "material",
                      "name": "PLA Black",
                      "custom": false,
                      "technology": "fdm",
                      "color": "Black",
                      "color_hex": "#000000",
                      "filament_type": "pla"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/MaterialList"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 120 per minute per IP address. Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "public_read",
            "limit": 120,
            "window_seconds": 60,
            "per": "ip"
          }
        ]
      }
    },
    "/v2/materials/{id}": {
      "get": {
        "operationId": "get_material",
        "x-required-scopes": [],
        "x-audience": [
          "designer",
          "integrator"
        ],
        "tags": [
          "Materials"
        ],
        "summary": "Get a catalog material",
        "description": "Returns one catalog material by id. Public: no credentials needed.",
        "security": [],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^mat_[0-9A-Za-z]+$",
              "description": "Unique identifier of the material, prefixed with `mat_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the material, prefixed with `mat_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The material.",
            "content": {
              "application/json": {
                "example": {
                  "id": "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "material",
                  "name": "PLA Black",
                  "custom": false,
                  "technology": "fdm",
                  "color": "Black",
                  "color_hex": "#000000",
                  "filament_type": "pla"
                },
                "schema": {
                  "$ref": "#/components/schemas/Material"
                }
              }
            }
          },
          "404": {
            "description": "No catalog material has this id (`material_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "material_not_found"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 120 per minute per IP address. Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "public_read",
            "limit": 120,
            "window_seconds": 60,
            "per": "ip"
          }
        ]
      }
    },
    "/v2/categories": {
      "get": {
        "operationId": "list_categories",
        "x-required-scopes": [],
        "x-audience": [
          "designer",
          "integrator"
        ],
        "tags": [
          "Categories"
        ],
        "summary": "List product categories",
        "description": "Lists every product category as a flat list; each subcategory points at its `parent`. Public: no credentials needed. Use it to pick a category for a product and to see the safety requirements that come with it.",
        "security": [],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^cat_[0-9A-Za-z]+$",
              "description": "Only return the subcategories of this top-level category.",
              "example": "cat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": false,
            "description": "Only return the subcategories of this top-level category.",
            "name": "parent",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "description": "How many results per page, from 1 to 100.",
              "example": 20
            },
            "required": false,
            "description": "How many results per page, from 1 to 100.",
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Where to continue from: the `next_cursor` of the previous page. Omit for the first page."
            },
            "required": false,
            "description": "Where to continue from: the `next_cursor` of the previous page. Omit for the first page.",
            "name": "cursor",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of categories, in display order.",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "id": "cat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "category",
                      "name": "Action Figures & Dolls",
                      "slug": "toys-and-games--action-figures-and-dolls",
                      "description": null,
                      "parent": {
                        "id": "cat_9Qa1GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "category",
                        "name": "Toys & Games"
                      },
                      "compliance": {
                        "risk_level": "high",
                        "ce_marking_required": false,
                        "age_restriction": null,
                        "safety_warnings_required": true,
                        "safety_flags": [
                          "choking_hazard",
                          "age_restriction"
                        ],
                        "mandatory_warnings": [
                          "Warning. Not suitable for children under 36 months. Small parts. Choking hazard."
                        ],
                        "required_documents": [
                          {
                            "key": "material-toxicity-assessment",
                            "name": "Material Toxicity Assessment"
                          },
                          {
                            "key": "technical-file-and-risk-assessment",
                            "name": "Technical File & Risk Assessment"
                          }
                        ]
                      }
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                },
                "schema": {
                  "$ref": "#/components/schemas/CategoryList"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "validation_failed",
              "invalid_expand"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 120 per minute per IP address. Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "public_read",
            "limit": 120,
            "window_seconds": 60,
            "per": "ip"
          }
        ]
      }
    },
    "/v2/categories/{id}": {
      "get": {
        "operationId": "get_category",
        "x-required-scopes": [],
        "x-audience": [
          "designer",
          "integrator"
        ],
        "tags": [
          "Categories"
        ],
        "summary": "Get a product category",
        "description": "Returns one category with its resolved safety requirements. Public: no credentials needed.",
        "security": [],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^cat_[0-9A-Za-z]+$",
              "description": "Unique identifier of the category, prefixed with `cat_`. Treat it as an opaque string: store it and compare it, never parse it.",
              "example": "cat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
            },
            "required": true,
            "description": "Unique identifier of the category, prefixed with `cat_`. Treat it as an opaque string: store it and compare it, never parse it.",
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The category.",
            "content": {
              "application/json": {
                "example": {
                  "id": "cat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                  "object": "category",
                  "name": "Action Figures & Dolls",
                  "slug": "toys-and-games--action-figures-and-dolls",
                  "description": null,
                  "parent": {
                    "id": "cat_9Qa1GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "category",
                    "name": "Toys & Games"
                  },
                  "compliance": {
                    "risk_level": "high",
                    "ce_marking_required": false,
                    "age_restriction": null,
                    "safety_warnings_required": true,
                    "safety_flags": [
                      "choking_hazard",
                      "age_restriction"
                    ],
                    "mandatory_warnings": [
                      "Warning. Not suitable for children under 36 months. Small parts. Choking hazard."
                    ],
                    "required_documents": [
                      {
                        "key": "material-toxicity-assessment",
                        "name": "Material Toxicity Assessment"
                      },
                      {
                        "key": "technical-file-and-risk-assessment",
                        "name": "Technical File & Risk Assessment"
                      }
                    ]
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Category"
                }
              }
            }
          },
          "404": {
            "description": "No category has this id (`category_not_found`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "category_not_found"
            ]
          },
          "429": {
            "description": "Too many requests (`rate_limited`): the limit is 120 per minute per IP address. Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-error-codes": [
              "rate_limited"
            ]
          }
        },
        "x-ratelimit": [
          {
            "policy": "public_read",
            "limit": 120,
            "window_seconds": 60,
            "per": "ip"
          }
        ]
      }
    }
  },
  "webhooks": {
    "order.created": {
      "post": {
        "operationId": "order_created",
        "x-audience": [
          "designer",
          "integrator"
        ],
        "tags": [
          "Webhooks"
        ],
        "summary": "Order created",
        "description": "An order was placed, from any channel. Designers receive the order as get_order returns it; integrators as get_integrator_order returns it. Every delivery is a POST with a JSON event and three headers: `X-Dapi-Webhook-Signature: sha256=<hex>`, an HMAC-SHA256 of `<timestamp>.<raw body>` with your endpoint secret; `X-Dapi-Webhook-Timestamp`, the Unix time in seconds it was signed (every attempt is signed again; reject a timestamp more than 5 minutes from your clock to stop replays); and `X-Dapi-Event-Id`, the event id. Answer with any 2xx within 10 seconds. Failed deliveries are retried with backoff for 24 hours, so the same event can arrive more than once: deduplicate on the event id.",
        "requestBody": {
          "description": "The event.",
          "content": {
            "application/json": {
              "example": {
                "id": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                "object": "event",
                "type": "order.created",
                "created_at": "2026-10-14T10:02:00Z",
                "data": {
                  "object": {
                    "id": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "order",
                    "number": "PV-SCN-1701A372",
                    "channel": "storefront",
                    "status": "paid",
                    "customer": {
                      "name": "Ana Popescu",
                      "email": "ana@example.com",
                      "phone": null,
                      "company": null
                    },
                    "shipping_address": {
                      "formatted": "Str. Test 1, Cluj, RO",
                      "city": "Cluj",
                      "country": "Romania",
                      "country_code": "RO"
                    },
                    "charges": {
                      "paid_by": "buyer",
                      "items": {
                        "amount": 1800,
                        "currency": "usd"
                      },
                      "shipping": {
                        "amount": 600,
                        "currency": "usd"
                      },
                      "vat": {
                        "amount": {
                          "amount": 0,
                          "currency": "usd"
                        },
                        "rate": 0,
                        "included": false,
                        "country": null
                      },
                      "sample_credit": null,
                      "total": {
                        "amount": 2400,
                        "currency": "usd"
                      }
                    },
                    "earnings": {
                      "manufacturing": {
                        "amount": 900,
                        "currency": "usd"
                      },
                      "platform_fee": {
                        "amount": 300,
                        "currency": "usd"
                      },
                      "payment_processing": {
                        "amount": 102,
                        "currency": "usd"
                      },
                      "refunded": {
                        "amount": 0,
                        "currency": "usd"
                      },
                      "net": {
                        "amount": 498,
                        "currency": "usd"
                      }
                    },
                    "shipping": {
                      "tier": "small",
                      "carrier": null,
                      "tracking_number": null,
                      "shipped_at": null
                    },
                    "external": null,
                    "customer_notes": null,
                    "qc": {
                      "open_tickets": 1,
                      "blocks_shipping": true
                    },
                    "created_at": "2026-10-09T08:39:29Z",
                    "updated_at": "2026-10-09T08:40:02Z",
                    "item_count": 1,
                    "items": [
                      {
                        "id": "itm_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "order_item",
                        "product": {
                          "id": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "product",
                          "name": "Clicker"
                        },
                        "variant": {
                          "id": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "variant",
                          "name": "Clicker Blue"
                        },
                        "sku": "CLICK-BLUE",
                        "quantity": 1,
                        "unit_price": {
                          "amount": 1800,
                          "currency": "usd"
                        },
                        "total": {
                          "amount": 1800,
                          "currency": "usd"
                        },
                        "stock_used": [],
                        "fulfilled_from_stock": false
                      }
                    ],
                    "refunds": []
                  },
                  "previous": null,
                  "region": null
                }
              },
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "pattern": "^evt_[0-9A-Za-z]+$",
                    "description": "Unique identifier of the event, prefixed with `evt_`. Treat it as an opaque string: store it and compare it, never parse it.",
                    "example": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                  },
                  "object": {
                    "type": "string",
                    "enum": [
                      "event"
                    ],
                    "description": "Names the type of this object. Always `event`."
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "order.created"
                    ]
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened.",
                    "example": "2026-04-21T07:34:06Z"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "object": {
                        "anyOf": [
                          {
                            "$ref": "#/components/schemas/Order"
                          },
                          {
                            "$ref": "#/components/schemas/IntegratorOrder"
                          }
                        ]
                      },
                      "previous": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": {},
                        "description": "The fields that changed, with their values before the change. Null when nothing changed, as on creation."
                      },
                      "region": {
                        "type": "null",
                        "description": "Always null: only `stock.low` names a region."
                      }
                    },
                    "required": [
                      "object",
                      "previous",
                      "region"
                    ],
                    "description": "The object as it is now, exactly as its GET endpoint returns it, and what changed."
                  }
                },
                "required": [
                  "id",
                  "object",
                  "type",
                  "created_at",
                  "data"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the event."
          }
        }
      }
    },
    "order.paid": {
      "post": {
        "operationId": "order_paid",
        "x-audience": [
          "designer",
          "integrator"
        ],
        "tags": [
          "Webhooks"
        ],
        "summary": "Order paid",
        "description": "The order was paid. Designers receive the order as get_order returns it; integrators as get_integrator_order returns it. Every delivery is a POST with a JSON event and three headers: `X-Dapi-Webhook-Signature: sha256=<hex>`, an HMAC-SHA256 of `<timestamp>.<raw body>` with your endpoint secret; `X-Dapi-Webhook-Timestamp`, the Unix time in seconds it was signed (every attempt is signed again; reject a timestamp more than 5 minutes from your clock to stop replays); and `X-Dapi-Event-Id`, the event id. Answer with any 2xx within 10 seconds. Failed deliveries are retried with backoff for 24 hours, so the same event can arrive more than once: deduplicate on the event id.",
        "requestBody": {
          "description": "The event.",
          "content": {
            "application/json": {
              "example": {
                "id": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                "object": "event",
                "type": "order.paid",
                "created_at": "2026-10-14T10:02:00Z",
                "data": {
                  "object": {
                    "id": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "order",
                    "number": "PV-SCN-1701A372",
                    "channel": "storefront",
                    "status": "paid",
                    "customer": {
                      "name": "Ana Popescu",
                      "email": "ana@example.com",
                      "phone": null,
                      "company": null
                    },
                    "shipping_address": {
                      "formatted": "Str. Test 1, Cluj, RO",
                      "city": "Cluj",
                      "country": "Romania",
                      "country_code": "RO"
                    },
                    "charges": {
                      "paid_by": "buyer",
                      "items": {
                        "amount": 1800,
                        "currency": "usd"
                      },
                      "shipping": {
                        "amount": 600,
                        "currency": "usd"
                      },
                      "vat": {
                        "amount": {
                          "amount": 0,
                          "currency": "usd"
                        },
                        "rate": 0,
                        "included": false,
                        "country": null
                      },
                      "sample_credit": null,
                      "total": {
                        "amount": 2400,
                        "currency": "usd"
                      }
                    },
                    "earnings": {
                      "manufacturing": {
                        "amount": 900,
                        "currency": "usd"
                      },
                      "platform_fee": {
                        "amount": 300,
                        "currency": "usd"
                      },
                      "payment_processing": {
                        "amount": 102,
                        "currency": "usd"
                      },
                      "refunded": {
                        "amount": 0,
                        "currency": "usd"
                      },
                      "net": {
                        "amount": 498,
                        "currency": "usd"
                      }
                    },
                    "shipping": {
                      "tier": "small",
                      "carrier": null,
                      "tracking_number": null,
                      "shipped_at": null
                    },
                    "external": null,
                    "customer_notes": null,
                    "qc": {
                      "open_tickets": 1,
                      "blocks_shipping": true
                    },
                    "created_at": "2026-10-09T08:39:29Z",
                    "updated_at": "2026-10-09T08:40:02Z",
                    "item_count": 1,
                    "items": [
                      {
                        "id": "itm_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "order_item",
                        "product": {
                          "id": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "product",
                          "name": "Clicker"
                        },
                        "variant": {
                          "id": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "variant",
                          "name": "Clicker Blue"
                        },
                        "sku": "CLICK-BLUE",
                        "quantity": 1,
                        "unit_price": {
                          "amount": 1800,
                          "currency": "usd"
                        },
                        "total": {
                          "amount": 1800,
                          "currency": "usd"
                        },
                        "stock_used": [],
                        "fulfilled_from_stock": false
                      }
                    ],
                    "refunds": []
                  },
                  "previous": {
                    "status": "paid"
                  },
                  "region": null
                }
              },
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "pattern": "^evt_[0-9A-Za-z]+$",
                    "description": "Unique identifier of the event, prefixed with `evt_`. Treat it as an opaque string: store it and compare it, never parse it.",
                    "example": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                  },
                  "object": {
                    "type": "string",
                    "enum": [
                      "event"
                    ],
                    "description": "Names the type of this object. Always `event`."
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "order.paid"
                    ]
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened.",
                    "example": "2026-04-21T07:34:06Z"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "object": {
                        "anyOf": [
                          {
                            "$ref": "#/components/schemas/Order"
                          },
                          {
                            "$ref": "#/components/schemas/IntegratorOrder"
                          }
                        ]
                      },
                      "previous": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": {},
                        "description": "The fields that changed, with their values before the change. Null when nothing changed, as on creation."
                      },
                      "region": {
                        "type": "null",
                        "description": "Always null: only `stock.low` names a region."
                      }
                    },
                    "required": [
                      "object",
                      "previous",
                      "region"
                    ],
                    "description": "The object as it is now, exactly as its GET endpoint returns it, and what changed."
                  }
                },
                "required": [
                  "id",
                  "object",
                  "type",
                  "created_at",
                  "data"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the event."
          }
        }
      }
    },
    "order.assigned": {
      "post": {
        "operationId": "order_assigned",
        "x-audience": [
          "designer",
          "integrator"
        ],
        "tags": [
          "Webhooks"
        ],
        "summary": "Order assigned",
        "description": "A production partner took the order. Designers receive the order as get_order returns it; integrators as get_integrator_order returns it. Every delivery is a POST with a JSON event and three headers: `X-Dapi-Webhook-Signature: sha256=<hex>`, an HMAC-SHA256 of `<timestamp>.<raw body>` with your endpoint secret; `X-Dapi-Webhook-Timestamp`, the Unix time in seconds it was signed (every attempt is signed again; reject a timestamp more than 5 minutes from your clock to stop replays); and `X-Dapi-Event-Id`, the event id. Answer with any 2xx within 10 seconds. Failed deliveries are retried with backoff for 24 hours, so the same event can arrive more than once: deduplicate on the event id.",
        "requestBody": {
          "description": "The event.",
          "content": {
            "application/json": {
              "example": {
                "id": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                "object": "event",
                "type": "order.assigned",
                "created_at": "2026-10-14T10:02:00Z",
                "data": {
                  "object": {
                    "id": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "order",
                    "number": "PV-SCN-1701A372",
                    "channel": "storefront",
                    "status": "assigned",
                    "customer": {
                      "name": "Ana Popescu",
                      "email": "ana@example.com",
                      "phone": null,
                      "company": null
                    },
                    "shipping_address": {
                      "formatted": "Str. Test 1, Cluj, RO",
                      "city": "Cluj",
                      "country": "Romania",
                      "country_code": "RO"
                    },
                    "charges": {
                      "paid_by": "buyer",
                      "items": {
                        "amount": 1800,
                        "currency": "usd"
                      },
                      "shipping": {
                        "amount": 600,
                        "currency": "usd"
                      },
                      "vat": {
                        "amount": {
                          "amount": 0,
                          "currency": "usd"
                        },
                        "rate": 0,
                        "included": false,
                        "country": null
                      },
                      "sample_credit": null,
                      "total": {
                        "amount": 2400,
                        "currency": "usd"
                      }
                    },
                    "earnings": {
                      "manufacturing": {
                        "amount": 900,
                        "currency": "usd"
                      },
                      "platform_fee": {
                        "amount": 300,
                        "currency": "usd"
                      },
                      "payment_processing": {
                        "amount": 102,
                        "currency": "usd"
                      },
                      "refunded": {
                        "amount": 0,
                        "currency": "usd"
                      },
                      "net": {
                        "amount": 498,
                        "currency": "usd"
                      }
                    },
                    "shipping": {
                      "tier": "small",
                      "carrier": null,
                      "tracking_number": null,
                      "shipped_at": null
                    },
                    "external": null,
                    "customer_notes": null,
                    "qc": {
                      "open_tickets": 1,
                      "blocks_shipping": true
                    },
                    "created_at": "2026-10-09T08:39:29Z",
                    "updated_at": "2026-10-09T08:40:02Z",
                    "item_count": 1,
                    "items": [
                      {
                        "id": "itm_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "order_item",
                        "product": {
                          "id": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "product",
                          "name": "Clicker"
                        },
                        "variant": {
                          "id": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "variant",
                          "name": "Clicker Blue"
                        },
                        "sku": "CLICK-BLUE",
                        "quantity": 1,
                        "unit_price": {
                          "amount": 1800,
                          "currency": "usd"
                        },
                        "total": {
                          "amount": 1800,
                          "currency": "usd"
                        },
                        "stock_used": [],
                        "fulfilled_from_stock": false
                      }
                    ],
                    "refunds": []
                  },
                  "previous": {
                    "status": "paid"
                  },
                  "region": null
                }
              },
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "pattern": "^evt_[0-9A-Za-z]+$",
                    "description": "Unique identifier of the event, prefixed with `evt_`. Treat it as an opaque string: store it and compare it, never parse it.",
                    "example": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                  },
                  "object": {
                    "type": "string",
                    "enum": [
                      "event"
                    ],
                    "description": "Names the type of this object. Always `event`."
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "order.assigned"
                    ]
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened.",
                    "example": "2026-04-21T07:34:06Z"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "object": {
                        "anyOf": [
                          {
                            "$ref": "#/components/schemas/Order"
                          },
                          {
                            "$ref": "#/components/schemas/IntegratorOrder"
                          }
                        ]
                      },
                      "previous": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": {},
                        "description": "The fields that changed, with their values before the change. Null when nothing changed, as on creation."
                      },
                      "region": {
                        "type": "null",
                        "description": "Always null: only `stock.low` names a region."
                      }
                    },
                    "required": [
                      "object",
                      "previous",
                      "region"
                    ],
                    "description": "The object as it is now, exactly as its GET endpoint returns it, and what changed."
                  }
                },
                "required": [
                  "id",
                  "object",
                  "type",
                  "created_at",
                  "data"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the event."
          }
        }
      }
    },
    "order.in_production": {
      "post": {
        "operationId": "order_in_production",
        "x-audience": [
          "designer",
          "integrator"
        ],
        "tags": [
          "Webhooks"
        ],
        "summary": "Order in production",
        "description": "Production started. Designers receive the order as get_order returns it; integrators as get_integrator_order returns it. Every delivery is a POST with a JSON event and three headers: `X-Dapi-Webhook-Signature: sha256=<hex>`, an HMAC-SHA256 of `<timestamp>.<raw body>` with your endpoint secret; `X-Dapi-Webhook-Timestamp`, the Unix time in seconds it was signed (every attempt is signed again; reject a timestamp more than 5 minutes from your clock to stop replays); and `X-Dapi-Event-Id`, the event id. Answer with any 2xx within 10 seconds. Failed deliveries are retried with backoff for 24 hours, so the same event can arrive more than once: deduplicate on the event id.",
        "requestBody": {
          "description": "The event.",
          "content": {
            "application/json": {
              "example": {
                "id": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                "object": "event",
                "type": "order.in_production",
                "created_at": "2026-10-14T10:02:00Z",
                "data": {
                  "object": {
                    "id": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "order",
                    "number": "PV-SCN-1701A372",
                    "channel": "storefront",
                    "status": "in_production",
                    "customer": {
                      "name": "Ana Popescu",
                      "email": "ana@example.com",
                      "phone": null,
                      "company": null
                    },
                    "shipping_address": {
                      "formatted": "Str. Test 1, Cluj, RO",
                      "city": "Cluj",
                      "country": "Romania",
                      "country_code": "RO"
                    },
                    "charges": {
                      "paid_by": "buyer",
                      "items": {
                        "amount": 1800,
                        "currency": "usd"
                      },
                      "shipping": {
                        "amount": 600,
                        "currency": "usd"
                      },
                      "vat": {
                        "amount": {
                          "amount": 0,
                          "currency": "usd"
                        },
                        "rate": 0,
                        "included": false,
                        "country": null
                      },
                      "sample_credit": null,
                      "total": {
                        "amount": 2400,
                        "currency": "usd"
                      }
                    },
                    "earnings": {
                      "manufacturing": {
                        "amount": 900,
                        "currency": "usd"
                      },
                      "platform_fee": {
                        "amount": 300,
                        "currency": "usd"
                      },
                      "payment_processing": {
                        "amount": 102,
                        "currency": "usd"
                      },
                      "refunded": {
                        "amount": 0,
                        "currency": "usd"
                      },
                      "net": {
                        "amount": 498,
                        "currency": "usd"
                      }
                    },
                    "shipping": {
                      "tier": "small",
                      "carrier": null,
                      "tracking_number": null,
                      "shipped_at": null
                    },
                    "external": null,
                    "customer_notes": null,
                    "qc": {
                      "open_tickets": 1,
                      "blocks_shipping": true
                    },
                    "created_at": "2026-10-09T08:39:29Z",
                    "updated_at": "2026-10-09T08:40:02Z",
                    "item_count": 1,
                    "items": [
                      {
                        "id": "itm_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "order_item",
                        "product": {
                          "id": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "product",
                          "name": "Clicker"
                        },
                        "variant": {
                          "id": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "variant",
                          "name": "Clicker Blue"
                        },
                        "sku": "CLICK-BLUE",
                        "quantity": 1,
                        "unit_price": {
                          "amount": 1800,
                          "currency": "usd"
                        },
                        "total": {
                          "amount": 1800,
                          "currency": "usd"
                        },
                        "stock_used": [],
                        "fulfilled_from_stock": false
                      }
                    ],
                    "refunds": []
                  },
                  "previous": {
                    "status": "paid"
                  },
                  "region": null
                }
              },
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "pattern": "^evt_[0-9A-Za-z]+$",
                    "description": "Unique identifier of the event, prefixed with `evt_`. Treat it as an opaque string: store it and compare it, never parse it.",
                    "example": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                  },
                  "object": {
                    "type": "string",
                    "enum": [
                      "event"
                    ],
                    "description": "Names the type of this object. Always `event`."
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "order.in_production"
                    ]
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened.",
                    "example": "2026-04-21T07:34:06Z"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "object": {
                        "anyOf": [
                          {
                            "$ref": "#/components/schemas/Order"
                          },
                          {
                            "$ref": "#/components/schemas/IntegratorOrder"
                          }
                        ]
                      },
                      "previous": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": {},
                        "description": "The fields that changed, with their values before the change. Null when nothing changed, as on creation."
                      },
                      "region": {
                        "type": "null",
                        "description": "Always null: only `stock.low` names a region."
                      }
                    },
                    "required": [
                      "object",
                      "previous",
                      "region"
                    ],
                    "description": "The object as it is now, exactly as its GET endpoint returns it, and what changed."
                  }
                },
                "required": [
                  "id",
                  "object",
                  "type",
                  "created_at",
                  "data"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the event."
          }
        }
      }
    },
    "order.shipped": {
      "post": {
        "operationId": "order_shipped",
        "x-audience": [
          "designer",
          "integrator"
        ],
        "tags": [
          "Webhooks"
        ],
        "summary": "Order shipped",
        "description": "The order shipped; `shipping` has the tracking details. Designers receive the order as get_order returns it; integrators as get_integrator_order returns it. Every delivery is a POST with a JSON event and three headers: `X-Dapi-Webhook-Signature: sha256=<hex>`, an HMAC-SHA256 of `<timestamp>.<raw body>` with your endpoint secret; `X-Dapi-Webhook-Timestamp`, the Unix time in seconds it was signed (every attempt is signed again; reject a timestamp more than 5 minutes from your clock to stop replays); and `X-Dapi-Event-Id`, the event id. Answer with any 2xx within 10 seconds. Failed deliveries are retried with backoff for 24 hours, so the same event can arrive more than once: deduplicate on the event id.",
        "requestBody": {
          "description": "The event.",
          "content": {
            "application/json": {
              "example": {
                "id": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                "object": "event",
                "type": "order.shipped",
                "created_at": "2026-10-14T10:02:00Z",
                "data": {
                  "object": {
                    "id": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "order",
                    "number": "PV-SCN-1701A372",
                    "channel": "storefront",
                    "status": "shipped",
                    "customer": {
                      "name": "Ana Popescu",
                      "email": "ana@example.com",
                      "phone": null,
                      "company": null
                    },
                    "shipping_address": {
                      "formatted": "Str. Test 1, Cluj, RO",
                      "city": "Cluj",
                      "country": "Romania",
                      "country_code": "RO"
                    },
                    "charges": {
                      "paid_by": "buyer",
                      "items": {
                        "amount": 1800,
                        "currency": "usd"
                      },
                      "shipping": {
                        "amount": 600,
                        "currency": "usd"
                      },
                      "vat": {
                        "amount": {
                          "amount": 0,
                          "currency": "usd"
                        },
                        "rate": 0,
                        "included": false,
                        "country": null
                      },
                      "sample_credit": null,
                      "total": {
                        "amount": 2400,
                        "currency": "usd"
                      }
                    },
                    "earnings": {
                      "manufacturing": {
                        "amount": 900,
                        "currency": "usd"
                      },
                      "platform_fee": {
                        "amount": 300,
                        "currency": "usd"
                      },
                      "payment_processing": {
                        "amount": 102,
                        "currency": "usd"
                      },
                      "refunded": {
                        "amount": 0,
                        "currency": "usd"
                      },
                      "net": {
                        "amount": 498,
                        "currency": "usd"
                      }
                    },
                    "shipping": {
                      "tier": "small",
                      "carrier": null,
                      "tracking_number": null,
                      "shipped_at": null
                    },
                    "external": null,
                    "customer_notes": null,
                    "qc": {
                      "open_tickets": 1,
                      "blocks_shipping": true
                    },
                    "created_at": "2026-10-09T08:39:29Z",
                    "updated_at": "2026-10-09T08:40:02Z",
                    "item_count": 1,
                    "items": [
                      {
                        "id": "itm_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "order_item",
                        "product": {
                          "id": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "product",
                          "name": "Clicker"
                        },
                        "variant": {
                          "id": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "variant",
                          "name": "Clicker Blue"
                        },
                        "sku": "CLICK-BLUE",
                        "quantity": 1,
                        "unit_price": {
                          "amount": 1800,
                          "currency": "usd"
                        },
                        "total": {
                          "amount": 1800,
                          "currency": "usd"
                        },
                        "stock_used": [],
                        "fulfilled_from_stock": false
                      }
                    ],
                    "refunds": []
                  },
                  "previous": {
                    "status": "paid"
                  },
                  "region": null
                }
              },
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "pattern": "^evt_[0-9A-Za-z]+$",
                    "description": "Unique identifier of the event, prefixed with `evt_`. Treat it as an opaque string: store it and compare it, never parse it.",
                    "example": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                  },
                  "object": {
                    "type": "string",
                    "enum": [
                      "event"
                    ],
                    "description": "Names the type of this object. Always `event`."
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "order.shipped"
                    ]
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened.",
                    "example": "2026-04-21T07:34:06Z"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "object": {
                        "anyOf": [
                          {
                            "$ref": "#/components/schemas/Order"
                          },
                          {
                            "$ref": "#/components/schemas/IntegratorOrder"
                          }
                        ]
                      },
                      "previous": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": {},
                        "description": "The fields that changed, with their values before the change. Null when nothing changed, as on creation."
                      },
                      "region": {
                        "type": "null",
                        "description": "Always null: only `stock.low` names a region."
                      }
                    },
                    "required": [
                      "object",
                      "previous",
                      "region"
                    ],
                    "description": "The object as it is now, exactly as its GET endpoint returns it, and what changed."
                  }
                },
                "required": [
                  "id",
                  "object",
                  "type",
                  "created_at",
                  "data"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the event."
          }
        }
      }
    },
    "order.completed": {
      "post": {
        "operationId": "order_completed",
        "x-audience": [
          "designer",
          "integrator"
        ],
        "tags": [
          "Webhooks"
        ],
        "summary": "Order completed",
        "description": "The order was delivered and settled. Designers receive the order as get_order returns it; integrators as get_integrator_order returns it. Every delivery is a POST with a JSON event and three headers: `X-Dapi-Webhook-Signature: sha256=<hex>`, an HMAC-SHA256 of `<timestamp>.<raw body>` with your endpoint secret; `X-Dapi-Webhook-Timestamp`, the Unix time in seconds it was signed (every attempt is signed again; reject a timestamp more than 5 minutes from your clock to stop replays); and `X-Dapi-Event-Id`, the event id. Answer with any 2xx within 10 seconds. Failed deliveries are retried with backoff for 24 hours, so the same event can arrive more than once: deduplicate on the event id.",
        "requestBody": {
          "description": "The event.",
          "content": {
            "application/json": {
              "example": {
                "id": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                "object": "event",
                "type": "order.completed",
                "created_at": "2026-10-14T10:02:00Z",
                "data": {
                  "object": {
                    "id": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "order",
                    "number": "PV-SCN-1701A372",
                    "channel": "storefront",
                    "status": "completed",
                    "customer": {
                      "name": "Ana Popescu",
                      "email": "ana@example.com",
                      "phone": null,
                      "company": null
                    },
                    "shipping_address": {
                      "formatted": "Str. Test 1, Cluj, RO",
                      "city": "Cluj",
                      "country": "Romania",
                      "country_code": "RO"
                    },
                    "charges": {
                      "paid_by": "buyer",
                      "items": {
                        "amount": 1800,
                        "currency": "usd"
                      },
                      "shipping": {
                        "amount": 600,
                        "currency": "usd"
                      },
                      "vat": {
                        "amount": {
                          "amount": 0,
                          "currency": "usd"
                        },
                        "rate": 0,
                        "included": false,
                        "country": null
                      },
                      "sample_credit": null,
                      "total": {
                        "amount": 2400,
                        "currency": "usd"
                      }
                    },
                    "earnings": {
                      "manufacturing": {
                        "amount": 900,
                        "currency": "usd"
                      },
                      "platform_fee": {
                        "amount": 300,
                        "currency": "usd"
                      },
                      "payment_processing": {
                        "amount": 102,
                        "currency": "usd"
                      },
                      "refunded": {
                        "amount": 0,
                        "currency": "usd"
                      },
                      "net": {
                        "amount": 498,
                        "currency": "usd"
                      }
                    },
                    "shipping": {
                      "tier": "small",
                      "carrier": null,
                      "tracking_number": null,
                      "shipped_at": null
                    },
                    "external": null,
                    "customer_notes": null,
                    "qc": {
                      "open_tickets": 1,
                      "blocks_shipping": true
                    },
                    "created_at": "2026-10-09T08:39:29Z",
                    "updated_at": "2026-10-09T08:40:02Z",
                    "item_count": 1,
                    "items": [
                      {
                        "id": "itm_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "order_item",
                        "product": {
                          "id": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "product",
                          "name": "Clicker"
                        },
                        "variant": {
                          "id": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "variant",
                          "name": "Clicker Blue"
                        },
                        "sku": "CLICK-BLUE",
                        "quantity": 1,
                        "unit_price": {
                          "amount": 1800,
                          "currency": "usd"
                        },
                        "total": {
                          "amount": 1800,
                          "currency": "usd"
                        },
                        "stock_used": [],
                        "fulfilled_from_stock": false
                      }
                    ],
                    "refunds": []
                  },
                  "previous": {
                    "status": "paid"
                  },
                  "region": null
                }
              },
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "pattern": "^evt_[0-9A-Za-z]+$",
                    "description": "Unique identifier of the event, prefixed with `evt_`. Treat it as an opaque string: store it and compare it, never parse it.",
                    "example": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                  },
                  "object": {
                    "type": "string",
                    "enum": [
                      "event"
                    ],
                    "description": "Names the type of this object. Always `event`."
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "order.completed"
                    ]
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened.",
                    "example": "2026-04-21T07:34:06Z"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "object": {
                        "anyOf": [
                          {
                            "$ref": "#/components/schemas/Order"
                          },
                          {
                            "$ref": "#/components/schemas/IntegratorOrder"
                          }
                        ]
                      },
                      "previous": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": {},
                        "description": "The fields that changed, with their values before the change. Null when nothing changed, as on creation."
                      },
                      "region": {
                        "type": "null",
                        "description": "Always null: only `stock.low` names a region."
                      }
                    },
                    "required": [
                      "object",
                      "previous",
                      "region"
                    ],
                    "description": "The object as it is now, exactly as its GET endpoint returns it, and what changed."
                  }
                },
                "required": [
                  "id",
                  "object",
                  "type",
                  "created_at",
                  "data"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the event."
          }
        }
      }
    },
    "order.on_hold": {
      "post": {
        "operationId": "order_on_hold",
        "x-audience": [
          "designer",
          "integrator"
        ],
        "tags": [
          "Webhooks"
        ],
        "summary": "Order on hold",
        "description": "There is not enough stock to fulfil the order. Designers receive the order as get_order returns it; integrators as get_integrator_order returns it. Every delivery is a POST with a JSON event and three headers: `X-Dapi-Webhook-Signature: sha256=<hex>`, an HMAC-SHA256 of `<timestamp>.<raw body>` with your endpoint secret; `X-Dapi-Webhook-Timestamp`, the Unix time in seconds it was signed (every attempt is signed again; reject a timestamp more than 5 minutes from your clock to stop replays); and `X-Dapi-Event-Id`, the event id. Answer with any 2xx within 10 seconds. Failed deliveries are retried with backoff for 24 hours, so the same event can arrive more than once: deduplicate on the event id.",
        "requestBody": {
          "description": "The event.",
          "content": {
            "application/json": {
              "example": {
                "id": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                "object": "event",
                "type": "order.on_hold",
                "created_at": "2026-10-14T10:02:00Z",
                "data": {
                  "object": {
                    "id": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "order",
                    "number": "PV-SCN-1701A372",
                    "channel": "storefront",
                    "status": "on_hold",
                    "customer": {
                      "name": "Ana Popescu",
                      "email": "ana@example.com",
                      "phone": null,
                      "company": null
                    },
                    "shipping_address": {
                      "formatted": "Str. Test 1, Cluj, RO",
                      "city": "Cluj",
                      "country": "Romania",
                      "country_code": "RO"
                    },
                    "charges": {
                      "paid_by": "buyer",
                      "items": {
                        "amount": 1800,
                        "currency": "usd"
                      },
                      "shipping": {
                        "amount": 600,
                        "currency": "usd"
                      },
                      "vat": {
                        "amount": {
                          "amount": 0,
                          "currency": "usd"
                        },
                        "rate": 0,
                        "included": false,
                        "country": null
                      },
                      "sample_credit": null,
                      "total": {
                        "amount": 2400,
                        "currency": "usd"
                      }
                    },
                    "earnings": {
                      "manufacturing": {
                        "amount": 900,
                        "currency": "usd"
                      },
                      "platform_fee": {
                        "amount": 300,
                        "currency": "usd"
                      },
                      "payment_processing": {
                        "amount": 102,
                        "currency": "usd"
                      },
                      "refunded": {
                        "amount": 0,
                        "currency": "usd"
                      },
                      "net": {
                        "amount": 498,
                        "currency": "usd"
                      }
                    },
                    "shipping": {
                      "tier": "small",
                      "carrier": null,
                      "tracking_number": null,
                      "shipped_at": null
                    },
                    "external": null,
                    "customer_notes": null,
                    "qc": {
                      "open_tickets": 1,
                      "blocks_shipping": true
                    },
                    "created_at": "2026-10-09T08:39:29Z",
                    "updated_at": "2026-10-09T08:40:02Z",
                    "item_count": 1,
                    "items": [
                      {
                        "id": "itm_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "order_item",
                        "product": {
                          "id": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "product",
                          "name": "Clicker"
                        },
                        "variant": {
                          "id": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "variant",
                          "name": "Clicker Blue"
                        },
                        "sku": "CLICK-BLUE",
                        "quantity": 1,
                        "unit_price": {
                          "amount": 1800,
                          "currency": "usd"
                        },
                        "total": {
                          "amount": 1800,
                          "currency": "usd"
                        },
                        "stock_used": [],
                        "fulfilled_from_stock": false
                      }
                    ],
                    "refunds": []
                  },
                  "previous": {
                    "status": "paid"
                  },
                  "region": null
                }
              },
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "pattern": "^evt_[0-9A-Za-z]+$",
                    "description": "Unique identifier of the event, prefixed with `evt_`. Treat it as an opaque string: store it and compare it, never parse it.",
                    "example": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                  },
                  "object": {
                    "type": "string",
                    "enum": [
                      "event"
                    ],
                    "description": "Names the type of this object. Always `event`."
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "order.on_hold"
                    ]
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened.",
                    "example": "2026-04-21T07:34:06Z"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "object": {
                        "anyOf": [
                          {
                            "$ref": "#/components/schemas/Order"
                          },
                          {
                            "$ref": "#/components/schemas/IntegratorOrder"
                          }
                        ]
                      },
                      "previous": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": {},
                        "description": "The fields that changed, with their values before the change. Null when nothing changed, as on creation."
                      },
                      "region": {
                        "type": "null",
                        "description": "Always null: only `stock.low` names a region."
                      }
                    },
                    "required": [
                      "object",
                      "previous",
                      "region"
                    ],
                    "description": "The object as it is now, exactly as its GET endpoint returns it, and what changed."
                  }
                },
                "required": [
                  "id",
                  "object",
                  "type",
                  "created_at",
                  "data"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the event."
          }
        }
      }
    },
    "order.rejected": {
      "post": {
        "operationId": "order_rejected",
        "x-audience": [
          "designer",
          "integrator"
        ],
        "tags": [
          "Webhooks"
        ],
        "summary": "Order rejected",
        "description": "The production partner refused the order. Designers receive the order as get_order returns it; integrators as get_integrator_order returns it. Every delivery is a POST with a JSON event and three headers: `X-Dapi-Webhook-Signature: sha256=<hex>`, an HMAC-SHA256 of `<timestamp>.<raw body>` with your endpoint secret; `X-Dapi-Webhook-Timestamp`, the Unix time in seconds it was signed (every attempt is signed again; reject a timestamp more than 5 minutes from your clock to stop replays); and `X-Dapi-Event-Id`, the event id. Answer with any 2xx within 10 seconds. Failed deliveries are retried with backoff for 24 hours, so the same event can arrive more than once: deduplicate on the event id.",
        "requestBody": {
          "description": "The event.",
          "content": {
            "application/json": {
              "example": {
                "id": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                "object": "event",
                "type": "order.rejected",
                "created_at": "2026-10-14T10:02:00Z",
                "data": {
                  "object": {
                    "id": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "order",
                    "number": "PV-SCN-1701A372",
                    "channel": "storefront",
                    "status": "rejected",
                    "customer": {
                      "name": "Ana Popescu",
                      "email": "ana@example.com",
                      "phone": null,
                      "company": null
                    },
                    "shipping_address": {
                      "formatted": "Str. Test 1, Cluj, RO",
                      "city": "Cluj",
                      "country": "Romania",
                      "country_code": "RO"
                    },
                    "charges": {
                      "paid_by": "buyer",
                      "items": {
                        "amount": 1800,
                        "currency": "usd"
                      },
                      "shipping": {
                        "amount": 600,
                        "currency": "usd"
                      },
                      "vat": {
                        "amount": {
                          "amount": 0,
                          "currency": "usd"
                        },
                        "rate": 0,
                        "included": false,
                        "country": null
                      },
                      "sample_credit": null,
                      "total": {
                        "amount": 2400,
                        "currency": "usd"
                      }
                    },
                    "earnings": {
                      "manufacturing": {
                        "amount": 900,
                        "currency": "usd"
                      },
                      "platform_fee": {
                        "amount": 300,
                        "currency": "usd"
                      },
                      "payment_processing": {
                        "amount": 102,
                        "currency": "usd"
                      },
                      "refunded": {
                        "amount": 0,
                        "currency": "usd"
                      },
                      "net": {
                        "amount": 498,
                        "currency": "usd"
                      }
                    },
                    "shipping": {
                      "tier": "small",
                      "carrier": null,
                      "tracking_number": null,
                      "shipped_at": null
                    },
                    "external": null,
                    "customer_notes": null,
                    "qc": {
                      "open_tickets": 1,
                      "blocks_shipping": true
                    },
                    "created_at": "2026-10-09T08:39:29Z",
                    "updated_at": "2026-10-09T08:40:02Z",
                    "item_count": 1,
                    "items": [
                      {
                        "id": "itm_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "order_item",
                        "product": {
                          "id": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "product",
                          "name": "Clicker"
                        },
                        "variant": {
                          "id": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "variant",
                          "name": "Clicker Blue"
                        },
                        "sku": "CLICK-BLUE",
                        "quantity": 1,
                        "unit_price": {
                          "amount": 1800,
                          "currency": "usd"
                        },
                        "total": {
                          "amount": 1800,
                          "currency": "usd"
                        },
                        "stock_used": [],
                        "fulfilled_from_stock": false
                      }
                    ],
                    "refunds": []
                  },
                  "previous": {
                    "status": "paid"
                  },
                  "region": null
                }
              },
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "pattern": "^evt_[0-9A-Za-z]+$",
                    "description": "Unique identifier of the event, prefixed with `evt_`. Treat it as an opaque string: store it and compare it, never parse it.",
                    "example": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                  },
                  "object": {
                    "type": "string",
                    "enum": [
                      "event"
                    ],
                    "description": "Names the type of this object. Always `event`."
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "order.rejected"
                    ]
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened.",
                    "example": "2026-04-21T07:34:06Z"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "object": {
                        "anyOf": [
                          {
                            "$ref": "#/components/schemas/Order"
                          },
                          {
                            "$ref": "#/components/schemas/IntegratorOrder"
                          }
                        ]
                      },
                      "previous": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": {},
                        "description": "The fields that changed, with their values before the change. Null when nothing changed, as on creation."
                      },
                      "region": {
                        "type": "null",
                        "description": "Always null: only `stock.low` names a region."
                      }
                    },
                    "required": [
                      "object",
                      "previous",
                      "region"
                    ],
                    "description": "The object as it is now, exactly as its GET endpoint returns it, and what changed."
                  }
                },
                "required": [
                  "id",
                  "object",
                  "type",
                  "created_at",
                  "data"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the event."
          }
        }
      }
    },
    "order.returned": {
      "post": {
        "operationId": "order_returned",
        "x-audience": [
          "designer",
          "integrator"
        ],
        "tags": [
          "Webhooks"
        ],
        "summary": "Order returned",
        "description": "The order was sent back after shipping. Designers receive the order as get_order returns it; integrators as get_integrator_order returns it. Every delivery is a POST with a JSON event and three headers: `X-Dapi-Webhook-Signature: sha256=<hex>`, an HMAC-SHA256 of `<timestamp>.<raw body>` with your endpoint secret; `X-Dapi-Webhook-Timestamp`, the Unix time in seconds it was signed (every attempt is signed again; reject a timestamp more than 5 minutes from your clock to stop replays); and `X-Dapi-Event-Id`, the event id. Answer with any 2xx within 10 seconds. Failed deliveries are retried with backoff for 24 hours, so the same event can arrive more than once: deduplicate on the event id.",
        "requestBody": {
          "description": "The event.",
          "content": {
            "application/json": {
              "example": {
                "id": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                "object": "event",
                "type": "order.returned",
                "created_at": "2026-10-14T10:02:00Z",
                "data": {
                  "object": {
                    "id": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "order",
                    "number": "PV-SCN-1701A372",
                    "channel": "storefront",
                    "status": "returned",
                    "customer": {
                      "name": "Ana Popescu",
                      "email": "ana@example.com",
                      "phone": null,
                      "company": null
                    },
                    "shipping_address": {
                      "formatted": "Str. Test 1, Cluj, RO",
                      "city": "Cluj",
                      "country": "Romania",
                      "country_code": "RO"
                    },
                    "charges": {
                      "paid_by": "buyer",
                      "items": {
                        "amount": 1800,
                        "currency": "usd"
                      },
                      "shipping": {
                        "amount": 600,
                        "currency": "usd"
                      },
                      "vat": {
                        "amount": {
                          "amount": 0,
                          "currency": "usd"
                        },
                        "rate": 0,
                        "included": false,
                        "country": null
                      },
                      "sample_credit": null,
                      "total": {
                        "amount": 2400,
                        "currency": "usd"
                      }
                    },
                    "earnings": {
                      "manufacturing": {
                        "amount": 900,
                        "currency": "usd"
                      },
                      "platform_fee": {
                        "amount": 300,
                        "currency": "usd"
                      },
                      "payment_processing": {
                        "amount": 102,
                        "currency": "usd"
                      },
                      "refunded": {
                        "amount": 0,
                        "currency": "usd"
                      },
                      "net": {
                        "amount": 498,
                        "currency": "usd"
                      }
                    },
                    "shipping": {
                      "tier": "small",
                      "carrier": null,
                      "tracking_number": null,
                      "shipped_at": null
                    },
                    "external": null,
                    "customer_notes": null,
                    "qc": {
                      "open_tickets": 1,
                      "blocks_shipping": true
                    },
                    "created_at": "2026-10-09T08:39:29Z",
                    "updated_at": "2026-10-09T08:40:02Z",
                    "item_count": 1,
                    "items": [
                      {
                        "id": "itm_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "order_item",
                        "product": {
                          "id": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "product",
                          "name": "Clicker"
                        },
                        "variant": {
                          "id": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "variant",
                          "name": "Clicker Blue"
                        },
                        "sku": "CLICK-BLUE",
                        "quantity": 1,
                        "unit_price": {
                          "amount": 1800,
                          "currency": "usd"
                        },
                        "total": {
                          "amount": 1800,
                          "currency": "usd"
                        },
                        "stock_used": [],
                        "fulfilled_from_stock": false
                      }
                    ],
                    "refunds": []
                  },
                  "previous": {
                    "status": "paid"
                  },
                  "region": null
                }
              },
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "pattern": "^evt_[0-9A-Za-z]+$",
                    "description": "Unique identifier of the event, prefixed with `evt_`. Treat it as an opaque string: store it and compare it, never parse it.",
                    "example": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                  },
                  "object": {
                    "type": "string",
                    "enum": [
                      "event"
                    ],
                    "description": "Names the type of this object. Always `event`."
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "order.returned"
                    ]
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened.",
                    "example": "2026-04-21T07:34:06Z"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "object": {
                        "anyOf": [
                          {
                            "$ref": "#/components/schemas/Order"
                          },
                          {
                            "$ref": "#/components/schemas/IntegratorOrder"
                          }
                        ]
                      },
                      "previous": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": {},
                        "description": "The fields that changed, with their values before the change. Null when nothing changed, as on creation."
                      },
                      "region": {
                        "type": "null",
                        "description": "Always null: only `stock.low` names a region."
                      }
                    },
                    "required": [
                      "object",
                      "previous",
                      "region"
                    ],
                    "description": "The object as it is now, exactly as its GET endpoint returns it, and what changed."
                  }
                },
                "required": [
                  "id",
                  "object",
                  "type",
                  "created_at",
                  "data"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the event."
          }
        }
      }
    },
    "order.cancelled": {
      "post": {
        "operationId": "order_cancelled",
        "x-audience": [
          "designer",
          "integrator"
        ],
        "tags": [
          "Webhooks"
        ],
        "summary": "Order cancelled",
        "description": "The order was cancelled. Designers receive the order as get_order returns it; integrators as get_integrator_order returns it. Every delivery is a POST with a JSON event and three headers: `X-Dapi-Webhook-Signature: sha256=<hex>`, an HMAC-SHA256 of `<timestamp>.<raw body>` with your endpoint secret; `X-Dapi-Webhook-Timestamp`, the Unix time in seconds it was signed (every attempt is signed again; reject a timestamp more than 5 minutes from your clock to stop replays); and `X-Dapi-Event-Id`, the event id. Answer with any 2xx within 10 seconds. Failed deliveries are retried with backoff for 24 hours, so the same event can arrive more than once: deduplicate on the event id.",
        "requestBody": {
          "description": "The event.",
          "content": {
            "application/json": {
              "example": {
                "id": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                "object": "event",
                "type": "order.cancelled",
                "created_at": "2026-10-14T10:02:00Z",
                "data": {
                  "object": {
                    "id": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "order",
                    "number": "PV-SCN-1701A372",
                    "channel": "storefront",
                    "status": "cancelled",
                    "customer": {
                      "name": "Ana Popescu",
                      "email": "ana@example.com",
                      "phone": null,
                      "company": null
                    },
                    "shipping_address": {
                      "formatted": "Str. Test 1, Cluj, RO",
                      "city": "Cluj",
                      "country": "Romania",
                      "country_code": "RO"
                    },
                    "charges": {
                      "paid_by": "buyer",
                      "items": {
                        "amount": 1800,
                        "currency": "usd"
                      },
                      "shipping": {
                        "amount": 600,
                        "currency": "usd"
                      },
                      "vat": {
                        "amount": {
                          "amount": 0,
                          "currency": "usd"
                        },
                        "rate": 0,
                        "included": false,
                        "country": null
                      },
                      "sample_credit": null,
                      "total": {
                        "amount": 2400,
                        "currency": "usd"
                      }
                    },
                    "earnings": {
                      "manufacturing": {
                        "amount": 900,
                        "currency": "usd"
                      },
                      "platform_fee": {
                        "amount": 300,
                        "currency": "usd"
                      },
                      "payment_processing": {
                        "amount": 102,
                        "currency": "usd"
                      },
                      "refunded": {
                        "amount": 0,
                        "currency": "usd"
                      },
                      "net": {
                        "amount": 498,
                        "currency": "usd"
                      }
                    },
                    "shipping": {
                      "tier": "small",
                      "carrier": null,
                      "tracking_number": null,
                      "shipped_at": null
                    },
                    "external": null,
                    "customer_notes": null,
                    "qc": {
                      "open_tickets": 1,
                      "blocks_shipping": true
                    },
                    "created_at": "2026-10-09T08:39:29Z",
                    "updated_at": "2026-10-09T08:40:02Z",
                    "item_count": 1,
                    "items": [
                      {
                        "id": "itm_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "order_item",
                        "product": {
                          "id": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "product",
                          "name": "Clicker"
                        },
                        "variant": {
                          "id": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "variant",
                          "name": "Clicker Blue"
                        },
                        "sku": "CLICK-BLUE",
                        "quantity": 1,
                        "unit_price": {
                          "amount": 1800,
                          "currency": "usd"
                        },
                        "total": {
                          "amount": 1800,
                          "currency": "usd"
                        },
                        "stock_used": [],
                        "fulfilled_from_stock": false
                      }
                    ],
                    "refunds": []
                  },
                  "previous": {
                    "status": "paid"
                  },
                  "region": null
                }
              },
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "pattern": "^evt_[0-9A-Za-z]+$",
                    "description": "Unique identifier of the event, prefixed with `evt_`. Treat it as an opaque string: store it and compare it, never parse it.",
                    "example": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                  },
                  "object": {
                    "type": "string",
                    "enum": [
                      "event"
                    ],
                    "description": "Names the type of this object. Always `event`."
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "order.cancelled"
                    ]
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened.",
                    "example": "2026-04-21T07:34:06Z"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "object": {
                        "anyOf": [
                          {
                            "$ref": "#/components/schemas/Order"
                          },
                          {
                            "$ref": "#/components/schemas/IntegratorOrder"
                          }
                        ]
                      },
                      "previous": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": {},
                        "description": "The fields that changed, with their values before the change. Null when nothing changed, as on creation."
                      },
                      "region": {
                        "type": "null",
                        "description": "Always null: only `stock.low` names a region."
                      }
                    },
                    "required": [
                      "object",
                      "previous",
                      "region"
                    ],
                    "description": "The object as it is now, exactly as its GET endpoint returns it, and what changed."
                  }
                },
                "required": [
                  "id",
                  "object",
                  "type",
                  "created_at",
                  "data"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the event."
          }
        }
      }
    },
    "checkout.expired": {
      "post": {
        "operationId": "checkout_expired",
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Webhooks"
        ],
        "summary": "Checkout link expired",
        "description": "Integrators only. A `checkout` order was not paid within 24 hours of its link opening, so the order was cancelled. `data.object` is the order as get_integrator_order returns it. Every delivery is a POST with a JSON event and three headers: `X-Dapi-Webhook-Signature: sha256=<hex>`, an HMAC-SHA256 of `<timestamp>.<raw body>` with your endpoint secret; `X-Dapi-Webhook-Timestamp`, the Unix time in seconds it was signed (every attempt is signed again; reject a timestamp more than 5 minutes from your clock to stop replays); and `X-Dapi-Event-Id`, the event id. Answer with any 2xx within 10 seconds. Failed deliveries are retried with backoff for 24 hours, so the same event can arrive more than once: deduplicate on the event id.",
        "requestBody": {
          "description": "The event.",
          "content": {
            "application/json": {
              "example": {
                "id": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                "object": "event",
                "type": "checkout.expired",
                "created_at": "2026-10-14T10:02:00Z",
                "data": {
                  "object": {
                    "id": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "order",
                    "number": "2AC7K2M9X",
                    "channel": "integrator_checkout",
                    "status": "cancelled",
                    "customer": {
                      "name": "Ana Popescu",
                      "email": "ana@example.com",
                      "phone": null,
                      "company": null
                    },
                    "shipping_address": {
                      "formatted": "Str. Test 1, Cluj, RO",
                      "city": "Cluj",
                      "country": "Romania",
                      "country_code": "RO"
                    },
                    "charges": {
                      "paid_by": "you",
                      "items": {
                        "amount": 5000,
                        "currency": "usd"
                      },
                      "shipping": {
                        "amount": 800,
                        "currency": "usd"
                      },
                      "vat": {
                        "amount": {
                          "amount": 0,
                          "currency": "usd"
                        },
                        "rate": 0,
                        "included": false,
                        "country": null
                      },
                      "sample_credit": null,
                      "total": {
                        "amount": 5800,
                        "currency": "usd"
                      }
                    },
                    "earnings": null,
                    "shipping": {
                      "tier": "small",
                      "carrier": null,
                      "tracking_number": null,
                      "shipped_at": null
                    },
                    "customer_notes": null,
                    "created_at": "2026-10-09T08:39:29Z",
                    "updated_at": "2026-10-09T08:40:02Z",
                    "item_count": 10,
                    "mode": "checkout",
                    "checkout": {
                      "object": "checkout_link",
                      "url": "https://checkout.stripe.com/c/pay/cs_test_a1b2c3",
                      "status": "expired",
                      "expires_at": "2026-10-23T10:00:00.000Z"
                    },
                    "items": [
                      {
                        "id": "itm_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "order_item",
                        "quote": {
                          "id": "qte_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "quote",
                          "name": "phone-stand.stl"
                        },
                        "materials": [
                          {
                            "id": "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                            "object": "material",
                            "name": "PLA Black"
                          }
                        ],
                        "material_slots": [
                          {
                            "slot": 1,
                            "material": {
                              "id": "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                              "object": "material",
                              "name": "PLA Black"
                            }
                          }
                        ],
                        "quantity": 10,
                        "unit_price": {
                          "amount": 500,
                          "currency": "usd"
                        },
                        "production_unit_price": {
                          "amount": 500,
                          "currency": "usd"
                        },
                        "extras": [],
                        "total": {
                          "amount": 5000,
                          "currency": "usd"
                        }
                      }
                    ],
                    "refunds": [],
                    "reference": "SHOP-1042"
                  },
                  "previous": {
                    "status": "awaiting_payment"
                  },
                  "region": null
                }
              },
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "pattern": "^evt_[0-9A-Za-z]+$",
                    "description": "Unique identifier of the event, prefixed with `evt_`. Treat it as an opaque string: store it and compare it, never parse it.",
                    "example": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                  },
                  "object": {
                    "type": "string",
                    "enum": [
                      "event"
                    ],
                    "description": "Names the type of this object. Always `event`."
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "checkout.expired"
                    ]
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened.",
                    "example": "2026-04-21T07:34:06Z"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "object": {
                        "$ref": "#/components/schemas/IntegratorOrder"
                      },
                      "previous": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": {},
                        "description": "The fields that changed, with their values before the change. Null when nothing changed, as on creation."
                      },
                      "region": {
                        "type": "null",
                        "description": "Always null: only `stock.low` names a region."
                      }
                    },
                    "required": [
                      "object",
                      "previous",
                      "region"
                    ],
                    "description": "The object as it is now, exactly as its GET endpoint returns it, and what changed."
                  }
                },
                "required": [
                  "id",
                  "object",
                  "type",
                  "created_at",
                  "data"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the event."
          }
        }
      }
    },
    "qc_ticket.needs_your_action": {
      "post": {
        "operationId": "qc_ticket_needs_your_action",
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Webhooks"
        ],
        "summary": "QC ticket needs your action",
        "description": "A quality check has photos waiting for your review, or a production partner asked you a question. Every delivery is a POST with a JSON event and three headers: `X-Dapi-Webhook-Signature: sha256=<hex>`, an HMAC-SHA256 of `<timestamp>.<raw body>` with your endpoint secret; `X-Dapi-Webhook-Timestamp`, the Unix time in seconds it was signed (every attempt is signed again; reject a timestamp more than 5 minutes from your clock to stop replays); and `X-Dapi-Event-Id`, the event id. Answer with any 2xx within 10 seconds. Failed deliveries are retried with backoff for 24 hours, so the same event can arrive more than once: deduplicate on the event id.",
        "requestBody": {
          "description": "The event.",
          "content": {
            "application/json": {
              "example": {
                "id": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                "object": "event",
                "type": "qc_ticket.needs_your_action",
                "created_at": "2026-10-14T10:02:00Z",
                "data": {
                  "object": {
                    "id": "qct_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "qc_ticket",
                    "kind": "quality_check",
                    "reason": "spot_check",
                    "status": "awaiting_your_review",
                    "needs_your_action": true,
                    "blocks_shipping": true,
                    "order": {
                      "id": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "order",
                      "name": "PV-SCN-1701A372"
                    },
                    "round": 1,
                    "replacement": null,
                    "created_at": "2026-10-09T15:20:00Z",
                    "submitted_at": "2026-10-10T09:01:00Z",
                    "resolved_at": null,
                    "items": [
                      {
                        "id": "qci_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "qc_item",
                        "title": "Clicker Blue",
                        "product": {
                          "id": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "product",
                          "name": "Clicker"
                        },
                        "variant": {
                          "id": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "variant",
                          "name": "Clicker Blue"
                        },
                        "status": "in_review",
                        "round": 1,
                        "photos": [
                          {
                            "id": "qcp_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                            "url": "https://files.example.com/qc/clicker-front.jpg?signature=abc",
                            "round": 1,
                            "uploaded_at": "2026-10-10T09:00:00Z"
                          }
                        ],
                        "history": [
                          {
                            "action": "submitted",
                            "by": "production_partner",
                            "round": 1,
                            "note": null,
                            "at": "2026-10-10T09:01:00Z"
                          }
                        ]
                      }
                    ]
                  },
                  "previous": {
                    "status": "awaiting_photos"
                  },
                  "region": null
                }
              },
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "pattern": "^evt_[0-9A-Za-z]+$",
                    "description": "Unique identifier of the event, prefixed with `evt_`. Treat it as an opaque string: store it and compare it, never parse it.",
                    "example": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                  },
                  "object": {
                    "type": "string",
                    "enum": [
                      "event"
                    ],
                    "description": "Names the type of this object. Always `event`."
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "qc_ticket.needs_your_action"
                    ]
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened.",
                    "example": "2026-04-21T07:34:06Z"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "object": {
                        "$ref": "#/components/schemas/QcTicket"
                      },
                      "previous": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": {},
                        "description": "The fields that changed, with their values before the change. Null when nothing changed, as on creation."
                      },
                      "region": {
                        "type": "null",
                        "description": "Always null: only `stock.low` names a region."
                      }
                    },
                    "required": [
                      "object",
                      "previous",
                      "region"
                    ],
                    "description": "The object as it is now, exactly as its GET endpoint returns it, and what changed."
                  }
                },
                "required": [
                  "id",
                  "object",
                  "type",
                  "created_at",
                  "data"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the event."
          }
        }
      }
    },
    "qc_ticket.resolved": {
      "post": {
        "operationId": "qc_ticket_resolved",
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Webhooks"
        ],
        "summary": "QC ticket resolved",
        "description": "A QC ticket was approved, closed or cancelled. Every delivery is a POST with a JSON event and three headers: `X-Dapi-Webhook-Signature: sha256=<hex>`, an HMAC-SHA256 of `<timestamp>.<raw body>` with your endpoint secret; `X-Dapi-Webhook-Timestamp`, the Unix time in seconds it was signed (every attempt is signed again; reject a timestamp more than 5 minutes from your clock to stop replays); and `X-Dapi-Event-Id`, the event id. Answer with any 2xx within 10 seconds. Failed deliveries are retried with backoff for 24 hours, so the same event can arrive more than once: deduplicate on the event id.",
        "requestBody": {
          "description": "The event.",
          "content": {
            "application/json": {
              "example": {
                "id": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                "object": "event",
                "type": "qc_ticket.resolved",
                "created_at": "2026-10-14T10:02:00Z",
                "data": {
                  "object": {
                    "id": "qct_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "qc_ticket",
                    "kind": "quality_check",
                    "reason": "spot_check",
                    "status": "approved",
                    "needs_your_action": false,
                    "blocks_shipping": false,
                    "order": {
                      "id": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "order",
                      "name": "PV-SCN-1701A372"
                    },
                    "round": 1,
                    "replacement": null,
                    "created_at": "2026-10-09T15:20:00Z",
                    "submitted_at": "2026-10-10T09:01:00Z",
                    "resolved_at": null,
                    "items": [
                      {
                        "id": "qci_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "qc_item",
                        "title": "Clicker Blue",
                        "product": {
                          "id": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "product",
                          "name": "Clicker"
                        },
                        "variant": {
                          "id": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "variant",
                          "name": "Clicker Blue"
                        },
                        "status": "in_review",
                        "round": 1,
                        "photos": [
                          {
                            "id": "qcp_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                            "url": "https://files.example.com/qc/clicker-front.jpg?signature=abc",
                            "round": 1,
                            "uploaded_at": "2026-10-10T09:00:00Z"
                          }
                        ],
                        "history": [
                          {
                            "action": "submitted",
                            "by": "production_partner",
                            "round": 1,
                            "note": null,
                            "at": "2026-10-10T09:01:00Z"
                          }
                        ]
                      }
                    ]
                  },
                  "previous": {
                    "status": "awaiting_your_review"
                  },
                  "region": null
                }
              },
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "pattern": "^evt_[0-9A-Za-z]+$",
                    "description": "Unique identifier of the event, prefixed with `evt_`. Treat it as an opaque string: store it and compare it, never parse it.",
                    "example": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                  },
                  "object": {
                    "type": "string",
                    "enum": [
                      "event"
                    ],
                    "description": "Names the type of this object. Always `event`."
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "qc_ticket.resolved"
                    ]
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened.",
                    "example": "2026-04-21T07:34:06Z"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "object": {
                        "$ref": "#/components/schemas/QcTicket"
                      },
                      "previous": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": {},
                        "description": "The fields that changed, with their values before the change. Null when nothing changed, as on creation."
                      },
                      "region": {
                        "type": "null",
                        "description": "Always null: only `stock.low` names a region."
                      }
                    },
                    "required": [
                      "object",
                      "previous",
                      "region"
                    ],
                    "description": "The object as it is now, exactly as its GET endpoint returns it, and what changed."
                  }
                },
                "required": [
                  "id",
                  "object",
                  "type",
                  "created_at",
                  "data"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the event."
          }
        }
      }
    },
    "product.approved": {
      "post": {
        "operationId": "product_approved",
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Webhooks"
        ],
        "summary": "Product approved",
        "description": "3DAPI approved every design and extra in a product. Every delivery is a POST with a JSON event and three headers: `X-Dapi-Webhook-Signature: sha256=<hex>`, an HMAC-SHA256 of `<timestamp>.<raw body>` with your endpoint secret; `X-Dapi-Webhook-Timestamp`, the Unix time in seconds it was signed (every attempt is signed again; reject a timestamp more than 5 minutes from your clock to stop replays); and `X-Dapi-Event-Id`, the event id. Answer with any 2xx within 10 seconds. Failed deliveries are retried with backoff for 24 hours, so the same event can arrive more than once: deduplicate on the event id.",
        "requestBody": {
          "description": "The event.",
          "content": {
            "application/json": {
              "example": {
                "id": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                "object": "event",
                "type": "product.approved",
                "created_at": "2026-10-14T10:02:00Z",
                "data": {
                  "object": {
                    "id": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "product",
                    "name": "PR Tracker",
                    "slug": "pr-tracker",
                    "category": {
                      "id": "cat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "category",
                      "name": "Desk accessories"
                    },
                    "review": {
                      "status": "approved",
                      "reason": null
                    },
                    "publishing": {
                      "is_public": true,
                      "unpublished_by": null,
                      "can_publish": true,
                      "blockers": []
                    },
                    "variant_count": 1,
                    "price_range": {
                      "min": {
                        "amount": 5000,
                        "currency": "usd"
                      },
                      "max": {
                        "amount": 5000,
                        "currency": "usd"
                      }
                    },
                    "images": [
                      {
                        "url": "https://cdn.example.com/tracker.png",
                        "primary": true
                      }
                    ],
                    "created_at": "2026-04-21T07:34:06Z",
                    "updated_at": "2026-10-02T11:12:40Z",
                    "description": "A desk tracker for your personal records.",
                    "options": [
                      {
                        "name": "Color",
                        "values": [
                          "Black",
                          "Blue"
                        ]
                      }
                    ],
                    "variants": [
                      {
                        "id": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "variant",
                        "sku": "TRACKER-BLUE",
                        "name": "PR Tracker Blue",
                        "status": "active",
                        "options": [
                          {
                            "name": "Color",
                            "value": "Blue"
                          }
                        ],
                        "category": null,
                        "pricing": {
                          "price": {
                            "amount": 5000,
                            "currency": "usd"
                          },
                          "prices_include_vat": true,
                          "costs": {
                            "manufacturing": {
                              "amount": 332,
                              "currency": "usd"
                            },
                            "extra_parts": {
                              "amount": 40,
                              "currency": "usd"
                            },
                            "platform_fee": {
                              "amount": 250,
                              "currency": "usd"
                            }
                          },
                          "earnings": {
                            "amount": 4378,
                            "currency": "usd"
                          }
                        },
                        "shipping": {
                          "tier": "medium",
                          "determined_by": "volume",
                          "chargeable_weight_grams": 820,
                          "extra_small": false
                        },
                        "contents": [
                          {
                            "type": "design",
                            "quantity": 1,
                            "design": {
                              "id": "dsg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                              "object": "design",
                              "name": "tracker.3mf"
                            },
                            "materials": [
                              {
                                "id": "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                                "object": "material",
                                "name": "PLA Blue",
                                "custom": false
                              }
                            ]
                          },
                          {
                            "type": "extra",
                            "quantity": 4,
                            "unit_price": {
                              "amount": 10,
                              "currency": "usd"
                            },
                            "extra": {
                              "id": "xtr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                              "object": "extra",
                              "name": "M3 screw"
                            }
                          }
                        ],
                        "image": null
                      }
                    ],
                    "attachments": [
                      {
                        "id": "att_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "attachment",
                        "name": "assembly-guide.pdf"
                      }
                    ],
                    "storefront": {
                      "url": "https://dapi.digital/store/acme/pr-tracker",
                      "show_compliance_labels": true,
                      "disclaimers": [
                        {
                          "title": "Production safety",
                          "text": "Keep away from heat."
                        }
                      ]
                    },
                    "custom_label_url": null,
                    "compliance": {
                      "object": "product_compliance",
                      "enforced": false,
                      "complete": false,
                      "missing": 1
                    }
                  },
                  "previous": {
                    "review": {
                      "status": "pending",
                      "reason": null
                    }
                  },
                  "region": null
                }
              },
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "pattern": "^evt_[0-9A-Za-z]+$",
                    "description": "Unique identifier of the event, prefixed with `evt_`. Treat it as an opaque string: store it and compare it, never parse it.",
                    "example": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                  },
                  "object": {
                    "type": "string",
                    "enum": [
                      "event"
                    ],
                    "description": "Names the type of this object. Always `event`."
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "product.approved"
                    ]
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened.",
                    "example": "2026-04-21T07:34:06Z"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "object": {
                        "$ref": "#/components/schemas/Product"
                      },
                      "previous": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": {},
                        "description": "The fields that changed, with their values before the change. Null when nothing changed, as on creation."
                      },
                      "region": {
                        "type": "null",
                        "description": "Always null: only `stock.low` names a region."
                      }
                    },
                    "required": [
                      "object",
                      "previous",
                      "region"
                    ],
                    "description": "The object as it is now, exactly as its GET endpoint returns it, and what changed."
                  }
                },
                "required": [
                  "id",
                  "object",
                  "type",
                  "created_at",
                  "data"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the event."
          }
        }
      }
    },
    "product.rejected": {
      "post": {
        "operationId": "product_rejected",
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Webhooks"
        ],
        "summary": "Product rejected",
        "description": "3DAPI rejected a design or extra in a product; `review.reason` says why. Every delivery is a POST with a JSON event and three headers: `X-Dapi-Webhook-Signature: sha256=<hex>`, an HMAC-SHA256 of `<timestamp>.<raw body>` with your endpoint secret; `X-Dapi-Webhook-Timestamp`, the Unix time in seconds it was signed (every attempt is signed again; reject a timestamp more than 5 minutes from your clock to stop replays); and `X-Dapi-Event-Id`, the event id. Answer with any 2xx within 10 seconds. Failed deliveries are retried with backoff for 24 hours, so the same event can arrive more than once: deduplicate on the event id.",
        "requestBody": {
          "description": "The event.",
          "content": {
            "application/json": {
              "example": {
                "id": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                "object": "event",
                "type": "product.rejected",
                "created_at": "2026-10-14T10:02:00Z",
                "data": {
                  "object": {
                    "id": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "product",
                    "name": "PR Tracker",
                    "slug": "pr-tracker",
                    "category": {
                      "id": "cat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "category",
                      "name": "Desk accessories"
                    },
                    "review": {
                      "status": "approved",
                      "reason": null
                    },
                    "publishing": {
                      "is_public": true,
                      "unpublished_by": null,
                      "can_publish": true,
                      "blockers": []
                    },
                    "variant_count": 1,
                    "price_range": {
                      "min": {
                        "amount": 5000,
                        "currency": "usd"
                      },
                      "max": {
                        "amount": 5000,
                        "currency": "usd"
                      }
                    },
                    "images": [
                      {
                        "url": "https://cdn.example.com/tracker.png",
                        "primary": true
                      }
                    ],
                    "created_at": "2026-04-21T07:34:06Z",
                    "updated_at": "2026-10-02T11:12:40Z",
                    "description": "A desk tracker for your personal records.",
                    "options": [
                      {
                        "name": "Color",
                        "values": [
                          "Black",
                          "Blue"
                        ]
                      }
                    ],
                    "variants": [
                      {
                        "id": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "variant",
                        "sku": "TRACKER-BLUE",
                        "name": "PR Tracker Blue",
                        "status": "active",
                        "options": [
                          {
                            "name": "Color",
                            "value": "Blue"
                          }
                        ],
                        "category": null,
                        "pricing": {
                          "price": {
                            "amount": 5000,
                            "currency": "usd"
                          },
                          "prices_include_vat": true,
                          "costs": {
                            "manufacturing": {
                              "amount": 332,
                              "currency": "usd"
                            },
                            "extra_parts": {
                              "amount": 40,
                              "currency": "usd"
                            },
                            "platform_fee": {
                              "amount": 250,
                              "currency": "usd"
                            }
                          },
                          "earnings": {
                            "amount": 4378,
                            "currency": "usd"
                          }
                        },
                        "shipping": {
                          "tier": "medium",
                          "determined_by": "volume",
                          "chargeable_weight_grams": 820,
                          "extra_small": false
                        },
                        "contents": [
                          {
                            "type": "design",
                            "quantity": 1,
                            "design": {
                              "id": "dsg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                              "object": "design",
                              "name": "tracker.3mf"
                            },
                            "materials": [
                              {
                                "id": "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                                "object": "material",
                                "name": "PLA Blue",
                                "custom": false
                              }
                            ]
                          },
                          {
                            "type": "extra",
                            "quantity": 4,
                            "unit_price": {
                              "amount": 10,
                              "currency": "usd"
                            },
                            "extra": {
                              "id": "xtr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                              "object": "extra",
                              "name": "M3 screw"
                            }
                          }
                        ],
                        "image": null
                      }
                    ],
                    "attachments": [
                      {
                        "id": "att_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "attachment",
                        "name": "assembly-guide.pdf"
                      }
                    ],
                    "storefront": {
                      "url": "https://dapi.digital/store/acme/pr-tracker",
                      "show_compliance_labels": true,
                      "disclaimers": [
                        {
                          "title": "Production safety",
                          "text": "Keep away from heat."
                        }
                      ]
                    },
                    "custom_label_url": null,
                    "compliance": {
                      "object": "product_compliance",
                      "enforced": false,
                      "complete": false,
                      "missing": 1
                    }
                  },
                  "previous": {
                    "review": {
                      "status": "pending",
                      "reason": null
                    }
                  },
                  "region": null
                }
              },
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "pattern": "^evt_[0-9A-Za-z]+$",
                    "description": "Unique identifier of the event, prefixed with `evt_`. Treat it as an opaque string: store it and compare it, never parse it.",
                    "example": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                  },
                  "object": {
                    "type": "string",
                    "enum": [
                      "event"
                    ],
                    "description": "Names the type of this object. Always `event`."
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "product.rejected"
                    ]
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened.",
                    "example": "2026-04-21T07:34:06Z"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "object": {
                        "$ref": "#/components/schemas/Product"
                      },
                      "previous": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": {},
                        "description": "The fields that changed, with their values before the change. Null when nothing changed, as on creation."
                      },
                      "region": {
                        "type": "null",
                        "description": "Always null: only `stock.low` names a region."
                      }
                    },
                    "required": [
                      "object",
                      "previous",
                      "region"
                    ],
                    "description": "The object as it is now, exactly as its GET endpoint returns it, and what changed."
                  }
                },
                "required": [
                  "id",
                  "object",
                  "type",
                  "created_at",
                  "data"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the event."
          }
        }
      }
    },
    "stock_batch.purchased": {
      "post": {
        "operationId": "stock_batch_purchased",
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Webhooks"
        ],
        "summary": "Stock batch purchased",
        "description": "A stock batch was bought and queued for production, by you or by auto-reorder; `origin` says which. One checkout buying several batches sends one event per batch, sharing `purchase_reference`. Every delivery is a POST with a JSON event and three headers: `X-Dapi-Webhook-Signature: sha256=<hex>`, an HMAC-SHA256 of `<timestamp>.<raw body>` with your endpoint secret; `X-Dapi-Webhook-Timestamp`, the Unix time in seconds it was signed (every attempt is signed again; reject a timestamp more than 5 minutes from your clock to stop replays); and `X-Dapi-Event-Id`, the event id. Answer with any 2xx within 10 seconds. Failed deliveries are retried with backoff for 24 hours, so the same event can arrive more than once: deduplicate on the event id.",
        "requestBody": {
          "description": "The event.",
          "content": {
            "application/json": {
              "example": {
                "id": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                "object": "event",
                "type": "stock_batch.purchased",
                "created_at": "2026-10-14T10:02:00Z",
                "data": {
                  "object": {
                    "id": "bat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "stock_batch",
                    "stock": {
                      "id": "stk_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "stock",
                      "name": "Clicker 50 pack"
                    },
                    "region": {
                      "id": "reg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "region",
                      "name": "Western Europe"
                    },
                    "status": "queued",
                    "batches": 2,
                    "units": {
                      "purchased": 100,
                      "made": 60,
                      "used": 20,
                      "available": 40
                    },
                    "price_paid": {
                      "amount": 60000,
                      "currency": "usd"
                    },
                    "unit_price": {
                      "amount": 600,
                      "currency": "usd"
                    },
                    "purchase_reference": "ST-8KD2X",
                    "origin": "manual",
                    "purchased_at": "2026-09-30T10:00:00Z",
                    "completed_at": null
                  },
                  "previous": null,
                  "region": null
                }
              },
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "pattern": "^evt_[0-9A-Za-z]+$",
                    "description": "Unique identifier of the event, prefixed with `evt_`. Treat it as an opaque string: store it and compare it, never parse it.",
                    "example": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                  },
                  "object": {
                    "type": "string",
                    "enum": [
                      "event"
                    ],
                    "description": "Names the type of this object. Always `event`."
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "stock_batch.purchased"
                    ]
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened.",
                    "example": "2026-04-21T07:34:06Z"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "object": {
                        "$ref": "#/components/schemas/StockBatch"
                      },
                      "previous": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": {},
                        "description": "The fields that changed, with their values before the change. Null when nothing changed, as on creation."
                      },
                      "region": {
                        "type": "null",
                        "description": "Always null: only `stock.low` names a region."
                      }
                    },
                    "required": [
                      "object",
                      "previous",
                      "region"
                    ],
                    "description": "The object as it is now, exactly as its GET endpoint returns it, and what changed."
                  }
                },
                "required": [
                  "id",
                  "object",
                  "type",
                  "created_at",
                  "data"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the event."
          }
        }
      }
    },
    "stock_batch.completed": {
      "post": {
        "operationId": "stock_batch_completed",
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Webhooks"
        ],
        "summary": "Stock batch completed",
        "description": "Every unit of a stock batch has been made. Every delivery is a POST with a JSON event and three headers: `X-Dapi-Webhook-Signature: sha256=<hex>`, an HMAC-SHA256 of `<timestamp>.<raw body>` with your endpoint secret; `X-Dapi-Webhook-Timestamp`, the Unix time in seconds it was signed (every attempt is signed again; reject a timestamp more than 5 minutes from your clock to stop replays); and `X-Dapi-Event-Id`, the event id. Answer with any 2xx within 10 seconds. Failed deliveries are retried with backoff for 24 hours, so the same event can arrive more than once: deduplicate on the event id.",
        "requestBody": {
          "description": "The event.",
          "content": {
            "application/json": {
              "example": {
                "id": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                "object": "event",
                "type": "stock_batch.completed",
                "created_at": "2026-10-14T10:02:00Z",
                "data": {
                  "object": {
                    "id": "bat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "stock_batch",
                    "stock": {
                      "id": "stk_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "stock",
                      "name": "Clicker 50 pack"
                    },
                    "region": {
                      "id": "reg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                      "object": "region",
                      "name": "Western Europe"
                    },
                    "status": "completed",
                    "batches": 2,
                    "units": {
                      "purchased": 100,
                      "made": 60,
                      "used": 20,
                      "available": 40
                    },
                    "price_paid": {
                      "amount": 60000,
                      "currency": "usd"
                    },
                    "unit_price": {
                      "amount": 600,
                      "currency": "usd"
                    },
                    "purchase_reference": "ST-8KD2X",
                    "origin": "manual",
                    "purchased_at": "2026-09-30T10:00:00Z",
                    "completed_at": null
                  },
                  "previous": {
                    "status": "in_production"
                  },
                  "region": null
                }
              },
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "pattern": "^evt_[0-9A-Za-z]+$",
                    "description": "Unique identifier of the event, prefixed with `evt_`. Treat it as an opaque string: store it and compare it, never parse it.",
                    "example": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                  },
                  "object": {
                    "type": "string",
                    "enum": [
                      "event"
                    ],
                    "description": "Names the type of this object. Always `event`."
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "stock_batch.completed"
                    ]
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened.",
                    "example": "2026-04-21T07:34:06Z"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "object": {
                        "$ref": "#/components/schemas/StockBatch"
                      },
                      "previous": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": {},
                        "description": "The fields that changed, with their values before the change. Null when nothing changed, as on creation."
                      },
                      "region": {
                        "type": "null",
                        "description": "Always null: only `stock.low` names a region."
                      }
                    },
                    "required": [
                      "object",
                      "previous",
                      "region"
                    ],
                    "description": "The object as it is now, exactly as its GET endpoint returns it, and what changed."
                  }
                },
                "required": [
                  "id",
                  "object",
                  "type",
                  "created_at",
                  "data"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the event."
          }
        }
      }
    },
    "stock.low": {
      "post": {
        "operationId": "stock_low",
        "x-audience": [
          "designer"
        ],
        "tags": [
          "Webhooks"
        ],
        "summary": "Stock running low",
        "description": "A region reached the restock threshold you set (`restock[].when_available_below`, checked when an order draws units). `data.region` names the region. Every delivery is a POST with a JSON event and three headers: `X-Dapi-Webhook-Signature: sha256=<hex>`, an HMAC-SHA256 of `<timestamp>.<raw body>` with your endpoint secret; `X-Dapi-Webhook-Timestamp`, the Unix time in seconds it was signed (every attempt is signed again; reject a timestamp more than 5 minutes from your clock to stop replays); and `X-Dapi-Event-Id`, the event id. Answer with any 2xx within 10 seconds. Failed deliveries are retried with backoff for 24 hours, so the same event can arrive more than once: deduplicate on the event id.",
        "requestBody": {
          "description": "The event.",
          "content": {
            "application/json": {
              "example": {
                "id": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                "object": "event",
                "type": "stock.low",
                "created_at": "2026-10-14T10:02:00Z",
                "data": {
                  "object": {
                    "id": "stk_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "stock",
                    "name": "Clicker 50 pack",
                    "description": "Pre-assembled clickers, bagged.",
                    "review": {
                      "status": "approved",
                      "reason": null,
                      "reviewed_at": "2026-05-02T10:00:00Z"
                    },
                    "units_per_batch": 50,
                    "batch_price": {
                      "amount": 30000,
                      "currency": "usd"
                    },
                    "unit_price": {
                      "amount": 600,
                      "currency": "usd"
                    },
                    "contents": [
                      {
                        "type": "design",
                        "design": {
                          "id": "dsg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "design",
                          "name": "clicker.3mf"
                        }
                      },
                      {
                        "type": "extra",
                        "extra": {
                          "id": "xtr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "extra",
                          "name": "M3 screw"
                        }
                      }
                    ],
                    "inventory": {
                      "available": 32,
                      "in_production": 18,
                      "by_region": [
                        {
                          "region": {
                            "id": "reg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                            "object": "region",
                            "name": "Western Europe"
                          },
                          "available": 32,
                          "in_production": 18
                        }
                      ]
                    },
                    "restock": [
                      {
                        "region": {
                          "id": "reg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "region",
                          "name": "Western Europe"
                        },
                        "mode": "auto_buy",
                        "when_available_below": 10,
                        "reorder_units": 50
                      }
                    ],
                    "images": [
                      {
                        "url": "https://cdn.example.com/clicker.png",
                        "primary": true
                      }
                    ],
                    "attachments": [
                      {
                        "id": "att_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                        "object": "attachment",
                        "name": "packing-guide.pdf"
                      }
                    ],
                    "created_at": "2026-04-30T12:00:00Z",
                    "updated_at": "2026-05-02T10:00:00Z"
                  },
                  "previous": null,
                  "region": {
                    "id": "reg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "region",
                    "name": "Western Europe"
                  }
                }
              },
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "pattern": "^evt_[0-9A-Za-z]+$",
                    "description": "Unique identifier of the event, prefixed with `evt_`. Treat it as an opaque string: store it and compare it, never parse it.",
                    "example": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                  },
                  "object": {
                    "type": "string",
                    "enum": [
                      "event"
                    ],
                    "description": "Names the type of this object. Always `event`."
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "stock.low"
                    ]
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened.",
                    "example": "2026-04-21T07:34:06Z"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "object": {
                        "$ref": "#/components/schemas/Stock"
                      },
                      "previous": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": {},
                        "description": "The fields that changed, with their values before the change. Null when nothing changed, as on creation."
                      },
                      "region": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "pattern": "^reg_[0-9A-Za-z]+$",
                            "description": "Unique identifier of the region, prefixed with `reg_`. Treat it as an opaque string: store it and compare it, never parse it.",
                            "example": "reg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                          },
                          "object": {
                            "type": "string",
                            "enum": [
                              "region"
                            ],
                            "description": "Names the type of this object. Always `region`."
                          },
                          "name": {
                            "type": "string",
                            "description": "Display name of the region."
                          }
                        },
                        "required": [
                          "id",
                          "object",
                          "name"
                        ],
                        "description": "The region that ran low."
                      }
                    },
                    "required": [
                      "object",
                      "previous",
                      "region"
                    ],
                    "description": "The object as it is now, exactly as its GET endpoint returns it, and what changed."
                  }
                },
                "required": [
                  "id",
                  "object",
                  "type",
                  "created_at",
                  "data"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the event."
          }
        }
      }
    },
    "quote.ready": {
      "post": {
        "operationId": "quote_ready",
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Webhooks"
        ],
        "summary": "Quote ready",
        "description": "Integrators only. Slicing finished and the quote is priced: `price` is set, for the file that gets printed (`file.production_version` when 3DAPI adapted your upload, otherwise the upload). Sent once per quote. `data.object` is the quote as get_quote returns it; its download links were signed when the event was sent and expire 15 minutes later, so fetch the quote for fresh ones. Every delivery is a POST with a JSON event and three headers: `X-Dapi-Webhook-Signature: sha256=<hex>`, an HMAC-SHA256 of `<timestamp>.<raw body>` with your endpoint secret; `X-Dapi-Webhook-Timestamp`, the Unix time in seconds it was signed (every attempt is signed again; reject a timestamp more than 5 minutes from your clock to stop replays); and `X-Dapi-Event-Id`, the event id. Answer with any 2xx within 10 seconds. Failed deliveries are retried with backoff for 24 hours, so the same event can arrive more than once: deduplicate on the event id.",
        "requestBody": {
          "description": "The event.",
          "content": {
            "application/json": {
              "example": {
                "id": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                "object": "event",
                "type": "quote.ready",
                "created_at": "2026-10-14T10:02:00Z",
                "data": {
                  "object": {
                    "id": "qte_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "quote",
                    "name": "Phone stand v2",
                    "file": {
                      "name": "phone-stand-v2.3mf",
                      "size_bytes": 2758723,
                      "download_url": "https://files.example.com/public-api/quotes/7f3a/input/phone-stand-v2.3mf?signature=abc",
                      "production_version": {
                        "name": "phone-stand-v2.3mf",
                        "format": "3mf",
                        "size_bytes": 2913004,
                        "download_url": "https://files.example.com/public-api/quotes/7f3a/production/phone-stand-v2.3mf?signature=def",
                        "prepared_at": "2026-10-20T09:12:09Z"
                      }
                    },
                    "status": "ready",
                    "suggested_materials": [
                      {
                        "slot": 1,
                        "file_color": "#C12E1F",
                        "file_filament_type": "PLA",
                        "material": {
                          "id": "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "material",
                          "name": "PLA Red",
                          "custom": false
                        },
                        "match": "exact"
                      },
                      {
                        "slot": 3,
                        "file_color": "#0B2A88",
                        "file_filament_type": null,
                        "material": {
                          "id": "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "material",
                          "name": "PLA Blue",
                          "custom": false
                        },
                        "match": "close"
                      }
                    ],
                    "price": {
                      "amount": 1245,
                      "currency": "usd"
                    },
                    "failure_reason": null,
                    "approval": {
                      "status": "not_submitted",
                      "reason": null
                    },
                    "created_at": "2026-10-20T09:12:00Z",
                    "finished_at": "2026-10-20T09:13:41Z"
                  },
                  "previous": {
                    "status": "processing"
                  },
                  "region": null
                }
              },
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "pattern": "^evt_[0-9A-Za-z]+$",
                    "description": "Unique identifier of the event, prefixed with `evt_`. Treat it as an opaque string: store it and compare it, never parse it.",
                    "example": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                  },
                  "object": {
                    "type": "string",
                    "enum": [
                      "event"
                    ],
                    "description": "Names the type of this object. Always `event`."
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "quote.ready"
                    ]
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened.",
                    "example": "2026-04-21T07:34:06Z"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "object": {
                        "$ref": "#/components/schemas/Quote"
                      },
                      "previous": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": {},
                        "description": "The fields that changed, with their values before the change. Null when nothing changed, as on creation."
                      },
                      "region": {
                        "type": "null",
                        "description": "Always null: only `stock.low` names a region."
                      }
                    },
                    "required": [
                      "object",
                      "previous",
                      "region"
                    ],
                    "description": "The object as it is now, exactly as its GET endpoint returns it, and what changed."
                  }
                },
                "required": [
                  "id",
                  "object",
                  "type",
                  "created_at",
                  "data"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the event."
          }
        }
      }
    },
    "quote.failed": {
      "post": {
        "operationId": "quote_failed",
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Webhooks"
        ],
        "summary": "Quote failed",
        "description": "Integrators only. The file could not be priced: slicing failed, took too long and was given up, or could not be started. `failure_reason` says why; upload a corrected file as a new quote. A failed quote no longer counts toward your upload allowance. Every delivery is a POST with a JSON event and three headers: `X-Dapi-Webhook-Signature: sha256=<hex>`, an HMAC-SHA256 of `<timestamp>.<raw body>` with your endpoint secret; `X-Dapi-Webhook-Timestamp`, the Unix time in seconds it was signed (every attempt is signed again; reject a timestamp more than 5 minutes from your clock to stop replays); and `X-Dapi-Event-Id`, the event id. Answer with any 2xx within 10 seconds. Failed deliveries are retried with backoff for 24 hours, so the same event can arrive more than once: deduplicate on the event id.",
        "requestBody": {
          "description": "The event.",
          "content": {
            "application/json": {
              "example": {
                "id": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                "object": "event",
                "type": "quote.failed",
                "created_at": "2026-10-14T10:02:00Z",
                "data": {
                  "object": {
                    "id": "qte_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "quote",
                    "name": "Phone stand v2",
                    "file": {
                      "name": "phone-stand-v2.3mf",
                      "size_bytes": 2758723,
                      "download_url": "https://files.example.com/public-api/quotes/7f3a/input/phone-stand-v2.3mf?signature=abc",
                      "production_version": {
                        "name": "phone-stand-v2.3mf",
                        "format": "3mf",
                        "size_bytes": 2913004,
                        "download_url": "https://files.example.com/public-api/quotes/7f3a/production/phone-stand-v2.3mf?signature=def",
                        "prepared_at": "2026-10-20T09:12:09Z"
                      }
                    },
                    "status": "failed",
                    "suggested_materials": [
                      {
                        "slot": 1,
                        "file_color": "#C12E1F",
                        "file_filament_type": "PLA",
                        "material": {
                          "id": "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "material",
                          "name": "PLA Red",
                          "custom": false
                        },
                        "match": "exact"
                      },
                      {
                        "slot": 3,
                        "file_color": "#0B2A88",
                        "file_filament_type": null,
                        "material": {
                          "id": "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "material",
                          "name": "PLA Blue",
                          "custom": false
                        },
                        "match": "close"
                      }
                    ],
                    "price": null,
                    "failure_reason": "Slicing did not complete within 15 minutes. The quote was not produced; please try again.",
                    "approval": {
                      "status": "not_submitted",
                      "reason": null
                    },
                    "created_at": "2026-10-20T09:12:00Z",
                    "finished_at": "2026-10-20T09:13:41Z"
                  },
                  "previous": {
                    "status": "processing"
                  },
                  "region": null
                }
              },
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "pattern": "^evt_[0-9A-Za-z]+$",
                    "description": "Unique identifier of the event, prefixed with `evt_`. Treat it as an opaque string: store it and compare it, never parse it.",
                    "example": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                  },
                  "object": {
                    "type": "string",
                    "enum": [
                      "event"
                    ],
                    "description": "Names the type of this object. Always `event`."
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "quote.failed"
                    ]
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened.",
                    "example": "2026-04-21T07:34:06Z"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "object": {
                        "$ref": "#/components/schemas/Quote"
                      },
                      "previous": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": {},
                        "description": "The fields that changed, with their values before the change. Null when nothing changed, as on creation."
                      },
                      "region": {
                        "type": "null",
                        "description": "Always null: only `stock.low` names a region."
                      }
                    },
                    "required": [
                      "object",
                      "previous",
                      "region"
                    ],
                    "description": "The object as it is now, exactly as its GET endpoint returns it, and what changed."
                  }
                },
                "required": [
                  "id",
                  "object",
                  "type",
                  "created_at",
                  "data"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the event."
          }
        }
      }
    },
    "quote.approved": {
      "post": {
        "operationId": "quote_approved",
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Webhooks"
        ],
        "summary": "Quote approved",
        "description": "Integrators only. 3DAPI approved the file in your approval request: `approval.status` is `approved` and the file counts toward your account approval. Sent when 3DAPI completes the review of your approval request, with the final decision on the file, just before the outcome event. Every delivery is a POST with a JSON event and three headers: `X-Dapi-Webhook-Signature: sha256=<hex>`, an HMAC-SHA256 of `<timestamp>.<raw body>` with your endpoint secret; `X-Dapi-Webhook-Timestamp`, the Unix time in seconds it was signed (every attempt is signed again; reject a timestamp more than 5 minutes from your clock to stop replays); and `X-Dapi-Event-Id`, the event id. Answer with any 2xx within 10 seconds. Failed deliveries are retried with backoff for 24 hours, so the same event can arrive more than once: deduplicate on the event id.",
        "requestBody": {
          "description": "The event.",
          "content": {
            "application/json": {
              "example": {
                "id": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                "object": "event",
                "type": "quote.approved",
                "created_at": "2026-10-14T10:02:00Z",
                "data": {
                  "object": {
                    "id": "qte_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "quote",
                    "name": "Phone stand v2",
                    "file": {
                      "name": "phone-stand-v2.3mf",
                      "size_bytes": 2758723,
                      "download_url": "https://files.example.com/public-api/quotes/7f3a/input/phone-stand-v2.3mf?signature=abc",
                      "production_version": {
                        "name": "phone-stand-v2.3mf",
                        "format": "3mf",
                        "size_bytes": 2913004,
                        "download_url": "https://files.example.com/public-api/quotes/7f3a/production/phone-stand-v2.3mf?signature=def",
                        "prepared_at": "2026-10-20T09:12:09Z"
                      }
                    },
                    "status": "ready",
                    "suggested_materials": [
                      {
                        "slot": 1,
                        "file_color": "#C12E1F",
                        "file_filament_type": "PLA",
                        "material": {
                          "id": "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "material",
                          "name": "PLA Red",
                          "custom": false
                        },
                        "match": "exact"
                      },
                      {
                        "slot": 3,
                        "file_color": "#0B2A88",
                        "file_filament_type": null,
                        "material": {
                          "id": "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "material",
                          "name": "PLA Blue",
                          "custom": false
                        },
                        "match": "close"
                      }
                    ],
                    "price": {
                      "amount": 1245,
                      "currency": "usd"
                    },
                    "failure_reason": null,
                    "approval": {
                      "status": "approved",
                      "reason": null
                    },
                    "created_at": "2026-10-20T09:12:00Z",
                    "finished_at": "2026-10-20T09:13:41Z"
                  },
                  "previous": {
                    "approval": {
                      "status": "in_review",
                      "reason": null
                    }
                  },
                  "region": null
                }
              },
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "pattern": "^evt_[0-9A-Za-z]+$",
                    "description": "Unique identifier of the event, prefixed with `evt_`. Treat it as an opaque string: store it and compare it, never parse it.",
                    "example": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                  },
                  "object": {
                    "type": "string",
                    "enum": [
                      "event"
                    ],
                    "description": "Names the type of this object. Always `event`."
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "quote.approved"
                    ]
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened.",
                    "example": "2026-04-21T07:34:06Z"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "object": {
                        "$ref": "#/components/schemas/Quote"
                      },
                      "previous": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": {},
                        "description": "The fields that changed, with their values before the change. Null when nothing changed, as on creation."
                      },
                      "region": {
                        "type": "null",
                        "description": "Always null: only `stock.low` names a region."
                      }
                    },
                    "required": [
                      "object",
                      "previous",
                      "region"
                    ],
                    "description": "The object as it is now, exactly as its GET endpoint returns it, and what changed."
                  }
                },
                "required": [
                  "id",
                  "object",
                  "type",
                  "created_at",
                  "data"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the event."
          }
        }
      }
    },
    "quote.rejected": {
      "post": {
        "operationId": "quote_rejected",
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Webhooks"
        ],
        "summary": "Quote rejected",
        "description": "Integrators only. 3DAPI rejected the file in your approval request; `approval.reason` says why. Rejected quotes cannot be submitted again: upload a corrected file as a new quote. Sent when 3DAPI completes the review of your approval request, with the final decision on the file. Every delivery is a POST with a JSON event and three headers: `X-Dapi-Webhook-Signature: sha256=<hex>`, an HMAC-SHA256 of `<timestamp>.<raw body>` with your endpoint secret; `X-Dapi-Webhook-Timestamp`, the Unix time in seconds it was signed (every attempt is signed again; reject a timestamp more than 5 minutes from your clock to stop replays); and `X-Dapi-Event-Id`, the event id. Answer with any 2xx within 10 seconds. Failed deliveries are retried with backoff for 24 hours, so the same event can arrive more than once: deduplicate on the event id.",
        "requestBody": {
          "description": "The event.",
          "content": {
            "application/json": {
              "example": {
                "id": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                "object": "event",
                "type": "quote.rejected",
                "created_at": "2026-10-14T10:02:00Z",
                "data": {
                  "object": {
                    "id": "qte_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "quote",
                    "name": "Phone stand v2",
                    "file": {
                      "name": "phone-stand-v2.3mf",
                      "size_bytes": 2758723,
                      "download_url": "https://files.example.com/public-api/quotes/7f3a/input/phone-stand-v2.3mf?signature=abc",
                      "production_version": {
                        "name": "phone-stand-v2.3mf",
                        "format": "3mf",
                        "size_bytes": 2913004,
                        "download_url": "https://files.example.com/public-api/quotes/7f3a/production/phone-stand-v2.3mf?signature=def",
                        "prepared_at": "2026-10-20T09:12:09Z"
                      }
                    },
                    "status": "ready",
                    "suggested_materials": [
                      {
                        "slot": 1,
                        "file_color": "#C12E1F",
                        "file_filament_type": "PLA",
                        "material": {
                          "id": "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "material",
                          "name": "PLA Red",
                          "custom": false
                        },
                        "match": "exact"
                      },
                      {
                        "slot": 3,
                        "file_color": "#0B2A88",
                        "file_filament_type": null,
                        "material": {
                          "id": "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "material",
                          "name": "PLA Blue",
                          "custom": false
                        },
                        "match": "close"
                      }
                    ],
                    "price": {
                      "amount": 1245,
                      "currency": "usd"
                    },
                    "failure_reason": null,
                    "approval": {
                      "status": "rejected",
                      "reason": "The model has open edges; close the mesh and upload it again."
                    },
                    "created_at": "2026-10-20T09:12:00Z",
                    "finished_at": "2026-10-20T09:13:41Z"
                  },
                  "previous": {
                    "approval": {
                      "status": "in_review",
                      "reason": null
                    }
                  },
                  "region": null
                }
              },
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "pattern": "^evt_[0-9A-Za-z]+$",
                    "description": "Unique identifier of the event, prefixed with `evt_`. Treat it as an opaque string: store it and compare it, never parse it.",
                    "example": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                  },
                  "object": {
                    "type": "string",
                    "enum": [
                      "event"
                    ],
                    "description": "Names the type of this object. Always `event`."
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "quote.rejected"
                    ]
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened.",
                    "example": "2026-04-21T07:34:06Z"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "object": {
                        "$ref": "#/components/schemas/Quote"
                      },
                      "previous": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": {},
                        "description": "The fields that changed, with their values before the change. Null when nothing changed, as on creation."
                      },
                      "region": {
                        "type": "null",
                        "description": "Always null: only `stock.low` names a region."
                      }
                    },
                    "required": [
                      "object",
                      "previous",
                      "region"
                    ],
                    "description": "The object as it is now, exactly as its GET endpoint returns it, and what changed."
                  }
                },
                "required": [
                  "id",
                  "object",
                  "type",
                  "created_at",
                  "data"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the event."
          }
        }
      }
    },
    "approval_request.approved": {
      "post": {
        "operationId": "approval_request_approved",
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Webhooks"
        ],
        "summary": "Account approved",
        "description": "Integrators only. Your account became approved: quotes are unlimited and you can place orders. Sent once, when a completed review approves the account or when 3DAPI approves it directly. `data.object` is your latest approval request as get_approval_request returns it (still `in_review` when approved directly during a review). Every delivery is a POST with a JSON event and three headers: `X-Dapi-Webhook-Signature: sha256=<hex>`, an HMAC-SHA256 of `<timestamp>.<raw body>` with your endpoint secret; `X-Dapi-Webhook-Timestamp`, the Unix time in seconds it was signed (every attempt is signed again; reject a timestamp more than 5 minutes from your clock to stop replays); and `X-Dapi-Event-Id`, the event id. Answer with any 2xx within 10 seconds. Failed deliveries are retried with backoff for 24 hours, so the same event can arrive more than once: deduplicate on the event id.",
        "requestBody": {
          "description": "The event.",
          "content": {
            "application/json": {
              "example": {
                "id": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                "object": "event",
                "type": "approval_request.approved",
                "created_at": "2026-10-14T10:02:00Z",
                "data": {
                  "object": {
                    "id": "apr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "approval_request",
                    "status": "reviewed",
                    "quotes": [
                      {
                        "quote": {
                          "id": "qte_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "quote",
                          "name": "Phone stand v2"
                        },
                        "decision": "approved",
                        "reason": null
                      }
                    ],
                    "submitted_at": "2026-10-20T10:00:00Z",
                    "reviewed_at": "2026-10-21T08:30:00Z"
                  },
                  "previous": {
                    "status": "in_review"
                  },
                  "region": null
                }
              },
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "pattern": "^evt_[0-9A-Za-z]+$",
                    "description": "Unique identifier of the event, prefixed with `evt_`. Treat it as an opaque string: store it and compare it, never parse it.",
                    "example": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                  },
                  "object": {
                    "type": "string",
                    "enum": [
                      "event"
                    ],
                    "description": "Names the type of this object. Always `event`."
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "approval_request.approved"
                    ]
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened.",
                    "example": "2026-04-21T07:34:06Z"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "object": {
                        "$ref": "#/components/schemas/ApprovalRequest"
                      },
                      "previous": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": {},
                        "description": "The fields that changed, with their values before the change. Null when nothing changed, as on creation."
                      },
                      "region": {
                        "type": "null",
                        "description": "Always null: only `stock.low` names a region."
                      }
                    },
                    "required": [
                      "object",
                      "previous",
                      "region"
                    ],
                    "description": "The object as it is now, exactly as its GET endpoint returns it, and what changed."
                  }
                },
                "required": [
                  "id",
                  "object",
                  "type",
                  "created_at",
                  "data"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the event."
          }
        }
      }
    },
    "approval_request.changes_requested": {
      "post": {
        "operationId": "approval_request_changes_requested",
        "x-audience": [
          "integrator"
        ],
        "tags": [
          "Webhooks"
        ],
        "summary": "Changes requested",
        "description": "Integrators only. 3DAPI completed the review of your approval request and rejected some files, so your account is not approved yet. Each rejected file in `quotes` has a `reason`; upload corrected files and send a new request (get_integrator_onboarding says how many). You get a fresh upload allowance. Every delivery is a POST with a JSON event and three headers: `X-Dapi-Webhook-Signature: sha256=<hex>`, an HMAC-SHA256 of `<timestamp>.<raw body>` with your endpoint secret; `X-Dapi-Webhook-Timestamp`, the Unix time in seconds it was signed (every attempt is signed again; reject a timestamp more than 5 minutes from your clock to stop replays); and `X-Dapi-Event-Id`, the event id. Answer with any 2xx within 10 seconds. Failed deliveries are retried with backoff for 24 hours, so the same event can arrive more than once: deduplicate on the event id.",
        "requestBody": {
          "description": "The event.",
          "content": {
            "application/json": {
              "example": {
                "id": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                "object": "event",
                "type": "approval_request.changes_requested",
                "created_at": "2026-10-14T10:02:00Z",
                "data": {
                  "object": {
                    "id": "apr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                    "object": "approval_request",
                    "status": "reviewed",
                    "quotes": [
                      {
                        "quote": {
                          "id": "qte_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                          "object": "quote",
                          "name": "Phone stand v2"
                        },
                        "decision": "rejected",
                        "reason": "The model has open edges; close the mesh and upload it again."
                      }
                    ],
                    "submitted_at": "2026-10-20T10:00:00Z",
                    "reviewed_at": "2026-10-21T08:30:00Z"
                  },
                  "previous": {
                    "status": "in_review"
                  },
                  "region": null
                }
              },
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "pattern": "^evt_[0-9A-Za-z]+$",
                    "description": "Unique identifier of the event, prefixed with `evt_`. Treat it as an opaque string: store it and compare it, never parse it.",
                    "example": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
                  },
                  "object": {
                    "type": "string",
                    "enum": [
                      "event"
                    ],
                    "description": "Names the type of this object. Always `event`."
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "approval_request.changes_requested"
                    ]
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened.",
                    "example": "2026-04-21T07:34:06Z"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "object": {
                        "$ref": "#/components/schemas/ApprovalRequest"
                      },
                      "previous": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": {},
                        "description": "The fields that changed, with their values before the change. Null when nothing changed, as on creation."
                      },
                      "region": {
                        "type": "null",
                        "description": "Always null: only `stock.low` names a region."
                      }
                    },
                    "required": [
                      "object",
                      "previous",
                      "region"
                    ],
                    "description": "The object as it is now, exactly as its GET endpoint returns it, and what changed."
                  }
                },
                "required": [
                  "id",
                  "object",
                  "type",
                  "created_at",
                  "data"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the event."
          }
        }
      }
    }
  }
}
