Sage Evolution Integration Platform BUILT FOR BUSINESS

Everything connected. Every transaction recorded.

OLIMEM connects your business channels and systems to your Sage Evolution database in real time — accurately, securely and automatically.

In partnership with ZB Bank BancABC
Connected channels WhatsApp Resident Portal Banks Android POS Sage Evolution
Channels → OLIMEM Hub → Sage Database LIVE CONNECTION
WhatsApp Payments & Inquiries
Resident Portal Web & Mobile
Bank Payments Multiple Banks
Sage Evolution
Sage Evolution Sage Database
OLIMEM INTEGRATION HUB
ALL SYSTEMS CONNECTED
85+
Organisations supported
7
API surfaces documented
SHA-256
API key hashing
USD · ZWG
Supported currencies
Real-time SyncInstant updates across channels
Secure & CompliantBank-grade security & audit ready
Always AvailableReliable integration performance
Actionable InsightsPowerful dashboards & reports
Expert SupportWe're here to help you succeed
Architecture

Channels → hub → Sage Database

Every council runs its own local connector that dials out to the hub — the hub never reaches into a council's network.

Channels
  • WhatsApp bot (per-council number)
  • Zaka / Plumtree resident portals
  • Bank payments
  • Android receipting devices
OLIMEM Hub
  • Laravel 12 · client-scoped API
  • API-key / Sanctum tenant resolution
  • Idempotent payment posting
  • Append-only audit trail
Client back office
  • SageConnectorService (per council)
  • Sage Evolution, on-LAN
  • Connector dials out, never in
Powered by Sage Evolution
Our Clients

Trusted by organisations across Zimbabwe

Businesses, banks, schools, healthcare providers, government organisations and local authorities connected through OLIMEM.

85+organisations
Alviva International (Axiz Pty Ltd)
Business
Apex Lodge (Gwanda)
Hospitality
AXIZ (PTY) LTD
Business
Banc ABC
Financial
Beitbridge Municipality
Local Authority
Bella Investments
Business
Binga RDC
Local Authority
Bubi RDC
Local Authority
Buhera RDC
Local Authority
Bulilima RDC
Local Authority
Chanburry Flat
Business
Chanbury Court
Business
Chipinge RDC
Local Authority
Chivi RDC
Local Authority
City of Gweru
Business
Divine Exquisite
Business
First Capital Bank
Financial
First Capital Bank (Affluent Branch)
Financial
Fiscal Gateway
Technology
Gokwe South RDC
Local Authority
Golden Knot
Hospitality
Guard Alert Pvt Ltd
Business
Gutu RDC
Local Authority
Gwanda RDC
Local Authority
Gwanda State
Business
Hurungwe RDC
Local Authority
Hwange RDC
Local Authority
I Think Systems
Technology
Inyathi High School
Education
IPMZ
Education
Konica Minolta Zim
Technology
Lumela Tech
Technology
Mahatshula Staff House
Business
Makonde RDC
Local Authority
Makoni RDC
Local Authority
Mangwe RDC
Local Authority
Manyame RDC
Local Authority
Marist Brothers Sec Sch.
Business
Marymount Teachers College
Education
Masvingo RDC
Local Authority
Matobo RDC
Local Authority
Mauriberg Investments
Business
Mazowe RDC
Local Authority
Mberengwa RDC
Local Authority
Mhondoro Ngezi RDC
Local Authority
Ministry of Finance
Government
MOG
Business
Mudzi RDC
Local Authority
Murewa RDC
Local Authority
Mutirikwi Sub-catchment Council
Business
Muzarabani RDC
Local Authority
Nkayi RDC
Local Authority
NUST
Education
Nyaminyami RDC
Local Authority
Nyanga High School
Education
Nyanga RDC
Local Authority
Para RDC
Local Authority
Plumtree Town Council
Local Authority
Quad Core
Business
Runde RDC
Local Authority
Ruvents Supply Chain (Pvt) Ltd
Business
Sage SA
Business
Sanyati RDC
Local Authority
St Patricks Hospital
Healthcare
Stratedge Gate Business
Business
Syvern Investments
Business
Talent Legacy Solutions
Business
Tempo Tyres
Business
Tertiary Education Service Council (TESC)
Education
Tongogara RDC
Local Authority
Tsholotsho RDC
Local Authority
Tyre Depot
Business
UBH
Healthcare
Umguza RDC
Local Authority
Umzingwane RDC
Local Authority
Upper Runde Sub-catchment Council
Business
Vungu RDC
Local Authority
Zaka RDC
Local Authority
Zibagwe RDC
Local Authority
Zim Hosiery
Business
Zimbabwe Foreign Service Institute
Government
Zimra/Company Registrations
Government
Zimre Properties
Business
Zomac Digital Equity House
Business
Zvimba RDC
Local Authority
Design principles

