---
title: "Apply a site plan"
description: "Write a site's business details, theme, navigation, entries and pictures in one call, as one history entry."
canonical: "https://atelier201.com/docs/api/sites/apply-plan"
updated: "2026-11-06"
---

# Apply a site plan

Requires `sites:write`. Costs no credits: stock pictures are free. The path ends in `/plan:apply`. Changes the draft only. Visitors see nothing until you [publish](/docs/api/publishing/publish).

The fastest way to write a first version of a site, or to rework one: everything a first version needs, in one request. Read the [content model](/docs/api/content/model) first for the fields.

- **`name`**: the site's name in Atelier201.
- **`singletons`**: `business`, `theme`, `navigation`... Each is merged onto what the draft holds, key by key at the top level; a nested group you give (such as `hero` or `colors`) replaces that group.
- **`entries`**: up to 200 entries to create, update or delete, as [Save many entries](/docs/api/content/save-entries) takes them.
- **`images`**: up to 12 pictures, as [Put a picture in an image field](/docs/api/media/place) takes them: `target`, `alt`, and one of `query`, `stock_id` or `media_path`. A picture that cannot be found comes back with a `warning`, and the rest of the plan is still applied.
- **`keep_samples`**: the starter's sample entries (`service-one`, `service-two`) are deleted unless the plan writes them or this is `true`.

Everything is validated before anything is written. If any part is refused, nothing is saved and the `400` lists every part to fix, so you correct them all in one pass. On success it is one history entry and the build starts: the response carries `build`. [Review the publish](/docs/api/publishing/review) afterwards: it refuses while any of the starter's sample text is left.

```http
POST https://api.atelier201.com/v1/sites/{site_id}/plan:apply
```

## Authorizations

- `Authorization` (bearer, in header, required)

## Path parameters

- `site_id` (`string`, required): The site's id, or its slug (the address before .atelier201.page).

## Body

`application/json`, required

- `name` (`string`, length: `1–80 chars`): The site's name in Atelier201 (the business name).
- `singletons` (`Record<string, Record<string, object>>`): business, theme, navigation...: each merged onto what the draft holds (top-level keys; a nested group given here replaces that group). Fields as get_content_model defines them.
- `entries` (`object[]`, items: `1–200 items`): Entries to create, update or delete (delete: true), as save_entries takes them, up to 200.
- `entries.collection` (`string`, required)
- `entries.slug` (`string`, required): Lowercase letters, digits and hyphens.
- `entries.data` (`Record<string, object>`): The fields, as the collection's schema defines them (not for delete).
- `entries.body` (`string`, length: `≤ 500000 chars`): Markdown body, for content collections.
- `entries.delete` (`boolean`): true to delete this entry.
- `images` (`object[]`, items: `≤ 12 items`): Pictures to put in image fields, as place_image takes them (target, alt, and one of query, stock_id, media_path). A picture that cannot be found is a warning; the rest is still applied.
- `images.target` (`string`, required, length: `3–200 chars`): The image field: business:/hero/image for business details, or <collection>/<slug>:/image for an entry (services/sourdough:/image). get_content_model shows the image fields.
- `images.alt` (`string`, required, length: `1–300 chars`): What the picture shows, for people who cannot see it, in the site's language. Saved in the alt field next to the image when there is one.
- `images.query` (`string`, length: `≤ 100 chars`): A description searched in the free stock library, in English with concrete words ("minimalist concrete house at dusk"): the first result is imported.
- `images.stock_id` (`string`, length: `≤ 20 chars`): A result id from search_stock_media, when you chose the picture yourself.
- `images.media_path` (`string`, length: `≤ 200 chars`): A file already in the site's media (/media/storefront.jpg), e.g. the owner's own photo.
- `images.orientation` (`enum<string>`, one of: `landscape, portrait, square`): With query: the shape to search for. Landscape suits a hero.
- `keep_samples` (`boolean`): Keep the starter's sample entries (service-one, service-two). Default: they are deleted unless the plan writes them.
- `summary` (`string`, length: `≤ 200 chars`): One sentence for the history.

## Response

**200**: Apply a site plan

- `data` (`Record<string, object>`, required)
- `data.renamed` (`string,null`, required)
- `data.singletons` (`string[]`, required): The singletons written.
- `data.saved` (`integer`, required, range: `-9007199254740991–9007199254740991`)
- `data.deleted` (`integer`, required, range: `-9007199254740991–9007199254740991`)
- `data.images` (`object[]`, required)
- `data.images.target` (`string`, required)
- `data.images.image` (`string,null`, required)
- `data.images.credit` (`string,null`, required)
- `data.images.warning` (`string,null`, required)
- `data.revision` (`string | null`, required)
- `data.changed` (`boolean`, required)
- `data.build` (`object | null`, required): The build this change started (or joined): the preview follows it. No build_site call needed.
- `data.build.id` (`string`, required)
- `data.build.state` (`string`, required)
- `data.build.next_action` (`string`, required)
- `data.next_action` (`string`, required)
- `request_id` (`string`, required)

