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¶
- The error code and the request identifier
- Error Code Reference
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 |
SESSION_REVOKED |
401 | A signed-in user's session was ended before the token expired. The body carries a reason |
you, or admin when the reason is an administrator's action | No — sign in again |
PASSWORD_TOO_SHORT, PASSWORD_TOO_LONG, PASSWORD_BLANK |
400 | A new password is under 8 characters, over 72 bytes, or only spaces. Returned by change-password, reset-password and an administrator creating a user | you | Yes, with a password that meets the rule |
SESSION_REVOKE_FAILED |
500 | The person's sessions could not be ended. When it came with an edit or a delete, nothing was changed: the edit is not saved while its sessions cannot be ended | admin | Yes — repeat the same request; if it keeps failing, treat it as a platform incident |
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.
SESSION_REVOKED says why¶
When an administrator removes or changes someone's access, that person is signed out on their next request, and the response says why. A user token is only valid while its session is live, and a role, approval role or GSTIN list is fixed into the token when the person signs in. Ending the session is therefore the only way a change reaches someone who is already signed in.
reason |
What happened | What happens next |
|---|---|---|
ACCOUNT_DEACTIVATED |
An administrator disabled the account | The person cannot sign in again until an administrator re-enables it |
ACCOUNT_DELETED |
An administrator removed the account | As above; an administrator must recreate it |
ROLE_CHANGED |
The system role changed | The person signs in again and gets the new role |
ACCESS_CHANGED |
The approval role, the GSTIN access or the primary GSTIN changed | The person signs in again and gets the new access |
ADMIN_ENDED |
An administrator used End sessions | The person can sign in again straight away |
logout, logout_all, concurrent_limit, user_terminated |
The person ended it themselves, or signed in elsewhere | Nothing to fix |
An edit that changes none of the role, approval role, GSTIN list or primary GSTIN, such as a name change, does not end anyone's session.
SESSION_REVOKE_FAILED on an edit or a delete means nothing was changed. The edit and the ending of the person's sessions are one all-or-nothing step: an access change is not saved while the person could still use the old access. The body says changeSaved: false, the user still has their old role and access, and repeating the same request is a real retry. On the End sessions action by itself there was no change to save; the body has no changeSaved, and the message says only that the sessions could not be ended.
Why it is built this way matters if you script against the API: a 500 that left the change saved would have made a retry of the same edit do nothing, because the stored role already matched the request, and would have left a deleted user's token valid with no way to end it. The failure is recorded in the audit log as SESSIONS_REVOKE_FAILED, with changeRolledBack.
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. From the user-administration endpoints (POST /users, PUT /users/{id}): an Org Admin tried to grant a GSTIN not registered under their own company; the body carries gstins, the ones refused |
admin | No |
FORBIDDEN |
403 | The document does not belong to this credential's company | admin | No |
PERMISSION_DENIED |
403 | Application routes, user token. The caller's role does not hold the permission the route needs. The body carries permission (for example tds.view) and a message naming the role. Nothing was changed and the attempt is audited |
admin | No — give the user a role that holds it |
PLATFORM_ROLE_REQUIRES_SUPER_ADMIN |
403 | User administration (POST /users, PUT /users/{id}). An Org Admin tried to assign super_admin, support_engineer or platform_engineer. These are Complifly's own roles and reach across customers. Nothing was changed; the attempt is audited as ACCESS_DENIED |
support | No. Only a Complifly Super Admin can assign them |
PLATFORM_USER_NOT_MANAGEABLE |
403 | User administration (PUT, DELETE, POST /users/{id}/end-sessions). An Org Admin tried to change, deactivate, delete or sign out a person who holds a platform role in their company. Nothing was changed; audited |
support | No |
ROLE_NOT_CLASSIFIED |
403 | User administration. The role exists but is neither a platform nor a tenant role (for example the legacy admin), so nobody can assign it, and an Org Admin cannot manage its holder unless the edit moves them onto a tenant role |
admin | Yes: edit the person and choose a standard role |
UNKNOWN_ROLE |
403 | User administration. role is not a role that exists, or is not one the caller may assign (names are exact: Super_Admin is unknown). The message lists the roles this caller may assign |
you | Yes, with a listed role |
ROLE_ASSIGNMENT_NOT_PERMITTED |
403 | User administration. The caller's role may not assign or manage system roles at all | admin | No |
SIGNUP_RETIRED |
410 | POST /api/auth/signup no longer exists. It created users with no role and no company |
you | No. Use POST /api/admin/users |
| Missing registration header | 400 | A user-token call omitted the registration header | you | No |
When it comes back from user administration, it is a company-boundary refusal, not a credential problem. An Org Admin may grant only GSTINs registered under their own company, and a refused request saves nothing. The answer is the same whether the GSTIN belongs to another company or does not exist, so it cannot be used to discover other companies' registrations. Only a Super Admin can grant across companies; see GSTIN Access Control.
Otherwise, 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.
Access explanations¶
Returned by /api/admin/access/*, the read-only API behind the Access tab. See Administration API.
| Code | HTTP | Meaning | Fixed by | Retry? |
|---|---|---|---|---|
INVALID_PERMISSION |
400 | permission is not a permission this build knows |
you | Yes, with a key from GET /access/permissions |
GSTIN_REQUIRED |
400 | A registration-level permission was asked without a gstin |
you | Yes, name the registration. Organisation-level permissions (tds.*) need none |
USER_REQUIRED |
400 | No userId |
you | Yes |
COMPANY_REQUIRED |
400 | Complifly staff or a Super Admin called /people without companyId |
you | Yes |
NO_ORGANISATION |
403 | A company-scoped caller's token names no organisation | admin | No, sign in again as a user with a company |
NOT_FOUND |
404 | No such user or registration in the caller's company. The same answer whether it belongs to another company or does not exist | you | No |
PART_REQUIRED |
400 | GET /access/review with format=csv and no part. A CSV holds one table |
you | Yes, with part=people, registrations, changes, basis or summary |
INVALID_FORMAT, INVALID_DATE, INVALID_RANGE, INVALID_ASOF, RANGE_TOO_LARGE |
400 | /access/review input the server cannot use: a format other than json or csv; from, to or asOf not a date; from after to; asOf in the future; a period longer than 400 days. Nothing is produced |
you | Yes, corrected. Run one review per quarter |
EXPLAIN_FAILED, PEOPLE_FAILED, MATRIX_FAILED, REVIEW_FAILED |
500 | The facts could not be read | support | Yes, once. Persisting: report it with the request id |
Integration policy limits¶
Returned to an ERP or agent when a limit on Integration Policies is hit. The ERP cannot change a limit; the customer asks Complifly.
| Code | HTTP | Meaning | Fixed by | Retry? |
|---|---|---|---|---|
RATE_LIMIT |
429 | More requests in a minute than the connection's limit | you — slow down; or support to raise it | Yes, after the minute |
SPIKE_ARREST |
429 | More requests in one second than allowed, even inside the per-minute limit | you — pace the batch | Yes, after a second |
CONCURRENCY_LIMIT |
429 | Too many requests in progress at once | you — reduce parallelism | Yes, when one completes |
IP_NOT_ALLOWED |
403 | The caller's address is not on the allowlist | support — the allowlist is set by Complifly | No |
GSTIN_LIMIT |
403 | The plan's number of GSTINs is reached and this is a new one | support | No |
There is no quota code. The monthly invoice quota is advisory: an invoice over it is accepted and the overage is recorded, so an ERP never sees QUOTA_EXCEEDED (retired 2026-10-24).
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 |
EWB_IN_DOUBT |
— | An e-way bill request may have reached NIC and the answer did not come back, so it is not known whether the bill exists. It is in the result body as errorCode, with inDoubt: true, and the document is recorded as failed |
support, or you after checking the e-way bill dashboard | Not until the state is confirmed |
EWB_GENERATION_IN_PROGRESS |
409 | An e-way bill is being generated for this document right now. The request was not sent and the document was not changed | you | Yes — after a few seconds |
EWB_ALREADY_EXISTS |
409 | The document already has a live e-way bill. Returned by the standalone flow, and now also by generation from an IRN | you | 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 |
CONCURRENT_MODIFICATION |
409 | Someone else changed this record since you last read it (a stale rowVersion, or the equivalent editable-state check) |
you | Yes — reload the current version and re-apply your change |
WORKFLOW_VIOLATION |
409 | The action doesn't fit the record's current state (e.g. it moved to a terminal status such as DELIVERED/CANCELLED between your read and your write) |
you | No — reload to see the current state; the action may no longer be valid at all |
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.
EWB_IN_DOUBT is the e-way bill counterpart of an indeterminate IRN outcome, with one difference that matters to anyone scripting against it. An IRN that may have been registered is retried automatically, because a repeat is answered with the existing IRN. An e-way bill that may have been issued is never retried automatically, because a repeat has no such answer: it could register a second bill, or be refused as a duplicate and make a document that has a live bill look as if it failed. A request that provably never left (no connection at all, or the provider's "busy" or "unavailable") is retried, a few times, with the same content each time. An ERP that integrates with Complifly is not sent an e-way bill failure for a document that is in doubt, because an ERP that believes NIC refused it would regenerate it.
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. Also returned by POST /api/tds/returns/{id}/approve when the caller filed that return |
you | Never — not waivable by anyone; another approver must act |
SELF_OVERRIDE |
403 | The caller submitted this document and tried to force-approve or force-reject it through POST /api/v1/igm/invoices/{docId}/approval/override. Nothing was changed |
you | Never for that caller — another Org Administrator must override |
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 |
CONCURRENT_MODIFICATION |
409 | Another decision on this same level landed a moment before yours — e.g. two approvers racing, or a double-submitted click | you | Yes — reload; the level was already decided by the other action, so check what it recorded before acting again |
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 — whether the change is a GSTIN override, a company default an affected GSTIN inherits, or the removal of either. gstins[] names every affected registration |
admin | Yes, once the queue is clear — or a Complifly Super Admin 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. Granting more permissions will not change the outcome. Either another holder of the level decides it, or a different Org Administrator overrides with a reason. SELF_OVERRIDE is the same rule applied to the override route: the person who submitted the document cannot force it either.
Paged list errors¶
Returned by GET /api/irn/workbench when called with grid=2 (see List Columns and Saved Layouts API).
All are 400 with { "code", "message" }; none runs a query. A request without grid never returns them.
| Code | Meaning | Fixed by | Retry? |
|---|---|---|---|
GRID_VERSION |
grid is not 2 |
you | Yes, with grid=2 |
GRID_PARAM_UNKNOWN |
A parameter the paged form does not take (it is refused, not ignored) | you | Yes, without it |
GRID_QUERY_INVALID |
A malformed page, size or filter (wrong shape, out of range, too many) |
you | Yes, corrected |
GRID_SORT_KEY |
A sort key, direction or count the list does not offer (the e-way bill status is not sortable) | you | Yes, with a listed key |
GRID_FILTER_KEY |
A filter key the list does not offer | you | Yes, with a listed key |
GRID_FILTER_OP |
An operator the key does not support | you | Yes |
GRID_FILTER_VALUE |
A value the filter cannot take: empty, too long, not ASCII, not a known status, not a real YYYY-MM-DD date, or a range that ends before it starts |
you | Yes, corrected |
GRID_SEARCH |
q is longer than 40 characters |
you | Yes, shorter |
GRID_FY |
fy is not like 2026-27, or month is not all or 1 to 12 |
you | Yes, corrected |
GRID_ATTENTION |
attention and finding together, or a value that is not a rule key / number |
you | Yes, one of them |
GRID_FACETS |
facets is not 1 or only |
you | Yes |
GSTIN_REQUIRED |
The paged form needs one GSTIN; x-gstin is missing or all |
you | After selecting one |
FEATURE_DISABLED_BY_OPERATOR (403) |
ENTERPRISE_GRID_V2 is off for this GSTIN |
admin | Yes, once enabled |
Platform operations screens¶
Returned when a customer's administrator asks for a screen or API that shows how the whole platform is running, across every company (Observability, Operations).
| Code | HTTP | Meaning | Fixed by | Retry? |
|---|---|---|---|---|
PLATFORM_STAFF_ONLY |
403 | The screen shows every company's activity and is for Complifly staff. Nothing is wrong with your account. Your own activity is under Audit Log and Usage | you | No |
Company status¶
Returned when a company is suspended or archived, and by the endpoint that changes a status. What each status means is in Company Status.
| Code | HTTP | Meaning | Fixed by | Retry? |
|---|---|---|---|---|
TENANT_ARCHIVED |
403 | The company is archived: sign-in, every request and document intake are refused. On a request to add something (a user, API key, ERP connection or registration) it is 409 |
admin — a Complifly Super Admin must reactivate the company | No |
TENANT_SUSPENDED |
409 | The company is suspended and a new user, API key, ERP connection or registration was requested. Existing work is unaffected | admin — a Complifly Super Admin must reactivate the company | No |
COMMERCIAL_SUSPENSION_NOT_SUPPORTED |
422 | A status change named a commercial reason (NON_PAYMENT, OVERAGE, PLAN, QUOTA …). The platform does not record one |
you — use a permitted reason code | No |
REASON_CODE_INVALID |
400 | reasonCode is missing or not one of SECURITY, LEGAL, CUSTOMER_REQUEST, OPERATIONAL, OFFBOARDING |
you | Yes, corrected |
REASON_REQUIRED |
400 | reason is missing or outside 5 to 300 characters |
you | Yes, corrected |
STATUS_INVALID |
400 | status is not ACTIVE, SUSPENDED or ARCHIVED |
you | Yes, corrected |
STATUS_UNCHANGED |
409 | The company already has that status | you | No |
STATUS_CHANGED_MEANWHILE |
409 | Someone changed the status between your read and your write | you | Yes, after re-reading |
COMPANY_NOT_FOUND |
404 | No such company | you | No |
COMPANY_REQUIRED |
400 | No company id was given | you | Yes, corrected |
A suspended company is not refused sign-in, requests or document intake. If a client treats TENANT_SUSPENDED as "stop all work", it is wrong: the code appears only on a request to add something.
Usage and integration visibility¶
Returned by the Usage API and by /api/admin/integration-visibility.
| Code | HTTP | Meaning | Fixed by | Retry? |
|---|---|---|---|---|
USAGE_PERIOD_INVALID |
400 | A date is malformed, to is not after from, only one bound was given, or the span exceeds 366 days |
you | Yes, corrected |
USAGE_METRIC_UNKNOWN |
400 | A metric id that is not in the catalogue (GET /api/usage/metrics lists them) |
you | Yes, corrected |
USAGE_GSTIN_INVALID |
400 | gstin is not a 15-character GSTIN |
you | Yes, corrected |
USAGE_COMPANY_REQUIRED |
400 | The session carries no company | admin | Yes, after signing in again |
USAGE_GSTIN_NOT_IN_COMPANY |
403 | The GSTIN belongs to another company | you | No |
USAGE_SCOPE_NOT_PERMITTED |
403 | A company other than your own, all, or the reconciliation was requested by someone who is not a Complifly Super Admin |
you | No |
STATEMENT_NOT_FOUND |
404 | The statement asked for does not exist: your company holds no registration under that PAN, or a CSV was asked for and there is nothing to export | you | No |
STATEMENT_PAN_REQUIRED |
400 | A CSV was asked for and your company holds registrations under more than one PAN. The message lists them; add ?pan= with one |
you | Yes, with pan |
USAGE_FAILED |
500 | An unexpected failure; no detail is returned | support | Once |
SUPER_ADMIN_ONLY |
403 | The integration-visibility endpoints are for a Complifly Super Admin | you | No |
INVENTORY_FAILED |
500 | The integration inventory could not be read | support | Once |
Commercial¶
Returned by the Commercial API. Only the codes a customer's own administrator can receive are listed here; the rest belong to Complifly's internal administration of customer records. None of these is raised by a licence or capacity state: a licence in grace or expired, or a limit exceeded, is reported and never refused.
| Code | HTTP | Meaning | Fixed by | Retry? |
|---|---|---|---|---|
COMMERCIAL_SUPER_ADMIN_ONLY |
403 | The platform commercial endpoints are for a Complifly Super Admin | you | No |
COMMERCIAL_COMPANY_REQUIRED |
400 | The session carries no company | admin | Yes, after signing in again |
COMMERCIAL_FAILED |
500 | An unexpected failure; no detail is returned | support | Once |
When adding a user would go past your plan¶
These are the only refusals a plan limit can cause, and only when Complifly has set your company's Plan & Limits control to Enforce (it is Off unless Complifly sets it). They stop an administrator adding something: nothing is changed, and nothing about issuing an IRN or an e-way bill, or receiving documents from your ERP, is ever affected.
| Code | HTTP | Meaning | Fixed by | Retry? |
|---|---|---|---|---|
ENTITLEMENT_LIMIT_REACHED |
409 | Adding or reactivating a user, or giving one access to another registration, would take your company past the number of users your plan allows. The message says what the plan allows and how many are in use. Nothing was changed | admin — free one up, or ask your Complifly contact to raise the limit | Yes, once there is room |
ENTITLEMENT_NOT_INCLUDED |
409 | A Complifly Super Admin tried to switch on an optional module the customer's plan says is not included. details.canForce is true: repeat the change with force and a written reason to override it, and the exception is recorded |
support | Yes, with force and a reason |
If your company shares its PAN with another company on Complifly, you are never refused: Complifly is told instead.
Go-live¶
Returned by the Go-Live Readiness API. None of these is raised by a company's readiness or go-live state: a company that is not ready, or is not approved, is never refused sign-in, requests or documents. REASON_REQUIRED and DATE_INVALID (listed under Commercial) are also returned here for a missing reason or a malformed date.
| Code | HTTP | Meaning | Fixed by | Retry? |
|---|---|---|---|---|
GO_LIVE_SUPER_ADMIN_ONLY |
403 | The go-live administration endpoints are for a Complifly Super Admin | you | No |
GO_LIVE_COMPANY_REQUIRED |
400 | The session carries no company | admin | Yes, after signing in again |
GO_LIVE_FAILED |
500 | An unexpected failure; no detail is returned | support | Once |
Feature configuration¶
Returned by the Operational Controls endpoints — POST /api/admin/features/toggle, POST /api/admin/gstins/bulk-toggle and the two DELETE …/features/{featureKey} routes — and by any route gated with requireFeature.
Which 403 you get tells you whom to ask. Application routes check the caller's permission first and the module switch second, on every route. PERMISSION_DENIED means the role lacks the right: ask the Org Admin for a role change. FEATURE_DISABLED_BY_OPERATOR means the role was fine and the module is off: ask Complifly, because module switches are part of the subscription. A caller without the right is never told whether the module is on. Both codes are in the response body's code field; the HTTP status is the same, so branch on code, never on 403.
The two 409s are raised for every GSTIN a change reaches, not only the one named. A company default is inherited by every GSTIN with no override of its own, so one company-level write — or the removal of one override — can be refused on behalf of a GSTIN you did not mention. The body's gstins[] says which.
| Code | HTTP | Meaning | Fixed by | Retry? |
|---|---|---|---|---|
VIEW_INVALID |
400 | A saved view or its name is not acceptable (a sort or filter the screen cannot show, a value the list would refuse, a name outside 1 to 60 characters, a view over 4,000 characters). message names the part. Nothing is stored |
you | Yes, with a corrected view |
VIEW_UNKNOWN_SCREEN |
400 | The screen key cannot hold views (only einvoice_irn-workbench-table today) |
you | Yes, with a supported screen |
VIEW_LIMIT |
400 | The user already has 20 views for the screen | you | Yes, after deleting one |
VIEW_NOT_FOUND |
404 | No such view for this user. A view belonging to another user is answered identically, on purpose | you | No |
VIEW_NAME_TAKEN |
409 | The user already has a view with that name for the screen (compared ignoring capitals) | you | Yes, with another name |
PRINT_CONFIG_INVALID |
400 | POST /api/print-config/save refused a corporate field (Udyam_No, Prepared_By, Checked_By, Authorised_By, Reference_Partner, Reference_Manager) that is not text, is too long, contains a character that cannot be printed, or (Udyam) is not in the registered form. message names each field; fields lists them. Nothing is saved |
you | Yes, with the fields corrected |
PRINT_BANK_INVALID |
400 | POST or PUT /api/print-config/bank-accounts refused an account number, IFSC, SWIFT code, account type or name that does not meet its rule. message names each; fields lists them. Nothing is saved |
you | Yes, with the values corrected |
BANK_ACCOUNT_EXISTS |
409 | An active bank account with the same number already exists for the registration | you | Yes, after editing the existing account or deactivating it |
BANK_ACCOUNT_NOT_FOUND |
404 | The account id does not exist for the registration in X-GSTIN (another registration's account is indistinguishable from a missing one) |
you | Yes, with the right registration and id |
PRINT_DTO_SCHEMA_MISMATCH |
500 | A print was refused because the template and the document data no longer agree (the template was built for a different version of the print data). Raised only with CORPORATE_PRINT_FRAMEWORK on; without it the same mismatch prints blanks. The message names the template, its version and the document's schema |
support | No - report it with the document; switching the control off prints the document with the original renderer |
FEATURE_DISABLED_BY_OPERATOR |
403 | The route's feature flag is off for this GSTIN, or for the company where the module is company-wide (TDS, the agent). The caller's permission was checked first and passed, so this is never a role problem. A Super Admin is not exempt | support | Yes, once enabled |
ACCESS_WIDENING_REASON_REQUIRED |
400 | The change would relax segregation of duties (a registration's effective SOD_ENFORCED moving from on to off) and no reason was sent. The body names the registrations in gstins. Nothing was changed. Any caller, a Super Admin included |
you | Yes, with a written reason. The company's other Org Admins are then emailed |
SOD_LOCKED |
403 | A company administrator tried to relax segregation of duties (SOD_ENFORCED moving from on to off for any registration, including by removing an older GSTIN override that kept one enforced) after the company started live work: a live IRN, a generated e-way bill or a decided approval. The body names the registrations in gstins, with lockedSince and evidence (IRN, EWB or APPROVAL). Nothing was changed; the attempt is audited as FEATURE_CHANGE_BLOCKED. Turning it on is never refused |
support | No — the Org Admin cannot. A Complifly Super Admin can, with a reason and approvalRef |
SOD_APPROVAL_REF_REQUIRED |
400 | A Complifly Super Admin relaxed segregation of duties for a locked company without a usable approvalRef (3 to 120 characters after trimming). Nothing was changed. Sent before the reason is judged, so send both |
you | Yes, with the customer's approvalRef and a written reason |
EMERGENCY_GRANT_NOT_PERMITTED |
403 | The caller is not an Org Admin of the company or a Super Admin | you | No |
EMERGENCY_GRANT_STRICT |
403 | The company is in Strict governance mode and the caller is an Org Admin: only a Super Admin can lend | support | Yes, from a Complifly Super Admin with the customer's approvalRef |
EMERGENCY_SELF_GRANT |
403 | The caller tried to lend emergency access to themselves, in either mode | you | Yes, by another administrator |
EMERGENCY_ROLE_NOT_LENDABLE |
400 | roleKey is not one of tax_controller, tax_executive, transport_manager, logistics_operator. The body lists them in lendable |
you | Yes, with a lendable role |
EMERGENCY_DURATION_INVALID |
400 | durationMinutes is under 15 or over the mode's limit (1440 Standard, 240 Strict). The body carries minMinutes and maxMinutes |
you | Yes, with a duration in range |
EMERGENCY_REASON_REQUIRED |
400 | A reason of at least 10 characters is required to lend (5 to revoke) | you | Yes, with a reason |
EMERGENCY_APPROVAL_REF_REQUIRED |
400 | Strict mode: the customer's approvalRef (3 to 120 characters) is required |
you | Yes, with the reference |
EMERGENCY_GRANTEE_NOT_FOUND |
404 | No such person in this company (another company's person answers the same) | you | Yes, with a user of the company |
EMERGENCY_GRANTEE_INACTIVE |
409 | The account is deactivated | you | After reactivating it |
EMERGENCY_GRANTEE_NOT_ELIGIBLE |
403 | Auditor and Complifly accounts cannot be lent emergency access | you | No |
EMERGENCY_GRANT_EXISTS |
409 | The person already has emergency access that has not ended. The body carries grantId |
you | After revoking it or when it expires |
EMERGENCY_GRANT_ENDED |
409 | The grant being revoked has already ended | you | No |
EMERGENCY_GRANT_NOT_FOUND |
404 | No such grant in the caller's company | you | No |
SUPPORT_WINDOW_REQUIRED |
403 | The company is in Strict mode and has no support window open; the caller is a Complifly support or platform engineer. Audited against the customer as SUPPORT_WINDOW_DENIED |
support | Yes, once the company's Org Admin opens a window |
SUPPORT_WINDOW_UNAVAILABLE |
503 | Whether a window is open could not be checked. The request is refused, never allowed | support | Yes, shortly |
SUPPORT_WINDOW_CUSTOMER_ONLY |
403 | Only the company's own Org Admin can open a window | you | No |
SUPPORT_WINDOW_EXISTS |
409 | A window is already open. The body carries windowId |
you | After closing it |
SUPPORT_WINDOW_DURATION_INVALID |
400 | hours is under 1 or over 168 |
you | Yes, in range |
SUPPORT_WINDOW_REASON_REQUIRED |
400 | A reason of at least 5 characters is required | you | Yes, with a reason |
SUPPORT_WINDOW_NOT_PERMITTED |
403 | Only the company's Org Admin or a Super Admin can close a window | you | No |
SUPPORT_WINDOW_NOT_FOUND |
404 | No such window in the caller's company | you | No |
SUPPORT_WINDOW_ENDED |
409 | The window being closed has already ended | you | No |
ROLE_REQUEST_PENDING |
409 | The person has a role request waiting for a second approval, so they cannot be activated or re-roled by PUT /users. The body carries requestId |
you | After the request is decided |
ROLE_REQUEST_EXISTS |
409 | The person already has a pending request | you | After it is decided |
ROLE_REQUEST_SELF_DECISION |
403 | The caller made the request and cannot decide it | you | By another administrator |
ROLE_REQUEST_NOT_PERMITTED |
403 | The caller may not decide this request (Super Admin role: another Super Admin only; Org Admin role: another Org Admin of the company or a Super Admin), or may not withdraw it (requester only) | you | By the right person |
ROLE_REQUEST_NOTE_REQUIRED |
400 | Rejecting needs a note of at least 3 characters | you | Yes, with a note |
ROLE_REQUEST_DECIDED |
409 | The request is no longer pending | you | No |
ROLE_REQUEST_NOT_FOUND |
404 | No such request in the caller's company | you | No |
ROLE_REQUEST_TARGET_GONE |
409 | The person the request is about no longer exists in the company | you | No |
ROLE_REQUEST_REASON_REQUIRED |
400 | A roleChangeReason, if sent, must be at least 5 characters |
you | Yes |
ELEVATED_ACCESS_FAILED |
500 | An unexpected failure in an elevated-access call | support | Yes, and report it if it persists |
UNKNOWN_FEATURE_KEY |
400 | featureKey is not a control in this build |
you | Yes, with a key from GET /api/admin/feature-definitions |
INVALID_FEATURE_VALUE |
400 | The control does not accept that value — wrong type, outside its range, or not one of its options. expects in the body says what it accepts. Nothing is stored |
you | Yes, with an accepted value |
FEATURE_SCOPE_NOT_ALLOWED |
400 | A company-wide control (MODULE_TDS_ENABLED, AGENT_ENABLED, SOD_ENFORCED and the other company-scoped controls) was sent as a GSTIN override |
you | Yes — send it as a company default (scope: "company") |
FEATURE_WRITE_NOT_PERMITTED |
403 | The control is a licensing / subscription control (writeRole: PLATFORM) and the caller is not a Complifly Super Admin — or the caller's role can only view controls. Nothing is changed; the attempt is audited |
support | No — ask Complifly |
FORCE_NOT_PERMITTED |
403 | force: true from anyone but a Complifly Super Admin. Overriding a safety check is Complifly's alone |
support | No — resolve what the check reported, or ask Complifly |
COMPANY_NOT_PERMITTED |
403 | The company in the request is not the caller's own, and the caller is not a Super Admin | admin | No — a Super Admin must make the change |
GSTIN_NOT_PERMITTED |
403 | The GSTIN is not registered under the caller's company. The same answer is given whether it belongs to another company or does not exist | admin | No |
UNKNOWN_COMPANY |
404 | The company is not in the company catalogue | you | Yes, with a company from GET /api/admin/companies |
GSTIN_NOT_IN_COMPANY |
404 | The GSTIN in the path does not belong to the company in the path | you | Yes, with the right company |
NO_STORED_VALUE |
404 | A DELETE found no override or company default to remove |
you | No — there is nothing to remove |
CONCURRENT_MODIFICATION |
409 | The rowVersion sent no longer matches — someone else changed the value first |
you | Yes — reload, then decide again |
TOO_MANY_GSTINS |
400 | More than 500 GSTINs in one bulk call | you | Yes, in smaller batches |
NON_ERP_POSTING_BLOCKED |
409 | The change would turn EXCEL_UPLOAD_ENABLED or INVOICING_ENABLED on for a GSTIN with an active ERP connection — directly, through a company default it inherits, or by removing the override that kept it off |
admin | Yes, once cleared — or a Complifly Super Admin with force + reason |
REASON_REQUIRED |
400 | force: true was sent on the feature toggle without a reason |
you | Yes, with a reason |
NON_ERP_POSTING_BLOCKED protects against the same document being posted twice¶
A GSTIN fed by a connected ERP that also accepts Excel uploads or Invoice Generation Module entries can receive one commercial transaction through both doors — each raising its own IRN and e-Way Bill. Nothing else in the platform detects or merges the duplicate, so the toggle refuses the change rather than opening a second channel silently.
{
"code": "NON_ERP_POSTING_BLOCKED",
"message": "29AAACW3775F000 is fed by D365FO. Turning Excel Upload on opens a second door for the same GSTIN to post documents — …",
"gstins": ["29AAACW3775F000"],
"connections": ["D365FO"]
}
Leave the channel off, route the correction through the ERP instead, or — for a genuine multi-source setup such as an ERP migration running two systems in parallel — repeat the call with force: true and a written reason. The reason is written to the audit trail against this change. Forcing does not enable ERP_WRITEBACK_ENABLED and adds no duplicate detection between channels. See Feature Flags.
Compliance profile¶
Returned by the Compliance Profile endpoints under /api/admin/…/compliance-profile, /api/admin/gstins/{gstin}/registration-class and /api/admin/companies/{companyId}/onboarding-tasks. See the Compliance Profile API. Every refusal changes nothing.
| Code | HTTP | Meaning | Fixed by | Retry? |
|---|---|---|---|---|
PERMISSION_DENIED |
403 | A write needs compliance_profile.manage (a Complifly Super Admin); a read needs compliance_profile.view. A role without the permission is also refused earlier with a plain 403 carrying only a message |
support | No. A customer cannot obtain write access, so ask your Complifly implementation consultant |
COMPANY_NOT_PERMITTED |
403 | The company in the path is not the caller's own, and the caller is not Complifly staff | admin | No |
UNKNOWN_COMPANY |
404 | The company is not in the company catalogue | you | Yes, with a company from GET /api/admin/companies |
UNKNOWN_GSTIN |
404 | The GSTIN does not belong to a company the caller may use. The same answer is given whether it belongs to another company or does not exist | you | Yes, with the right GSTIN |
UNSUPPORTED_ERP |
404 | {erp} in the path is not TALLY |
you | Yes, with TALLY |
INVALID_COMPANY, INVALID_GSTIN, INVALID_PARAMETER, INVALID_STATUS, INVALID_TASK_CODE |
400 | A path or query value is malformed | you | Yes, corrected |
DELTA_REQUIRED |
400 | The body has no delta object |
you | Yes |
BASE_REVISION_REQUIRED |
400 | baseRevision is missing. Send the revision you loaded, or null when the company has none. It cannot be left out, because that would skip the check that stops one person overwriting another |
you | Yes |
NOTE_REQUIRED |
400 | A change was sent with no reason | you | Yes, with a note |
NOTE_TOO_LONG |
400 | The note is over 500 characters | you | Yes, shortened |
PROFILE_DELTA_INVALID |
422 | The settings do not validate. problems[] lists each, in words: a section a customer may not change, a key the base profile does not define, or a value that is not an accepted option |
you | Yes, after fixing each problem |
CONCURRENT_MODIFICATION |
409 | The profile changed since you loaded it. latestRevision says what it is now |
you | Yes. Reload, then make your change again. It is not merged for you |
REVISION_NOT_FOUND |
404 | The revision does not exist for this company | you | No |
ROLLBACK_TARGET_IS_CURRENT |
422 | The revision you asked to roll back to is already the active one | you | No. There is nothing to restore |
CLASS_REQUIRED |
400 | registrationClass is missing from the body. Send a class, or null to clear it |
you | Yes |
INVALID_CLASS |
400 | The class is not TURNOVER_GE_5CR or TURNOVER_LT_5CR |
you | Yes |
TASK_NOT_FOUND |
404 | The task is not this company's | you | No |
TASK_NOT_OPEN |
409 | The task is already closed, or someone closed it first | you | No |
ASSIGNEE_REQUIRED |
400 | assignee is missing |
you | Yes |
ASSIGNEE_NOT_ELIGIBLE |
422 | The assignee is not an active user of this company | you | Yes, with a user from the task list's assignableUsers |
COMPLIANCE_PROFILE_ERROR |
500 | An unexpected failure. The message says nothing was changed | support | Yes, once; then quote the request identifier |
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 |
Agent held reasons¶
A HELD receipt means Complifly sent nothing and assumed nothing; the agent keeps the voucher and sends it again once the cause is fixed. It is not a rejection and is not stored in the intake-refusals list. The heldReason in the receipt is one of:
| Code | Meaning | Who resolves it | Retry? |
|---|---|---|---|
OBLIGATION_RULE_MISSING |
No obligation rule covers this document type, sale type, turnover band and date | Complifly support, after compliance confirms the rule | Yes, automatically, once the rule exists |
MOVEMENT_PURPOSE_UNMAPPED |
A challan-type voucher whose voucher type the customer's setup does not map to a purpose | Complifly support, from the customer's list of purposes | Yes, after the setup is updated |
MOVEMENT_PURPOSE_INVALID_FOR_DOCUMENT |
NIC does not allow this purpose on this document type | Complifly support | Yes, after the setup is corrected |
MOVEMENT_DIRECTION_MISMATCH |
The voucher type is set as inward but the purpose is outward, or the reverse | Complifly support | Yes, after the setup is corrected |
MOVEMENT_PURPOSE_UNKNOWN |
The purpose named in the setup is not one Complifly defines | Complifly support | Yes, after the setup is corrected |
EXPORT_PORT_NOT_CONFIGURED |
An export that needs an e-way bill, and the company has no export port saved. The voucher is not stored | Complifly support saves the port once the customer gives its name, address, city, state and PIN | Yes: send the voucher again from Tally after the port is saved. It is not retried automatically |
PROFILE_DELTA_INVALID |
The customer's saved setup no longer validates against Complifly's standard settings. The whole batch is held; errors[] names each fault |
Complifly support | Yes, after the setup is corrected |
Warnings on an accepted document (the receipt's warnings[]; the document was stored):
| Code | Meaning | Action |
|---|---|---|
MOVEMENT_UNKNOWN_AT_THRESHOLD |
At or over the e-way bill value limit, with no dispatch details on the voucher. Complifly did not assume the goods are not moving. A B2CS or B2CL sale is stored as EWB_PENDING for the e-Way Bill Doc Workbench; any other sale is stored with no e-way bill flagged |
Add the shipping details in the e-Way Bill Doc Workbench (B2C), or in Tally for any other sale. If nothing moves, leave it: nothing is sent to NIC until an e-way bill is generated |
UNKNOWN_TURNOVER |
No turnover band is recorded for the GSTIN. The document was not held; it was routed as if above the e-invoicing limit. An AGENT_UNKNOWN_TURNOVER audit entry was written and an onboarding task opened |
Ask Complifly support to record the band |
SHIPPING_DATA_ABSENT |
The voucher has no dispatch details. Complifly does not read that as "no movement" | Add dispatch details in Tally if goods are moving |
EXPORT_PORT_NOT_CONFIGURED |
An export that only needs an IRN, and the company has no export port. The IRN was filed; the e-way bill cannot be made until a port is saved. An onboarding task CONFIGURE_EXPORT_PORT was opened (one per company) |
Complifly support saves the export port |
STOCK_TRANSFER_MODE_UNSET |
A stock-transfer voucher was held because the customer's setup does not say whether transfers are challans or tax invoices | Complifly support records the mode once the customer confirms it (appears in the held receipt's errors[]) |
SHIP_BLOCK_INCOMPLETE |
The ship-to or dispatch-from address has some but not all of state, location and pin. The block was omitted, not sent partial; the document was accepted | Fill in all three on the voucher, or clear the partial fields |
LUT_SOURCE_UNCONFIGURED |
The setup says to read the LUT from a field but names no field; no LUT was found | Complifly support names the field or restores narration reading |
RULE_IS_COMPLIANCE_PLACEHOLDER |
The rule that decided the route has not been confirmed by Complifly's compliance review | None for the customer; Complifly confirms the rule |
Agent enrolment¶
These come from POST /api/v1/agent/enrol (the agent redeeming its token) and from the enrolment requests under /api/admin/agent-enrolments. A refusal that says "the token was not used up" can be tried again with the same token.
| Code | HTTP | Meaning | Fixed by |
|---|---|---|---|
ENROLMENT_TOKEN_INVALID |
403 | The token is not one Complifly issued | admin: copy it again, or request a new enrolment |
ENROLMENT_TOKEN_USED |
403 | The token has already been redeemed. A token works once | admin: request a new enrolment |
ENROLMENT_TOKEN_EXPIRED |
403 | The token is older than 24 hours | admin: request a new enrolment |
ENROLMENT_NOT_APPROVED |
403 | The token is not tied to an approved request (it was rejected, or the token was not issued through a request) | admin |
AGENT_DISABLED |
403 | The Complifly Agent control is off for the company. The token was not used up | support switches it on |
ENROLMENT_CSR_INVALID |
400 | The certificate request is malformed, its key is weak or of a disallowed type, or the key was used before. problems[] names each. The token was not used up |
support |
ENROLMENT_CA_UNAVAILABLE |
503 | The certificate service did not answer or is not configured for this environment. retryable: true. The token was not used up |
try again; support if it persists |
ENROLMENT_CA_REFUSED |
502 | The certificate service declined to issue. The token was not used up | support |
BAD_REQUEST |
400 | enrolmentToken, csr or machineFingerprint is missing |
the installer |
ENROLMENT_REQUEST_DECIDED |
409 | The request was approved or rejected already (status says which). No second token was issued |
reload |
ENROLMENT_REQUEST_EXPIRED |
409 | Nobody decided the request within 7 days | admin: request again |
SELF_APPROVAL_NOT_ALLOWED |
403 | The person deciding is the person who asked | a different Complifly person |
REJECTION_REASON_REQUIRED, MACHINE_LABEL_REQUIRED, GSTIN_INVALID, DECISION_INVALID, COMPANY_REQUIRED |
400 | A field is missing or malformed; the message names it | you |
ENROLMENT_REQUEST_NOT_FOUND |
404 | No such request | you |
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.
Attention Center findings¶
Returned by /api/attention — see Attention API.
| Code | HTTP | Meaning | Fixed by | Retry? |
|---|---|---|---|---|
GSTIN_REQUIRED |
400 | No single GSTIN selected | you | After selecting one |
INVALID_RESOLUTION_CODE |
400 | The decision is not allowed for this kind of finding — most kinds allow none and close only when the document is fixed | you | No — fix the document instead |
INVALID_TRANSITION |
400 / 409 | The action does not fit the finding's status, or the bulk request is malformed | you | After re-reading the finding |
RULE_NOT_PERMITTED |
403 | The role lacks the document right for this kind of finding | admin | No |
FINDING_NOT_FOUND |
404 | No such finding for this GSTIN, or not visible to the caller | you | No |
STALE_FINDING |
409 | The finding changed since the caller read it | you | Yes, after re-reading |
NO_RULES_ENABLED |
409 | A check was requested but no rules are enabled | support | No |
EVIDENCE_REQUIRED |
422 | A decision's required evidence or note is missing or malformed | you | Yes, with the missing detail |
ASSIGNEE_NOT_ELIGIBLE |
422 | The chosen user cannot own findings on this GSTIN or of this kind | you | With an eligible user |
EVALUATE_RATE_LIMITED |
429 | A check for this GSTIN ran less than a minute ago | you | Yes, after a minute |
ATTENTION_ERROR |
500 | Unexpected failure; nothing was changed | support | Yes, bounded |
Notification settings (/api/attention/notification-profiles, Attention API):
| Code | HTTP | Meaning | Fixed by | Retry? |
|---|---|---|---|---|
INVALID_EVENT, INVALID_SEVERITY, INVALID_HOUR, INVALID_WEEKDAY, INVALID_TIMEZONE, INVALID_GSTIN, INVALID_COMPANY |
400 | A value is not one the API accepts. OVERDUE is not an event |
you | After correcting it |
EVENT_HAS_NO_EMAIL |
400 | A registration or organization setting for the bell-only critical-item event. A person sets that one for themselves | you | No |
USER_PROFILE_HAS_NO_RECIPIENTS |
400 | Recipients were sent with a personal setting | you | After removing them |
TOO_MANY_RECIPIENTS, INVALID_RECIPIENT_TYPE, INVALID_ROLE, INVALID_EMAIL |
400 / 422 | A recipient is not valid | you | After correcting it |
SCOPE_NOT_PERMITTED |
403 | The role may not edit that scope: a registration needs an organization administrator or admin.manage_users, an organization needs an organization administrator |
admin | No |
GSTIN_NOT_PERMITTED, COMPANY_NOT_PERMITTED |
403 | The registration or company is outside the caller's own. The same answer whether it belongs to someone else or does not exist | admin | No |
EMAIL_RECIPIENT_NEEDS_ORG_ADMIN |
403 | An external address was added by someone who is not an organization administrator | admin | No |
NOT_PENDING, UNKNOWN_COMPANY, UNKNOWN_GSTIN |
404 | A confirmation was re-sent for an address that is already confirmed or not on the setting; or the company is not in the catalogue; or the registration belongs to no company | you / admin | No |
STALE_PROFILE |
409 | Someone else saved the same setting first | you | Yes, after reloading |
BLOCKED_BY_ORGANIZATION |
409 | A registration was set On for an event the organization has switched Off. A lower level cannot turn on what a higher one switched off | admin | After the organization turns it on |
RECIPIENT_NOT_ELIGIBLE |
422 | A named user is not an active user of the registration or company, or has no email address | you | With an eligible user |
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 |
Related Articles¶
- API Authentication — the authentication codes in context
- Rate Limits and Errors — the retry policy
- Machine-to-Machine Authentication — credential scope
- Login and Access Failures