Built for public money, across authorities

Identity from the key, never the request

A caller proves its council by holding that council's API key — it can never assert a council via a request field.

Connectors dial out, the hub never dials in

Every council connector reaches the hub over HTTPS. No inbound hole is ever opened into a council's network.

Idempotent, always

Every payment carries a unique reference. A retried request after a dropped line can never post the same payment twice.

In the field

Real payments, real councils

From a resident tapping to pay on WhatsApp to a receipting officer reconciling on Sage — OLIMEM sits behind every one of these moments.

Resident paying a council bill from a tablet
Resident portal payments, on any device
Customer logging into online banking to pay a bill
Bank login → Bank Payment API → receipt
Cash collections reconciled into Sage Evolution
Cash & card collections, reconciled in Sage
Integration

API Reference for integrators

Everything needed to integrate against the Olimem Customer Portal API — Portal, Bot, Payment Gateway, Transact and Sync surfaces — with full request headers, payloads and responses.

Version 1.0 API Version v1 Base URL https://your-domain.com/api Format JSON · REST Auth Sanctum · API Key/Secret Living document — fields are still being finalised

01 Introduction

The Olimem Customer Portal API provides a secure integration framework allowing mobile applications, web applications, payment gateways, chatbots, and third-party systems to interact with the municipal billing and receipting platform.

The API supports
  • Customer Authentication
  • Account Balance Enquiries
  • Statements
  • Notifications
  • Service Requests
  • Meter Reading Submissions
  • Outage Management
  • Document Distribution
  • Payment Status Verification
  • Transaction Posting
  • Mobile POS Synchronisation

02 Authentication

2.1 Customer Authentication

Login authenticates as the calling council's customer-facing application first, then as the customer — every other Customer API request after that uses a Laravel Sanctum bearer token. Customer API is always council-scoped (no global-key support — see §4 Bot API for the channels that do span multiple clients).

POST /api/customer/login
Request headers
Content-Type: application/json
Accept: application/json
X-API-KEY: YOUR_COUNCIL_CUSTOMER_KEY
X-API-SECRET: YOUR_COUNCIL_CUSTOMER_SECRET
Request payload
{
  "account_number": "12345",
  "password": "Password@123"
}
X-API-KEY / X-API-SECRET identify which council's customer-facing application is calling (channel customer_portal, one key per council — issued from the hub's API Clients page) — not the customer. They scope the account_number lookup to one council so the same account number in two different councils can never collide. account_number / password then identify the specific customer within that council. The bearer token returned below is per-customer and is what every other Customer API request uses — it's safe to store in the browser precisely because it only ever represents one resident, not the whole council.
Successful response
{
  "success": true,
  "token": "1|fdfgdfgdfgdfgdfgdfg",
  "user": {
    "id": 1,
    "name": "John Doe",
    "fname": "John",
    "lname": "Doe",
    "email": "john@example.com",
    "phone": "0771234567",
    "account_no": "12345"
  },
  "account": {
    "name": "John Doe",
    "account": "12345",
    "currency": "USD",
    "balance": "123.45",
    "address": "123 Main Street"
  }
}
Failed response
{
  "success": false,
  "message": "Invalid account number or password."
}
Authorization header (all subsequent Customer API requests)
Authorization: Bearer {TOKEN}

