> ## Documentation Index
> Fetch the complete documentation index at: https://docs.chataigne.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Location & Organization Management

> How organizations, locations, settings, and special closings fit together — and how each API key maps to a workspace.

The Location & Organization domain is the foundation of the Chataigne API. An
**organization** groups one or more **locations**. Each location carries its own
configuration — currency, address, opening hours, delivery rules, AI
instructions — plus any **special closings** for holidays or one-off events.

Your API key determines which surface you can reach and which workspace you act
on, so understanding this mapping is the first step to integrating.

<Note>
  All requests go to the base URL `https://server.chataigne.ai`. Every resource is
  identified by an opaque, prefixed `id` (for example `org_…`, `loc_…`,
  `scl_…`) and exposes `object`, `created_at`, and `updated_at` (ISO 8601 UTC).
</Note>

## The domain model

```
organization (org_…)
└── locations (loc_…)
    ├── order_settings        — auto-accept, prep time
    ├── opening_hours         — delivery & pickup schedules
    ├── delivery_settings     — fees, radius, min/max order
    ├── acceptance_settings   — online / busy / paused
    ├── ai_instructions       — chatbot guidance
    └── special_closings (scl_…) — holidays, one-off closures
```

<CardGroup cols={2}>
  <Card title="Organizations" icon="building" href="/location-organization-management/organizations">
    The top-level container. Holds `location_ids` and `active_location_ids`.
  </Card>

  <Card title="Locations" icon="store" href="/location-organization-management/locations">
    A single restaurant: currency, country, timezone, language, address.
  </Card>

  <Card title="Settings" icon="sliders" href="/location-organization-management/location-settings">
    Per-location sub-resources: orders, hours, delivery, acceptance, AI.
  </Card>

  <Card title="Special closings" icon="calendar-xmark" href="/location-organization-management/special-closings">
    Time-bounded closures with an optional reason.
  </Card>
</CardGroup>

## Organizations contain locations

An organization is the workspace boundary. It tracks the locations it owns and
which of them are currently active.

<ResponseField name="id" type="string">
  Unique identifier, prefixed `org_…`.
</ResponseField>

<ResponseField name="object" type="string">
  Always `"organization"`.
</ResponseField>

<ResponseField name="name" type="string">
  Display name of the organization.
</ResponseField>

<ResponseField name="location_ids" type="string[]">
  All locations owned by the organization (`loc_…`).
</ResponseField>

<ResponseField name="active_location_ids" type="string[]">
  Subset of `location_ids` that are currently active.
</ResponseField>

<ResponseField name="created_at" type="string">
  Creation timestamp (ISO 8601 UTC).
</ResponseField>

<ResponseField name="updated_at" type="string">
  Last-update timestamp (ISO 8601 UTC).
</ResponseField>

