Delivery Sales with Customer Addresses

Delivery Sales with Customer Addresses

This tutorial explains how to create delivery sales with customer billing and shipping addresses using the public REST APIs.

See also: Sales 101, Fulfillments, States and attributes.

Understanding Sale Addresses

Billing vs Shipping Addresses

For each new delivery sale, both addresses must be created for the customer first:

  • Billing address: The address associated with the payment method. This is where invoices are sent and where the payment card is registered. Referenced from the sale's payments.
  • Shipping address: The address where goods will be delivered. Referenced from the sale's fulfillment_details.

Workflow Overview

Creating a delivery sale with customer addresses requires four steps:

  1. Create a customer (2026-01 API) — Skip if using an existing customer
  2. Create a billing address for the customer (2026-01 API)
  3. Create a shipping address for the customer (2026-01 API)
  4. Create the delivery sale with address references (2026-01 API)

Step 1: Create Customer (Optional)

If you're creating a sale for a new customer, first create the customer record. Skip this step if you already have the customer's ID.

POST https://DOMAIN_PREFIX.retail.lightspeed.app/api/2026-01/customers

{
  "first_name": "Jane",
  "last_name": "Doe",
  "email": "[email protected]",
  "phone": "+1 555 123 4567",
  "company_name": "Acme Corp"
}

Response (201 Created):

{
  "data": {
    "id": "b8ca3a65-0183-11e4-fbb5-4f73f02a9d1c",
    "customer_code": "Jane-N4ZJ",
    "name": "Jane Doe",
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "[email protected]",
    "phone": "+1 555 123 4567",
    "company_name": "Acme Corp",
    "year_to_date": 0,
    "balance": 0,
    "loyalty_balance": 0,
    "do_not_email": false,
    "enable_promotional_sms": false,
    "enable_loyalty": true,
    "created_at": "2026-01-20T09:00:00Z",
    "updated_at": "2026-01-20T09:00:00Z",
    "version": 3505346597
  }
}

Save the id from the response. You'll use this customer_id when creating addresses and the sale.

Step 2: Create Billing Address

Create a billing address for the customer. The response includes an id_token that you'll need for the sale.

POST https://DOMAIN_PREFIX.retail.lightspeed.app/api/2026-01/customers/{customer_id}/addresses

{
  "address_line_1": "100 Main Street",
  "address_line_2": "Suite 200",
  "city": "New York",
  "postcode": "10001",
  "state": "New York",
  "state_code": "NY",
  "country_code": "US",
  "type": "BILLING"
}

Response (201 Created):

{
  "data": {
    "id": 123456789,
    "id_token": "hmac_billing_token_abc123",
    "customer_id": "b8ca3a65-0183-11e4-fbb5-4f73f02a9d1c",
    "address_line_1": "100 Main Street",
    "address_line_2": "Suite 200",
    "city": "New York",
    "postcode": "10001",
    "state": "New York",
    "state_code": "NY",
    "country_code": "US",
    "country_name": "United States",
    "type": "BILLING",
    "created_at": "2026-01-20T10:00:00Z",
    "updated_at": "2026-01-20T10:00:00Z"
  }
}

Save the id and id_token from the response. You'll need both values when creating the sale.

Step 3: Create Shipping Address

Create a shipping address using the same endpoint with type: "SHIPPING".

POST https://DOMAIN_PREFIX.retail.lightspeed.app/api/2026-01/customers/{customer_id}/addresses

{
  "address_line_1": "456 Oak Avenue",
  "city": "Los Angeles",
  "postcode": "90001",
  "state": "California",
  "state_code": "CA",
  "country_code": "US",
  "type": "SHIPPING"
}

Response (201 Created):

