API Overview

Base URLhttps://api.workshop17.co.za
FrameworkJXP (jexpress-2) — opinionated API framework generating REST endpoints from Mongoose schemas
Version2.0.0
DatabaseMongoDB (via Mongoose ODM)
Models88 resources
Docsapi.workshop17.co.za
SourceJXP framework: j-norwood-young/jexpress-2 (GitHub)

Endpoint Patterns

Every model automatically gets CRUD endpoints following this pattern:

GET /api/{model} List all (paginated)
GET /api/{model}/{_id} Get single document
GET /count/{model} Count documents
POST /api/{model} Create document
POST /query/{model} Advanced query (MongoDB syntax)
POST /aggregate/{model} Aggregation pipeline
PUT /api/{model}/{_id} Update document (acts as PATCH)
DELETE /api/{model}/{_id} Soft-delete (sets _deleted=true)

No PATCH or OPTIONS methods. PUT functions as a partial update. DELETE is a soft-delete (document is flagged, not removed).

Query Parameters

Pagination

ParamExampleNotes
limit?limit=20Max documents per page
page?page=2Page number (starts at 1)

Response includes: limit, page_count, page, next for traversal.

Population (Related Objects)

ParamExampleNotes
autopopulate?autopopulate=1Expand ALL linked objects
populate?populate=organisation_idExpand single reference
populate[]?populate[]=organisation_id&populate[]=location_idExpand multiple
populate[ref]?populate[organisation_id]=name,emailExpand with field selection

Filtering

ParamExampleNotes
Exact match?filter[status]=activeField equals value
Greater/less?filter[amount]=$gte:1000MongoDB operators: $gte, $lte, $gt, $lt, $ne
Regex?filter[name]=$regex:/tech/iCase-insensitive pattern match

Search

ParamExampleNotes
Field search?search[email]=johnCase-insensitive substring match
Full-text?search=johnSearches text-indexed fields

Sorting & Fields

ParamExampleNotes
sort?sort=-createdAtPrefix - for descending
fields?fields=name,email,statusReturn only specified fields

Response Format

Single Document

{
  "data": {
    "_id": "60f7b2...",
    "name": "James Mokoena",
    "email": "james@techflow.co.za",
    "organisation_id": "60f7a1...",
    "status": "active",
    "_deleted": false,
    "_owner_id": "60f7b2...",
    "createdAt": "2024-01-15T08:30:00Z",
    "updatedAt": "2026-03-20T14:22:00Z"
  }
}

Collection

{
  "count": 342,
  "limit": 20,
  "page": 1,
  "page_count": 18,
  "next": "/api/user?page=2&limit=20",
  "data": [ ... ]
}

Count

{ "count": 342 }

Returns -1 for collections exceeding 100,000 documents.

Advanced Query

POST /query/invoice
Content-Type: application/json

{
  "query": {
    "$and": [
      { "status": "AUTHORISED" },
      { "total": { "$gte": 1000 } },
      { "organisation_id": "60f7a1..." }
    ]
  }
}

Aggregation Pipeline

POST /aggregate/ledger
Content-Type: application/json

[
  { "$match": { "user_id": "60f7b2..." } },
  { "$group": {
      "_id": "$cred_type",
      "total": { "$sum": "$amount" }
  }}
]

System Fields (All Models)

Every document includes these fields automatically:

FieldTypeNotes
_idObjectIDPrimary key (auto-generated)
_deletedBooleanSoft-delete flag (default: false)
_owner_idObjectIDUser who created the document
_updated_by_idObjectIDUser who last updated
createdAtDateImmutable creation timestamp
updatedAtDateLast modification timestamp
__vNumberMongoose version key

All 88 Models

Every model below has auto-generated CRUD endpoints at /api/{name}. Click any model to view its schema on the live API docs.

Core (Members, Spaces, Bookings)

Billing & Financial

CRM & Sales

Infrastructure & Access

Xero & Payments

System & Config

Implications for the New Platform

The live API at api.workshop17.co.za confirms this is the same WorkSpaceMan codebase we reviewed. Key takeaways for the Proximity Green build:

What the JXP Pattern Gets Right

What Needs to Change

Migration Path

During Phase 6 (migration), the new platform can provide a compatibility layer that accepts the JXP query format and translates to PostgreSQL queries. This allows existing API consumers (if any external integrations exist) to transition gradually.