# Home page sections

Your app can read and change the sections of a store's home page. A section is a block on the home page with a type and settings. Ten types can be added on every theme: `category-products`, `featured`, `categories`, `banner`, `image-with-text`, `rich-text`, `trust-badges`, `testimonials`, `faq` and `video`; a section theme such as `atlas` also offers `hero` and `product-grid`. Every write is live on the store as soon as it answers.

The full request and response reference is in the OpenAPI description, linked from the [API reference](https://dzbuild.dev/api-reference.md) page: `getStoreHomeLayout`, `addStoreHomeSection`, `updateStoreHomeSection`, `deleteStoreHomeSection`, `reorderStoreHomeSections` and `replaceStoreHomeLayout`. This page explains how the pieces fit together.

## Scopes[​](#scopes "Direct link to Scopes")

| Scope         | Allows                                                                                                                                                                                              |
| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `store:read`  | `GET /v1/store/home-layout`                                                                                                                                                                         |
| `store:write` | `POST /v1/store/home-layout/sections`, `PATCH /v1/store/home-layout/sections/{id}`, `DELETE /v1/store/home-layout/sections/{id}`, `POST /v1/store/home-layout/reorder`, `PUT /v1/store/home-layout` |

A call without the scope answers `403` with the code `forbidden` and the message `Missing scope: store:write` (or `store:read`). See [Scopes](https://dzbuild.dev/scopes.md).

## Read the layout[​](#read-the-layout "Direct link to Read the layout")

`GET /v1/store/home-layout` returns:

| Field          | Meaning                                                                                                                                                                                                                   |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `theme`        | The store's theme key.                                                                                                                                                                                                    |
| `rendered`     | `false` when the store's current theme does not show home page sections. The sections are kept and show again on a theme that does. On a section theme it is `true` only while a visible `product-grid` section is saved. |
| `max_sections` | 25, the most sections one home page holds.                                                                                                                                                                                |
| `cap`          | The sections the store's plan allows: 3 on Free or an expired plan, 25 from Pro. Your app installs on stores of every plan, so read `cap` instead of assuming 25.                                                         |
| `version`      | A fingerprint of the stored layout, for `PUT`.                                                                                                                                                                            |
| `sections`     | `{id, type, settings, is_active, available}` in render order, hidden sections included. `available` is `false` when the theme no longer has that type; the section is kept as stored.                                     |
| `types`        | The types that can be added on this theme, each with `name`, `description`, `icon`, `limit` and `settings_schema`.                                                                                                        |

Build your forms from `settings_schema` rather than hard-coding a type: the list depends on the store's theme. Each setting has an `id`, a `type` (`checkbox`, `range`, `select`, `text`, `textarea`, `color`, `link`, `image`, `category` or `youtube`), a `default`, `min` and `max` or `options` where they apply, and `label` and `option_labels` in Arabic and French.

The settings of `category-products` are `category` (a category id from `GET /v1/categories`, which needs `products:read`), `title` (up to 80 characters, empty shows the category name), `count` (4 to 12), `layout` (`grid` or `slider`) and `show_view_all` (a link to the category page).

A section whose content is not filled in yet (no category or one without products, a `banner` without `image`, an `image-with-text` without `image` or without both `title` and `text`, an empty `rich-text`, `testimonials`, `faq` or `video`) is stored and the write answers `2xx`, but buyers do not see it until it is filled. `rendered` only says whether the theme shows stored sections.

An `image` setting takes only the path of a picture the merchant uploaded in the dashboard for this store (`/uploads/banners/{store_id}/...`) or `""`. The API cannot upload images today, so a `banner` or an `image-with-text` needs that upload first.

## Change the layout[​](#change-the-layout "Direct link to Change the layout")

Every write except `PUT` needs an `Idempotency-Key` header (see [Rate limits](https://dzbuild.dev/rate-limits.md)).

```
POST /v1/store/home-layout/sections HTTP/1.1

Host: api.dzbuild.app

Authorization: Bearer dzpk_live_xxxxxxxx

Idempotency-Key: hs-add-64

Content-Type: application/json



{"type": "category-products", "settings": {"category": 64}, "position": 0}
```

The call answers `201` with the new section and the whole layout:

```
{

  "data": {

    "section": {"id": 418, "type": "category-products", "settings": {"category": 64, "title": "", "count": 8, "layout": "grid", "show_view_all": true}, "is_active": true, "available": true},

    "sections": [

      {"id": 418, "type": "category-products", "settings": {"category": 64, "title": "", "count": 8, "layout": "grid", "show_view_all": true}, "is_active": true, "available": true},

      {"id": 412, "type": "category-products", "settings": {"category": 57, "title": "", "count": 8, "layout": "grid", "show_view_all": true}, "is_active": true, "available": true}

    ],

    "version": "e27a90c4b1f36d05",

    "change_id": 90231,

    "rendered": true

  },

  "meta": { "request_id": "3b7d0e5a9c14f862", "api_version": "v1" }

}
```

| Operation                   | Body                                | Notes                                                                                                                                                                                                                              |
| --------------------------- | ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `POST .../sections`         | `{type, settings?, position?}`      | Unsent settings take the defaults. `position` 0 is the top; without it the section goes at the end.                                                                                                                                |
| `PATCH .../sections/{id}`   | `{settings?, is_active?, replace?}` | Settings are merged over the stored ones. `replace: true` resets the unsent ones to their defaults. `is_active: false` hides the section.                                                                                          |
| `DELETE .../sections/{id}`  | none                                | Answers `deleted: true` and the section `id`.                                                                                                                                                                                      |
| `POST .../reorder`          | `{ids}`                             | Every section id of the page exactly once, hidden ones included.                                                                                                                                                                   |
| `PUT /v1/store/home-layout` | `{sections, version?}`              | The whole list, 25 items at most. An item with an `id` keeps that section, an item without one creates a section, a section left out is deleted. `settings` is the whole object here: an omitted setting goes back to its default. |

Every write answers the whole layout in render order with the new `version`, a `change_id` (`null` when nothing changed) and `rendered`. Keep that answer. Through `api.dzbuild.app` a successful `GET` is cached for 30 seconds per token and query string, so a `GET` sent right after a write can return the layout from before it. Add a query string of your own when you must read again.

To make sure nobody changed the page between your read and your write, send the `version` you read with `PUT`. If the layout moved, the call answers `409 write_conflict` and writes nothing; `error` then carries the current `sections` and `version` to retry from.

## Undo[​](#undo "Direct link to Undo")

Install tokens cannot call `/v1/changes` or `POST /v1/changes/{id}/undo`: they answer `403` with `Apps cannot use this endpoint`. Each write is still recorded, so the merchant can undo it from your app's page in their dashboard. To revert inside your app, keep the `sections` you read before the change and send them back with `PUT`. A section you deleted comes back with a new id.

## Limits[​](#limits "Direct link to Limits")

* 25 sections per home page, and at most `cap` for the store's plan. Hidden sections count. A store above its cap after a downgrade keeps its sections; a write that leaves more sections than the cap and more than before answers `403 plan_required`.
* Each type has a `limit` in `types`, 12 for `category-products`.
* 8 KB of settings per section once encoded, and 1 MB per request body.
* 30 writes a minute and 5 at once per store, on top of your install's budget and the store's budget. See [Rate limits](https://dzbuild.dev/rate-limits.md).

## Answers and error codes[​](#answers-and-error-codes "Direct link to Answers and error codes")

| Status | Code                                    | Meaning                                                                                                                                                                                                           |
| ------ | --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `200`  |                                         | Read, changed, deleted, reordered or replaced.                                                                                                                                                                    |
| `201`  |                                         | Section added.                                                                                                                                                                                                    |
| `400`  | `bad_request`                           | The body is not a JSON object, a field has the wrong type, the path id is not a positive number, or `Idempotency-Key` is missing on `POST`, `PATCH` or `DELETE`.                                                  |
| `403`  | `forbidden`                             | The token lacks `store:read` or `store:write`.                                                                                                                                                                    |
| `403`  | `plan_required`                         | The write would leave more sections than the store's plan allows. `error` carries `plan` and `cap`.                                                                                                               |
| `404`  | `section_not_found`                     | No section with this id on the store's home page.                                                                                                                                                                 |
| `409`  | `write_conflict`                        | Another write changed the layout first, or the `version` sent on `PUT` is not the current one. When `error` carries `sections` and `version`, retry from them; otherwise read again. Retry with a new key.        |
| `413`  | `payload_too_large`                     | The body is over 1 MB.                                                                                                                                                                                            |
| `422`  | `invalid_settings`                      | A value was refused. `error.fields` lists the refused settings, such as `settings.category`; on `PUT`, those of the first refused section only, such as `sections.2.settings.layout`. The message names them too. |
| `422`  | `invalid_section_type`                  | The type does not exist or cannot be added on this theme.                                                                                                                                                         |
| `422`  | `limit_reached`                         | More than 25 sections, or more of one type than its `limit`.                                                                                                                                                      |
| `422`  | `invalid_order`                         | Reorder `ids` miss a section or repeat one.                                                                                                                                                                       |
| `422`  | `no_changes`                            | A `PATCH` without `settings` or `is_active`.                                                                                                                                                                      |
| `429`  | `rate_limited` or `too_many_concurrent` | A per-minute budget is used up, or 5 home layout writes are already running for the store. Wait `retry_after` seconds.                                                                                            |

See [Errors](https://dzbuild.dev/errors.md) for the codes every call can get, such as `app_uninstalled`.