By default an organization references its locations by id. Use
[expansion](#expansion) to inline the full location objects in a single call.

<CodeGroup>
  ```bash cURL theme={null}
  curl https://server.chataigne.ai/v1/organizations/org_8tKx2qP4mN \
    -H "x-api-key: ch_org_live_4f9c2a..." \
    --data-urlencode "expand[]=locations" -G
  ```

  ```js Node theme={null}
  const res = await fetch(
    "https://server.chataigne.ai/v1/organizations/org_8tKx2qP4mN?expand[]=locations",
    { headers: { "x-api-key": "ch_org_live_4f9c2a..." } },
  );
  const organization = await res.json();
  ```
</CodeGroup>

```json Response theme={null}
{
  "id": "org_8tKx2qP4mN",
  "object": "organization",
  "name": "Trattoria Group",
  "location_ids": ["loc_a1B2c3D4", "loc_e5F6g7H8"],
  "active_location_ids": ["loc_a1B2c3D4"],
  "created_at": "2026-01-12T09:30:00Z",
  "updated_at": "2026-05-20T14:02:11Z"
}
```

## Locations have settings and special closings

A location represents one restaurant. It owns its locale and address, and is the
parent of every settings sub-resource and special closing.

<ResponseField name="id" type="string">
  Unique identifier, prefixed `loc_…`.
</ResponseField>

<ResponseField name="organization_id" type="string | null">
  The owning organization (`org_…`), or `null` if standalone.
</ResponseField>

<ResponseField name="currency" type="string">
  One of `CHF`, `EUR`, `GBP`, `BRL`, `USD`, `MXN`.
</ResponseField>

<ResponseField name="country" type="string">
  One of `FR`, `CH`, `GB`, `BR`, `LU`, `MX`, `US`.
</ResponseField>

<ResponseField name="timezone" type="string">
  IANA timezone, e.g. `Europe/Zurich`.
</ResponseField>

<ResponseField name="default_language" type="string">
  Default language for the location.
</ResponseField>

<ResponseField name="address" type="object | null">
  `{ line1, line2, postal_code, city, country, latitude, longitude }`, or `null`.
</ResponseField>

```json Location theme={null}
{
  "id": "loc_a1B2c3D4",
  "object": "location",
  "organization_id": "org_8tKx2qP4mN",
  "name": "Trattoria — Geneva",
  "currency": "CHF",
  "country": "CH",
  "timezone": "Europe/Zurich",
  "default_language": "fr",
  "address": {
    "line1": "Rue du Rhône 42",
    "line2": null,
    "postal_code": "1204",
    "city": "Geneva",
    "country": "CH",
    "latitude": 46.2044,
    "longitude": 6.1432
  },
  "created_at": "2026-01-12T09:31:40Z",
  "updated_at": "2026-05-22T08:15:00Z"
}
```

### Settings sub-resources

Each location exposes five settings objects. Read them with `GET` and update
them with `PATCH` — except `opening_hours`, which is replaced wholesale with
`PUT`.

| Sub-resource          | Object type           | Shape (key fields)                                                                                                                                                                                                                                         | Methods       |
| --------------------- | --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- |
| `order_settings`      | `order_settings`      | `auto_accept_orders`, `average_preparation_time` (`HH:MM`), `enable_multiple_order_per_customer`                                                                                                                                                           | `GET`/`PATCH` |
| `opening_hours`       | `opening_hours`       | `delivery`, `pickup` — each `{ monday…sunday: [{ from "HH:MM", to "HH:MM" }] }`                                                                                                                                                                            | `GET`/`PUT`   |
| `delivery_settings`   | `delivery_settings`   | `delivery_enabled`, `pickup_enabled`, `provider`, `fee_strategy`, `min_order_amount`, `max_order_amount`, `max_items_number`, `max_delivery_radius_in_km`, `fee_percentage`, `max_delivery_fee` (`{amount,currency}` \| `null`), `uber_direct_ums_enabled` | `GET`/`PATCH` |
| `acceptance_settings` | `acceptance_settings` | `mode` (`online`/`busy`/`paused`), `extra_preparation_time`, `reason`                                                                                                                                                                                      | `GET`/`PATCH` |
| `ai_instructions`     | `ai_instructions`     | `chatbot_instructions`                                                                                                                                                                                                                                     | `GET`/`PATCH` |

<CodeGroup>
  ```bash cURL theme={null}
  curl https://server.chataigne.ai/v1/locations/loc_a1B2c3D4/acceptance_settings \
    -X PATCH \
    -H "x-api-key: ch_org_live_4f9c2a..." \
    -H "Content-Type: application/json" \
    -d '{ "mode": "busy", "extra_preparation_time": "00:15", "reason": "High demand" }'
  ```

  ```js Node theme={null}
  const res = await fetch(
    "https://server.chataigne.ai/v1/locations/loc_a1B2c3D4/acceptance_settings",
    {
      method: "PATCH",
      headers: {
        "x-api-key": "ch_org_live_4f9c2a...",
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        mode: "busy",
        extra_preparation_time: "00:15",
        reason: "High demand",
      }),
    },
  );
  const acceptanceSettings = await res.json();
  ```
</CodeGroup>

<Warning>
  `opening_hours` is a full replacement (`PUT`), not a merge. Any day you omit
  is cleared. Always send the complete weekly schedule for both `delivery` and
  `pickup`.
</Warning>

### Special closings

A special closing temporarily takes a location offline for a bounded window.

<ResponseField name="id" type="string">
  Unique identifier, prefixed `scl_…`.
</ResponseField>

<ResponseField name="location_id" type="string">
  The location this closing applies to (`loc_…`).
</ResponseField>

<ResponseField name="starts_at" type="string">
  Start of the closure (ISO 8601 UTC).
</ResponseField>

<ResponseField name="ends_at" type="string">
  End of the closure (ISO 8601 UTC).
</ResponseField>

<ResponseField name="reason" type="string | null">
  Optional human-readable reason.
</ResponseField>

<CodeGroup>
  ```bash cURL theme={null}
  curl https://server.chataigne.ai/v1/locations/loc_a1B2c3D4/special_closings \
    -X POST \
    -H "x-api-key: ch_org_live_4f9c2a..." \
    -H "Content-Type: application/json" \
    -H "Idempotency-Key: scl-2026-08-01-summer-break" \
    -d '{
      "starts_at": "2026-08-01T00:00:00Z",
      "ends_at": "2026-08-15T22:00:00Z",
      "reason": "Summer break"
    }'
  ```

  ```js Node theme={null}
  const res = await fetch(
    "https://server.chataigne.ai/v1/locations/loc_a1B2c3D4/special_closings",
    {
      method: "POST",
      headers: {
        "x-api-key": "ch_org_live_4f9c2a...",
        "Content-Type": "application/json",
        "Idempotency-Key": "scl-2026-08-01-summer-break",
      },
      body: JSON.stringify({
        starts_at: "2026-08-01T00:00:00Z",
        ends_at: "2026-08-15T22:00:00Z",
        reason: "Summer break",
      }),
    },
  );
  const specialClosing = await res.json();
  ```
</CodeGroup>

```json Response theme={null}
{
  "id": "scl_9rT5uV6w",
  "object": "special_closing",
  "location_id": "loc_a1B2c3D4",
  "starts_at": "2026-08-01T00:00:00Z",
  "ends_at": "2026-08-15T22:00:00Z",
  "reason": "Summer break",
  "created_at": "2026-05-29T10:00:00Z",
  "updated_at": "2026-05-29T10:00:00Z"
}
```

## How a workspace maps to an API key

The Chataigne API exposes **two surfaces**, and your key prefix determines which
one you can use. Both surfaces authenticate via the `x-api-key` header.

<CardGroup cols={2}>
  <Card title="Chataigne API — /v1" icon="key">
    Public surface, scoped to a single organization/location workspace.

    **Key prefix:** `ch_org_`
    **Base path:** `/v1`
  </Card>

  <Card title="Admin API — /admin/v1" icon="user-shield">
    Administrative surface for provisioning organizations and locations.

    **Key prefix:** `ch_admin_`
    **Base path:** `/admin/v1`
  </Card>
</CardGroup>

The two surfaces are strictly isolated:

<ResponseField name="Org key on /v1" type="200">
  Works. The key's workspace defines the organization and locations you can read
  and write.
</ResponseField>

<ResponseField name="Admin key on /v1" type="403">
  Rejected with `authorization_error`.
</ResponseField>

<ResponseField name="Org key on /admin/v1" type="403">
  Rejected with `authorization_error`.
</ResponseField>

<ResponseField name="Missing key (either surface)" type="401">
  Rejected with `authentication_error`.
</ResponseField>

<Warning>
  Dashboard sessions are not API credentials. A dashboard session cannot be used
  on `/v1` or `/admin/v1` — you must use an API key with the correct prefix.
</Warning>

```bash Authenticated request theme={null}
curl https://server.chataigne.ai/v1/organizations \
  -H "x-api-key: ch_org_live_4f9c2a..."
```

## Listing & pagination

List endpoints return a `list` object and use cursor-based pagination.

```json List shape theme={null}
{
  "object": "list",
  "data": [{ "object": "location", "id": "loc_a1B2c3D4" }],
  "has_more": false,
  "url": "/v1/organizations/org_8tKx2qP4mN/locations"
}
```

<ParamField query="limit" type="integer" default="10">
  Page size, between `1` and `100`.
</ParamField>

<ParamField query="starting_after" type="string">
  Cursor — return items after this id. Mutually exclusive with `ending_before`.
</ParamField>

<ParamField query="ending_before" type="string">
  Cursor — return items before this id. Mutually exclusive with `starting_after`.
</ParamField>

<ParamField query="created_after" type="string">
  Filter to items created after this timestamp (ISO 8601).
</ParamField>

<ParamField query="created_before" type="string">
  Filter to items created before this timestamp (ISO 8601).
</ParamField>

<ParamField query="updated_after" type="string">
  Filter to items updated after this timestamp (ISO 8601).
</ParamField>

<ParamField query="updated_before" type="string">
  Filter to items updated before this timestamp (ISO 8601).
</ParamField>

Results are ordered by `created_at` by default.

## Expansion

Replace id references with full objects using `expand[]`. For example, expanding
`locations` on an organization returns each location object inline.

```bash theme={null}
curl "https://server.chataigne.ai/v1/organizations/org_8tKx2qP4mN?expand[]=locations" \
  -H "x-api-key: ch_org_live_4f9c2a..."
```

## Idempotency

Resource-creating `POST` requests **require** an `Idempotency-Key` header.

* Replaying the same key with the **same body** returns the original response,
  with `Idempotent-Replayed: true`.
* Reusing a key with a **different body** returns `idempotency_error` (`409`).
* A concurrent in-flight duplicate returns `conflict_error` (`409`).
* Keys are retained for **24 hours**.

```bash theme={null}
curl https://server.chataigne.ai/v1/organizations/org_8tKx2qP4mN/locations \
  -X POST \
  -H "x-api-key: ch_org_live_4f9c2a..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: loc-geneva-2026-05-29" \
  -d '{ "name": "Trattoria — Lausanne", "currency": "CHF", "country": "CH" }'
```

## Errors

Errors return the relevant HTTP status with a structured body.

```json theme={null}
{
  "error": {
    "type": "authorization_error",
    "code": "wrong_api_surface",
    "message": "Admin keys cannot access the /v1 surface.",
    "request_id": "req_7Hn2Kp9Qz"
  }
}
```

| Type                    | Status | When                                  |
| ----------------------- | ------ | ------------------------------------- |
| `invalid_request_error` | `400`  | Malformed request or invalid params   |
| `authentication_error`  | `401`  | Missing or invalid API key            |
| `authorization_error`   | `403`  | Key not allowed on this surface       |
| `not_found_error`       | `404`  | Resource does not exist               |
| `conflict_error`        | `409`  | Concurrent duplicate / state conflict |
| `idempotency_error`     | `409`  | Idempotency key reused with new body  |
| `rate_limit_error`      | `429`  | Per-key rate limit exceeded           |
| `api_error`             | `500`  | Unexpected server error               |

The `error` object may also include `param` and `details`. Every response
carries an `X-Request-Id`; error bodies mirror it as `error.request_id`. You can
echo your own id with the `x-request-id` header.

<Note>
  Rate limits are enforced per key, per surface. Responses include
  `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset`; a `429`
  additionally returns `Retry-After`.
</Note>

## Versioning

The API is versioned in the path (`/v1`). Additive changes are non-breaking and
ship within the current major version. Breaking changes are introduced under a
new major version.

## Resource reference

<CardGroup cols={2}>
  <Card title="Organizations" icon="building" href="/location-organization-management/organizations">
    `GET /v1/organizations`, `GET`/`PATCH /v1/organizations/{id}`,
    `GET`/`POST /v1/organizations/{id}/locations`
  </Card>

  <Card title="Locations" icon="store" href="/location-organization-management/locations">
    `GET /v1/locations`, `GET`/`PATCH /v1/locations/{id}`
  </Card>

  <Card title="Settings" icon="sliders" href="/location-organization-management/location-settings">
    `order_settings`, `opening_hours`, `delivery_settings`,
    `acceptance_settings`, `ai_instructions`
  </Card>

  <Card title="Special closings" icon="calendar-xmark" href="/location-organization-management/special-closings">
    `GET`/`POST /v1/locations/{id}/special_closings`,
    `GET`/`PATCH`/`DELETE .../{special_closing_id}`
  </Card>
</CardGroup>