03 Customer API

All endpoints below require the Authorization: Bearer {TOKEN} header obtained from Customer login. Formerly Portal API — renamed, same behavior.

3.1 Logout

POST/api/customer/logout
Response
{
  "success": true,
  "message": "Logged out."
}

3.2 Get Customer Profile

GET/api/customer/profile
Response
{
  "success": true,
  "user": {
    "id": 1,
    "name": "John Doe",
    "fname": "John",
    "lname": "Doe",
    "email": "john@example.com",
    "phone": "0771234567",
    "account_no": "12345"
  },
  "account": {
    "name": "John Doe",
    "account": "12345",
    "currency": "USD",
    "balance": "123.45",
    "address": "Bulawayo"
  }
}

3.3 Update Profile

PUT/api/customer/profile
Request payload
{
  "fname": "Kelvin",
  "lname": "Mtetwa",
  "phone": "0771234567",
  "current_password": "OldPassword",
  "new_password": "NewPassword123",
  "new_password_confirmation": "NewPassword123"
}
Response
{
  "success": true,
  "message": "Profile updated."
}
Password fields are optional on this endpoint — only send them when the customer is actually changing their password.

3.4 Account Balance

GET/api/customer/balance
Response
{
  "success": true,
  "account": "12345",
  "name": "John Doe",
  "currency": "USD",
  "balance": "123.45"
}

3.5 Customer Statement

GET/api/customer/statement
Response
{
  "success": true,
  "account": "12345",
  "currency": "USD",
  "balance": "123.45",
  "receipts": [
    {
      "id": 1,
      "reference": "REC-0001",
      "amount": "50.00",
      "date": "28 Jul 2026",
      "source": "Customer"
    }
  ]
}

3.6 Notifications

GET/api/customer/notifications
Response
{
  "success": true,
  "unread_count": 3,
  "notifications": [
    {
      "id": 1,
      "type": "info",
      "title": "Water Outage",
      "message": "Scheduled maintenance.",
      "is_read": false,
      "created_at": "2026-07-28 10:00"
    }
  ]
}
POST/api/customer/notifications/{id}/read
Response
{
  "success": true
}

3.7 Service Requests

POST/api/customer/service-requests
Request payload
{
  "category": "water_leak",
  "title": "Burst Pipe",
  "description": "Water leaking outside property.",
  "property_id": 145
}
Response
{
  "success": true,
  "reference": "SR-ABCD1234",
  "message": "Service request submitted."
}
Available categories
  • water_leak
  • sewer_burst
  • refuse_not_collected
  • streetlight_fault
  • billing_query
  • meter_fault
  • road_damage
  • other

3.8 Meter Readings

POST/api/customer/meter-readings
Request payload
{
  "property_id": 145,
  "service_id": 5,
  "reading_date": "2026-07-28",
  "reading_value": 2530,
  "meter_number": "MTR-001",
  "notes": "Customer reading."
}
Response
{
  "success": true,
  "id": 10,
  "message": "Meter reading submitted successfully."
}
GET/api/customer/meter-readings
Response
{
  "success": true,
  "readings": [
    {
      "id": 1,
      "reading_date": "28 Jul 2026",
      "reading_value": 2500,
      "previous_reading": 2450,
      "consumption": 50,
      "meter_number": "MTR-001",
      "status": "pending"
    }
  ]
}

3.9 Outages

GET/api/customer/outages
Response
{
  "success": true,
  "outages": [
    {
      "id": 1,
      "title": "Water Outage",
      "description": "Pipe maintenance",
      "type": "water",
      "area": "CBD",
      "severity": "high",
      "status": "scheduled"
    }
  ]
}

3.10 Documents

GET/api/customer/documents
Response
{
  "success": true,
  "documents": [
    {
      "id": 1,
      "title": "July Invoice",
      "category": "Invoice",
      "file_name": "invoice.pdf",
      "file_type": "pdf",
      "file_size": "1.2 MB",
      "url": "https://yourdomain.com/storage/invoice.pdf"
    }
  ]
}

