OrderBridge REST API

Implement these endpoints on your middleware so OrderBridge can match catalogue data, check pricing and allocations, and create draft sales orders. Every call uses POST, JSON, and a required Bearer token. See the PO workflow for when each endpoint is called.

Endpoints

PathPurposeCalled during
POST /healthConnection validationSave / test in Settings
POST /catalog/customers/searchCustomer matchingPO match stage
POST /catalog/customers/addressesShip/bill address lookupPO match stage
POST /catalog/products/searchProduct matching by textPO match stage
POST /catalog/products/by-skuProduct matching by SKUPO match stage
POST /pricing/resolveUnit price validationPrice check
POST /credit/customerCredit exposure snapshotOptional credit check
POST /purchase-orders/listExisting PO readsOptional list views
POST /sales-orders/listExisting SO readsOptional list views
POST /sales-orders/usageHistorical order quantityAllocation caps
POST /sale-draftDraft sales order creationPO submit

Conventions

Request headers

All endpoints require:

  • Name
    Authorization
    Type
    string
    Description

    Bearer {token}: required. Missing or invalid tokens must return 401.

  • Name
    Content-Type
    Type
    string
    Description
    application/json

Request body

Every JSON body must include schemaVersion set to "1".

Catalogue hits and confidence

Search endpoints return a hits array of ERP entities. Do not compute or return confidence. Alderstone scores every hit after the response. Return rows in relevance order when possible; Alderstone uses position as a tie-breaker.

Request

POST
{baseUrl}/…
{
  "schemaVersion": "1"
}

Response

200
{
  "schemaVersion": "1",
  "ok": true,
  "service": "your-erp"
}

Error responses

Return this JSON shape for all 4xx and 5xx responses. See Errors for the full code reference:

  • Name
    error
    Type
    string
    Description

    Human-readable message.

  • Name
    code
    Type
    string
    Description

    One of UNAUTHORIZED, FORBIDDEN, INVALID_JSON, VALIDATION_FAILED, BAD_REQUEST, INTERNAL_ERROR.

  • Name
    details
    Type
    object
    Description

    Optional; commonly used for 422 validation (field errors).

Response

4xx / 5xx
{
  "schemaVersion": "1",
  "error": "Human-readable message",
  "code": "UNAUTHORIZED",
  "details": {}
}

POST/health

Health check

Verify connectivity and contract version. Alderstone calls this on Save and Test in Integrations.

Try in sandbox

Request

POST
/health
{
  "schemaVersion": "1"
}

Response

200
{
  "schemaVersion": "1",
  "ok": true,
  "service": "your-erp"
}

Errors

401
{
  "schemaVersion": "1",
  "error": "Missing or invalid Authorization header. Use: Authorization: Bearer <token>",
  "code": "UNAUTHORIZED"
}

POST/catalog/customers/search

Search customers

Search customers by name for PO buyer matching.

Body attributes

  • Name
    query
    Type
    string
    Description

    Search text. Empty string returns the first page.

  • Name
    limit
    Type
    integer
    Description

    Max results (1–500, default 20).

ERP SQL reference

The ERP reference panel shows example queries per dialect: a browse query for empty query strings and an indexed name search. Keep them read-only SELECTs (no FOR UPDATE, no writes) with indexed predicates and a LIMIT so the planner can stop early.

Try in sandbox

ERP reference

-- Browse (empty query)
SELECT c.external_id AS erp_id,
     c.display_name AS erp_name,
     c.city
FROM erp.customers AS c
WHERE c.is_active = TRUE
ORDER BY c.display_name
LIMIT $1;  -- :limit

-- Fuzzy name search (pg_trgm, PostgreSQL 9.1+)
-- CREATE INDEX customers_name_trgm_idx
--   ON erp.customers USING gin (display_name gin_trgm_ops);
SELECT c.external_id, c.display_name, c.city
FROM erp.customers AS c
WHERE c.is_active = TRUE
 AND c.display_name % $1
ORDER BY similarity(c.display_name, $1) DESC, c.display_name
LIMIT $2;

Request

POST
/catalog/customers/search
{
  "schemaVersion": "1",
  "query": "acme",
  "limit": 20
}

Response

