API conventions

These conventions apply to every Carriyo API. Read this page once before building; the individual spec references assume you already know it.

Base URL

All production endpoints are under:

https://api.carriyo.com

A demo environment is also available at https://demo-api.carriyo.com. It shares the same contract as production — use it to validate an integration before going live.

Authentication

Every request requires two things:

  • Authorization: Bearer <token> — an OAuth 2.0 access token obtained from the Authentication API.
  • tenant-id: <your-tenant-id> — your Carriyo tenant identifier, issued during onboarding.

A missing or expired token returns 401. A token that's valid but doesn't have access to the requested merchant or resource returns 403.

See Authentication for how to obtain and refresh tokens.

Versioning

The Carriyo API does not use URL path versioning (/v1/, /v2/). All endpoints are at the root path. Breaking changes follow a deprecation period: existing fields are marked deprecated and kept for at least one release cycle before removal. Additive changes (new fields, new enum values) are made without notice and are non-breaking.

Treat null or absent fields in responses as "not set", not as an error. New optional fields may appear in existing response shapes.

Request format

Send JSON with Content-Type: application/json. Bodies use snake_case field names throughout.

Dates and timestamps use ISO 8601 format with a timezone offset:

yyyy-MM-dd'T'HH:mm:ss.SSSXXX

Example: 2025-11-03T09:15:00.000+00:00. Timestamps without an offset are also accepted on input, but responses always include the offset.

Error responses

All errors return a JSON body in one of two shapes. Simple errors use errors — a list of human-readable strings:

{
  "status": "400",
  "timestamp": "2025-11-03T09:15:00.000+00:00",
  "errors": [
    "partner_shipment_reference : must not be blank"
  ]
}

Structured errors on shipment operations also return error_details alongside or instead of errors:

{
  "status": "400",
  "timestamp": "2025-11-03T09:15:00.000+00:00",
  "error_details": [
    {
      "level": "ERROR",
      "trigger": "VALIDATION",
      "source": "CARRIYO",
      "type": "MISSING_FIELD",
      "message": "partner_shipment_reference is required",
      "field": "partner_shipment_reference"
    }
  ]
}

The status field is the HTTP status code as a string, mirroring the HTTP response status. Both errors and error_details may be present in the same response.

HTTP status codes

CodeMeaning
200 OKRequest succeeded. Response body contains the result.
201 CreatedResource created. Response body contains the new resource.
204 No ContentRequest succeeded. No response body.
400 Bad RequestValidation failed. Check errors or error_details in the response body.
403 ForbiddenThe token is valid but access to this resource or merchant is denied.
404 Not FoundThe requested resource does not exist.
405 Method Not AllowedThe HTTP method is not supported on this path.
409 ConflictA concurrent write conflict occurred. Retry the request.
429 Too Many RequestsRate limit exceeded. Back off and retry. See Rate limits below.
500 Internal Server ErrorUnexpected server error.

401 Unauthorized is returned by the API Gateway before requests reach the application — it means the Authorization header is missing, malformed, or the token is expired.

Rate limits

Rate limits are enforced at the API Gateway edge, per API key. Exceeding the limit returns 429 Too Many Requests. The limits are per second (sustained rate) plus a burst allowance for short spikes.

PlanSustained rateBurst
Free5 req/s50
Starter10 req/s100
Pro20 req/s200
Enterprise50 req/s500

Your plan is set at the tenant level. Contact your account manager to change it.

When you receive a 429, wait before retrying. An exponential backoff strategy is recommended — start with a short pause and double it on each successive failure.

Idempotency

Most write operations are not idempotent. Sending the same request twice creates a second resource.

Two specific exceptions:

  • Shipment partner_shipment_reference — must be unique within your tenant. Reusing a reference that already exists returns 400. This is a uniqueness guard, not an idempotency replay.
  • Inventory bulk import request-id header — if you supply the same request-id on a second call, the request is rejected with 400. Look up the original batch by request-id using the status endpoint instead.

There is no general-purpose Idempotency-Key header.