Admin API

The management side of a store: products, orders, offers and promotions, invoices and API keys.

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.create
    Request
    {
      "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.update
    Request
    {
      "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.transition
    Request
    {
      "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.manage
    Request
    {
      "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.manage
    Request
    {
      "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