SokoHub API Reference

Integrate your store, Zapier, Make (Integromat), or custom backend with SokoHub using our REST API and outbound webhook system.

Base URL

https://api.greatzern.com

API Version

v1 (current)

Format

JSON / UTF-8

Authentication

All seller-authenticated endpoints require a valid session cookie issued by POST /api/auth/login. The cookie is HttpOnly; Secure; SameSite=Lax and is automatically sent by the browser. For server-to-server integrations, include it in the Cookie header.

http
POST /api/auth/login HTTP/1.1
Host: api.greatzern.com
Content-Type: application/json

{
  "email": "seller@example.com",
  "password": "your-password"
}

# Response sets:
# Set-Cookie: token=<jwt>; HttpOnly; Secure; Path=/

Public storefront endpoints (product listings, order placement, review submission) do not require authentication.

Webhooks

SokoHub sends an HTTP POST to your endpoint URL whenever a subscribed event occurs in your store. All webhook deliveries are signed with HMAC-SHA256 so you can verify they came from SokoHub.

Managing Endpoints

GET
/api/webhooks

List all webhook endpoints for your store

POST
/api/webhooks

Create a new webhook endpoint

GET
/api/webhooks/:id

Get endpoint details + last 20 deliveries

PUT
/api/webhooks/:id

Update URL, description, or subscribed events

PATCH
/api/webhooks/:id/toggle

Enable or disable an endpoint

DELETE
/api/webhooks/:id

Delete endpoint and all delivery history

POST
/api/webhooks/:id/test

Send a test ORDER_CREATED payload

GET
/api/webhooks/:id/deliveries

Paginated delivery log (50/page)

GET
/api/webhooks/events

List all available event types (public)

Create a Webhook

bash
curl -X POST https://api.greatzern.com/api/webhooks \
  -H "Content-Type: application/json" \
  -H "Cookie: token=<your-jwt>" \
  -d '{
    "url": "https://yourapp.com/hooks/sokohub",
    "description": "Order + payment events",
    "events": ["ORDER_CREATED", "ORDER_PAID", "ORDER_CANCELLED"]
  }'
json
{
  "id": "wh_01hx...",
  "url": "https://yourapp.com/hooks/sokohub",
  "description": "Order + payment events",
  "events": ["ORDER_CREATED", "ORDER_PAID", "ORDER_CANCELLED"],
  "secret": "4a7f2b...8e1d",
  "isActive": true,
  "createdAt": "2025-06-01T10:00:00.000Z"
}
Save your secret immediately. The secret is returned only on creation and is never exposed again. Use it to verify the X-SokoHub-Signature header on every incoming request.

Webhook Payload Shape

json
{
  "event": "ORDER_CREATED",
  "sellerId": "sel_01hz...",
  "timestamp": "2025-06-01T12:34:56.789Z",
  "data": {
    "orderId": "ord_01hz...",
    "referenceCode": "SKH-2025-00042",
    "status": "PENDING",
    "totalKes": 4500,
    "customerName": "Amina Wanjiku",
    "customerPhone": "+254712345678",
    "itemCount": 3
  }
}

Retry Policy

SokoHub makes up to 3 delivery attempts per event. Attempts are made with a 10-second timeout. Your endpoint must return a 2xx response within that window to be counted as a success. Delivery results are logged and visible via the deliveries endpoint.

Webhook Events

EventDescription
ORDER_CREATEDA new order has been placed on the storefront.
ORDER_PAIDM-Pesa payment confirmed for an order.
ORDER_CONFIRMEDSeller manually confirmed the order.
ORDER_DISPATCHEDOrder has been dispatched for delivery.
ORDER_DELIVEREDOrder marked as delivered.
ORDER_CANCELLEDOrder was cancelled.
PRODUCT_CREATEDA new product was added to the catalogue.
PRODUCT_UPDATEDAn existing product was edited.
PRODUCT_DELETEDA product was removed.
CUSTOMER_CREATEDA new customer record was created.
PAYMENT_RECEIVEDAny M-Pesa payment received (orders or subscriptions).
LOW_STOCK_ALERTProduct stock fell below the alert threshold.
REVIEW_SUBMITTEDA customer submitted a product review.

Signature Verification

Every webhook request includes an X-SokoHub-Signature header formatted as sha256=<hex-digest>. Verify it by computing HMAC-SHA256 of the raw request body with your endpoint secret.

Node.js / TypeScript

typescript
import { createHmac, timingSafeEqual } from 'crypto';

