Skip to content

Administration API

Applies to: All subscriptions

Purpose

Describe the administrative surface for teams automating provisioning or operational tasks, and state plainly where automation is appropriate and where the console is the better tool.

Audience

Integration developers automating administration, and system administrators.

Prerequisites

Reference

These endpoints exist to serve Complifly's own admin console. They are documented for automation, but the console is the supported path for occasional administration. Automate provisioning and bulk work; do not rebuild the console.

Users and access

Base path /api/admin

Method Path Purpose
GET /users List users
POST /users Create a user
PUT /users/{userId} Update a user
DELETE /users/{userId} Remove a user
GET /company-gstins Registrations for the company
GET /all-companies Companies visible to the caller
GET /approval-roles The assignable Approval Engine role ladder
POST /features/toggle Set a per-GSTIN feature flag
GET /operational-matrix Feature and permission overview
GET /audit-logs Audit trail
POST /audit/log Record an audit entry
GET /fiscal-years Fiscal calendar
POST /fiscal-years/toggle Open or close a year
POST /fiscal-years/seed Seed the calendar

Prefer disabling a user over deleting one. Deletion breaks the attribution of past actions in the audit trail, which a compliance system must preserve.

Assigning roles on a user

POST /users and PUT /users/{userId} accept two independent role fields. They are not interchangeable and a user usually needs both.

Field Required Values Effect
role Yes on POST, optional on PUT org_admin, tax_controller, tax_executive, approver, transport_manager, logistics_operator, external_auditor, super_admin The permission set — which endpoints the user may call
approvalRole No One of the keys from GET /approval-roles, or null to clear Which level of a multi-level approval the user occupies

Both are validated against a server-side allowlist. Two refusals to expect:

Response Condition
403Unknown system role: {role}. Valid roles: … role is not in the permission matrix. A typo would otherwise create a user with an empty permission set who can sign in and do nothing
403Only a Super Admin may assign the super_admin role An org administrator attempted to grant super_admin. That role reaches across tenant boundaries, so granting it is a platform act
400Unknown approval role: {value} approvalRole is not in the ladder served by GET /approval-roles

To clear an approval role, send approvalRole: null (or "") explicitly on PUT. Omitting the field leaves the current value unchanged — the two cases are distinguished deliberately, so an unrelated edit cannot unassign someone.

To let a user approve invoices in production, set both: a role that holds invoice.approve (approver or tax_controller) and an approvalRole naming their level. Setting one without the other is accepted and returns 200, but the user will never be able to complete an approval. Nothing warns you.

POST /api/admin/features/toggle

Sets one per-GSTIN feature flag. Requires admin.manage_users.

{ "gstin": "29AAACW3775F000", "featureKey": "IGM_APPROVAL_ENGINE", "value": false }

Most flags are written straight through. IGM_APPROVAL_ENGINE is the exception when the value is falsy, because switching it off changes the fate of documents already in flight rather than only what happens next.

Response Condition
200 Written. cancelledApprovals is present only on a forced disable
409APPROVALS_IN_FLIGHT Documents still await approval. The body carries open[], one row per blocking document with waitingOn and level
400REASON_REQUIRED force: true was sent without a reason

To force it through, add "force": true and a "reason". That path cancels the open approvals — OVERRIDDEN levels, CANCELLED requests, documents left with no approval decision — and never approves them. See Platform Error Codes.

GET /api/admin/approval-roles

The approval ladder an administrator may assign, in seniority order. Requires admin.manage_users. Tenant-independent — the ladder is the same for every organisation.

{
  "roles": [
    { "key": "finance_executive",  "label": "Finance Executive",  "rank": 10 },
    { "key": "finance_manager",    "label": "Finance Manager",    "rank": 20 },
    { "key": "finance_controller", "label": "Finance Controller", "rank": 30 },
    { "key": "business_head",      "label": "Business Head",      "rank": 40 },
    { "key": "plant_head",         "label": "Plant Head",         "rank": 40 },
    { "key": "director",           "label": "Director",           "rank": 50 },
    { "key": "cfo",                "label": "CFO",                "rank": 60 }
  ]
}

rank orders the ladder; equal ranks are peers at the same level, not consecutive steps. Read the list rather than hardcoding it — it is the same allowlist the write endpoints validate against, so a hardcoded copy can only ever go stale.

Response Condition
200 Success
403 The caller does not hold admin.manage_users

Machine credentials

Base path /api/admin/api-keys

Method Path Purpose
GET / List static keys, without their secrets
GET /connections List the OAuth connections — the credentials live integrations actually use
POST / Issue a static key
PATCH /{keyId}/oauth-only Require OAuth for this client, or allow its static key again. Reversible
DELETE /{keyId} Revoke a static key. Permanent

A static key's secret is returned once, at issue. It cannot be retrieved afterwards. Capture it into a secret manager in the same operation that creates it — a provisioning script that issues a key and does not store it has created an unusable credential.

GET / returns a computed still_accepted for each key, which answers a different question from is_active: would this key be accepted by the API right now? A key can be dormant in every ordinary sense — unused for months, its ERP long since moved to OAuth — and still authenticate the moment anyone presents it. Use still_accepted, not the active flag, when auditing what is open.

Where a key has been superseded by an OAuth connection, superseded_by names it. A key that is both superseded and still accepted is an open path nobody is watching.

ERP connection onboarding

Base path /api/admin/erp-connections. Platform Super Admin only — these routes decide which external directory may post documents into a tenant.

