openapi: 3.0.3 info: title: 'Lagr API' description: 'REST JSON API for Lagr inventory and warehouse management.' version: 1.0.0 servers: - url: 'https://api.lagrwms.no' tags: - name: 'Getting started' description: 'Health check, authentication, and session context.' - name: Warehouses description: 'Physical warehouses that hold stock.' - name: Catalog description: 'Product master data and relationships.' - name: Inventory description: 'On-hand, reserved, and available quantities per SKU and location.' - name: 'Warehouse operations' description: 'Shrinkage, damage, and valuation adjustments.' - name: Purchasing description: 'Suppliers and inbound purchase orders.' - name: Sales description: 'Customers and outbound sales order fulfillment.' - name: Integrations description: 'ERP connections, credentials, and Tripletex sync actions. Requires `integrations.manage`.' - name: Webhooks description: 'Incoming events from Tripletex (Bearer secret, not user token).' components: securitySchemes: default: type: http scheme: bearer description: "Authenticate with `POST /api/v1/auth/login` using `email`, `password`, and `tenant_slug`. The response includes a Bearer token.\n\nSend these headers on all other endpoints:\n\n- `Authorization: Bearer {token}`\n- `X-Tenant-Slug: {tenant_slug}`\n\nIntegration endpoints additionally require the `integrations.manage` permission for the authenticated user." security: - default: [] paths: /api/v1/health: get: summary: 'Health check' operationId: healthCheck description: 'Returns API and database status. No authentication required.' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: status: ok database: ok properties: status: type: string example: ok database: type: string example: ok tags: - 'Getting started' security: [] /api/v1/auth/login: post: summary: Login operationId: login description: 'Exchange email, password, and tenant slug for a Bearer token.' parameters: [] responses: { } tags: - 'Getting started' requestBody: required: true content: application/json: schema: type: object properties: email: type: string description: 'User email.' example: user@example.com password: type: string description: 'User password.' example: '|]|{+-' tenant_slug: type: string description: 'Tenant slug.' example: demo required: - email - password - tenant_slug security: [] /api/v1/me: get: summary: 'Get current user' operationId: getCurrentUser description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - 'Getting started' /api/v1/warehouses: get: summary: 'List warehouses' operationId: listWarehouses description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Warehouses post: summary: 'Create warehouses' operationId: createWarehouses description: '' parameters: [] responses: { } tags: - Warehouses requestBody: required: true content: application/json: schema: type: object properties: name: type: string description: 'Must not be greater than 255 characters.' example: b code: type: string description: 'Must not be greater than 64 characters.' example: 'n' status: type: string description: '' example: inactive enum: - active - inactive timezone: type: string description: 'Must not be greater than 64 characters.' example: Antarctica/Rothera nullable: true settings: type: object description: '' example: null properties: { } nullable: true required: - name - code '/api/v1/warehouses/{warehouseId}': get: summary: 'Get warehouses' operationId: getWarehouses description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Warehouses parameters: - in: path name: warehouseId description: '' example: architecto required: true schema: type: string '/api/v1/warehouses/{warehouseId}/locations': get: summary: 'List locations' operationId: listLocations description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Warehouses post: summary: 'Create locations' operationId: createLocations description: '' parameters: [] responses: { } tags: - Warehouses requestBody: required: true content: application/json: schema: type: object properties: name: type: string description: 'Must not be greater than 255 characters.' example: b code: type: string description: 'Must not be greater than 64 characters.' example: 'n' kind: type: string description: '' example: area enum: - area - location - slot parent_id: type: integer description: '' example: 16 nullable: true status: type: string description: '' example: inactive enum: - active - inactive settings: type: object description: '' example: null properties: { } nullable: true required: - name - code parameters: - in: path name: warehouseId description: '' example: architecto required: true schema: type: string '/api/v1/warehouses/{warehouseId}/locations/{locationId}': get: summary: 'Get locations' operationId: getLocations description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Warehouses parameters: - in: path name: warehouseId description: '' example: architecto required: true schema: type: string - in: path name: locationId description: '' example: architecto required: true schema: type: string /api/v1/products: get: summary: 'List products' operationId: listProducts description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Catalog post: summary: 'Create products' operationId: createProducts description: '' parameters: [] responses: { } tags: - Catalog requestBody: required: true content: application/json: schema: type: object properties: name: type: string description: 'Must not be greater than 255 characters.' example: b code: type: string description: 'Must not be greater than 64 characters.' example: 'n' category_id: type: integer description: '' example: 16 nullable: true vat_type_id: type: integer description: '' example: 16 nullable: true status: type: string description: '' example: inactive enum: - active - inactive description: type: string description: 'Must not be greater than 500 characters.' example: 'Et animi quos velit et fugiat.' nullable: true external_id: type: string description: 'Must not be greater than 255 characters.' example: d nullable: true source_system: type: string description: 'Must not be greater than 64 characters.' example: l nullable: true settings: type: object description: '' example: null properties: { } nullable: true required: - name - code '/api/v1/products/{productId}': get: summary: 'Get products' operationId: getProducts description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Catalog put: summary: 'Update products' operationId: updateProducts description: '' parameters: [] responses: { } tags: - Catalog requestBody: required: false content: application/json: schema: type: object properties: name: type: string description: 'Must not be greater than 255 characters.' example: b category_id: type: integer description: '' example: 16 nullable: true vat_type_id: type: integer description: '' example: 16 nullable: true status: type: string description: '' example: inactive enum: - active - inactive description: type: string description: 'Must not be greater than 500 characters.' example: 'Et animi quos velit et fugiat.' nullable: true parameters: - in: path name: productId description: '' example: architecto required: true schema: type: string '/api/v1/products/{productId}/relationships': post: summary: 'Add product relationship' operationId: addProductRelationship description: '' parameters: [] responses: { } tags: - Catalog requestBody: required: true content: application/json: schema: type: object properties: related_product_id: type: integer description: '' example: 16 relationship_type: type: string description: '' example: architecto required: - related_product_id parameters: - in: path name: productId description: '' example: architecto required: true schema: type: string '/api/v1/products/{productId}/relationships/{relatedProductId}': delete: summary: 'Remove product relationship' operationId: removeProductRelationship description: '' parameters: [] responses: { } tags: - Catalog parameters: - in: path name: productId description: '' example: architecto required: true schema: type: string - in: path name: relatedProductId description: '' example: architecto required: true schema: type: string '/api/v1/products/{productId}/skus': get: summary: 'List SKUs' operationId: listSKUs description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Catalog post: summary: 'Create SKUs' operationId: createSKUs description: '' parameters: [] responses: { } tags: - Catalog requestBody: required: true content: application/json: schema: type: object properties: code: type: string description: 'Must not be greater than 64 characters.' example: b manufacturer_code: type: string description: 'Must not be greater than 128 characters.' example: 'n' nullable: true barcode: type: string description: 'Must not be greater than 128 characters.' example: g nullable: true barcodes: type: array description: 'Must not be greater than 128 characters.' example: - z items: type: string distributor_codes: type: array description: '' example: null items: type: object properties: distributor_id: type: integer description: 'This field is required when distributor_codes is present.' example: 16 item_code: type: string description: 'This field is required when distributor_codes is present. Must not be greater than 128 characters.' example: 'n' status: type: string description: '' example: active enum: - active - inactive external_id: type: string description: 'Must not be greater than 255 characters.' example: m nullable: true source_system: type: string description: 'Must not be greater than 64 characters.' example: i nullable: true settings: type: object description: '' example: null properties: { } nullable: true required: - code parameters: - in: path name: productId description: '' example: architecto required: true schema: type: string '/api/v1/products/{productId}/skus/{skuId}': get: summary: 'Get SKUs' operationId: getSKUs description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Catalog parameters: - in: path name: productId description: '' example: architecto required: true schema: type: string - in: path name: skuId description: '' example: architecto required: true schema: type: string /api/v1/categories: get: summary: 'List categories' operationId: listCategories description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Catalog requestBody: required: false content: application/json: schema: type: object properties: parent_id: type: integer description: '' example: 16 nullable: true post: summary: 'Create categories' operationId: createCategories description: '' parameters: [] responses: { } tags: - Catalog requestBody: required: true content: application/json: schema: type: object properties: name: type: string description: 'Must not be greater than 255 characters.' example: b code: type: string description: 'Must not be greater than 64 characters.' example: 'n' parent_id: type: integer description: '' example: 16 nullable: true department_id: type: integer description: '' example: 16 nullable: true status: type: string description: '' example: active enum: - active - inactive tracking_rules: type: object description: '' example: null properties: serial: type: string description: '' example: architecto imei: type: string description: '' example: architecto fifo: type: boolean description: '' example: true nullable: true settings: type: object description: '' example: null properties: { } nullable: true required: - name - code '/api/v1/categories/{categoryId}': get: summary: 'Get categories' operationId: getCategories description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Catalog parameters: - in: path name: categoryId description: '' example: architecto required: true schema: type: string /api/v1/vat-types: get: summary: 'List VAT types' operationId: listVATTypes description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Catalog requestBody: required: false content: application/json: schema: type: object properties: direction: type: string description: '' example: inbound enum: - inbound - outbound post: summary: 'Create VAT types' operationId: createVATTypes description: '' parameters: [] responses: { } tags: - Catalog requestBody: required: true content: application/json: schema: type: object properties: code: type: string description: 'Must not be greater than 16 characters.' example: bngzmiyvdljnikhw name: type: string description: 'Must not be greater than 255 characters.' example: a direction: type: string description: '' example: outbound enum: - inbound - outbound basis_percentage: type: number description: 'Must be at least 0.' example: 50 tax_percentage: type: number description: 'Must be at least 0.' example: 62 status: type: string description: '' example: active enum: - active - inactive tripletex_id: type: integer description: '' example: 16 nullable: true source_system: type: string description: 'Must not be greater than 64 characters.' example: 'n' nullable: true required: - code - name - direction /api/v1/vat-types/seed-defaults: post: summary: 'Seed default VAT types' operationId: seedDefaultVATTypes description: '' parameters: [] responses: { } tags: - Catalog '/api/v1/vat-types/{vatTypeId}': get: summary: 'Get VAT types' operationId: getVATTypes description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Catalog parameters: - in: path name: vatTypeId description: '' example: architecto required: true schema: type: string /api/v1/currencies: get: summary: 'List currencies' operationId: listCurrencies description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Catalog /api/v1/exchange-rates: get: summary: 'List exchange rates' operationId: listExchangeRates description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Catalog requestBody: required: false content: application/json: schema: type: object properties: currency: type: string description: 'Must be 3 characters.' example: bng limit: type: integer description: 'Must be at least 1. Must not be greater than 100.' example: 16 /api/v1/exchange-rates/latest: get: summary: 'Get latest exchange rate' operationId: getLatestExchangeRate description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Catalog requestBody: required: true content: application/json: schema: type: object properties: currency: type: string description: 'Must be 3 characters.' example: bng date: type: string description: 'Must be a valid date.' example: '2026-07-10T21:07:01' required: - currency /api/v1/exchange-rates/sync: post: summary: 'Sync exchange rates' operationId: syncExchangeRates description: '' parameters: [] responses: { } tags: - Catalog requestBody: required: false content: application/json: schema: type: object properties: currencies: type: array description: 'Must be 3 characters.' example: - bng items: type: string observations: type: integer description: 'Must be at least 1. Must not be greater than 120.' example: 16 /api/v1/inventory: get: summary: 'List inventory levels' operationId: listInventoryLevels description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Inventory requestBody: required: false content: application/json: schema: type: object properties: sku_id: type: integer description: '' example: 16 location_id: type: integer description: '' example: 16 /api/v1/inventory-lots: get: summary: 'List inventory lots' operationId: listInventoryLots description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Inventory requestBody: required: false content: application/json: schema: type: object properties: sku_id: type: integer description: '' example: 16 location_id: type: integer description: '' example: 16 /api/v1/tracked-units: get: summary: 'List tracked units' operationId: listTrackedUnits description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Inventory requestBody: required: false content: application/json: schema: type: object properties: sku_id: type: integer description: '' example: 16 location_id: type: integer description: '' example: 16 /api/v1/stock-movements: get: summary: 'List stock movements' operationId: listStockMovements description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Inventory requestBody: required: false content: application/json: schema: type: object properties: sku_id: type: integer description: '' example: 16 location_id: type: integer description: '' example: 16 post: summary: 'Create stock movements' operationId: createStockMovements description: '' parameters: [] responses: { } tags: - Inventory requestBody: required: true content: application/json: schema: type: object properties: sku_id: type: integer description: '' example: 16 location_id: type: integer description: '' example: 16 movement_type: type: string description: '' example: adjust enum: - receive - pick - ship - write_off - adjust - reserve - release_reservation quantity_delta: type: string description: '' example: architecto unit_cost: type: number description: 'Must be at least 0.' example: 39 nullable: true units: type: array description: '' example: null items: type: object properties: serial_number: type: string description: 'Must not be greater than 128 characters.' example: g nullable: true imei: type: string description: 'Must not be greater than 32 characters.' example: z nullable: true unit_cost: type: number description: 'Must be at least 0.' example: 77 nullable: true tracked_unit_ids: type: array description: '' example: - 16 items: type: integer note: type: string description: '' example: architecto nullable: true source_document_type: type: string description: 'Must not be greater than 64 characters.' example: 'n' nullable: true source_document_id: type: integer description: '' example: 16 nullable: true source_system: type: string description: 'Must not be greater than 64 characters.' example: 'n' nullable: true metadata: type: object description: '' example: null properties: { } nullable: true required: - sku_id - location_id - movement_type - quantity_delta /api/v1/stock-transfers: post: summary: 'Create stock transfers' operationId: createStockTransfers description: '' parameters: [] responses: { } tags: - Inventory requestBody: required: true content: application/json: schema: type: object properties: sku_id: type: integer description: '' example: 16 from_location_id: type: integer description: '' example: 16 to_location_id: type: integer description: 'The value and from_location_id must be different.' example: 16 quantity: type: string description: '' example: architecto note: type: string description: '' example: architecto nullable: true metadata: type: object description: '' example: null properties: { } nullable: true required: - sku_id - from_location_id - to_location_id - quantity /api/v1/write-offs: get: summary: 'List write-off' operationId: listWriteOff description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - 'Warehouse operations' post: summary: 'Create write-off' operationId: createWriteOff description: '' parameters: [] responses: { } tags: - 'Warehouse operations' requestBody: required: true content: application/json: schema: type: object properties: warehouse_id: type: integer description: '' example: 16 type: type: string description: '' example: architecto reason_code: type: string description: '' example: architecto notes: type: string description: '' example: architecto nullable: true lines: type: array description: 'Must have at least 1 items.' example: - [] items: type: object properties: sku_id: type: integer description: '' example: 16 location_id: type: integer description: '' example: 16 quantity: type: string description: '' example: architecto new_unit_cost: type: number description: 'Must be at least 0.' example: 39 nullable: true tracked_unit_ids: type: array description: '' example: - 16 items: type: integer notes: type: string description: '' example: architecto nullable: true required: - sku_id - location_id - quantity required: - warehouse_id - lines '/api/v1/write-offs/{writeOffId}': get: summary: 'Get write-off' operationId: getWriteOff description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - 'Warehouse operations' parameters: - in: path name: writeOffId description: '' example: architecto required: true schema: type: string '/api/v1/write-offs/{writeOffId}/complete': post: summary: 'Complete write-off' operationId: completeWriteOff description: '' parameters: [] responses: { } tags: - 'Warehouse operations' parameters: - in: path name: writeOffId description: '' example: architecto required: true schema: type: string '/api/v1/write-offs/{writeOffId}/cancel': post: summary: 'Cancel write-off' operationId: cancelWriteOff description: '' parameters: [] responses: { } tags: - 'Warehouse operations' parameters: - in: path name: writeOffId description: '' example: architecto required: true schema: type: string /api/v1/stock-takes: get: summary: 'List stock take' operationId: listStockTake description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - 'Warehouse operations' post: summary: 'Create stock take' operationId: createStockTake description: '' parameters: [] responses: { } tags: - 'Warehouse operations' requestBody: required: true content: application/json: schema: type: object properties: warehouse_id: type: integer description: '' example: 16 type: type: string description: '' example: architecto scope: type: string description: '' example: architecto category_ids: type: array description: '' example: - 16 items: type: integer notes: type: string description: '' example: architecto nullable: true assigned_user_id: type: integer description: '' example: 16 nullable: true lines: type: array description: 'Must have at least 1 items.' example: null items: type: object properties: sku_id: type: integer description: 'This field is required when lines is present.' example: 16 location_id: type: integer description: 'This field is required when lines is present.' example: 16 notes: type: string description: '' example: architecto nullable: true required: - warehouse_id '/api/v1/stock-takes/{stockTakeId}': get: summary: 'Get stock take' operationId: getStockTake description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - 'Warehouse operations' parameters: - in: path name: stockTakeId description: '' example: architecto required: true schema: type: string '/api/v1/stock-takes/{stockTakeId}/start': post: summary: 'Start stock take' operationId: startStockTake description: '' parameters: [] responses: { } tags: - 'Warehouse operations' parameters: - in: path name: stockTakeId description: '' example: architecto required: true schema: type: string '/api/v1/stock-takes/{stockTakeId}/scan': post: summary: 'Scan during stock take' operationId: scanDuringStockTake description: '' parameters: [] responses: { } tags: - 'Warehouse operations' requestBody: required: true content: application/json: schema: type: object properties: scan: type: string description: 'Must not be greater than 128 characters.' example: b location_id: type: integer description: '' example: 16 nullable: true increment: type: boolean description: '' example: true required: - scan parameters: - in: path name: stockTakeId description: '' example: architecto required: true schema: type: string '/api/v1/stock-takes/{stockTakeId}/lines/{lineId}/count': post: summary: 'Record stock take count' operationId: recordStockTakeCount description: '' parameters: [] responses: { } tags: - 'Warehouse operations' requestBody: required: true content: application/json: schema: type: object properties: counted_quantity: type: string description: '' example: architecto notes: type: string description: '' example: architecto nullable: true required: - counted_quantity parameters: - in: path name: stockTakeId description: '' example: architecto required: true schema: type: string - in: path name: lineId description: '' example: architecto required: true schema: type: string '/api/v1/stock-takes/{stockTakeId}/complete': post: summary: 'Complete stock take' operationId: completeStockTake description: '' parameters: [] responses: { } tags: - 'Warehouse operations' parameters: - in: path name: stockTakeId description: '' example: architecto required: true schema: type: string '/api/v1/stock-takes/{stockTakeId}/cancel': post: summary: 'Cancel stock take' operationId: cancelStockTake description: '' parameters: [] responses: { } tags: - 'Warehouse operations' parameters: - in: path name: stockTakeId description: '' example: architecto required: true schema: type: string /api/v1/distributors: get: summary: 'List distributors' operationId: listDistributors description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Purchasing post: summary: 'Create distributors' operationId: createDistributors description: '' parameters: [] responses: { } tags: - Purchasing requestBody: required: true content: application/json: schema: type: object properties: name: type: string description: 'Must not be greater than 255 characters.' example: b distributor_number: type: string description: 'Must not be greater than 32 characters.' example: 'n' nullable: true address_line1: type: string description: 'Must not be greater than 255 characters.' example: g nullable: true address_line2: type: string description: 'Must not be greater than 255 characters.' example: z nullable: true postal_code: type: string description: 'Must not be greater than 32 characters.' example: m nullable: true city: type: string description: 'Must not be greater than 255 characters.' example: i nullable: true country: type: string description: 'Must be 2 characters.' example: yv nullable: true phone: type: string description: 'Must not be greater than 64 characters.' example: d nullable: true contact_email: type: string description: 'Must be a valid email address. Must not be greater than 255 characters.' example: jermaine.tillman@example.org nullable: true organization_number: type: string description: 'Must not be greater than 32 characters.' example: h nullable: true website: type: string description: 'Must not be greater than 255 characters.' example: w nullable: true notes: type: string description: '' example: architecto nullable: true status: type: string description: '' example: inactive enum: - active - inactive default_currency: type: string description: 'Must be 3 characters.' example: ngz nullable: true required: - name '/api/v1/distributors/{distributorId}': get: summary: 'Get distributors' operationId: getDistributors description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Purchasing parameters: - in: path name: distributorId description: '' example: architecto required: true schema: type: string /api/v1/purchase-orders: get: summary: 'List purchase order' operationId: listPurchaseOrder description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Purchasing post: summary: 'Create purchase order' operationId: createPurchaseOrder description: '' parameters: [] responses: { } tags: - Purchasing requestBody: required: true content: application/json: schema: type: object properties: warehouse_id: type: integer description: '' example: 16 distributor_id: type: integer description: '' example: 16 nullable: true vendor_name: type: string description: 'Must not be greater than 255 characters.' example: 'n' nullable: true reference: type: string description: 'Must not be greater than 255 characters.' example: g nullable: true expected_at: type: string description: 'Must be a valid date.' example: '2026-07-10T21:07:01' nullable: true notes: type: string description: '' example: architecto nullable: true currency_code: type: string description: 'Must be 3 characters.' example: ngz nullable: true lines: type: array description: 'Must have at least 1 items.' example: - [] items: type: object properties: sku_id: type: integer description: '' example: 16 quantity_ordered: type: string description: '' example: architecto unit_cost: type: number description: 'Must be at least 0.' example: 39 nullable: true required: - sku_id - quantity_ordered required: - warehouse_id - lines '/api/v1/purchase-orders/{purchaseOrderId}': get: summary: 'Get purchase order' operationId: getPurchaseOrder description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Purchasing parameters: - in: path name: purchaseOrderId description: '' example: architecto required: true schema: type: string '/api/v1/purchase-orders/{purchaseOrderId}/submit': post: summary: 'Submit purchase order' operationId: submitPurchaseOrder description: '' parameters: [] responses: { } tags: - Purchasing parameters: - in: path name: purchaseOrderId description: '' example: architecto required: true schema: type: string '/api/v1/purchase-orders/{purchaseOrderId}/cancel': post: summary: 'Cancel purchase order' operationId: cancelPurchaseOrder description: '' parameters: [] responses: { } tags: - Purchasing parameters: - in: path name: purchaseOrderId description: '' example: architecto required: true schema: type: string '/api/v1/purchase-orders/{purchaseOrderId}/lines/{lineId}/receive': post: summary: 'Receive purchase order line' operationId: receivePurchaseOrderLine description: '' parameters: [] responses: { } tags: - Purchasing requestBody: required: true content: application/json: schema: type: object properties: location_id: type: integer description: '' example: 16 quantity: type: string description: '' example: architecto unit_cost: type: number description: 'Must be at least 0.' example: 39 nullable: true units: type: array description: '' example: null items: type: object properties: serial_number: type: string description: 'Must not be greater than 128 characters.' example: 'n' nullable: true imei: type: string description: 'Must not be greater than 32 characters.' example: g nullable: true note: type: string description: '' example: architecto nullable: true required: - location_id - quantity parameters: - in: path name: purchaseOrderId description: '' example: architecto required: true schema: type: string - in: path name: lineId description: '' example: architecto required: true schema: type: string /api/v1/customers: get: summary: 'List customers' operationId: listCustomers description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Sales post: summary: 'Create customers' operationId: createCustomers description: '' parameters: [] responses: { } tags: - Sales requestBody: required: true content: application/json: schema: type: object properties: name: type: string description: 'Must not be greater than 255 characters.' example: b customer_number: type: string description: 'Must not be greater than 32 characters.' example: 'n' nullable: true address_line1: type: string description: 'Must not be greater than 255 characters.' example: g nullable: true address_line2: type: string description: 'Must not be greater than 255 characters.' example: z nullable: true postal_code: type: string description: 'Must not be greater than 32 characters.' example: m nullable: true city: type: string description: 'Must not be greater than 255 characters.' example: i nullable: true country: type: string description: 'Must be 2 characters.' example: yv nullable: true phone: type: string description: 'Must not be greater than 64 characters.' example: d nullable: true email: type: string description: 'Must be a valid email address. Must not be greater than 255 characters.' example: jermaine.tillman@example.org nullable: true organization_number: type: string description: 'Must not be greater than 32 characters.' example: h nullable: true status: type: string description: '' example: inactive enum: - active - inactive required: - name '/api/v1/customers/{customerId}': get: summary: 'Get customers' operationId: getCustomers description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Sales put: summary: 'Update customers' operationId: updateCustomers description: '' parameters: [] responses: { } tags: - Sales requestBody: required: false content: application/json: schema: type: object properties: name: type: string description: 'Must not be greater than 255 characters.' example: b address_line1: type: string description: 'Must not be greater than 255 characters.' example: 'n' nullable: true address_line2: type: string description: 'Must not be greater than 255 characters.' example: g nullable: true postal_code: type: string description: 'Must not be greater than 32 characters.' example: z nullable: true city: type: string description: 'Must not be greater than 255 characters.' example: m nullable: true country: type: string description: 'Must be 2 characters.' example: iy nullable: true phone: type: string description: 'Must not be greater than 64 characters.' example: v nullable: true email: type: string description: 'Must be a valid email address. Must not be greater than 255 characters.' example: jdach@example.org nullable: true organization_number: type: string description: 'Must not be greater than 32 characters.' example: i nullable: true status: type: string description: '' example: active enum: - active - inactive parameters: - in: path name: customerId description: '' example: architecto required: true schema: type: string /api/v1/sales-orders: get: summary: 'List sales order' operationId: listSalesOrder description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Sales post: summary: 'Create sales order' operationId: createSalesOrder description: '' parameters: [] responses: { } tags: - Sales requestBody: required: true content: application/json: schema: type: object properties: warehouse_id: type: integer description: '' example: 16 customer_name: type: string description: 'Must not be greater than 255 characters.' example: 'n' reference: type: string description: 'Must not be greater than 255 characters.' example: g nullable: true notes: type: string description: '' example: architecto nullable: true lines: type: array description: 'Must have at least 1 items.' example: - [] items: type: object properties: sku_id: type: integer description: '' example: 16 quantity_ordered: type: string description: '' example: architecto required: - sku_id - quantity_ordered required: - warehouse_id - customer_name - lines '/api/v1/sales-orders/{salesOrderId}': get: summary: 'Get sales order' operationId: getSalesOrder description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Sales parameters: - in: path name: salesOrderId description: '' example: architecto required: true schema: type: string '/api/v1/sales-orders/{salesOrderId}/confirm': post: summary: 'Confirm and reserve sales order' operationId: confirmAndReserveSalesOrder description: '' parameters: [] responses: { } tags: - Sales requestBody: required: true content: application/json: schema: type: object properties: allocations: type: array description: 'Must have at least 1 items.' example: - [] items: type: object properties: line_id: type: integer description: '' example: 16 location_id: type: integer description: '' example: 16 quantity: type: string description: '' example: architecto required: - line_id - location_id - quantity required: - allocations parameters: - in: path name: salesOrderId description: '' example: architecto required: true schema: type: string '/api/v1/sales-orders/{salesOrderId}/cancel': post: summary: 'Cancel sales order' operationId: cancelSalesOrder description: '' parameters: [] responses: { } tags: - Sales parameters: - in: path name: salesOrderId description: '' example: architecto required: true schema: type: string '/api/v1/sales-orders/{salesOrderId}/allocations/{allocationId}/pick': post: summary: 'Pick allocated stock' operationId: pickAllocatedStock description: '' parameters: [] responses: { } tags: - Sales requestBody: required: true content: application/json: schema: type: object properties: quantity: type: string description: '' example: architecto note: type: string description: '' example: architecto nullable: true required: - quantity parameters: - in: path name: salesOrderId description: '' example: architecto required: true schema: type: string - in: path name: allocationId description: '' example: architecto required: true schema: type: string '/api/v1/sales-orders/{salesOrderId}/allocations/{allocationId}/ship': post: summary: 'Ship picked stock' operationId: shipPickedStock description: '' parameters: [] responses: { } tags: - Sales requestBody: required: true content: application/json: schema: type: object properties: quantity: type: string description: '' example: architecto tracked_unit_ids: type: array description: '' example: - 16 items: type: integer note: type: string description: '' example: architecto nullable: true required: - quantity parameters: - in: path name: salesOrderId description: '' example: architecto required: true schema: type: string - in: path name: allocationId description: '' example: architecto required: true schema: type: string /api/v1/integrations/tripletex/webhook-guide: get: summary: 'Tripletex webhook guide' operationId: tripletexWebhookGuide description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Integrations /api/v1/integrations/tripletex/sync-products: post: summary: 'Sync products from Tripletex' operationId: syncProductsFromTripletex description: '' parameters: [] responses: { } tags: - Integrations requestBody: required: false content: application/json: schema: type: object properties: connection_id: type: integer description: '' example: 16 /api/v1/integrations/tripletex/sync-all: post: summary: 'Inbound sync (all entities)' operationId: inboundSyncallEntities description: '' parameters: [] responses: { } tags: - Integrations /api/v1/integrations/tripletex/reconcile: post: summary: 'Two-way reconciliation' operationId: twoWayReconciliation description: '' parameters: [] responses: { } tags: - Integrations /api/v1/integrations/tripletex/push-products: post: summary: 'Push products to Tripletex' operationId: pushProductsToTripletex description: '' parameters: [] responses: { } tags: - Integrations requestBody: required: false content: application/json: schema: type: object properties: connection_id: type: integer description: '' example: 16 product_id: type: integer description: '' example: 16 /api/v1/integrations/tripletex/sync-orders: post: summary: 'Sync sales orders from Tripletex' operationId: syncSalesOrdersFromTripletex description: '' parameters: [] responses: { } tags: - Integrations requestBody: required: false content: application/json: schema: type: object properties: connection_id: type: integer description: '' example: 16 /api/v1/integrations/tripletex/sync-suppliers: post: summary: 'Sync suppliers from Tripletex' operationId: syncSuppliersFromTripletex description: '' parameters: [] responses: { } tags: - Integrations requestBody: required: false content: application/json: schema: type: object properties: connection_id: type: integer description: '' example: 16 /api/v1/integrations/tripletex/sync-customers: post: summary: 'Sync customers from Tripletex' operationId: syncCustomersFromTripletex description: '' parameters: [] responses: { } tags: - Integrations requestBody: required: false content: application/json: schema: type: object properties: connection_id: type: integer description: '' example: 16 /api/v1/integrations/tripletex/sync-employees: post: summary: 'Sync Employees' operationId: syncEmployees description: '' parameters: [] responses: { } tags: - Integrations requestBody: required: false content: application/json: schema: type: object properties: connection_id: type: integer description: '' example: 16 /api/v1/integrations/webhook-events: get: summary: 'List webhook events' operationId: listWebhookEvents description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Integrations requestBody: required: false content: application/json: schema: type: object properties: connection_id: type: integer description: '' example: 16 status: type: string description: 'Must not be greater than 32 characters.' example: 'n' event: type: string description: 'Must not be greater than 128 characters.' example: g per_page: type: integer description: 'Must be at least 1. Must not be greater than 100.' example: 16 /api/v1/integrations/tripletex/webhook-subscriptions: get: summary: 'List webhook subscriptions' operationId: listWebhookSubscriptions description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Integrations put: summary: 'Update webhook subscriptions' operationId: updateWebhookSubscriptions description: '' parameters: [] responses: { } tags: - Integrations requestBody: required: false content: application/json: schema: type: object properties: connection_id: type: integer description: '' example: 16 events: type: array description: '' example: - architecto items: type: string /api/v1/integrations: get: summary: 'List integration connection' operationId: listIntegrationConnection description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Integrations post: summary: 'Create integration connection' operationId: createIntegrationConnection description: '' parameters: [] responses: { } tags: - Integrations requestBody: required: true content: application/json: schema: type: object properties: name: type: string description: 'Must not be greater than 255 characters.' example: b provider: type: string description: 'Must not be greater than 64 characters.' example: 'n' status: type: string description: '' example: inactive enum: - active - inactive - error credentials: type: object description: '' example: null properties: { } nullable: true settings: type: object description: '' example: null properties: { } nullable: true required: - name - provider '/api/v1/integrations/{connectionId}': get: summary: 'Get integration connection' operationId: getIntegrationConnection description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Integrations put: summary: 'Update integration connection' operationId: updateIntegrationConnection description: '' parameters: [] responses: { } tags: - Integrations requestBody: required: false content: application/json: schema: type: object properties: name: type: string description: 'Must not be greater than 255 characters.' example: b status: type: string description: '' example: error enum: - active - inactive - error credentials: type: object description: '' example: null properties: { } nullable: true settings: type: object description: '' example: null properties: { } nullable: true parameters: - in: path name: connectionId description: '' example: architecto required: true schema: type: string '/api/v1/integrations/{connectionId}/test': post: summary: 'Test integration connection' operationId: testIntegrationConnection description: '' parameters: [] responses: { } tags: - Integrations parameters: - in: path name: connectionId description: '' example: architecto required: true schema: type: string '/api/v1/webhooks/tripletex/{tenantSlug}': post: summary: 'Tripletex webhook receiver' operationId: tripletexWebhookReceiver description: 'Inbound events from Tripletex. Authenticated via webhook signature middleware, not Bearer tokens.' parameters: [] responses: { } tags: - Webhooks security: [] parameters: - in: path name: tenantSlug description: 'Tenant slug configured for the webhook.' example: demo required: true schema: type: string