Skip to content

Platform Error Codes

Applies to: All subscriptions

Purpose

Interpret the coded errors Complifly itself returns — authentication, authorisation, document state — and know immediately which are transient, which are terminal, and who resolves each.

Audience

Integration developers, administrators, support teams.

Prerequisites

Reference

Authentication

Code HTTP Meaning Fixed by Retry?
TOKEN_MISSING 401 No credential presented you No — send the header
TOKEN_INVALID 401 Malformed, wrong audience, wrong issuer, or a bad signature you No — fix the credential
TOKEN_EXPIRED 401 Expired, or the clocks disagree you Yes, once, after refreshing
OAUTH_REQUIRED 403 This client has been switched to identity-provider tokens; the static key is refused admin No — complete the migration
ROLE_MISSING 403 The token is valid but lacks the required application role admin No — grant admin consent
CLIENT_NOT_ONBOARDED 403 The token's directory and application together are not a connection Complifly serves, or the connection is not live admin No — finish onboarding

TOKEN_EXPIRED recurring after a refresh points at clock drift, not at the credential. Token validation is time-sensitive; check time synchronisation on both hosts.

CLIENT_NOT_ONBOARDED is about the pair, not the application

Complifly recognises an integration as this application, in this directory. An application ID on its own identifies nobody: the same application can be installed in many directories and keeps the same ID in each.

So this code has several distinct causes that look identical from the outside, and the message deliberately does not say which — telling an unauthenticated caller which half matched would let anyone enumerate other customers' integrations.

Cause How to tell
Onboarding never finished — Complifly has your directory but not your application ID Ask for the connection's stage. AWAITING_CLIENT means exactly this
The ERP moved to a different app registration Compare the appid in your token against what you sent Complifly
The token came from a different directory than the one recorded Compare the tid in your token against your Directory (tenant) ID
The connection was suspended Ask your Complifly contact

Decode the token and check tid and appid before reporting a fault. Between them they identify the connection, and the answer is almost always visible there.

Authorisation

Code HTTP Meaning Fixed by Retry?
GSTIN_NOT_PERMITTED 403 The document's supplier registration is outside the credential's scope admin No
FORBIDDEN 403 The document does not belong to this credential's company admin No
Missing registration header 400 A user-token call omitted the registration header you No

GSTIN_NOT_PERMITTED is a credential scope problem, not a user permission problem. It is the single most misdiagnosed platform error: teams check the user's role, find it correct, and conclude the system is broken.

Where that scope comes from depends on the credential, which is worth knowing before you go looking for it:

Credential Scope
OAuth connection The GSTIN list attached to that connection, set at onboarding
Static key with a recorded GSTIN That one registration
Static key without one Every GSTIN registered under the key's company

A registration added to the company after an OAuth connection was onboarded is not automatically in that connection's list. This is deliberate — an ERP's authority should not widen because someone registered a new GSTIN elsewhere — but it does mean a new registration needs the connection updated.

Document state

Code HTTP Meaning Fixed by Retry?
DUPLICATE 409 The document number already exists you Never — permanent
NOT_FOUND 404 No such document you No
IRN_ALREADY_CANCELLED The reference number is already cancelled you No
STATUS_LOCK The document is in a state that does not allow this action you No
INDETERMINATE_PAYLOAD_CHANGED A previous attempt's outcome is unknown, and the document has since changed support No
LOCAL_VALIDATION Validation failed before submission you No
NIC_PRINT_WINDOW_CLOSED 422 The official NIC print copy is no longer obtainable statute Never

NIC_PRINT_WINDOW_CLOSED deserves its own explanation, because the underlying provider message reads like a fault to fix and is not. The government serves the official e-invoice copy for two days after generation. Complifly fetches and caches it at generation time so it survives that window — but where no copy was cached, it becomes permanently unobtainable.

Retrying cannot help, and neither can support. Use Branded Print, which renders from the signed invoice and signed QR captured at generation; that stored evidence is what makes an e-invoice print valid in the first place.

INDETERMINATE_PAYLOAD_CHANGED deserves explanation. It arises when a submission's outcome could not be determined — a timeout, typically — and the document was then edited. The platform refuses to resubmit, because it cannot tell whether the original was registered, and resubmitting a changed payload under the same number risks either a duplicate or a document that does not match what was registered. Do not force it. Establish the true state first.

Approval workflow

Returned by the Invoice Generation approval endpoints when the multi-level Approval Engine (IGM_APPROVAL_ENGINE) is on.

Code HTTP Meaning Fixed by Retry?
ROLE_MISMATCH 403 The caller's approval role is not the one this level requires admin No — the right approver must act, or an administrator must override
SELF_APPROVAL 403 The caller submitted this document and cannot also decide it you Never — not waivable by anyone
NOT_PENDING 409 There is no open approval on this document, or no level left to act on you No — re-read the workflow state
APPROVAL_PENDING 409 Posting was attempted while an approval is still open you Yes, once the approval clears
APPROVAL_REJECTED 409 The document was rejected in approval and cannot post you Never for this document — raise a corrected one
REASON_REQUIRED 400 A force-approve, force-reject, or forced engine disable was sent without the mandatory reason you Yes, with a reason
APPROVALS_IN_FLIGHT 409 IGM_APPROVAL_ENGINE cannot be switched off while documents still await approval admin Yes, once the queue is clear — or with force + reason

