Skip to main content

Oman POS Report

One endpoint reads back the counter sales and refund credit notes your organisation has issued through GoRoute's Oman POS routes, either as a day book an auditor can read line by line, or as a cash-up reconciliation that shows what has and has not reached the platform.

All paths below are relative to https://app.goroute.ai/peppol-api. Every call is scoped to the organisation of the API key you send.

This report covers every POS counter, not just the fuel forecourt

It returns all four kinds of counter receipt: fuel pump sales, forecourt refunds, retail store sales and refunds, and money-exchange commission receipts. There is no parameter that restricts it to one counter, and no field in a row that names the counter it came from.

That is not a choice the report makes. Every Oman POS route shares one finalising step, and that step records the same channel value on every receipt it writes, whichever counter issued it. The report selects on that channel value, so all of them match. If you have only a forecourt, this distinction costs you nothing. If you run a store or an exchange counter as well, a total from this report is your whole POS estate, not your fuel business — do not reconcile it against a forecourt figure.

You can still separate them, using the values each counter writes into the station and product fields. How to tell the counters apart below sets out exactly what each one puts there.

The endpoint

Method and pathWhat it does
GET /api/v1/reports/posPOS counter sales and credit notes for a period, summarised by station, tender and product

It requires the transactions:read permission — the same permission that lets you read your transactions one at a time. There is no separate reporting scope, because the report aggregates documents you could already fetch one by one.

Query parameters

NameRequiredTypeMeaning
period_startyesdate (YYYY-MM-DD)First day counted, included
period_endyesdate (YYYY-MM-DD)Last day counted, included
station_idnostringRestrict to one station, matched exactly against the station_id recorded with each sale
kindnodaybook or reconciliationWhich of the two reports you want. Defaults to daybook

Both dates are whole days and both ends are inclusive, so a single day is period_start=2027-03-01&period_end=2027-03-01.

curl -H "X-API-Key: $GOROUTE_API_KEY" \
"https://app.goroute.ai/peppol-api/api/v1/reports/pos?period_start=2027-03-01&period_end=2027-03-31&kind=reconciliation"

The two kinds are not a long and a short report

This is the one thing on this page nobody would guess, so it comes before the response shape.

  • daybook returns every counter document in the period, sales and credit notes together, in the order they were recorded.
  • reconciliation returns the same summary over the same period, but its rows hold only the documents that have not reached the platform.

A reconciliation rows array is therefore an exception list to work through at cash-up, not a shortened day book. An empty rows on a reconciliation is the good outcome: everything in the period got through. The summary block is identical in both kinds, so the totals do not change when you switch.

The period is a UTC day range, and it is the recording time

Each day runs from 00:00:00 to 23:59:59.999999 UTC. Oman is four hours ahead, so a sale made in Muscat between midnight and 04:00 is recorded against the previous UTC day and falls into the previous day's report. This is the same trap as the retail usage count, and it matters more here, because a forecourt trades through the night.

The timestamp being compared is when GoRoute recorded the document, not a till time you supplied. A receipt printed at 23:58 and submitted at 00:03 belongs to the later day.

Two refusals, both 422

What you sentWhat happens
period_end before period_start422, period_end must not precede period_start
A period longer than 400 days422, period must not exceed 400 days

Both arrive in the platform's standard error envelope:

{
"error": "error",
"message": "period must not exceed 400 days",
"request_id": "5f0c9f6a-2c19-4a2e-8b77-0d6f1f9b4c31"
}

Quote the request_id to support if you need the call traced.

A kind that is neither daybook nor reconciliation is also refused with 422, but by request validation before the handler is reached, so that one response carries a field-level detail list instead of the envelope above.

Response shape

{
"kind": "daybook",
"period": { "start": "2027-03-01", "end": "2027-03-31" },
"station_id": null,
"summary": {
"sales": 18342,
"credit_notes": 57,
"gross": "412870.500",
"net": "393210.000",
"vat": "19660.500",
"credited_gross": "1284.750",
"reported": 18376,
"not_reported": 23,
"by_tender": {
"CASH": { "count": 9120, "gross": "198433.250" },
"CARD": { "count": 9279, "gross": "214437.250" }
},
"by_station": {
"ST-014": { "count": 7411, "gross": "165920.000", "name": "Al Khuwair" }
},
"by_product": {
"M95": { "count": 11288, "gross": "243118.500" }
}
},
"generated_at": "2027-04-01T06:12:44.118231+00:00",
"rows": [
{
"transaction_id": "9a2f1c9d-0a6b-4e21-8c77-7c1f7d542a3f",
"issued_at": "2027-03-14T07:41:09.512000+00:00",
"document": "sale",
"invoice_number": "FS-2027-0098231",
"receipt_no": "0098231",
"station_id": "ST-014",
"station_name": "Al Khuwair",
"pump_no": "4",
"product": "M95",
"invoice_type": "simplified",
"payment_method": "cash",
"cashier_name": "Salim",
"net_amount": "11.905",
"vat_amount": "0.595",
"gross_amount": "12.500",
"currency": "OMR",
"status": "delivered",
"credits_invoice": null,
"credit_reason": null,
"invoice_uuid": "1c9d0a6b-5e21-4a51-9a2f-7c1f7d542a3f"
}
]
}