```json
{
  "data": {
    "renamed": null,
    "singletons": [
      "<string>"
    ],
    "saved": 123,
    "deleted": 123,
    "images": [
      {
        "target": "<string>",
        "image": null,
        "credit": null,
        "warning": null
      }
    ],
    "revision": "<string>",
    "changed": true,
    "build": {
      "id": "<string>",
      "state": "<string>",
      "next_action": "<string>"
    },
    "next_action": "<string>"
  },
  "request_id": "<string>"
}
```

**400**: Possible codes: invalid_request, unknown_field, invalid_parameter, invalid_cursor

- `error` (`object`, required)
- `error.type` (`string`, required)
- `error.code` (`string`, required)
- `error.message` (`string`, required)
- `error.param` (`string`)
- `error.docs_url` (`string`, required)
- `error.action_url` (`string<uri>`)
- `error.details` (`Record<string, object>`)
- `request_id` (`string`, required)

```json
{
  "error": {
    "type": "<string>",
    "code": "<string>",
    "message": "<string>",
    "param": "<string>",
    "docs_url": "<string>",
    "action_url": "<string>",
    "details": {}
  },
  "request_id": "<string>"
}
```

**401**: Possible codes: authentication_required, invalid_api_key, api_key_expired, api_key_revoked

- `error` (`object`, required)
- `error.type` (`string`, required)
- `error.code` (`string`, required)
- `error.message` (`string`, required)
- `error.param` (`string`)
- `error.docs_url` (`string`, required)
- `error.action_url` (`string<uri>`)
- `error.details` (`Record<string, object>`)
- `request_id` (`string`, required)

```json
{
  "error": {
    "type": "<string>",
    "code": "<string>",
    "message": "<string>",
    "param": "<string>",
    "docs_url": "<string>",
    "action_url": "<string>",
    "details": {}
  },
  "request_id": "<string>"
}
```

**402**: Possible codes: insufficient_credits

- `error` (`object`, required)
- `error.type` (`string`, required)
- `error.code` (`string`, required)
- `error.message` (`string`, required)
- `error.param` (`string`)
- `error.docs_url` (`string`, required)
- `error.action_url` (`string<uri>`)
- `error.details` (`Record<string, object>`)
- `request_id` (`string`, required)

```json
{
  "error": {
    "type": "<string>",
    "code": "<string>",
    "message": "<string>",
    "param": "<string>",
    "docs_url": "<string>",
    "action_url": "<string>",
    "details": {}
  },
  "request_id": "<string>"
}
```

**403**: Possible codes: insufficient_scope, feature_not_available, admin_required

- `error` (`object`, required)
- `error.type` (`string`, required)
- `error.code` (`string`, required)
- `error.message` (`string`, required)
- `error.param` (`string`)
- `error.docs_url` (`string`, required)
- `error.action_url` (`string<uri>`)
- `error.details` (`Record<string, object>`)
- `request_id` (`string`, required)

```json
{
  "error": {
    "type": "<string>",
    "code": "<string>",
    "message": "<string>",
    "param": "<string>",
    "docs_url": "<string>",
    "action_url": "<string>",
    "details": {}
  },
  "request_id": "<string>"
}
```

**404**: Possible codes: resource_not_found

- `error` (`object`, required)
- `error.type` (`string`, required)
- `error.code` (`string`, required)
- `error.message` (`string`, required)
- `error.param` (`string`)
- `error.docs_url` (`string`, required)
- `error.action_url` (`string<uri>`)
- `error.details` (`Record<string, object>`)
- `request_id` (`string`, required)

```json
{
  "error": {
    "type": "<string>",
    "code": "<string>",
    "message": "<string>",
    "param": "<string>",
    "docs_url": "<string>",
    "action_url": "<string>",
    "details": {}
  },
  "request_id": "<string>"
}
```

**409**: Possible codes: conflict, result_not_ready, idempotency_key_reused

- `error` (`object`, required)
- `error.type` (`string`, required)
- `error.code` (`string`, required)
- `error.message` (`string`, required)
- `error.param` (`string`)
- `error.docs_url` (`string`, required)
- `error.action_url` (`string<uri>`)
- `error.details` (`Record<string, object>`)
- `request_id` (`string`, required)

