# Mozaik extension platform docs (full) > Extension API v0 — pre-1.0. Interfaces may change without notice until P2 seals the custom-code format. First-party extensions track HEAD. --- # Extension manifest Every extension is described by ONE manifest validated by `zExtensionManifest` (packages/core/src/extensions/manifest.ts). The taxonomy is closed: everything an extension can touch is a field here, and nothing else exists. First-party manifests are TypeScript objects validated at test time; `configSchema` is generated from the registry's Zod via `z.toJSONSchema`. **Runtime is a declared dependency** (ADR 0026 §2): `platform` = compiled into Mozaik's workers (first-party only — the schema refuses it for any other publisher); `sandbox` = sealed store-scoped worker (lands at P1; `extension.install` refuses it until then); `external` = standing service via webhooks (M11). Sync/async picks the interface contract, never the runtime. The machine-readable schema: [/schemas/manifest.schema.json](/schemas/manifest.schema.json). ## Fields | field | type | required | notes | |---|---|---|---| | `id` | string (/^[a-z0-9-]+\.[a-z0-9-]+$/) | yes | | | `version` | string (/^\d+\.\d+\.\d+$/) | yes | | | `publisher` | string (≤60 chars) | yes | | | `labelTR` | string (≤60 chars) | yes | | | `descriptionTR` | string (≤200 chars) | yes | | | `runtime` | "platform" \| "sandbox" \| "external" | yes | | | `scopes` | array (≤10) | yes | | | `pii` | object | no | | | ` buyer` | array<"name" \| "email" \| "phone" \| "address" \| "city" \| "ip"> (≤6) | yes | | | ` purposeTR` | string (≤200 chars) | yes | | | `provider` | object \| object \| object \| object \| object \| object \| object | no | | | `configSchema` | object | yes | | | `secrets` | array (≤8) | no | | | `webhooks` | array (≤4) | no | | | `jobs` | array (≤3) | no | | | `mail` | array (≤5) | no | | | `events` | array (≤5) | no | | | `ui` | object | no | | | ` settingsForm` | object | no | | | ` fieldLabelsTR` | object | yes | | | ` fieldHintsTR` | object | no | | | ` widgetHints` | object | no | | | ` hidden` | array (≤10) | no | | | ` order` | array (≤20) | no | | | ` selectOptions` | object | no | | | ` panels` | array (≤4) | no | | | ` actions` | array (≤3) | no | | | ` pages` | array (≤3) | no | | | ` themeSections` | array (≤3) | no | | | `csp` | object | no | | | ` scriptSrc` | array (≤4) | no | | | ` frameSrc` | array (≤4) | no | | | ` connectSrc` | array (≤4) | no | | | ` formAction` | array (≤4) | no | | ## Provider kinds The `provider` block is a discriminated union on `kind`: --- # Commands Every store mutation is a typed, permission-scoped, audit-logged command on `POST /v1/commands` (ADR 0005). The admin UI, the CLI, extensions, and agents call the SAME commands 1:1 — there is no privileged side channel. `GET /v1/commands` returns this list with JSON Schemas (scope-filtered for authenticated callers). Envelope: `{ command, storeId, payload, idempotencyKey (26-char ULID), dryRun?, deferBump? }`. Replays with the same key and payload return the original outcome; a different payload is an `idempotency_conflict`. ## scope `agents:admin` | command | summary | flags | |---|---|---| | [`token.mint`](/reference/commands/token.mint) | Mint an access token for this store (Ajan = propose-only, Geliştirici = direct). Owner sessions only; the plaintext is returned once by POST /v1/tokens, never audited. | | | [`token.revoke`](/reference/commands/token.revoke) | Revoke an access token immediately (its open proposal is withdrawn by the API layer). | destructive | | [`workspace.create`](/reference/commands/workspace.create) | Create a named workspace (an agent's own hidden draft; optionally a hosted sandbox container). | | | [`workspace.archive`](/reference/commands/workspace.archive) | Archive a workspace: its tokens are revoked and (optionally) its draft discarded. Open proposals stay reviewable. | destructive | ## scope `catalog:write` | command | summary | flags | |---|---|---| | [`catalog.category.create`](/reference/commands/catalog.category.create) | Create a category (flat compliance taxonomy; taksitCap = BDDK installment cap). | | | [`catalog.category.update`](/reference/commands/catalog.category.update) | Update a category's name, installment cap, or position. | | | [`catalog.category.archive`](/reference/commands/catalog.category.archive) | Archive a category (products keep their assignment; hidden from active lists). | destructive | | [`catalog.product.create`](/reference/commands/catalog.product.create) | Create a product. price/compareAt in kuruş; stock null = untracked; handle derived from title when omitted. | bumps generation | | [`catalog.product.update`](/reference/commands/catalog.product.update) | Patch product fields. Price and stock are NOT here — use catalog.price.set / catalog.stock.set. | bumps generation | | [`catalog.product.archive`](/reference/commands/catalog.product.archive) | Archive a product — removed from the storefront, history kept. | bumps generation, destructive | | [`catalog.price.set`](/reference/commands/catalog.price.set) | Set the price (kuruş) of a product (prd_) or variant (var_). compareAt only on products; null clears it. Variant price null = inherit product price. | bumps generation | | [`catalog.stock.adjust`](/reference/commands/catalog.stock.adjust) | Adjust stock by a ± delta (receiving, stocktake, connector sync). Fails on untracked items; expectedStock = compare-and-set. | | | [`catalog.stock.set`](/reference/commands/catalog.stock.set) | Set stock of a product (prd_) or variant (var_). null = untracked (always available). | | | [`catalog.digital.codes.add`](/reference/commands/catalog.digital.codes.add) | Add e-pin codes to a pooled digital product (sealed at rest; duplicates skipped). | | | [`catalog.digital.codes.void`](/reference/commands/catalog.digital.codes.void) | Void unused pool codes (oldest first) — they can never be allocated; nothing is deleted. | destructive | | [`catalog.product.setVariants`](/reference/commands/catalog.product.setVariants) | Wholesale-replace a product's variant STRUCTURE (order = array order). Existing variants update title/sku only; initial price/stock allowed solely for new variants. | bumps generation | | [`catalog.product.setMedia`](/reference/commands/catalog.product.setMedia) | Wholesale-replace a product's media list (order = array order; alt is per-usage). | bumps generation | | [`catalog.collection.create`](/reference/commands/catalog.collection.create) | Create a collection (handle derived from title when omitted). | bumps generation | | [`catalog.collection.update`](/reference/commands/catalog.collection.update) | Patch collection fields. | bumps generation | | [`catalog.collection.archive`](/reference/commands/catalog.collection.archive) | Archive a collection — its page 404s; product memberships are kept. | bumps generation, destructive | | [`catalog.collection.setProducts`](/reference/commands/catalog.collection.setProducts) | Wholesale-replace a collection's product list (order = array order = manual sort). | bumps generation | | [`catalog.media.commit`](/reference/commands/catalog.media.commit) | Commit a staged upload into the media library (variants generated async). | | | [`catalog.media.delete`](/reference/commands/catalog.media.delete) | Permanently delete an UNUSED media file (refused while referenced by catalog, live design, or the draft). | destructive | | [`catalog.product.setDigital`](/reference/commands/catalog.product.setDigital) | Set a product's digital/shipping posture: requiresShipping, delivered digital content, withdrawal exception. | | | [`metafield.set`](/reference/commands/metafield.set) | Set a metafield value on a product, variant, collection, or the store. | bumps generation | | [`metafield.unset`](/reference/commands/metafield.unset) | Remove a metafield value. | bumps generation | ## scope `checkout` | command | summary | flags | |---|---|---| | [`order.return.request`](/reference/commands/order.return.request) | Open a return/withdrawal (shopper from the order page, or merchant recording a phone/e-mail notice). | | | [`order.return.cancel`](/reference/commands/order.return.cancel) | Shopper withdraws their return request (only before the goods are received). | | | [`customer.identity.attach`](/reference/commands/customer.identity.attach) | Attach an order's customer to a Mozaik ID (explicit, shopper-initiated). | | | [`order.create`](/reference/commands/order.create) | Create an order from a completed checkout session (server-internal; shopper actor). | | ## scope `design:write` | command | summary | flags | |---|---|---| | [`design.publish`](/reference/commands/design.publish) | Publish the current draft as the next live version (validates strictly; flips the live pointer). | | | [`design.rollback`](/reference/commands/design.rollback) | Point the live site back at an earlier version (creates a new version entry; the draft is untouched). | destructive | ## scope `orders:write` | command | summary | flags | |---|---|---| | [`order.ship`](/reference/commands/order.ship) | Create a shipment for an order's shippable lines (a cargo extension or a manual carrier) — writes the VUK 509 forward fields. | | | [`order.shipment.track`](/reference/commands/order.shipment.track) | Record a carrier state transition (webhook/poll/manual) and aggregate the order's fulfillment status. | | | [`order.shipment.cancel`](/reference/commands/order.shipment.cancel) | Cancel a shipment before carrier handoff completes — resets the order to unfulfilled when nothing else is active. | destructive | | [`order.cod.collect`](/reference/commands/order.cod.collect) | Record that the carrier's COD cash for a shipped/delivered order reached the merchant — order becomes paid. | | | [`order.note.add`](/reference/commands/order.note.add) | Append a merchant note to an order's timeline. | | | [`order.return.approve`](/reference/commands/order.return.approve) | Approve a return — issues the instructions the shopper sees (snapshotted as evidence). | | | [`order.return.reject`](/reference/commands/order.return.reject) | Reject a return with a stated reason (the shopper mail carries the THH/consumer-court recourse). | destructive | | [`order.return.receive`](/reference/commands/order.return.receive) | Record the returned goods arriving — per-line condition; saleable lines restock (ledgered). | | | [`order.return.settle`](/reference/commands/order.return.settle) | Flip a return to refunded when its completed refunds cover the total (honest no-op otherwise). | | | [`order.line.epin.fulfill`](/reference/commands/order.line.epin.fulfill) | Allocate newly-added pool codes to a paid order line that was left short (sweep-driven). | | | [`customer.identity.unlink`](/reference/commands/customer.identity.unlink) | Clear a Mozaik ID pointer from this store's customers (revoke/erasure healing). | | | [`customer.anonymize`](/reference/commands/customer.anonymize) | Anonymize a customer row (KVKK request) — order snapshots stay under the statutory retention. | destructive | | [`order.transfer.confirm`](/reference/commands/order.transfer.confirm) | Confirm a bank-transfer payment arrived — order becomes paid; digital content is released. | | | [`order.cancel`](/reference/commands/order.cancel) | Cancel an unpaid order (pending_cod/awaiting_transfer) — restocks lines. | destructive | | [`order.refund.request`](/reference/commands/order.refund.request) | Reserve a refund amount on a paid order (intent row = the PSP conversation anchor). | | | [`order.refund.record`](/reference/commands/order.refund.record) | Finalize a refund intent after the PSP call (ok releases/settles the reservation). | | ## scope `proposals:approve` | command | summary | flags | |---|---|---| | [`proposal.approve`](/reference/commands/proposal.approve) | Approve a submitted proposal and start applying its items in order (store owner only). | | | [`proposal.reject`](/reference/commands/proposal.reject) | Reject a proposal without applying anything (store owner only). | | | [`proposal.item.skip`](/reference/commands/proposal.item.skip) | Skip one item of a paused proposal (store owner only) so the rest can continue. | | ## scope `proposals:write` | command | summary | flags | |---|---|---| | [`proposal.submit`](/reference/commands/proposal.submit) | Submit an open proposal for the store owner's review (the owner is mailed a link). | | | [`proposal.withdraw`](/reference/commands/proposal.withdraw) | Withdraw a proposal that has not been applied. | | ## scope `secrets:write` | command | summary | flags | |---|---|---| | [`secret.set`](/reference/commands/secret.set) | Store or rotate a write-only credential (PSP keys). Values are never readable back. | | | [`secret.delete`](/reference/commands/secret.delete) | Delete a stored credential. Extensions requiring it will refuse to enable until re-set. | destructive | ## scope `store:admin` | command | summary | flags | |---|---|---| | [`store.syncKv`](/reference/commands/store.syncKv) | Re-project store state (t:, l:, live b:, g:) from Postgres to KV; bumps generation once. | bumps generation | | [`settings.store.update`](/reference/commands/settings.store.update) | Update store settings (display name). Projected to KV tenant records. | | | [`settings.identity.update`](/reference/commands/settings.identity.update) | Set the legal merchant identity (6563 m.3) — complete rows only; rendered live in the footer. | bumps generation | | [`settings.flag.set`](/reference/commands/settings.flag.set) | Set or clear a store readiness flag (ETBİS beyanı, rehber kartları). | | | [`extension.install`](/reference/commands/extension.install) | Install an extension for this store (disabled until configured + enabled). Grants the manifest's scopes. | | | [`extension.uninstall`](/reference/commands/extension.uninstall) | Uninstall a disabled extension. Config is archived in the audit row; secrets and data are kept. | bumps generation, destructive | | [`extension.upgrade`](/reference/commands/extension.upgrade) | Upgrade an installed extension to the platform's current manifest version. | bumps generation | | [`extension.enable`](/reference/commands/extension.enable) | Enable an installed extension. Requires its secrets set and config valid. | bumps generation | | [`extension.disable`](/reference/commands/extension.disable) | Disable an extension. Its payment method / hooks / panels stop immediately. | bumps generation | | [`extension.configure`](/reference/commands/extension.configure) | Set an extension's configuration (validated against its schema). Does not enable it. | bumps generation | | [`extension.hook.deactivate`](/reference/commands/extension.hook.deactivate) | Deactivate (or reactivate) an extension's hooks without disabling the extension. | | | [`settings.checkout.update`](/reference/commands/settings.checkout.update) | Update checkout legal settings: delivery estimate, return-cargo carrier/cost disclosure. | | | [`metafield.definition.create`](/reference/commands/metafield.definition.create) | Define a typed custom field on products, variants, collections, or the store. | | | [`metafield.definition.update`](/reference/commands/metafield.definition.update) | Update a metafield definition's label, description, or validations (type/key are immutable). | | | [`metafield.definition.delete`](/reference/commands/metafield.definition.delete) | Delete a metafield definition AND every value stored under it. | bumps generation, destructive | --- # `store.syncKv` Re-project store state (t:, l:, live b:, g:) from Postgres to KV; bumps generation once. - **scope**: `store:admin` - **storefront-visible** (bumps the catalog generation): yes - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": {}, "additionalProperties": false } ``` --- # `design.publish` Publish the current draft as the next live version (validates strictly; flips the live pointer). - **scope**: `design:write` - **storefront-visible** (bumps the catalog generation): no - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `expectedHash` | string (/^[a-f0-9]{16}$/) | yes | | | `label` | string (≤120 chars) | no | | | `workspace` | string (/^(main\|ws_[0-9A-HJKMNP-TV-Z]{26})$/, default "main") | no | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "expectedHash": { "type": "string", "pattern": "^[a-f0-9]{16}$" }, "label": { "type": "string", "maxLength": 120 }, "workspace": { "default": "main", "type": "string", "pattern": "^(main|ws_[0-9A-HJKMNP-TV-Z]{26})$" } }, "required": [ "expectedHash" ], "additionalProperties": false } ``` --- # `design.rollback` Point the live site back at an earlier version (creates a new version entry; the draft is untouched). - **scope**: `design:write` - **storefront-visible** (bumps the catalog generation): no - **destructive**: yes — supports dryRun preview ## Payload | field | type | required | notes | |---|---|---|---| | `toSeq` | integer (1..9007199254740991) | yes | | | `label` | string (≤120 chars) | no | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "toSeq": { "type": "integer", "minimum": 1, "maximum": 9007199254740991 }, "label": { "type": "string", "maxLength": 120 } }, "required": [ "toSeq" ], "additionalProperties": false } ``` --- # `settings.store.update` Update store settings (display name). Projected to KV tenant records. - **scope**: `store:admin` - **storefront-visible** (bumps the catalog generation): no - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `patch` | object | yes | | | ` name` | string (≤80 chars) | no | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "patch": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 80 } }, "additionalProperties": false } }, "required": [ "patch" ], "additionalProperties": false } ``` --- # `settings.identity.update` Set the legal merchant identity (6563 m.3) — complete rows only; rendered live in the footer. - **scope**: `store:admin` - **storefront-visible** (bumps the catalog generation): yes - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `merchantType` | "tacir" \| "esnaf" | yes | | | `unvan` | string (≤200 chars) | no | | | `adSoyad` | string (≤100 chars) | no | | | `mersis` | string | no | | | `vergiNo` | string | no | | | `address` | string (≤400 chars) | yes | | | `il` | string | yes | | | `email` | string | yes | | | `phone` | string | yes | | | `kepAddress` | string | no | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "merchantType": { "type": "string", "enum": [ "tacir", "esnaf" ] }, "unvan": { "type": "string", "minLength": 1, "maxLength": 200 }, "adSoyad": { "type": "string", "minLength": 1, "maxLength": 100 }, "mersis": { "type": "string" }, "vergiNo": { "type": "string" }, "address": { "type": "string", "minLength": 1, "maxLength": 400 }, "il": { "type": "string" }, "email": { "type": "string" }, "phone": { "type": "string" }, "kepAddress": { "type": "string" } }, "required": [ "merchantType", "address", "il", "email", "phone" ], "additionalProperties": false } ``` --- # `settings.flag.set` Set or clear a store readiness flag (ETBİS beyanı, rehber kartları). - **scope**: `store:admin` - **storefront-visible** (bumps the catalog generation): no - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `key` | "etbis_confirmed" \| "psp_reviewed" \| "cargo_reviewed" \| "einvoice_reviewed" | yes | | | `done` | boolean | yes | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "key": { "type": "string", "enum": [ "etbis_confirmed", "psp_reviewed", "cargo_reviewed", "einvoice_reviewed" ] }, "done": { "type": "boolean" } }, "required": [ "key", "done" ], "additionalProperties": false } ``` --- # `catalog.category.create` Create a category (flat compliance taxonomy; taksitCap = BDDK installment cap). - **scope**: `catalog:write` - **storefront-visible** (bumps the catalog generation): no - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `name` | string (≤80 chars) | yes | | | `taksitCap` | integer (0..12) | no | | | `position` | integer (0..9007199254740991) | no | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 80 }, "taksitCap": { "type": "integer", "minimum": 0, "maximum": 12 }, "position": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 } }, "required": [ "name" ], "additionalProperties": false } ``` --- # `catalog.category.update` Update a category's name, installment cap, or position. - **scope**: `catalog:write` - **storefront-visible** (bumps the catalog generation): no - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `id` | string (/^cat_[a-zA-Z0-9]{1,32}$/) | yes | | | `patch` | object | yes | | | ` name` | string (≤80 chars) | no | | | ` taksitCap` | integer (0..12) \| null | no | | | ` position` | integer (0..9007199254740991) | no | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "id": { "type": "string", "pattern": "^cat_[a-zA-Z0-9]{1,32}$" }, "patch": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 80 }, "taksitCap": { "anyOf": [ { "type": "integer", "minimum": 0, "maximum": 12 }, { "type": "null" } ] }, "position": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 } }, "additionalProperties": false } }, "required": [ "id", "patch" ], "additionalProperties": false } ``` --- # `catalog.category.archive` Archive a category (products keep their assignment; hidden from active lists). - **scope**: `catalog:write` - **storefront-visible** (bumps the catalog generation): no - **destructive**: yes — supports dryRun preview ## Payload | field | type | required | notes | |---|---|---|---| | `id` | string (/^cat_[a-zA-Z0-9]{1,32}$/) | yes | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "id": { "type": "string", "pattern": "^cat_[a-zA-Z0-9]{1,32}$" } }, "required": [ "id" ], "additionalProperties": false } ``` --- # `catalog.product.create` Create a product. price/compareAt in kuruş; stock null = untracked; handle derived from title when omitted. - **scope**: `catalog:write` - **storefront-visible** (bumps the catalog generation): yes - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `title` | string (≤200 chars) | yes | | | `handle` | string (/^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/, ≤64 chars) | no | | | `descriptionMd` | string (≤20000 chars) | no | | | `categoryId` | string (/^cat_[a-zA-Z0-9]{1,32}$/) | no | | | `price` | integer (0..10000000000000) | yes | | | `compareAt` | integer (0..10000000000000) | no | | | `stock` | integer (0..9007199254740991) \| null | no | | | `badges` | array (≤10) | no | | | `status` | "draft" \| "active" | no | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "title": { "type": "string", "minLength": 1, "maxLength": 200 }, "handle": { "type": "string", "maxLength": 64, "pattern": "^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$" }, "descriptionMd": { "type": "string", "maxLength": 20000 }, "categoryId": { "type": "string", "pattern": "^cat_[a-zA-Z0-9]{1,32}$" }, "price": { "type": "integer", "minimum": 0, "maximum": 10000000000000 }, "compareAt": { "type": "integer", "minimum": 0, "maximum": 10000000000000 }, "stock": { "anyOf": [ { "type": "integer", "minimum": 0, "maximum": 9007199254740991 }, { "type": "null" } ] }, "badges": { "maxItems": 10, "type": "array", "items": { "type": "string", "pattern": "^[a-z0-9-]{1,24}$" } }, "status": { "type": "string", "enum": [ "draft", "active" ] } }, "required": [ "title", "price" ], "additionalProperties": false } ``` --- # `catalog.product.update` Patch product fields. Price and stock are NOT here — use catalog.price.set / catalog.stock.set. - **scope**: `catalog:write` - **storefront-visible** (bumps the catalog generation): yes - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `id` | string (/^prd_[a-zA-Z0-9]{1,32}$/) | yes | | | `patch` | object | yes | | | ` title` | string (≤200 chars) | no | | | ` handle` | string (/^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/, ≤64 chars) | no | | | ` descriptionMd` | string (≤20000 chars) | no | | | ` categoryId` | string (/^cat_[a-zA-Z0-9]{1,32}$/) \| null | no | | | ` badges` | array (≤10) | no | | | ` status` | "draft" \| "active" | no | | | ` lowStockThreshold` | integer (1..1000000) \| null | no | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "id": { "type": "string", "pattern": "^prd_[a-zA-Z0-9]{1,32}$" }, "patch": { "type": "object", "properties": { "title": { "type": "string", "minLength": 1, "maxLength": 200 }, "handle": { "type": "string", "maxLength": 64, "pattern": "^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$" }, "descriptionMd": { "type": "string", "maxLength": 20000 }, "categoryId": { "anyOf": [ { "type": "string", "pattern": "^cat_[a-zA-Z0-9]{1,32}$" }, { "type": "null" } ] }, "badges": { "maxItems": 10, "type": "array", "items": { "type": "string", "pattern": "^[a-z0-9-]{1,24}$" } }, "status": { "type": "string", "enum": [ "draft", "active" ] }, "lowStockThreshold": { "anyOf": [ { "type": "integer", "minimum": 1, "maximum": 1000000 }, { "type": "null" } ] } }, "additionalProperties": false } }, "required": [ "id", "patch" ], "additionalProperties": false } ``` --- # `catalog.product.archive` Archive a product — removed from the storefront, history kept. - **scope**: `catalog:write` - **storefront-visible** (bumps the catalog generation): yes - **destructive**: yes — supports dryRun preview ## Payload | field | type | required | notes | |---|---|---|---| | `id` | string (/^prd_[a-zA-Z0-9]{1,32}$/) | yes | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "id": { "type": "string", "pattern": "^prd_[a-zA-Z0-9]{1,32}$" } }, "required": [ "id" ], "additionalProperties": false } ``` --- # `catalog.price.set` Set the price (kuruş) of a product (prd_) or variant (var_). compareAt only on products; null clears it. Variant price null = inherit product price. - **scope**: `catalog:write` - **storefront-visible** (bumps the catalog generation): yes - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `id` | string (/^prd_[a-zA-Z0-9]{1,32}$/) \| string (/^var_[a-zA-Z0-9]{1,32}$/) | yes | | | `price` | integer (0..10000000000000) \| null | yes | | | `compareAt` | integer (0..10000000000000) \| null | no | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "id": { "anyOf": [ { "type": "string", "pattern": "^prd_[a-zA-Z0-9]{1,32}$" }, { "type": "string", "pattern": "^var_[a-zA-Z0-9]{1,32}$" } ] }, "price": { "anyOf": [ { "type": "integer", "minimum": 0, "maximum": 10000000000000 }, { "type": "null" } ] }, "compareAt": { "anyOf": [ { "type": "integer", "minimum": 0, "maximum": 10000000000000 }, { "type": "null" } ] } }, "required": [ "id", "price" ], "additionalProperties": false } ``` --- # `catalog.stock.adjust` Adjust stock by a ± delta (receiving, stocktake, connector sync). Fails on untracked items; expectedStock = compare-and-set. - **scope**: `catalog:write` - **storefront-visible** (bumps the catalog generation): no - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `id` | string (/^prd_[a-zA-Z0-9]{1,32}$/) \| string (/^var_[a-zA-Z0-9]{1,32}$/) | yes | | | `delta` | integer (-1000000..1000000) | yes | | | `reason` | "adjust" \| "correction" \| "return" \| "import" \| "sync" | no | | | `channel` | string (/^[a-z0-9_]{2,24}$/, default "storefront") | no | | | `refType` | string (≤32 chars) | no | | | `refId` | string (≤64 chars) | no | | | `note` | string (≤200 chars) | no | | | `expectedStock` | integer (0..9007199254740991) | no | | | `allowNegative` | boolean (default false) | no | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "id": { "anyOf": [ { "type": "string", "pattern": "^prd_[a-zA-Z0-9]{1,32}$" }, { "type": "string", "pattern": "^var_[a-zA-Z0-9]{1,32}$" } ] }, "delta": { "type": "integer", "minimum": -1000000, "maximum": 1000000 }, "reason": { "default": "adjust", "type": "string", "enum": [ "adjust", "correction", "return", "import", "sync" ] }, "channel": { "default": "storefront", "type": "string", "pattern": "^[a-z0-9_]{2,24}$" }, "refType": { "type": "string", "maxLength": 32 }, "refId": { "type": "string", "maxLength": 64 }, "note": { "type": "string", "minLength": 1, "maxLength": 200 }, "expectedStock": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 }, "allowNegative": { "default": false, "type": "boolean" } }, "required": [ "id", "delta" ], "additionalProperties": false } ``` --- # `catalog.stock.set` Set stock of a product (prd_) or variant (var_). null = untracked (always available). - **scope**: `catalog:write` - **storefront-visible** (bumps the catalog generation): no - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `id` | string (/^prd_[a-zA-Z0-9]{1,32}$/) \| string (/^var_[a-zA-Z0-9]{1,32}$/) | yes | | | `stock` | integer (0..9007199254740991) \| null | yes | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "id": { "anyOf": [ { "type": "string", "pattern": "^prd_[a-zA-Z0-9]{1,32}$" }, { "type": "string", "pattern": "^var_[a-zA-Z0-9]{1,32}$" } ] }, "stock": { "anyOf": [ { "type": "integer", "minimum": 0, "maximum": 9007199254740991 }, { "type": "null" } ] } }, "required": [ "id", "stock" ], "additionalProperties": false } ``` --- # `order.ship` Create a shipment for an order's shippable lines (a cargo extension or a manual carrier) — writes the VUK 509 forward fields. - **scope**: `orders:write` - **storefront-visible** (bumps the catalog generation): no - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `orderId` | string (/^ord_[a-zA-Z0-9]{1,32}$/) | yes | | | `shipmentId` | string (/^shp_[a-zA-Z0-9]{1,32}$/) | yes | | | `extensionId` | const "manual" \| string (/^[a-z0-9-]+\.[a-z0-9-]+$/) | yes | | | `carrierCode` | string (/^[A-Z0-9_]{2,32}$/) | no | | | `carrierName` | string (≤60 chars) | yes | | | `carrierVkn` | string (/^\d{10}$/) | no | | | `serviceCode` | string (≤32 chars) | no | | | `providerShipmentId` | string (≤80 chars) | no | | | `providerOfferId` | string (≤80 chars) | no | | | `trackingNumber` | string (≤64 chars) | no | | | `trackingUrl` | string (≤300 chars) | no | | | `labelUrl` | string (≤600 chars) | no | | | `labelFormat` | "PDF" \| "JPG" \| "ZPL" \| "ZPLII" | no | | | `parcel` | object | yes | | | ` weightGr` | integer (1..100000) | yes | | | ` lengthCm` | integer (1..200) | yes | | | ` widthCm` | integer (1..200) | yes | | | ` heightCm` | integer (1..200) | yes | | | `recipient` | object | yes | | | ` name` | string (≤120 chars) | yes | | | ` il` | string (≤40 chars) | yes | | | ` il_plaka` | string (/^\d{2}$/) | yes | | | ` ilce` | string (≤60 chars) | yes | | | ` address` | string (≤500 chars) | yes | | | `priceKurus` | integer (0..10000000000000) | no | | | `codAmountKurus` | integer (0..10000000000000) | no | | | `test` | boolean | yes | | | `lines` | array (≤50) | yes | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "orderId": { "type": "string", "pattern": "^ord_[a-zA-Z0-9]{1,32}$" }, "shipmentId": { "type": "string", "pattern": "^shp_[a-zA-Z0-9]{1,32}$" }, "extensionId": { "anyOf": [ { "type": "string", "const": "manual" }, { "type": "string", "pattern": "^[a-z0-9-]+\\.[a-z0-9-]+$" } ] }, "carrierCode": { "type": "string", "pattern": "^[A-Z0-9_]{2,32}$" }, "carrierName": { "type": "string", "minLength": 1, "maxLength": 60 }, "carrierVkn": { "type": "string", "pattern": "^\\d{10}$" }, "serviceCode": { "type": "string", "minLength": 1, "maxLength": 32 }, "providerShipmentId": { "type": "string", "minLength": 1, "maxLength": 80 }, "providerOfferId": { "type": "string", "minLength": 1, "maxLength": 80 }, "trackingNumber": { "type": "string", "minLength": 1, "maxLength": 64 }, "trackingUrl": { "type": "string", "minLength": 1, "maxLength": 300 }, "labelUrl": { "type": "string", "minLength": 1, "maxLength": 600 }, "labelFormat": { "type": "string", "enum": [ "PDF", "JPG", "ZPL", "ZPLII" ] }, "parcel": { "type": "object", "properties": { "weightGr": { "type": "integer", "minimum": 1, "maximum": 100000 }, "lengthCm": { "type": "integer", "minimum": 1, "maximum": 200 }, "widthCm": { "type": "integer", "minimum": 1, "maximum": 200 }, "heightCm": { "type": "integer", "minimum": 1, "maximum": 200 } }, "required": [ "weightGr", "lengthCm", "widthCm", "heightCm" ], "additionalProperties": false }, "recipient": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 120 }, "il": { "type": "string", "minLength": 1, "maxLength": 40 }, "il_plaka": { "type": "string", "pattern": "^\\d{2}$" }, "ilce": { "type": "string", "minLength": 1, "maxLength": 60 }, "address": { "type": "string", "minLength": 1, "maxLength": 500 } }, "required": [ "name", "il", "il_plaka", "ilce", "address" ], "additionalProperties": {} }, "priceKurus": { "type": "integer", "minimum": 0, "maximum": 10000000000000 }, "codAmountKurus": { "type": "integer", "minimum": 0, "maximum": 10000000000000 }, "test": { "type": "boolean" }, "lines": { "minItems": 1, "maxItems": 50, "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "pattern": "^shl_[a-zA-Z0-9]{1,32}$" }, "orderLineId": { "type": "string", "pattern": "^oln_[a-zA-Z0-9]{1,32}$" }, "qty": { "type": "integer", "minimum": 1, "maximum": 99 } }, "required": [ "id", "orderLineId", "qty" ], "additionalProperties": false } } }, "required": [ "orderId", "shipmentId", "extensionId", "carrierName", "parcel", "recipient", "test", "lines" ], "additionalProperties": false } ``` --- # `order.shipment.track` Record a carrier state transition (webhook/poll/manual) and aggregate the order's fulfillment status. - **scope**: `orders:write` - **storefront-visible** (bumps the catalog generation): no - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `shipmentId` | string (/^shp_[a-zA-Z0-9]{1,32}$/) | yes | | | `status` | "label_ready" \| "picked_up" \| "in_transit" \| "out_for_delivery" \| "delivered" \| "undelivered" \| "returned" | yes | | | `providerStatusCode` | string (≤60 chars) | no | | | `occurredAt` | string | no | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "shipmentId": { "type": "string", "pattern": "^shp_[a-zA-Z0-9]{1,32}$" }, "status": { "type": "string", "enum": [ "label_ready", "picked_up", "in_transit", "out_for_delivery", "delivered", "undelivered", "returned" ] }, "providerStatusCode": { "type": "string", "minLength": 1, "maxLength": 60 }, "occurredAt": { "type": "string" } }, "required": [ "shipmentId", "status" ], "additionalProperties": false } ``` --- # `order.shipment.cancel` Cancel a shipment before carrier handoff completes — resets the order to unfulfilled when nothing else is active. - **scope**: `orders:write` - **storefront-visible** (bumps the catalog generation): no - **destructive**: yes — supports dryRun preview ## Payload | field | type | required | notes | |---|---|---|---| | `shipmentId` | string (/^shp_[a-zA-Z0-9]{1,32}$/) | yes | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "shipmentId": { "type": "string", "pattern": "^shp_[a-zA-Z0-9]{1,32}$" } }, "required": [ "shipmentId" ], "additionalProperties": false } ``` --- # `order.cod.collect` Record that the carrier's COD cash for a shipped/delivered order reached the merchant — order becomes paid. - **scope**: `orders:write` - **storefront-visible** (bumps the catalog generation): no - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `orderId` | string (/^ord_[a-zA-Z0-9]{1,32}$/) | yes | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "orderId": { "type": "string", "pattern": "^ord_[a-zA-Z0-9]{1,32}$" } }, "required": [ "orderId" ], "additionalProperties": false } ``` --- # `order.note.add` Append a merchant note to an order's timeline. - **scope**: `orders:write` - **storefront-visible** (bumps the catalog generation): no - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `orderId` | string (/^ord_[a-zA-Z0-9]{1,32}$/) | yes | | | `noteId` | string (/^ont_[a-zA-Z0-9]{1,32}$/) | yes | | | `body` | string (≤2000 chars) | yes | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "orderId": { "type": "string", "pattern": "^ord_[a-zA-Z0-9]{1,32}$" }, "noteId": { "type": "string", "pattern": "^ont_[a-zA-Z0-9]{1,32}$" }, "body": { "type": "string", "minLength": 1, "maxLength": 2000 } }, "required": [ "orderId", "noteId", "body" ], "additionalProperties": false } ``` --- # `order.return.request` Open a return/withdrawal (shopper from the order page, or merchant recording a phone/e-mail notice). - **scope**: `checkout` - **storefront-visible** (bumps the catalog generation): no - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `orderId` | string (/^ord_[a-zA-Z0-9]{1,32}$/) | yes | | | `returnId` | string (/^ret_[a-zA-Z0-9]{1,32}$/) | yes | | | `kind` | "cayma" \| "ayipli" \| "gec_teslimat" | yes | | | `channel` | "shopper_page" \| "merchant_panel" \| "email" \| "phone" | yes | | | `lines` | array (≤50) | yes | | | `reasonCode` | "vazgectim" \| "beden_uyumsuz" \| "hasarli" \| "yanlis_urun" \| "diger" | no | | | `reasonText` | string (≤500 chars) | no | | | `refundIban` | string (/^TR\d{24}$/) | no | | | `fullOrder` | boolean | yes | | | `returnShippingKurus` | integer (0..10000000000000, default 0) | no | | | `notice` | object | yes | | | ` ip` | string (≤60 chars) | yes | | | ` ua` | string (≤300 chars) | yes | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "orderId": { "type": "string", "pattern": "^ord_[a-zA-Z0-9]{1,32}$" }, "returnId": { "type": "string", "pattern": "^ret_[a-zA-Z0-9]{1,32}$" }, "kind": { "type": "string", "enum": [ "cayma", "ayipli", "gec_teslimat" ] }, "channel": { "type": "string", "enum": [ "shopper_page", "merchant_panel", "email", "phone" ] }, "lines": { "minItems": 1, "maxItems": 50, "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "pattern": "^rtl_[a-zA-Z0-9]{1,32}$" }, "orderLineId": { "type": "string", "pattern": "^oln_[a-zA-Z0-9]{1,32}$" }, "qty": { "type": "integer", "minimum": 1, "maximum": 99 } }, "required": [ "id", "orderLineId", "qty" ], "additionalProperties": false } }, "reasonCode": { "type": "string", "enum": [ "vazgectim", "beden_uyumsuz", "hasarli", "yanlis_urun", "diger" ] }, "reasonText": { "type": "string", "minLength": 1, "maxLength": 500 }, "refundIban": { "type": "string", "pattern": "^TR\\d{24}$" }, "fullOrder": { "type": "boolean" }, "returnShippingKurus": { "default": 0, "type": "integer", "minimum": 0, "maximum": 10000000000000 }, "notice": { "type": "object", "properties": { "ip": { "type": "string", "maxLength": 60 }, "ua": { "type": "string", "maxLength": 300 } }, "required": [ "ip", "ua" ], "additionalProperties": false } }, "required": [ "orderId", "returnId", "kind", "channel", "lines", "fullOrder", "notice" ], "additionalProperties": false } ``` --- # `order.return.approve` Approve a return — issues the instructions the shopper sees (snapshotted as evidence). - **scope**: `orders:write` - **storefront-visible** (bumps the catalog generation): no - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `returnId` | string (/^ret_[a-zA-Z0-9]{1,32}$/) | yes | | | `instructions` | object | yes | | | ` address` | string (≤300 chars) | yes | | | ` carrierName` | string (≤60 chars) | no | | | ` costBearer` | "merchant" \| "consumer" | yes | | | ` costKurus` | integer (0..10000000000000) | no | | | ` note` | string (≤500 chars) | no | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "returnId": { "type": "string", "pattern": "^ret_[a-zA-Z0-9]{1,32}$" }, "instructions": { "type": "object", "properties": { "address": { "type": "string", "minLength": 1, "maxLength": 300 }, "carrierName": { "type": "string", "minLength": 1, "maxLength": 60 }, "costBearer": { "type": "string", "enum": [ "merchant", "consumer" ] }, "costKurus": { "type": "integer", "minimum": 0, "maximum": 10000000000000 }, "note": { "type": "string", "minLength": 1, "maxLength": 500 } }, "required": [ "address", "costBearer" ], "additionalProperties": false } }, "required": [ "returnId", "instructions" ], "additionalProperties": false } ``` --- # `order.return.reject` Reject a return with a stated reason (the shopper mail carries the THH/consumer-court recourse). - **scope**: `orders:write` - **storefront-visible** (bumps the catalog generation): no - **destructive**: yes — supports dryRun preview ## Payload | field | type | required | notes | |---|---|---|---| | `returnId` | string (/^ret_[a-zA-Z0-9]{1,32}$/) | yes | | | `rejectCode` | "kullanilmis" \| "eksik_parca" \| "istisna_kapsaminda" \| "sure_disi" \| "diger" | yes | | | `rejectText` | string (≤200 chars) | no | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "returnId": { "type": "string", "pattern": "^ret_[a-zA-Z0-9]{1,32}$" }, "rejectCode": { "type": "string", "enum": [ "kullanilmis", "eksik_parca", "istisna_kapsaminda", "sure_disi", "diger" ] }, "rejectText": { "type": "string", "minLength": 1, "maxLength": 200 } }, "required": [ "returnId", "rejectCode" ], "additionalProperties": false } ``` --- # `order.return.receive` Record the returned goods arriving — per-line condition; saleable lines restock (ledgered). - **scope**: `orders:write` - **storefront-visible** (bumps the catalog generation): no - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `returnId` | string (/^ret_[a-zA-Z0-9]{1,32}$/) | yes | | | `lines` | array (≤50) | yes | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "returnId": { "type": "string", "pattern": "^ret_[a-zA-Z0-9]{1,32}$" }, "lines": { "minItems": 1, "maxItems": 50, "type": "array", "items": { "type": "object", "properties": { "returnLineId": { "type": "string", "pattern": "^rtl_[a-zA-Z0-9]{1,32}$" }, "qtyReceived": { "type": "integer", "minimum": 0, "maximum": 99 }, "condition": { "type": "string", "enum": [ "saleable", "damaged", "missing" ] }, "restock": { "type": "boolean" } }, "required": [ "returnLineId", "qtyReceived", "condition", "restock" ], "additionalProperties": false } } }, "required": [ "returnId", "lines" ], "additionalProperties": false } ``` --- # `order.return.settle` Flip a return to refunded when its completed refunds cover the total (honest no-op otherwise). - **scope**: `orders:write` - **storefront-visible** (bumps the catalog generation): no - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `returnId` | string (/^ret_[a-zA-Z0-9]{1,32}$/) | yes | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "returnId": { "type": "string", "pattern": "^ret_[a-zA-Z0-9]{1,32}$" } }, "required": [ "returnId" ], "additionalProperties": false } ``` --- # `order.return.cancel` Shopper withdraws their return request (only before the goods are received). - **scope**: `checkout` - **storefront-visible** (bumps the catalog generation): no - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `returnId` | string (/^ret_[a-zA-Z0-9]{1,32}$/) | yes | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "returnId": { "type": "string", "pattern": "^ret_[a-zA-Z0-9]{1,32}$" } }, "required": [ "returnId" ], "additionalProperties": false } ``` --- # `catalog.digital.codes.add` Add e-pin codes to a pooled digital product (sealed at rest; duplicates skipped). - **scope**: `catalog:write` - **storefront-visible** (bumps the catalog generation): no - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `productId` | string (/^prd_[a-zA-Z0-9]{1,32}$/) | yes | | | `codes` | array (≤1000) | yes | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "productId": { "type": "string", "pattern": "^prd_[a-zA-Z0-9]{1,32}$" }, "codes": { "minItems": 1, "maxItems": 1000, "type": "array", "items": { "type": "string", "minLength": 4, "maxLength": 200 } } }, "required": [ "productId", "codes" ], "additionalProperties": false } ``` --- # `catalog.digital.codes.void` Void unused pool codes (oldest first) — they can never be allocated; nothing is deleted. - **scope**: `catalog:write` - **storefront-visible** (bumps the catalog generation): no - **destructive**: yes — supports dryRun preview ## Payload | field | type | required | notes | |---|---|---|---| | `productId` | string (/^prd_[a-zA-Z0-9]{1,32}$/) | yes | | | `count` | integer (1..1000) | yes | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "productId": { "type": "string", "pattern": "^prd_[a-zA-Z0-9]{1,32}$" }, "count": { "type": "integer", "minimum": 1, "maximum": 1000 } }, "required": [ "productId", "count" ], "additionalProperties": false } ``` --- # `order.line.epin.fulfill` Allocate newly-added pool codes to a paid order line that was left short (sweep-driven). - **scope**: `orders:write` - **storefront-visible** (bumps the catalog generation): no - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `orderLineId` | string (/^oln_[a-zA-Z0-9]{1,32}$/) | yes | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "orderLineId": { "type": "string", "pattern": "^oln_[a-zA-Z0-9]{1,32}$" } }, "required": [ "orderLineId" ], "additionalProperties": false } ``` --- # `customer.identity.attach` Attach an order's customer to a Mozaik ID (explicit, shopper-initiated). - **scope**: `checkout` - **storefront-visible** (bumps the catalog generation): no - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `orderId` | string (/^ord_[a-zA-Z0-9]{1,32}$/) | yes | | | `userId` | string (/^usr_[a-zA-Z0-9]{1,32}$/) | yes | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "orderId": { "type": "string", "pattern": "^ord_[a-zA-Z0-9]{1,32}$" }, "userId": { "type": "string", "pattern": "^usr_[a-zA-Z0-9]{1,32}$" } }, "required": [ "orderId", "userId" ], "additionalProperties": false } ``` --- # `customer.identity.unlink` Clear a Mozaik ID pointer from this store's customers (revoke/erasure healing). - **scope**: `orders:write` - **storefront-visible** (bumps the catalog generation): no - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `userId` | string (/^usr_[a-zA-Z0-9]{1,32}$/) | yes | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "userId": { "type": "string", "pattern": "^usr_[a-zA-Z0-9]{1,32}$" } }, "required": [ "userId" ], "additionalProperties": false } ``` --- # `customer.anonymize` Anonymize a customer row (KVKK request) — order snapshots stay under the statutory retention. - **scope**: `orders:write` - **storefront-visible** (bumps the catalog generation): no - **destructive**: yes — supports dryRun preview ## Payload | field | type | required | notes | |---|---|---|---| | `customerId` | string (/^cus_[a-zA-Z0-9]{1,32}$/) | yes | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "customerId": { "type": "string", "pattern": "^cus_[a-zA-Z0-9]{1,32}$" } }, "required": [ "customerId" ], "additionalProperties": false } ``` --- # `catalog.product.setVariants` Wholesale-replace a product's variant STRUCTURE (order = array order). Existing variants update title/sku only; initial price/stock allowed solely for new variants. - **scope**: `catalog:write` - **storefront-visible** (bumps the catalog generation): yes - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `productId` | string (/^prd_[a-zA-Z0-9]{1,32}$/) | yes | | | `variants` | array (≤100) | yes | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "productId": { "type": "string", "pattern": "^prd_[a-zA-Z0-9]{1,32}$" }, "variants": { "maxItems": 100, "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "pattern": "^var_[a-zA-Z0-9]{1,32}$" }, "title": { "type": "string", "minLength": 1, "maxLength": 80 }, "sku": { "type": "string", "minLength": 1, "maxLength": 48 }, "price": { "type": "integer", "minimum": 0, "maximum": 10000000000000 }, "stock": { "anyOf": [ { "type": "integer", "minimum": 0, "maximum": 9007199254740991 }, { "type": "null" } ] } }, "required": [ "title" ], "additionalProperties": false } } }, "required": [ "productId", "variants" ], "additionalProperties": false } ``` --- # `catalog.product.setMedia` Wholesale-replace a product's media list (order = array order; alt is per-usage). - **scope**: `catalog:write` - **storefront-visible** (bumps the catalog generation): yes - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `productId` | string (/^prd_[a-zA-Z0-9]{1,32}$/) | yes | | | `media` | array (≤20) | yes | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "productId": { "type": "string", "pattern": "^prd_[a-zA-Z0-9]{1,32}$" }, "media": { "maxItems": 20, "type": "array", "items": { "type": "object", "properties": { "mediaId": { "type": "string", "pattern": "^med_[a-zA-Z0-9]{1,32}$" }, "alt": { "type": "string", "maxLength": 200 } }, "required": [ "mediaId", "alt" ], "additionalProperties": false } } }, "required": [ "productId", "media" ], "additionalProperties": false } ``` --- # `catalog.collection.create` Create a collection (handle derived from title when omitted). - **scope**: `catalog:write` - **storefront-visible** (bumps the catalog generation): yes - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `title` | string (≤120 chars) | yes | | | `handle` | string (/^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/, ≤64 chars) | no | | | `descriptionMd` | string (≤20000 chars) | no | | | `imageMediaId` | string (/^med_[a-zA-Z0-9]{1,32}$/) | no | | | `imageAlt` | string (≤200 chars) | no | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "title": { "type": "string", "minLength": 1, "maxLength": 120 }, "handle": { "type": "string", "maxLength": 64, "pattern": "^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$" }, "descriptionMd": { "type": "string", "maxLength": 20000 }, "imageMediaId": { "type": "string", "pattern": "^med_[a-zA-Z0-9]{1,32}$" }, "imageAlt": { "type": "string", "maxLength": 200 } }, "required": [ "title" ], "additionalProperties": false } ``` --- # `catalog.collection.update` Patch collection fields. - **scope**: `catalog:write` - **storefront-visible** (bumps the catalog generation): yes - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `id` | string (/^col_[a-zA-Z0-9]{1,32}$/) | yes | | | `patch` | object | yes | | | ` title` | string (≤120 chars) | no | | | ` handle` | string (/^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/, ≤64 chars) | no | | | ` descriptionMd` | string (≤20000 chars) \| null | no | | | ` imageMediaId` | string (/^med_[a-zA-Z0-9]{1,32}$/) \| null | no | | | ` imageAlt` | string (≤200 chars) \| null | no | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "id": { "type": "string", "pattern": "^col_[a-zA-Z0-9]{1,32}$" }, "patch": { "type": "object", "properties": { "title": { "type": "string", "minLength": 1, "maxLength": 120 }, "handle": { "type": "string", "maxLength": 64, "pattern": "^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$" }, "descriptionMd": { "anyOf": [ { "type": "string", "maxLength": 20000 }, { "type": "null" } ] }, "imageMediaId": { "anyOf": [ { "type": "string", "pattern": "^med_[a-zA-Z0-9]{1,32}$" }, { "type": "null" } ] }, "imageAlt": { "anyOf": [ { "type": "string", "maxLength": 200 }, { "type": "null" } ] } }, "additionalProperties": false } }, "required": [ "id", "patch" ], "additionalProperties": false } ``` --- # `catalog.collection.archive` Archive a collection — its page 404s; product memberships are kept. - **scope**: `catalog:write` - **storefront-visible** (bumps the catalog generation): yes - **destructive**: yes — supports dryRun preview ## Payload | field | type | required | notes | |---|---|---|---| | `id` | string (/^col_[a-zA-Z0-9]{1,32}$/) | yes | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "id": { "type": "string", "pattern": "^col_[a-zA-Z0-9]{1,32}$" } }, "required": [ "id" ], "additionalProperties": false } ``` --- # `catalog.collection.setProducts` Wholesale-replace a collection's product list (order = array order = manual sort). - **scope**: `catalog:write` - **storefront-visible** (bumps the catalog generation): yes - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `id` | string (/^col_[a-zA-Z0-9]{1,32}$/) | yes | | | `productIds` | array (≤500) | yes | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "id": { "type": "string", "pattern": "^col_[a-zA-Z0-9]{1,32}$" }, "productIds": { "maxItems": 500, "type": "array", "items": { "type": "string", "pattern": "^prd_[a-zA-Z0-9]{1,32}$" } } }, "required": [ "id", "productIds" ], "additionalProperties": false } ``` --- # `catalog.media.commit` Commit a staged upload into the media library (variants generated async). - **scope**: `catalog:write` - **storefront-visible** (bumps the catalog generation): no - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `sha` | string (/^[a-f0-9]{64}$/) | yes | | | `mime` | "image/jpeg" \| "image/png" \| "image/webp" \| "image/gif" \| "image/svg+xml" | yes | | | `bytes` | integer (1..20971520) | yes | | | `width` | integer (1..20000) | no | | | `height` | integer (1..20000) | no | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "sha": { "type": "string", "pattern": "^[a-f0-9]{64}$" }, "mime": { "type": "string", "enum": [ "image/jpeg", "image/png", "image/webp", "image/gif", "image/svg+xml" ] }, "bytes": { "type": "integer", "minimum": 1, "maximum": 20971520 }, "width": { "type": "integer", "minimum": 1, "maximum": 20000 }, "height": { "type": "integer", "minimum": 1, "maximum": 20000 } }, "required": [ "sha", "mime", "bytes" ], "additionalProperties": false } ``` --- # `catalog.media.delete` Permanently delete an UNUSED media file (refused while referenced by catalog, live design, or the draft). - **scope**: `catalog:write` - **storefront-visible** (bumps the catalog generation): no - **destructive**: yes — supports dryRun preview ## Payload | field | type | required | notes | |---|---|---|---| | `id` | string (/^med_[a-zA-Z0-9]{1,32}$/) | yes | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "id": { "type": "string", "pattern": "^med_[a-zA-Z0-9]{1,32}$" } }, "required": [ "id" ], "additionalProperties": false } ``` --- # `secret.set` Store or rotate a write-only credential (PSP keys). Values are never readable back. - **scope**: `secrets:write` - **storefront-visible** (bumps the catalog generation): no - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `key` | string (/^[a-z0-9_]+(\.[a-z0-9_]+)*$/, ≤128 chars) | yes | | | `value` | string (≤4096 chars) | yes | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "key": { "type": "string", "maxLength": 128, "pattern": "^[a-z0-9_]+(\\.[a-z0-9_]+)*$" }, "value": { "type": "string", "minLength": 1, "maxLength": 4096 } }, "required": [ "key", "value" ], "additionalProperties": false } ``` --- # `secret.delete` Delete a stored credential. Extensions requiring it will refuse to enable until re-set. - **scope**: `secrets:write` - **storefront-visible** (bumps the catalog generation): no - **destructive**: yes — supports dryRun preview ## Payload | field | type | required | notes | |---|---|---|---| | `key` | string (/^[a-z0-9_]+(\.[a-z0-9_]+)*$/, ≤128 chars) | yes | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "key": { "type": "string", "maxLength": 128, "pattern": "^[a-z0-9_]+(\\.[a-z0-9_]+)*$" } }, "required": [ "key" ], "additionalProperties": false } ``` --- # `extension.install` Install an extension for this store (disabled until configured + enabled). Grants the manifest's scopes. - **scope**: `store:admin` - **storefront-visible** (bumps the catalog generation): no - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `extensionId` | string (/^[a-z0-9-]+\.[a-z0-9-]+$/) | yes | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "extensionId": { "type": "string", "pattern": "^[a-z0-9-]+\\.[a-z0-9-]+$" } }, "required": [ "extensionId" ], "additionalProperties": false } ``` --- # `extension.uninstall` Uninstall a disabled extension. Config is archived in the audit row; secrets and data are kept. - **scope**: `store:admin` - **storefront-visible** (bumps the catalog generation): yes - **destructive**: yes — supports dryRun preview ## Payload | field | type | required | notes | |---|---|---|---| | `extensionId` | string (/^[a-z0-9-]+\.[a-z0-9-]+$/) | yes | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "extensionId": { "type": "string", "pattern": "^[a-z0-9-]+\\.[a-z0-9-]+$" } }, "required": [ "extensionId" ], "additionalProperties": false } ``` --- # `extension.upgrade` Upgrade an installed extension to the platform's current manifest version. - **scope**: `store:admin` - **storefront-visible** (bumps the catalog generation): yes - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `extensionId` | string (/^[a-z0-9-]+\.[a-z0-9-]+$/) | yes | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "extensionId": { "type": "string", "pattern": "^[a-z0-9-]+\\.[a-z0-9-]+$" } }, "required": [ "extensionId" ], "additionalProperties": false } ``` --- # `extension.enable` Enable an installed extension. Requires its secrets set and config valid. - **scope**: `store:admin` - **storefront-visible** (bumps the catalog generation): yes - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `extensionId` | string (/^[a-z0-9-]+\.[a-z0-9-]+$/) | yes | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "extensionId": { "type": "string", "pattern": "^[a-z0-9-]+\\.[a-z0-9-]+$" } }, "required": [ "extensionId" ], "additionalProperties": false } ``` --- # `extension.disable` Disable an extension. Its payment method / hooks / panels stop immediately. - **scope**: `store:admin` - **storefront-visible** (bumps the catalog generation): yes - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `extensionId` | string (/^[a-z0-9-]+\.[a-z0-9-]+$/) | yes | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "extensionId": { "type": "string", "pattern": "^[a-z0-9-]+\\.[a-z0-9-]+$" } }, "required": [ "extensionId" ], "additionalProperties": false } ``` --- # `extension.configure` Set an extension's configuration (validated against its schema). Does not enable it. - **scope**: `store:admin` - **storefront-visible** (bumps the catalog generation): yes - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `extensionId` | string (/^[a-z0-9-]+\.[a-z0-9-]+$/) | yes | | | `config` | object | yes | | | `position` | integer (0..99) | no | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "extensionId": { "type": "string", "pattern": "^[a-z0-9-]+\\.[a-z0-9-]+$" }, "config": { "type": "object", "propertyNames": { "type": "string" }, "additionalProperties": {} }, "position": { "type": "integer", "minimum": 0, "maximum": 99 } }, "required": [ "extensionId", "config" ], "additionalProperties": false } ``` --- # `extension.hook.deactivate` Deactivate (or reactivate) an extension's hooks without disabling the extension. - **scope**: `store:admin` - **storefront-visible** (bumps the catalog generation): no - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `extensionId` | string (/^[a-z0-9-]+\.[a-z0-9-]+$/) | yes | | | `disabled` | boolean | yes | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "extensionId": { "type": "string", "pattern": "^[a-z0-9-]+\\.[a-z0-9-]+$" }, "disabled": { "type": "boolean" } }, "required": [ "extensionId", "disabled" ], "additionalProperties": false } ``` --- # `settings.checkout.update` Update checkout legal settings: delivery estimate, return-cargo carrier/cost disclosure. - **scope**: `store:admin` - **storefront-visible** (bumps the catalog generation): no - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `deliveryEstimateDays` | array \| null | no | | | `returnCarrierName` | string (≤60 chars) \| null | no | | | `returnCostBearer` | "merchant" \| "consumer" \| null | no | | | `returnCostKurus` | integer (0..10000000000000) \| null | no | | | `returnAddress` | string (≤300 chars) \| null | no | | | `returnInstructionsNote` | string (≤500 chars) \| null | no | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "deliveryEstimateDays": { "anyOf": [ { "type": "array", "prefixItems": [ { "type": "integer", "minimum": 1, "maximum": 30 }, { "type": "integer", "minimum": 1, "maximum": 30 } ] }, { "type": "null" } ] }, "returnCarrierName": { "anyOf": [ { "type": "string", "minLength": 1, "maxLength": 60 }, { "type": "null" } ] }, "returnCostBearer": { "anyOf": [ { "type": "string", "enum": [ "merchant", "consumer" ] }, { "type": "null" } ] }, "returnCostKurus": { "anyOf": [ { "type": "integer", "minimum": 0, "maximum": 10000000000000 }, { "type": "null" } ] }, "returnAddress": { "anyOf": [ { "type": "string", "minLength": 10, "maxLength": 300 }, { "type": "null" } ] }, "returnInstructionsNote": { "anyOf": [ { "type": "string", "minLength": 1, "maxLength": 500 }, { "type": "null" } ] } }, "additionalProperties": false } ``` --- # `order.create` Create an order from a completed checkout session (server-internal; shopper actor). - **scope**: `checkout` - **storefront-visible** (bumps the catalog generation): no - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `orderId` | string (/^ord_[a-zA-Z0-9]{1,32}$/) | yes | | | `customerId` | string (/^cus_[a-zA-Z0-9]{1,32}$/) | yes | | | `paymentId` | string (/^pay_[a-zA-Z0-9]{1,32}$/) | yes | | | `sessionId` | string (/^cos_[a-zA-Z0-9]{1,32}$/) | yes | | | `status` | "paid" \| "pending_cod" \| "awaiting_transfer" | yes | | | `method` | string (/^[a-z0-9_]{2,32}$/) | yes | | | `test` | boolean | yes | | | `guardStock` | boolean | yes | | | `channel` | string (/^[a-z0-9_]{2,24}$/, default "storefront") | no | | | `channelRef` | string (≤64 chars) | no | | | `legalTerms` | object (default {}) | no | | | `userId` | string (/^usr_[a-zA-Z0-9]{1,32}$/) | no | | | `email` | string (/^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$/, ≤200 chars) | yes | | | `emailNorm` | string (≤200 chars) | yes | | | `phone` | string (≤20 chars) | yes | | | `billingAddress` | object | yes | canonical-il address (M6 cargo keys on il_plaka) | | ` name` | string (≤120 chars) | yes | | | ` il` | string (≤40 chars) | yes | | | ` il_plaka` | string (/^\d{2}$/) | yes | | | ` ilce` | string (≤60 chars) | yes | | | ` address` | string (≤500 chars) | yes | | | `shippingAddress` | object \| null | yes | | | `requiresShipping` | boolean | yes | | | `lines` | array (≤50) | yes | | | `totals` | object | yes | | | ` itemsSubtotalKurus` | integer (0..10000000000000) | yes | | | ` shippingKurus` | integer (0..10000000000000) | yes | | | ` codFeeKurus` | integer (0..10000000000000) | yes | | | ` discountKurus` | integer (0..10000000000000) | yes | | | ` totalKurus` | integer (0..10000000000000) | yes | | | `saleUrl` | string (≤300 chars) | yes | | | `taksitCount` | integer (1..24) | no | | | `pspPaymentId` | string (≤80 chars) | no | | | `paidAt` | string | no | | | `attemptId` | string (/^pat_[a-zA-Z0-9]{1,32}$/) | no | | | `consents` | object | yes | | | ` docSha256s` | array (≤2) | yes | | | ` acceptedAt` | string | yes | | | ` ip` | string (≤60 chars) | yes | | | ` ua` | string (≤300 chars) | yes | | | ` digitalWaiver` | object \| null | yes | | | `digitalWaiver` | boolean | yes | | | `docs` | array (≤2) | yes | | | `accessTokenHash` | string (/^[0-9a-f]{64}$/) | yes | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "orderId": { "type": "string", "pattern": "^ord_[a-zA-Z0-9]{1,32}$" }, "customerId": { "type": "string", "pattern": "^cus_[a-zA-Z0-9]{1,32}$" }, "paymentId": { "type": "string", "pattern": "^pay_[a-zA-Z0-9]{1,32}$" }, "sessionId": { "type": "string", "pattern": "^cos_[a-zA-Z0-9]{1,32}$" }, "status": { "type": "string", "enum": [ "paid", "pending_cod", "awaiting_transfer" ] }, "method": { "type": "string", "pattern": "^[a-z0-9_]{2,32}$" }, "test": { "type": "boolean" }, "guardStock": { "type": "boolean" }, "channel": { "default": "storefront", "type": "string", "pattern": "^[a-z0-9_]{2,24}$" }, "channelRef": { "type": "string", "minLength": 1, "maxLength": 64 }, "legalTerms": { "default": {}, "type": "object", "propertyNames": { "type": "string" }, "additionalProperties": {} }, "userId": { "type": "string", "pattern": "^usr_[a-zA-Z0-9]{1,32}$" }, "email": { "type": "string", "maxLength": 200, "format": "email", "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$" }, "emailNorm": { "type": "string", "maxLength": 200 }, "phone": { "type": "string", "minLength": 1, "maxLength": 20 }, "billingAddress": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 120 }, "il": { "type": "string", "minLength": 1, "maxLength": 40 }, "il_plaka": { "type": "string", "pattern": "^\\d{2}$" }, "ilce": { "type": "string", "minLength": 1, "maxLength": 60 }, "address": { "type": "string", "minLength": 1, "maxLength": 500 } }, "required": [ "name", "il", "il_plaka", "ilce", "address" ], "additionalProperties": {}, "description": "canonical-il address (M6 cargo keys on il_plaka)" }, "shippingAddress": { "anyOf": [ { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 120 }, "il": { "type": "string", "minLength": 1, "maxLength": 40 }, "il_plaka": { "type": "string", "pattern": "^\\d{2}$" }, "ilce": { "type": "string", "minLength": 1, "maxLength": 60 }, "address": { "type": "string", "minLength": 1, "maxLength": 500 } }, "required": [ "name", "il", "il_plaka", "ilce", "address" ], "additionalProperties": {}, "description": "canonical-il address (M6 cargo keys on il_plaka)" }, { "type": "null" } ] }, "requiresShipping": { "type": "boolean" }, "lines": { "minItems": 1, "maxItems": 50, "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "pattern": "^oln_[a-zA-Z0-9]{1,32}$" }, "productId": { "type": "string", "pattern": "^prd_[a-zA-Z0-9]{1,32}$" }, "variantId": { "type": "string", "pattern": "^var_[a-zA-Z0-9]{1,32}$" }, "qty": { "type": "integer", "minimum": 1, "maximum": 99 }, "title": { "type": "string", "minLength": 1, "maxLength": 200 }, "variantTitle": { "type": "string", "minLength": 1, "maxLength": 120 }, "sku": { "type": "string", "minLength": 1, "maxLength": 80 }, "unitPriceKurus": { "type": "integer", "minimum": 0, "maximum": 10000000000000 }, "lineTotalKurus": { "type": "integer", "minimum": 0, "maximum": 10000000000000 }, "compareAtKurus": { "type": "integer", "minimum": 0, "maximum": 10000000000000 }, "lowest30dKurus": { "type": "integer", "minimum": 0, "maximum": 10000000000000 }, "requiresShipping": { "type": "boolean" }, "withdrawalException": { "type": "string", "maxLength": 40 }, "exceptionAvailable": { "type": "boolean" }, "taksitCapApplied": { "type": "integer", "minimum": 0, "maximum": 24 } }, "required": [ "id", "productId", "qty", "title", "unitPriceKurus", "lineTotalKurus", "requiresShipping" ], "additionalProperties": false } }, "totals": { "type": "object", "properties": { "itemsSubtotalKurus": { "type": "integer", "minimum": 0, "maximum": 10000000000000 }, "shippingKurus": { "type": "integer", "minimum": 0, "maximum": 10000000000000 }, "codFeeKurus": { "type": "integer", "minimum": 0, "maximum": 10000000000000 }, "discountKurus": { "type": "integer", "minimum": 0, "maximum": 10000000000000 }, "totalKurus": { "type": "integer", "minimum": 0, "maximum": 10000000000000 } }, "required": [ "itemsSubtotalKurus", "shippingKurus", "codFeeKurus", "discountKurus", "totalKurus" ], "additionalProperties": false }, "saleUrl": { "type": "string", "minLength": 1, "maxLength": 300 }, "taksitCount": { "type": "integer", "minimum": 1, "maximum": 24 }, "pspPaymentId": { "type": "string", "minLength": 1, "maxLength": 80 }, "paidAt": { "type": "string" }, "attemptId": { "type": "string", "pattern": "^pat_[a-zA-Z0-9]{1,32}$" }, "consents": { "type": "object", "properties": { "docSha256s": { "minItems": 2, "maxItems": 2, "type": "array", "items": { "type": "string", "pattern": "^[0-9a-f]{64}$" } }, "acceptedAt": { "type": "string" }, "ip": { "type": "string", "maxLength": 60 }, "ua": { "type": "string", "maxLength": 300 }, "digitalWaiver": { "anyOf": [ { "type": "object", "properties": { "at": { "type": "string" }, "labelSha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" } }, "required": [ "at", "labelSha256" ], "additionalProperties": false }, { "type": "null" } ] } }, "required": [ "docSha256s", "acceptedAt", "ip", "ua", "digitalWaiver" ], "additionalProperties": false }, "digitalWaiver": { "type": "boolean" }, "docs": { "minItems": 2, "maxItems": 2, "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "pattern": "^doc_[a-zA-Z0-9]{1,32}$" }, "kind": { "type": "string", "enum": [ "on_bilgilendirme", "mesafeli_sozlesme" ] }, "bodyHtml": { "type": "string", "minLength": 100, "maxLength": 400000 }, "sha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" }, "shownAt": { "type": "string" }, "acceptedAt": { "type": "string" } }, "required": [ "id", "kind", "bodyHtml", "sha256", "shownAt", "acceptedAt" ], "additionalProperties": false } }, "accessTokenHash": { "type": "string", "pattern": "^[0-9a-f]{64}$" } }, "required": [ "orderId", "customerId", "paymentId", "sessionId", "status", "method", "test", "guardStock", "email", "emailNorm", "phone", "billingAddress", "shippingAddress", "requiresShipping", "lines", "totals", "saleUrl", "consents", "digitalWaiver", "docs", "accessTokenHash" ], "additionalProperties": false } ``` --- # `order.transfer.confirm` Confirm a bank-transfer payment arrived — order becomes paid; digital content is released. - **scope**: `orders:write` - **storefront-visible** (bumps the catalog generation): no - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `orderId` | string (/^ord_[a-zA-Z0-9]{1,32}$/) | yes | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "orderId": { "type": "string", "pattern": "^ord_[a-zA-Z0-9]{1,32}$" } }, "required": [ "orderId" ], "additionalProperties": false } ``` --- # `order.cancel` Cancel an unpaid order (pending_cod/awaiting_transfer) — restocks lines. - **scope**: `orders:write` - **storefront-visible** (bumps the catalog generation): no - **destructive**: yes — supports dryRun preview ## Payload | field | type | required | notes | |---|---|---|---| | `orderId` | string (/^ord_[a-zA-Z0-9]{1,32}$/) | yes | | | `reason` | "merchant" \| "expired" | yes | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "orderId": { "type": "string", "pattern": "^ord_[a-zA-Z0-9]{1,32}$" }, "reason": { "type": "string", "enum": [ "merchant", "expired" ] } }, "required": [ "orderId", "reason" ], "additionalProperties": false } ``` --- # `order.refund.request` Reserve a refund amount on a paid order (intent row = the PSP conversation anchor). - **scope**: `orders:write` - **storefront-visible** (bumps the catalog generation): no - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `orderId` | string (/^ord_[a-zA-Z0-9]{1,32}$/) | yes | | | `refundId` | string (/^ref_[a-zA-Z0-9]{1,32}$/) | yes | | | `amountKurus` | integer (0..10000000000000) | yes | | | `reason` | string (≤200 chars) | no | | | `returnId` | string (/^ret_[a-zA-Z0-9]{1,32}$/) | no | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "orderId": { "type": "string", "pattern": "^ord_[a-zA-Z0-9]{1,32}$" }, "refundId": { "type": "string", "pattern": "^ref_[a-zA-Z0-9]{1,32}$" }, "amountKurus": { "type": "integer", "minimum": 0, "maximum": 10000000000000 }, "reason": { "type": "string", "minLength": 1, "maxLength": 200 }, "returnId": { "type": "string", "pattern": "^ret_[a-zA-Z0-9]{1,32}$" } }, "required": [ "orderId", "refundId", "amountKurus" ], "additionalProperties": false } ``` --- # `order.refund.record` Finalize a refund intent after the PSP call (ok releases/settles the reservation). - **scope**: `orders:write` - **storefront-visible** (bumps the catalog generation): no - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `refundId` | string (/^ref_[a-zA-Z0-9]{1,32}$/) | yes | | | `ok` | boolean | yes | | | `providerRefundId` | string (≤80 chars) | no | | | `errorCode` | string (≤60 chars) | no | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "refundId": { "type": "string", "pattern": "^ref_[a-zA-Z0-9]{1,32}$" }, "ok": { "type": "boolean" }, "providerRefundId": { "type": "string", "minLength": 1, "maxLength": 80 }, "errorCode": { "type": "string", "minLength": 1, "maxLength": 60 } }, "required": [ "refundId", "ok" ], "additionalProperties": false } ``` --- # `catalog.product.setDigital` Set a product's digital/shipping posture: requiresShipping, delivered digital content, withdrawal exception. - **scope**: `catalog:write` - **storefront-visible** (bumps the catalog generation): no - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `id` | string (/^prd_[a-zA-Z0-9]{1,32}$/) | yes | | | `requiresShipping` | boolean | yes | | | `digitalContent` | string (≤10000 chars) \| null | no | | | `withdrawalException` | string (/^[a-z_]{2,40}$/) \| null | no | | | `deliveryMode` | "shared" \| "pool" | no | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "id": { "type": "string", "pattern": "^prd_[a-zA-Z0-9]{1,32}$" }, "requiresShipping": { "type": "boolean" }, "digitalContent": { "anyOf": [ { "type": "string", "minLength": 1, "maxLength": 10000 }, { "type": "null" } ] }, "withdrawalException": { "anyOf": [ { "type": "string", "pattern": "^[a-z_]{2,40}$" }, { "type": "null" } ] }, "deliveryMode": { "type": "string", "enum": [ "shared", "pool" ] } }, "required": [ "id", "requiresShipping" ], "additionalProperties": false } ``` --- # `metafield.definition.create` Define a typed custom field on products, variants, collections, or the store. - **scope**: `store:admin` - **storefront-visible** (bumps the catalog generation): no - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `ownerType` | "product" \| "variant" \| "collection" \| "store" | yes | | | `namespace` | string (/^(custom\|app\.[a-z0-9-]{2,32})$/) | yes | | | `key` | string (/^[a-z0-9_]{1,64}$/) | yes | | | `valueType` | "text" \| "integer" \| "decimal" \| "boolean" \| "date" \| "json" | yes | | | `labelTR` | string (≤100 chars) | yes | | | `descriptionTR` | string (≤500 chars) | no | | | `validations` | object | no | | | ` min` | number | no | | | ` max` | number | no | | | ` maxLength` | integer (..65536) | no | | | ` regex` | string (≤200 chars) | no | | | ` choices` | array (≤50) | no | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "ownerType": { "type": "string", "enum": [ "product", "variant", "collection", "store" ] }, "namespace": { "type": "string", "pattern": "^(custom|app\\.[a-z0-9-]{2,32})$" }, "key": { "type": "string", "pattern": "^[a-z0-9_]{1,64}$" }, "valueType": { "type": "string", "enum": [ "text", "integer", "decimal", "boolean", "date", "json" ] }, "labelTR": { "type": "string", "minLength": 1, "maxLength": 100 }, "descriptionTR": { "type": "string", "maxLength": 500 }, "validations": { "type": "object", "properties": { "min": { "type": "number" }, "max": { "type": "number" }, "maxLength": { "type": "integer", "exclusiveMinimum": 0, "maximum": 65536 }, "regex": { "type": "string", "maxLength": 200 }, "choices": { "minItems": 1, "maxItems": 50, "type": "array", "items": { "type": "string", "maxLength": 100 } } }, "additionalProperties": false } }, "required": [ "ownerType", "namespace", "key", "valueType", "labelTR" ], "additionalProperties": false } ``` --- # `metafield.definition.update` Update a metafield definition's label, description, or validations (type/key are immutable). - **scope**: `store:admin` - **storefront-visible** (bumps the catalog generation): no - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `id` | string (/^mfd_[0-9A-HJKMNP-TV-Z]{26}$/) | yes | | | `patch` | object | yes | | | ` labelTR` | string (≤100 chars) | no | | | ` descriptionTR` | string (≤500 chars) | no | | | ` validations` | object | no | | | ` min` | number | no | | | ` max` | number | no | | | ` maxLength` | integer (..65536) | no | | | ` regex` | string (≤200 chars) | no | | | ` choices` | array (≤50) | no | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "id": { "type": "string", "pattern": "^mfd_[0-9A-HJKMNP-TV-Z]{26}$" }, "patch": { "type": "object", "properties": { "labelTR": { "type": "string", "minLength": 1, "maxLength": 100 }, "descriptionTR": { "type": "string", "maxLength": 500 }, "validations": { "type": "object", "properties": { "min": { "type": "number" }, "max": { "type": "number" }, "maxLength": { "type": "integer", "exclusiveMinimum": 0, "maximum": 65536 }, "regex": { "type": "string", "maxLength": 200 }, "choices": { "minItems": 1, "maxItems": 50, "type": "array", "items": { "type": "string", "maxLength": 100 } } }, "additionalProperties": false } }, "additionalProperties": false } }, "required": [ "id", "patch" ], "additionalProperties": false } ``` --- # `metafield.definition.delete` Delete a metafield definition AND every value stored under it. - **scope**: `store:admin` - **storefront-visible** (bumps the catalog generation): yes - **destructive**: yes — supports dryRun preview ## Payload | field | type | required | notes | |---|---|---|---| | `id` | string (/^mfd_[0-9A-HJKMNP-TV-Z]{26}$/) | yes | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "id": { "type": "string", "pattern": "^mfd_[0-9A-HJKMNP-TV-Z]{26}$" } }, "required": [ "id" ], "additionalProperties": false } ``` --- # `metafield.set` Set a metafield value on a product, variant, collection, or the store. - **scope**: `catalog:write` - **storefront-visible** (bumps the catalog generation): yes - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `ownerType` | "product" \| "variant" \| "collection" \| "store" | yes | | | `ownerId` | string (≤64 chars) | yes | | | `namespace` | string (/^(custom\|app\.[a-z0-9-]{2,32})$/) | yes | | | `key` | string (/^[a-z0-9_]{1,64}$/) | yes | | | `value` | object | yes | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "ownerType": { "type": "string", "enum": [ "product", "variant", "collection", "store" ] }, "ownerId": { "type": "string", "minLength": 1, "maxLength": 64 }, "namespace": { "type": "string", "pattern": "^(custom|app\\.[a-z0-9-]{2,32})$" }, "key": { "type": "string", "pattern": "^[a-z0-9_]{1,64}$" }, "value": {} }, "required": [ "ownerType", "ownerId", "namespace", "key", "value" ], "additionalProperties": false } ``` --- # `metafield.unset` Remove a metafield value. - **scope**: `catalog:write` - **storefront-visible** (bumps the catalog generation): yes - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `ownerType` | "product" \| "variant" \| "collection" \| "store" | yes | | | `ownerId` | string (≤64 chars) | yes | | | `namespace` | string (/^(custom\|app\.[a-z0-9-]{2,32})$/) | yes | | | `key` | string (/^[a-z0-9_]{1,64}$/) | yes | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "ownerType": { "type": "string", "enum": [ "product", "variant", "collection", "store" ] }, "ownerId": { "type": "string", "minLength": 1, "maxLength": 64 }, "namespace": { "type": "string", "pattern": "^(custom|app\\.[a-z0-9-]{2,32})$" }, "key": { "type": "string", "pattern": "^[a-z0-9_]{1,64}$" } }, "required": [ "ownerType", "ownerId", "namespace", "key" ], "additionalProperties": false } ``` --- # `token.mint` Mint an access token for this store (Ajan = propose-only, Geliştirici = direct). Owner sessions only; the plaintext is returned once by POST /v1/tokens, never audited. - **scope**: `agents:admin` - **storefront-visible** (bumps the catalog generation): no - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `kind` | "agent" \| "developer" | yes | | | `label` | string (≤60 chars) | yes | | | `scopes` | array (≤10) | yes | | | `expiresInDays` | integer (1..365, default 90) | no | | | `rateLimitPerMin` | integer (10..1000) | no | | | `tokenHash` | string (/^[a-f0-9]{64}$/) | yes | | | `prefix` | string (/^[A-Za-z0-9_-]{8}$/) | yes | | | `workspaceId` | string (/^ws_[0-9A-HJKMNP-TV-Z]{26}$/) | no | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "kind": { "type": "string", "enum": [ "agent", "developer" ] }, "label": { "type": "string", "minLength": 1, "maxLength": 60 }, "scopes": { "minItems": 1, "maxItems": 10, "type": "array", "items": { "type": "string", "pattern": "^[a-z]+(:[a-z]+)?$" } }, "expiresInDays": { "default": 90, "type": "integer", "minimum": 1, "maximum": 365 }, "rateLimitPerMin": { "type": "integer", "minimum": 10, "maximum": 1000 }, "tokenHash": { "type": "string", "pattern": "^[a-f0-9]{64}$" }, "prefix": { "type": "string", "pattern": "^[A-Za-z0-9_-]{8}$" }, "workspaceId": { "type": "string", "pattern": "^ws_[0-9A-HJKMNP-TV-Z]{26}$" } }, "required": [ "kind", "label", "scopes", "tokenHash", "prefix" ], "additionalProperties": false } ``` --- # `token.revoke` Revoke an access token immediately (its open proposal is withdrawn by the API layer). - **scope**: `agents:admin` - **storefront-visible** (bumps the catalog generation): no - **destructive**: yes — supports dryRun preview ## Payload | field | type | required | notes | |---|---|---|---| | `id` | string (/^tok_[0-9A-HJKMNP-TV-Z]{26}$/) | yes | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "id": { "type": "string", "pattern": "^tok_[0-9A-HJKMNP-TV-Z]{26}$" } }, "required": [ "id" ], "additionalProperties": false } ``` --- # `workspace.create` Create a named workspace (an agent's own hidden draft; optionally a hosted sandbox container). - **scope**: `agents:admin` - **storefront-visible** (bumps the catalog generation): no - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `name` | string (≤60 chars) | yes | | | `kind` | "agent" \| "developer" | no | | | `hosted` | boolean (default false) | no | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 60 }, "kind": { "default": "agent", "type": "string", "enum": [ "agent", "developer" ] }, "hosted": { "default": false, "type": "boolean" } }, "required": [ "name" ], "additionalProperties": false } ``` --- # `workspace.archive` Archive a workspace: its tokens are revoked and (optionally) its draft discarded. Open proposals stay reviewable. - **scope**: `agents:admin` - **storefront-visible** (bumps the catalog generation): no - **destructive**: yes — supports dryRun preview ## Payload | field | type | required | notes | |---|---|---|---| | `workspaceId` | string (/^ws_[0-9A-HJKMNP-TV-Z]{26}$/) | yes | | | `discardDraft` | boolean (default false) | no | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "workspaceId": { "type": "string", "pattern": "^ws_[0-9A-HJKMNP-TV-Z]{26}$" }, "discardDraft": { "default": false, "type": "boolean" } }, "required": [ "workspaceId" ], "additionalProperties": false } ``` --- # `proposal.submit` Submit an open proposal for the store owner's review (the owner is mailed a link). - **scope**: `proposals:write` - **storefront-visible** (bumps the catalog generation): no - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `proposalId` | string (/^prp_[0-9A-HJKMNP-TV-Z]{26}$/) | yes | | | `title` | string (≤120 chars) | yes | | | `summary` | string (≤2000 chars, default "") | no | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "proposalId": { "type": "string", "pattern": "^prp_[0-9A-HJKMNP-TV-Z]{26}$" }, "title": { "type": "string", "minLength": 1, "maxLength": 120 }, "summary": { "default": "", "type": "string", "maxLength": 2000 } }, "required": [ "proposalId", "title" ], "additionalProperties": false } ``` --- # `proposal.withdraw` Withdraw a proposal that has not been applied. - **scope**: `proposals:write` - **storefront-visible** (bumps the catalog generation): no - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `proposalId` | string (/^prp_[0-9A-HJKMNP-TV-Z]{26}$/) | yes | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "proposalId": { "type": "string", "pattern": "^prp_[0-9A-HJKMNP-TV-Z]{26}$" } }, "required": [ "proposalId" ], "additionalProperties": false } ``` --- # `proposal.approve` Approve a submitted proposal and start applying its items in order (store owner only). - **scope**: `proposals:approve` - **storefront-visible** (bumps the catalog generation): no - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `proposalId` | string (/^prp_[0-9A-HJKMNP-TV-Z]{26}$/) | yes | | | `expectedItemCount` | integer (1..500) | yes | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "proposalId": { "type": "string", "pattern": "^prp_[0-9A-HJKMNP-TV-Z]{26}$" }, "expectedItemCount": { "type": "integer", "minimum": 1, "maximum": 500 } }, "required": [ "proposalId", "expectedItemCount" ], "additionalProperties": false } ``` --- # `proposal.reject` Reject a proposal without applying anything (store owner only). - **scope**: `proposals:approve` - **storefront-visible** (bumps the catalog generation): no - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `proposalId` | string (/^prp_[0-9A-HJKMNP-TV-Z]{26}$/) | yes | | | `note` | string (≤500 chars) | no | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "proposalId": { "type": "string", "pattern": "^prp_[0-9A-HJKMNP-TV-Z]{26}$" }, "note": { "type": "string", "maxLength": 500 } }, "required": [ "proposalId" ], "additionalProperties": false } ``` --- # `proposal.item.skip` Skip one item of a paused proposal (store owner only) so the rest can continue. - **scope**: `proposals:approve` - **storefront-visible** (bumps the catalog generation): no - **destructive**: no ## Payload | field | type | required | notes | |---|---|---|---| | `proposalId` | string (/^prp_[0-9A-HJKMNP-TV-Z]{26}$/) | yes | | | `itemId` | string (/^pri_[0-9A-HJKMNP-TV-Z]{26}$/) | yes | | | `note` | string (≤500 chars) | no | | ### JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "proposalId": { "type": "string", "pattern": "^prp_[0-9A-HJKMNP-TV-Z]{26}$" }, "itemId": { "type": "string", "pattern": "^pri_[0-9A-HJKMNP-TV-Z]{26}$" }, "note": { "type": "string", "maxLength": 500 } }, "required": [ "proposalId", "itemId" ], "additionalProperties": false } ``` --- # Admin contribution zones Zone ids are a CLOSED, platform-owned enum and a public contract (ADR 0026 §8): breaking a zone's context shape means a new id, never mutation. An extension declares WHICH zone a panel mounts in (`ui.panels[].zone`), never its position — the platform owns arrangement. `when` is a closed predicate vocabulary (`orderStatusIn`, `requiresShipping`, `testOnly`) evaluated before the panel's code loads. | zone | kind | status | context passed | |---|---|---|---| | `settings.payments` | list | active | none — the platform renders the whole card from the manifest | | `settings.cargo` | list | active | none — the platform renders the whole card from the manifest | | `order.detail.ship` | panel | active | { order: OrderView, store: StoreCtx, onDone, onChanged } | | `order.detail.panel` | panel | active | { order: OrderView, store: StoreCtx, onChanged } | | `extension.settings.action` | action | active | { store: StoreCtx, extension: InstalledView, close, refresh } | | `product.detail.panel` | panel | reserved | { product, store } (reserved) | | `dashboard.widget` | panel | reserved | { store } (reserved) | | `returns.detail.panel` | panel | reserved | { return, order, store } (reserved) | Kind-derived **list** zones render every installed extension of the provider kind as a platform card (description, secrets, the auto-generated config form, actions, enable) — most extensions ship zero UI code. **panel** zones mount an extension's compiled panel (runtime:platform at E1; server-driven component trees for sandboxed extensions at P1+). **action** = a modal opened from the extension's settings card (`ui.actions[]`, with `doneWhen.configSet` for a "done" badge). --- # Checkout hooks Five frozen decision points on the checkout path (ADR 0020 §4). A hook is a pure function `f(CartSnapshot, config) → HookOp[]`: JSON in, a closed union of operations out — no I/O, no SQL, no fetch. The platform applies the ops with clamps; a throwing or over-budget hook fails OPEN (pricing/shipping/payment.filter) and trips a breaker after 5 consecutive failures (`extension.hook.deactivate`, one-click re-enable). Budgets: snapshot ≤ 128 KB, ops ≤ 20 KB. ## `checkout.cart.validate` Allowed ops (JSON Schema): ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "maxItems": 50, "type": "array", "items": { "oneOf": [ { "type": "object", "properties": { "op": { "type": "string", "const": "reject" }, "code": { "type": "string", "pattern": "^[a-z0-9_]{2,40}$" }, "messageCode": { "type": "string", "pattern": "^[a-z0-9_]{2,40}$" }, "lineId": { "type": "string", "maxLength": 16 } }, "required": [ "op", "code", "messageCode" ], "additionalProperties": false }, { "type": "object", "properties": { "op": { "type": "string", "const": "capQty" }, "lineId": { "type": "string", "maxLength": 16 }, "qty": { "type": "integer", "minimum": 1, "maximum": 99 } }, "required": [ "op", "lineId", "qty" ], "additionalProperties": false } ] } } ``` ## `checkout.pricing.adjust` Allowed ops (JSON Schema): ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "maxItems": 50, "type": "array", "items": { "type": "object", "properties": { "op": { "type": "string", "const": "addDiscount" }, "lineId": { "type": "string", "maxLength": 16 }, "kurus": { "type": "integer", "minimum": 0, "maximum": 10000000000000 }, "label": { "type": "string", "minLength": 1, "maxLength": 60 } }, "required": [ "op", "kurus", "label" ], "additionalProperties": false } } ``` ## `checkout.shipping.rates` Allowed ops (JSON Schema): ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "maxItems": 50, "type": "array", "items": { "oneOf": [ { "type": "object", "properties": { "op": { "type": "string", "const": "addRate" }, "id": { "type": "string", "pattern": "^[a-z0-9_-]{1,32}$" }, "labelTR": { "type": "string", "minLength": 1, "maxLength": 60 }, "kurus": { "type": "integer", "minimum": 0, "maximum": 10000000000000 }, "etaDays": { "type": "array", "prefixItems": [ { "type": "integer", "minimum": 0, "maximum": 60 }, { "type": "integer", "minimum": 0, "maximum": 60 } ] } }, "required": [ "op", "id", "labelTR", "kurus" ], "additionalProperties": false }, { "type": "object", "properties": { "op": { "type": "string", "const": "filterRate" }, "id": { "type": "string", "pattern": "^[a-z0-9_-]{1,32}$" } }, "required": [ "op", "id" ], "additionalProperties": false }, { "type": "object", "properties": { "op": { "type": "string", "const": "renameRate" }, "id": { "type": "string", "pattern": "^[a-z0-9_-]{1,32}$" }, "labelTR": { "type": "string", "minLength": 1, "maxLength": 60 } }, "required": [ "op", "id", "labelTR" ], "additionalProperties": false } ] } } ``` ## `checkout.payment.filter` Allowed ops (JSON Schema): ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "maxItems": 50, "type": "array", "items": { "oneOf": [ { "type": "object", "properties": { "op": { "type": "string", "const": "exclude" }, "methodId": { "type": "string", "pattern": "^[a-z0-9_]{2,32}$" } }, "required": [ "op", "methodId" ], "additionalProperties": false }, { "type": "object", "properties": { "op": { "type": "string", "const": "reorder" }, "order": { "maxItems": 10, "type": "array", "items": { "type": "string", "pattern": "^[a-z0-9_]{2,32}$" } } }, "required": [ "op", "order" ], "additionalProperties": false }, { "type": "object", "properties": { "op": { "type": "string", "const": "capTaksit" }, "methodId": { "type": "string", "pattern": "^[a-z0-9_]{2,32}$" }, "max": { "type": "integer", "minimum": 1, "maximum": 12 } }, "required": [ "op", "methodId", "max" ], "additionalProperties": false } ] } } ``` ## `checkout.order.validate` Allowed ops (JSON Schema): ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "maxItems": 50, "type": "array", "items": { "type": "object", "properties": { "op": { "type": "string", "const": "reject" }, "code": { "type": "string", "pattern": "^[a-z0-9_]{2,40}$" }, "messageCode": { "type": "string", "pattern": "^[a-z0-9_]{2,40}$" } }, "required": [ "op", "code", "messageCode" ], "additionalProperties": false } } ``` ## Shopper-facing message codes Hooks never emit prose; they emit codes the platform maps to Turkish: | code | TR | |---|---| | `out_of_stock` | Üründe yeterli stok yok. | | `insufficient_stock` | Stok, istediğin adet için yeterli değil. | | `qty_capped` | Bu ürün için adet sınırına ulaşıldı. | | `line_unavailable` | Ürün artık satışta değil ve sepetten çıkarıldı. | | `price_changed` | Fiyat güncellendi — lütfen yeni tutarı kontrol et. | | `shipping_unavailable` | Bu adrese şu anda kargo yapılamıyor. | | `payment_unavailable` | Şu anda ödeme alınamıyor — lütfen daha sonra tekrar dene. | | `digital_kapida_disallowed` | Dijital ürünler kapıda ödeme ile satın alınamaz. | | `session_expired` | Oturumun zaman aşımına uğradı. | | `amount_mismatch` | Ödeme tutarı doğrulanamadı — sipariş oluşturulmadı, kartından çekim yapıldıysa iade edilir. | | `order_rejected` | Sipariş şu anda oluşturulamıyor. | | `checkout_disabled` | Bu mağazada ödeme yakında açılıyor. | | `cart_empty` | Sepetin boş. | --- # Limits The table the runtime enforces IS the table below (`LIMITS` in packages/core/src/extensions/limits.ts) — design within them. | limit | value | |---|---| | `hookSnapshotMaxBytes` | 131072 | | `hookOpsMaxBytes` | 20480 | | `hookBreakerThreshold` | 5 | | `metafieldDefinitionsMax` | 200 | | `metafieldValueMaxBytes` | 65536 | | `manifestSecretsMax` | 8 | | `manifestScopesMax` | 10 | | `manifestWebhooksMax` | 4 | | `manifestJobsMax` | 3 | | `manifestMailTemplatesMax` | 5 | | `manifestPanelsMax` | 4 | | `manifestActionsMax` | 3 | | `manifestPagesMax` | 3 | | `extensionConfigMaxBytes` | 32768 | | `webhookBodyMaxBytes` | 65536 | | `jobTimeoutMaxMs` | 30000 | | `islandBudgetGzBytes` | 30720 | | `proposalMaxItems` | 500 | | `proposalOpenIdleDays` | 7 | | `proposalSubmittedDays` | 14 | | `workspacesPerStore` | 5 | | `workspaceIdleArchiveDays` | 7 | | `tokenDefaultExpiryDays` | 90 | | `tokenRateLimitPerMinDefault` | 120 | --- # Scopes Permissions are granted at install, verbatim from the manifest's `scopes`, and shown on the Mozaik-owned consent screen with Turkish labels from the platform scope registry (KVKK-sensitive scopes are highlighted). An extension-attributed action runs with exactly its granted scopes. | scope | commands it gates | |---|---| | `agents:admin` | `token.mint`, `token.revoke`, `workspace.create`, `workspace.archive` | | `catalog:write` | `catalog.category.create`, `catalog.category.update`, `catalog.category.archive`, `catalog.product.create`, `catalog.product.update`, `catalog.product.archive`, `catalog.price.set`, `catalog.stock.adjust`, `catalog.stock.set`, `catalog.digital.codes.add`, `catalog.digital.codes.void`, `catalog.product.setVariants`, `catalog.product.setMedia`, `catalog.collection.create`, `catalog.collection.update`, `catalog.collection.archive`, `catalog.collection.setProducts`, `catalog.media.commit`, `catalog.media.delete`, `catalog.product.setDigital`, `metafield.set`, `metafield.unset` | | `checkout` | `order.return.request`, `order.return.cancel`, `customer.identity.attach`, `order.create` | | `design:write` | `design.publish`, `design.rollback` | | `orders:write` | `order.ship`, `order.shipment.track`, `order.shipment.cancel`, `order.cod.collect`, `order.note.add`, `order.return.approve`, `order.return.reject`, `order.return.receive`, `order.return.settle`, `order.line.epin.fulfill`, `customer.identity.unlink`, `customer.anonymize`, `order.transfer.confirm`, `order.cancel`, `order.refund.request`, `order.refund.record` | | `proposals:approve` | `proposal.approve`, `proposal.reject`, `proposal.item.skip` | | `proposals:write` | `proposal.submit`, `proposal.withdraw` | | `secrets:write` | `secret.set`, `secret.delete` | | `store:admin` | `store.syncKv`, `settings.store.update`, `settings.identity.update`, `settings.flag.set`, `extension.install`, `extension.uninstall`, `extension.upgrade`, `extension.enable`, `extension.disable`, `extension.configure`, `extension.hook.deactivate`, `settings.checkout.update`, `metafield.definition.create`, `metafield.definition.update`, `metafield.definition.delete` | `admin` implies every scope. `checkout` is the shopper-session scope used by the enclave itself. --- # Storefront sections The section types a store bundle may reference today (first-party; `GET /v1/sections/meta` serves the same list with JSON Schemas for the builder). Custom sections as extension contributions land at P2 (ADR 0019) — the manifest reserves `ui.themeSections` for them. | key | label (TR) | placement | |---|---|---| | `header@1` | Üst Menü | {"regions":["header"],"regionsOnly":true,"max":1} | | `footer@1` | Alt Bilgi | {"regions":["footer"],"regionsOnly":true,"max":1,"requiredInRegion":"footer"} | | `announcement-bar@1` | Duyuru Şeridi | {"regions":["header"],"max":1} | | `hero-basic@1` | Basit Kapak | {} | | `hero@1` | Kapak | {} | | `trust-bar@1` | Güven Şeridi | {"regions":["footer","header"]} | | `product-detail@1` | Ürün Detayı | {"pages":["product"],"max":1,"requiredOn":"product"} | | `product-grid@1` | Ürün Vitrini | {} | | `collection-list@1` | Koleksiyon Listesi | {"pages":["home","collection","static"]} | | `image-with-text@1` | Görsel + Metin | {} | | `rich-text@1` | Metin | {} | --- # First-party extensions Every extension Mozaik ships, rendered from its real manifest. All of them are included in ₺49/ay — installable never means paywalled. `listed: false` entries exist but don't appear in the Eklentiler gallery. ## `mozaik.iyzico` — iyzico ile kart ödemesi Kendi iyzico hesabınla kredi/banka kartı ödemesi al — 3D Secure ve taksit destekli. - version **1.1.0** · publisher `mozaik` · runtime `platform` · listed: true - provider kind: `payment` · methodId `card_iyzico` · settlement `card` - scopes: `checkout` - secrets: `psp.iyzico.api_key` (sandbox prefix `sandbox-`), `psp.iyzico.secret_key` (sandbox prefix `sandbox-`) - webhooks: `/v1/ext/mozaik.iyzico/wh/psp/{storeId}` → payment_events (provider_fetch) - jobs: — - admin panels: — · actions: — - PII: name, email, phone, address, city, ip — Ödeme işlemini iyzico üzerinden başlatmak için alıcı bilgileri iyzico'ya iletilir. Config schema: ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "mode": { "default": "sandbox", "type": "string", "const": "sandbox" }, "enabledInstallments": { "maxItems": 12, "type": "array", "items": { "type": "integer", "minimum": 1, "maximum": 12 } } }, "additionalProperties": false } ``` ## `mozaik.paytr` — PayTR ile kart ödemesi Kendi PayTR hesabınla kart ödemesi al. (Başvurun onaylandığında etkinleşir.) - version **0.2.0** · publisher `mozaik` · runtime `platform` · listed: false - provider kind: `payment` · methodId `card_paytr` · settlement `card` - scopes: `checkout` - secrets: `psp.paytr.merchant_id`, `psp.paytr.merchant_key`, `psp.paytr.merchant_salt` - webhooks: — - jobs: — - admin panels: — · actions: — - PII: none Config schema: ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "mode": { "default": "sandbox", "type": "string", "const": "sandbox" } }, "additionalProperties": false } ``` ## `mozaik.kapida` — Kapıda ödeme Teslimatta nakit veya kartla ödeme. Ücreti ayrı bir kalem olarak gösterilir. - version **1.1.0** · publisher `mozaik` · runtime `platform` · listed: true - provider kind: `payment` · methodId `kapida` · settlement `cod` - scopes: `checkout` - secrets: — - webhooks: — - jobs: — - admin panels: — · actions: — - PII: none Config schema: ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "feeKurus": { "default": 0, "type": "integer", "minimum": 0, "maximum": 50000 } }, "additionalProperties": false } ``` ## `mozaik.havale` — Havale / EFT Banka havalesi ile ödeme — sipariş, ödemeyi onaylayana kadar bekletilir. - version **1.1.0** · publisher `mozaik` · runtime `platform` · listed: true - provider kind: `payment` · methodId `havale` · settlement `transfer` - scopes: `checkout` - secrets: — - webhooks: — - jobs: — - admin panels: — · actions: — - PII: none Config schema: ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "ibans": { "minItems": 1, "maxItems": 5, "type": "array", "items": { "type": "object", "properties": { "banka": { "type": "string", "minLength": 1, "maxLength": 60 }, "iban": { "type": "string" }, "hesapSahibi": { "type": "string", "minLength": 1, "maxLength": 120 } }, "required": [ "banka", "iban", "hesapSahibi" ], "additionalProperties": false } }, "aciklamaOnEki": { "default": "SP", "type": "string", "pattern": "^[A-Z0-9-]{2,10}$" }, "ttlHours": { "default": 72, "type": "integer", "minimum": 24, "maximum": 168 } }, "required": [ "ibans" ], "additionalProperties": false } ``` ## `mozaik.flat-shipping` — Sabit kargo ücreti Tek sabit kargo ücreti; istersen belirli tutar üzeri ücretsiz kargo. - version **1.1.0** · publisher `mozaik` · runtime `platform` · listed: false · auto-installed (system floor) - provider kind: `shipping` · methodId `flat` - scopes: `checkout` - secrets: — - webhooks: — - jobs: — - admin panels: — · actions: — - PII: none Config schema: ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "feeKurus": { "type": "integer", "minimum": 0, "maximum": 100000 }, "freeAboveKurus": { "type": "integer", "minimum": 0, "maximum": 1000000000 } }, "required": [ "feeKurus" ], "additionalProperties": false } ``` ## `mozaik.geliver` — Geliver ile kargo Kendi Geliver hesabınla etiket oluştur, kargo fiyatlarını karşılaştır ve takibi otomatik al. - version **1.1.0** · publisher `mozaik` · runtime `platform` · listed: true - provider kind: `cargo` · methodId `geliver` - scopes: `orders:write` - secrets: `cargo.geliver.api_token` - webhooks: `/v1/ext/mozaik.geliver/wh/cargo/{storeId}` → cargo_events (provider_fetch) - jobs: `track_poll` (hourly) - admin panels: `order.detail.ship` · actions: `sender-address` - PII: name, phone, address, city — Kargo etiketi oluşturmak için alıcı adı, telefonu ve teslimat adresi Geliver'e iletilir. Config schema: ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "mode": { "default": "test", "type": "string", "const": "test" }, "senderAddressId": { "type": "string", "maxLength": 64 }, "defaultPackage": { "default": { "weightGr": 1000, "lengthCm": 20, "widthCm": 15, "heightCm": 10 }, "type": "object", "properties": { "weightGr": { "default": 1000, "type": "integer", "minimum": 100, "maximum": 50000 }, "lengthCm": { "default": 20, "type": "integer", "minimum": 1, "maximum": 150 }, "widthCm": { "default": 15, "type": "integer", "minimum": 1, "maximum": 150 }, "heightCm": { "default": 10, "type": "integer", "minimum": 1, "maximum": 150 } }, "additionalProperties": false }, "offerPolicy": { "default": "merchant_picks", "type": "string", "enum": [ "merchant_picks", "cheapest" ] }, "labelFormat": { "default": "PDF", "type": "string", "enum": [ "PDF", "JPG" ] }, "webhookId": { "type": "string", "maxLength": 64 } }, "additionalProperties": false } ``` --- # Mozaik extension platform Mozaik is a multi-tenant e-commerce platform for the Turkish market. **Everything provider-specific is an extension** ("Eklenti" in the merchant UI): payments, cargo, e-invoice, marketplace sync, messaging, reporting. Nothing provider-shaped is default — a fresh store has zero listed extensions, and Mozaik's own providers (iyzico, Geliver, havale, kapıda) are installable extensions built on the exact contract documented here. This site is **generated from the live platform registries at build time**: the manifest schema, every command, the admin zones, the hook contract, the limits table and the first-party catalog are emitted from source, never hand-copied. Hand-written pages (these concept pages) may only name things that exist — an unresolved reference fails the build. ## Start here 1. [What is an extension](/what-is-an-extension) — the manifest, runtimes, and what stays core. 2. [Runtime model](/runtime-model) — `platform` · `sandbox` · `external`, honestly. 3. [Install lifecycle](/install-lifecycle) — gallery → consent → [`extension.install`](/reference/commands/extension.install) → configure → [`extension.enable`](/reference/commands/extension.enable). 4. [Provider kinds](/provider-kinds) — payment, shipping, cargo, e-invoice, channel, messaging, reporting. 5. [Building an extension](/building-an-extension) — how first-party extensions are laid out today. 6. [For agents](/for-agents) — `llms.txt`, `.md` variants, schemas. ## Reference - [Extension manifest](/reference/manifest) · [Commands](/reference/commands) · [Admin zones](/reference/admin-zones) · [Checkout hooks](/reference/hooks) · [Limits](/reference/limits) · [Scopes](/reference/scopes) · [Storefront sections](/reference/sections) · [First-party catalog](/catalog) ## Non-negotiables - Commands are the ONLY write path. No extension SQL, no extension-defined inbound routes, no free HTML injection into the admin or the storefront shell, no per-store code forks. - Money never flows through Mozaik: every provider extension uses the **merchant's own** account (secrets are write-only and sealed). - Every first-party extension is included in the flat ₺49/ay — installable never means paywalled. --- # What is an extension An extension is a **manifest plus, optionally, code**. The manifest ([reference](/reference/manifest)) declares everything the platform hosts for it — and the platform hosts almost all of it *declaratively*, with no extension code involved: - **Identity**: `id` (`vendor.name`), `version`, `publisher`, Turkish label and description. - **Role**: a provider kind — payment, shipping, cargo, einvoice, channel, messaging, reporting — with kind-specific contract fields (a payment extension declares its `settlement`; a shipping extension declares its hot-path timeout/fallback contract). - **Config**: a JSON Schema. The admin renders a consistent Turkish settings form from it automatically. Most extensions ship zero UI code because of this. - **Secrets**: write-only credential fields (sealed with AES-GCM, never readable back, optionally locked to a sandbox prefix until live mode). - **Admin UI**: panels mounted into named [zones](/reference/admin-zones) of existing pages, actions (modals), and — rarely — own pages. - **Webhooks** it needs to receive (served by ONE platform-owned route), **jobs** (hourly/daily), **mail** template contributions (promo-free by attestation and lint), checkout **CSP** origins, **PII field selection**, and order-lifecycle **events** (field defined; dispatcher lands with the first e-invoice extension). - **`runtime`** — the declared dependency: see [Runtime model](/runtime-model). ## Install is per store Code physically present in Mozaik's workers does **not** exist for a store until that store installs the extension. Dispatch routes only through installed + enabled rows ([`extension.install`](/reference/commands/extension.install) → [`extension.enable`](/reference/commands/extension.enable)). The per-store row pins the extension's version and a snapshot of its manifest; upgrades are explicit audited commands ([`extension.upgrade`](/reference/commands/extension.upgrade)). ## What stays core (and why) Payment session state machine (money truth can't depend on extension correctness) · checkout orchestration (the composition point) · order + legal document generation (Turkish consumer-law artifacts) · the stock ledger (append-only, trigger-enforced) · the manual cargo path (a store with zero extensions must still fulfill) · the carrier VKN reference table · the hook runner and breaker · the mail transport and its promo-free enforcement · GİB mükellef lookup and e-Fatura/e-Arşiv routing (M7) · the İYS consent ledger (M10). ## The four permanent refusals From ADR 0020, unchanged: no extension-defined inbound routes or request middleware; no direct database access from extension code; no free `documentShell` injection; no per-store storefront forks. --- # Runtime model Every manifest declares `runtime`. **Trust picks the runtime; sync/async picks the interface contract.** | runtime | what it means | who | status | |---|---|---|---| | `platform` | compiled into Mozaik's workers, full privileges | first-party ONLY (`publisher: "mozaik"`, enforced by the schema) | **live** — every extension in the [catalog](/catalog) | | `sandbox` | a sealed, content-addressed, store-scoped worker: no ambient DB or network, CPU/subrequest budgets, kill switch | third-party developers, agent-written extensions, per-store custom code | **not yet** — lands at P2 (ADR 0027); [`extension.install`](/reference/commands/extension.install) refuses it until then | | `external` | a standing service outside Mozaik's infrastructure reached through webhooks | existing SaaS | **not yet** — M11 | Because install semantics, admin UI, and dispatch all key off the manifest, moving an extension from `platform` to `sandbox` later changes only the `runtime` field and packaging — no merchant-visible change, no reinstall. ## Sync vs async is about contracts Hot-path contributions (payment sessions, [shipping rates](/reference/hooks)) carry timeout + fallback contracts in the manifest (`provider.rates` for shipping kinds; the checkout hook budgets in [limits](/reference/limits)). Async contributions (cargo labels/tracking, e-invoice issuance, marketplace sync) use webhooks + jobs. A first-party async extension is perfectly fine as `platform` — the sandbox exists for code Mozaik did not compile. --- # Install lifecycle All lifecycle steps are audited commands (see [Commands](/reference/commands)); the admin's Eklentiler gallery is just a client of them. 1. **Browse** — the gallery lists `listed` first-party extensions by category. Never a price. 2. **Consent** — a Mozaik-owned screen shows the manifest's `scopes` with Turkish labels from the platform scope registry (KVKK-sensitive scopes highlighted) and the `pii.purposeTR` statement with the number of buyer fields the extension will receive. 3. **Install** — [`extension.install`](/reference/commands/extension.install) creates the store's row DISABLED, pins `version` + a `manifest_snapshot`, and grants the scopes verbatim. Sandbox-runtime manifests are refused until P2 (ADR 0027). 4. **Configure** — [`extension.configure`](/reference/commands/extension.configure) stores the config after server-side Zod validation and normalization (defaults applied, IBANs canonicalized). The admin form is generated from the manifest's JSON Schema. Secrets go through `secret.set` (write-only; sandbox prefix enforced while `config.mode !== "live"`). 5. **Enable** — [`extension.enable`](/reference/commands/extension.enable) requires every required secret present and a valid config, enforces one enabled extension per `methodId`, and stamps the snapshot. Enable/disable/configure bump the store's catalog generation because payment capabilities and checkout CSP origins project into cached storefront HTML. 6. **Upgrade** — [`extension.upgrade`](/reference/commands/extension.upgrade) moves the pinned version to the platform's current manifest after re-validating the stored config. Never silent. 7. **Uninstall** — two-step: [`extension.disable`](/reference/commands/extension.disable) first, then [`extension.uninstall`](/reference/commands/extension.uninstall). The row is deleted; the config is archived in the command's audit result; secrets are KEPT (merchant-owned credentials — `secret.delete` is explicit); `app.{vendor}.*` metafields are kept (orders reference them). A fresh store seeds exactly one row: the unlisted `mozaik.flat-shipping` floor, installed but disabled, enabled by the merchant's first "Kargo ücreti" save. --- # Provider kinds The manifest's `provider` block is a discriminated union on `kind` (exact fields in the [manifest reference](/reference/manifest)). Each kind has a closed platform-side interface; the interface IS the contract — there are no manifest-declared free-form actions. ## payment (live) `methodId` + `settlement: card | cod | transfer`. The platform owns the payment session state machine (attempts, 3DS continuation, finalize assertions, refunds as intent-first reservations); a `card` provider implements `installments / initiate / finalize / handleWebhook / refund` and only resolves or rejects sessions. `methodFee` (e.g. kapıda) and `pendingOrder` (expiry config key + a typed `orderInstructions()` block rendered in the order view, the confirmation mail and the sözleşme) are generic contracts — the platform never reads a provider's config shape directly. Webhooks: `/v1/ext/{id}/wh/{token}/{storeId}`; browser callbacks: `/_mz/co/cb/{id}/{attemptId}`. ## shipping (live) A checkout rate source via the [`checkout.shipping.rates`](/reference/hooks) hook with a hot-path contract (`rates.timeoutMs`, `cacheTtlSeconds`, `onError`). The first-party `mozaik.flat-shipping` is the auto-installed floor. ## cargo (live) Fulfillment after the order: `createShipment / offers / acceptOffer / label / track / cancel / createReturn / registerWebhook / handleWebhook / senderAddressCreate`. Typed op routes `POST /v1/orders/{ord}/cargo/{offers|book|ship}` and `/cargo/{shp}/{track|cancel|update|label}` are capability-gated by the manifest (`rateShopping`, `tracking`, `labelFormats`, `senderProfile`). Tracking webhooks are a *nudge*: the platform re-fetches with its own credentials before any state transition. ## einvoice (kind defined; first extension at M7) Per-entegratör transmission (Nilvera first). The platform keeps GİB mükellef lookup, e-Fatura-vs-e-Arşiv routing and the invoice-at-fulfillment trigger UX. Needs the order-lifecycle events dispatcher (manifest field `events` is defined now). ## channel (kind defined; M9) Marketplace sync consuming the stock-ledger outbox (`seq > cursor`, absolute-quantity push) and pulling orders as `order.import`. ## messaging (kind defined; M10) SMS/e-mail transports. The İYS consent ledger stays core. ## reporting (kind defined) Report packs with CSV/XLSX exports. --- # Building an extension > Third-party submission does not exist yet (M11). Today this page documents how **first-party** extensions are laid out in the monorepo — the exact contract third parties will get. A first-party extension has three homes: 1. **Manifest + config schema** — an entry in `packages/core/src/extensions/registry.ts`: `buildManifest({...}, configZod)` attaches the generated JSON Schema. The Zod is the single source of truth for config validation; the manifest is what the platform and this site read. 2. **Runtime module** (only if the extension has code) — `apps/api/src/extensions/runtime.ts` maps the extension id to its `ExtensionRuntime`: `payment` / `cargo` provider implementations, `orderInstructions()`, and (via `apps/api/src/extensions/jobs.ts`) job handlers named in `manifest.jobs`. Provider code receives a `ProviderCtx` with **typed `secrets`** — exactly the manifest-declared keys — and `config`; it never reads the database or the request. 3. **Admin UI module** (only if a panel or action needs code) — `apps/admin/src/extensions/{name}/index.tsx` default-exports `{ panels: { [panelId]: Component }, actions: { [actionId]: Modal } }`; zone hosts lazy-load it only when the manifest declares a panel for that zone. Extension text is module-local — it never enters the shared panel dictionary. Everything else — settings forms, secret fields, enable/disable, ordering, the consent screen, webhook ingestion, job scheduling, CSP origins — is rendered or dispatched by the platform from the manifest. ## Checklist - Declare only the `scopes` you need; they appear on the consent screen verbatim. Declare `pii.buyer` honestly — undeclared fields never reach your code. - Keep your config schema inside the renderable subset (booleans, numbers/money, strings/enums, one-level objects, arrays of flat objects, arrays of scalars). A registry test rejects anything the form generator cannot render. - Mail contributions must be promo-free (`promoFree: true` is an attestation AND a runtime lint). - Respect the [limits](/reference/limits). Hooks fail open and trip a breaker; don't rely on being called. - Never name a provider id in core code paths. If you need the platform to know something about your extension, put it in the manifest. --- # For agents This site is meant to be consumed by coding agents as much as by people. - [`/llms.txt`](/llms.txt) — an index of every page with links to its Markdown variant. - [`/llms-full.txt`](/llms-full.txt) — the whole site as one Markdown document (pull it into context). - Every HTML page has a `.md` twin at the same path (`/reference/manifest.md`), linked via ``. - `/schemas/manifest.schema.json` and `/schemas/hooks.schema.json` — machine-readable contracts generated from the live Zod. Validate a manifest against the former before proposing it. - `GET https://api.mozaik-dev.com/v1/commands` returns the command registry with JSON Schemas (anonymous: the frozen public subset; authenticated with `?store=`: the scope-filtered full list) — the same list as [Commands](/reference/commands). ## What's coming — P1 (in progress, ADR 0027) Nothing in this list is live yet; this page will switch to present tense as each piece ships. - **Admin MCP** at `api.mozaik-dev.com/mcp` with two token kinds: **Ajan** tokens are propose-only (every write is recorded into a proposal the store owner approves in the panel), **Geliştirici** tokens execute directly. The store is derived from the token; tool names are the registry's dotted ids. - **Proposals** — the consent mechanism for agent work: dry-run previews recorded, owner approval, apply re-checks every preview before executing. - **Workspaces** — named drafts of the store design; agents get their own, previewable at a stable URL, and the owner adopts it into the builder's draft. - **`@mozaik/cli`** on npm (`mozaik open/pull/push/dev/propose`), pinned to the platform build it talks to; a project lock and a public `/v1/meta` endpoint make version mismatches a refused request rather than a surprise. - **Dev MCP** (`mozaik dev-mcp`): docs search, schema introspection and validators (manifest, command payload, bundle), always serving the platform's current build. - **Hosted workspaces** — a per-workspace container with the pinned CLI, the store project and an `AGENTS.md`, driven from your own agent over MCP. - **Required `agent` manifest block** — every extension ships a machine-readable setup + ops contract and a generated "For agents" section on its catalog page; an extension without it cannot be installed. --- # Changelog Append-only, dated. Reference pages regenerate on every deploy; this page records intent. - **2026-08-21 — E1 (extension platform v1).** "plugin" → extension/Eklenti; manifest v2 with `runtime` and discriminated provider kinds; per-store install with consent, version pinning and manifest snapshots; generic dispatch (`resolveExtension`, typed secrets), one platform-owned webhook route, typed cargo op routes, manifest jobs; admin contribution zones + schema-rendered forms; the Eklentiler gallery; this docs site. All six first-party extensions ported ([`mozaik.iyzico`](/catalog), [`mozaik.paytr`](/catalog), [`mozaik.kapida`](/catalog), [`mozaik.havale`](/catalog), [`mozaik.flat-shipping`](/catalog), [`mozaik.geliver`](/catalog)).