{
  "data": {
    "id": 987654321,
    "id_token": "hmac_shipping_token_xyz789",
    "customer_id": "b8ca3a65-0183-11e4-fbb5-4f73f02a9d1c",
    "address_line_1": "456 Oak Avenue",
    "city": "Los Angeles",
    "postcode": "90001",
    "state": "California",
    "state_code": "CA",
    "country_code": "US",
    "country_name": "United States",
    "type": "SHIPPING",
    "created_at": "2026-01-20T10:05:00Z",
    "updated_at": "2026-01-20T10:05:00Z"
  }
}

Save the id and id_token from the response. You'll need both values when creating the sale.

Step 4: Create Delivery Sale with Addresses

Create a delivery sale that references both addresses using the IDs and tokens from the previous steps.

POST https://DOMAIN_PREFIX.retail.lightspeed.app/api/2026-01/sales

{
  "source": {
    "register_id": "b1e198a9-f019-11e3-a0f5-b8ca3a64f8f4",
    "author_id": "b1ed6158-f019-11e3-a0f5-b8ca3a64f8f4"
  },
  "customer_id": "b8ca3a65-0183-11e4-fbb5-4f73f02a9d1c",
  "state": "pending",
  "attributes": ["delivery"],
  "line_items": [
    {
      "product": { "id": "b1d87b58-f019-11e3-a0f5-b8ca3a64f8f4" },
      "quantity": 1,
      "pricing": { "price": "100" },
      "tax": { "id": "b1d192bc-f019-11e3-a0f5-b8ca3a64f8f4", "amount": "15" }
    }
  ],
  "payments": [
    {
      "type": { "config_id": "b1e1d70e-f019-11e3-a0f5-b8ca3a64f8f4" },
      "amount": 115,
      "billing_address_id": "123456789",
      "billing_address_token": "hmac_billing_token_abc123"
    }
  ],
  "fulfillment_details": [
    {
      "type": "DISPATCH",
      "shipping_address_id": "987654321",
      "shipping_address_token": "hmac_shipping_token_xyz789"
    }
  ]
}
FieldLocationDescription
source.author_idsourceThe id of the user (cashier) creating the sale, from /api/2026-01/users
type.config_idpaymentsThe payment type id from /api/2026-01/payment_types
billing_address_idpaymentsThe id from the billing address response (as a string)
billing_address_tokenpaymentsThe id_token from the billing address response
shipping_address_idfulfillment_detailsThe id from the shipping address response (as a string)
shipping_address_tokenfulfillment_detailsThe id_token from the shipping address response

Note: This sale is created with the date-versioned POST /api/2026-01/sales endpoint. The legacy POST /api/register_sales (v0.9) endpoint is deprecated; see the Sales migration guide for the full field mapping. There is no status field in the date-versioned API; use state together with attributes. See States and attributes.

Important Notes

  • The id_token is only returned when creating an address. It is not returned on subsequent GET requests.
  • The token is validated server-side to prevent address ID tampering.
  • The address must belong to the same customer specified in the sale.
  • Address IDs must be passed as strings in the sale request payload.

Retrieving Customer Addresses (Optional)

If you need to retrieve existing addresses for a customer (for example, to display them in a UI or verify address details), you can use the following endpoints.

List All Addresses

Retrieve all addresses associated with a customer.

GET https://DOMAIN_PREFIX.retail.lightspeed.app/api/2026-01/customers/{customer_id}/addresses

Response (200 OK):

{
  "data": [
    {
      "id": 123456789,
      "customer_id": "b8ca3a65-0183-11e4-fbb5-4f73f02a9d1c",
      "address_line_1": "100 Main Street",
      "address_line_2": "Suite 200",
      "city": "New York",
      "postcode": "10001",
      "state": "New York",
      "state_code": "NY",
      "country_code": "US",
      "country_name": "United States",
      "type": "BILLING",
      "created_at": "2026-01-20T10:00:00Z",
      "updated_at": "2026-01-20T10:00:00Z"
    },
    {
      "id": 987654321,
      "customer_id": "b8ca3a65-0183-11e4-fbb5-4f73f02a9d1c",
      "address_line_1": "456 Oak Avenue",
      "city": "Los Angeles",
      "postcode": "90001",
      "state": "California",
      "state_code": "CA",
      "country_code": "US",
      "country_name": "United States",
      "type": "SHIPPING",
      "created_at": "2026-01-20T10:05:00Z",
      "updated_at": "2026-01-20T10:05:00Z"
    }
  ]
}

