Place a home delivery order

Updated September 9, 20263 min read

The foundational order-ingestion flow. Your storefront posts a customer order to Carriyo with a single delivery method and address. Carriyo's allocation engine picks the fulfillment location and returns the resulting order with its fulfillment orders attached. No checkout-side delivery option, no collection method: just standard home delivery.

Scenario

A customer in London is buying two accessories, a pair of wireless earbuds and a USB-C cable, total £92.98. You send the order to Carriyo with a delivery_method of DELIVERY and a single shipping address. Carriyo's allocation engine has both items in stock at the London warehouse and produces a single fulfillment order.

Prerequisites

  • API credentials: see Getting started for the one-time setup.
  • At least one fulfillment location configured for the merchant whose coverage (fulfillment_zones or fulfillment_radius) includes the delivery address. With inventory management enabled, it also needs stock for the SKUs being ordered.
  • Automatic allocation enabled in the tenant's order management settings (enable_automatic_order_allocation). It is off until those settings are created. With it off, the order is created in open, its fulfillment order carries a fulfillment_location_missing error, and it waits for manual allocation. See Allocation engine.

Create the order

Submit the order to Carriyo. No delivery_option is set: Carriyo allocates on location coverage (and stock, when inventory management is on) without a customer-chosen delivery option. Identify each product by sku or product_ref; Carriyo fills in its own product_id on the response.

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": "DELIVERY",
    "order_date": "2026-05-10T09:15:22.000Z",
    "sales_channel": "web",
    "payment": {
      "currency": "GBP",
      "order_total": 92.98
    },
    "customer": {
      "contact_name": "Oliver Bennett",
      "contact_email": "oliver.bennett@example.co.uk",
      "contact_phone": "+44 7700 900123"
    },
    "delivery_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"
    },
    "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"
    },
    "line_items": [
      {
        "sku": "SKU-EAR-003",
        "description": "Wireless Earbuds",
        "quantity": 1,
        "unit_price": 79.99
      },
      {
        "sku": "SKU-CBL-004",
        "description": "USB-C Cable",
        "quantity": 1,
        "unit_price": 12.99
      }
    ]
  }'

The response, abridged here, is the created order with allocation already applied. The London warehouse covers the address and holds both items, so Carriyo produces a single fulfillment order:

{
  "tenant": "your-tenant",
  "order_id": "OMP1A8B7C2D9KZ",
  "merchant": "ACME",
  "partner_order_reference": "YOUR_ORDER_REF",
  "order_date": "2026-05-10T09:15:22.000Z",
  "sales_channel": "web",
  "status": "allocated",
  "payment": {
    "currency": "GBP",
    "order_total": 92.98
  },
  "customer": {
    "contact_name": "Oliver Bennett",
    "contact_email": "oliver.bennett@example.co.uk",
    "contact_phone": "+44 7700 900123"
  },
  "line_items": [
    { "id": "K7M3X", "sku": "SKU-EAR-003", "product_ref": "SKU-EAR-003", "product_id": "PDTHDMCY4YN4Z7L0Q", "quantity": 1, "unit_price": 79.99 },
    { "id": "P9N4R", "sku": "SKU-CBL-004", "product_ref": "SKU-CBL-004", "product_id": "PDTHDMCY4YN4Z7L0R", "quantity": 1, "unit_price": 12.99 }
  ],
  "fulfillment_orders": [
    {
      "fulfillment_order_id": "FOMP1A8B7VJAMFZ",
      "fulfillment_location": {
        "partner_location_id": "ACCOUNT_b5c325c0-edeb-4559-803c-d1b0ca3ec9c2",
        "partner_location_code": "LDN-WH-01",
        "partner_location_name": "London Warehouse"
      },
      "status": "allocated",
      "delivery_method": "DELIVERY",
      "delivery_address": {
        "contact_name": "Oliver Bennett",
        "address1": "45 Gloucester Place",
        "address2": "Marylebone",
        "city": "London",
        "postcode": "W1U 8HU",
        "country": "GB"
      },
      "line_items": [
        { "id": "K7M3X", "quantity": 1, "status": "allocated" },
        { "id": "P9N4R", "quantity": 1, "status": "allocated" }
      ],
      "creation_date": "2026-05-10T09:15:23.105Z"
    }
  ],
  "creation_date": "2026-05-10T09:15:23.000Z"
}

What just happened

  • The order was accepted and immediately moved to allocated.
  • The allocation engine found one fulfillment location covering the address that can supply both line items (the London warehouse). It produced a single fulfillment order, the simplest possible outcome.
  • Both line items are in allocated status under the FO and will move through the pick and pack statuses to fulfilled as the warehouse picks, packs, and ships them.
  • Fulfilling the FO creates and books its shipment.

Variation: items split across locations

The request you send is identical, but the response can change if no single location has stock for every line item. In that case the allocation engine produces multiple fulfillment orders, one per chosen location. For example, say the earbuds are in stock at the London warehouse and the cable only at the Manchester warehouse. The fulfillment_orders array would then contain two entries:

"fulfillment_orders": [
  {
    "fulfillment_order_id": "FOMP1A8B7VJAMFZ",
    "fulfillment_location": {
      "partner_location_id": "ACCOUNT_b5c325c0-edeb-4559-803c-d1b0ca3ec9c2",
      "partner_location_code": "LDN-WH-01",
      "partner_location_name": "London Warehouse"
    },
    "status": "allocated",
    "delivery_method": "DELIVERY",
    "delivery_address": { /* same shipping address */ },
    "line_items": [{ "id": "K7M3X", "quantity": 1, "status": "allocated" }]
  },
  {
    "fulfillment_order_id": "FOMP1A8B7XJ9YZ2",
    "fulfillment_location": {
      "partner_location_id": "ACCOUNT_64c72d0e-19f5-4ec1-b572-d51c97688606",
      "partner_location_code": "MAN-WH-01",
      "partner_location_name": "Manchester Warehouse"
    },
    "status": "allocated",
    "delivery_method": "DELIVERY",
    "delivery_address": { /* same shipping address */ },
    "line_items": [{ "id": "P9N4R", "quantity": 1, "status": "allocated" }]
  }
]

Both FOs ship to the same address (the customer didn't ask for two destinations) but originate from different warehouses, so each will produce its own shipment. The order itself remains a single record with status: allocated.

How splits are decided is covered on the Allocation engine page; Splits and merges explains the downstream consequences.

Pitfalls

  • partner_order_reference is the dedupe key. Re-sending the same value returns an error rather than upserting. Generate it deterministically per customer order and reuse it on retries.
  • Payment is amount-based, not mode-based. Orders don't carry a payment_mode enum. Express cash-on-delivery by setting payment.payment_on_delivery to the amount due at delivery (omit or leave at zero for fully prepaid orders).
  • Allocation can land in partially_allocated. If some items can be allocated and others can't (e.g. stock-out), the Order status is partially_allocated rather than allocated. Listen for the order webhook and send the unallocated lines to ops.