# Changelog

The API is in pilot. Personal API keys must belong to a store on an active Enterprise plan, or every call returns `403`; installed-app tokens work on every plan. `https://api.dzbuild.app` is the only supported host.

## v1.10 - 2026-10-09 (pilot)

* ✨ **DZBuild POS.** The free Windows till links to a store through OAuth, with discovery at `https://dzbuild.com/.well-known/oauth-authorization-server`, and calls its own endpoints with a till token: `GET /v1/me`, `POST /v1/devices` and heartbeats, `GET /v1/locations` and `POST /v1/locations`, `POST /v1/products/batch`, `POST /v1/media/uploads`, `POST /v1/inventory/adjustments/batch`, `POST /v1/pos/sales`, `POST /v1/pos/sales/{sale_id}/refunds`, `POST /v1/pos/closures`, `POST /v1/orders/{id}/claim` and `GET /v1/events`. The four backup paths answer `501 not_implemented`. Personal API keys and app tokens get `403` on the till-only paths. See [DZBuild POS](https://dzbuild.com/api-docs/pos.md).
* ✨ **`GET /v1/store` returns `currency` (`DZD`), `stock_deduction` (`on_create` or `on_confirm`), `plan` and `plan_limits`.** An expired paid plan reads as `free`, and `plan_limits.active_products` is `null` when the plan has no cap.
* ⚠️ **No change for `POST /v1/products` at the plan cap**: it still answers `400 bad_request` with the same message. The new `402 product_limit_reached` code is for the till's batch call only.

## v1.9 - 2026-10-08 (pilot)

* ✨ **Buy bar fields.** `PATCH /v1/store/design` takes `buybar_show_mobile`, `buybar_show_qty`, `fc_show_qty` and `product_button_size` (`normal` or `large`) on every plan: they show or hide the phone buy bar and the quantity in the bar and in the fast-checkout form, and set the size of the buy buttons. See [Store](https://dzbuild.com/api-docs/resources/store.md).
* ✨ **`GET /v1/store` returns `store_theme`, `fast_checkout_theme` and `variant_card_style`**, the three keys in use, read-only.
* ✨ **`GET /v1/themes` lists the fast-checkout themes and the variant styles** in `fast_checkout` and `variant_styles`, each with its plan, whether the store can use it and which one is in use. See [Themes](https://dzbuild.com/api-docs/resources/themes.md).
* ✨ **Design and theme writes return `change_id`.** `PATCH /v1/store/design`, `POST /v1/store/theme`, `POST /v1/store/fast-checkout-theme` and `POST /v1/store/variant-style` carry the id to send to `POST /v1/changes/{id}/undo`.

## v1.8.1 - 2026-10-02 (pilot)

* ⚠️ **Deleting a product that a landing page uses is refused** with `409 product_in_use_by_landing_page`. The error lists the pages in `landing_pages[]` with `id`, `title` and `slug`. Delete the landing page first, or attach another product to it with `PATCH /v1/landing-pages/{id}`, then delete the product. See [Products](https://dzbuild.com/api-docs/resources/products.md).

## v1.8 - 2026-09-28 (pilot)

* ✨ **Home page sections.** `GET /v1/store/home-layout` reads a store's home page sections and the section types its theme accepts. `POST /v1/store/home-layout/sections` adds one, `PATCH` and `DELETE /v1/store/home-layout/sections/{id}` change or remove one, `POST /v1/store/home-layout/reorder` sets the order and `PUT /v1/store/home-layout` replaces the whole list. Ten section types can be added on every theme, among them `category-products`, `banner`, `faq` and `video`. Image settings take a picture uploaded in the dashboard: the API cannot upload one yet. The endpoints use `store:read` and `store:write`, so existing keys work. See [Home page sections](https://dzbuild.com/api-docs/resources/home-layout.md).
* ✨ **Every home layout write can be undone, adding a section included.** The answer carries a `change_id` for `POST /v1/changes/{id}/undo`, which answers `409 layout_changed` when the page changed again since.
* ⚠️ **Home layout writes have their own budget** of 30 a minute and 5 at once per store. See [Rate limits](https://dzbuild.com/api-docs/rate-limits.md).

## v1.7 - 2026-09-26 (pilot)

* ✨ **Third-party apps.** An app installs on a store through OAuth and calls the API with an install token, on any plan. App calls can get four new `403` codes, `app_uninstalled`, `app_suspended`, `app_not_approved` and `app_plan_required`, and each install has its own limit of 120 requests a minute, checked before the store's. See [Errors](https://dzbuild.com/api-docs/errors.md).
* ⚠️ **A key's `expires_at` is now enforced**: once it passes, the key answers `401`.
* ⚠️ **Copilot and connector keys cannot manage keys**: they get `403` on `/v1/keys`.
* ✨ **New keys carry `analytics:read`**; older keys keep the scopes they were minted with.
* ⚠️ **API v1 webhooks are signed with each webhook's own secret** (`X-DZ-Signature: t=<ts>,v1=<hmac>`), registration accepts `https://` only, `408` and `429` are retried, and any other 4xx or 3xx is dead-lettered at once. Webhooks registered earlier need registering again. See [Verifying signatures](https://dzbuild.com/api-docs/webhooks/verifying-signatures.md).
* ✨ **`PUT` accepts an optional `Idempotency-Key`** and then follows the same replay rules.
* ⚠️ **Idempotent replay runs on both hosts**, and reusing a key with a different body answers `422 idempotency_key_reuse`. See [Idempotency](https://dzbuild.com/api-docs/idempotency.md).
* ⚠️ **No CORS preflight**: an `OPTIONS` request gets the usual `401`, and the API is for server-to-server calls only.
* ⚠️ **Refusals now pass through the edge unchanged**: an app's `403`, or a `429`, from `api.dzbuild.app` arrives with its real status and body instead of `401`.

## v1.6 - 2026-09-26 (pilot)

* ✨ **WhatsApp messages over the API.** `GET /v1/whatsapp/templates` lists the platform-approved order templates with their approval status, `GET /v1/whatsapp/balance` reads the store's WhatsApp wallet, `GET /v1/whatsapp/messages` lists the messages sent to buyers, and `POST /v1/orders/{id}/whatsapp` sends one template to an order's buyer, paid from the wallet. The WhatsApp Sender addon must be active. The two new scopes are `whatsapp:read` and `whatsapp:send`. See [WhatsApp messages](https://dzbuild.com/api-docs/resources/whatsapp.md).
* ⚠️ **Scopes are frozen at mint time**, so a key created before this release does not have the two new scopes: create a new key to use them.
* ⚠️ **A retry after `402 no_credit` or a `422` needs a new `Idempotency-Key`.** The first answer is stored for 24 hours and replayed for the same key and body, so topping up the wallet or fixing the order changes nothing for the old key.
* ✨ **Each message in `GET /v1/whatsapp/messages` carries `billing`**: `charged` once WhatsApp billed it, `free` once its credit is back in the wallet, `null` while the outcome is not known yet.

## v1.5 - 2026-09-23 (pilot)

* ⚠️ Webhook retries now back off (1 min, 5 min, 30 min, 2 h) and stop after 5 failed attempts; timeouts and DNS/TLS failures are retried instead of dropped.

## v1.4 — 2026-09-05 (pilot)

* ✨ **Orders can be written.** `POST /v1/orders` creates an order, `PATCH /v1/orders/{id}` moves its status, `POST /v1/orders/{id}/cancel` cancels it, and `POST /v1/orders/{id}/send-to-delivery` hands one parcel to the store's courier. The two new scopes are `orders:write` and `delivery:send`.
* ⚠️ **Scopes are frozen at mint time**, so an existing key does not gain the new scopes: issue a new key, or reconnect the connector, to use them. A key created in the dashboard gets `orders:write`. Keys created in the dashboard or through `POST /v1/keys` never get `delivery:send`, so they cannot call `send-to-delivery`.
* ⚠️ **A courier send always needs a server-issued confirmation.** The first call answers `409 confirmation_required` with a single-use token and a summary naming the customer, phone, destination, total and courier; only that token sends the parcel. A `confirm: true` in the body is not accepted for this endpoint, from any caller. An order already sent is refused with `409 already_sent`.
* ⚠️ **Order money is now server-authoritative.** `shipping_cost` and `payment_fee` sent by the caller are ignored: the delivery cost comes from the store's own rate table for that wilaya and delivery type, and `discount` is capped at the order subtotal plus shipping. Item prices already worked this way.
* ✨ **`GET /v1/shipping/coverage`** answers whether the linked courier serves a commune and whether it has a stop desk in a wilaya, with the freshness of the courier's own data.
* ✨ **`GET /v1/shipping/providers`** now also reports a courier configured directly on the store rather than added from the provider list, plus `is_send_default`, `economic_available`, `synced_tier`, `stock_account`, `auto_validate` and `custom_name`. Credentials and endpoints are never returned.
* ✨ **`GET /v1/landing-pages/{id}/check`** reports what a buyer would hit on a page: an order form with no product, no order form, more than one, or a section pointing at a product from another store.
* ⚠️ **Publishing a broken landing page is refused.** `PATCH /v1/landing-pages/{id}` with `status: active` fails with `landing_page_has_no_product` when the page would take orders at zero. Editing a page that is already live still works, so a broken page can be fixed. A section write naming a `product_id` from another store is refused as a validation error.
* ⚠️ **`PATCH /v1/landing-page-sections/{id}` with `replace: true`** now re-seeds the section type's default settings under the object you send, so an omitted key falls back to its default instead of disappearing from the page.
* ✨ **`GET /v1/connection`** lists the stores one connector grant covers and which is active; `POST /v1/connection/active-store` moves the pointer. The pointer is not a permission: a store the merchant never approved has no key and cannot be selected.

## v1.3 — 2026-08-13 (pilot)

* ✨ **Self-service key management** — Enterprise store owners can now generate and revoke API keys from the merchant dashboard at **Settings → API** (`/dashboard/api`). Secrets are shown once, at creation.
* ⚠️ The per-minute rate limit is now enforced **per store**, shared across all of the store's keys (previously per key). The Enterprise ceiling is unchanged at **600 requests/minute**.
* ⚠️ A store can now hold at most **3 active keys** (down from 20), on every mint path — dashboard, `POST /v1/keys`, and support-issued. Revoking a key frees its slot.

## v1.2 — 2026-08-13 (pilot)

* ⚠️ **The API is now Enterprise-only.** Keys authenticate only while their store is on an **active Enterprise plan**; every other plan — and an expired Enterprise subscription — gets `403 forbidden` ("API access requires an active Enterprise plan"). New keys can be minted for Enterprise stores only. Existing keys on non-Enterprise stores stop working immediately but are not deleted: they resume the moment the store moves to (or renews) Enterprise, with nothing to re-issue.
* ⚠️ The legacy Free / Pro / Unlimited rate-limit tiers are retired. The Enterprise ceiling stays **600 requests/minute** per key with no monthly cap; per-store overrides from support still apply.

## v1.1 — 2026-08-12 (pilot)

* ✨ **Product images over the API** — `POST /v1/products/{id}/images` adds an image from a public `https` URL (DZBuild downloads, optimises and hosts it), `PATCH .../images/{image_id}` sets alt text / display order / primary, `DELETE .../images/{image_id}` removes one. Duplicate URLs are de-duplicated, the first image becomes primary automatically, max 20 images per product.
* ✨ **`PUT /v1/products/{id}/variants`** — create and manage variant groups, options and per-combination stock in one call (full replace). Per-option `price_adjustment`, `stock`, `sku`, `image_id` and `show_as_card` are now writable, and the stock mode flags are set for you.
* ✨ `GET /v1/products/{id}` now returns the **`combinations`** block plus the full option fields (`price_adjustment`, `sku`, `show_as_card`, `sort_order`, `is_active`) and image `alt_text`.
* ⚠️ **Breaking-ish:** `primary_image` and `images[].url` now return **full CDN URLs** instead of bare filenames. If your code prefixes them manually, remove that logic.

## v1.0.1 — 2026-05-02 (pilot)

* ✨ **`POST /v1/orders`** — create orders via API. Designed for custom themes, headless storefronts, mobile apps, and reseller automation. Server-authoritative line pricing; full variants support; idempotent.
* 📚 New guide: **Custom themes & storefronts** — end-to-end build, including catalog rendering, variants UI, cart, checkout, and webhook integration.
* 📚 New guide: **For resellers** — manage multiple client stores, bulk operations, white-labeling, billing models.
* 📚 New guide: **Environment & .env setup** — safe credential storage across Node, Python, PHP, Go, Vercel, Cloudflare, AWS, Docker/k8s, GitHub Actions.
* 📚 Expanded `Orders` reference — full variants documentation including per-variant stock, per-combination stock, cascading variants, image-text variants, multi-piece offers.

## v1.0 — 2026-04-30 (pilot)

* 🎉 Initial pilot launch.
* Per-key authentication, rate limiting and read caching.
* Read endpoints for store / products / orders / customers / landing-pages.
* Write endpoints with idempotency for products / orders / landing-pages.
* `/v1/signups` and `/v1/events` asynchronous ingest (`202 Accepted`).
* Outbound webhooks with automatic retries.
