> ## 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.

# Quickstart

> Make your first authenticated call to the Chataigne API and list your locations.

This guide walks you through your first successful request: authenticating with an API key and calling `GET /v1/locations` to retrieve the locations you have access to.

## 1. Get an API key

API keys are **issued by a Chataigne admin** — there is no self-service key creation. Contact your Chataigne representative and request a public API key for your organization or location.

You'll receive a key prefixed with `ch_org_`. This is a **location/organization key** scoped to the public API surface (`/v1`).

<Warning>
  Treat your API key like a password. Send it only over HTTPS, never embed it in client-side code or commit it to source control, and rotate it immediately if it leaks. Keys are issued and revoked by Chataigne admins.
</Warning>

<Note>
  There are two API surfaces. The **Chataigne API** (`/v1`) uses organization keys (`ch_org_…`). The **Admin API** (`/admin/v1`) uses admin keys (`ch_admin_…`). The two are isolated: `/v1` rejects admin keys and `/admin/v1` rejects org keys, both with `403`. This guide uses the public `/v1` surface.
</Note>

## 2. Authenticate

All requests use the **base URL** `https://server.chataigne.ai` and authenticate by passing your key in the `x-api-key` header.

```bash theme={null}
x-api-key: ch_org_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

| Scenario                                  | Result                                        |
| ----------------------------------------- | --------------------------------------------- |
| Missing key                               | `401 authentication_error`                    |
| Org key on `/admin/v1`                    | `403 authorization_error`                     |
| Admin key on `/v1`                        | `403 authorization_error`                     |
| Dashboard session on `/v1` or `/admin/v1` | Rejected — sessions cannot be used on the API |

## 3. Make your first call

Call `GET /v1/locations` to list the locations your key can access.

<CodeGroup>
  ```bash curl theme={null}
  curl https://server.chataigne.ai/v1/locations \
    -H "x-api-key: ch_org_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  ```

  ```javascript Node theme={null}
  const res = await fetch("https://server.chataigne.ai/v1/locations", {
    headers: {
      "x-api-key": process.env.CHATAIGNE_API_KEY,
    },
  });

  if (!res.ok) {
    const { error } = await res.json();
    throw new Error(`${error.type}: ${error.message} (request_id: ${error.request_id})`);
  }

  const locations = await res.json();
  console.log(locations.data);
  ```

  ```python Python theme={null}
  import os
  import requests

  res = requests.get(
      "https://server.chataigne.ai/v1/locations",
      headers={"x-api-key": os.environ["CHATAIGNE_API_KEY"]},
  )

  if not res.ok:
      error = res.json()["error"]
      raise RuntimeError(f"{error['type']}: {error['message']} (request_id: {error['request_id']})")

  locations = res.json()
  print(locations["data"])
  ```
</CodeGroup>

## 4. Read the response

A successful call returns a **list object**. The `data` array holds the location objects, `has_more` indicates whether more pages exist, and `url` echoes the resource path.

```json theme={null}
{
  "object": "list",
  "data": [
    {
      "id": "loc_8f2c91d4a7e34b10",
      "object": "location",
      "organization_id": "org_4a1b7c9e2d6f48a3",
      "name": "Café de la Gare",
      "currency": "CHF",
      "country": "CH",
      "timezone": "Europe/Zurich",
      "default_language": "fr",
      "address": {
        "line1": "Rue de la Gare 12",
        "line2": null,
        "postal_code": "1003",
        "city": "Lausanne",
        "country": "CH",
        "latitude": 46.5168,
        "longitude": 6.6291
      },
      "created_at": "2025-11-04T09:21:00Z",
      "updated_at": "2026-01-18T14:02:33Z"
    }
  ],
  "has_more": false,
  "url": "/v1/locations"
}
```

### Response fields

<ResponseField name="object" type="string">
  Always `"list"` for list responses.
</ResponseField>

<ResponseField name="data" type="array">
  An array of `location` objects. See the fields below.
</ResponseField>

<ResponseField name="has_more" type="boolean">
  `true` if there are more results beyond this page. Use cursor pagination to fetch them.
</ResponseField>

<ResponseField name="url" type="string">
  The path this list was retrieved from.
</ResponseField>

Each `location` object includes:

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

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

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

<ResponseField name="name" type="string">
  The location's display name.
</ResponseField>

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

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

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

<ResponseField name="default_language" type="string">
  The location's default language.
</ResponseField>

<ResponseField name="address" type="object | null">
  Postal address with `line1`, `line2`, `postal_code`, `city`, `country`, `latitude`, `longitude`. May be `null`.
</ResponseField>

<ResponseField name="created_at" type="string">
  ISO 8601 UTC creation timestamp.
</ResponseField>

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

## 5. Refine the list

The list endpoint supports cursor pagination and timestamp filters as query parameters.

<ParamField query="limit" type="integer" default="10">
  Number of results per page, between `1` and `100`.
</ParamField>

<ParamField query="starting_after" type="string">
  Cursor for the next page — the `id` of the last object in the previous page. Mutually exclusive with `ending_before`.
</ParamField>

<ParamField query="ending_before" type="string">
  Cursor for the previous page — the `id` of the first object in the current page. Mutually exclusive with `starting_after`.
</ParamField>

<ParamField query="created_after" type="string">
  Return objects created after this ISO 8601 timestamp.
</ParamField>

<ParamField query="created_before" type="string">
  Return objects created before this ISO 8601 timestamp.
</ParamField>

<ParamField query="updated_after" type="string">
  Return objects updated after this ISO 8601 timestamp.
</ParamField>

<ParamField query="updated_before" type="string">
  Return objects updated before this ISO 8601 timestamp.
</ParamField>

Results are ordered by `created_at` by default. For example, fetch the next page of 50 locations created in 2026:

```bash theme={null}
curl "https://server.chataigne.ai/v1/locations?limit=50&created_after=2026-01-01T00:00:00Z&starting_after=loc_8f2c91d4a7e34b10" \
  -H "x-api-key: ch_org_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
```

<Note>
  Every response includes an `X-Request-Id` header. Include it when contacting support — error bodies also mirror it as `error.request_id`. Responses additionally carry `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset` headers; a `429` response adds `Retry-After`.
</Note>

## 6. Handle errors

Errors return the appropriate HTTP status with a structured body. Always read `error.type` and `error.code`, and log `error.request_id` for support.

```json theme={null}
{
  "error": {
    "type": "authentication_error",
    "code": "missing_api_key",
    "message": "No API key provided. Include your key in the x-api-key header.",
    "request_id": "req_2y8Fk1Zx0Qm7"
  }
}
```

| HTTP status | `error.type`                           |
| ----------- | -------------------------------------- |
| `400`       | `invalid_request_error`                |
| `401`       | `authentication_error`                 |
| `403`       | `authorization_error`                  |
| `404`       | `not_found_error`                      |
| `409`       | `conflict_error` / `idempotency_error` |
| `429`       | `rate_limit_error`                     |
| `500`       | `api_error`                            |

## Next steps

<Card title="Inspect a single location" icon="store" href="/location-organization-management/locations">
  Retrieve full details for one location with `GET /v1/locations/{location_id}`.
</Card>

<Card title="Expand related objects" icon="layer-group">
  Use `expand[]=locations` on an organization to inline its location objects: `GET /v1/organizations/{organization_id}?expand[]=locations`.
</Card>

<Card title="Create with idempotency" icon="rotate" href="/location-organization-management/special-closings">
  Resource-creating `POST`s require an `Idempotency-Key` header. Replaying the same key with the same body returns the original response (with `Idempotent-Replayed: true`); keys are retained for 24h.
</Card>
