ERP integration

For ERP partners: onboard your customers, feed their catalogues, sync stock, and receive their orders on delivery — all from your own platform.

Surface status

Partner API shapes follow the conventions (envelope, error codes, rate limits, request ids). A surface is Live only after its verification run passed.

  • Partner tokensLive
  • Stores & onboardingLive
  • Catalogue pushLive
  • Stock — push, levels, runsLive
  • Delivered orders — poll, ledger, retryLive
  • Delivered orders — signed webhook delivery to the ERPLive
  • Delivered orders as files — the matgrk drop (list, download, acknowledge; JSON Lines or CSV) and a partner SFTP serverLive
  • Delivered orders as files to partner S3-compatible storageIn final verification
  • Partner billing — invoices and suspension, readable even while the partner is suspendedLive
  • Partner portal at https://partners.matgrk.com — keys, API sessions, team, billing, delivery setupLive
  • Catalogue pull on a schedule from the partner's SFTP / S3-compatible storagePlanned

How onboarding works

  1. Create a draft store from your wizard — name, address (slug), languages, billing country, plan, and the admin's name and email. Currency is derived from the billing country and is never sent. A taken slug is a 409 you can retry.
  2. Configure it with as many PATCH steps as your wizard has screens; nothing is live yet.
  3. Check readiness — the store resource carries a checklist your wizard can render.
  4. Activate. One idempotent call: the store goes live on its plan, the admin account is created, and the data flows below are provisioned automatically.
  5. The admin receives an email with a temporary password and the dashboard address; first sign-in forces a change. The password is never returned in the API response.

The data flows

Catalogue in

Push is live: a preview that writes nothing, per-row failures in Arabic and English, then one apply. Scheduled pull from the partner’s SFTP or S3-compatible storage is planned per partner.

Stock sync

Bulk on-hand updates, set up automatically at activation. A run that would suddenly zero a large share of items is refused, not applied.

Orders out

Poll, signed webhook delivery (HMAC-SHA256 over the raw body, at-least-once, deduplicated by id) and files are all live. The store's transport is chosen with orders-transport, and poll is always available as the catch-up path.

Authentication

One partner key (pk_live_…, shown once, stored as a digest) buys 15-minute access tokens and a 30-day single-use refresh token bound to the same key, with the scopes your integration asked for: stores:manage, catalog:write, stock:write, orders:read, webhooks:manage, billing:read. A partner token acts only on stores that partner created — the path names the store, the partner-store link is the permission.

  • Keys are managed by the partner's own staff at https://partners.matgrk.com (issue — shown once —, rotate, revoke; see each key's live API sessions and end one). A revoked key is refused at once; access already granted ends within 15 minutes.
  • SESSION_ENDED (401) on refresh: the session was ended in the partner portal; the refresh token is not spent — request a new token with the key.
  • PARTNER_SUSPENDED (403): while a partner is suspended its tokens carry only billing:read; every other operation answers this code.
  • Rate limit per key: 300 requests a minute by default, and at most twice the average per-second rate in any one second (10 a second at 300/min); x-ratelimit-* headers, Retry-After on a 429.

Partner tokensLive

  • POST/partner/v1/auth/tokenMint a partner session
  • POST/partner/v1/auth/refreshRotate a partner session

Stores and onboardingLive

Required scope: stores:manage

  • GET/partner/v1/slugs/{slug}/availabilityCheck a store address
  • POST/partner/v1/storesCreate a DRAFT store
    Request
    {
      "name": "متجر النور",
      "slug": "alnoor",
      "locales": ["ar", "en"],
      "billing_country": "SA",
      "plan": "pro",
      "admin": {
        "name": "…",
        "email": "owner@example.com",
        "phone": "+9665…"
      }
    }
    Response
    {
      "data": {
        "store_id": "01JB…",
        "status": "draft"
      }
    }
  • GET/partner/v1/storesThe partner’s linked stores
  • GET/partner/v1/stores/{id}One linked store, readiness included
  • PATCH/partner/v1/stores/{id}Configure a draft
  • DELETE/partner/v1/stores/{id}Abandon a draft
  • POST/partner/v1/stores/{id}/activateActivate a store — admin created, password emailed, flows provisioned; idempotent
    Response
    {
      "data": {
        "store_id": "01JB…",
        "status": "active",
        "dashboard_url": "https://app.matgrk.com",
        "store_url": "https://matgrk.com/stores/alnoor",
        "admin": { "email": "owner@example.com", "password_delivery": "email" },
        "flows": { "stock": "01JB…", "orders": "01JB…" }
      }
    }
  • POST/partner/v1/stores/{id}/credential-emailRe-send the admin credential email