04 Bot API

Used by the per-council WhatsApp bot only. Authenticated with an API key/secret pair instead of a bearer token — a per-council key (channel whatsapp), never a bank's global key. Banks have their own separate §4c Bank API, with its own routes and its own credential shape — not shared with this one.

Authentication headers (all Bot API requests)
X-API-KEY: YOUR_API_KEY
X-API-SECRET: YOUR_API_SECRET

Bot Login

POST/api/bot/login
Request payload
{
  "account_number": "12345",
  "password": "Password@123"
}
Response
{
  "success": true,
  "name": "John Doe",
  "account": "12345",
  "currency": "USD",
  "balance": "123.45"
}

Account Lookup

GET/api/bot/account/{account}
Response
{
  "success": true,
  "dc_link": 123,
  "account": "12345",
  "name": "John Doe",
  "currency": "USD",
  "balance": "123.45"
}
GET/api/bot/balance/{account}

Balance Lookup — same response shape as Account Lookup.

GET/api/bot/statement/{account}

Statement Lookup — same response shape as Customer Statement.

Service Request

POST/api/bot/service-request
Request payload
{
  "account": "12345",
  "category": "water_leak",
  "title": "Burst Pipe",
  "description": "Pipe burst outside property."
}

04b Accounts API

Lists every client (account) in the calling council — account number, name, phone, address, balance. Usable by any front-door key (Banks, WhatsApp, Customer Portal) for the council it belongs to.

Authentication headers
X-API-KEY: YOUR_API_KEY
X-API-SECRET: YOUR_API_SECRET
GET/api/accounts
Query parameters
  • per_page (optional, default 50, max 200)
Response
{
  "success": true,
  "council": { "code": "RIVERVIEW_TC", "name": "Riverview Town Council" },
  "clients": [
    {
      "account_number": "56114",
      "name": "Shanel Connelly",
      "phone": "0728723933",
      "address": "8953 Herbert Court",
      "currency": "USD",
      "balance": "113.88"
    }
  ],
  "meta": { "current_page": 1, "per_page": 50, "total": 202, "last_page": 5 }
}
Scoped to the calling council from the API key — there is no way to request another council's clients through this endpoint.

04c Bank API

Its own fully separate channel — own routes, own controllers, not shared with Bot API. Banks have full access to any council through a global key (no single council on the credential itself, unlike every other channel) — every other endpoint on the hub rejects a global key outright. A per-council bank key (issued the normal way) still works on every route below too, exactly like any other channel's key.

Authentication headers
X-API-KEY: YOUR_GLOBAL_API_KEY
X-API-SECRET: YOUR_GLOBAL_API_SECRET

Bank-wide (global key only)

These three see every council at once — there's no council_code involved, and a per-council key gets 401 here.

GET/api/bank/councils
Response
{
  "success": true,
  "councils": [
    { "code": "CENTRAL_MUN", "name": "Central Municipality", "type": "Municipality" },
    { "code": "NORTHDALE_RDC", "name": "Northdale Rural District Council", "type": "RDC" },
    { "code": "RIVERVIEW_TC", "name": "Riverview Town Council", "type": "Town Council" },
    { "code": "EASTFIELD_RDC", "name": "Eastfield Rural District Council", "type": "RDC" }
  ]
}
The reference list of every active council's code, name and type — useful before calling /api/bank/clients below.
GET/api/bank/transactions
Query parameters
  • council_code (optional — omit for every council)
  • status (optional: pending, posted, failed)
  • date (optional, single day)
  • from / to (optional, date range)
  • per_page (optional, default 50, max 200)
