CAM POS APIby CamTech and Financial Solutions

Connect any system to CAM POS

Any accounting system, ERP or spreadsheet tool can read a CAM POS business's books and records. The shop owner makes a key in CAM POS (Configuration → Accounting software → API keys) and gives it to whoever builds the connection. Keys can only read: nothing in CAM POS can be changed with them.

QuickBooks Online, Xero and Sage Business Cloud Accounting can also be linked directly, with no programming: Configuration → Accounting software → Live link. Any other system can import the files CAM POS makes for it (QuickBooks, Xero, Sage, Pastel, Zoho Books, or a plain journal).

Keys and limits

Every request carries the key in the Authorization header. Addresses start with https://campos.camtechsolutions.co.zw/api/v1/.

curl https://campos.camtechsolutions.co.zw/api/v1/journal?from=2026-10-01&to=2026-10-31&daily=1 \
  -H "Authorization: Bearer cpk_..."

Amounts, days and accounts

GET/api/v1/business

Who the key belongs to: name, number, currency, TIN, VAT number and contacts.

GET/api/v1/accounts

The chart of accounts CAM POS books into: key, code, name and type (asset, liability, equity, income, contra-income, expense).

GET/api/v1/journal

The books: every sale, refund, payment on account, stock delivery, supplier payment, expense, cash movement, stock adjustment and cash count, as balanced journal entries.

ParameterMeaning
from, toFirst and last day (both included). Leave out for everything.
daily=1One entry per day with the day's totals by account (reference CAMPOS-2026-10-21). Best for posting to a ledger.
formatjson (the default), or a CSV import file: quickbooks, xero, sage, pastel, zoho, camtech (plain journal).
taxRateXero file only: the tax rate name on each line (default Tax Exempt).
yearStartPastel file only: the month the financial year starts (1 to 12), for the period numbers.
{
  "currency": "USD", "from": "2026-10-21", "to": "2026-10-21", "daily": true,
  "entries": [{
    "number": "CAMPOS-2026-10-21", "day": "2026-10-21", "reference": "CAMPOS-2026-10-21",
    "description": "CAM POS takings and records for 2026-10-21 (42 entries)", "kind": "day",
    "lines": [
      { "account": "cash",   "code": "1000", "name": "Cash on hand",      "debit": 512.40, "credit": 0 },
      { "account": "salesF", "code": "4000", "name": "Sales - fiscal",    "debit": 0, "credit": 445.57 },
      { "account": "vat",    "code": "2200", "name": "VAT output (ZIMRA)", "debit": 0, "credit": 66.83 }
    ]
  }]
}

GET/api/v1/trial-balance

Each account's movement for from to to, as a debit or a credit. format=csv gives a file.

GET/api/v1/sales

Sales and refunds (refunds have kind: "refund" and negative amounts), oldest first, with their lines. Fiscal receipts carry their ZIMRA details.

ParameterMeaning
from, toDays, as above.
limitHow many (1 to 1000, default 200).
afterThe next value from the previous page. more: true means there are more.

GET/api/v1/products

Products with price, cost, barcode, reorder level, crate or pack details, and units on hand for products whose stock is counted.

GET/api/v1/customers

Saved customers: name, phone, email, TIN, VAT number, address, credit limit.

Webhooks

Instead of asking, a system can be told. The owner gives an https:// address under Configuration → Accounting software → Webhook and chooses the events:

Each one is a POST with a JSON body and these headers:

X-CamPOS-Event: sale.created
X-CamPOS-Timestamp: 1792569600
X-CamPOS-Signature: sha256=<hex HMAC-SHA256 of "<timestamp>.<body>" with your signing secret>

{ "id": "evt_...", "event": "sale.created", "createdAt": "...", "business": { "id": "...", "name": "...", "number": "..." }, "data": { ... } }

Check the signature with the secret shown when the webhook was turned on, and refuse messages older than a few minutes. Answer with any 2xx within 15 seconds. Anything else is tried again after 1, 5 and 30 minutes, then 2, 6, 12 and 24 hours. The same id can arrive twice, so ignore ones you already have.

// Node.js
const expected = "sha256=" + crypto.createHmac("sha256", SECRET).update(req.headers["x-campos-timestamp"] + "." + rawBody).digest("hex");
const ok = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(req.headers["x-campos-signature"]));

Errors

An error answer has a status code and { "error": "what went wrong, in words" }: 400 wrong parameters, 401 no key or a removed key, 403 subscription not active, 429 too many requests.

Questions: contact CamTech and Financial Solutions through Help in CAM POS.