# Locations and addresses

Manage customer locations, customer addresses, and supplier ship-from locations.

Canonical HTML: https://developer.shelfcycle.com/guides/location-addresses

Manage customer locations, customer addresses, and supplier ship-from locations only after selecting the exact visible parent.

Location and address endpoints are parent scoped. They are for confirmed customer and supplier workflows, not broad address export, location sync, or private profile enrichment.

## Endpoint map

| Endpoint | Scope | Use it for |
| --- | --- | --- |
| `GET /customers/{id}/locations` | `customers:read` or `search:read` | List active customer locations under one customer. |
| `POST /customers/{id}/locations` | `customers:write` | Create a customer location with its initial billing or shipping address. |
| `GET /customers/{id}/locations/{locationId}` | `customers:read` or `search:read` | Read one customer location under its parent customer. |
| `PATCH /customers/{id}/locations/{locationId}` | `customers:write` | Update safe location fields with optimistic concurrency. |
| `GET /customers/{id}/locations/{locationId}/addresses` | `customers:read` or `search:read` | List active addresses under one customer location. |
| `POST /customers/{id}/locations/{locationId}/addresses` | `customers:write` | Create a billing or shipping address under one location. |
| `PATCH /customers/{id}/locations/{locationId}/addresses/{addressId}` | `customers:write` | Update safe address fields. The address must remain billing or shipping eligible. |
| `GET /suppliers/{id}/ship-from-locations` | `suppliers:read` or `search:read` | List active ship-from locations under one supplier. |
| `POST /suppliers/{id}/ship-from-locations` | `suppliers:write` | Create a supplier ship-from location. |
| `PATCH /suppliers/{id}/ship-from-locations/{locationId}` | `suppliers:write` | Update safe ship-from fields with optimistic concurrency. |

POST requests require `Idempotency-Key`. PATCH requests require `If-Match` with the latest `updatedAt` from the detail response. Add `dryRun=true` to the same POST or PATCH endpoint when your workflow needs a readiness check before execution.

## Create a customer location

Customer location create includes the initial address. That address must be billing, shipping, or both.

```bash
curl "$SHELFCYCLE_API_BASE_URL/customers/customer-id/locations?dryRun=true" \
  -X POST \
  -H "Authorization: Bearer $SHELFCYCLE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: crm:site-742:customer-id:create-location" \
  -d '{
    "name": "Northstar Rail Dock",
    "email": "rail@northstar.example",
    "phone": "555-4000",
    "address": {
      "name": "Northstar Rail Dock",
      "street1": "700 Rail Spur",
      "city": "Tampa",
      "state": "FL",
      "zip": "33603",
      "country": "US",
      "isShipping": true
    }
  }'
```

```json
{
  "data": {
    "type": "write_readiness",
    "operation": "customer_locations.create",
    "status": "ready",
    "wouldWrite": false,
    "checks": [
      { "code": "scope_authorized", "status": "passed" },
      { "code": "target_available", "status": "passed" },
      { "code": "idempotency_available", "status": "passed" },
      { "code": "duplicate_check_clear", "status": "passed" }
    ],
    "duplicateCandidates": [],
    "verification": {
      "available": true,
      "path": null,
      "pathTemplate": "/api/v1/customers/{customerId}/locations/{locationId}",
      "fallback": "detail_get"
    },
    "requestId": "request-id"
  }
}
```

Execute the same request without `dryRun=true` when your policy allows it. If the customer has no active primary location, ShelfCycle may make the first created location primary. Clients cannot request primary switching.

## Add a customer address

Create additional billing or shipping addresses under the selected location.

```bash
curl "$SHELFCYCLE_API_BASE_URL/customers/customer-id/locations/location-id/addresses" \
  -X POST \
  -H "Authorization: Bearer $SHELFCYCLE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: crm:site-742:customer-id:location-id:create-bill-to" \
  -d '{
    "name": "Northstar Billing",
    "externalId": "NS-BILL-01",
    "street1": "100 Market Street",
    "city": "Chicago",
    "state": "IL",
    "zip": "60601",
    "country": "US",
    "isBilling": true,
    "defaultOrderShippingRemarks": "Use customer PO number on paperwork."
  }'
```

If both `isBilling` and `isShipping` are false, the API blocks the write with `address_role_required`. Duplicate candidates are advisory and bounded; when the key cannot read the duplicate detail, candidate fields can be redacted.

## Update child records

Use the current `updatedAt` value as `If-Match`.

```bash
curl "$SHELFCYCLE_API_BASE_URL/customers/customer-id/locations/location-id/addresses/address-id" \
  -X PATCH \
  -H "Authorization: Bearer $SHELFCYCLE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "If-Match: 2026-06-29T16:00:00.000Z" \
  -d '{
    "phone": "555-4100",
    "defaultOrderShippingRemarks": "Use east dock for pickups."
  }'
```

Customer location updates support `name`, `email`, `website`, and `phone`. Sending `primary` or `makePrimary` to change primary state is blocked. Customer address updates support safe address, contact, billing/shipping role, and shipping-remark fields, but the address must remain billing or shipping eligible.

## Supplier ship-from locations

Supplier child writes are ship-from only. They do not mutate the supplier root address or private supplier settings.

```bash
curl "$SHELFCYCLE_API_BASE_URL/suppliers/supplier-id/ship-from-locations?dryRun=true" \
  -X POST \
  -H "Authorization: Bearer $SHELFCYCLE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: supplier-portal:warehouse-22:supplier-id:create-ship-from" \
  -d '{
    "name": "Acme Houston Warehouse",
    "externalId": "ACME-HOU",
    "street1": "20 Industrial Way",
    "city": "Houston",
    "state": "TX",
    "zip": "77002",
    "country": "US",
    "phone": "555-2999",
    "defaultSupplierShippingInstructions": "Call before pickup."
  }'
```

## Lists and cursors

Child lists accept `limit` and `cursor`. Cursors are bound to the resource and parent chain, so a cursor from one customer or address list cannot be replayed against another parent.

Customer location list rows include an address preview. If `addressesTruncated` is true, call `GET /customers/{id}/locations/{locationId}/addresses` for the complete active address list under that location.

> **Guardrail**: Do not use child endpoints for broad sync, primary switching, archive/unarchive, supplier root address mutation, tax, credit, payment terms, ledger, workflow defaults, or hidden internal fields.