function verifySignature(
  rawBody: string | Buffer,
  signatureHeader: string,
  secret: string
): boolean {
  const expected = 'sha256=' + createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');

  const a = Buffer.from(signatureHeader);
  const b = Buffer.from(expected);
  if (a.length !== b.length) return false;
  return timingSafeEqual(a, b);
}

// Express / Fastify example
app.post('/hooks/sokohub', express.raw({ type: 'application/json' }), (req, res) => {
  const sig = req.headers['x-sokohub-signature'] as string;
  if (!verifySignature(req.body, sig, process.env.SOKOHUB_WEBHOOK_SECRET!)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }
  const event = JSON.parse(req.body.toString());
  // Handle event...
  res.sendStatus(200);
});

Python

python
import hmac, hashlib

def verify_signature(raw_body: bytes, signature_header: str, secret: str) -> bool:
    expected = 'sha256=' + hmac.new(
        secret.encode(), raw_body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature_header)

# Flask example
from flask import Flask, request, abort
app = Flask(__name__)

@app.route('/hooks/sokohub', methods=['POST'])
def webhook():
    sig = request.headers.get('X-SokoHub-Signature', '')
    if not verify_signature(request.get_data(), sig, SOKOHUB_SECRET):
        abort(401)
    event = request.get_json(force=True)
    # Handle event...
    return '', 200

Products API

GET
/api/products

List all products (paginated, filterable)

POST
/api/products

Create product with image (multipart/form-data)

POST
/api/products/bulk

Bulk create from JSON (CSV import)

GET
/api/products/:id

Get single product with variants

PUT
/api/products/:id

Update product fields

DELETE
/api/products/:id

Delete product

POST
/api/products/:id/variants

Add a variant to a product

PUT
/api/products/:id/variants/:vId

Update a variant

DELETE
/api/products/:id/variants/:vId

Delete a variant

bash
# List products
curl https://api.greatzern.com/api/products?page=1&limit=20 \
  -H "Cookie: token=<jwt>"

# Get a single product
curl https://api.greatzern.com/api/products/prod_01hz... \
  -H "Cookie: token=<jwt>"

Orders API

GET
/api/orders

List orders (auth required)

GET
/api/orders/:id

Get order details with items and timeline

POST
/api/orders

Place a new order (public — storefront)

PATCH
/api/orders/:id/confirm

Confirm an order

PATCH
/api/orders/:id/dispatch

Mark order as dispatched

PATCH
/api/orders/:id/deliver

Mark order as delivered

PATCH
/api/orders/:id/cancel

Cancel an order

json
// Order object
{
  "id": "ord_01hz...",
  "referenceCode": "SKH-2025-00042",
  "status": "PAID",
  "totalKes": 4500,
  "customerName": "Amina Wanjiku",
  "customerPhone": "+254712345678",
  "deliveryMethod": "DELIVERY",
  "mpesaReceipt": "QGH1234X5Y",
  "paidAt": "2025-06-01T12:36:00.000Z",
  "items": [
    {
      "productName": "African Print Tote Bag",
      "quantity": 2,
      "unitPriceKes": 1500,
      "subtotalKes": 3000
    }
  ]
}

Customers API

GET
/api/customers

List all customers for your store

GET
/api/customers/:id

Get customer profile, orders, and stats

PATCH
/api/customers/:id

Update customer information

GET
/api/customers/:id/orders

List all orders for a customer

Analytics API

GET
/api/analytics/overview

Revenue, order counts, conversion rate

GET
/api/analytics/revenue

Revenue time series (daily/weekly/monthly)

GET
/api/analytics/top-products

Best-selling products

GET
/api/analytics/customers

Customer acquisition and retention stats

All analytics endpoints accept a ?period=7d|30d|90d query parameter.

Error Reference

All errors return JSON with success: false and a human-readable message.

HTTPCodeMeaning
400VALIDATION_ERRORRequest body failed schema validation
401UNAUTHORIZEDMissing or invalid auth token
403FORBIDDENAuthenticated but insufficient permissions
404NOT_FOUNDResource does not exist or is not yours
409CONFLICTDuplicate resource (e.g. duplicate receipt)
429RATE_LIMITEDToo many requests — back off and retry
500INTERNAL_ERRORUnexpected server error
json
// Error response shape
{
  "success": false,
  "message": "Product not found",
  "statusCode": 404
}

Ready to integrate?

Open your dashboard, navigate to Settings → Integrations, and create your first webhook endpoint in seconds.

Go to Dashboard