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
- 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.
- Configure it with as many PATCH steps as your wizard has screens; nothing is live yet.
- Check readiness — the store resource carries a checklist your wizard can render.
- Activate. One idempotent call: the store goes live on its plan, the admin account is created, and the data flows below are provisioned automatically.
- 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 onlybilling: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-Afteron 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 storeRequest { "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; idempotentResponse { "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 levelsResponse { "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.