ROLE_MISMATCH is usually a provisioning gap, not a bug

The message names the role the level requires and the role the caller actually holds. Read both halves — they distinguish the three causes:

Message ends with Cause Fix
…you have no approval role assigned The user has no approval_role_key Assign one on the user record
…your assigned approval role is X The user is a real approver, but at a different level The named level must act first. Approvals are sequential
The call never reached this check (403 from the permission guard instead) The user's system role does not hold invoice.approve Assign the approver or tax_controller system role

There is deliberately no administrative exemption. An org admin or Platform Super Admin whose approval role does not match is refused exactly like anyone else. The supported way out is POST /invoices/{docId}/approval/override, which requires admin.api, demands a written reason, and records the remaining levels as OVERRIDDEN rather than APPROVED — so a bypass is never indistinguishable from a real approval in the audit trail.

APPROVALS_IN_FLIGHT protects approvals already in progress

Switching the multi-level engine off does not merely stop future routing. Without this refusal, a document routed to three approvers and signed by none would stop being pending and post unapproved: the legacy flat gate takes over, its thresholds default to 0 (meaning off), and it rewrites the document from PENDING to NOT_REQUIRED. Nothing is rejected and nothing is logged as a bypass — the only trace is an approval_request still reading PENDING for a document already in the ledger.

The response body lists every blocking document with the role it is waiting on:

{
  "code": "APPROVALS_IN_FLIGHT",
  "message": "22 document(s) on 29AAACW3775F000 are still awaiting approval (waiting on finance_controller, finance_manager). …",
  "open": [
    { "docNo": "INV/00042", "docId": "…", "waitingOn": "finance_controller",
      "level": "1 of 3", "amount": 1180000, "submittedBy": "U0000013" }
  ]
}

Clear them — approve, reject, or per-document override — then retry. If a level names an approval role nobody holds, those documents can never clear and would block the setting for ever; for that case only, repeat the call with force: true and a written reason. Forcing cancels the approvals rather than approving them: every level is stamped OVERRIDDEN with the reason, the request becomes CANCELLED, and the documents return to having no approval decision at all.

SELF_APPROVAL cannot be resolved by changing a role

It is checked on identity, not permission: whoever submitted the document is refused, whatever role they hold, including Platform Super Admin. Granting more permissions will not change the outcome. Either another holder of the level decides it, or an administrator overrides with a reason.

Validation and ingest

Code HTTP Meaning Fixed by
REJECTED with errors 422 Validation failed you
VALIDATION 422 Same, recorded as a rejection reason you
ERROR 500 Server error support

Write-back feed

Code HTTP Meaning Fixed by
NO_SUBSCRIPTION 404 No active subscription for the credential's registrations admin
NO_EVENT_IDS 422 Acknowledgement body missing identifiers you
TOO_MANY 422 Too many identifiers in one acknowledgement you
FEED_ERROR 500 Feed query failed support
ACK_ERROR 500 Acknowledgement failed support

NO_SUBSCRIPTION is a configuration state, not a transient one. Retrying it forever is a common integration defect; it will never succeed until someone configures a subscription.

Rate limiting

Condition HTTP Fixed by Retry?
Limit exceeded 429 you Yes, after backing off

The retry summary

Never retry Retry
DUPLICATE 429
REJECTED and VALIDATION 5xx, bounded
GSTIN_NOT_PERMITTED, FORBIDDEN TOKEN_EXPIRED, once, after refresh
OAUTH_REQUIRED Timeouts, after reconciling
NO_SUBSCRIPTION
STATUS_LOCK, IRN_ALREADY_CANCELLED

Validation

Check Pass condition
The code is captured Along with the request identifier
Authentication and authorisation are distinguished 401 and 403 take different paths
Nothing terminal is retried Verified by forcing each response
Scope errors reach an administrator Not treated as a data fault
Support-class errors carry evidence Request identifier and document number included

Troubleshooting

Symptom Cause Action
TOKEN_EXPIRED immediately after refresh Clock drift Fix time synchronisation on both hosts
TOKEN_INVALID with a token that decodes correctly Wrong audience or issuer Compare the claims against the configuration
Valid token, 403 Application role not granted Grant admin consent
GSTIN_NOT_PERMITTED while the user's role is correct It is a credential scope problem Check the credential's registration scope
OAUTH_REQUIRED on a working key The client was switched to token-only Complete the migration
DUPLICATE on a document never sent Numbering restarted, or two systems share a series Fix the series. Numbers are consumed permanently
STATUS_LOCK The document's state does not allow the action Check its status; the action may already have occurred
INDETERMINATE_PAYLOAD_CHANGED Unknown outcome plus a subsequent edit Do not force. Establish the true state, with support if necessary
NO_SUBSCRIPTION retried indefinitely Treated as transient It is configuration. Configure a subscription
Persistent 429 at modest volume Retrying terminal failures, or authenticating per call Fix the retry policy; cache tokens
ERROR repeatedly A genuine server fault Escalate with the request identifier