CanopyDocs

Conventions

Idempotency, error schema, rate limits, pagination, filtering, and incremental sync — the rules that apply across every endpoint.

These rules apply across the whole API.

Idempotency

Every create endpoint (POST /api/orders, POST /api/receiving/shipments, POST /api/shipping/labels) accepts an Idempotency-Key header. Send a unique key (e.g. a UUID) per logical operation and you can safely retry on timeouts or network errors without creating duplicates.

Idempotent create
curl -X POST https://api.staging.canopywms.com/api/orders \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Idempotency-Key: 6f1a8c2e-..." \
  -H "Content-Type: application/json" \
  -d '{ "...": "..." }'

Behavior:

SituationResult
First request with a keyRuns normally; the 2xx response is stored against the key.
Retry after successReturns the original status and body verbatim, with an Idempotent-Replay: true header.
Concurrent duplicate (first still in flight)409A request with this Idempotency-Key is already in progress.
Same key reused for a different operation422Idempotency-Key was already used for a different operation.

Only successful (2xx) responses are cached, so a transient validation error never "poisons" a key.

Domain-level idempotency for purchase orders

Purchase-order creates are also idempotent on your external PO number — re-creating a PO with an externalPoNumber that already exists returns the existing PO rather than a duplicate, independent of the Idempotency-Key header. See Endpoints → Purchase orders.

Errors

Errors use a consistent JSON shape:

Error response
{
  "success": false,
  "error": "Human-readable message describing what went wrong"
}

Conditions are distinguished by HTTP status:

StatusMeaning
400Validation error (malformed or missing fields)
401Missing or invalid token
403Token lacks the required scope, or the endpoint isn't credential-callable
404Resource not found within your tenant/client scope
409Idempotency conflict (duplicate in flight)
422Idempotency key reused for a different operation
429Rate limit exceeded
5xxTransient server error — safe to retry with the same Idempotency-Key

Rate limits

Each credential has its own requests-per-minute limit. Responses carry the standard rate-limit headers so you can pace yourself:

x-ratelimit-limit: 600
x-ratelimit-remaining: 591
x-ratelimit-reset: 42

When you exceed the limit you get 429 with a retry-after header. The token endpoint (/oauth/token) is separately limited per source IP. Need a higher limit? Ask your CanopyWMS administrator — the per-credential limit is configurable.

Pagination

List endpoints are paginated with page and limit (max 500) query parameters, and return a pagination block:

List envelope
{
  "success": true,
  "data": [ /* ...rows... */ ],
  "pagination": { "page": 1, "limit": 50, "total": 1284, "totalPages": 26 }
}

Most lists also accept sortBy, sortOrder (asc/desc), and search.

Filtering & incremental sync

List endpoints expose resource-specific filters (status, date range, and exact external-reference lookups — see each resource under Endpoints).

For incremental sync / changed-since polling, orders, purchase orders, and shipments accept an updatedSince parameter (ISO-8601). Combine it with sortBy=updatedAt&sortOrder=asc to walk every change since your last poll:

Pull everything changed since the last sync
curl "https://api.staging.canopywms.com/api/orders?updatedSince=2026-06-29T00:00:00Z&sortBy=updatedAt&sortOrder=asc&limit=200" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

Versioning

All endpoints live under the /api path. Event-type names and response fields are treated as a stable, additive contract — new fields and new webhook event types are added without breaking existing consumers, so build your integration to ignore unknown fields.

On this page