Response
{
  "success": true,
  "transactions": [
    {
      "council_code": "RIVERVIEW_TC",
      "council_name": "Riverview Town Council",
      "reference": "PAY-20260827-001",
      "zb_reference": null,
      "account_number": "10001",
      "customer_name": "John Doe",
      "amount": "5.00",
      "type": "payment",
      "source": "connector",
      "paid_by": "John Doe",
      "status": "posted",
      "sage_reference": "1249.001",
      "failure_reason": null,
      "created_at": "2026-08-27T10:05:44+00:00",
      "posted_at": "2026-08-27T10:06:01+00:00"
    }
  ],
  "meta": { "current_page": 1, "per_page": 50, "total": 4, "last_page": 1 }
}
The reconciliation feed — match this against your own settlement file instead of polling /api/zb/payment/{reference} one reference at a time.
GET/api/bank/clients
Query parameters
  • per_page (optional, default 50, max 200)
Response
{
  "success": true,
  "clients": [
    {
      "council_code": "EASTFIELD_RDC",
      "council_name": "Eastfield Rural District Council",
      "account_number": "39480",
      "name": "Jane Moyo",
      "phone": "0771234567",
      "email": "jane@example.com",
      "address": "12 Main Street",
      "currency": "USD",
      "balance": "45.20"
    }
  ],
  "meta": { "current_page": 1, "per_page": 50, "total": 2066, "last_page": 42 }
}
Every client, from every active council, in one response — each one tagged with council_code/council_name. Issue a global key via php artisan olimem:issue-global-key.
GET/api/bank/services

?council_code= (strongly recommended — some councils carry 1,000s of services) + optional ?type=Municipal|Normal. Omitting council_code sweeps every council in one paginated response.

GET/api/bank/tenders

?council_code= (same recommendation as bank/services above).

GET/api/bank/properties

?council_code= (same recommendation) — property id, owner name, address, erf number, for every council or one.

At real scale (60+ councils, some with 3,000+ services/properties) sweeping every council for a lookup is expensive — you're almost always about to post a payment for one specific council anyway, so always pass council_code here rather than relying on the no-filter fallback.

Single-account (global or per-council key)

Same shape as Bot API's equivalents (§4), on their own /api/bank/* routes. A global key must add council_code — a per-council bank key resolves it from the key itself and can omit it.

POST/api/bank/login
Request payload (global key)
{
  "council_code": "EASTFIELD_RDC",
  "account_number": "12345",
  "password": "Password@123"
}
Response
{
  "success": true,
  "name": "John Doe",
  "account": "12345",
  "currency": "USD",
  "balance": "123.45"
}
GET/api/bank/account/{account}
Query parameters
  • council_code (required for a global key only)
Response
{
  "success": true,
  "dc_link": 123,
  "account": "12345",
  "name": "John Doe",
  "currency": "USD",
  "balance": "123.45"
}
GET/api/bank/balance/{account}

Balance Lookup — same response shape as Account Lookup.

GET/api/bank/statement/{account}

Statement Lookup — same response shape as Bot API's Statement Lookup.

POST/api/bank/service-request
Request payload (global key)
{
  "council_code": "EASTFIELD_RDC",
  "account": "12345",
  "category": "billing_query",
  "title": "Disputed charge",
  "description": "Customer disputes the water bill amount."
}

04d Services API

Lists the calling council's service/product catalog, as synced up from Sage by the connector — StkItem for Normal-type sales, _ccg_EB_Services for Municipal-type billing. A council with no municipal billing module configured in Sage simply returns zero Municipal rows — not an error. Use the returned id as property_id+service_id (Municipal) or stock_id (Normal) on §6 Transact API.

Authentication headers
X-API-KEY: YOUR_API_KEY
X-API-SECRET: YOUR_API_SECRET
GET/api/services
Query parameters
  • type (optional: Municipal, Normal — omit for both)
  • per_page (optional, default 100, max 500)
Response
{
  "success": true,
  "council": { "code": "RIVERVIEW_TC", "name": "Riverview Town Council" },
  "services": [
    { "id": 1, "type": "Municipal", "name": "Water Consumption", "description": "Water Cons" },
    { "id": 42, "type": "Normal", "name": "Stationery Pack", "description": null }
  ],
  "meta": { "current_page": 1, "per_page": 100, "total": 2, "last_page": 1 }
}
Scoped to the calling council from the API key. Populated by the connector's read-direction sync — if a council was only just onboarded, this list fills in over the connector's first few cycles.

