Exchange is excluded from this API/CLI release. Any historical Exchange API or CLI example in this guide is unavailable; native portal behavior is outside this exclusion. See [current release scope](/developers/guides/release-scope?lang=en).

# Product and pricing administration

An internal administrator manages sales options and future catalogue prices for domains, Exchange and spam filters using the same product records as the portal. This explicitly requires `admin:read`/`admin:write`, the separate administration and sensitive feature flags, and recent passkey verification. A company owner, reseller or service connection does not gain internal administrator rights through this flow.

## Read and modify products

```bash
binadit login --scope account:read --scope admin:read --scope admin:write
binadit admin domains products list --account 123 --output json
binadit admin domains products get 12 --account 123 --output json
binadit admin domains products update 12 --account 123 \
  --version 'ETAG_UIT_GET' --set sell_price=12.50 --dry-run
binadit admin domains products update 12 --account 123 \
  --version 'ETAG_UIT_GET' --set sell_price=12.50
```

The API paths are `/api/v1/accounts/{account}/admin/{area}/products`, with `area` equal to `domains`, `exchange` or `spam`. `GET` supports `limit` from 1 to 100 and `cursor`; `GET /{product}` returns an `ETag`. `PATCH /{product}` requires that exact value in `If-Match` and an idempotency key. The CLI retains the intent across repeated requests; an unclear error is no reason for a new key.

The explicit fields are listed in OpenAPI: `name`, `sell_price`, `purchase_price`, `margin`, `important`, `active`, `wefact_product_code` and module-specific `details`. Sale and purchase amounts have at most two decimal places; margins at most four and no more than 9999.9999 percent. Amounts are non-negative and bounded. The currency remains EUR. Supplier data, secrets and arbitrary product details are not returned.

For domains, `details` includes only `period` (years), `enabled`, `fixedPrice` and `domicilie`. Extension names are normalised to lowercase without a leading dot. For Exchange, `mailboxQuota` (GB) and `period` (months) are available; a new product requires a quota. Names must be unique within a module. The identity of an existing extension remains fixed; retire an old option and create a new one to sell a different extension. The identity name of a spam product remains bound to the supplier product and cannot be changed through this API.

New domain and Exchange options use `POST` on the collection or `products create --from-file product.json`. `DELETE /{product}` retires an option from sale and retains the product for existing subscriptions and historical references. Use:

```bash
binadit admin exchange products retire 12 --account 123 \
  --version 'ETAG_UIT_GET' --confirm-product 12
```

This action does not delete mailboxes, subscriptions or customer data. Spam products come from the supplier catalogue; this administration flow does not add arbitrary new products.

## Default margin and synchronisation policy

```bash
binadit admin spam pricing-policy get --account 123 --output json
binadit admin spam pricing-policy update --account 123 \
  --version 'ETAG_UIT_GET' --set margin=30 --set apply_to_all=true --dry-run
```

The API uses `GET`/`PUT /admin/{area}/pricing-policy`. `margin` is required. `apply_to_all` is off by default: a new default margin then leaves existing product prices unchanged. With explicit `true`, future catalogue prices are recalculated using the configured rounding. Domains with `fixedPrice=Y` retain their fixed sale price. Existing billing agreements, subscriptions and invoices are not repriced.

You can also configure `rounding` (`none`, `cents`, `whole`, `charm`), `auto_update_sell_price`, `auto_create_products` and `auto_enable_products`. These fields control the existing supplier synchronisation; a policy change does not itself run a supplier sync. Read the policy again to receive the new ETag.

Reads do not initialise policy records. A dry-run also validates current permissions, versions, duplicate names, arithmetic limits and the complete intended change; it does not save a product, policy or operation. A missing version returns 428, an outdated version 412 and invalid input 422. Reload and review the changed configuration before executing again. An expected validation refusal does not consume an idempotency key.

This document describes implemented code; the feature matrix and release information remain authoritative for actual production acceptance and the other administration features, including Hosting provisioning, imports and ownership transfers.
