{
  "openapi": "3.0.3",
  "paths": {
    "/api/service/products": {
      "get": {
        "description": "Returns a page of products with their variants. Pass `updatedAfter` to pull only what changed since your last sync; products removed since then are included with `deleted: true`. Any valid API key can call this, read-only or read-write.",
        "operationId": "listProducts",
        "parameters": [
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "How many rows to return in one page.",
            "schema": {
              "minimum": 1,
              "maximum": 100,
              "default": 50,
              "type": "integer"
            }
          },
          {
            "name": "cursor",
            "required": false,
            "in": "query",
            "description": "Opaque paging cursor. Pass back `page.nextCursor` from the previous response to get the next page; omit it for the first page.",
            "schema": {
              "maxLength": 512,
              "type": "string"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "Return only rows that match this text.",
            "schema": {
              "maxLength": 200,
              "type": "string"
            }
          },
          {
            "name": "updatedAfter",
            "required": false,
            "in": "query",
            "description": "Return only rows changed strictly after this instant (ISO 8601). Store the newest `updatedAt` you have seen and pass it next time to fetch only what changed.",
            "schema": {
              "format": "date-time",
              "example": "2026-01-15T09:30:00.000Z",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of products.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceProductListResponse"
                },
                "example": {
                  "success": true,
                  "data": [
                    {
                      "id": "5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b",
                      "name": "Sample Widget",
                      "description": "A sample product used in documentation.",
                      "status": "ACTIVE",
                      "brand": {
                        "id": "0d4b8f2e-6a13-4c97-b5e0-3f1a9c7d2e64",
                        "name": "Sample Brand"
                      },
                      "categories": [
                        {
                          "id": "a3c7e1b5-8d29-4f40-9e6a-0b2d4f6a8c10",
                          "name": "Widgets"
                        }
                      ],
                      "tags": [
                        "sample"
                      ],
                      "imageUrls": [
                        "https://cdn.example.com/images/widget.jpg"
                      ],
                      "isTaxExempt": false,
                      "baseUom": {
                        "id": "c4a1e7f2-3b58-4c19-8e6d-5a2b9f0d7e31",
                        "code": "EA",
                        "name": "Each",
                        "symbol": "ea"
                      },
                      "salesUom": {
                        "id": "c4a1e7f2-3b58-4c19-8e6d-5a2b9f0d7e31",
                        "code": "EA",
                        "name": "Each",
                        "symbol": "ea"
                      },
                      "variants": [
                        {
                          "id": "9d2f6b1c-8e47-4a30-b5c2-0e1f3a4b5c6d",
                          "productId": "5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b",
                          "sku": "SKU-1001",
                          "name": "Sample Widget, Large",
                          "barcode": "0123456789012",
                          "attributes": {
                            "size": "Large"
                          },
                          "price": 15,
                          "salePrice": 12.5,
                          "isActive": true,
                          "isDefault": true,
                          "minOrderQuantity": 1,
                          "maxOrderQuantity": 1000,
                          "salesUom": {
                            "id": "c4a1e7f2-3b58-4c19-8e6d-5a2b9f0d7e31",
                            "code": "EA",
                            "name": "Each",
                            "symbol": "ea"
                          },
                          "updatedAt": "2026-01-15T09:30:00.000Z"
                        }
                      ],
                      "deleted": false,
                      "updatedAt": "2026-01-15T09:30:00.000Z"
                    }
                  ],
                  "page": {
                    "hasMore": true,
                    "nextCursor": "bzo1MA"
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid: a field failed validation, an id is malformed, or the request is not allowed in the current state. `message` says which.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 400,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/products",
                  "method": "GET",
                  "message": [
                    "limit must not be greater than 100"
                  ],
                  "error": "Bad Request",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "401": {
            "description": "The API key is missing, malformed, revoked or expired, the user who created the key is no longer active, the request comes from an address the key does not allow, or the key is read-only and the route writes. The rate-limit headers are not sent.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 401,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/products",
                  "method": "GET",
                  "message": "Invalid API key",
                  "error": "Unauthorized",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but not allowed to do this: the workspace's plan does not include API access, or the user who created the key lacks the permission this route needs. The rate-limit headers are not sent when the plan is the reason.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 403,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/products",
                  "method": "GET",
                  "message": "Missing required permission: products:product:read",
                  "error": "Forbidden",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "429": {
            "description": "The API key exceeded its requests-per-minute limit; wait `Retry-After` seconds. A 429 can also come from the separate per-address limit (600 requests a minute), which sends `X-RateLimit-*` headers instead of the `X-ApiKey-RateLimit-*` ones.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 429,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/products",
                  "method": "GET",
                  "error": "Too Many Requests",
                  "code": "API_KEY_RATE_LIMITED",
                  "message": "This API key is limited to 100 requests per minute. Retry after 42 seconds.",
                  "retryAfterSeconds": 42,
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "List products",
        "tags": [
          "Products"
        ],
        "x-required-permission": [
          "products:product:read"
        ]
      }
    },
    "/api/service/products/{id}": {
      "get": {
        "description": "Returns one product with its variants, each variant's price tiers, and every unit the product can be sold in.",
        "operationId": "getProduct",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Product id.",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The product.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceProductResponse"
                },
                "example": {
                  "success": true,
                  "data": {
                    "id": "5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b",
                    "name": "Sample Widget",
                    "description": "A sample product used in documentation.",
                    "status": "ACTIVE",
                    "brand": {
                      "id": "0d4b8f2e-6a13-4c97-b5e0-3f1a9c7d2e64",
                      "name": "Sample Brand"
                    },
                    "categories": [
                      {
                        "id": "a3c7e1b5-8d29-4f40-9e6a-0b2d4f6a8c10",
                        "name": "Widgets"
                      }
                    ],
                    "tags": [
                      "sample"
                    ],
                    "imageUrls": [
                      "https://cdn.example.com/images/widget.jpg"
                    ],
                    "isTaxExempt": false,
                    "baseUom": {
                      "id": "c4a1e7f2-3b58-4c19-8e6d-5a2b9f0d7e31",
                      "code": "EA",
                      "name": "Each",
                      "symbol": "ea"
                    },
                    "salesUom": {
                      "id": "c4a1e7f2-3b58-4c19-8e6d-5a2b9f0d7e31",
                      "code": "EA",
                      "name": "Each",
                      "symbol": "ea"
                    },
                    "variants": [
                      {
                        "id": "9d2f6b1c-8e47-4a30-b5c2-0e1f3a4b5c6d",
                        "productId": "5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b",
                        "sku": "SKU-1001",
                        "name": "Sample Widget, Large",
                        "barcode": "0123456789012",
                        "attributes": {
                          "size": "Large"
                        },
                        "price": 15,
                        "salePrice": 12.5,
                        "isActive": true,
                        "isDefault": true,
                        "minOrderQuantity": 1,
                        "maxOrderQuantity": 1000,
                        "updatedAt": "2026-01-15T09:30:00.000Z",
                        "prices": [
                          {
                            "name": "Standard",
                            "minQuantity": 1,
                            "maxQuantity": 9,
                            "price": 12.5,
                            "discountPercent": 0,
                            "uomId": "c4a1e7f2-3b58-4c19-8e6d-5a2b9f0d7e31",
                            "currency": "USD",
                            "validFrom": null,
                            "validTo": null,
                            "isActive": true
                          },
                          {
                            "name": "Bulk",
                            "minQuantity": 10,
                            "maxQuantity": null,
                            "price": 11,
                            "discountPercent": 0,
                            "uomId": "c4a1e7f2-3b58-4c19-8e6d-5a2b9f0d7e31",
                            "currency": "USD",
                            "validFrom": null,
                            "validTo": null,
                            "isActive": true
                          }
                        ]
                      }
                    ],
                    "deleted": false,
                    "updatedAt": "2026-01-15T09:30:00.000Z",
                    "uoms": [
                      {
                        "uomId": "c4a1e7f2-3b58-4c19-8e6d-5a2b9f0d7e31",
                        "variantId": null,
                        "uom": {
                          "id": "c4a1e7f2-3b58-4c19-8e6d-5a2b9f0d7e31",
                          "code": "EA",
                          "name": "Each",
                          "symbol": "ea"
                        },
                        "isBase": true,
                        "isSales": true,
                        "conversionFactor": 1,
                        "minOrderQty": 1,
                        "maxOrderQty": 1000,
                        "orderMultiple": 1,
                        "listPrice": 15,
                        "salePrice": 12.5,
                        "barcode": "0123456789012"
                      }
                    ]
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid: a field failed validation, an id is malformed, or the request is not allowed in the current state. `message` says which.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 400,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/products/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b",
                  "method": "GET",
                  "message": [
                    "limit must not be greater than 100"
                  ],
                  "error": "Bad Request",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "401": {
            "description": "The API key is missing, malformed, revoked or expired, the user who created the key is no longer active, the request comes from an address the key does not allow, or the key is read-only and the route writes. The rate-limit headers are not sent.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 401,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/products/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b",
                  "method": "GET",
                  "message": "Invalid API key",
                  "error": "Unauthorized",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but not allowed to do this: the workspace's plan does not include API access, or the user who created the key lacks the permission this route needs. The rate-limit headers are not sent when the plan is the reason.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 403,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/products/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b",
                  "method": "GET",
                  "message": "Missing required permission: products:product:read",
                  "error": "Forbidden",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "404": {
            "description": "No product with this id in the workspace.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 404,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/products/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b",
                  "method": "GET",
                  "message": "Not found",
                  "error": "Not Found",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "429": {
            "description": "The API key exceeded its requests-per-minute limit; wait `Retry-After` seconds. A 429 can also come from the separate per-address limit (600 requests a minute), which sends `X-RateLimit-*` headers instead of the `X-ApiKey-RateLimit-*` ones.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 429,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/products/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b",
                  "method": "GET",
                  "error": "Too Many Requests",
                  "code": "API_KEY_RATE_LIMITED",
                  "message": "This API key is limited to 100 requests per minute. Retry after 42 seconds.",
                  "retryAfterSeconds": 42,
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Get a product",
        "tags": [
          "Products"
        ],
        "x-required-permission": [
          "products:product:read"
        ]
      }
    },
    "/api/service/uoms": {
      "get": {
        "description": "Returns the workspace's active units of measure. Use a unit's `id` as `uomId` on quote and order lines.",
        "operationId": "listUnitsOfMeasure",
        "parameters": [],
        "responses": {
          "200": {
            "description": "The active units of measure.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceUomListResponse"
                },
                "example": {
                  "success": true,
                  "data": [
                    {
                      "id": "c4a1e7f2-3b58-4c19-8e6d-5a2b9f0d7e31",
                      "code": "EA",
                      "name": "Each",
                      "symbol": "ea",
                      "category": "COUNT",
                      "isBase": true,
                      "isActive": true,
                      "decimalPlaces": 0,
                      "allowFractional": false
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid: a field failed validation, an id is malformed, or the request is not allowed in the current state. `message` says which.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 400,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/uoms",
                  "method": "GET",
                  "message": [
                    "limit must not be greater than 100"
                  ],
                  "error": "Bad Request",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "401": {
            "description": "The API key is missing, malformed, revoked or expired, the user who created the key is no longer active, the request comes from an address the key does not allow, or the key is read-only and the route writes. The rate-limit headers are not sent.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 401,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/uoms",
                  "method": "GET",
                  "message": "Invalid API key",
                  "error": "Unauthorized",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but not allowed to do this: the workspace's plan does not include API access, or the user who created the key lacks the permission this route needs. The rate-limit headers are not sent when the plan is the reason.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 403,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/uoms",
                  "method": "GET",
                  "message": "Missing required permission: products:product:read",
                  "error": "Forbidden",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "429": {
            "description": "The API key exceeded its requests-per-minute limit; wait `Retry-After` seconds. A 429 can also come from the separate per-address limit (600 requests a minute), which sends `X-RateLimit-*` headers instead of the `X-ApiKey-RateLimit-*` ones.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 429,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/uoms",
                  "method": "GET",
                  "error": "Too Many Requests",
                  "code": "API_KEY_RATE_LIMITED",
                  "message": "This API key is limited to 100 requests per minute. Retry after 42 seconds.",
                  "retryAfterSeconds": 42,
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "List units of measure",
        "tags": [
          "Reference data"
        ],
        "x-required-permission": [
          "products:product:read"
        ]
      }
    },
    "/api/service/payment-terms": {
      "get": {
        "description": "Returns the payment terms the workspace offers. Use a term's `code` as `paymentTerms` when creating a quote or order. Any one of the listed permissions is enough.",
        "operationId": "listPaymentTerms",
        "parameters": [],
        "responses": {
          "200": {
            "description": "The payment terms.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServicePaymentTermListResponse"
                },
                "example": {
                  "success": true,
                  "data": [
                    {
                      "code": "NET_30",
                      "label": "Net 30",
                      "kind": "NET",
                      "netDays": 30,
                      "isDefault": true,
                      "isActive": true
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid: a field failed validation, an id is malformed, or the request is not allowed in the current state. `message` says which.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 400,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/payment-terms",
                  "method": "GET",
                  "message": [
                    "limit must not be greater than 100"
                  ],
                  "error": "Bad Request",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "401": {
            "description": "The API key is missing, malformed, revoked or expired, the user who created the key is no longer active, the request comes from an address the key does not allow, or the key is read-only and the route writes. The rate-limit headers are not sent.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 401,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/payment-terms",
                  "method": "GET",
                  "message": "Invalid API key",
                  "error": "Unauthorized",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but not allowed to do this: the workspace's plan does not include API access, or the user who created the key lacks the permission this route needs. The rate-limit headers are not sent when the plan is the reason.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 403,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/payment-terms",
                  "method": "GET",
                  "message": "Missing required permission: products:product:read",
                  "error": "Forbidden",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "429": {
            "description": "The API key exceeded its requests-per-minute limit; wait `Retry-After` seconds. A 429 can also come from the separate per-address limit (600 requests a minute), which sends `X-RateLimit-*` headers instead of the `X-ApiKey-RateLimit-*` ones.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 429,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/payment-terms",
                  "method": "GET",
                  "error": "Too Many Requests",
                  "code": "API_KEY_RATE_LIMITED",
                  "message": "This API key is limited to 100 requests per minute. Retry after 42 seconds.",
                  "retryAfterSeconds": 42,
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "List payment terms",
        "tags": [
          "Reference data"
        ],
        "x-required-permission": [
          "sales:quote:read",
          "sales:sales_order:read",
          "sales:sales_invoice:read",
          "payments:invoice:read",
          "purchase:purchase_order:read",
          "customers:customer:read",
          "suppliers:supplier:read",
          "settings:tenant:read"
        ]
      }
    },
    "/api/service/warehouses": {
      "get": {
        "description": "Returns the workspace's warehouses.",
        "operationId": "listWarehouses",
        "parameters": [],
        "responses": {
          "200": {
            "description": "The warehouses.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceWarehouseListResponse"
                },
                "example": {
                  "success": true,
                  "data": [
                    {
                      "id": "2a6f9c1d-7b35-4e80-a4d2-9c3e5b1f8a67",
                      "code": "MAIN",
                      "name": "Main warehouse",
                      "type": "WAREHOUSE",
                      "status": "ACTIVE",
                      "address": {
                        "street1": "1 Example Street",
                        "city": "Exampleville"
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid: a field failed validation, an id is malformed, or the request is not allowed in the current state. `message` says which.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 400,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/warehouses",
                  "method": "GET",
                  "message": [
                    "limit must not be greater than 100"
                  ],
                  "error": "Bad Request",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "401": {
            "description": "The API key is missing, malformed, revoked or expired, the user who created the key is no longer active, the request comes from an address the key does not allow, or the key is read-only and the route writes. The rate-limit headers are not sent.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 401,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/warehouses",
                  "method": "GET",
                  "message": "Invalid API key",
                  "error": "Unauthorized",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but not allowed to do this: the workspace's plan does not include API access, or the user who created the key lacks the permission this route needs. The rate-limit headers are not sent when the plan is the reason.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 403,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/warehouses",
                  "method": "GET",
                  "message": "Missing required permission: products:product:read",
                  "error": "Forbidden",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "429": {
            "description": "The API key exceeded its requests-per-minute limit; wait `Retry-After` seconds. A 429 can also come from the separate per-address limit (600 requests a minute), which sends `X-RateLimit-*` headers instead of the `X-ApiKey-RateLimit-*` ones.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 429,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/warehouses",
                  "method": "GET",
                  "error": "Too Many Requests",
                  "code": "API_KEY_RATE_LIMITED",
                  "message": "This API key is limited to 100 requests per minute. Retry after 42 seconds.",
                  "retryAfterSeconds": 42,
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "List warehouses",
        "tags": [
          "Reference data"
        ],
        "x-required-permission": [
          "inventory:warehouse:read"
        ]
      }
    },
    "/api/service/stock": {
      "get": {
        "description": "Returns on-hand, reserved and available quantity per warehouse for up to 200 variants. Ids that do not exist in the workspace are listed in `unknownVariantIds` rather than reported as zero stock.",
        "operationId": "getStock",
        "parameters": [
          {
            "name": "variantIds",
            "required": true,
            "in": "query",
            "description": "Comma-separated variant ids, from 1 to 200. Ids that do not exist in this workspace come back in `unknownVariantIds`.",
            "schema": {
              "example": "5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b,9d2f6b1c-8e47-4a30-b5c2-0e1f3a4b5c6d",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Stock rows, plus any ids that were not recognised.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceStockResponse"
                },
                "example": {
                  "success": true,
                  "data": [
                    {
                      "variantId": "9d2f6b1c-8e47-4a30-b5c2-0e1f3a4b5c6d",
                      "warehouseId": "2a6f9c1d-7b35-4e80-a4d2-9c3e5b1f8a67",
                      "warehouseName": "Main warehouse",
                      "onHandQuantity": 120,
                      "reservedQuantity": 20,
                      "availableQuantity": 100,
                      "tracked": true
                    }
                  ],
                  "unknownVariantIds": []
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid: a field failed validation, an id is malformed, or the request is not allowed in the current state. `message` says which.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 400,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/stock",
                  "method": "GET",
                  "message": [
                    "limit must not be greater than 100"
                  ],
                  "error": "Bad Request",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "401": {
            "description": "The API key is missing, malformed, revoked or expired, the user who created the key is no longer active, the request comes from an address the key does not allow, or the key is read-only and the route writes. The rate-limit headers are not sent.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 401,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/stock",
                  "method": "GET",
                  "message": "Invalid API key",
                  "error": "Unauthorized",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but not allowed to do this: the workspace's plan does not include API access, or the user who created the key lacks the permission this route needs. The rate-limit headers are not sent when the plan is the reason.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 403,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/stock",
                  "method": "GET",
                  "message": "Missing required permission: products:product:read",
                  "error": "Forbidden",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "429": {
            "description": "The API key exceeded its requests-per-minute limit; wait `Retry-After` seconds. A 429 can also come from the separate per-address limit (600 requests a minute), which sends `X-RateLimit-*` headers instead of the `X-ApiKey-RateLimit-*` ones.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 429,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/stock",
                  "method": "GET",
                  "error": "Too Many Requests",
                  "code": "API_KEY_RATE_LIMITED",
                  "message": "This API key is limited to 100 requests per minute. Retry after 42 seconds.",
                  "retryAfterSeconds": 42,
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Get stock for variants",
        "tags": [
          "Stock"
        ],
        "x-required-permission": [
          "inventory:stock_level:read"
        ]
      }
    },
    "/api/service/customers": {
      "get": {
        "description": "Returns a page of customers with their addresses and contacts. Pass `updatedAfter` to pull only what changed since your last sync; customers removed since then are included with `deleted: true`. The list covers the whole workspace.",
        "operationId": "listCustomers",
        "parameters": [
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "How many rows to return in one page.",
            "schema": {
              "minimum": 1,
              "maximum": 100,
              "default": 50,
              "type": "integer"
            }
          },
          {
            "name": "cursor",
            "required": false,
            "in": "query",
            "description": "Opaque paging cursor. Pass back `page.nextCursor` from the previous response to get the next page; omit it for the first page.",
            "schema": {
              "maxLength": 512,
              "type": "string"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "Return only rows that match this text.",
            "schema": {
              "maxLength": 200,
              "type": "string"
            }
          },
          {
            "name": "updatedAfter",
            "required": false,
            "in": "query",
            "description": "Return only rows changed strictly after this instant (ISO 8601). Store the newest `updatedAt` you have seen and pass it next time to fetch only what changed.",
            "schema": {
              "format": "date-time",
              "example": "2026-01-15T09:30:00.000Z",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of customers.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceCustomerListResponse"
                },
                "example": {
                  "success": true,
                  "data": [
                    {
                      "id": "e8d3b5a9-1c47-4f62-9b0e-6a7c2d4f1b83",
                      "code": "CUST-0001",
                      "customerCode": "CA-0001",
                      "name": "Example Trading Co",
                      "displayName": "Example Trading",
                      "legalName": "Example Trading Company Ltd",
                      "taxId": "TAX-000000",
                      "status": "ACTIVE",
                      "paymentTerms": "NET_30",
                      "currency": "USD",
                      "creditLimit": 5000,
                      "availableCredit": 3200,
                      "taxExempt": false,
                      "defaultWarehouseId": "2a6f9c1d-7b35-4e80-a4d2-9c3e5b1f8a67",
                      "addresses": [
                        {
                          "id": "d2f6a8c0-4b19-4e73-8a5d-9c1e3b7f5a26",
                          "type": "BILLING",
                          "label": "Head office",
                          "street1": "1 Example Street",
                          "street2": null,
                          "city": "Exampleville",
                          "state": "Example State",
                          "country": "Exampleland",
                          "postalCode": "12345",
                          "isDefault": true,
                          "isBranch": false,
                          "isActive": true
                        }
                      ],
                      "contacts": [
                        {
                          "type": "EMAIL",
                          "value": "buyer@example.com",
                          "name": "Sample Buyer",
                          "isDefault": true
                        }
                      ],
                      "deleted": false,
                      "updatedAt": "2026-01-15T09:30:00.000Z"
                    }
                  ],
                  "page": {
                    "hasMore": true,
                    "nextCursor": "bzo1MA"
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid: a field failed validation, an id is malformed, or the request is not allowed in the current state. `message` says which.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 400,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/customers",
                  "method": "GET",
                  "message": [
                    "limit must not be greater than 100"
                  ],
                  "error": "Bad Request",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "401": {
            "description": "The API key is missing, malformed, revoked or expired, the user who created the key is no longer active, the request comes from an address the key does not allow, or the key is read-only and the route writes. The rate-limit headers are not sent.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 401,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/customers",
                  "method": "GET",
                  "message": "Invalid API key",
                  "error": "Unauthorized",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but not allowed to do this: the workspace's plan does not include API access, or the user who created the key lacks the permission this route needs. The rate-limit headers are not sent when the plan is the reason.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 403,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/customers",
                  "method": "GET",
                  "message": "Missing required permission: products:product:read",
                  "error": "Forbidden",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "429": {
            "description": "The API key exceeded its requests-per-minute limit; wait `Retry-After` seconds. A 429 can also come from the separate per-address limit (600 requests a minute), which sends `X-RateLimit-*` headers instead of the `X-ApiKey-RateLimit-*` ones.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 429,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/customers",
                  "method": "GET",
                  "error": "Too Many Requests",
                  "code": "API_KEY_RATE_LIMITED",
                  "message": "This API key is limited to 100 requests per minute. Retry after 42 seconds.",
                  "retryAfterSeconds": 42,
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "List customers",
        "tags": [
          "Customers"
        ],
        "x-required-permission": [
          "customers:customer:read"
        ]
      }
    },
    "/api/service/customers/{id}": {
      "get": {
        "description": "Returns one customer with their addresses and contacts.",
        "operationId": "getCustomer",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Customer id.",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The customer.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceCustomerResponse"
                },
                "example": {
                  "success": true,
                  "data": {
                    "id": "e8d3b5a9-1c47-4f62-9b0e-6a7c2d4f1b83",
                    "code": "CUST-0001",
                    "customerCode": "CA-0001",
                    "name": "Example Trading Co",
                    "displayName": "Example Trading",
                    "legalName": "Example Trading Company Ltd",
                    "taxId": "TAX-000000",
                    "status": "ACTIVE",
                    "paymentTerms": "NET_30",
                    "currency": "USD",
                    "creditLimit": 5000,
                    "availableCredit": 3200,
                    "taxExempt": false,
                    "defaultWarehouseId": "2a6f9c1d-7b35-4e80-a4d2-9c3e5b1f8a67",
                    "addresses": [
                      {
                        "id": "d2f6a8c0-4b19-4e73-8a5d-9c1e3b7f5a26",
                        "type": "BILLING",
                        "label": "Head office",
                        "street1": "1 Example Street",
                        "street2": null,
                        "city": "Exampleville",
                        "state": "Example State",
                        "country": "Exampleland",
                        "postalCode": "12345",
                        "isDefault": true,
                        "isBranch": false,
                        "isActive": true
                      }
                    ],
                    "contacts": [
                      {
                        "type": "EMAIL",
                        "value": "buyer@example.com",
                        "name": "Sample Buyer",
                        "isDefault": true
                      }
                    ],
                    "deleted": false,
                    "updatedAt": "2026-01-15T09:30:00.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid: a field failed validation, an id is malformed, or the request is not allowed in the current state. `message` says which.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 400,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/customers/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b",
                  "method": "GET",
                  "message": [
                    "limit must not be greater than 100"
                  ],
                  "error": "Bad Request",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "401": {
            "description": "The API key is missing, malformed, revoked or expired, the user who created the key is no longer active, the request comes from an address the key does not allow, or the key is read-only and the route writes. The rate-limit headers are not sent.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 401,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/customers/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b",
                  "method": "GET",
                  "message": "Invalid API key",
                  "error": "Unauthorized",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but not allowed to do this: the workspace's plan does not include API access, or the user who created the key lacks the permission this route needs. The rate-limit headers are not sent when the plan is the reason.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 403,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/customers/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b",
                  "method": "GET",
                  "message": "Missing required permission: products:product:read",
                  "error": "Forbidden",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "404": {
            "description": "No customer with this id in the workspace.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 404,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/customers/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b",
                  "method": "GET",
                  "message": "Not found",
                  "error": "Not Found",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "429": {
            "description": "The API key exceeded its requests-per-minute limit; wait `Retry-After` seconds. A 429 can also come from the separate per-address limit (600 requests a minute), which sends `X-RateLimit-*` headers instead of the `X-ApiKey-RateLimit-*` ones.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 429,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/customers/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b",
                  "method": "GET",
                  "error": "Too Many Requests",
                  "code": "API_KEY_RATE_LIMITED",
                  "message": "This API key is limited to 100 requests per minute. Retry after 42 seconds.",
                  "retryAfterSeconds": 42,
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Get a customer",
        "tags": [
          "Customers"
        ],
        "x-required-permission": [
          "customers:customer:read"
        ]
      }
    },
    "/api/service/sales-orders": {
      "get": {
        "description": "Returns a page of sales orders, oldest change first, so you can page with `updatedAfter` set to the last `updatedAt` you stored. Filter by `customerId` or `status`.",
        "operationId": "listSalesOrders",
        "parameters": [
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "How many rows to return in one page.",
            "schema": {
              "minimum": 1,
              "maximum": 100,
              "default": 50,
              "type": "integer"
            }
          },
          {
            "name": "cursor",
            "required": false,
            "in": "query",
            "description": "Opaque paging cursor. Pass back `page.nextCursor` from the previous response to get the next page; omit it for the first page.",
            "schema": {
              "maxLength": 512,
              "type": "string"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "Return only rows that match this text.",
            "schema": {
              "maxLength": 200,
              "type": "string"
            }
          },
          {
            "name": "updatedAfter",
            "required": false,
            "in": "query",
            "description": "Return only rows changed strictly after this instant (ISO 8601). Store the newest `updatedAt` you have seen and pass it next time to fetch only what changed.",
            "schema": {
              "format": "date-time",
              "example": "2026-01-15T09:30:00.000Z",
              "type": "string"
            }
          },
          {
            "name": "customerId",
            "required": false,
            "in": "query",
            "description": "Return only rows for this customer.",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "status",
            "required": false,
            "in": "query",
            "description": "Return only rows in these statuses. Repeat the parameter (`status=A&status=B`) or comma-separate the values.",
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/SalesOrderStatus"
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of sales orders.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceSalesOrderListResponse"
                },
                "example": {
                  "success": true,
                  "data": [
                    {
                      "id": "f1a8d6c3-9e24-4b57-a3c1-7e0d5b2f9a48",
                      "orderNumber": "SO-000001",
                      "status": "CONFIRMED",
                      "paymentStatus": "PENDING",
                      "customerId": "e8d3b5a9-1c47-4f62-9b0e-6a7c2d4f1b83",
                      "orderDate": "2026-01-15T09:30:00.000Z",
                      "promisedDate": "2026-01-22T00:00:00.000Z",
                      "shippedDate": null,
                      "deliveredDate": null,
                      "currency": "USD",
                      "exchangeRate": 1,
                      "subtotal": 25,
                      "discountAmount": 0,
                      "taxAmount": 2.5,
                      "shippingAmount": 0,
                      "totalAmount": 27.5,
                      "paymentTerms": "NET_30",
                      "customerPoNumber": "PO-12345",
                      "reference": "INT-0001",
                      "trackingNumber": null,
                      "items": [
                        {
                          "productId": "5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b",
                          "variantId": "9d2f6b1c-8e47-4a30-b5c2-0e1f3a4b5c6d",
                          "sku": "SKU-1001",
                          "name": "Sample Widget, Large",
                          "quantity": 2,
                          "uomId": "c4a1e7f2-3b58-4c19-8e6d-5a2b9f0d7e31",
                          "unitPrice": 12.5,
                          "discountPercent": 0,
                          "discountAmount": 0,
                          "taxRate": 10,
                          "taxAmount": 2.5,
                          "totalAmount": 27.5,
                          "shippedQuantity": 0,
                          "invoicedQuantity": 0,
                          "canceledQuantity": 0
                        }
                      ],
                      "updatedAt": "2026-01-15T09:30:00.000Z"
                    }
                  ],
                  "page": {
                    "hasMore": true,
                    "nextCursor": "bzo1MA"
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid: a field failed validation, an id is malformed, or the request is not allowed in the current state. `message` says which.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 400,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/sales-orders",
                  "method": "GET",
                  "message": [
                    "limit must not be greater than 100"
                  ],
                  "error": "Bad Request",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "401": {
            "description": "The API key is missing, malformed, revoked or expired, the user who created the key is no longer active, the request comes from an address the key does not allow, or the key is read-only and the route writes. The rate-limit headers are not sent.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 401,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/sales-orders",
                  "method": "GET",
                  "message": "Invalid API key",
                  "error": "Unauthorized",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but not allowed to do this: the workspace's plan does not include API access, or the user who created the key lacks the permission this route needs. The rate-limit headers are not sent when the plan is the reason.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 403,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/sales-orders",
                  "method": "GET",
                  "message": "Missing required permission: products:product:read",
                  "error": "Forbidden",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "429": {
            "description": "The API key exceeded its requests-per-minute limit; wait `Retry-After` seconds. A 429 can also come from the separate per-address limit (600 requests a minute), which sends `X-RateLimit-*` headers instead of the `X-ApiKey-RateLimit-*` ones.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 429,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/sales-orders",
                  "method": "GET",
                  "error": "Too Many Requests",
                  "code": "API_KEY_RATE_LIMITED",
                  "message": "This API key is limited to 100 requests per minute. Retry after 42 seconds.",
                  "retryAfterSeconds": 42,
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "List sales orders",
        "tags": [
          "Sales orders"
        ],
        "x-required-permission": [
          "sales:sales_order:read"
        ]
      },
      "post": {
        "description": "Creates a sales order for a customer. Needs a read-write API key. Prices, tax and totals are worked out by the server: leave `unitPrice` off a line to use the catalogue price. A price you send can be refused, depending on the workspace's pricing settings and the permissions of the user who created the key. The order starts as `DRAFT`, which reserves no stock, unless you send `status: CONFIRMED`, which confirms it and reserves stock. A confirmed order is refused if the customer's credit limit does not allow it or, when the workspace does not allow negative stock, stock does not cover it; with negative stock allowed it is confirmed and the shortfall is a backorder. Send an `Idempotency-Key` so a retry never creates a second order.",
        "operationId": "createSalesOrder",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "A unique value you choose for this create (a UUID is a good choice). Retrying with the same key and the same body returns the original document instead of creating a second one; the same key with a different body is refused with 409. You can send the same value as `idempotencyKey` in the body instead.",
            "required": false,
            "schema": {
              "type": "string",
              "example": "11111111-2222-4333-8444-555555555555"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "The order to create.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateSalesOrderInput"
              },
              "examples": {
                "example": {
                  "summary": "Example",
                  "value": {
                    "customerId": "e8d3b5a9-1c47-4f62-9b0e-6a7c2d4f1b83",
                    "paymentTerms": "NET_30",
                    "currency": "USD",
                    "reference": "INT-0001",
                    "customerPoNumber": "PO-12345",
                    "items": [
                      {
                        "productId": "5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b",
                        "variantId": "9d2f6b1c-8e47-4a30-b5c2-0e1f3a4b5c6d",
                        "uomId": "c4a1e7f2-3b58-4c19-8e6d-5a2b9f0d7e31",
                        "quantity": 2
                      }
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The sales order that was created.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceSalesOrderResponse"
                },
                "example": {
                  "success": true,
                  "data": {
                    "id": "f1a8d6c3-9e24-4b57-a3c1-7e0d5b2f9a48",
                    "orderNumber": "SO-000001",
                    "status": "CONFIRMED",
                    "paymentStatus": "PENDING",
                    "customerId": "e8d3b5a9-1c47-4f62-9b0e-6a7c2d4f1b83",
                    "orderDate": "2026-01-15T09:30:00.000Z",
                    "promisedDate": "2026-01-22T00:00:00.000Z",
                    "shippedDate": null,
                    "deliveredDate": null,
                    "currency": "USD",
                    "exchangeRate": 1,
                    "subtotal": 25,
                    "discountAmount": 0,
                    "taxAmount": 2.5,
                    "shippingAmount": 0,
                    "totalAmount": 27.5,
                    "paymentTerms": "NET_30",
                    "customerPoNumber": "PO-12345",
                    "reference": "INT-0001",
                    "trackingNumber": null,
                    "items": [
                      {
                        "productId": "5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b",
                        "variantId": "9d2f6b1c-8e47-4a30-b5c2-0e1f3a4b5c6d",
                        "sku": "SKU-1001",
                        "name": "Sample Widget, Large",
                        "quantity": 2,
                        "uomId": "c4a1e7f2-3b58-4c19-8e6d-5a2b9f0d7e31",
                        "unitPrice": 12.5,
                        "discountPercent": 0,
                        "discountAmount": 0,
                        "taxRate": 10,
                        "taxAmount": 2.5,
                        "totalAmount": 27.5,
                        "shippedQuantity": 0,
                        "invoicedQuantity": 0,
                        "canceledQuantity": 0
                      }
                    ],
                    "updatedAt": "2026-01-15T09:30:00.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid: a field failed validation, an id is malformed, or the request is not allowed in the current state. `message` says which.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 400,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/sales-orders",
                  "method": "POST",
                  "message": [
                    "limit must not be greater than 100"
                  ],
                  "error": "Bad Request",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "401": {
            "description": "The API key is missing, malformed, revoked or expired, the user who created the key is no longer active, the request comes from an address the key does not allow, or the key is read-only and the route writes. The rate-limit headers are not sent.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 401,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/sales-orders",
                  "method": "POST",
                  "message": "Invalid API key",
                  "error": "Unauthorized",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but not allowed to do this: the workspace's plan does not include API access, or the user who created the key lacks the permission this route needs. The rate-limit headers are not sent when the plan is the reason.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 403,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/sales-orders",
                  "method": "POST",
                  "message": "Missing required permission: products:product:read",
                  "error": "Forbidden",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "409": {
            "description": "The `Idempotency-Key` cannot be used for this request. `code` says why: `IDEMPOTENCY_KEY_CONFLICT` — the key was already used with a different request body (send the original body again, or use a new key for a new request); `IDEMPOTENCY_IN_PROGRESS` — the first request with this key is still running (retry the same request shortly); `IDEMPOTENCY_OUTCOME_UNKNOWN` — the earlier attempt may have created the document (look for it before trying again; a new key does not resolve this).",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "examples": {
                  "keyConflict": {
                    "summary": "Key reused with a different body",
                    "value": {
                      "statusCode": 409,
                      "timestamp": "2026-01-15T09:30:00.000Z",
                      "path": "/api/service/sales-orders",
                      "method": "POST",
                      "code": "IDEMPOTENCY_KEY_CONFLICT",
                      "message": "Idempotency key \"11111111-2222-4333-8444-555555555555\" was already used for a different \"createSalesOrder\" request. Use a new key for a new request.",
                      "operation": "createSalesOrder",
                      "idempotencyKey": "11111111-2222-4333-8444-555555555555",
                      "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                    }
                  },
                  "inProgress": {
                    "summary": "First request still running",
                    "value": {
                      "statusCode": 409,
                      "timestamp": "2026-01-15T09:30:00.000Z",
                      "path": "/api/service/sales-orders",
                      "method": "POST",
                      "code": "IDEMPOTENCY_IN_PROGRESS",
                      "message": "A \"createSalesOrder\" request with this idempotency key is already in progress. Retry shortly.",
                      "operation": "createSalesOrder",
                      "idempotencyKey": "11111111-2222-4333-8444-555555555555",
                      "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                    }
                  },
                  "outcomeUnknown": {
                    "summary": "Earlier attempt may have succeeded",
                    "value": {
                      "statusCode": 409,
                      "timestamp": "2026-01-15T09:30:00.000Z",
                      "path": "/api/service/sales-orders",
                      "method": "POST",
                      "code": "IDEMPOTENCY_OUTCOME_UNKNOWN",
                      "operation": "createSalesOrder",
                      "idempotencyKey": "11111111-2222-4333-8444-555555555555",
                      "message": "The previous attempt may have changed data. Review its receipt and reconcile or undo the effects before approving another attempt. A new key does not resolve an uncertain outcome.",
                      "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "The API key exceeded its requests-per-minute limit; wait `Retry-After` seconds. A 429 can also come from the separate per-address limit (600 requests a minute), which sends `X-RateLimit-*` headers instead of the `X-ApiKey-RateLimit-*` ones.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 429,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/sales-orders",
                  "method": "POST",
                  "error": "Too Many Requests",
                  "code": "API_KEY_RATE_LIMITED",
                  "message": "This API key is limited to 100 requests per minute. Retry after 42 seconds.",
                  "retryAfterSeconds": 42,
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Create a sales order",
        "tags": [
          "Sales orders"
        ],
        "x-required-scope": "READ_WRITE"
      }
    },
    "/api/service/sales-orders/{id}": {
      "get": {
        "description": "Returns one sales order with its lines, including shipped and invoiced quantities.",
        "operationId": "getSalesOrder",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Sales order id.",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The sales order.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceSalesOrderResponse"
                },
                "example": {
                  "success": true,
                  "data": {
                    "id": "f1a8d6c3-9e24-4b57-a3c1-7e0d5b2f9a48",
                    "orderNumber": "SO-000001",
                    "status": "CONFIRMED",
                    "paymentStatus": "PENDING",
                    "customerId": "e8d3b5a9-1c47-4f62-9b0e-6a7c2d4f1b83",
                    "orderDate": "2026-01-15T09:30:00.000Z",
                    "promisedDate": "2026-01-22T00:00:00.000Z",
                    "shippedDate": null,
                    "deliveredDate": null,
                    "currency": "USD",
                    "exchangeRate": 1,
                    "subtotal": 25,
                    "discountAmount": 0,
                    "taxAmount": 2.5,
                    "shippingAmount": 0,
                    "totalAmount": 27.5,
                    "paymentTerms": "NET_30",
                    "customerPoNumber": "PO-12345",
                    "reference": "INT-0001",
                    "trackingNumber": null,
                    "items": [
                      {
                        "productId": "5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b",
                        "variantId": "9d2f6b1c-8e47-4a30-b5c2-0e1f3a4b5c6d",
                        "sku": "SKU-1001",
                        "name": "Sample Widget, Large",
                        "quantity": 2,
                        "uomId": "c4a1e7f2-3b58-4c19-8e6d-5a2b9f0d7e31",
                        "unitPrice": 12.5,
                        "discountPercent": 0,
                        "discountAmount": 0,
                        "taxRate": 10,
                        "taxAmount": 2.5,
                        "totalAmount": 27.5,
                        "shippedQuantity": 0,
                        "invoicedQuantity": 0,
                        "canceledQuantity": 0
                      }
                    ],
                    "updatedAt": "2026-01-15T09:30:00.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid: a field failed validation, an id is malformed, or the request is not allowed in the current state. `message` says which.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 400,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/sales-orders/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b",
                  "method": "GET",
                  "message": [
                    "limit must not be greater than 100"
                  ],
                  "error": "Bad Request",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "401": {
            "description": "The API key is missing, malformed, revoked or expired, the user who created the key is no longer active, the request comes from an address the key does not allow, or the key is read-only and the route writes. The rate-limit headers are not sent.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 401,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/sales-orders/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b",
                  "method": "GET",
                  "message": "Invalid API key",
                  "error": "Unauthorized",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but not allowed to do this: the workspace's plan does not include API access, or the user who created the key lacks the permission this route needs. The rate-limit headers are not sent when the plan is the reason.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 403,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/sales-orders/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b",
                  "method": "GET",
                  "message": "Missing required permission: products:product:read",
                  "error": "Forbidden",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "404": {
            "description": "No sales order with this id in the workspace.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 404,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/sales-orders/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b",
                  "method": "GET",
                  "message": "Not found",
                  "error": "Not Found",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "429": {
            "description": "The API key exceeded its requests-per-minute limit; wait `Retry-After` seconds. A 429 can also come from the separate per-address limit (600 requests a minute), which sends `X-RateLimit-*` headers instead of the `X-ApiKey-RateLimit-*` ones.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 429,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/sales-orders/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b",
                  "method": "GET",
                  "error": "Too Many Requests",
                  "code": "API_KEY_RATE_LIMITED",
                  "message": "This API key is limited to 100 requests per minute. Retry after 42 seconds.",
                  "retryAfterSeconds": 42,
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Get a sales order",
        "tags": [
          "Sales orders"
        ],
        "x-required-permission": [
          "sales:sales_order:read"
        ]
      }
    },
    "/api/service/sales-quotes": {
      "get": {
        "description": "Returns a page of quotes for the whole workspace, oldest change first, so you can page with `updatedAfter` set to the last `updatedAt` you stored. Filter by `customerId` or `status`.",
        "operationId": "listSalesQuotes",
        "parameters": [
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "How many rows to return in one page.",
            "schema": {
              "minimum": 1,
              "maximum": 100,
              "default": 50,
              "type": "integer"
            }
          },
          {
            "name": "cursor",
            "required": false,
            "in": "query",
            "description": "Opaque paging cursor. Pass back `page.nextCursor` from the previous response to get the next page; omit it for the first page.",
            "schema": {
              "maxLength": 512,
              "type": "string"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "Return only rows that match this text.",
            "schema": {
              "maxLength": 200,
              "type": "string"
            }
          },
          {
            "name": "updatedAfter",
            "required": false,
            "in": "query",
            "description": "Return only rows changed strictly after this instant (ISO 8601). Store the newest `updatedAt` you have seen and pass it next time to fetch only what changed.",
            "schema": {
              "format": "date-time",
              "example": "2026-01-15T09:30:00.000Z",
              "type": "string"
            }
          },
          {
            "name": "customerId",
            "required": false,
            "in": "query",
            "description": "Return only rows for this customer.",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "status",
            "required": false,
            "in": "query",
            "description": "Return only rows in these statuses. Repeat the parameter (`status=A&status=B`) or comma-separate the values.",
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/QuoteStatus"
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of quotes.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceQuoteListResponse"
                },
                "example": {
                  "success": true,
                  "data": [
                    {
                      "id": "b7e2c9d4-5a18-4f36-8c0b-1d9e4a7f3c25",
                      "quoteNumber": "QT-000001",
                      "status": "DRAFT",
                      "customerId": "e8d3b5a9-1c47-4f62-9b0e-6a7c2d4f1b83",
                      "quoteDate": "2026-01-15T09:30:00.000Z",
                      "validUntil": "2026-02-14T09:30:00.000Z",
                      "currency": "USD",
                      "exchangeRate": 1,
                      "subtotal": 25,
                      "discountAmount": 0,
                      "taxAmount": 2.5,
                      "shippingAmount": 0,
                      "totalAmount": 27.5,
                      "paymentTerms": "NET_30",
                      "convertedToSalesOrderId": null,
                      "items": [
                        {
                          "productId": "5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b",
                          "variantId": "9d2f6b1c-8e47-4a30-b5c2-0e1f3a4b5c6d",
                          "sku": "SKU-1001",
                          "name": "Sample Widget, Large",
                          "quantity": 2,
                          "uomId": "c4a1e7f2-3b58-4c19-8e6d-5a2b9f0d7e31",
                          "unitPrice": 12.5,
                          "discountPercent": 0,
                          "discountAmount": 0,
                          "taxRate": 10,
                          "taxAmount": 2.5,
                          "totalAmount": 27.5
                        }
                      ],
                      "updatedAt": "2026-01-15T09:30:00.000Z"
                    }
                  ],
                  "page": {
                    "hasMore": true,
                    "nextCursor": "bzo1MA"
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid: a field failed validation, an id is malformed, or the request is not allowed in the current state. `message` says which.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 400,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/sales-quotes",
                  "method": "GET",
                  "message": [
                    "limit must not be greater than 100"
                  ],
                  "error": "Bad Request",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "401": {
            "description": "The API key is missing, malformed, revoked or expired, the user who created the key is no longer active, the request comes from an address the key does not allow, or the key is read-only and the route writes. The rate-limit headers are not sent.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 401,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/sales-quotes",
                  "method": "GET",
                  "message": "Invalid API key",
                  "error": "Unauthorized",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but not allowed to do this: the workspace's plan does not include API access, or the user who created the key lacks the permission this route needs. The rate-limit headers are not sent when the plan is the reason.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 403,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/sales-quotes",
                  "method": "GET",
                  "message": "Missing required permission: products:product:read",
                  "error": "Forbidden",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "429": {
            "description": "The API key exceeded its requests-per-minute limit; wait `Retry-After` seconds. A 429 can also come from the separate per-address limit (600 requests a minute), which sends `X-RateLimit-*` headers instead of the `X-ApiKey-RateLimit-*` ones.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 429,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/sales-quotes",
                  "method": "GET",
                  "error": "Too Many Requests",
                  "code": "API_KEY_RATE_LIMITED",
                  "message": "This API key is limited to 100 requests per minute. Retry after 42 seconds.",
                  "retryAfterSeconds": 42,
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "List sales quotes",
        "tags": [
          "Sales quotes"
        ],
        "x-required-permission": [
          "sales:quote:read"
        ]
      },
      "post": {
        "description": "Creates a quote for a customer. Needs a read-write API key. Send the lines, the discount, tax and shipping settings, and the document totals (`subtotal`, `discountAmount`, `taxAmount`, `totalAmount`) as your system computed them. The quote is created in the status you give, `DRAFT` by default, and the response is the stored quote. Send an `Idempotency-Key` so a retry never creates a second quote. Creating a quote counts against the workspace plan's monthly quote limit.",
        "operationId": "createSalesQuote",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "A unique value you choose for this create (a UUID is a good choice). Retrying with the same key and the same body returns the original document instead of creating a second one; the same key with a different body is refused with 409. You can send the same value as `idempotencyKey` in the body instead.",
            "required": false,
            "schema": {
              "type": "string",
              "example": "11111111-2222-4333-8444-555555555555"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "The quote to create.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateQuoteInput"
              },
              "examples": {
                "example": {
                  "summary": "Example",
                  "value": {
                    "customerId": "e8d3b5a9-1c47-4f62-9b0e-6a7c2d4f1b83",
                    "currency": "USD",
                    "validUntil": "2026-02-14",
                    "paymentTerms": "NET_30",
                    "reference": "INT-0001",
                    "discountType": "PERCENTAGE",
                    "discountValue": 0,
                    "taxRate": 10,
                    "shippingCost": 0,
                    "items": [
                      {
                        "productId": "5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b",
                        "variantId": "9d2f6b1c-8e47-4a30-b5c2-0e1f3a4b5c6d",
                        "productName": "Sample Widget",
                        "productSku": "SKU-1001",
                        "quantity": 2,
                        "uomId": "c4a1e7f2-3b58-4c19-8e6d-5a2b9f0d7e31",
                        "unitPrice": 12.5,
                        "discount": 0,
                        "discountType": "PERCENTAGE",
                        "taxRate": 10,
                        "taxAmount": 2.5
                      }
                    ],
                    "subtotal": 25,
                    "discountAmount": 0,
                    "taxAmount": 2.5,
                    "totalAmount": 27.5
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The quote that was created.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceQuoteResponse"
                },
                "example": {
                  "success": true,
                  "data": {
                    "id": "b7e2c9d4-5a18-4f36-8c0b-1d9e4a7f3c25",
                    "quoteNumber": "QT-000001",
                    "status": "DRAFT",
                    "customerId": "e8d3b5a9-1c47-4f62-9b0e-6a7c2d4f1b83",
                    "quoteDate": "2026-01-15T09:30:00.000Z",
                    "validUntil": "2026-02-14T09:30:00.000Z",
                    "currency": "USD",
                    "exchangeRate": 1,
                    "subtotal": 25,
                    "discountAmount": 0,
                    "taxAmount": 2.5,
                    "shippingAmount": 0,
                    "totalAmount": 27.5,
                    "paymentTerms": "NET_30",
                    "convertedToSalesOrderId": null,
                    "items": [
                      {
                        "productId": "5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b",
                        "variantId": "9d2f6b1c-8e47-4a30-b5c2-0e1f3a4b5c6d",
                        "sku": "SKU-1001",
                        "name": "Sample Widget, Large",
                        "quantity": 2,
                        "uomId": "c4a1e7f2-3b58-4c19-8e6d-5a2b9f0d7e31",
                        "unitPrice": 12.5,
                        "discountPercent": 0,
                        "discountAmount": 0,
                        "taxRate": 10,
                        "taxAmount": 2.5,
                        "totalAmount": 27.5
                      }
                    ],
                    "updatedAt": "2026-01-15T09:30:00.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid: a field failed validation, an id is malformed, or the request is not allowed in the current state. `message` says which.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 400,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/sales-quotes",
                  "method": "POST",
                  "message": [
                    "limit must not be greater than 100"
                  ],
                  "error": "Bad Request",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "401": {
            "description": "The API key is missing, malformed, revoked or expired, the user who created the key is no longer active, the request comes from an address the key does not allow, or the key is read-only and the route writes. The rate-limit headers are not sent.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 401,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/sales-quotes",
                  "method": "POST",
                  "message": "Invalid API key",
                  "error": "Unauthorized",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "402": {
            "description": "The workspace has reached a limit of its plan, so the document was not created. `requiredPlan` names the plan that lifts the limit when one does.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 402,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/sales-quotes",
                  "method": "POST",
                  "message": "Monthly quote limit reached (50/50). Please upgrade your plan.",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but not allowed to do this: the workspace's plan does not include API access, or the user who created the key lacks the permission this route needs. The rate-limit headers are not sent when the plan is the reason.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 403,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/sales-quotes",
                  "method": "POST",
                  "message": "Missing required permission: products:product:read",
                  "error": "Forbidden",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "409": {
            "description": "The `Idempotency-Key` cannot be used for this request. `code` says why: `IDEMPOTENCY_KEY_CONFLICT` — the key was already used with a different request body (send the original body again, or use a new key for a new request); `IDEMPOTENCY_IN_PROGRESS` — the first request with this key is still running (retry the same request shortly); `IDEMPOTENCY_OUTCOME_UNKNOWN` — the earlier attempt may have created the document (look for it before trying again; a new key does not resolve this).",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "examples": {
                  "keyConflict": {
                    "summary": "Key reused with a different body",
                    "value": {
                      "statusCode": 409,
                      "timestamp": "2026-01-15T09:30:00.000Z",
                      "path": "/api/service/sales-quotes",
                      "method": "POST",
                      "code": "IDEMPOTENCY_KEY_CONFLICT",
                      "message": "Idempotency key \"11111111-2222-4333-8444-555555555555\" was already used for a different \"createQuote\" request. Use a new key for a new request.",
                      "operation": "createQuote",
                      "idempotencyKey": "11111111-2222-4333-8444-555555555555",
                      "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                    }
                  },
                  "inProgress": {
                    "summary": "First request still running",
                    "value": {
                      "statusCode": 409,
                      "timestamp": "2026-01-15T09:30:00.000Z",
                      "path": "/api/service/sales-quotes",
                      "method": "POST",
                      "code": "IDEMPOTENCY_IN_PROGRESS",
                      "message": "A \"createQuote\" request with this idempotency key is already in progress. Retry shortly.",
                      "operation": "createQuote",
                      "idempotencyKey": "11111111-2222-4333-8444-555555555555",
                      "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                    }
                  },
                  "outcomeUnknown": {
                    "summary": "Earlier attempt may have succeeded",
                    "value": {
                      "statusCode": 409,
                      "timestamp": "2026-01-15T09:30:00.000Z",
                      "path": "/api/service/sales-quotes",
                      "method": "POST",
                      "code": "IDEMPOTENCY_OUTCOME_UNKNOWN",
                      "operation": "createQuote",
                      "idempotencyKey": "11111111-2222-4333-8444-555555555555",
                      "message": "The previous attempt may have changed data. Review its receipt and reconcile or undo the effects before approving another attempt. A new key does not resolve an uncertain outcome.",
                      "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "The API key exceeded its requests-per-minute limit; wait `Retry-After` seconds. A 429 can also come from the separate per-address limit (600 requests a minute), which sends `X-RateLimit-*` headers instead of the `X-ApiKey-RateLimit-*` ones.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 429,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/sales-quotes",
                  "method": "POST",
                  "error": "Too Many Requests",
                  "code": "API_KEY_RATE_LIMITED",
                  "message": "This API key is limited to 100 requests per minute. Retry after 42 seconds.",
                  "retryAfterSeconds": 42,
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Create a sales quote",
        "tags": [
          "Sales quotes"
        ],
        "x-required-scope": "READ_WRITE"
      }
    },
    "/api/service/sales-quotes/{id}": {
      "get": {
        "description": "Returns one quote with its lines.",
        "operationId": "getSalesQuote",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Quote id.",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The quote.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceQuoteResponse"
                },
                "example": {
                  "success": true,
                  "data": {
                    "id": "b7e2c9d4-5a18-4f36-8c0b-1d9e4a7f3c25",
                    "quoteNumber": "QT-000001",
                    "status": "DRAFT",
                    "customerId": "e8d3b5a9-1c47-4f62-9b0e-6a7c2d4f1b83",
                    "quoteDate": "2026-01-15T09:30:00.000Z",
                    "validUntil": "2026-02-14T09:30:00.000Z",
                    "currency": "USD",
                    "exchangeRate": 1,
                    "subtotal": 25,
                    "discountAmount": 0,
                    "taxAmount": 2.5,
                    "shippingAmount": 0,
                    "totalAmount": 27.5,
                    "paymentTerms": "NET_30",
                    "convertedToSalesOrderId": null,
                    "items": [
                      {
                        "productId": "5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b",
                        "variantId": "9d2f6b1c-8e47-4a30-b5c2-0e1f3a4b5c6d",
                        "sku": "SKU-1001",
                        "name": "Sample Widget, Large",
                        "quantity": 2,
                        "uomId": "c4a1e7f2-3b58-4c19-8e6d-5a2b9f0d7e31",
                        "unitPrice": 12.5,
                        "discountPercent": 0,
                        "discountAmount": 0,
                        "taxRate": 10,
                        "taxAmount": 2.5,
                        "totalAmount": 27.5
                      }
                    ],
                    "updatedAt": "2026-01-15T09:30:00.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid: a field failed validation, an id is malformed, or the request is not allowed in the current state. `message` says which.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 400,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/sales-quotes/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b",
                  "method": "GET",
                  "message": [
                    "limit must not be greater than 100"
                  ],
                  "error": "Bad Request",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "401": {
            "description": "The API key is missing, malformed, revoked or expired, the user who created the key is no longer active, the request comes from an address the key does not allow, or the key is read-only and the route writes. The rate-limit headers are not sent.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 401,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/sales-quotes/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b",
                  "method": "GET",
                  "message": "Invalid API key",
                  "error": "Unauthorized",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but not allowed to do this: the workspace's plan does not include API access, or the user who created the key lacks the permission this route needs. The rate-limit headers are not sent when the plan is the reason.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 403,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/sales-quotes/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b",
                  "method": "GET",
                  "message": "Missing required permission: products:product:read",
                  "error": "Forbidden",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "404": {
            "description": "No quote with this id in the workspace.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 404,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/sales-quotes/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b",
                  "method": "GET",
                  "message": "Not found",
                  "error": "Not Found",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "429": {
            "description": "The API key exceeded its requests-per-minute limit; wait `Retry-After` seconds. A 429 can also come from the separate per-address limit (600 requests a minute), which sends `X-RateLimit-*` headers instead of the `X-ApiKey-RateLimit-*` ones.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 429,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/sales-quotes/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b",
                  "method": "GET",
                  "error": "Too Many Requests",
                  "code": "API_KEY_RATE_LIMITED",
                  "message": "This API key is limited to 100 requests per minute. Retry after 42 seconds.",
                  "retryAfterSeconds": 42,
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Get a sales quote",
        "tags": [
          "Sales quotes"
        ],
        "x-required-permission": [
          "sales:quote:read"
        ]
      }
    },
    "/api/service/products/bulk": {
      "post": {
        "description": "Creates an import and returns a time-limited upload URL. Upload your CSV or Excel file to that URL with an HTTP PUT, then call the stage route. A workspace can have one active import at a time; starting another while one is unfinished is refused. Needs a read-write API key.",
        "operationId": "createProductImport",
        "parameters": [],
        "requestBody": {
          "required": true,
          "description": "The file you are about to upload.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ServiceCreateProductImportBody"
              },
              "examples": {
                "example": {
                  "summary": "Example",
                  "value": {
                    "filename": "products.csv",
                    "mode": "CREATE_ONLY"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The new import and where to upload its file.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceProductImportCreatedResponse"
                },
                "example": {
                  "success": true,
                  "data": {
                    "importId": "6c9e3a1f-4d72-4b85-9f06-2b8a7d5c1e93",
                    "uploadUrl": "https://uploads.example.com/product-imports/products.csv?signature=example",
                    "fileRef": "product-imports/products.csv"
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid: a field failed validation, an id is malformed, or the request is not allowed in the current state. `message` says which.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 400,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/products/bulk",
                  "method": "POST",
                  "message": [
                    "limit must not be greater than 100"
                  ],
                  "error": "Bad Request",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "401": {
            "description": "The API key is missing, malformed, revoked or expired, the user who created the key is no longer active, the request comes from an address the key does not allow, or the key is read-only and the route writes. The rate-limit headers are not sent.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 401,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/products/bulk",
                  "method": "POST",
                  "message": "Invalid API key",
                  "error": "Unauthorized",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but not allowed to do this: the workspace's plan does not include API access, or the user who created the key lacks the permission this route needs. The rate-limit headers are not sent when the plan is the reason.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 403,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/products/bulk",
                  "method": "POST",
                  "message": "Missing required permission: products:product:read",
                  "error": "Forbidden",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "429": {
            "description": "The API key exceeded its requests-per-minute limit; wait `Retry-After` seconds. A 429 can also come from the separate per-address limit (600 requests a minute), which sends `X-RateLimit-*` headers instead of the `X-ApiKey-RateLimit-*` ones.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 429,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/products/bulk",
                  "method": "POST",
                  "error": "Too Many Requests",
                  "code": "API_KEY_RATE_LIMITED",
                  "message": "This API key is limited to 100 requests per minute. Retry after 42 seconds.",
                  "retryAfterSeconds": 42,
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Start a bulk product import",
        "tags": [
          "Bulk product import"
        ],
        "x-required-scope": "READ_WRITE"
      }
    },
    "/api/service/products/bulk/{importId}/stage": {
      "post": {
        "description": "Reads the uploaded file and checks every row in the background. Call it once the upload has finished, then poll the status route until the import reaches `PREVIEW`. Needs a read-write API key.",
        "operationId": "stageProductImport",
        "parameters": [
          {
            "name": "importId",
            "required": true,
            "in": "path",
            "description": "Import id, from the create call.",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Staging has started.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceAckResponse"
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "The import id is unknown, or the import is not in the state this step needs.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 400,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/products/bulk/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b/stage",
                  "method": "POST",
                  "message": [
                    "limit must not be greater than 100"
                  ],
                  "error": "Bad Request",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "401": {
            "description": "The API key is missing, malformed, revoked or expired, the user who created the key is no longer active, the request comes from an address the key does not allow, or the key is read-only and the route writes. The rate-limit headers are not sent.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 401,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/products/bulk/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b/stage",
                  "method": "POST",
                  "message": "Invalid API key",
                  "error": "Unauthorized",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but not allowed to do this: the workspace's plan does not include API access, or the user who created the key lacks the permission this route needs. The rate-limit headers are not sent when the plan is the reason.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 403,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/products/bulk/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b/stage",
                  "method": "POST",
                  "message": "Missing required permission: products:product:read",
                  "error": "Forbidden",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "429": {
            "description": "The API key exceeded its requests-per-minute limit; wait `Retry-After` seconds. A 429 can also come from the separate per-address limit (600 requests a minute), which sends `X-RateLimit-*` headers instead of the `X-ApiKey-RateLimit-*` ones.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 429,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/products/bulk/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b/stage",
                  "method": "POST",
                  "error": "Too Many Requests",
                  "code": "API_KEY_RATE_LIMITED",
                  "message": "This API key is limited to 100 requests per minute. Retry after 42 seconds.",
                  "retryAfterSeconds": 42,
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Stage an uploaded file",
        "tags": [
          "Bulk product import"
        ],
        "x-required-scope": "READ_WRITE"
      }
    },
    "/api/service/products/bulk/{importId}/cancel": {
      "post": {
        "description": "Abandons an import that has not started committing — uploaded, staging or in preview — so the workspace can start another one. The import moves to `FAILED` with the reason `Cancelled by user`. An import that is committing or completed cannot be cancelled; roll a completed import back instead. Needs a read-write API key.",
        "operationId": "cancelProductImport",
        "parameters": [
          {
            "name": "importId",
            "required": true,
            "in": "path",
            "description": "Import id, from the create call.",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "The import is cancelled.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceAckResponse"
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "The import id is unknown, or the import has already started committing or completed.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 400,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/products/bulk/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b/cancel",
                  "method": "POST",
                  "message": [
                    "limit must not be greater than 100"
                  ],
                  "error": "Bad Request",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "401": {
            "description": "The API key is missing, malformed, revoked or expired, the user who created the key is no longer active, the request comes from an address the key does not allow, or the key is read-only and the route writes. The rate-limit headers are not sent.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 401,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/products/bulk/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b/cancel",
                  "method": "POST",
                  "message": "Invalid API key",
                  "error": "Unauthorized",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but not allowed to do this: the workspace's plan does not include API access, or the user who created the key lacks the permission this route needs. The rate-limit headers are not sent when the plan is the reason.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 403,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/products/bulk/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b/cancel",
                  "method": "POST",
                  "message": "Missing required permission: products:product:read",
                  "error": "Forbidden",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "429": {
            "description": "The API key exceeded its requests-per-minute limit; wait `Retry-After` seconds. A 429 can also come from the separate per-address limit (600 requests a minute), which sends `X-RateLimit-*` headers instead of the `X-ApiKey-RateLimit-*` ones.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 429,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/products/bulk/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b/cancel",
                  "method": "POST",
                  "error": "Too Many Requests",
                  "code": "API_KEY_RATE_LIMITED",
                  "message": "This API key is limited to 100 requests per minute. Retry after 42 seconds.",
                  "retryAfterSeconds": 42,
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Cancel an import",
        "tags": [
          "Bulk product import"
        ],
        "x-required-scope": "READ_WRITE"
      }
    },
    "/api/service/products/bulk/{importId}/preview": {
      "get": {
        "description": "Returns the counts, a sample of failing rows and a sample of the rows that will be written. Review it before approving. Available once the import is in `PREVIEW`.",
        "operationId": "previewProductImport",
        "parameters": [
          {
            "name": "importId",
            "required": true,
            "in": "path",
            "description": "Import id, from the create call.",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The preview.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceProductImportPreviewResponse"
                },
                "example": {
                  "success": true,
                  "data": {
                    "importId": "6c9e3a1f-4d72-4b85-9f06-2b8a7d5c1e93",
                    "status": "PREVIEW",
                    "mode": "CREATE_ONLY",
                    "totalRows": 120,
                    "validRows": 118,
                    "errorRows": 1,
                    "warningRows": 1,
                    "createCount": 118,
                    "updateCount": 0,
                    "totalChunks": 3,
                    "sampleErrors": [
                      {
                        "rowNumber": 7,
                        "sku": "SKU-1007",
                        "errors": [
                          "Unknown unit of measure"
                        ]
                      }
                    ],
                    "sampleRows": [
                      {
                        "rowNumber": 2,
                        "sku": "SKU-1001",
                        "productName": "Sample Widget",
                        "action": "CREATE"
                      }
                    ],
                    "autoAppliedRules": [
                      "Unit \"pcs\" matched to \"EA\""
                    ],
                    "currencySummary": [
                      {
                        "currency": "USD",
                        "isBase": true,
                        "rowCount": 118
                      }
                    ]
                  }
                }
              }
            }
          },
          "400": {
            "description": "The import id is unknown, or the import is not in the state this step needs.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 400,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/products/bulk/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b/preview",
                  "method": "GET",
                  "message": [
                    "limit must not be greater than 100"
                  ],
                  "error": "Bad Request",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "401": {
            "description": "The API key is missing, malformed, revoked or expired, the user who created the key is no longer active, the request comes from an address the key does not allow, or the key is read-only and the route writes. The rate-limit headers are not sent.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 401,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/products/bulk/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b/preview",
                  "method": "GET",
                  "message": "Invalid API key",
                  "error": "Unauthorized",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but not allowed to do this: the workspace's plan does not include API access, or the user who created the key lacks the permission this route needs. The rate-limit headers are not sent when the plan is the reason.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 403,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/products/bulk/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b/preview",
                  "method": "GET",
                  "message": "Missing required permission: products:product:read",
                  "error": "Forbidden",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "429": {
            "description": "The API key exceeded its requests-per-minute limit; wait `Retry-After` seconds. A 429 can also come from the separate per-address limit (600 requests a minute), which sends `X-RateLimit-*` headers instead of the `X-ApiKey-RateLimit-*` ones.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 429,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/products/bulk/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b/preview",
                  "method": "GET",
                  "error": "Too Many Requests",
                  "code": "API_KEY_RATE_LIMITED",
                  "message": "This API key is limited to 100 requests per minute. Retry after 42 seconds.",
                  "retryAfterSeconds": 42,
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Preview what an import will do",
        "tags": [
          "Bulk product import"
        ]
      }
    },
    "/api/service/products/bulk/{importId}/approve": {
      "post": {
        "description": "Approves a previewed import and starts writing the products in the background. Poll the status route until it reaches `COMPLETED`. Rows that failed validation are left out. Needs a read-write API key.",
        "operationId": "approveProductImport",
        "parameters": [
          {
            "name": "importId",
            "required": true,
            "in": "path",
            "description": "Import id, from the create call.",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "description": "Optional. Send `modeOverride` to run the import in a different mode than it was created with; send an empty body or none to keep it.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ServiceApproveProductImportBody"
              },
              "examples": {
                "example": {
                  "summary": "Example",
                  "value": {
                    "modeOverride": "UPSERT"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The import was approved and is being written.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceAckResponse"
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "The import id is unknown, or the import is not in the state this step needs.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 400,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/products/bulk/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b/approve",
                  "method": "POST",
                  "message": [
                    "limit must not be greater than 100"
                  ],
                  "error": "Bad Request",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "401": {
            "description": "The API key is missing, malformed, revoked or expired, the user who created the key is no longer active, the request comes from an address the key does not allow, or the key is read-only and the route writes. The rate-limit headers are not sent.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 401,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/products/bulk/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b/approve",
                  "method": "POST",
                  "message": "Invalid API key",
                  "error": "Unauthorized",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but not allowed to do this: the workspace's plan does not include API access, or the user who created the key lacks the permission this route needs. The rate-limit headers are not sent when the plan is the reason.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 403,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/products/bulk/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b/approve",
                  "method": "POST",
                  "message": "Missing required permission: products:product:read",
                  "error": "Forbidden",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "429": {
            "description": "The API key exceeded its requests-per-minute limit; wait `Retry-After` seconds. A 429 can also come from the separate per-address limit (600 requests a minute), which sends `X-RateLimit-*` headers instead of the `X-ApiKey-RateLimit-*` ones.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 429,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/products/bulk/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b/approve",
                  "method": "POST",
                  "error": "Too Many Requests",
                  "code": "API_KEY_RATE_LIMITED",
                  "message": "This API key is limited to 100 requests per minute. Retry after 42 seconds.",
                  "retryAfterSeconds": 42,
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Approve an import",
        "tags": [
          "Bulk product import"
        ],
        "x-required-scope": "READ_WRITE"
      }
    },
    "/api/service/products/bulk/{importId}/rollback": {
      "post": {
        "description": "Removes the products an import created. Products the import only updated are left as the import wrote them and are counted in `notRevertedCount`. Needs a read-write API key.",
        "operationId": "rollbackProductImport",
        "parameters": [
          {
            "name": "importId",
            "required": true,
            "in": "path",
            "description": "Import id, from the create call.",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "What the rollback removed.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceProductImportRollbackResponse"
                },
                "example": {
                  "success": true,
                  "data": {
                    "deletedVariants": 118,
                    "deactivatedVariants": 0,
                    "deletedProducts": 118,
                    "notRevertedCount": 0
                  }
                }
              }
            }
          },
          "400": {
            "description": "The import id is unknown, or the import is not in the state this step needs.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 400,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/products/bulk/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b/rollback",
                  "method": "POST",
                  "message": [
                    "limit must not be greater than 100"
                  ],
                  "error": "Bad Request",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "401": {
            "description": "The API key is missing, malformed, revoked or expired, the user who created the key is no longer active, the request comes from an address the key does not allow, or the key is read-only and the route writes. The rate-limit headers are not sent.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 401,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/products/bulk/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b/rollback",
                  "method": "POST",
                  "message": "Invalid API key",
                  "error": "Unauthorized",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but not allowed to do this: the workspace's plan does not include API access, or the user who created the key lacks the permission this route needs. The rate-limit headers are not sent when the plan is the reason.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 403,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/products/bulk/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b/rollback",
                  "method": "POST",
                  "message": "Missing required permission: products:product:read",
                  "error": "Forbidden",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "429": {
            "description": "The API key exceeded its requests-per-minute limit; wait `Retry-After` seconds. A 429 can also come from the separate per-address limit (600 requests a minute), which sends `X-RateLimit-*` headers instead of the `X-ApiKey-RateLimit-*` ones.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 429,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/products/bulk/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b/rollback",
                  "method": "POST",
                  "error": "Too Many Requests",
                  "code": "API_KEY_RATE_LIMITED",
                  "message": "This API key is limited to 100 requests per minute. Retry after 42 seconds.",
                  "retryAfterSeconds": 42,
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Roll back an import",
        "tags": [
          "Bulk product import"
        ],
        "x-required-scope": "READ_WRITE"
      }
    },
    "/api/service/products/bulk/{importId}": {
      "get": {
        "description": "Returns the import and its progress counts. Poll it while the import is staging or being written.",
        "operationId": "getProductImport",
        "parameters": [
          {
            "name": "importId",
            "required": true,
            "in": "path",
            "description": "Import id, from the create call.",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The import.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceProductImportResponse"
                },
                "example": {
                  "success": true,
                  "data": {
                    "id": "6c9e3a1f-4d72-4b85-9f06-2b8a7d5c1e93",
                    "filename": "products.csv",
                    "mode": "CREATE_ONLY",
                    "status": "PREVIEW",
                    "totalRows": 120,
                    "stagedRows": 120,
                    "validRows": 118,
                    "warningRows": 1,
                    "errorRows": 1,
                    "createCount": 118,
                    "updateCount": 0,
                    "skipCount": 0,
                    "failureReason": null,
                    "createdAt": "2026-01-15T09:30:00.000Z",
                    "startedAt": "2026-01-15T09:30:00.000Z",
                    "approvedAt": null,
                    "completedAt": null,
                    "rolledBackAt": null,
                    "updatedAt": "2026-01-15T09:30:00.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "The import id is unknown, or the import is not in the state this step needs.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 400,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/products/bulk/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b",
                  "method": "GET",
                  "message": [
                    "limit must not be greater than 100"
                  ],
                  "error": "Bad Request",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "401": {
            "description": "The API key is missing, malformed, revoked or expired, the user who created the key is no longer active, the request comes from an address the key does not allow, or the key is read-only and the route writes. The rate-limit headers are not sent.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 401,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/products/bulk/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b",
                  "method": "GET",
                  "message": "Invalid API key",
                  "error": "Unauthorized",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but not allowed to do this: the workspace's plan does not include API access, or the user who created the key lacks the permission this route needs. The rate-limit headers are not sent when the plan is the reason.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 403,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/products/bulk/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b",
                  "method": "GET",
                  "message": "Missing required permission: products:product:read",
                  "error": "Forbidden",
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          },
          "429": {
            "description": "The API key exceeded its requests-per-minute limit; wait `Retry-After` seconds. A 429 can also come from the separate per-address limit (600 requests a minute), which sends `X-RateLimit-*` headers instead of the `X-ApiKey-RateLimit-*` ones.",
            "headers": {
              "X-ApiKey-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-ApiKey-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-ApiKey-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceError"
                },
                "example": {
                  "statusCode": 429,
                  "timestamp": "2026-01-15T09:30:00.000Z",
                  "path": "/api/service/products/bulk/5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b",
                  "method": "GET",
                  "error": "Too Many Requests",
                  "code": "API_KEY_RATE_LIMITED",
                  "message": "This API key is limited to 100 requests per minute. Retry after 42 seconds.",
                  "retryAfterSeconds": 42,
                  "requestId": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Get an import",
        "tags": [
          "Bulk product import"
        ]
      }
    }
  },
  "info": {
    "title": "Growmax ARC Service API",
    "description": "Server-to-server REST API for a Growmax ARC workspace: read the catalogue, customers, quotes and orders, create quotes and sales orders, and run bulk product imports.\n\n**Authentication.** Send a workspace API key as `Authorization: Bearer gm_live_...`. Create keys in the workspace's API key settings. A `READ_ONLY` key can call every read route; creating or changing anything needs a `READ_WRITE` key.\n\n**Permissions.** A key acts as the user who created it: each read route needs that user's current read permission (named in `x-required-permission`), and deactivating the user disables the key. The workspace's plan must include API access.\n\n**Paging.** The product, customer, sales order and sales quote lists take `limit` and `cursor` and return `page.nextCursor`. To keep a copy in sync, store the newest `updatedAt` you have seen and pass it as `updatedAfter` next time. On the product and customer lists, rows removed since then come back with `deleted: true`; the order and quote lists do not report removals.\n\n**Money.** Amounts are plain numbers next to an ISO 4217 currency code. They are never formatted.\n\n**Rate limits.** Every key has a requests-per-minute limit, reported in the `X-ApiKey-RateLimit-*` response headers. Over it, you get `429` with `Retry-After`.\n\n**Retries.** Creates accept an `Idempotency-Key` header, so a retried request never creates a second document.",
    "version": "2026-10-20",
    "contact": {}
  },
  "tags": [
    {
      "name": "Products",
      "description": "Catalogue products and their variants, prices and units."
    },
    {
      "name": "Reference data",
      "description": "Units of measure, payment terms and warehouses."
    },
    {
      "name": "Stock",
      "description": "Stock levels per variant and warehouse."
    },
    {
      "name": "Customers",
      "description": "Customers with their addresses and contacts."
    },
    {
      "name": "Sales quotes",
      "description": "Read quotes and create new ones."
    },
    {
      "name": "Sales orders",
      "description": "Read sales orders and create new ones."
    },
    {
      "name": "Bulk product import",
      "description": "Upload, preview, approve and roll back a bulk product import."
    }
  ],
  "servers": [
    {
      "url": "https://{workspaceHost}",
      "description": "Your workspace",
      "variables": {
        "workspaceHost": {
          "default": "your-workspace.example.com",
          "description": "The host name of your workspace."
        }
      }
    }
  ],
  "components": {
    "securitySchemes": {
      "ApiKey": {
        "scheme": "bearer",
        "bearerFormat": "gm_live_…",
        "type": "http",
        "description": "Tenant API key, `gm_live_...`. Send it as `Authorization: Bearer <key>`."
      }
    },
    "schemas": {
      "ServiceNamedRef": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Id.",
            "example": "5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b"
          },
          "name": {
            "type": "string",
            "nullable": true,
            "description": "Name.",
            "example": "Sample"
          }
        },
        "required": [
          "id",
          "name"
        ]
      },
      "ServiceUomRef": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "nullable": true,
            "description": "Unit id.",
            "example": "5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b"
          },
          "code": {
            "type": "string",
            "nullable": true,
            "description": "Unit code.",
            "example": "EA"
          },
          "name": {
            "type": "string",
            "nullable": true,
            "description": "Unit name.",
            "example": "Each"
          },
          "symbol": {
            "type": "string",
            "nullable": true,
            "description": "Display symbol.",
            "example": "ea"
          }
        },
        "required": [
          "id",
          "code",
          "name",
          "symbol"
        ]
      },
      "ServiceVariantPrice": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "nullable": true,
            "description": "Name of the price tier.",
            "example": "Standard"
          },
          "minQuantity": {
            "type": "number",
            "nullable": true,
            "description": "Smallest quantity the tier applies to.",
            "example": 1
          },
          "maxQuantity": {
            "type": "number",
            "nullable": true,
            "description": "Largest quantity the tier applies to; null when unbounded.",
            "example": 9
          },
          "price": {
            "type": "number",
            "nullable": true,
            "description": "Unit price in the tier's currency.",
            "example": 12.5
          },
          "discountPercent": {
            "type": "number",
            "nullable": true,
            "description": "Discount percent the tier carries, when set.",
            "example": 0
          },
          "uomId": {
            "type": "string",
            "nullable": true,
            "format": "uuid",
            "description": "The unit the tier is priced in.",
            "example": "5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b"
          },
          "currency": {
            "type": "string",
            "nullable": true,
            "description": "ISO 4217 currency code of `price`.",
            "example": "USD"
          },
          "validFrom": {
            "type": "string",
            "nullable": true,
            "format": "date-time",
            "description": "Tier is valid from this instant.",
            "example": "2026-01-15T09:30:00.000Z"
          },
          "validTo": {
            "type": "string",
            "nullable": true,
            "format": "date-time",
            "description": "Tier is valid until this instant.",
            "example": "2026-01-15T09:30:00.000Z"
          },
          "isActive": {
            "type": "boolean",
            "nullable": true,
            "description": "False when the tier is switched off.",
            "example": true
          }
        },
        "required": [
          "name",
          "minQuantity",
          "maxQuantity",
          "price",
          "discountPercent",
          "uomId",
          "currency",
          "validFrom",
          "validTo",
          "isActive"
        ]
      },
      "ServiceVariant": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Variant id. Use it as `variantId` when creating quotes and orders.",
            "example": "5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b"
          },
          "productId": {
            "type": "string",
            "nullable": true,
            "format": "uuid",
            "description": "The product the variant belongs to.",
            "example": "9d2f6b1c-8e47-4a30-b5c2-0e1f3a4b5c6d"
          },
          "sku": {
            "type": "string",
            "nullable": true,
            "description": "Stock keeping unit.",
            "example": "SKU-1001"
          },
          "name": {
            "type": "string",
            "nullable": true,
            "description": "Variant name.",
            "example": "Sample Widget, Large"
          },
          "barcode": {
            "type": "string",
            "nullable": true,
            "description": "Barcode.",
            "example": "0123456789012"
          },
          "attributes": {
            "type": "object",
            "additionalProperties": true,
            "nullable": true,
            "description": "Variant attributes as key/value pairs.",
            "example": {
              "size": "Large"
            }
          },
          "price": {
            "type": "number",
            "nullable": true,
            "description": "List price in the workspace currency.",
            "example": 15
          },
          "salePrice": {
            "type": "number",
            "nullable": true,
            "description": "Sale price in the workspace currency.",
            "example": 12.5
          },
          "isActive": {
            "type": "boolean",
            "nullable": true,
            "description": "False when the variant is switched off.",
            "example": true
          },
          "isDefault": {
            "type": "boolean",
            "nullable": true,
            "description": "True for the product's default variant.",
            "example": true
          },
          "minOrderQuantity": {
            "type": "number",
            "nullable": true,
            "description": "Smallest quantity that can be ordered.",
            "example": 1
          },
          "maxOrderQuantity": {
            "type": "number",
            "nullable": true,
            "description": "Largest quantity that can be ordered.",
            "example": 1000
          },
          "salesUom": {
            "nullable": true,
            "description": "The unit the variant is sold in. Present on list rows only.",
            "type": "object",
            "allOf": [
              {
                "$ref": "#/components/schemas/ServiceUomRef"
              }
            ]
          },
          "prices": {
            "description": "The variant's price tiers, one entry per quantity break and currency. Present on the single-product read only.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ServiceVariantPrice"
            }
          },
          "updatedAt": {
            "type": "string",
            "nullable": true,
            "format": "date-time",
            "description": "When the variant last changed.",
            "example": "2026-01-15T09:30:00.000Z"
          }
        },
        "required": [
          "id",
          "productId",
          "sku",
          "name",
          "barcode",
          "attributes",
          "price",
          "salePrice",
          "isActive",
          "isDefault",
          "minOrderQuantity",
          "maxOrderQuantity",
          "updatedAt"
        ]
      },
      "ServiceProductUom": {
        "type": "object",
        "properties": {
          "uomId": {
            "type": "string",
            "nullable": true,
            "format": "uuid",
            "description": "The unit id.",
            "example": "5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b"
          },
          "variantId": {
            "type": "string",
            "nullable": true,
            "format": "uuid",
            "description": "The variant this unit setting is for; null when it applies to the product.",
            "example": "5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b"
          },
          "uom": {
            "nullable": true,
            "description": "The unit.",
            "type": "object",
            "allOf": [
              {
                "$ref": "#/components/schemas/ServiceUomRef"
              }
            ]
          },
          "isBase": {
            "type": "boolean",
            "nullable": true,
            "description": "True when this is the product's base unit.",
            "example": true
          },
          "isSales": {
            "type": "boolean",
            "nullable": true,
            "description": "True when the unit can be sold.",
            "example": true
          },
          "conversionFactor": {
            "type": "number",
            "nullable": true,
            "description": "How many base units one of this unit contains.",
            "example": 1
          },
          "minOrderQty": {
            "type": "number",
            "nullable": true,
            "description": "Smallest quantity that can be ordered in this unit.",
            "example": 1
          },
          "maxOrderQty": {
            "type": "number",
            "nullable": true,
            "description": "Largest quantity that can be ordered in this unit.",
            "example": 1000
          },
          "orderMultiple": {
            "type": "number",
            "nullable": true,
            "description": "Quantities must be a multiple of this.",
            "example": 1
          },
          "listPrice": {
            "type": "number",
            "nullable": true,
            "description": "List price in this unit, in the workspace currency.",
            "example": 15
          },
          "salePrice": {
            "type": "number",
            "nullable": true,
            "description": "Sale price in this unit, in the workspace currency.",
            "example": 12.5
          },
          "barcode": {
            "type": "string",
            "nullable": true,
            "description": "Barcode for this unit.",
            "example": "0123456789012"
          }
        },
        "required": [
          "uomId",
          "variantId",
          "uom",
          "isBase",
          "isSales",
          "conversionFactor",
          "minOrderQty",
          "maxOrderQty",
          "orderMultiple",
          "listPrice",
          "salePrice",
          "barcode"
        ]
      },
      "ServiceProduct": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Product id.",
            "example": "5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b"
          },
          "name": {
            "type": "string",
            "nullable": true,
            "description": "Product name.",
            "example": "Sample Widget"
          },
          "description": {
            "type": "string",
            "nullable": true,
            "description": "Product description.",
            "example": "A sample product used in documentation."
          },
          "status": {
            "nullable": true,
            "description": "Lifecycle status.",
            "type": "string",
            "enum": [
              "ACTIVE",
              "INACTIVE",
              "DISCONTINUED",
              null
            ]
          },
          "brand": {
            "nullable": true,
            "description": "The product's brand.",
            "type": "object",
            "allOf": [
              {
                "$ref": "#/components/schemas/ServiceNamedRef"
              }
            ]
          },
          "categories": {
            "description": "Categories the product is filed under.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ServiceNamedRef"
            }
          },
          "tags": {
            "description": "Free-form tags.",
            "example": [
              "sample"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "imageUrls": {
            "description": "Image URLs, first is the primary image.",
            "example": [
              "https://cdn.example.com/images/widget.jpg"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "isTaxExempt": {
            "type": "boolean",
            "nullable": true,
            "description": "True when the product is exempt from tax.",
            "example": false
          },
          "baseUom": {
            "nullable": true,
            "description": "The unit stock is held in.",
            "type": "object",
            "allOf": [
              {
                "$ref": "#/components/schemas/ServiceUomRef"
              }
            ]
          },
          "salesUom": {
            "nullable": true,
            "description": "The unit the product is sold in.",
            "type": "object",
            "allOf": [
              {
                "$ref": "#/components/schemas/ServiceUomRef"
              }
            ]
          },
          "variants": {
            "description": "The product's variants.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ServiceVariant"
            }
          },
          "uoms": {
            "description": "Every unit the product can be sold in, with conversion factors. Present on the single-product read only.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ServiceProductUom"
            }
          },
          "deleted": {
            "type": "boolean",
            "description": "Always present. True only for a removed product, which a delta pull (`updatedAfter`) returns so a sync can drop the row.",
            "example": false
          },
          "updatedAt": {
            "type": "string",
            "nullable": true,
            "format": "date-time",
            "description": "When the product last changed. Use the newest value as the next `updatedAfter`.",
            "example": "2026-01-15T09:30:00.000Z"
          }
        },
        "required": [
          "id",
          "name",
          "description",
          "status",
          "brand",
          "categories",
          "tags",
          "imageUrls",
          "isTaxExempt",
          "baseUom",
          "salesUom",
          "variants",
          "deleted",
          "updatedAt"
        ]
      },
      "ServicePage": {
        "type": "object",
        "properties": {
          "hasMore": {
            "type": "boolean",
            "description": "True when more rows follow this page.",
            "example": true
          },
          "nextCursor": {
            "type": "string",
            "nullable": true,
            "description": "Opaque cursor. Pass it back as the `cursor` query parameter to fetch the next page; null on the last page.",
            "example": "bzo1MA"
          }
        },
        "required": [
          "hasMore",
          "nextCursor"
        ]
      },
      "ServiceProductListResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "Always true on a 2xx response.",
            "example": true
          },
          "data": {
            "description": "One page of products.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ServiceProduct"
            }
          },
          "page": {
            "description": "Paging state.",
            "allOf": [
              {
                "$ref": "#/components/schemas/ServicePage"
              }
            ]
          }
        },
        "required": [
          "success",
          "data",
          "page"
        ]
      },
      "ServiceError": {
        "type": "object",
        "properties": {
          "statusCode": {
            "type": "number",
            "description": "HTTP status code.",
            "example": 400
          },
          "timestamp": {
            "type": "string",
            "format": "date-time",
            "description": "When the error was produced.",
            "example": "2026-01-15T09:30:00.000Z"
          },
          "path": {
            "type": "string",
            "description": "The request path.",
            "example": "/api/service/products"
          },
          "method": {
            "type": "string",
            "description": "The request method.",
            "example": "GET"
          },
          "message": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            ],
            "description": "What went wrong. A list when several request fields failed validation.",
            "example": "limit must not be greater than 100"
          },
          "error": {
            "type": "string",
            "description": "Short name of the HTTP status.",
            "example": "Bad Request"
          },
          "code": {
            "type": "string",
            "description": "Machine-readable code, present on some errors (for example when a key's rate limit is hit).",
            "example": "API_KEY_RATE_LIMITED"
          },
          "operation": {
            "type": "string",
            "description": "On an idempotency conflict: the operation the key was used for.",
            "example": "createSalesOrder"
          },
          "idempotencyKey": {
            "type": "string",
            "description": "On an idempotency conflict: the `Idempotency-Key` involved.",
            "example": "11111111-2222-4333-8444-555555555555"
          },
          "requiredPlan": {
            "type": "string",
            "description": "On a plan-limit refusal (402): the plan that lifts the limit, when there is one.",
            "example": "PROFESSIONAL"
          },
          "retryAfterSeconds": {
            "type": "number",
            "description": "On a rate-limit refusal (429): seconds to wait before retrying.",
            "example": 42
          },
          "requestId": {
            "type": "string",
            "description": "The request id. It matches the `x-request-id` response header; quote it when contacting support.",
            "example": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
          }
        },
        "required": [
          "statusCode",
          "timestamp",
          "path",
          "method",
          "message"
        ]
      },
      "ServiceProductResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "Always true on a 2xx response.",
            "example": true
          },
          "data": {
            "description": "The product, with its price tiers and every unit it can be sold in.",
            "allOf": [
              {
                "$ref": "#/components/schemas/ServiceProduct"
              }
            ]
          }
        },
        "required": [
          "success",
          "data"
        ]
      },
      "ServiceUom": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Unit id.",
            "example": "5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b"
          },
          "code": {
            "type": "string",
            "description": "Unit code, unique within the workspace.",
            "example": "EA"
          },
          "name": {
            "type": "string",
            "description": "Unit name.",
            "example": "Each"
          },
          "symbol": {
            "type": "string",
            "nullable": true,
            "description": "Display symbol.",
            "example": "ea"
          },
          "category": {
            "nullable": true,
            "description": "What the unit measures.",
            "type": "string",
            "enum": [
              "COUNT",
              "WEIGHT",
              "LENGTH",
              "VOLUME",
              "AREA",
              "TIME",
              "TEMPERATURE",
              "CUSTOM",
              null
            ]
          },
          "isBase": {
            "type": "boolean",
            "description": "True for the base unit of its category.",
            "example": false
          },
          "isActive": {
            "type": "boolean",
            "description": "False once the unit is retired.",
            "example": true
          },
          "decimalPlaces": {
            "type": "number",
            "nullable": true,
            "description": "Decimal places quantities in this unit are kept to.",
            "example": 0
          },
          "allowFractional": {
            "type": "boolean",
            "description": "True when quantities in this unit may be fractional.",
            "example": false
          }
        },
        "required": [
          "id",
          "code",
          "name",
          "symbol",
          "category",
          "isBase",
          "isActive",
          "decimalPlaces",
          "allowFractional"
        ]
      },
      "ServiceUomListResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "Always true on a 2xx response.",
            "example": true
          },
          "data": {
            "description": "Every active unit of measure.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ServiceUom"
            }
          }
        },
        "required": [
          "success",
          "data"
        ]
      },
      "ServicePaymentTerm": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "description": "Payment terms code. Use it as `paymentTerms` when creating a quote or order.",
            "example": "NET_30"
          },
          "label": {
            "type": "string",
            "nullable": true,
            "description": "Display label.",
            "example": "Net 30"
          },
          "kind": {
            "nullable": true,
            "description": "What the term does: NET gives credit days, COD is collected on delivery, PREPAID is collected before dispatch.",
            "type": "string",
            "enum": [
              "NET",
              "COD",
              "PREPAID",
              null
            ]
          },
          "netDays": {
            "type": "number",
            "nullable": true,
            "description": "Credit days for NET terms.",
            "example": 30
          },
          "isDefault": {
            "type": "boolean",
            "description": "True for the workspace's default term.",
            "example": false
          },
          "isActive": {
            "type": "boolean",
            "nullable": true,
            "description": "False when the term is retired.",
            "example": true
          }
        },
        "required": [
          "code",
          "label",
          "kind",
          "netDays",
          "isDefault",
          "isActive"
        ]
      },
      "ServicePaymentTermListResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "Always true on a 2xx response.",
            "example": true
          },
          "data": {
            "description": "The payment terms the workspace offers.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ServicePaymentTerm"
            }
          }
        },
        "required": [
          "success",
          "data"
        ]
      },
      "ServiceWarehouse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Warehouse id.",
            "example": "5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b"
          },
          "code": {
            "type": "string",
            "nullable": true,
            "description": "Warehouse code.",
            "example": "MAIN"
          },
          "name": {
            "type": "string",
            "nullable": true,
            "description": "Warehouse name.",
            "example": "Main warehouse"
          },
          "type": {
            "type": "string",
            "nullable": true,
            "description": "Warehouse type.",
            "example": "WAREHOUSE"
          },
          "status": {
            "nullable": true,
            "description": "Operating status.",
            "type": "string",
            "enum": [
              "ACTIVE",
              "INACTIVE",
              "MAINTENANCE",
              null
            ]
          },
          "address": {
            "type": "object",
            "additionalProperties": true,
            "nullable": true,
            "description": "The warehouse address as key/value pairs.",
            "example": {
              "street1": "1 Example Street",
              "city": "Exampleville"
            }
          }
        },
        "required": [
          "id",
          "code",
          "name",
          "type",
          "status",
          "address"
        ]
      },
      "ServiceWarehouseListResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "Always true on a 2xx response.",
            "example": true
          },
          "data": {
            "description": "The workspace's warehouses.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ServiceWarehouse"
            }
          }
        },
        "required": [
          "success",
          "data"
        ]
      },
      "ServiceStock": {
        "type": "object",
        "properties": {
          "variantId": {
            "type": "string",
            "format": "uuid",
            "description": "Variant id.",
            "example": "5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b"
          },
          "warehouseId": {
            "type": "string",
            "nullable": true,
            "format": "uuid",
            "description": "Warehouse id.",
            "example": "9d2f6b1c-8e47-4a30-b5c2-0e1f3a4b5c6d"
          },
          "warehouseName": {
            "type": "string",
            "nullable": true,
            "description": "Warehouse name.",
            "example": "Main warehouse"
          },
          "onHandQuantity": {
            "type": "number",
            "nullable": true,
            "description": "Quantity physically on hand.",
            "example": 120
          },
          "reservedQuantity": {
            "type": "number",
            "nullable": true,
            "description": "Quantity held for open orders.",
            "example": 20
          },
          "availableQuantity": {
            "type": "number",
            "nullable": true,
            "description": "Quantity available to sell: on hand minus reserved.",
            "example": 100
          },
          "tracked": {
            "type": "boolean",
            "nullable": true,
            "description": "False when the variant does not track inventory.",
            "example": true
          }
        },
        "required": [
          "variantId",
          "warehouseId",
          "warehouseName",
          "onHandQuantity",
          "reservedQuantity",
          "availableQuantity",
          "tracked"
        ]
      },
      "ServiceStockResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "Always true on a 2xx response.",
            "example": true
          },
          "data": {
            "description": "Stock for each variant in each warehouse that holds it.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ServiceStock"
            }
          },
          "unknownVariantIds": {
            "description": "Requested variant ids that do not exist in this workspace. They are not reported as zero stock.",
            "example": [],
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          }
        },
        "required": [
          "success",
          "data",
          "unknownVariantIds"
        ]
      },
      "ServiceAddress": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Address id.",
            "example": "5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b"
          },
          "type": {
            "nullable": true,
            "description": "What the address is used for.",
            "type": "string",
            "enum": [
              "BILLING",
              "SHIPPING",
              "WAREHOUSE",
              "OFFICE",
              "HOME",
              "PICKUP",
              "DELIVERY",
              "RETURN",
              null
            ]
          },
          "label": {
            "type": "string",
            "nullable": true,
            "description": "Short label.",
            "example": "Head office"
          },
          "street1": {
            "type": "string",
            "nullable": true,
            "description": "Street, line 1.",
            "example": "1 Example Street"
          },
          "street2": {
            "type": "string",
            "nullable": true,
            "description": "Street, line 2.",
            "example": "Suite 100"
          },
          "city": {
            "type": "string",
            "nullable": true,
            "description": "City.",
            "example": "Exampleville"
          },
          "state": {
            "type": "string",
            "nullable": true,
            "description": "State or province.",
            "example": "Example State"
          },
          "country": {
            "type": "string",
            "nullable": true,
            "description": "Country.",
            "example": "Exampleland"
          },
          "postalCode": {
            "type": "string",
            "nullable": true,
            "description": "Postal code.",
            "example": "12345"
          },
          "isDefault": {
            "type": "boolean",
            "description": "True for the customer's default address of its type.",
            "example": true
          },
          "isBranch": {
            "type": "boolean",
            "description": "True when the address is a branch location.",
            "example": false
          },
          "isActive": {
            "type": "boolean",
            "nullable": true,
            "description": "False when the address is retired.",
            "example": true
          }
        },
        "required": [
          "id",
          "type",
          "label",
          "street1",
          "street2",
          "city",
          "state",
          "country",
          "postalCode",
          "isDefault",
          "isBranch",
          "isActive"
        ]
      },
      "ServiceContact": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "nullable": true,
            "description": "Contact kind, such as EMAIL or PHONE.",
            "example": "EMAIL"
          },
          "value": {
            "type": "string",
            "nullable": true,
            "description": "The email address, phone number or other value.",
            "example": "buyer@example.com"
          },
          "name": {
            "type": "string",
            "nullable": true,
            "description": "Name of the person.",
            "example": "Sample Buyer"
          },
          "isDefault": {
            "type": "boolean",
            "description": "True for the customer's default contact of its type.",
            "example": true
          }
        },
        "required": [
          "type",
          "value",
          "name",
          "isDefault"
        ]
      },
      "ServiceCustomer": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Customer id. Use it as `customerId` when creating quotes and orders.",
            "example": "5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b"
          },
          "code": {
            "type": "string",
            "nullable": true,
            "description": "Customer code.",
            "example": "CUST-0001"
          },
          "customerCode": {
            "type": "string",
            "nullable": true,
            "description": "Customer account number.",
            "example": "CA-0001"
          },
          "name": {
            "type": "string",
            "nullable": true,
            "description": "Name.",
            "example": "Example Trading Co"
          },
          "displayName": {
            "type": "string",
            "nullable": true,
            "description": "Display name.",
            "example": "Example Trading"
          },
          "legalName": {
            "type": "string",
            "nullable": true,
            "description": "Registered legal name.",
            "example": "Example Trading Company Ltd"
          },
          "taxId": {
            "type": "string",
            "nullable": true,
            "description": "Tax registration number.",
            "example": "TAX-000000"
          },
          "status": {
            "nullable": true,
            "description": "Account status.",
            "type": "string",
            "enum": [
              "ACTIVE",
              "INACTIVE",
              "PENDING_APPROVAL",
              null
            ]
          },
          "paymentTerms": {
            "type": "string",
            "nullable": true,
            "description": "Default payment terms code.",
            "example": "NET_30"
          },
          "currency": {
            "type": "string",
            "nullable": true,
            "description": "Preferred ISO 4217 currency code.",
            "example": "USD"
          },
          "creditLimit": {
            "type": "number",
            "nullable": true,
            "description": "Credit limit in the workspace currency. Zero or less means unlimited.",
            "example": 5000
          },
          "availableCredit": {
            "type": "number",
            "nullable": true,
            "description": "Credit still available, in the workspace currency.",
            "example": 3200
          },
          "taxExempt": {
            "type": "boolean",
            "nullable": true,
            "description": "True when the customer is exempt from tax; null when no account exists.",
            "example": false
          },
          "defaultWarehouseId": {
            "type": "string",
            "nullable": true,
            "format": "uuid",
            "description": "Default warehouse for the customer's orders.",
            "example": "9d2f6b1c-8e47-4a30-b5c2-0e1f3a4b5c6d"
          },
          "addresses": {
            "description": "Saved addresses.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ServiceAddress"
            }
          },
          "contacts": {
            "description": "Contact details.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ServiceContact"
            }
          },
          "deleted": {
            "type": "boolean",
            "description": "Always present. True only for a removed customer, which a delta pull (`updatedAfter`) returns so a sync can drop the row.",
            "example": false
          },
          "updatedAt": {
            "type": "string",
            "nullable": true,
            "format": "date-time",
            "description": "When the customer last changed.",
            "example": "2026-01-15T09:30:00.000Z"
          }
        },
        "required": [
          "id",
          "code",
          "customerCode",
          "name",
          "displayName",
          "legalName",
          "taxId",
          "status",
          "paymentTerms",
          "currency",
          "creditLimit",
          "availableCredit",
          "taxExempt",
          "defaultWarehouseId",
          "addresses",
          "contacts",
          "deleted",
          "updatedAt"
        ]
      },
      "ServiceCustomerListResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "Always true on a 2xx response.",
            "example": true
          },
          "data": {
            "description": "One page of customers.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ServiceCustomer"
            }
          },
          "page": {
            "description": "Paging state.",
            "allOf": [
              {
                "$ref": "#/components/schemas/ServicePage"
              }
            ]
          }
        },
        "required": [
          "success",
          "data",
          "page"
        ]
      },
      "ServiceCustomerResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "Always true on a 2xx response.",
            "example": true
          },
          "data": {
            "description": "The customer.",
            "allOf": [
              {
                "$ref": "#/components/schemas/ServiceCustomer"
              }
            ]
          }
        },
        "required": [
          "success",
          "data"
        ]
      },
      "SalesOrderStatus": {
        "type": "string",
        "enum": [
          "DRAFT",
          "AWAITING_PAYMENT",
          "CONFIRMED",
          "PARTIALLY_INVOICED",
          "INVOICED",
          "PARTIALLY_SHIPPED",
          "SHIPPED",
          "DELIVERED",
          "CANCELLED",
          "RETURNED"
        ]
      },
      "ServiceSalesOrderLine": {
        "type": "object",
        "properties": {
          "productId": {
            "type": "string",
            "nullable": true,
            "format": "uuid",
            "description": "Product id.",
            "example": "5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b"
          },
          "variantId": {
            "type": "string",
            "nullable": true,
            "format": "uuid",
            "description": "Variant id.",
            "example": "9d2f6b1c-8e47-4a30-b5c2-0e1f3a4b5c6d"
          },
          "sku": {
            "type": "string",
            "nullable": true,
            "description": "Stock keeping unit.",
            "example": "SKU-1001"
          },
          "name": {
            "type": "string",
            "nullable": true,
            "description": "Line name.",
            "example": "Sample Widget"
          },
          "quantity": {
            "type": "number",
            "nullable": true,
            "description": "Quantity.",
            "example": 2
          },
          "uomId": {
            "type": "string",
            "nullable": true,
            "format": "uuid",
            "description": "The unit the quantity is in.",
            "example": "5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b"
          },
          "unitPrice": {
            "type": "number",
            "nullable": true,
            "description": "Unit price in the document currency.",
            "example": 12.5
          },
          "discountPercent": {
            "type": "number",
            "nullable": true,
            "description": "Line discount percent.",
            "example": 0
          },
          "discountAmount": {
            "type": "number",
            "nullable": true,
            "description": "Line discount amount in the document currency.",
            "example": 0
          },
          "taxRate": {
            "type": "number",
            "nullable": true,
            "description": "Tax rate percent.",
            "example": 10
          },
          "taxAmount": {
            "type": "number",
            "nullable": true,
            "description": "Tax on the line in the document currency.",
            "example": 2.5
          },
          "totalAmount": {
            "type": "number",
            "nullable": true,
            "description": "Line total in the document currency.",
            "example": 27.5
          },
          "shippedQuantity": {
            "type": "number",
            "nullable": true,
            "description": "Quantity shipped so far.",
            "example": 0
          },
          "invoicedQuantity": {
            "type": "number",
            "nullable": true,
            "description": "Quantity invoiced so far.",
            "example": 0
          },
          "canceledQuantity": {
            "type": "number",
            "nullable": true,
            "description": "Quantity cancelled.",
            "example": 0
          }
        },
        "required": [
          "productId",
          "variantId",
          "sku",
          "name",
          "quantity",
          "uomId",
          "unitPrice",
          "discountPercent",
          "discountAmount",
          "taxRate",
          "taxAmount",
          "totalAmount",
          "shippedQuantity",
          "invoicedQuantity",
          "canceledQuantity"
        ]
      },
      "ServiceSalesOrder": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Sales order id.",
            "example": "5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b"
          },
          "orderNumber": {
            "type": "string",
            "nullable": true,
            "description": "Order number.",
            "example": "SO-000001"
          },
          "status": {
            "nullable": true,
            "description": "Lifecycle status.",
            "type": "string",
            "enum": [
              "DRAFT",
              "AWAITING_PAYMENT",
              "CONFIRMED",
              "PARTIALLY_INVOICED",
              "INVOICED",
              "PARTIALLY_SHIPPED",
              "SHIPPED",
              "DELIVERED",
              "CANCELLED",
              "RETURNED",
              null
            ]
          },
          "paymentStatus": {
            "nullable": true,
            "description": "How much of the order has been paid.",
            "type": "string",
            "enum": [
              "PENDING",
              "PARTIAL",
              "PAID",
              "OVERDUE",
              "REFUNDED",
              "DRAFT",
              "PENDING_APPROVAL",
              "APPROVED",
              "PROCESSED",
              "CANCELLED",
              "REVERSED",
              null
            ]
          },
          "customerId": {
            "type": "string",
            "nullable": true,
            "format": "uuid",
            "description": "The customer the order is for.",
            "example": "9d2f6b1c-8e47-4a30-b5c2-0e1f3a4b5c6d"
          },
          "orderDate": {
            "type": "string",
            "nullable": true,
            "format": "date-time",
            "description": "Order date.",
            "example": "2026-01-15T09:30:00.000Z"
          },
          "promisedDate": {
            "type": "string",
            "nullable": true,
            "format": "date-time",
            "description": "Date the order is promised for.",
            "example": "2026-01-15T09:30:00.000Z"
          },
          "shippedDate": {
            "type": "string",
            "nullable": true,
            "format": "date-time",
            "description": "When the order shipped.",
            "example": "2026-01-15T09:30:00.000Z"
          },
          "deliveredDate": {
            "type": "string",
            "nullable": true,
            "format": "date-time",
            "description": "When the order was delivered.",
            "example": "2026-01-15T09:30:00.000Z"
          },
          "currency": {
            "type": "string",
            "nullable": true,
            "description": "ISO 4217 currency code of every amount on the order.",
            "example": "USD"
          },
          "exchangeRate": {
            "type": "number",
            "nullable": true,
            "description": "Rate from the document currency to the workspace currency.",
            "example": 1
          },
          "subtotal": {
            "type": "number",
            "nullable": true,
            "description": "Sum of the line values before discount and tax.",
            "example": 25
          },
          "discountAmount": {
            "type": "number",
            "nullable": true,
            "description": "Total discount.",
            "example": 0
          },
          "taxAmount": {
            "type": "number",
            "nullable": true,
            "description": "Total tax.",
            "example": 2.5
          },
          "shippingAmount": {
            "type": "number",
            "nullable": true,
            "description": "Shipping charge.",
            "example": 0
          },
          "totalAmount": {
            "type": "number",
            "nullable": true,
            "description": "Grand total in the document currency.",
            "example": 27.5
          },
          "paymentTerms": {
            "type": "string",
            "nullable": true,
            "description": "Payment terms code.",
            "example": "NET_30"
          },
          "customerPoNumber": {
            "type": "string",
            "nullable": true,
            "description": "The buyer's purchase order number.",
            "example": "PO-12345"
          },
          "reference": {
            "type": "string",
            "nullable": true,
            "description": "Your own reference for the order.",
            "example": "INT-0001"
          },
          "trackingNumber": {
            "type": "string",
            "nullable": true,
            "description": "Carrier tracking number.",
            "example": "TRACK-000000"
          },
          "items": {
            "description": "Order lines.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ServiceSalesOrderLine"
            }
          },
          "updatedAt": {
            "type": "string",
            "nullable": true,
            "format": "date-time",
            "description": "When the order last changed. Use the newest value as the next `updatedAfter`.",
            "example": "2026-01-15T09:30:00.000Z"
          }
        },
        "required": [
          "id",
          "orderNumber",
          "status",
          "paymentStatus",
          "customerId",
          "orderDate",
          "promisedDate",
          "shippedDate",
          "deliveredDate",
          "currency",
          "exchangeRate",
          "subtotal",
          "discountAmount",
          "taxAmount",
          "shippingAmount",
          "totalAmount",
          "paymentTerms",
          "customerPoNumber",
          "reference",
          "trackingNumber",
          "items",
          "updatedAt"
        ]
      },
      "ServiceSalesOrderListResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "Always true on a 2xx response.",
            "example": true
          },
          "data": {
            "description": "One page of sales orders, oldest change first.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ServiceSalesOrder"
            }
          },
          "page": {
            "description": "Paging state.",
            "allOf": [
              {
                "$ref": "#/components/schemas/ServicePage"
              }
            ]
          }
        },
        "required": [
          "success",
          "data",
          "page"
        ]
      },
      "ServiceSalesOrderResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "Always true on a 2xx response.",
            "example": true
          },
          "data": {
            "description": "The sales order.",
            "allOf": [
              {
                "$ref": "#/components/schemas/ServiceSalesOrder"
              }
            ]
          }
        },
        "required": [
          "success",
          "data"
        ]
      },
      "QuoteStatus": {
        "type": "string",
        "enum": [
          "DRAFT",
          "PENDING",
          "SENT",
          "VIEWED",
          "ACCEPTED",
          "APPROVED",
          "REJECTED",
          "EXPIRED",
          "CONVERTED",
          "CANCELLED"
        ]
      },
      "ServiceDocumentLine": {
        "type": "object",
        "properties": {
          "productId": {
            "type": "string",
            "nullable": true,
            "format": "uuid",
            "description": "Product id.",
            "example": "5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b"
          },
          "variantId": {
            "type": "string",
            "nullable": true,
            "format": "uuid",
            "description": "Variant id.",
            "example": "9d2f6b1c-8e47-4a30-b5c2-0e1f3a4b5c6d"
          },
          "sku": {
            "type": "string",
            "nullable": true,
            "description": "Stock keeping unit.",
            "example": "SKU-1001"
          },
          "name": {
            "type": "string",
            "nullable": true,
            "description": "Line name.",
            "example": "Sample Widget"
          },
          "quantity": {
            "type": "number",
            "nullable": true,
            "description": "Quantity.",
            "example": 2
          },
          "uomId": {
            "type": "string",
            "nullable": true,
            "format": "uuid",
            "description": "The unit the quantity is in.",
            "example": "5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b"
          },
          "unitPrice": {
            "type": "number",
            "nullable": true,
            "description": "Unit price in the document currency.",
            "example": 12.5
          },
          "discountPercent": {
            "type": "number",
            "nullable": true,
            "description": "Line discount percent.",
            "example": 0
          },
          "discountAmount": {
            "type": "number",
            "nullable": true,
            "description": "Line discount amount in the document currency.",
            "example": 0
          },
          "taxRate": {
            "type": "number",
            "nullable": true,
            "description": "Tax rate percent.",
            "example": 10
          },
          "taxAmount": {
            "type": "number",
            "nullable": true,
            "description": "Tax on the line in the document currency.",
            "example": 2.5
          },
          "totalAmount": {
            "type": "number",
            "nullable": true,
            "description": "Line total in the document currency.",
            "example": 27.5
          }
        },
        "required": [
          "productId",
          "variantId",
          "sku",
          "name",
          "quantity",
          "uomId",
          "unitPrice",
          "discountPercent",
          "discountAmount",
          "taxRate",
          "taxAmount",
          "totalAmount"
        ]
      },
      "ServiceQuote": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Quote id.",
            "example": "5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b"
          },
          "quoteNumber": {
            "type": "string",
            "nullable": true,
            "description": "Quote number.",
            "example": "QT-000001"
          },
          "status": {
            "nullable": true,
            "description": "Lifecycle status.",
            "type": "string",
            "enum": [
              "DRAFT",
              "PENDING",
              "SENT",
              "VIEWED",
              "ACCEPTED",
              "APPROVED",
              "REJECTED",
              "EXPIRED",
              "CONVERTED",
              "CANCELLED",
              null
            ]
          },
          "customerId": {
            "type": "string",
            "nullable": true,
            "format": "uuid",
            "description": "The customer the quote is for.",
            "example": "9d2f6b1c-8e47-4a30-b5c2-0e1f3a4b5c6d"
          },
          "quoteDate": {
            "type": "string",
            "nullable": true,
            "format": "date-time",
            "description": "Quote date.",
            "example": "2026-01-15T09:30:00.000Z"
          },
          "validUntil": {
            "type": "string",
            "nullable": true,
            "format": "date-time",
            "description": "The quote is valid until this instant.",
            "example": "2026-01-15T09:30:00.000Z"
          },
          "currency": {
            "type": "string",
            "nullable": true,
            "description": "ISO 4217 currency code of every amount on the quote.",
            "example": "USD"
          },
          "exchangeRate": {
            "type": "number",
            "nullable": true,
            "description": "Rate from the document currency to the workspace currency.",
            "example": 1
          },
          "subtotal": {
            "type": "number",
            "nullable": true,
            "description": "Sum of the line values before discount and tax.",
            "example": 25
          },
          "discountAmount": {
            "type": "number",
            "nullable": true,
            "description": "Total discount.",
            "example": 0
          },
          "taxAmount": {
            "type": "number",
            "nullable": true,
            "description": "Total tax.",
            "example": 2.5
          },
          "shippingAmount": {
            "type": "number",
            "nullable": true,
            "description": "Shipping charge.",
            "example": 0
          },
          "totalAmount": {
            "type": "number",
            "nullable": true,
            "description": "Grand total in the document currency.",
            "example": 27.5
          },
          "paymentTerms": {
            "type": "string",
            "nullable": true,
            "description": "Payment terms code.",
            "example": "NET_30"
          },
          "convertedToSalesOrderId": {
            "type": "string",
            "nullable": true,
            "format": "uuid",
            "description": "The sales order this quote was converted into, once converted.",
            "example": "5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b"
          },
          "items": {
            "description": "Quote lines.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ServiceDocumentLine"
            }
          },
          "updatedAt": {
            "type": "string",
            "nullable": true,
            "format": "date-time",
            "description": "When the quote last changed. Use the newest value as the next `updatedAfter`.",
            "example": "2026-01-15T09:30:00.000Z"
          }
        },
        "required": [
          "id",
          "quoteNumber",
          "status",
          "customerId",
          "quoteDate",
          "validUntil",
          "currency",
          "exchangeRate",
          "subtotal",
          "discountAmount",
          "taxAmount",
          "shippingAmount",
          "totalAmount",
          "paymentTerms",
          "convertedToSalesOrderId",
          "items",
          "updatedAt"
        ]
      },
      "ServiceQuoteListResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "Always true on a 2xx response.",
            "example": true
          },
          "data": {
            "description": "One page of quotes, oldest change first.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ServiceQuote"
            }
          },
          "page": {
            "description": "Paging state.",
            "allOf": [
              {
                "$ref": "#/components/schemas/ServicePage"
              }
            ]
          }
        },
        "required": [
          "success",
          "data",
          "page"
        ]
      },
      "ServiceQuoteResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "Always true on a 2xx response.",
            "example": true
          },
          "data": {
            "description": "The quote.",
            "allOf": [
              {
                "$ref": "#/components/schemas/ServiceQuote"
              }
            ]
          }
        },
        "required": [
          "success",
          "data"
        ]
      },
      "CreateQuoteItemInput": {
        "type": "object",
        "properties": {
          "productId": {
            "type": "string",
            "description": "Id of the product, from the products list.",
            "example": "9d2f6b1c-8e47-4a30-b5c2-0e1f3a4b5c6d"
          },
          "variantId": {
            "type": "string",
            "description": "Variant id, from the products list.",
            "example": "9d2f6b1c-8e47-4a30-b5c2-0e1f3a4b5c6d"
          },
          "productName": {
            "type": "string",
            "description": "Name to show on the line.",
            "example": "Sample Widget"
          },
          "productSku": {
            "type": "string",
            "description": "Stock keeping unit to show on the line.",
            "example": "SKU-1001"
          },
          "description": {
            "type": "string",
            "description": "Longer description to show on the line."
          },
          "quantity": {
            "type": "number",
            "description": "Quantity quoted. Must be greater than zero.",
            "example": 2
          },
          "uomId": {
            "type": "string",
            "description": "Id of the unit of measure the quantity is in, from the units list.",
            "example": "9d2f6b1c-8e47-4a30-b5c2-0e1f3a4b5c6d"
          },
          "uomName": {
            "type": "string",
            "description": "Name of the unit, to show on the line.",
            "example": "Each"
          },
          "unitPrice": {
            "type": "number",
            "minimum": 0,
            "description": "Unit price in the quote currency. Required.",
            "example": 12.5
          },
          "salePrice": {
            "type": "number",
            "description": "The variant's sale price, when it has one."
          },
          "priceOverride": {
            "type": "boolean",
            "description": "Set to true when `unitPrice` deliberately differs from the catalogue price. The user who created the key may need the price-override permission."
          },
          "overrideReason": {
            "type": "string",
            "maxLength": 500,
            "description": "Why the price was overridden."
          },
          "discount": {
            "type": "number",
            "minimum": 0,
            "description": "Line discount, a percent or an amount according to the line's `discountType`. Required; use 0 for none.",
            "example": 0
          },
          "discountType": {
            "type": "string",
            "description": "How `discount` is read: as a percent or as an amount.",
            "enum": [
              "PERCENTAGE",
              "AMOUNT"
            ]
          },
          "discountAmount": {
            "type": "number",
            "minimum": 0,
            "description": "Line discount as an amount in the quote currency."
          },
          "taxRate": {
            "type": "number",
            "description": "Tax rate percent for the line. Required.",
            "example": 10
          },
          "taxAmount": {
            "type": "number",
            "description": "Tax on the line in the quote currency, as your system computed it. Required.",
            "example": 2.5
          },
          "taxCalculationMethod": {
            "type": "string",
            "description": "Whether the line price includes tax (`INCLUSIVE`) or tax is added on top (`EXCLUSIVE`).",
            "enum": [
              "INCLUSIVE",
              "EXCLUSIVE"
            ]
          },
          "totalPrice": {
            "type": "number",
            "description": "Line total in the quote currency.",
            "example": 27.5
          },
          "warehouseId": {
            "type": "string",
            "description": "Warehouse this line ships from.",
            "example": "9d2f6b1c-8e47-4a30-b5c2-0e1f3a4b5c6d"
          }
        },
        "required": [
          "productId",
          "variantId",
          "productName",
          "quantity",
          "unitPrice",
          "discount",
          "discountType",
          "taxRate",
          "taxAmount"
        ]
      },
      "DocumentOwnershipInput": {
        "type": "object",
        "properties": {
          "ownerId": {
            "type": "string",
            "description": "Id of the user who owns the quote.",
            "example": "9d2f6b1c-8e47-4a30-b5c2-0e1f3a4b5c6d"
          }
        }
      },
      "CreateQuoteInput": {
        "type": "object",
        "properties": {
          "idempotencyKey": {
            "type": "string",
            "description": "A value you generate for this quote. Same effect as the `Idempotency-Key` header: a retry with the same key and body returns the original quote."
          },
          "status": {
            "type": "string",
            "description": "Status the quote is created in. Defaults to `DRAFT`. A quote cannot be created already accepted, rejected, expired, converted or cancelled.",
            "enum": [
              "DRAFT",
              "PENDING",
              "SENT",
              "VIEWED",
              "APPROVED"
            ]
          },
          "customerId": {
            "type": "string",
            "description": "Id of the customer the quote is for, from the customers list.",
            "example": "9d2f6b1c-8e47-4a30-b5c2-0e1f3a4b5c6d"
          },
          "customerName": {
            "type": "string",
            "description": "Customer name to show on the quote.",
            "example": "Example Trading Co"
          },
          "customerEmail": {
            "type": "string",
            "description": "Customer email to show on the quote.",
            "example": "buyer@example.com"
          },
          "customerMobile": {
            "type": "string",
            "description": "Customer phone number to show on the quote.",
            "example": "+1 555 0100"
          },
          "validUntil": {
            "type": "string",
            "description": "The quote is valid until this date, ISO 8601.",
            "example": "2026-02-14"
          },
          "quoteDate": {
            "type": "string",
            "description": "Quote date, ISO 8601. Defaults to today. Any other day needs the user who created the key to hold the back-dating permission.",
            "example": "2026-01-15"
          },
          "reference": {
            "type": "string",
            "description": "Your own reference for the quote, such as its id in your system.",
            "example": "INT-0001"
          },
          "priority": {
            "type": "string",
            "description": "Priority of the quote.",
            "enum": [
              "LOW",
              "NORMAL",
              "HIGH",
              "URGENT"
            ]
          },
          "notes": {
            "type": "string",
            "description": "Free-text notes on the quote."
          },
          "internalNotes": {
            "type": "string",
            "description": "Free-text notes for your own team."
          },
          "deliveryInstructions": {
            "type": "string",
            "description": "Instructions for the delivery."
          },
          "terms": {
            "type": "string",
            "description": "Terms and conditions text for the quote."
          },
          "currency": {
            "type": "string",
            "description": "ISO 4217 currency code of every amount on the quote.",
            "example": "USD"
          },
          "exchangeRate": {
            "type": "number",
            "minimum": 0,
            "description": "Rate from the quote currency to the workspace currency. Greater than zero.",
            "example": 1,
            "exclusiveMinimum": true
          },
          "discountType": {
            "type": "string",
            "description": "How `discountValue` is read: as a percent or as an amount.",
            "enum": [
              "PERCENTAGE",
              "AMOUNT"
            ]
          },
          "discountValue": {
            "type": "number",
            "minimum": 0,
            "description": "Quote-level discount, a percent or an amount according to `discountType`. Use 0 for none.",
            "example": 0
          },
          "taxRate": {
            "type": "number",
            "description": "Quote-level tax rate percent.",
            "example": 10
          },
          "shippingCost": {
            "type": "number",
            "minimum": 0,
            "description": "Shipping charge in the quote currency. Use 0 for none.",
            "example": 0
          },
          "taxCalculationMethod": {
            "type": "string",
            "description": "Whether line prices already include tax (`INCLUSIVE`) or tax is added on top (`EXCLUSIVE`).",
            "enum": [
              "INCLUSIVE",
              "EXCLUSIVE"
            ]
          },
          "billingAddress": {
            "type": "string",
            "description": "Billing address as text, as it should print on the quote."
          },
          "shippingAddress": {
            "type": "string",
            "description": "Shipping address as text, as it should print on the quote."
          },
          "warehouseId": {
            "type": "string",
            "description": "Warehouse the quote is for.",
            "example": "9d2f6b1c-8e47-4a30-b5c2-0e1f3a4b5c6d"
          },
          "paymentTerms": {
            "type": "string",
            "description": "Payment terms code, from the payment terms list.",
            "example": "NET_30"
          },
          "items": {
            "minItems": 1,
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CreateQuoteItemInput"
            },
            "description": "The quote lines. At least one is required."
          },
          "subtotal": {
            "type": "number",
            "description": "Sum of the line values before discount and tax, as your system computed it. Required.",
            "example": 25
          },
          "discountAmount": {
            "type": "number",
            "minimum": 0,
            "description": "Total discount in the quote currency, as your system computed it. Required; use 0 for none.",
            "example": 0
          },
          "taxAmount": {
            "type": "number",
            "description": "Total tax in the quote currency, as your system computed it. Required.",
            "example": 2.5
          },
          "totalAmount": {
            "type": "number",
            "description": "Grand total in the quote currency, as your system computed it. Required.",
            "example": 27.5
          },
          "ownership": {
            "allOf": [
              {
                "$ref": "#/components/schemas/DocumentOwnershipInput"
              }
            ],
            "description": "Who owns the quote."
          }
        },
        "required": [
          "customerId",
          "currency",
          "discountType",
          "discountValue",
          "taxRate",
          "shippingCost",
          "items",
          "subtotal",
          "discountAmount",
          "taxAmount",
          "totalAmount"
        ]
      },
      "CreateSalesOrderItemInput": {
        "type": "object",
        "properties": {
          "productId": {
            "type": "string",
            "description": "Id of the product the variant belongs to. An empty string lets the server take it from the variant; a product that does not match the variant is refused.",
            "example": "9d2f6b1c-8e47-4a30-b5c2-0e1f3a4b5c6d"
          },
          "variantId": {
            "type": "string",
            "description": "Variant id, from the products list.",
            "example": "9d2f6b1c-8e47-4a30-b5c2-0e1f3a4b5c6d"
          },
          "warehouseId": {
            "type": "string",
            "description": "Warehouse this line ships from.",
            "example": "9d2f6b1c-8e47-4a30-b5c2-0e1f3a4b5c6d"
          },
          "quantity": {
            "type": "number",
            "description": "Quantity ordered, in the unit given by `uomId`. Must be greater than zero.",
            "example": 2
          },
          "unitPrice": {
            "type": "number",
            "description": "Unit price in the order currency. Leave it off to use the catalogue price. A price you send can be refused, depending on the workspace's pricing settings and the permissions of the user who created the key.",
            "example": 12.5
          },
          "priceOverride": {
            "type": "boolean",
            "description": "Set to true when `unitPrice` deliberately differs from the catalogue price. The user who created the key may need the price-override permission."
          },
          "overrideReason": {
            "type": "string",
            "maxLength": 500,
            "description": "Why the price was overridden."
          },
          "taxRate": {
            "type": "number",
            "description": "Tax rate percent for the line, used in place of the product's configured tax.",
            "example": 10
          },
          "discountPercent": {
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "description": "Line discount, from 0 to 100 percent.",
            "example": 0
          },
          "notes": {
            "type": "string",
            "description": "Notes on the line."
          },
          "uomId": {
            "type": "string",
            "format": "uuid",
            "description": "Id of the unit of measure the quantity is in, from the units list.",
            "example": "9d2f6b1c-8e47-4a30-b5c2-0e1f3a4b5c6d"
          },
          "taxGroupId": {
            "type": "string",
            "format": "uuid",
            "description": "Tax group to use for the line, in place of the product's configured tax.",
            "example": "9d2f6b1c-8e47-4a30-b5c2-0e1f3a4b5c6d"
          }
        },
        "required": [
          "productId",
          "variantId",
          "quantity",
          "uomId"
        ]
      },
      "CreateSalesOrderAddressInput": {
        "type": "object",
        "properties": {
          "street1": {
            "type": "string",
            "description": "Street address, line 1.",
            "example": "1 Example Street"
          },
          "street2": {
            "type": "string",
            "description": "Street address, line 2."
          },
          "city": {
            "type": "string",
            "description": "City.",
            "example": "Exampleville"
          },
          "state": {
            "type": "string",
            "description": "State or province.",
            "example": "Example State"
          },
          "postalCode": {
            "type": "string",
            "description": "Postal code.",
            "example": "12345"
          },
          "country": {
            "type": "string",
            "description": "Country.",
            "example": "Exampleland"
          },
          "id": {
            "type": "string",
            "description": "Id of the saved address this was copied from. Kept for reference only."
          },
          "label": {
            "type": "string",
            "description": "Short label for the address, kept for reference only."
          },
          "type": {
            "type": "string",
            "description": "Kind of address, such as `BILLING` or `SHIPPING`. Kept for reference only."
          }
        },
        "required": [
          "street1",
          "city",
          "state",
          "postalCode",
          "country"
        ]
      },
      "CreateSalesOrderInput": {
        "type": "object",
        "properties": {
          "customerId": {
            "type": "string",
            "format": "uuid",
            "description": "Id of the customer the order is for, from the customers list.",
            "example": "9d2f6b1c-8e47-4a30-b5c2-0e1f3a4b5c6d"
          },
          "idempotencyKey": {
            "type": "string",
            "format": "uuid",
            "description": "A UUID you generate for this order. Same effect as the `Idempotency-Key` header: a retry with the same key and body returns the original order."
          },
          "orderDate": {
            "type": "string",
            "description": "Order date, ISO 8601. Defaults to today. Any other day needs the user who created the key to hold the back-dating permission.",
            "example": "2026-01-15"
          },
          "customerEmail": {
            "type": "string",
            "format": "email",
            "description": "Contact email to record on the order.",
            "example": "buyer@example.com"
          },
          "customerMobile": {
            "type": "string",
            "description": "Contact phone number to record on the order.",
            "example": "+1 555 0100"
          },
          "reference": {
            "type": "string",
            "description": "Your own reference for the order, such as its id in your system.",
            "example": "INT-0001"
          },
          "currency": {
            "type": "string",
            "description": "ISO 4217 currency code of the order. Defaults to the workspace currency. A line is priced only from a price in this currency; a line with none is refused.",
            "example": "USD"
          },
          "exchangeRate": {
            "type": "number",
            "minimum": 0,
            "description": "Rate from the order currency to the workspace currency. Greater than zero. Worked out by the server when you leave it off for a foreign currency.",
            "example": 1,
            "exclusiveMinimum": true
          },
          "warehouseId": {
            "type": "string",
            "format": "uuid",
            "description": "Warehouse the order is fulfilled from.",
            "example": "9d2f6b1c-8e47-4a30-b5c2-0e1f3a4b5c6d"
          },
          "promisedDate": {
            "format": "date-time",
            "type": "string",
            "description": "Date the order is promised to the customer, ISO 8601."
          },
          "paymentTerms": {
            "type": "string",
            "description": "Payment terms code, from the payment terms list.",
            "example": "NET_30"
          },
          "shippingMethod": {
            "type": "string",
            "description": "Shipping method to record on the order."
          },
          "notes": {
            "type": "string",
            "description": "Free-text notes on the order."
          },
          "internalNotes": {
            "type": "string",
            "description": "Free-text notes for your own team."
          },
          "deliveryInstructions": {
            "type": "string",
            "description": "Instructions for the delivery."
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CreateSalesOrderItemInput"
            },
            "description": "The order lines."
          },
          "shippingAmount": {
            "type": "number",
            "description": "Shipping charge in the order currency. Defaults to 0.",
            "example": 0
          },
          "discountAmount": {
            "type": "number",
            "minimum": 0,
            "description": "Order-level discount amount in the order currency. Defaults to 0.",
            "example": 0
          },
          "discountSchemeId": {
            "type": "string",
            "format": "uuid",
            "description": "Id of a discount scheme to apply to the order.",
            "example": "9d2f6b1c-8e47-4a30-b5c2-0e1f3a4b5c6d"
          },
          "taxCalculationMethod": {
            "type": "string",
            "description": "Whether line prices already include tax (`INCLUSIVE`) or tax is added on top (`EXCLUSIVE`).",
            "enum": [
              "INCLUSIVE",
              "EXCLUSIVE"
            ]
          },
          "billingAddress": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CreateSalesOrderAddressInput"
              }
            ],
            "description": "Billing address for the order."
          },
          "shippingAddress": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CreateSalesOrderAddressInput"
              }
            ],
            "description": "Shipping address for the order."
          },
          "status": {
            "type": "string",
            "enum": [
              "DRAFT",
              "AWAITING_PAYMENT",
              "CONFIRMED"
            ],
            "description": "Status the order starts in. `DRAFT` reserves no stock. `AWAITING_PAYMENT` also reserves none; use it for an order that waits for payment before it is confirmed. `CONFIRMED` confirms the order and reserves stock, and is refused if the customer's credit limit does not allow it or, when the workspace does not allow negative stock, stock does not cover it. Defaults to `DRAFT`."
          },
          "customerPoNumber": {
            "type": "string",
            "maxLength": 100,
            "description": "The customer's own purchase order number.",
            "example": "PO-12345"
          }
        },
        "required": [
          "customerId",
          "paymentTerms",
          "items"
        ]
      },
      "ProductImportMode": {
        "type": "string",
        "enum": [
          "CREATE_ONLY",
          "UPSERT"
        ],
        "description": "CREATE_ONLY adds new products and skips ones that already exist; UPSERT also updates existing products."
      },
      "ServiceCreateProductImportBody": {
        "type": "object",
        "properties": {
          "filename": {
            "type": "string",
            "maxLength": 255,
            "description": "Name of the file you will upload, with its extension. Supported formats are .csv, .xlsx and .xls.",
            "example": "products.csv"
          },
          "mode": {
            "default": "CREATE_ONLY",
            "description": "CREATE_ONLY adds new products and skips ones that already exist; UPSERT also updates existing products.",
            "allOf": [
              {
                "$ref": "#/components/schemas/ProductImportMode"
              }
            ]
          }
        },
        "required": [
          "filename"
        ]
      },
      "ServiceProductImportCreated": {
        "type": "object",
        "properties": {
          "importId": {
            "type": "string",
            "format": "uuid",
            "description": "Import id. Use it in the other import routes.",
            "example": "5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b"
          },
          "uploadUrl": {
            "type": "string",
            "description": "A time-limited URL. Upload the CSV or Excel file to it with an HTTP PUT, then call the stage route.",
            "example": "https://uploads.example.com/product-imports/products.csv?signature=example"
          },
          "fileRef": {
            "type": "string",
            "description": "Storage reference of the uploaded file.",
            "example": "product-imports/products.csv"
          }
        },
        "required": [
          "importId",
          "uploadUrl",
          "fileRef"
        ]
      },
      "ServiceProductImportCreatedResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "Always true on a 2xx response.",
            "example": true
          },
          "data": {
            "description": "The new import and where to upload its file.",
            "allOf": [
              {
                "$ref": "#/components/schemas/ServiceProductImportCreated"
              }
            ]
          }
        },
        "required": [
          "success",
          "data"
        ]
      },
      "ServiceAckResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "Always true on a 2xx response.",
            "example": true
          }
        },
        "required": [
          "success"
        ]
      },
      "ProductImportStatus": {
        "type": "string",
        "enum": [
          "UPLOADED",
          "MAPPING",
          "VALIDATING",
          "PREVIEW",
          "COMMITTING",
          "COMPLETED",
          "FAILED",
          "ROLLED_BACK"
        ],
        "description": "Import status."
      },
      "ServiceImportRowError": {
        "type": "object",
        "properties": {
          "rowNumber": {
            "type": "number",
            "description": "Row number in the file, starting at 1.",
            "example": 7
          },
          "sku": {
            "type": "string",
            "description": "The row's SKU, when it has one.",
            "example": "SKU-1001"
          },
          "errors": {
            "description": "What is wrong with the row.",
            "example": [
              "Unknown unit of measure"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "rowNumber",
          "errors"
        ]
      },
      "ServiceImportSampleRow": {
        "type": "object",
        "properties": {
          "rowNumber": {
            "type": "number",
            "description": "Row number in the file, starting at 1.",
            "example": 2
          },
          "sku": {
            "type": "string",
            "description": "The row's SKU.",
            "example": "SKU-1001"
          },
          "productName": {
            "type": "string",
            "description": "The row's product name.",
            "example": "Sample Widget"
          },
          "action": {
            "type": "string",
            "description": "What the import will do with the row, such as CREATE or UPDATE.",
            "example": "CREATE"
          }
        },
        "required": [
          "rowNumber"
        ]
      },
      "ServiceImportCurrencySummary": {
        "type": "object",
        "properties": {
          "currency": {
            "type": "string",
            "description": "ISO 4217 currency code of the price tiers.",
            "example": "USD"
          },
          "isBase": {
            "type": "boolean",
            "description": "True when this is the workspace's own currency.",
            "example": true
          },
          "rowCount": {
            "type": "number",
            "description": "Rows that carry price tiers in this currency.",
            "example": 100
          }
        },
        "required": [
          "currency",
          "isBase",
          "rowCount"
        ]
      },
      "ServiceProductImportPreview": {
        "type": "object",
        "properties": {
          "importId": {
            "type": "string",
            "format": "uuid",
            "description": "Import id.",
            "example": "5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b"
          },
          "status": {
            "description": "Import status.",
            "allOf": [
              {
                "$ref": "#/components/schemas/ProductImportStatus"
              }
            ]
          },
          "mode": {
            "description": "Import mode.",
            "allOf": [
              {
                "$ref": "#/components/schemas/ProductImportMode"
              }
            ]
          },
          "totalRows": {
            "type": "number",
            "description": "Rows found in the file.",
            "example": 120
          },
          "validRows": {
            "type": "number",
            "description": "Rows that passed validation.",
            "example": 118
          },
          "errorRows": {
            "type": "number",
            "description": "Rows that failed validation.",
            "example": 1
          },
          "warningRows": {
            "type": "number",
            "description": "Rows that passed with a warning.",
            "example": 1
          },
          "createCount": {
            "type": "number",
            "description": "Products the import will create.",
            "example": 100
          },
          "updateCount": {
            "type": "number",
            "description": "Products the import will update.",
            "example": 18
          },
          "totalChunks": {
            "type": "number",
            "description": "Number of processing chunks.",
            "example": 3
          },
          "sampleErrors": {
            "description": "Up to 50 failing rows, with the reason for each.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ServiceImportRowError"
            }
          },
          "sampleRows": {
            "description": "Up to 10 rows that will be written, so you can see what approving will do.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ServiceImportSampleRow"
            }
          },
          "autoAppliedRules": {
            "description": "Adjustments the import made for you, such as matching a unit spelling to one of your units.",
            "example": [
              "Unit \"pcs\" matched to \"EA\""
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "currencySummary": {
            "description": "How many rows carry price tiers in each currency.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ServiceImportCurrencySummary"
            }
          }
        },
        "required": [
          "importId",
          "status",
          "mode",
          "totalRows",
          "validRows",
          "errorRows",
          "warningRows",
          "createCount",
          "updateCount",
          "totalChunks",
          "sampleErrors",
          "sampleRows",
          "autoAppliedRules",
          "currencySummary"
        ]
      },
      "ServiceProductImportPreviewResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "Always true on a 2xx response.",
            "example": true
          },
          "data": {
            "description": "What approving the import will do.",
            "allOf": [
              {
                "$ref": "#/components/schemas/ServiceProductImportPreview"
              }
            ]
          }
        },
        "required": [
          "success",
          "data"
        ]
      },
      "ServiceApproveProductImportBody": {
        "type": "object",
        "properties": {
          "modeOverride": {
            "description": "Run the import in this mode instead of the one it was created with.",
            "allOf": [
              {
                "$ref": "#/components/schemas/ProductImportMode"
              }
            ]
          }
        }
      },
      "ServiceProductImportRollbackResult": {
        "type": "object",
        "properties": {
          "deletedVariants": {
            "type": "number",
            "description": "Variants the import created that were deleted.",
            "example": 90
          },
          "deactivatedVariants": {
            "type": "number",
            "description": "Variants the import created that could not be deleted and were switched off instead.",
            "example": 10
          },
          "deletedProducts": {
            "type": "number",
            "description": "Products deleted.",
            "example": 90
          },
          "notRevertedCount": {
            "type": "number",
            "description": "Updated rows. Updates are not reverted; they stay as the import wrote them.",
            "example": 18
          }
        },
        "required": [
          "deletedVariants",
          "deactivatedVariants",
          "deletedProducts",
          "notRevertedCount"
        ]
      },
      "ServiceProductImportRollbackResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "Always true on a 2xx response.",
            "example": true
          },
          "data": {
            "description": "What the rollback removed.",
            "allOf": [
              {
                "$ref": "#/components/schemas/ServiceProductImportRollbackResult"
              }
            ]
          }
        },
        "required": [
          "success",
          "data"
        ]
      },
      "ServiceProductImport": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Import id.",
            "example": "5b0c8a6e-2f4d-4d6e-9a53-7c1d2e3f4a5b"
          },
          "filename": {
            "type": "string",
            "nullable": true,
            "description": "Name of the uploaded file.",
            "example": "products.csv"
          },
          "mode": {
            "nullable": true,
            "description": "Whether the import may update existing products.",
            "type": "string",
            "enum": [
              "CREATE_ONLY",
              "UPSERT",
              null
            ]
          },
          "status": {
            "nullable": true,
            "description": "Where the import is in its lifecycle.",
            "type": "string",
            "enum": [
              "UPLOADED",
              "MAPPING",
              "VALIDATING",
              "PREVIEW",
              "COMMITTING",
              "COMPLETED",
              "FAILED",
              "ROLLED_BACK",
              null
            ]
          },
          "totalRows": {
            "type": "number",
            "nullable": true,
            "description": "Rows found in the file.",
            "example": 120
          },
          "stagedRows": {
            "type": "number",
            "nullable": true,
            "description": "Rows staged so far.",
            "example": 120
          },
          "validRows": {
            "type": "number",
            "nullable": true,
            "description": "Rows that passed validation.",
            "example": 118
          },
          "warningRows": {
            "type": "number",
            "nullable": true,
            "description": "Rows that passed with a warning.",
            "example": 1
          },
          "errorRows": {
            "type": "number",
            "nullable": true,
            "description": "Rows that failed validation and are excluded.",
            "example": 1
          },
          "createCount": {
            "type": "number",
            "nullable": true,
            "description": "Products the import created.",
            "example": 100
          },
          "updateCount": {
            "type": "number",
            "nullable": true,
            "description": "Products the import updated.",
            "example": 18
          },
          "skipCount": {
            "type": "number",
            "nullable": true,
            "description": "Rows skipped because they would change nothing.",
            "example": 0
          },
          "failureReason": {
            "type": "string",
            "nullable": true,
            "description": "Why the import failed, when it did."
          },
          "createdAt": {
            "type": "string",
            "nullable": true,
            "format": "date-time",
            "description": "When the import was created.",
            "example": "2026-01-15T09:30:00.000Z"
          },
          "startedAt": {
            "type": "string",
            "nullable": true,
            "format": "date-time",
            "description": "When processing started.",
            "example": "2026-01-15T09:30:00.000Z"
          },
          "approvedAt": {
            "type": "string",
            "nullable": true,
            "format": "date-time",
            "description": "When the import was approved.",
            "example": "2026-01-15T09:30:00.000Z"
          },
          "completedAt": {
            "type": "string",
            "nullable": true,
            "format": "date-time",
            "description": "When the import finished.",
            "example": "2026-01-15T09:30:00.000Z"
          },
          "rolledBackAt": {
            "type": "string",
            "nullable": true,
            "format": "date-time",
            "description": "When the import was rolled back.",
            "example": "2026-01-15T09:30:00.000Z"
          },
          "updatedAt": {
            "type": "string",
            "nullable": true,
            "format": "date-time",
            "description": "When the import last changed.",
            "example": "2026-01-15T09:30:00.000Z"
          }
        },
        "required": [
          "id",
          "filename",
          "mode",
          "status",
          "totalRows",
          "stagedRows",
          "validRows",
          "warningRows",
          "errorRows",
          "createCount",
          "updateCount",
          "skipCount",
          "failureReason",
          "createdAt",
          "startedAt",
          "approvedAt",
          "completedAt",
          "rolledBackAt",
          "updatedAt"
        ]
      },
      "ServiceProductImportResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "Always true on a 2xx response.",
            "example": true
          },
          "data": {
            "description": "The import and its progress.",
            "allOf": [
              {
                "$ref": "#/components/schemas/ServiceProductImport"
              }
            ]
          }
        },
        "required": [
          "success",
          "data"
        ]
      }
    },
    "headers": {
      "RateLimitLimit": {
        "description": "Requests per minute this API key is allowed.",
        "schema": {
          "type": "integer",
          "example": 100
        }
      },
      "RateLimitRemaining": {
        "description": "Requests left in the current one-minute window.",
        "schema": {
          "type": "integer",
          "example": 97
        }
      },
      "RateLimitReset": {
        "description": "Seconds until the current window ends and the count resets.",
        "schema": {
          "type": "integer",
          "example": 42
        }
      },
      "RequestId": {
        "description": "Identifier of this request. Send your own `x-request-id` to have it echoed back; otherwise one is generated. Quote it when contacting support.",
        "schema": {
          "type": "string",
          "example": "7f3c1d52-0a9b-4e6f-8d21-3b5a6c7d8e9f"
        }
      },
      "RetryAfter": {
        "description": "Seconds to wait before retrying.",
        "schema": {
          "type": "integer",
          "example": 42
        }
      }
    }
  }
}