200
{
  "schemaVersion": "1",
  "hits": [
    {
      "erpId": "STUB-CUST-001",
      "erpName": "Acme Retail (Stub)",
      "city": "Cape Town"
    },
    {
      "erpId": "STUB-CUST-002",
      "erpName": "Beta Wholesale (Stub)",
      "city": "Johannesburg"
    }
  ]
}

Errors

401
{
  "schemaVersion": "1",
  "error": "Missing or invalid Authorization header. Use: Authorization: Bearer <token>",
  "code": "UNAUTHORIZED"
}

POST/catalog/customers/addresses

Customer addresses

List ship-to or billing addresses for a matched customer.

Body attributes

  • Name
    customerId
    Type
    string
    Description

    ERP customer id from search (erpId in hits).

  • Name
    kind
    Type
    string
    Description

    Optional. shipping or billing. Omit to return all kinds.

ERP SQL reference

The ERP reference panel shows the point lookup per dialect. Index (customer_id, kind) or (customer_id) plus a filter. No row locks required; a plain read under the database default isolation level (READ COMMITTED on PostgreSQL) is sufficient.

Try in sandbox

ERP reference

-- Point lookup by customer; index (customer_id, kind)
SELECT a.external_id AS erp_location_id,
     a.label AS erp_label,
     trim(both ', ' from concat_ws(', ',
       a.line1, a.city, a.region, a.postal_code)) AS address,
     a.kind AS address_kind
FROM erp.customer_addresses AS a
WHERE a.customer_id = $1
 AND a.is_active = TRUE
 AND ($2::text IS NULL OR a.kind = $2)
ORDER BY a.is_default DESC, a.label
LIMIT 50;

Request

POST
/catalog/customers/addresses
{
  "schemaVersion": "1",
  "customerId": "STUB-CUST-001",
  "kind": "shipping"
}

Response

200
{
  "schemaVersion": "1",
  "hits": [
    {
      "erpLocationId": "STUB-ADDR-SHIP-001",
      "erpLabel": "Main warehouse",
      "address": "1 Dock Rd, Cape Town",
      "addressKind": "shipping"
    },
    {
      "erpLocationId": "STUB-ADDR-BILL-001",
      "erpLabel": "Accounts payable",
      "address": "2 Finance Ave, Cape Town",
      "addressKind": "billing"
    }
  ]
}

Errors

401
{
  "schemaVersion": "1",
  "error": "Missing or invalid Authorization header. Use: Authorization: Bearer <token>",
  "code": "UNAUTHORIZED"
}

POST/catalog/products/search

Search products

Search products by name or description for line matching.

Body attributes

  • Name
    query
    Type
    string
    Description

    Search text. Empty string returns the first page.

  • Name
    limit
    Type
    integer
    Description

    Max results (1–500, default 20).

ERP SQL reference

The ERP reference panel shows a browse query and a full-text search per dialect. Engines without full-text support can fall back to a prefix match on SKU plus a case-insensitive LIKE on name; rank SKU-prefix hits first.

Try in sandbox

ERP reference

-- Browse (empty query)
SELECT p.external_id AS erp_product_id,
     p.sku AS erp_sku,
     p.name AS erp_desc
FROM erp.products AS p
WHERE p.is_active = TRUE
ORDER BY p.sku
LIMIT $1;

-- Text search (full-text, GIN on tsvector)
-- CREATE INDEX products_search_idx
--   ON erp.products USING gin (search_tsv);
SELECT p.external_id, p.sku, p.name
FROM erp.products AS p
WHERE p.is_active = TRUE
 AND p.search_tsv @@ plainto_tsquery('simple', $1)
ORDER BY ts_rank(p.search_tsv, plainto_tsquery('simple', $1)) DESC,
        p.sku
LIMIT $2;

Request

POST
/catalog/products/search
{
  "schemaVersion": "1",
  "query": "widget",
  "limit": 20
}

Response

200
{
  "schemaVersion": "1",
  "hits": [
    {
      "erpSku": "DEMO-SKU",
      "erpDesc": "Demo product (stub)",
      "erpProductId": "STUB-PROD-001"
    },
    {
      "erpSku": "WIDGET-42",
      "erpDesc": "Widget 42 (stub)",
      "erpProductId": "STUB-PROD-002"
    }
  ]
}