Get a Single Address

Retrieve a specific address by its ID.

GET https://DOMAIN_PREFIX.retail.lightspeed.app/api/2026-01/customers/{customer_id}/addresses/{address_id}

Response (200 OK):

{
  "data": {
    "id": 123456789,
    "customer_id": "b8ca3a65-0183-11e4-fbb5-4f73f02a9d1c",
    "address_line_1": "100 Main Street",
    "address_line_2": "Suite 200",
    "city": "New York",
    "postcode": "10001",
    "state": "New York",
    "state_code": "NY",
    "country_code": "US",
    "country_name": "United States",
    "type": "BILLING",
    "created_at": "2026-01-20T10:00:00Z",
    "updated_at": "2026-01-20T10:00:00Z"
  }
}

Complete Example with cURL

This section provides a complete working example using cURL commands. Replace DOMAIN_PREFIX with your store's domain prefix and ACCESS_TOKEN with your personal access token.

Prerequisites: Gather Required IDs

Before creating a sale, you need to collect the required IDs from your store.

Get a Register ID:

curl -s -H "Authorization: Bearer ACCESS_TOKEN" \
  "https://DOMAIN_PREFIX.retail.lightspeed.app/api/{version}/registers" | jq '.data[0]'

Response:

{
  "id": "0698ab21-5fc8-11f0-f6d9-88a9c20f3f9c",
  "name": "Front Counter"
}

Get a User ID:

curl -s -H "Authorization: Bearer ACCESS_TOKEN" \
  "https://DOMAIN_PREFIX.retail.lightspeed.app/api/2026-01/users" | jq '.data[0]'

Response:

{
  "id": "020b2c2a-4661-11f0-e200-88a31fbafec6",
  "display_name": "John Smith"
}

Get a Product ID:

curl -s -H "Authorization: Bearer ACCESS_TOKEN" \
  "https://DOMAIN_PREFIX.retail.lightspeed.app/api/2026-01/products?page_size=5" | jq '.data[] | {id, name}'

Response:

{
  "id": "083d58eb-0d39-4fa2-9df8-4c3c73abf008",
  "name": "iPhone 16"
}
{
  "id": "ffe9c2be-f45d-461d-b6a7-ba7d61a001dd",
  "name": "Samsung Galaxy S24"
}

Get a Tax ID:

curl -s -H "Authorization: Bearer ACCESS_TOKEN" \
  "https://DOMAIN_PREFIX.retail.lightspeed.app/api/taxes" | jq '.taxes[0]'

Response:

{
  "id": "0698ab21-5f09-11f0-f6d9-88a3200cbd80",
  "name": "No Tax",
  "rate": 0,
  "default": false,
  "active": true,
  "rates": [
    {
      "id": "200d91fb-88a3-11f0-96d9-0698ab215f09",
      "rate": 0,
      "name": "No Tax"
    }
  ]
}

Get a Payment Type ID:

curl -s -H "Authorization: Bearer ACCESS_TOKEN" \
  "https://DOMAIN_PREFIX.retail.lightspeed.app/api/payment_types" | jq '.payment_types[0]'

Response:

{
  "id": "7692490c-97f6-4528-8260-60a65a606891",
  "name": "Cash",
  "type_id": 1,
  "disabled": false,
  "deleted_at": null,
  "internal": false
}

Use this id as payments[].type.config_id in the sale request.

Step 1: Create Customer

curl -X POST \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  "https://DOMAIN_PREFIX.retail.lightspeed.app/api/2026-01/customers" \
  -d '{
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "[email protected]",
    "phone": "+1 555 123 4567"
  }'

