Marketplace Seller API

Version 1

The API connects Marketplace with your accounting system: export products, update prices and stock, fetch orders. It is available to verified sellers with an active shop.

One direction only. Your system calls Marketplace. We call you back only with webhooks, and only at the address you set yourself.

Getting started

Endpoint and authorization

All requests are POST to a single address:

https://YOUR-PROJECT-REF.supabase.co/functions/v1/secure-action

The token is passed in a header, the method — in the action field of the body:

Authorization: Bearer sg_live_your_token
Content-Type: application/json

{ "action": "api_products_list", "payload": { "limit": 50 } }
Never expose the token on the client. It gives access to your products and orders. Keep it on the server side of your system or in the protected secrets storage of a no-code service.

Limits

RuleValue
Request rate60 per minute per token
ExceededHTTP 429, error: "rate_limited"
Products per requestup to 200
Items per updateup to 200
Active tokensup to 5

Token permissions

A token gets only the permissions you selected. A script that syncs stock should not be able to change prices.

PermissionGrants
listings:readRead products and stock
listings:writeChange the price
stock:writeChange the stock
orders:readRead orders

Methods

POST api_products_list

The seller's products with current stock. Requires listings:read.

{
  "action": "api_products_list",
  "payload": { "limit": 100, "offset": 0 }
}

Response:

{
  "success": true,
  "products": [
    {
      "id": "8f3c…",
      "title": "Sneakers",
      "price": 49,
      "currency": "USD",
      "status": "active",
      "product_type": "physical",
      "city": "Dubai",
      "created_at": "2026-08-01T10:00:00Z",
      "stock": { "quantity": 12, "reserved": 2 }
    }
  ]
}

stock is null if stock is not tracked for the product. reserved — how many units are held by active orders.

POST api_products_update

Bulk update of prices and stock. Requires stock:write or listings:write.

{
  "action": "api_products_update",
  "payload": {
    "items": [
      { "id": "8f3c…", "quantity": 10 },
      { "id": "9a21…", "quantity": 0, "price": 54 }
    ]
  }
}

The response comes for each item separately — one error doesn't cancel the others:

{
  "success": true,
  "results": [
    { "id": "8f3c…", "ok": true },
    { "id": "9a21…", "ok": false, "error": "below_reserved", "reserved": 3 }
  ]
}
Stock can't be set below the reserved amount. If a product has confirmed orders for 3 units, setting 0 won't work — you'll get below_reserved with the number. Otherwise buyers would be left with confirmed orders and no goods.

POST api_orders_list

The seller's orders. Requires orders:read.

{
  "action": "api_orders_list",
  "payload": { "limit": 50, "status": "pending" }
}

Order statuses:

StatusMeaning
pendingWaiting for the seller's response
confirmedConfirmed
paidPaid
shippedShipped or handed over
completedReceived, closed
returnedNot picked up, returned
declinedDeclined by the seller
expiredThe seller didn't respond in time
cancelledCancelled by the buyer

Webhooks

To avoid polling orders in a loop, set the address for notifications in the “Integrations” section. https only; internal addresses are not accepted.

Each event arrives as a separate POST:

POST https://your-endpoint/webhook
X-Webhook-Event: order.created
X-Webhook-Delivery: 4b7e…
X-Webhook-Signature: 9c1f…

{
  "event": "order.created",
  "delivery_id": "4b7e…",
  "created_at": "2026-08-21T09:12:00Z",
  "data": {
    "order_id": "…",
    "public_no": "SG-2608-41207",
    "kind": "physical",
    "status": "pending",
    "city": "Dubai",
    "items_total": 49,
    "currency": "USD",
    "expires_at": "2026-08-22T09:12:00Z"
  }
}

Signature verification

X-Webhook-Signature is an HMAC-SHA256 of the request body, signed with the secret from the “Integrations” section. Verifying it is mandatory: without it anyone could call your endpoint.

// Node.js
const crypto = require('crypto');

function verify(rawBody, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(rawBody)          // the raw body, not JSON.parse
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(expected), Buffer.from(signature)
  );
}

Retries

Respond with a 2xx code. If there is no response or an error comes back, the delivery is retried after 1, 5 and then every 25 minutes, up to six times in total. After twenty failures in a row the endpoint is disabled automatically.

A retry has the same delivery_id — use it so as not to process one event twice.

Events

EventWhen
order.createdA buyer placed an order
order.updatedThe order status changed: confirmed, declined, paid, shipped, completed or cancelled
chat.messageA new chat message (off by default)

order.updated carries status, and also tracking_number and carrier once the shipment has been created. The order is identified by order_id.

Errors

CodeerrorReason
401no_tokenNo Authorization header
401bad_tokenThe token is invalid or revoked
403scope_requiredThe token lacks the required permission
429rate_limitedMore than 60 requests per minute
400no_itemsEmpty list in an update
400too_manyMore than 200 items

What the API doesn't do

Support

Integration questions — support@example.com. If a token has leaked, revoke it in the dashboard and issue a new one: the old one stops working immediately.