generated: '2026-08-09' method: derived source: - openapi/canix-openapi-original.yml - 'live probes of https://api.canix.com/api/v1/ (2026-08-09)' docs: https://api.canix.com/api-docs-swagger/index.html description: >- Cross-cutting request/response semantics for the Canix REST API, derived from the shared components in Canix's OpenAPI and from response headers observed on live calls. Canix publishes no separate conventions guide, so everything here is read off the contract or the wire — including the absences, which are recorded rather than filled in. authentication: style: api-key-header header: X-API-KEY scope: company see: authentication/canix-authentication.yml versioning: scheme: uri-path current: v1 base_url: https://api.canix.com/api/v1 document_version: 1.3.10 document_version_signal: >- The Swagger UI loads the spec as ../api.yaml?ver=1.3.10, so the spec document carries a semantic version even though the URI path stays at v1. There is no version header, no date-based versioning, and no published policy connecting document versions to changes. see: lifecycle/canix-lifecycle.yml pagination: style: limit-offset parameters: - name: limit in: query default: 2000 maximum: 2000 description: Max number of records to return. Cannot exceed 2000. - name: offset in: query default: 0 description: Offset for the returned records. response_envelope: >- Collections are returned as a bare JSON array. There is no wrapper object, no total count, no next/prev cursor and no Link header — a client cannot tell a full last page from a page that happens to be exactly `limit` long except by requesting one more. count_endpoints: - GetLocationsCount - GetPlantsCount count_note: >- Only two resources (locations, plants) expose a dedicated count operation. Every other collection must be counted by walking it. applies_to: 27 collection operations sharing $ref limitParam/offsetParam sorting: parameter: order_by syntax: SQL ORDER BY fragment example: id desc filtering: parameter: where syntax: SQL-like WHERE clause operators: - '=' - '>' - < - '>=' - <= - BETWEEN - IN - LIKE - AND - OR patterns: - equality: status='Active' - comparison: updated_at >= '2024-01-01' - range: id BETWEEN 1 AND 10000 - list: status IN ('Active', 'Pending') - pattern: sku LIKE 'ABC%' - compound: facility_id=123 AND is_active=true incremental_sync: >- The documented idiom for incremental sync is a where clause on updated_at, e.g. "updated_at >= '2024-01-01'", combined with order_by and limit/offset. agent_caution: >- This is a string-assembled query language passed over the wire. An agent constructing a `where` clause from user text is doing untrusted query assembly — parameterize values, never interpolate raw user input, and constrain the operator set. idempotency: supported: false header: null evidence: >- No Idempotency-Key parameter, header or extension appears anywhere in the 6,289-line OpenAPI, and no idempotency guidance is published. The 19 write operations — including CreateSalesOrder, PostPurchaseOrder, AdjustCustomerCredit, CreateItem and CreateVendor — have no documented safe-retry contract. consequence: >- A retried POST after a timeout may create a duplicate sales order, purchase order or credit adjustment. Clients must dedupe on their own side. NOTE for the rating: no `Idempotency` pointer is emitted in apis.yml, because Canix has no idempotency contract to point at. request_tracing: header: x-request-id direction: response observed: true evidence: 'x-request-id: 5455cbbc-d833-8da8-55d4-af806ec7a2aa on GET /api/v1/facilities' documented: false note: >- Rails emits x-request-id on every response, including errors. It is the only correlation handle a caller has when contacting support, but it is not documented and there is no documented way to supply a client-side request id. rate_limiting: documented: false headers_observed: none evidence: >- No RateLimit-*, X-RateLimit-*, or Retry-After header was returned on any probed call, no 429 response is declared on any of the 74 operations, and no rate-limit page is published. The only published ceiling is the 2000-record cap on `limit`, which is a page size, not a rate limit. error_envelope: content_type: application/json application_shape: '{"message": ""}' routing_shape: '{"status": , "error": ""}' rfc9457: false machine_readable_code: false see: errors/canix-problem-types.yml field_expansion: supported: false note: >- No expand/fields/include parameter. Related records are either embedded by default (SalesOrder embeds customer, contents, payments) or fetched through a dedicated sub-resource operation such as GetSalesOrderContentsById. metadata: custom_fields: not exposed via the API media_types: request: - application/json - multipart/form-data (CreateMETRCItemPhotos, CreateMETRCItemFiles) response: - application/json async_operations: pattern: submission polling note: >- Some writes are queued rather than applied inline. They return a Submission with a SubmissionStatus, polled through GetSubmissionById at /submissions/{submission_id}. This is the closest Canix comes to an async contract; there are no webhooks and no callbacks. transport_security: https: enforced tls: TLSv1.3 hsts: 'max-age=63072000; includeSubDomains observed on api.canix.com/mcp responses' see: security/canix-domain-security.yml cross_links: errors: errors/canix-problem-types.yml lifecycle: lifecycle/canix-lifecycle.yml authentication: authentication/canix-authentication.yml scopes: scopes/canix-scopes.yml data_model: data-model/canix-data-model.yml