SellPass

API & webhooks v1

Automate your store: manage products and stock, read orders, issue refunds and receive signed event notifications.

Authentication

Create a key in Dashboard → API keys. Send it as a Bearer token. Each key only ever sees its own shop. Limit: 120 requests/minute per key (HTTP 429 beyond). Money is always an integer in minor units (cents) plus a currency code.

curl https://www.sellpass.net/api/v1/ping \
  -H "Authorization: Bearer sk_live_…" \
  -H "Accept: application/json"

Errors use standard HTTP codes with {"message": "…", "errors": {…}}. Lists return {"data": [...], "meta": {current_page, last_page, per_page, total}}.

Shop

GET /ping Verify your key; returns the shop slug and currency.

Products

GET /products List products. Filters: type, group_id, q, per_page (≤100).
POST /products Create. Body: title, type (serials|file|service|dynamic|subscription), price (minor units), currency, description, visibility, group_id…
GET /products/{id} Product detail incl. variants, custom fields, images.
PUT /products/{id} Update any field from create (except type).
DELETE /products/{id} Delete (soft — order history is kept).
GET /products/{id}/stock Serial counts: available / reserved / delivered.
POST /products/{id}/stock Add serials. Body: {"items": ["KEY-1", "KEY-2"], "variant_id": null}. Duplicates skipped.
DELETE /products/{id}/stock Remove unsold serials (all, or {"items": [...]}).

Categories

GET /groups List categories.
POST /groups Create: name, visibility, sort.
PUT /groups/{id} Update.
DELETE /groups/{id} Delete (products are kept, uncategorised).

Coupons

GET /coupons List coupons.
POST /coupons Create: code, type (percent|fixed), value (percent in basis points: 1000 = 10%; fixed in minor units), scope, targets[], min_order, max_uses, per_customer_uses, starts_at, expires_at.
PUT /coupons/{id} Update is_active, value, max_uses, expires_at.
DELETE /coupons/{id} Delete.

Orders

GET /orders List. Filters: status, email, gateway, from, to.
GET /orders/{uuid} Order with items and delivered content.
POST /orders/{uuid}/complete Mark paid (unpaid orders), release (held orders) or fulfilled (service orders).
POST /orders/{uuid}/void Void an unpaid order and release its stock.
POST /orders/{uuid}/replace Deliver a replacement serial. Body: item_id (optional).
POST /orders/{uuid}/refund Refund through the gateway. Body: amount (minor units, default full).
POST /payments Create an order and get a pay URL: product_id, quantity, email, gateway, variant_id, coupon_code, custom_fields{}.

Customers & blacklist

GET /customers Buyers aggregated by email: orders, total_spent, first/last seen.
GET /blacklist Your Fraud Shield rules.
POST /blacklist Add: type (email|email_domain|ip|cidr|country|custom), value, note.
DELETE /blacklist/{id} Remove a rule.

Feedback & tickets

GET /feedback Reviews (filter: rating).
POST /feedback/{id}/reply Public seller reply.
GET /tickets Support tickets (filter: status).
GET /tickets/{id} Ticket with messages.
POST /tickets/{id}/reply Reply (emails the buyer). Body: body, close.

Webhooks

Add endpoints in Dashboard → Webhooks. We POST JSON and retry failed deliveries (non-2xx or timeout after 10 s) up to 5 times: after 1 min, 5 min, 30 min, 2 h and 6 h. Every attempt is logged with the response so you can debug and resend.

order:createdorder:paidorder:partialorder:completedorder:cancelledorder:refundedorder:disputedorder:on_holdproduct:stock_lowquery:createdquery:repliedfeedback:receivedsubscription:createdsubscription:renewedsubscription:cancelled
POST https://your-server/hook
X-Signature: 3f1c…   (hex HMAC-SHA512 of the raw body, keyed with your webhook secret)
X-Event: order:paid
X-Delivery-Id: evt_…

{"id":"evt_…","event":"order:paid","created_at":"2026-01-01T12:00:00+00:00",
 "data":{"uuid":"…","status":"completed","customer_email":"buyer@example.com","total":1299,"currency":"USD","items":[…]}}

Verify the signature (PHP)

$raw = file_get_contents('php://input');
$expected = hash_hmac('sha512', $raw, getenv('WEBHOOK_SECRET'));
if (! hash_equals($expected, $_SERVER['HTTP_X_SIGNATURE'] ?? '')) {
    http_response_code(401); exit;
}
$event = json_decode($raw, true);   // dedupe on $event['id']

Node.js

const crypto = require('crypto');
const expected = crypto.createHmac('sha512', process.env.WEBHOOK_SECRET).update(rawBody).digest('hex');
const ok = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(req.get('X-Signature') || ''));

Dynamic products

For products generated on demand (license servers, account creation). After payment we POST the order to your URL; the response body (≤64 KB, within 10 s) is delivered to the buyer. The request is signed exactly like webhooks with the product's signing secret. Only public addresses are allowed; redirects are not followed. If your server fails, the order stays "paid · to fulfil" and you can deliver manually.

{"event":"order:paid","invoice":"…","product_id":12,"variant_id":null,"quantity":1,
 "customer_email":"buyer@example.com","custom_fields":{"Username":"neo"},"total":999,"currency":"USD"}

Embed a Buy button

<script src="https://www.sellpass.net/embed.js" async></script>
<button data-sellpass-shop="your-shop" data-sellpass-product="product-slug">Buy now</button>