The summary, field by field

FieldWhat it is
sales / credit_notesHow many documents of each kind fell in the period
gross / net / vatPeriod totals. Credit notes are subtracted
credited_grossThe value of the credit notes, accumulated as a positive figure
reported / not_reportedDocuments that have reached the platform, and those that have not
by_tenderCount and gross per payment method, the key upper-cased
by_stationCount and gross per station, with the station's name
by_productCount and gross per product code

credited_gross sitting beside a reduced gross looks contradictory until you know the rule: a refund pulls gross, net and vat down, and is counted up in credited_gross. So gross is what you actually took, and credited_gross is how much was handed back. Do not add them together.

Every money figure in the summary, including the ones inside the three breakdowns, is a string with exactly three decimal places, because OMR runs to 1000 baisa. A sale with no payment method, station or product recorded is grouped under the key rather than being dropped, so the breakdowns always add up to the period totals.

The totals and all three breakdowns cover every counter, so if you run more than a forecourt they are mixed. by_station will carry your stores and exchange branches beside your stations, and by_product will carry goods and service descriptions beside fuel grades. The breakdown keys are what lets you separate them again; the next section says which key belongs to which counter.

reported means it reached our platform, not that it reached the Tax Authority

reported counts the documents whose platform status is delivered, and nothing else. Every other status — queued, submitted, accepted, retrying, failed — is counted in not_reported, because the document is still in flight. A document that has not reached the platform must not be presented as filed.

delivered is a statement about GoRoute, not about the Oman Tax Authority. A validated consumer sale is cleared by GoRoute as the certified Oman Access Point and marked delivered at that moment; the Tax Data Document behind it is generated, stored and auditable, and is forwarded to OTA Fawtara once Fawtara is accepting submissions. So read reported as "cleared through GoRoute", and do not put it in front of a tax inspector as a filing record.

That Tax Data Document is a separate record on the platform. It is not returned as a row here — the rows are the receipts your counters issued, not the reports made about them.

The rows, field by field

FieldWhat it is
transaction_idThe GoRoute transaction, for use against the Transactions API
issued_atWhen GoRoute recorded the document, in UTC
documentsale, or credit_note for a refund
invoice_numberThe document number you issued
receipt_noThe till receipt number, as printed
station_id / station_nameThe forecourt on a fuel sale. On the other counters these carry the store or branch instead — see How to tell the counters apart
pump_noThe pump on a fuel sale. The teller on an exchange receipt, and - on a retail one
productThe fuel product code on a fuel sale. The service or the goods sold on the other counters
invoice_typeThe kind of invoice issued, as supplied with the sale
payment_methodThe tender — the figure by_tender groups on
cashier_nameWho served the sale
net_amount / vat_amount / gross_amountThe document's own figures, exactly as recorded with the sale
currencyThe document currency. OMR where none was recorded
statusThe platform status, the value reported and not_reported are decided from
credits_invoiceFor a credit note, the invoice number it credits. null on a sale
credit_reasonFor a credit note, why it was issued. null on a sale
invoice_uuidThe document's own unique identifier, which is also the message identifier GoRoute stored it under

Row amounts are returned as they were recorded for the receipt, so treat them as the document's own figures. The three-decimal rounding is applied to the totals in summary, not to the rows.

A field that was never supplied with the sale comes back as null. Nothing is inferred for it — a missing cashier or pump is reported as missing rather than guessed.

How to tell the counters apart

No row says which counter issued it. What each counter does is fill the station, pump and product fields with its own terms, so those three values are how you separate a mixed report.

Fuel forecourtRetail storeMoney exchange
station_idYour station code, as suppliedweb, pos or app — the retail channelThe branch code
station_nameThe station name, as suppliedAlways Online storeThe branch name
pump_noThe pump, as suppliedAlways -The teller
productThe fuel grade, for example M95The first line's description, with (+2 more) appended when the sale has more linesThe service, for example Money exchange service
payment_methodThe tender, as suppliedNot recorded, so the row groups under The tender, defaulting to CASH

The practical test on a mixed report is station_name: retail sales are the ones reading Online store, and everything else is a physical site. To pull one forecourt out on its own, pass station_id — it is matched exactly, and a station code will not collide with web, pos or app.

A retail product is a description typed by whoever listed the goods, not a code from a list, so treat by_product on a mixed report as a grouping of text rather than a product breakdown.

  • Oman Retail POS — issuing store and kiosk sales, and the retail usage count that excludes fuel
  • VAT Return Report — the same documents aggregated into the Oman VAT return boxes
  • Oman country guide — the mandate dates, PINT OM validation rules and the QR code