Catalogue pushLive

Required scope: catalog:write

  • POST/partner/v1/stores/{id}/catalog/importsSubmit a catalogue file (preview)
  • POST/partner/v1/stores/{id}/catalog/imports/{importId}/applyApply a previewed import
  • GET/partner/v1/stores/{id}/catalog/imports/{importId}An import’s progress and failures

StockLive

Required scope: stock:write

  • PUT/partner/v1/stores/{id}/stockPush absolute stock (a suddenly shrinking run is refused, not applied)
    Request
    {
      "rows": [
        { "sku": "TSHIRT-001", "on_hand": 40 },
        { "sku": "TSHIRT-002", "on_hand": 12 }
      ]
    }
  • GET/partner/v1/stores/{id}/stockCurrent stock levels
    Response
    {
      "data": [
        {
          "sku": "TSHIRT-001",
          "on_hand": 40,
          "reserved": 2,
          "available": 38,
          "branches": [
            { "branch_id": "01JB…", "on_hand": 40, "reserved": 2, "available": 38 }
          ]
        }
      ],
      "meta": { "next_after": "TSHIRT-001" }
    }
  • GET/partner/v1/stores/{id}/runs/{runId}A provisioned flow’s run

Delivered orders (poll + ledger + retry)Live

Required scope: orders:read

  • GET/partner/v1/stores/{id}/ordersPoll delivered orders
  • GET/partner/v1/stores/{id}/orders/{orderId}One delivered order (same shape as the webhook body)
    Response
    {
      "data": {
        "id": "01JB…",
        "type": "order.delivered",
        "occurred_at": "2026-09-27T14:21:34Z",
        "store_id": "01JB…",
        "data": {
          "order_id": "01JB…",
          "order_number": "ORD-000001",
          "delivered_at": "2026-09-27T14:21:34Z",
          "branch": "01JB…",
          "lines": [
            { "sku": "TSHIRT-001", "quantity": 2, "price_minor": 11500, "tax_minor": 1500 }
          ],
          "total_minor": 24500,
          "tax_minor": 3000,
          "currency": "SAR",
          "payment_state": "paid",
          "shopper_ref": "01JB…"
        }
      }
    }
  • GET/partner/v1/stores/{id}/deliveriesThe outbound delivery ledger
  • POST/partner/v1/stores/{id}/deliveries/{deliveryId}/retryResume an exhausted delivery (scope webhooks:manage)

WebhooksLive

Required scope: webhooks:manage

  • GET/partner/v1/stores/{id}/webhooksThe delivered-orders endpoint
  • POST/partner/v1/stores/{id}/webhooksRegister the delivered-orders endpoint
  • DELETE/partner/v1/stores/{id}/webhooksRemove the delivered-orders endpoint

Orders transport and order filesLive

Required scope: orders:read, webhooks:manage

  • GET/partner/v1/stores/{id}/orders-transportThe store’s orders transport settings (scope webhooks:manage)
  • PUT/partner/v1/stores/{id}/orders-transportSet the store’s orders transport (poll, webhook or file; file: matgrk drop or a partner destination; jsonl or csv) (scope webhooks:manage)
  • GET/partner/v1/stores/{id}/order-filesList order files in the matgrk drop (scope orders:read)
  • GET/partner/v1/stores/{id}/order-files/{name}Download one order file (exact bytes) (scope orders:read)
  • DELETE/partner/v1/stores/{id}/order-files/{name}Acknowledge (delete) an order file (scope webhooks:manage)

BillingLive

Required scope: billing:read

  • GET/partner/v1/billing/invoicesThe partner’s invoices
  • GET/partner/v1/billing/invoices/{invoiceId}One partner invoice
  • GET/partner/v1/billing/suspensionThe partner’s suspension (preview while scheduled)

Delivered-order deliveries carry X-Matgrk-Signature: sha256=<hex> over the raw body — verify exactly as on the webhooks page. Amounts are integer minor units; total_minor is the number that was charged.