Method Path Purpose
GET / Connections at every stage, plus the consent configuration
POST / Create a connection and its first consent link
POST /{id}/consent-link Mint a fresh consent link
PATCH /{id}/tenant Record the customer's directory ID by hand
PATCH /{id}/client-app Record their application ID — this activates the connection
PATCH /{id}/status Suspend or resume

There is one public route, and it is public by necessity:

Method Path Purpose
GET /api/erp-consent/callback Where Microsoft Entra returns the customer's administrator after consent

That callback has no session and no bearer token — the person arriving is an administrator at another company who has never signed in. A single-use, short-lived state nonce is the whole control, and it is redeemed once. Do not treat it as an ordinary admin endpoint, and do not put it behind the admin gate: every real customer would be refused.

A connection's status is PENDING_CONSENT, AWAITING_CLIENT, ACTIVE or SUSPENDED. See Machine-to-Machine Authentication for what each stage is waiting on.

Feature configuration

Method Path Purpose
POST /api/admin/features/toggle Enable or disable a feature for a registration

Features are per registration. A script enabling a module across a group must iterate every registration.

Provider configuration

Base path /api/gsp and /api/admin/gsp

Method Path Purpose
GET /capabilities Capability matrix
GET /capabilities/document Capabilities for a specific document, following its provenance
GET /providers Known providers
GET /config Current assignment
POST /config Assign a provider
DELETE /config/{id} Remove an assignment
POST /test Test the connection
GET /live-documents Documents still live against a provider

live-documents is the endpoint to consult before decommissioning a provider's credentials after a switch. Documents issued by that provider must still be serviceable by it.

Document routing

Base path /api/admin/doc-type-routing

Method Path Purpose
GET / Current rules
GET /resolve/{docType} How a type resolves
POST / Create a company rule (no update; see below)
DELETE /{id} Remove a company rule

These are the only way to manage routing — there is no admin console screen for it, and there is no update operation: changing a route means deleting the company rule and creating it again. Global defaults cannot be deleted. See Document Type Routing.

Write-back operations

Base path /api/v1/integration/admin

Method Path Purpose
GET /events Inspect events
GET /events/{eventPk}/attempts Delivery attempt history
GET /requeue/preview Preview what a requeue would affect
POST /requeue Requeue failed events
POST /events/{eventPk}/replay Replay one event
POST /skip Skip events deliberately

Always run the preview before a requeue. A requeue with a wider scope than intended replays a large volume into a consumer that may not be ready for it.

Write-back subscriptions

Base path /api/admin/writeback-subscriptions. Platform Super Admin only.

Method Path Purpose
GET / Subscriptions, the connections eligible for one, and the emitted event catalogue
POST / Create a subscription for a connection and registration
PATCH /{id}/active Pause or resume

GET / reports which active connections have no subscriber, which is the condition worth acting on: without a subscription, that ERP's documents emit no events at all and nothing errors on either side.

There is deliberately no delete. Pausing stops delivery while events keep accumulating and drain on resume; a deleted row is indistinguishable from one that was never created, which is the state this endpoint exists to make visible.

The event catalogue is served by the API rather than held by any client. A subscription's event filter is an allowlist, so a type missing from a stale local list would silently drop that whole event type for that ERP.

Onboarding

Base path /api/admin/onboarding

Method Path Purpose
GET /defaults Default provisioning settings
GET /companies Existing companies
GET /preflight Check before provisioning
POST /provision Provision a company or registration

Provisioning creates a tenant boundary, so it is a platform-level act and is restricted accordingly. Run the preflight check first.

Validation

Check Method Pass condition
Administrative authority required Call with a non-administrative token Refused
User creation works Create one and sign in as it Succeeds
Credential issue captures the secret Inspect your provisioning script The secret is stored at issue
Revocation is immediate Revoke and call an ingest endpoint Refused
Feature toggle applies per registration Toggle for one registration and check another Only the intended one changed
Provider test works Run it Success
Live documents checked before decommissioning Query before withdrawing old credentials An empty result before withdrawal
Requeue previewed Preview before requeuing The scope matches expectation
Audit entries appear Perform an administrative action Recorded with the correct actor

Troubleshooting

Symptom Cause Action
403 on every administrative call Token lacks an administrative role Use an administrative account
403 Unknown system role when creating a user role misspelt or not a real role Send one of the eight values listed under Users and access
403 Only a Super Admin may assign the super_admin role An org administrator tried to grant super_admin Correct. Escalate to Complifly if a platform-level account is genuinely needed
A user was created successfully but cannot approve anything Only one of role / approvalRole was set Both are required. Check the user record and set the missing one
Clearing an approval role has no effect The field was omitted rather than sent as null Send approvalRole: null explicitly on PUT
A credential is unusable after provisioning The secret was not captured at issue Revoke and re-issue, capturing it this time
A feature toggle appears not to work Applied to one registration only, or the brief cache has not expired Iterate all registrations; wait a minute
Cancellations fail after decommissioning a provider Live documents still existed for it Restore the credentials; check live-documents before withdrawal
A requeue flooded a consumer Requeued without previewing Preview first. Pause the consumer before a large replay
Audit trail cannot attribute an old action The user was deleted rather than disabled Disable, never delete
Provisioning fails Preflight conditions not met Run the preflight check and resolve what it reports
An automation broke after an upgrade It depends on the console's own endpoints, which follow the interface Automate provisioning and bulk work only; use the console for occasional administration