04e Tenders API

Lists the calling council's payment methods, as synced up from Sage's own dbo.Tender table by the connector (Cash, EcoCash, Card, bank transfer, etc.). Pass the returned id back as tender_id on §6 Transact API to choose how a payment posts — omit it and Municipal payments still fall back to the service's own tariff, unchanged.

Authentication headers
X-API-KEY: YOUR_API_KEY
X-API-SECRET: YOUR_API_SECRET
GET/api/tenders
Query parameters
  • per_page (optional, default 100, max 500)
Response
{
  "success": true,
  "council": { "code": "RIVERVIEW_TC", "name": "Riverview Town Council" },
  "tenders": [
    { "id": 6, "tender_no": "6", "name": "Cash USD" },
    { "id": 7, "tender_no": "7", "name": "ECOCASH (Merchant)-ZiG" }
  ],
  "meta": { "current_page": 1, "per_page": 100, "total": 2, "last_page": 1 }
}
Scoped to the calling council from the API key. A chosen tender only actually changes how a payment posts once the council's rcpt_service_tr_codes mapping is configured for that service+tender combination.

04f Properties API

Lists the calling council's property register, as synced up from Sage's own Properties by the connector — property id, owner name, address, erf number. Use the returned property_id together with a service_id from §4d Services API for a Municipal-type payment on §6 Transact API.

Authentication headers
X-API-KEY: YOUR_API_KEY
X-API-SECRET: YOUR_API_SECRET
GET/api/properties
Query parameters
  • per_page (optional, default 100, max 500)
Response
{
  "success": true,
  "council": { "code": "RIVERVIEW_TC", "name": "Riverview Town Council" },
  "properties": [
    { "property_id": 3, "owner_name": "John Doe", "address": "12 Main Street", "erf_number": "ERF-100" }
  ],
  "meta": { "current_page": 1, "per_page": 100, "total": 1, "last_page": 1 }
}
Scoped to the calling council from the API key. A council with no municipal billing module configured in Sage returns an empty list, same as Services API's Municipal type — not an error. owner_name is the client's current billing name (Sage Client.Name), not the deed-registered name, which can go stale.

05 Payment Gateway API

Payment Status

GET/api/zb/payment/{reference}
Query parameters
  • council_code (required for a global/bank key only — per-council keys resolve it from the key itself)
Response
{
  "success": true,
  "merchant_reference": "ZB-1234",
  "zb_reference": "ZB202607280001",
  "status": "paid",
  "is_paid": true,
  "amount": "50.00",
  "email": "customer@example.com",
  "phone": "0771234567",
  "receipt": {
    "id": 1,
    "reference": "REC-0001"
  }
}

06 Transact API

Allows external systems (payment gateways, third-party billers) to post transactions directly into Sage Evolution. type picks which of Sage's two billing shapes the payment belongs to — look the right ids up first via §4d Services API and §4e Tenders API.