Errors

401
{
  "schemaVersion": "1",
  "error": "Missing or invalid Authorization header. Use: Authorization: Bearer <token>",
  "code": "UNAUTHORIZED"
}

POST/catalog/products/by-sku

Product by SKU

Exact or partial SKU lookup during product matching.

Body attributes

  • Name
    sku
    Type
    string
    Description

    SKU from the PO line or operator search.

  • Name
    limit
    Type
    integer
    Description

    Max hits (1–100, default 10).

ERP SQL reference

The ERP reference panel shows both paths per dialect: prefer an exact match on a unique sku column first, falling back to a prefix match when the PO line is truncated. Avoid %suffix or %infix% patterns on large tables; they force sequential scans and increase lock contention risk on heap pages under concurrent writes.

Try in sandbox

ERP reference

-- Exact match first (unique btree on sku, cheapest path)
SELECT p.external_id AS erp_product_id,
     p.sku AS erp_sku,
     p.name AS erp_desc
FROM erp.products AS p
WHERE p.sku = $1
LIMIT 1;

-- Partial / prefix SKU (leading literal only, index-friendly)
SELECT p.external_id, p.sku, p.name
FROM erp.products AS p
WHERE p.sku LIKE $1 || '%'
ORDER BY length(p.sku), p.sku
LIMIT $2;

Request

POST
/catalog/products/by-sku
{
  "schemaVersion": "1",
  "sku": "DEMO-SKU",
  "limit": 10
}

Response

200
{
  "schemaVersion": "1",
  "hits": [
    {
      "erpSku": "DEMO-SKU",
      "erpDesc": "Demo product (stub)",
      "erpProductId": "STUB-PROD-001"
    }
  ]
}

Errors

401
{
  "schemaVersion": "1",
  "error": "Missing or invalid Authorization header. Use: Authorization: Bearer <token>",
  "code": "UNAUTHORIZED"
}

POST/pricing/resolve

Resolve pricing

Return the unit price Alderstone should compare against the PO line price. Use null for systemUnitPrice when no price exists.

Body attributes

  • Name
    customerErpId
    Type
    string
    Description

    Customer erpId from catalogue search.

  • Name
    productErpId
    Type
    string
    Description

    Product erpProductId from catalogue search.

Try in sandbox

Request

POST
/pricing/resolve
{
  "schemaVersion": "1",
  "customerErpId": "STUB-CUST-001",
  "productErpId": "STUB-PROD-001"
}

Response

200
{
  "schemaVersion": "1",
  "systemUnitPrice": 99.5,
  "source": "tier-1"
}

Errors

401
{
  "schemaVersion": "1",
  "error": "Missing or invalid Authorization header. Use: Authorization: Bearer <token>",
  "code": "UNAUTHORIZED"
}

POST/credit/customer

Customer credit

Optional credit exposure snapshot for the customer. Fields may be null when credit is not tracked.

Body attributes

  • Name
    customerErpId
    Type
    string
    Description

    Customer erpId from catalogue search.

Try in sandbox

Request

POST
/credit/customer
{
  "schemaVersion": "1",
  "customerErpId": "STUB-CUST-001"
}

Response

200
{
  "schemaVersion": "1",
  "creditLimit": 100000,
  "creditUsed": 12500,
  "currency": "ZAR"
}

Errors

401
{
  "schemaVersion": "1",
  "error": "Missing or invalid Authorization header. Use: Authorization: Bearer <token>",
  "code": "UNAUTHORIZED"
}

POST/purchase-orders/list

List purchase orders

List existing purchase orders (optional customer filter).

Body attributes

  • Name
    customerErpId
    Type
    string
    Description

    Optional; filter by customer erpId.

  • Name
    limit
    Type
    integer
    Description

    Max rows (1–100, default 20).

Try in sandbox

Request

POST
/purchase-orders/list
{
  "schemaVersion": "1",
  "customerErpId": "STUB-CUST-001",
  "limit": 20
}

Response

200
{
  "schemaVersion": "1",
  "orders": [
    {
      "erpPoId": "STUB-PO-001",
      "poNumber": "PO-STUB-001",
      "customerErpId": "STUB-CUST-001",
      "status": "open",
      "orderDate": "2026-01-15"
    }
  ]
}

