Status: in development. These are the surfaces the matgrk dashboard itself runs on today. Public API access — with its own credentials and permissions — is planned; until it ships, the shapes on this page describe the services as they run and may still change.
- Every resource belongs to one store; nothing here can read or write across stores.
- Writes are permission-checked per resource — the same permissions the dashboard’s roles are built from. The chip on a row names the permission that route checks; a row without one is machine-to-machine or not yet pinned down here.
- Permissions are declared by each service and bundled into roles by the platform, capped by the store’s plan — an undeclared name is refused, not accepted.
- Catalogue changes reach the storefront through publish: a product edit is not visible until published.
- Rows with a chevron expand to a request or response sample.
Products
- GET
/productsList productscatalog.products.view POST
/productsCreate a productcatalog.products.createRequest { "sku": "TSHIRT-001", "base_price_minor": 11500, "currency": "SAR", "tax_class": "standard", "compare_at_minor": null, "weight_grams": 250, "brand_id": null, "status": "draft", "enabled_locales": ["ar", "en"], "translations": { "ar": { … }, "en": { … } }, "on_hand": 100 }- GET
/products/{id}One product with variants, options, attributes and mediacatalog.products.view - PATCH
/products/{id}Change a product — send only what changescatalog.products.update - DELETE
/products/{id}Delete a product; existing orders keep itcatalog.products.delete - POST
/products/{id}/publishPublish; refuses and names incomplete localescatalog.products.publish - POST
/products/{id}/unpublishTake it off the storefrontcatalog.products.publish - GET
/products/by-sku/{sku}Look a product up by SKUcatalog.products.view - POST
/products/{id}/optionsAdd an optioncatalog.products.update - PATCH
/options/{id}Change an optioncatalog.products.update - DELETE
/options/{id}Delete an optioncatalog.products.update - POST
/products/{id}/variantsGenerate variants from the optionscatalog.products.update PATCH
/variants/{id}Change a variantcatalog.products.updateRequest { "sku": "TSHIRT-001-L", "price_minor": 12500, "compare_at_minor": null, "weight_grams": 260, "primary_media_id": null, "on_hand": 40 }Response { "variant": { … }, "effective_price_minor": 12500 }
Categories, brands and attributes
- GET
/categoriesList categoriescatalog.categories.manage - POST
/categoriesCreate a categorycatalog.categories.manage - PATCH
/categories/{id}Change a categorycatalog.categories.manage - DELETE
/categories/{id}Delete a categorycatalog.categories.manage - GET
/brandsList brandscatalog.brands.manage - POST
/brandsCreate a brandcatalog.brands.manage - PATCH
/brands/{id}Change a brandcatalog.brands.manage - DELETE
/brands/{id}Delete a brandcatalog.brands.manage - GET
/attributesList attributescatalog.attributes.manage - POST
/attributesCreate an attributecatalog.attributes.manage - PATCH
/attributes/{id}Change an attributecatalog.attributes.manage - DELETE
/attributes/{id}Delete an attributecatalog.attributes.manage
Stock and media
- GET
/stockStock levelscatalog.products.view - POST
/stock/availabilityAvailability for a set of variantscatalog.products.view - GET
/mediaA product’s media - POST
/media/attachAttach imagescatalog.media.manage - POST
/media/reorderReorder imagescatalog.media.manage - POST
/media/detachDetach imagescatalog.media.manage
Catalogue imports and exports
- POST
/imports/previewPreview a catalogue import — writes nothingcatalog.import.run - POST
/imports/{id}/applyApply the previewed importcatalog.import.run - GET
/imports/{id}The import’s progresscatalog.import.run - GET
/imports/{id}/failuresThe rows that failedcatalog.import.run - GET
/exportsList exportscatalog.export.run
Orders and invoices
- GET
/ordersList the store’s orderscommerce.orders.view - GET
/orders/{id}One ordercommerce.orders.view POST
/orders/{id}/transitionsMove an order through its lifecycle — declared transitions onlycommerce.orders.transitionRequest { "dimension": "fulfilment", "to_state": "shipped", "cause": "Shipped with SMSA, AWB 12345" }Response { "data": { … } }- GET
/invoicesList invoicescommerce.orders.view - GET
/invoices/{id}One invoicecommerce.orders.view - POST
/invoices/{id}/resubmitResubmit an e-invoice
Offers and promotions
- GET
/offersOffer labels for a set of variants - GET
/promotionsList promotions, with the plan’s limitcommerce.promotions.manage POST
/promotionsCreate a promotioncommerce.promotions.manageRequest { "name_ar": "خصم الافتتاح", "name_en": "Opening discount", "scope": "lines", "targets": [{ "kind": "category", "ref": "01J8…" }], "type": "percentage", "percent_bps": 1500, "application": "coupon", "code": "WELCOME15", "starts_at": "2026-10-01", "ends_at": "2026-10-15", "minimum_cart_minor": 10000, "usage_limit_total": 1000, "usage_limit_per_shopper": 1, "position": 1, "stackable": false, "is_active": true }Response { "data": { …the promotion, with its coupons and targets… } }- PATCH
/promotions/{id}Change a promotioncommerce.promotions.manage - DELETE
/promotions/{id}Delete a promotion — one that was used is deactivated insteadcommerce.promotions.manage - POST
/promotions/importsImport promotions in bulk
API credentials
- GET
/api-credentialsList the store’s API keyscommerce.api_keys.manage POST
/api-credentialsIssue a key — shown oncecommerce.api_keys.manageRequest { "name": "Mobile app", "rate_limit_per_minute": 300 }Response { "data": { …the credential… }, "key": "mk_live_…", "key_shown_once": true }- DELETE
/api-credentials/{id}Revoke a key — its sessions end at their next refreshcommerce.api_keys.manage