Tax reporting status: TDD and MLS
Tax reporting status (TDD and MLS)โ
Oman's CTC protocol produces two artefacts alongside the invoice itself: a Tax Data
Document (TDD) addressed to the Oman Tax Authority, and one or more Message Level Status
(MLS) responses exchanged between access points. Four endpoints expose them: one for the
reporting status, one to fetch the TDD, and two to fetch an MLS โ which of the two you need
depends on who wrote the MLS, and
Fetching an MLS document is the rule. All four require the
transactions:read permission.
Reporting status for one transactionโ
GET /api/v1/transactions/{transaction_id}/reporting
Returns the tax-reporting state of a transaction in a single call.
| Field | Meaning |
|---|---|
transaction_id | The transaction queried. |
clearance_status | One of prepared_awaiting_authority, cleared, failed, rejected, reported, reported_via_substitute, awaiting_ota_go_live, held (a report held before any TDD was built for it, with its reason in reason_code and reason), or null when no TDD exists yet. failed is final; reporting_state says whether it is a report that cannot be filed. rejected: the tax authority rejected the report and GoRoute's corrected report is under way; not final. See When a report does not reach the tax authority and When the tax authority rejects a report. |
clearance_reason_code, clearance_reason | Why the report reads prepared_awaiting_authority, held or rejected: a stable code and one sentence you can show to a person, the same pair GET /transactions/{id} carries. null for any other clearance_status. |
reporting_state, reason_code, reason | null while nothing has gone wrong. error when the report, or a correction to it, cannot be filed; held while a report that did not reach the tax authority waits for reporting for your taxpayer to be switched back on, or while a report whose processing stopped waits for GoRoute (see When a report's processing stops). reason_code is a stable code such as C5_OM_TDD_TRANSPORT_FAILED, and reason is one sentence you can show to a person. |
hold | null unless the report's processing stopped: its reason (awaiting_operator_decision or lookup_unavailable) and since (UTC). See When a report's processing stops. |
ota_live | Whether this environment is configured to report to a live tax authority. |
ota_target | The participant identifier Tax Data Documents are addressed to, as scheme:id. |
ota_target_is_testbed | true when ota_target is the OTA test participant. |
note | A plain-language explanation, or null. See below. |
mls_status | The MLS response code if one has arrived, otherwise sent if an MLS was dispatched, otherwise null. |
tdd.transaction_id | The transaction holding the TDD, or null. |
tdd.sent | Enqueued for transmission โ not "arrived". Read together with tdd.held. |
tdd.held | true when the TDD was built, validated and stored but deliberately not transmitted. A held TDD has reached nobody. Two things hold one: the environment not being configured to report to the tax authority, or โ where an enabled-reporter list is in use โ this taxpayer not being on it. |
tdd.delivered | true cleared, false failed, null not yet reconciled. |
tdd.authority_response | The tax authority's answer to the TDD, or null until it answers: response_code (AP accepted, RE rejected), its description, received_at and mls_id. |
tdd.redrive | null unless the TDD failed to reach the tax authority. Otherwise GoRoute's automatic re-sends of it: attempts, max_attempts, state (in_progress, held, error, reported, or answered when the authority answered it after all), next_attempt_at and last_error_code. |
tdd.scope | Scope of a substitute report, where one was used. |
tdd.document_url | Relative URL of the stored TDD, or null when there is no TDD. |
mls.transaction_id | The MLS we sent, kept for backward compatibility. |
mls.response_code | Response code carried by the MLS. |
mls.document_url | Relative URL of the MLS we sent, or null. |
mls.legs[] | One entry per leg that exists โ leg is sent or received, each with its own transaction_id and document_url. A received leg also carries response_code and received_at. The document_url on a received leg does not work: it points at the core MLS call, which cannot return that leg. Read the leg's presence and response_code from here, but fetch it with the Oman call โ see Fetching an MLS document. The Tax Authority's MLS is not listed here at all; tdd.authority_response is where it appears. |
substitute.used | true when a substitute participant was reported to. |
substitute.participant | The substitute participant identifier. |
substitute.reason | Why a substitute was used. |
sla_breached | Whether the reporting SLA for this transaction was breached. |
reason_code as an open setThe codes named on this page are the ones worth writing code for, but the set grows as
reporting gains new cases, and your own service may be a release behind or ahead of this page.
So branch on the codes you have deliberately handled, and for anything else show the
reason sentence to the person instead of a fallback of your own. reason is written to be
read by a human being and is never an internal error message, so it is always safe to show. A
switch with no default is the one thing to avoid here: a code you have not seen should
produce a useful message, not silence.
tdd.sent does not mean the tax authority received itsent means enqueued for transmission. A held TDD is enqueued and then deliberately
not transmitted, so tdd.sent and tdd.held are both true at once. Always read the two
together: if tdd.held is true, nothing has reached the tax authority. clearance_status
already applies that precedence for you โ prefer it over inferring the state from tdd.sent.
Why you may see an explanatory noteโ
When clearance_status is anything other than reported or rejected, reporting_state is
not error, the report's processing has not stopped (hold is null; while it has, note
is the sentence in reason), and this environment is pointed at the OTA test participant
or is not configured as live, the response carries a note explaining that in prose. This is
the expected state today rather than a fault: the invoice itself is delivered to the buyer
over Peppol normally, the TDD is still generated and stored against the transaction, and the
original issue time is preserved so the reporting obligation can be evidenced later. When the
note is absent it is null.
GoRoute publishes no date for OTA production reporting. Use ota_live and
ota_target_is_testbed on this endpoint to see what your own environment is pointed at,
rather than assuming.
A held report with no note at all means something specific. If clearance_status is
prepared_awaiting_authority while ota_live is true and ota_target_is_testbed is
false, the response carries no explanatory note, because neither condition above
applies. That combination means the environment is configured to report to the tax
authority, but this taxpayer is not one of those enabled for transmission. Where an
enabled-reporter list is in use, only the taxpayers named on it have their Tax Data
Documents dispatched; everyone else's are held exactly as if reporting were switched off.
The list is a deployment setting and no API call exposes it, so there is nothing you
can read to confirm it โ ask GoRoute whether your taxpayer identifier is enabled.
clearance_status remains the field to read: it applies the held-first precedence for you,
whichever of the two reasons applies.
Fetching the Tax Data Documentโ
GET /api/v1/transactions/{transaction_id}/tdd
Returns the stored TDD as UBL XML with content type application/xml. The document is
returned exactly as it was stored โ nothing is reformatted or re-serialised. Use the
transaction identifier of the invoice, not of the TDD itself; the endpoint resolves the
TDD from the invoice for you.
There is no parameter to choose which TDD you get. A transaction can have up to three:
the TDD GoRoute produced itself, a TDD produced by the sending access point (C2), and a
substitute TDD. The endpoint returns the first of those three that exists, in that order,
and there is no equivalent of the leg parameter on the MLS call. To see which one you are
about to receive, call the reporting endpoint first: tdd.transaction_id applies the same
order, so it identifies the document this call will return, and substitute.used tells you
whether a substitute was reported to.
Errors
| Status | error_code | When |
|---|---|---|
| 404 | TRANSACTION_NOT_FOUND | No such transaction in your organisation. |
| 404 | TDD_NOT_AVAILABLE | The transaction exists but no TDD has been produced for it. |
| 404 | DOCUMENT_NOT_STORED | A TDD exists but its XML is not retrievable from storage. |
document_url is not a complete path โ add /api/v1The document_url values in the reporting response are built without the /api/v1
prefix: tdd.document_url comes back as /transactions/{transaction_id}/tdd, and the MLS
URLs are built the same way. Joining your API base URL to that value as-is produces a 404.
Either prepend /api/v1, or build the address from the request line above rather than from
the response field. For the MLS there is a second reason to build your own address: the
received leg's document_url names a call that cannot return it. See
Fetching an MLS document.
Being able to download a TDD does not mean the Tax Authority received it. A held TDD is
built, validated, stored and fetchable through this endpoint while having reached nobody โ
check tdd.held and clearance_status on the reporting endpoint before treating a
successful download as evidence of reporting.
TDD (Tax Data Document)โ
Oman's continuous transaction controls require a Tax Data Document (TDD) to be reported to the Oman Tax Authority. GoRoute builds and files it for you:
- Root element:
pxs:TaxData(namespaceurn:peppol:schema:om-taxdata:1.0) - Receiving party: the Oman Tax Authority's Peppol participant (scheme
0242). GoRoute fills it in; you never set it. - Validated against
TDD-OM-peppol-om-tdd.xsltbefore submission - Document type codes (
pxs:DocumentTypeCode):SSubmit,RResubmit,DDisregard (OpenPeppol code list)
This page does not give a reporting deadline, because we could not trace one to a Tax Authority or OpenPeppol publication.