Commercial portal
Fleet accounting, payments, billable services, and partner commissions
Commercial portal
These routes require operator type Commercial. A merchant JWT is 403 (portal guard), not 501. Live Commercial JWT on 2026-09-10:
| Call | Result |
|---|---|
GET /commercial/accounting-overview | 200 — 1,150 SAR collected, 9 payment-status rows |
GET /commercial/fleet-metrics | 200 — 29 orgs / 7,575 orders / 677,405.57 SAR |
GET /commercial/billable-services | 200 {items:[],total_items:0} |
GET /commercial/recent-payments?page=1&perPage=5 | 200 {page,perPage,totalItems,items} |
GET /commercial/customers | 404 — never designed (the previous portal read the organizations collection directly) |
GET /commercial/tenant-credit | 404 — there is no GET; credit is POST + GET …/preview |
Do not invent GET /api/v1/commercial/customers or a contacts loop over /api/collections/… (that path is 404). Partner billing-impact is still absent. Leads are GET /api/v1/leads, not a commercial-prefixed twin.
Mounted
| Endpoint | Envelope notes |
|---|---|
GET /api/v1/commercial/accounting-overview | Snake_case object + timestamp |
GET /api/v1/commercial/fleet-metrics | Fleet totals + organizations[] |
GET /api/v1/commercial/recent-payments | CamelCase pager: page, perPage, totalItems, items. Rows are snake_case |
GET /api/v1/commercial/balance-adjustments | Same camelCase pager. perPage default 25, clamped to 100 |
GET /api/v1/commercial/billable-services | {items,total_items} — catalog |
POST /api/v1/commercial/billable-services | Create a catalog row |
PATCH /api/v1/commercial/billable-services/{id} | Price / metadata |
POST /api/v1/commercial/billable-services/{id}/assets | Upload |
POST /api/v1/commercial/billable-services/assign | Attach to an install |
GET /api/v1/commercial/billable-services/assignments | |
PATCH /api/v1/commercial/billable-services/assignments/{orgAppId} | Update assignment |
DELETE /api/v1/commercial/billable-services/assignments/{orgAppId} | Unassign |
GET /api/v1/commercial/erp-commission-rates | Partner share + tenants |
GET /api/v1/commercial/partner-users | Payees |
GET /api/v1/commercial/partner-commissions | Bank-transfer payouts |
POST /api/v1/commercial/partner-commissions | Record a payout |
GET /api/v1/commercial/partner-commissions/{id}/proof | Receipt blob |
GET /api/v1/commercial/tenant-credit/preview | Same CheckBillingStatus as the write |
POST /api/v1/commercial/tenant-credit | Apply credit. Not GET |
GET /api/v1/leads | Marketing pipeline, cap 500. See Leads |
The React commercial Billing tab already assigns billable services. Do not rebuild it.
Accounting overview (live shape)
{
"timestamp": "2026-09-10T14:21:50Z",
"totals": {
"cash_collected_sar": 1150,
"cash_collected_30d_sar": 460,
"wallet_balance_sar": 0,
"outstanding_branch_fees_sar": 0,
"overdue_branch_fees_sar": 0,
"credits_issued_sar": 1150,
"refund_like_debits_sar": 0,
"recognized_vat_sar": 150,
"net_revenue_sar": 1000
},
"receivables": {
"current_sar": 0,
"days_1_to_30_sar": 0,
"days_31_to_60_sar": 0,
"days_61_plus_sar": 0,
"pending_rows": 0,
"overdue_rows": 0
},
"status_breakdown": [
{ "status": "COMPLETED", "count": 5, "amount": 1150 }
],
"ledger_breakdown": [
{ "entry_type": "subscription_cycle_debit", "direction": "debit", "count": 5, "amount": 1150 }
],
"recent_branch_fees": [],
"reconciliation": {
"successful_payments_sar": 1150,
"ledger_credits_sar": 1150,
"variance_sar": 0
}
}Money fields are SAR numbers, not halalas.
Fleet metrics (live shape)
{
"timestamp": "2026-09-10T14:21:50Z",
"total_organizations": 29,
"active_organizations": 29,
"inactive_organizations": 0,
"total_branches": 2,
"total_users": 19,
"total_orders": 7575,
"total_order_value": 677405.57,
"total_collected_sar": 1150,
"collected_last_30d_sar": 460,
"organizations": [
{
"organization_id": "3xrde8yfgeqeaas",
"name_en": "Nova Plus Medical Pharmacy",
"name_ar": "صيدلية نوفا بلس الطبية",
"is_active": true,
"branch_count": 2,
"user_count": 3,
"order_count": 7575,
"order_value": 677405.57,
"collected_sar": 1150
}
]
}