Beta

The Partner Trading API v1 is in beta — endpoints and response shapes may still change.

Developers
Cru World Wine
API v1 · REST & MCP
Getting started

Response format

Successes and failures share one JSON envelope. The HTTP status is the real outcome — an error is never hidden inside a 200 — and a failure carries a stable, symbolic code to branch on.

Success envelope
{
  "status": "OK",
  "message": "Credential is valid",
  "meta": {
    "version": "1.0",
    "timestamp": "2026-10-19T10:10:55Z",
    "request_id": "6b183f2425c81699f1f3d3e5"
  },
  "data": {
    "pong": true,
    "client": {
      "environment": "live",
      "key_prefix": "cru_live_7KQ2...",
      "organisation": "Example Wine Merchants Ltd",
      "label": "Partner integration",
      "market": "uk",
      "currency": "GBP",
      "scopes": [
        "meta:read",
        "market:read",
        "bids:read",
        "bids:write"
      ],
      "expires_at": null
    }
  }
}

Envelope fields

statusstring

OK on success, ERROR on failure. It describes the call, never a resource.

messagestring

A sentence for people. It is not stable: never branch on it.

metaobject

version ("1.0"), timestamp (RFC 3339 UTC) and request_id, which is also sent as X-Request-Id.

meta.paginationobject · paged endpoints

limit, offset, total and has_more. What is counted depends on the endpoint, and /wines has no total.

dataobject | null

The payload. Null on error.

errorsarray · failures only

Absent on success. On failure, one entry per problem: code, message, and field and details where they apply.

Errors

On failure status is ERROR, data is null, and errors holds one entry per problem. There is usually exactly one; an unknown query parameter is reported for every one you sent, each with its field.

Error envelope · 403
{
  "status": "ERROR",
  "message": "This client does not have permission to perform this operation",
  "meta": {
    "version": "1.0",
    "timestamp": "2026-10-19T10:10:55Z",
    "request_id": "6b183f2425c81699f1f3d3e5"
  },
  "data": null,
  "errors": [
    {
      "code": "AUTH_INSUFFICIENT_SCOPE",
      "message": "This credential is not granted the scope this endpoint requires",
      "details": {
        "required_scope": "market:read"
      }
    }
  ]
}

Each item in errors[]

codestring

A stable, symbolic code such as AUTH_INSUFFICIENT_SCOPE. Branch on this.

fieldstring · optional

The query parameter or body field the problem is about.

messagestring

What went wrong. For TRADE_REFUSED and PRICE_CHANGED it is the reason, worded so it can be shown to a customer.

detailsobject · optional

Structured context: required_scope, store and allowed_stores, allowed_methods, retry_after.

Every error code, and what to do about each →

Pagination

Endpoints that page add meta.pagination. has_more is true while offset + limit is less than total; advance offset by limit until it is false. What a page counts differs by endpoint — see the table.

A paged response
{
  "status": "OK",
  "message": "Bids retrieved successfully",
  "meta": {
    "version": "1.0",
    "timestamp": "2026-10-19T10:10:55Z",
    "request_id": "6b183f2425c81699f1f3d3e5",
    "pagination": {
      "limit": 50,
      "offset": 0,
      "total": 1,
      "has_more": false
    }
  },
  "data": {
    "bids": [
      {
        "code": "BID-la4FCBHRAnKZ2vOaR8VF1KnHDYmFdaa_5N_mrFEFWzQ",
        "client_ref": "PO-2026-0193",
        "lwin18": "101187220240600750",
        "wine_name": "Lafite Rothschild 2024 (6x75cl)",
        "vintage": 2024,
        "pack_size": "6x75cl",
        "product_url": "https://markets.cruworldwine.com/uk/product/lafite-rothschild-2024-6x75cl",
        "price": 1650.5,
        "currency": "GBP",
        "bid_currency": "GBP",
        "quantity": 1,
        "quantity_matched": 0,
        "quantity_open": 1,
        "order_numbers": [],
        "order_urls": [],
        "status": "live",
        "renewable": false,
        "market": "uk",
        "local_only": false,
        "auto_transfer": false,
        "expires_on": "2027-01-17",
        "created_at": "2026-10-19T10:10:54Z",
        "updated_at": "2026-10-19T10:10:54Z"
      }
    ]
  }
}

Status codes

200A read, or a write that changed or cancelled something.
201 A write that created something: a bid, a listing, a purchase, an inbond address or a suggested warehouse.
202 A connected app’s write, staged and not yet done. Confirm it →
204 Discarding a staged instruction. No body and no envelope — only the X-Request-Id header.
4xx, 5xxThe error envelope, with errors[].code.

Response headers

X-Request-IdOn every response. Yours, if you sent a valid one; otherwise Cru’s.
Idempotent-Replayed: true The write, or confirmation, already ran: this is its stored outcome, replayed rather than done again.
AllowOn 405: the methods the URL accepts.
WWW-Authenticate Where connected apps are enabled: on 401, and on 403 for a missing scope when a bearer token was sent — where a connected app can get a token, and which scope it needs.
Cache-Control: no-store On every JSON response: it is never cached, and the API sets no cookies. A photo is the exception, cached for a day with an ETag.
Keep the request ID

meta.request_id is also returned as X-Request-Id. Include it when contacting Cru about a request.

Changed in September 2026

Earlier drafts of this reference described http_code, internal_error_code, api_info and page_info. They are gone: use the HTTP status, errors[].code, meta and meta.pagination instead.

Cru World Wine Developers · Partner Trading API v1
Based on the API as built on 15 October 2026.

API credentials

Enter the client key and secret issued by Cru. They are kept for this browser tab’s session — a reload keeps them, closing the tab or clearing them ends them.

Credentials are never included in copied examples, and are redacted from anything the console prints back.

Request API access

Tell us who you are and what you want to build. Cru issues a client key and secret for your organisation, scoped to what you need.

Markets you trade in (choose one or more)

We use what you send here to reply about API access, and for nothing else. Cloudflare Turnstile checks the form for bots (privacy policy).