```json
{
  "error": {
    "type": "<string>",
    "code": "<string>",
    "message": "<string>",
    "param": "<string>",
    "docs_url": "<string>",
    "action_url": "<string>",
    "details": {}
  },
  "request_id": "<string>"
}
```

**410**: Possible codes: result_expired

- `error` (`object`, required)
- `error.type` (`string`, required)
- `error.code` (`string`, required)
- `error.message` (`string`, required)
- `error.param` (`string`)
- `error.docs_url` (`string`, required)
- `error.action_url` (`string<uri>`)
- `error.details` (`Record<string, object>`)
- `request_id` (`string`, required)

```json
{
  "error": {
    "type": "<string>",
    "code": "<string>",
    "message": "<string>",
    "param": "<string>",
    "docs_url": "<string>",
    "action_url": "<string>",
    "details": {}
  },
  "request_id": "<string>"
}
```

**413**: Possible codes: request_too_large

- `error` (`object`, required)
- `error.type` (`string`, required)
- `error.code` (`string`, required)
- `error.message` (`string`, required)
- `error.param` (`string`)
- `error.docs_url` (`string`, required)
- `error.action_url` (`string<uri>`)
- `error.details` (`Record<string, object>`)
- `request_id` (`string`, required)

```json
{
  "error": {
    "type": "<string>",
    "code": "<string>",
    "message": "<string>",
    "param": "<string>",
    "docs_url": "<string>",
    "action_url": "<string>",
    "details": {}
  },
  "request_id": "<string>"
}
```

**422**: Possible codes: request_too_large_for_plan

- `error` (`object`, required)
- `error.type` (`string`, required)
- `error.code` (`string`, required)
- `error.message` (`string`, required)
- `error.param` (`string`)
- `error.docs_url` (`string`, required)
- `error.action_url` (`string<uri>`)
- `error.details` (`Record<string, object>`)
- `request_id` (`string`, required)

```json
{
  "error": {
    "type": "<string>",
    "code": "<string>",
    "message": "<string>",
    "param": "<string>",
    "docs_url": "<string>",
    "action_url": "<string>",
    "details": {}
  },
  "request_id": "<string>"
}
```

**429**: Possible codes: rate_limited

- `error` (`object`, required)
- `error.type` (`string`, required)
- `error.code` (`string`, required)
- `error.message` (`string`, required)
- `error.param` (`string`)
- `error.docs_url` (`string`, required)
- `error.action_url` (`string<uri>`)
- `error.details` (`Record<string, object>`)
- `request_id` (`string`, required)

```json
{
  "error": {
    "type": "<string>",
    "code": "<string>",
    "message": "<string>",
    "param": "<string>",
    "docs_url": "<string>",
    "action_url": "<string>",
    "details": {}
  },
  "request_id": "<string>"
}
```

**500**: Possible codes: internal_error

- `error` (`object`, required)
- `error.type` (`string`, required)
- `error.code` (`string`, required)
- `error.message` (`string`, required)
- `error.param` (`string`)
- `error.docs_url` (`string`, required)
- `error.action_url` (`string<uri>`)
- `error.details` (`Record<string, object>`)
- `request_id` (`string`, required)

```json
{
  "error": {
    "type": "<string>",
    "code": "<string>",
    "message": "<string>",
    "param": "<string>",
    "docs_url": "<string>",
    "action_url": "<string>",
    "details": {}
  },
  "request_id": "<string>"
}
```

**503**: Possible codes: service_unavailable

- `error` (`object`, required)
- `error.type` (`string`, required)
- `error.code` (`string`, required)
- `error.message` (`string`, required)
- `error.param` (`string`)
- `error.docs_url` (`string`, required)
- `error.action_url` (`string<uri>`)
- `error.details` (`Record<string, object>`)
- `request_id` (`string`, required)

```json
{
  "error": {
    "type": "<string>",
    "code": "<string>",
    "message": "<string>",
    "param": "<string>",
    "docs_url": "<string>",
    "action_url": "<string>",
    "details": {}
  },
  "request_id": "<string>"
}
```

**Example request**

```bash
curl --request POST \
  --url 'https://api.atelier201.com/v1/sites/<site_id>/plan:apply' \
  --header 'Authorization: <api-key>' \
  --header 'Content-Type: application/json' \
  --data '{
  "name": "<string>",
  "singletons": {},
  "entries": [
    {
      "collection": "<string>",
      "slug": "<string>"
    }
  ],
  "images": [
    {
      "target": "<string>",
      "alt": "<string>"
    }
  ],
  "keep_samples": true,
  "summary": "<string>"
}'
```