Errors

401
{
  "schemaVersion": "1",
  "error": "Missing or invalid Authorization header. Use: Authorization: Bearer <token>",
  "code": "UNAUTHORIZED"
}

POST/sales-orders/list

List sales orders

List sales orders for a customer (optional filter).

Body attributes

  • Name
    customerErpId
    Type
    string
    Description

    Optional; filter by customer erpId.

  • Name
    limit
    Type
    integer
    Description

    Max rows (1–100, default 20).

Try in sandbox

Request

POST
/sales-orders/list
{
  "schemaVersion": "1",
  "customerErpId": "STUB-CUST-001",
  "limit": 20
}

Response

200
{
  "schemaVersion": "1",
  "orders": [
    {
      "erpSoId": "STUB-SO-001",
      "soNumber": "SO-STUB-001",
      "customerErpId": "STUB-CUST-001",
      "status": "authorised",
      "orderDate": "2026-02-01"
    }
  ]
}

Errors

401
{
  "schemaVersion": "1",
  "error": "Missing or invalid Authorization header. Use: Authorization: Bearer <token>",
  "code": "UNAUTHORIZED"
}

POST/sales-orders/usage

Sales usage

Sum ordered quantity for a customer + SKU in an allocation window. Use periodEnd: null for an open-ended window.

Body attributes

  • Name
    customerErpId
    Type
    string
    Description

    Customer erpId.

  • Name
    sku
    Type
    string
    Description

    Product SKU.

  • Name
    periodStart
    Type
    string
    Description

    Inclusive start date (YYYY-MM-DD).

  • Name
    periodEnd
    Type
    string | null
    Description

    Inclusive end date, or null for no upper bound.

Try in sandbox

Request

POST
/sales-orders/usage
{
  "schemaVersion": "1",
  "customerErpId": "STUB-CUST-001",
  "sku": "DEMO-SKU",
  "periodStart": "2026-01-01",
  "periodEnd": "2026-12-31"
}

Response

200
{
  "schemaVersion": "1",
  "units": 42
}

Errors

401
{
  "schemaVersion": "1",
  "error": "Missing or invalid Authorization header. Use: Authorization: Bearer <token>",
  "code": "UNAUTHORIZED"
}

POST/sale-draft

Create draft sale

Create a draft sales order when a PO is confirmed. Return saleId (ERP internal reference). When you also return saleNumber, OrderBridge stores and displays that human-readable SO number instead of saleId.

Body attributes

  • Name
    poNumber
    Type
    string
    Description

    Purchase order number from the uploaded PO.

  • Name
    currency
    Type
    string
    Description

    ISO 4217 code (3 letters), e.g. ZAR.

  • Name
    lines
    Type
    array
    Description

    One or more line objects (see below).

Line attributes

  • Name
    sku
    Type
    string
    Description

    Product SKU (required).

  • Name
    qty
    Type
    integer
    Description

    Quantity: positive integer.

  • Name
    unitPrice
    Type
    number
    Description

    Unit price from the PO line.

Try in sandbox

Request

POST
/sale-draft
{
  "schemaVersion": "1",
  "poNumber": "DEMO-PO-001",
  "currency": "ZAR",
  "lines": [
    {
      "sku": "DEMO-SKU",
      "qty": 10,
      "unitPrice": 12.5
    },
    {
      "sku": "WIDGET-42",
      "qty": 2,
      "unitPrice": 99
    }
  ]
}

Response

200
{
  "schemaVersion": "1",
  "saleId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "saleNumber": "SO-12345",
  "status": "draft",
  "receivedAt": "2026-05-26T10:15:30.000Z"
}

Errors

401
{
  "schemaVersion": "1",
  "error": "Missing or invalid Authorization header. Use: Authorization: Bearer <token>",
  "code": "UNAUTHORIZED"
}

Test in the sandbox

The recommended way to test your middleware is the Developer Sandbox. It sends the same requests Alderstone makes, validates responses against this contract, and includes a built-in mock ERP when you have not deployed middleware yet.

Open sandbox

Quick link

GET
/sandbox
/sandbox?product=orderbridge&endpoint=health

Was this page helpful?