POST/api/transact
Headers
X-API-KEY: YOUR_API_KEY
X-API-SECRET: YOUR_API_SECRET
Accept: application/json
Content-Type: application/json
  • account (required)
  • type (optional: Municipal, Normal — default Municipal)
  • property_id + service_id (required if type=Municipal — see Services API)
  • stock_id (required if type=Normal — see Services API)
  • tender_id (optional — see Tenders API)
  • amount (required)
  • currency (optional: ZWG, USD, ZAR — the currency the payer paid in; omit to use the account's own currency)
  • reference (required, unique)
  • description, paid_by (optional)
Municipal payment (per-council key)
{
  "account": "12345",
  "type": "Municipal",
  "property_id": 1001,
  "service_id": 1,
  "tender_id": 6,
  "amount": 100.00,
  "reference": "PAY-20260728-001",
  "description": "Online Payment",
  "paid_by": "John Doe"
}
Normal payment — a plain stock item, no property involved
{
  "account": "12345",
  "type": "Normal",
  "stock_id": 42,
  "tender_id": 7,
  "amount": 25.00,
  "reference": "PAY-20260728-002"
}
Global key (banks) — add council_code
{
  "council_code": "EASTFIELD_RDC",
  "account": "12345",
  "amount": 100.00,
  "reference": "PAY-20260728-001",
  "description": "Online Payment"
}
Success response
{
  "success": true,
  "receipt_id": 123,
  "reference": "REC-0001",
  "message": "Transaction posted successfully."
}
reference must be unique per transaction — retrying with the same reference must not post the payment twice. There is no currency field — a payment always posts in the account's own synced currency.
tender_id is optional and Municipal payments still work without it (falls back to the service's own tariff, unchanged from before). Normal payments have no fallback — without a resolvable tender/service TR-code mapping configured on the council's side, the payment is accepted but fails to post into Sage.

07 Sync API

Used by the Android receipting application to pull reference data and push offline receipts once connectivity returns.

Available endpoints
GET /api/sync/stk-items
GET /api/sync/clients
GET /api/sync/accounts
GET /api/sync/tenders
GET /api/sync/currencies
GET /api/sync/currency-hist
GET /api/sync/price-list-names
GET /api/sync/price-list-prices
GET /api/sync/tr-codes
GET /api/sync/properties
GET /api/sync/services
GET /api/sync/property-services
GET /api/sync/tender-configurations
GET /api/sync/service-trcodes

Offline Receipt Synchronisation

POST/api/sync/receipts-post
Full payload
{
  "clientLink": 1001,
  "customerCurrencyId": 1,
  "type": "Municipal",
  "userId": 1,
  "openBalance": 500.00,
  "paid_by": "John Doe",
  "posted": true,
  "createdAt": "2026-07-28 10:00:00",
  "reference": "ANDROID-0001",
  "source": "Android",
  "items": [
    {
      "is_cash": true,
      "createdAt": "2026-07-28 10:00:00",
      "type": "Municipal",
      "clientLink": 1001,
      "trcode": 1,
      "amount": 100.00,
      "tendered_amount": 100.00,
      "change": 0,
      "taxAcc": 1,
      "taxTypeID": 1,
      "description": "Water Charges",
      "stockId": 1,
      "iot": 0,
      "refundTrcode": 0,
      "isBillable": true,
      "currencyID": 1,
      "userId": 1
    }
  ]
}
Same idempotency rule as the Transact API — reference must be unique per receipt so a retried sync never double-posts.

Mobile Device Login

POST/api/sync/login
Request payload
{
  "email": "user@council.co.zw",
  "password": "Password@123",
  "deviceUUID": "123456789",
  "deviceName": "Samsung Tablet",
  "deviceModel": "SM-T500"
}

08 Miscellaneous Endpoints

GET/api/user

Current authenticated user (Sanctum session).

GET/api/test
Response
{
  "status": "working",
  "app": "Olimem Customer Portal"
}

09 Error Handling

Standard error response
{
  "success": false,
  "message": "Error Description"
}
Common status codes
CodeDescription
200Success
201Created
400Bad Request
401Unauthorized
403Forbidden
404Not Found
422Validation Error
500Internal Server Error

10 Security Recommendations

  • Enforce HTTPS for all environments.
  • Rotate API keys periodically.
  • Store API secrets securely.
  • Implement IP whitelisting.
  • Enable request auditing.
  • Use JWT / Sanctum token expiration.
  • Encrypt sensitive data.
  • Rate limiting — done: 120 req/min per API key on every machine-client route, 60 req/min per customer on Customer API routes (keyed by identity, not IP, so councils sharing this server never throttle each other).
  • Enable API monitoring.

11 Integration Flow

Customer App → Customer API → Laravel Application → Sage Evolution Database
Payment Gateway → Payment API → Receipting Engine → Sage Evolution
Android POS → Sync API → Sage Evolution → Fiscalisation Service
Chatbot → Bot API → Customer Portal → Sage Evolution

12 Appendix

Supported currencies
  • USD
  • ZWG
Supported authentication methods
  • Bearer Token
  • API Key
  • API Secret
Supported content types
application/json