Response:

{
  "data": {
    "id": "0698ab21-5fc8-11f0-f6d9-fdc9572dd1c5",
    "customer_code": "Jane-YPK5",
    "name": "Jane Doe",
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "[email protected]",
    "phone": "+1 555 123 4567",
    "year_to_date": 0,
    "balance": 0,
    "loyalty_balance": 0,
    "do_not_email": false,
    "enable_promotional_sms": false,
    "enable_loyalty": true,
    "created_at": "2026-01-30T10:49:24+00:00",
    "updated_at": "2026-01-30T10:49:24+00:00"
  }
}

Step 2: Create Billing Address

curl -X POST \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  "https://DOMAIN_PREFIX.retail.lightspeed.app/api/2026-01/customers/0698ab21-5fc8-11f0-f6d9-fdc9572dd1c5/addresses" \
  -d '{
    "address_line_1": "200 Broadway",
    "city": "New York",
    "postcode": "10001",
    "state": "New York",
    "state_code": "NY",
    "country_code": "US",
    "type": "BILLING"
  }'

Response:

{
  "data": {
    "id": 2017188673192951808,
    "id_token": "XCLB5Bzuh7_8g3t53XrdosRrUoiZ9cRFRAK1oUQ5MXY",
    "customer_id": "0698ab21-5fc8-11f0-f6d9-fdc9572dd1c5",
    "address_line_1": "200 Broadway",
    "address_line_2": "",
    "city": "New York",
    "postcode": "10001",
    "state": "New York",
    "state_code": "NY",
    "country_code": "US",
    "country_name": "United States",
    "type": "BILLING",
    "created_at": "2026-01-30T10:50:17Z",
    "updated_at": "2026-01-30T10:50:17Z"
  }
}

Save the id and id_token for the sale request.

Step 3: Create Shipping Address

curl -X POST \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  "https://DOMAIN_PREFIX.retail.lightspeed.app/api/2026-01/customers/0698ab21-5fc8-11f0-f6d9-fdc9572dd1c5/addresses" \
  -d '{
    "address_line_1": "789 Sunset Blvd",
    "city": "Los Angeles",
    "postcode": "90028",
    "state": "California",
    "state_code": "CA",
    "country_code": "US",
    "type": "SHIPPING"
  }'

Response:

{
  "data": {
    "id": 2017188697067581440,
    "id_token": "aPAfSNxKcd2PVbt0PSTNhoRf7gjsOns18bVTV-LTSE4",
    "customer_id": "0698ab21-5fc8-11f0-f6d9-fdc9572dd1c5",
    "address_line_1": "789 Sunset Blvd",
    "address_line_2": "",
    "city": "Los Angeles",
    "postcode": "90028",
    "state": "California",
    "state_code": "CA",
    "country_code": "US",
    "country_name": "United States",
    "type": "SHIPPING",
    "created_at": "2026-01-30T10:50:23Z",
    "updated_at": "2026-01-30T10:50:23Z"
  }
}

Save the id and id_token for the sale request.

Step 4: Create Delivery Sale

Using all the IDs collected, create the delivery sale:

curl -X POST \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  "https://DOMAIN_PREFIX.retail.lightspeed.app/api/2026-01/sales" \
  -d '{
    "source": {
      "register_id": "0698ab21-5fc8-11f0-f6d9-88a9c20f3f9c",
      "author_id": "020b2c2a-4661-11f0-e200-88a31fbafec6"
    },
    "customer_id": "0698ab21-5fc8-11f0-f6d9-fdc9572dd1c5",
    "state": "pending",
    "attributes": ["delivery"],
    "line_items": [
      {
        "product": { "id": "083d58eb-0d39-4fa2-9df8-4c3c73abf008" },
        "quantity": 1,
        "pricing": { "price": "100" },
        "tax": { "id": "0698ab21-5f09-11f0-f6d9-88a3200cbd80", "amount": "0" }
      }
    ],
    "payments": [
      {
        "type": { "config_id": "7692490c-97f6-4528-8260-60a65a606891" },
        "amount": 100,
        "billing_address_id": "2017188673192951808",
        "billing_address_token": "XCLB5Bzuh7_8g3t53XrdosRrUoiZ9cRFRAK1oUQ5MXY"
      }
    ],
    "fulfillment_details": [
      {
        "type": "DISPATCH",
        "shipping_address_id": "2017188697067581440",
        "shipping_address_token": "aPAfSNxKcd2PVbt0PSTNhoRf7gjsOns18bVTV-LTSE4"
      }
    ]
  }'

