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

# Placements API

> Create, update, list, and delete placement slot definitions that describe where offers are displayed.

Placements define the slots within a channel where offers can be rendered. Each placement belongs to a channel and specifies the slot type, maximum number of offers, content schema, and targeting rules.

<Info>
  See the [Channels feature page](/studio/channels) for details on how placements work within channel configurations.
</Info>

## Base path

```
/api/v1/placements
```

***

## List placements

```
GET /api/v1/placements
```

Returns a paginated list of placements for the current tenant, ordered alphabetically by name. Each placement includes its parent channel and associated creatives.

### Query parameters

| Parameter        | Required | Type    | Description                                                              |
| ---------------- | -------- | ------- | ------------------------------------------------------------------------ |
| `channelId`      | No       | string  | Filter placements by channel.                                            |
| `limit`          | No       | integer | Maximum results per page. Default `50`, max `100`.                       |
| `cursor`         | No       | string  | Cursor for keyset pagination. Pass the last `id` from the previous page. |
| `includeDeleted` | No       | string  | Set to `"true"` to include soft-deleted placements.                      |

### Response `200`

```json theme={null}
{
  "data": [
    {
      "id": "pl_001",
      "tenantId": "t_001",
      "channelId": "ch_web",
      "name": "Homepage Hero Banner",
      "description": "Full-width banner on the homepage above the fold.",
      "slotType": "banner",
      "maxSlots": 1,
      "schema": {},
      "targeting": {},
      "channel": {
        "id": "ch_web",
        "name": "Web",
        "channelType": "web"
      },
      "creatives": [],
      "version": 1,
      "deletedAt": null
    }
  ],
  "pagination": {
    "limit": 50,
    "cursor": null,
    "hasMore": false,
    "total": 6
  }
}
```

***

## Create a placement

```
POST /api/v1/placements
```

Creates a new placement slot definition.

### Request body

| Field         | Required | Type           | Description                                                                                                                                        |
| ------------- | -------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `channelId`   | **Yes**  | string         | Parent channel ID.                                                                                                                                 |
| `name`        | **Yes**  | string (1-255) | Placement name.                                                                                                                                    |
| `description` | No       | string         | Placement description. Default `""`.                                                                                                               |
| `slotType`    | No       | string         | Slot type — any string is accepted. Common values: `"hero"`, `"banner"` (default), `"sidebar"`, `"modal"`, `"full_screen"`, `"inline"`, `"toast"`. |
| `maxSlots`    | No       | integer (>= 1) | Maximum number of offers in this placement. Default `1`.                                                                                           |
| `schema`      | No       | object         | Content schema definition for this placement. Default `{}`.                                                                                        |
| `targeting`   | No       | object         | Targeting rules for placement eligibility. Default `{}`.                                                                                           |

### Example request

```json theme={null}
{
  "channelId": "ch_web",
  "name": "Homepage Hero Banner",
  "description": "Full-width banner on the homepage above the fold.",
  "slotType": "banner",
  "maxSlots": 1,
  "targeting": {
    "pages": ["homepage"],
    "segments": ["high_value"]
  }
}
```

### Response `201`

Returns the created placement object with channel and creatives relations.

### Side effects

Creating the first placement on a tenant triggers `ensureBaseFlow` (`lib/decision-flows/base-flow.ts`) so a default `base-nba-flow` exists for routing. The auto-created flow includes a `match_creatives` node with `placementMatchMode: "exact"`, which means subsequent `/api/v1/recommend` calls that pass either a single `placement` or a `placements: [...]` array filter candidates to creatives whose `placementId` matches the request — without any flow editing required. See [Decision Flow Internals](/api-reference/decision-flow-internals#post-apiv1decision-flowsensure-base) for the full default node order.

A flow-route entry is also created automatically, pointing the new `(channelId, placementId)` combination at the default flow; this is best-effort and silently no-ops if a route already exists.

### Error codes

| Code  | Reason                                                                                                                                     |
| ----- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `400` | Validation error (missing `channelId` or `name`), or a `channelId` that doesn't exist in your tenant (foreign-key ownership is validated). |
| `401` | Missing or invalid API key / session.                                                                                                      |
| `403` | Insufficient role (requires `editor` or `admin`).                                                                                          |
| `409` | A placement with that name already exists.                                                                                                 |
| `413` | Request body exceeds the 2 MB limit.                                                                                                       |
| `415` | `Content-Type` is not `application/json`.                                                                                                  |

***

## Update a placement

```
PUT /api/v1/placements
```

Updates an existing placement. Only provided fields are changed.

### Request body

| Field         | Required | Type           | Description                 |
| ------------- | -------- | -------------- | --------------------------- |
| `id`          | **Yes**  | string         | The placement ID to update. |
| `name`        | No       | string (1-255) | Updated name.               |
| `description` | No       | string         | Updated description.        |
| `slotType`    | No       | string         | Updated slot type.          |
| `maxSlots`    | No       | integer (>= 1) | Updated max slots.          |
| `schema`      | No       | object         | Updated content schema.     |
| `targeting`   | No       | object         | Updated targeting rules.    |

### Response `200`

Returns the updated placement object with channel and creatives relations.

### Error codes

| Code  | Reason                                                |
| ----- | ----------------------------------------------------- |
| `400` | Validation error.                                     |
| `401` | Missing or invalid API key / session.                 |
| `403` | Insufficient role.                                    |
| `404` | No placement with that `id` (or name) in your tenant. |
| `409` | A placement with that name already exists.            |
| `413` | Request body exceeds the 2 MB limit.                  |
| `415` | `Content-Type` is not `application/json`.             |

***

## Delete a placement

```
DELETE /api/v1/placements?id={placementId}
```

Soft-deletes a placement by setting its `deletedAt` timestamp. The `version` is incremented.

### Query parameters

| Parameter | Required | Type   | Description             |
| --------- | -------- | ------ | ----------------------- |
| `id`      | **Yes**  | string | Placement ID to delete. |

### Response `200`

```json theme={null}
{
  "success": true,
  "cascaded": 0
}
```

### Error codes

| Code  | Reason                                                                     |
| ----- | -------------------------------------------------------------------------- |
| `400` | Missing `id` query parameter, entity not found, or entity already deleted. |
| `401` | Missing or invalid API key / session.                                      |
| `403` | Insufficient role.                                                         |

<Note>
  To restore a soft-deleted placement, use `POST /api/v1/restore?entityType=placement&id={placementId}` (admin only).
</Note>

***

## Role requirements

| Method | Minimum role |
| ------ | ------------ |
| GET    | `viewer`     |
| POST   | `editor`     |
| PUT    | `editor`     |
| DELETE | `editor`     |
