Place a click-and-collect order

Updated September 9, 20264 min read

The COLLECTION variant of order ingestion. Instead of a delivery address, the order carries a customer collection address, the store or pickup point where the customer will collect the parcel. Carriyo's allocation engine uses that address as the reference point and allocates the fulfillment order to a fulfillment location whose coverage includes it. When the collection point is itself a fulfillment location, as in this recipe, Carriyo allocates the order to the store and no shipment is needed.

This is a single-endpoint recipe. The downstream Customer collection entity tracks the in-store handover and OTP verification. The Fulfillment App creates it at fulfillment time, not at order create.

Scenario

A customer in London is buying a single pair of wireless earbuds for £79.99 and wants to collect them from the Acme Marylebone store the same evening. You send the order to Carriyo with delivery_method set to COLLECTION and a customer_collection_address pointing to the store. The store is set up as both a collection point and a fulfillment location, so Carriyo produces a single fulfillment order allocated to it.

Prerequisites

  • API credentials: see Getting started for the one-time setup.
  • A collection point configured for the merchant: a location with the customer collection flag. For local collection, as here, the same location is also a fulfillment location whose coverage includes its own address.
  • Automatic allocation enabled in the tenant's order management settings (enable_automatic_order_allocation). It is off until those settings are created. Without it, the order is created in open and its fulfillment order carries a fulfillment_location_missing error until you allocate it.

Create the order

curl -X POST 'https://api.carriyo.com/orders' \
  -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  -H 'tenant-id: YOUR_TENANT_ID' \
  -H 'x-api-key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "merchant": "ACME",
    "partner_order_reference": "YOUR_ORDER_REF",
    "delivery_method": "COLLECTION",
    "order_date": "2026-05-10T13:42:00.000Z",
    "sales_channel": "web",
    "payment": {
      "currency": "GBP",
      "order_total": 79.99
    },
    "customer": {
      "contact_name": "Oliver Bennett",
      "contact_email": "oliver.bennett@example.co.uk",
      "contact_phone": "+44 7700 900123"
    },
    "billing_address": {
      "contact_name": "Oliver Bennett",
      "contact_email": "oliver.bennett@example.co.uk",
      "contact_phone": "+44 7700 900123",
      "address1": "45 Gloucester Place",
      "address2": "Marylebone",
      "city": "London",
      "country": "GB",
      "postcode": "W1U 8HU"
    },
    "customer_collection_address": {
      "partner_location_code": "STORE-UK-LON-MARYLEBONE"
    },
    "line_items": [
      {
        "sku": "SKU-EAR-003",
        "description": "Wireless Earbuds",
        "quantity": 1,
        "unit_price": 79.99
      }
    ]
  }'

The response is the created order, abridged here, with a single fulfillment order allocated to the store:

{
  "tenant": "your-tenant",
  "order_id": "OMP1B9C8D3EAYZ",
  "merchant": "ACME",
  "partner_order_reference": "YOUR_ORDER_REF",
  "order_date": "2026-05-10T13:42:00.000Z",
  "sales_channel": "web",
  "status": "allocated",
  "payment": {
    "currency": "GBP",
    "order_total": 79.99
  },
  "customer": {
    "contact_name": "Oliver Bennett",
    "contact_email": "oliver.bennett@example.co.uk",
    "contact_phone": "+44 7700 900123"
  },
  "line_items": [
    {
      "id": "Q3R7T",
      "sku": "SKU-EAR-003",
      "product_ref": "SKU-EAR-003",
      "product_id": "PDTHDMCY4YN4Z7L0Q",
      "quantity": 1,
      "unit_price": 79.99
    }
  ],
  "fulfillment_orders": [
    {
      "fulfillment_order_id": "FOMP1B9C8VKGNHY",
      "fulfillment_location": {
        "partner_location_id": "ACCOUNT_a1b2c3d4-store-marylebone",
        "partner_location_code": "STORE-UK-LON-MARYLEBONE",
        "partner_location_name": "Acme Marylebone Store"
      },
      "status": "allocated",
      "delivery_method": "COLLECTION",
      "customer_collection_address": {
        "partner_location_id": "ACCOUNT_a1b2c3d4-store-marylebone",
        "partner_location_code": "STORE-UK-LON-MARYLEBONE",
        "partner_location_name": "Acme Marylebone Store",
        "contact_name": "Acme Marylebone Store",
        "address1": "120 Marylebone High Street",
        "city": "London",
        "postcode": "W1U 4SD",
        "country": "GB"
      },
      "line_items": [
        { "id": "Q3R7T", "quantity": 1, "status": "allocated" }
      ],
      "creation_date": "2026-05-10T13:42:01.205Z"
    }
  ],
  "creation_date": "2026-05-10T13:42:01.000Z"
}

What just happened

  • The order was accepted and immediately moved to allocated.
  • Carriyo resolved the line item to the catalog product (product_id is Carriyo's own id, product_ref your reference). It allocated the item to the Marylebone store because the store is a fulfillment location whose coverage includes the collection address. With inventory management enabled the store also needs stock for the SKU.
  • The FO carries the customer_collection_address you submitted, enriched with the store's full address and partner_location_id; downstream events (notifications, OTP send) use it.
  • No shipment is created in this scenario: the fulfillment location is the collection point, so the parcel is already there. This is local collection.

For the remote collection variant, where stock is held at a warehouse and a shipment carries it to the collection point, see Fulfillment orders → what a fulfillment order produces.

After this call

The customer collection entity (which tracks the in-store handover, OTP verification, and expiry) isn't created synchronously. The Fulfillment App creates it when packing completes, and it appears in open status. From that point the lifecycle is:

  1. The collection is created in open when the FO is fulfilled.
  2. Staff confirms the parcel is at the collection counter and the collection moves to ready_to_collect.
  3. Carriyo sends the OTP to the customer's email.
  4. The customer presents the OTP; staff verifies; the collection moves to collected.
  5. The line item closes, the FO closes, and the order auto-closes.

The full lifecycle, OTP rules, and auto-expiry behavior are in Customer collection.

Pitfalls

  • customer_collection_address should be provided for COLLECTION orders. If omitted, the order is still created but no customer collection entity is created downstream. Sending delivery_address on a COLLECTION order is ignored.
  • Allocation follows fulfillment coverage, not the collection flag. If the collection point is not a fulfillment location, the engine allocates to another location whose zone or radius covers the collection address, and that location ships the parcel to the store (remote collection).
  • Identify products by sku or product_ref. product_id is Carriyo's own product id; the response fills it in from the catalog. Your SKU in product_id matches nothing.
  • Pass the location by partner_location_code (or partner_location_id) rather than by full address. Carriyo resolves the code to a location, enriches the response with the full address, and populates partner_location_id for you.
  • partner_order_reference is the dedupe key. Re-sending the same value returns an error rather than upserting.
  • The collection point must be a collection-capable location. Each location carries a collection_location flag (set in the Dashboard); only locations with this flag enabled can be used as a customer collection point.