Response:

{
  "data": {
    "id": "c73c3df7-ab69-4a34-a636-f399f7c789fc",
    "date": "2026-01-30T10:51:19+00:00",
    "invoice_number": "16",
    "short_code": "rbjapd",
    "state": "pending",
    "note": null,
    "source": {
      "id": null,
      "type": "USER",
      "register_id": "0698ab21-5fc8-11f0-f6d9-88a9c20f3f9c",
      "outlet_id": "0698ab21-5fc8-11f0-f6d9-88a9c2064dff",
      "author": { "id": "020b2c2a-4661-11f0-e200-88a31fbafec6" }
    },
    "customer_id": "0698ab21-5fc8-11f0-f6d9-fdc9572dd1c5",
    "attributes": ["delivery"],
    "payments": [
      {
        "id": "0698ab21-5fc8-11f0-f6d9-fdc99bd3eb0b",
        "amount": 100,
        "type": { "id": "1", "name": "Cash", "config_id": "7692490c-97f6-4528-8260-60a65a606891" },
        "billing_address_id": "2017188673192951808"
      }
    ],
    "line_items": [
      {
        "id": "801f14c3-1129-4b0b-aa49-ea06ae1fbf9f",
        "quantity": 1,
        "product": { "id": "083d58eb-0d39-4fa2-9df8-4c3c73abf008" },
        "pricing": { "price": 100, "total": 100 },
        "tax": { "id": "0698ab21-5f09-11f0-f6d9-88a3200cbd80", "amount": 0, "total": 0 },
        "status": "CONFIRMED"
      }
    ],
    "totals": { "price": 100, "tax": 0, "price_incl_tax": 100, "loyalty": 0, "surcharge": 0 },
    "receipt_number": "16",
    "created_at": "2026-01-30T10:51:19+00:00",
    "updated_at": "2026-01-30T10:51:19+00:00"
  }
}

The sale is created with state: "pending" and the delivery attribute, indicating it's ready for fulfillment. The shipping address passed in fulfillment_details is linked to the sale's DISPATCH fulfillment. It is not echoed on the sale object itself, but you can retrieve it via GET /api/2026-01/sales/{sale_id}/fulfillments (shown below).

Verify Customer Addresses

After creating addresses, you can verify them using the GET endpoints:

List all addresses:

curl -s -H "Authorization: Bearer ACCESS_TOKEN" \
  "https://DOMAIN_PREFIX.retail.lightspeed.app/api/2026-01/customers/0698ab21-5fc8-11f0-f6d9-fdc9572dd1c5/addresses"

Response:

{
  "data": [
    {
      "id": 2017188697067581440,
      "customer_id": "0698ab21-5fc8-11f0-f6d9-fdc9572dd1c5",
      "address_line_1": "789 Sunset Blvd",
      "city": "Los Angeles",
      "postcode": "90028",
      "state": "California",
      "state_code": "CA",
      "country_code": "US",
      "country_name": "United States",
      "type": "SHIPPING",
      "created_at": "2026-01-30T10:50:23Z",
      "updated_at": "2026-01-30T10:50:23Z"
    },
    {
      "id": 2017188673192951808,
      "customer_id": "0698ab21-5fc8-11f0-f6d9-fdc9572dd1c5",
      "address_line_1": "200 Broadway",
      "city": "New York",
      "postcode": "10001",
      "state": "New York",
      "state_code": "NY",
      "country_code": "US",
      "country_name": "United States",
      "type": "BILLING",
      "created_at": "2026-01-30T10:50:17Z",
      "updated_at": "2026-01-30T10:50:17Z"
    }
  ]
}

Note that id_token is not included in GET responses—it is only returned when creating an address.

Fulfill the Sale

After processing the delivery, mark the sale as shipped using the fulfillment endpoint. For comprehensive fulfillment documentation including partial fulfillments, picking, and packing, see the Fulfillments guide.

Fulfill the sale (mark as SHIPPED):

curl -X POST \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  "https://DOMAIN_PREFIX.retail.lightspeed.app/api/2026-01/sales/c73c3df7-ab69-4a34-a636-f399f7c789fc/fulfill" \
  -d '{
    "fulfillment_type": "SHIPPED"
  }'

Response:

{
  "data": {
    "id": "2017192376529309696",
    "sale_id": "c73c3df7-ab69-4a34-a636-f399f7c789fc",
    "outlet_id": "0698ab21-5fc8-11f0-f6d9-88a9c2064dff",
    "user_id": "020b2c2a-4661-11f0-e200-88a31fbafec6",
    "status": "SHIPPED",
    "created_at": "2026-01-30T11:05:14Z",
    "line_items": [
      {
        "id": "2017192376537698304",
        "product_id": "083d58eb-0d39-4fa2-9df8-4c3c73abf008",
        "quantity": "1"
      }
    ]
  }
}

The fulfillment_type can be either SHIPPED (for delivery) or PICKED_UP (for customer pickup).

Verify fulfillments:

curl -s -H "Authorization: Bearer ACCESS_TOKEN" \
  "https://DOMAIN_PREFIX.retail.lightspeed.app/api/2026-01/sales/c73c3df7-ab69-4a34-a636-f399f7c789fc/fulfillments"

Response:

{
  "data": [
    {
      "id": "2017192376529309696",
      "sale_id": "c73c3df7-ab69-4a34-a636-f399f7c789fc",
      "outlet_id": "0698ab21-5fc8-11f0-f6d9-88a9c2064dff",
      "user_id": "020b2c2a-4661-11f0-e200-88a31fbafec6",
      "status": "SHIPPED",
      "created_at": "2026-01-30T11:05:14Z",
      "line_items": [
        {
          "id": "2017192376537698304",
          "product_id": "083d58eb-0d39-4fa2-9df8-4c3c73abf008",
          "quantity": "1"
        }
      ],
      "shipping_address_id": "2017188697067581440"
    }
  ]
}

Note that the shipping_address_id appears in the fulfillment response, linking the fulfillment to the shipping address.

Sale status after fulfillment:

After fulfillment, the sale's state changes. Verify by fetching the sale:

curl -s -H "Authorization: Bearer ACCESS_TOKEN" \
  "https://DOMAIN_PREFIX.retail.lightspeed.app/api/2026-01/sales/c73c3df7-ab69-4a34-a636-f399f7c789fc"

Response (key fields):

{
  "data": {
    "id": "c73c3df7-ab69-4a34-a636-f399f7c789fc",
    "state": "closed",
    "attributes": ["delivery"],
    "customer_id": "0698ab21-5fc8-11f0-f6d9-fdc9572dd1c5",
    "totals": { "price": 100, "tax": 0, "price_incl_tax": 100 },
    "payments": [
      {
        "amount": 100,
        "type": { "id": "1", "name": "Cash" },
        "billing_address_id": "2017188673192951808"
      }
    ]
  }
}

After fulfillment the sale's state transitions from pending to closed; the delivery attribute is unchanged. The date-versioned API has no status field; the equivalent legacy statuses were AWAITING_DISPATCH (pending + delivery) and DISPATCHED_CLOSED (closed + delivery). See States and attributes.