Consumers & Applications Management API
Overview
This page covers two owner-centric surfaces of the Management API: Consumers (the platform's credential-based identities) and Applications (API Portal applications). Both expose bounded listing, one-record reads, and two sub-resources of a single owner — its API clients and its limit assignments — in the portable form the rest of the Management API uses. Applications also have a write surface of their own — create, update, delete, suspend/resume (also reachable as disable/enable), archive (also reachable as revoke), access, secret material and token settings — described in Application writes below. Both owners expose the move to another organization of the same project — see Transfer to another organization below.
Every address on this page is under /apiops/projects/{projectName} and follows the shared Common Contract: the stable error envelope, correlation IDs, and bounded pagination. A caller outside the project's scope always gets 404 resourceNotFound, never 403. The two surfaces treat the admin (platform) project differently:
- Consumers are project-owned, so a call on the admin project is refused with
400 malformedRequest(params.reason = platformScopeNotSupported). - Applications are reachable on the admin project's path. It lists the administration project's own applications and every
PORTALapplication, because a portal application belongs to no project. Three roles pass that path: System Administrator, Portal Business and Portal Manager. The two portal roles reachPORTALapplications and their API clients only; a System Administrator reaches all of them. Portal Developer and every other caller get404 resourceNotFound. On an ordinary project the path lists that project's ownINTERNALapplications. See Administration path and portal roles.
References on this surface are portable — no Mongo id ever crosses the boundary. A consumer is addressed by its consumerKey, an application by its applicationKey; organizations, API clients and limit-assignment references are all echoed back in the stable, name-based form the rest of the Management API uses.
Creating, updating and deleting a consumer, changing its password, granting access, and attaching certificate material or token settings are on the Consumers API, which addresses the same record by its consumerKey. The earlier Credentials API addressed the record by its username and is deprecated. The organization transfer described below is documented on this page.
An application has the same write surface as a consumer, documented in Application writes. It has two differences. There is no actions/change-password because an application never authenticates by itself (its API clients do), and no actions/sync-match because the Manager has no synchronization tab for applications. There is no promote action either: promotion is a step of the portal's approval flow. A portal application is created by its portal, so POST /applications creates only INTERNAL applications.
Endpoints
The /entitlements sub-resources of earlier versions are removed. Access is defined only on the ACL, which is managed on the Access Control screen, with the consumer access endpoints, and for an application with the application access endpoints below.
Consumers
| Method | Path | Description |
|---|---|---|
GET | /consumers | The project's consumers, by username by default; filterable by organization code, enabled flag and a username/full-name search |
GET | /consumers/{consumerKey} | One consumer, addressed by its consumer key; no secret material is ever included |
GET | /consumers/{consumerKey}/api-clients | Summary rows of the API clients owned by the consumer |
GET | /consumers/{consumerKey}/limit-assignments | Every limit assignment whose subject is the consumer |
POST | /consumers/{consumerKey}/actions/transfer-organization | Move the consumer to another organization of the same project |
POST | /consumers/{consumerKey}/actions/suspend | Disable the consumer, reversibly; the same step as actions/disable |
POST | /consumers/{consumerKey}/actions/resume | Enable the consumer again; the same step as actions/enable |
POST | /consumers/{consumerKey}/actions/revoke | End the consumer permanently and keep its record (one-way) |
GET /consumers?organizationCode&enabledOnly&search&page&size&sort sorts by username,asc by default; sort may also name fullName or enabled. search is a case-insensitive contains-match against username and full name.
Applications
| Method | Path | Description |
|---|---|---|
GET | /applications | The project's applications, by name by default; filterable by organization code, status and a name search |
GET | /applications/{applicationKey} | One application, addressed by its application key |
GET | /applications/{applicationKey}/api-clients | Summary rows of the API clients owned by the application |
GET | /applications/{applicationKey}/limit-assignments | Every limit assignment whose subject is the application |
POST | /applications/{applicationKey}/actions/suspend | Disable and withdraw the application's enabled consumers and re-push its API clients as suspended |
POST | /applications/{applicationKey}/actions/resume | Re-enable exactly the consumers the suspension disabled and re-push the application's API clients |
POST | /applications/{applicationKey}/actions/transfer-organization | Move the application, the consumers it owns, and its subscriptions to another organization of the same project |
POST | /applications | Create an INTERNAL application (201, with Location) |
PUT | /applications/{applicationKey} | Update the application |
DELETE | /applications/{applicationKey} | Delete the application with its access rows |
POST | /applications/{applicationKey}/actions/enable | The same step as actions/resume |
POST | /applications/{applicationKey}/actions/disable | The same step as actions/suspend |
POST | /applications/{applicationKey}/actions/archive | End the application and keep its record (one-way) |
POST | /applications/{applicationKey}/actions/revoke | The same step as actions/archive |
GET | /applications/{applicationKey}/access | The API proxies and API proxy groups the application may call |
POST | /applications/{applicationKey}/access | Grant access to API proxies and API proxy groups |
POST | /applications/{applicationKey}/access/actions/revoke | Revoke access |
PUT | /applications/{applicationKey}/access/api-proxies/{apiProxyName} | Edit the direct grant of one API proxy |
PUT | /applications/{applicationKey}/access/proxy-groups/{proxyGroupName} | Edit the direct grant of one API proxy group |
PUT / DELETE | /applications/{applicationKey}/{material}/{name} (PUT) and /applications/{applicationKey}/{material} (DELETE) | Point the application at a named secret key, certificate, public key, private key, keystore, truststore or JWK, or detach it. {material} is secret-key, certificate, public-key, private-key, keystore, truststore, jwk/validation or jwk/encryption |
GET | /applications/{applicationKey}/secrets | The names and scopes of the secret material the application references |
GET / PUT / DELETE | /applications/{applicationKey}/token-settings | Read, set or reset the token settings |
GET /applications?organizationCode&status&search&page&size&sort sorts by name,asc by default; sort may also name appStatus. status is one of ACTIVE, SUSPENDED, PENDING_DELETION, ARCHIVED. search is a case-insensitive contains-match against the application name.
Consumer suspend, resume and revoke
Three actions change the lifecycle of a consumer. None takes a request body, and each answers 200 with a write response: result is the consumer and runtimeDeployment reports the gateway push.
- Suspend is the same step as
actions/disable. It is reversible: the consumer and the tokens it was issued stay as they are, and traffic passes again after a resume. - Resume is the same step as
actions/enable. - Revoke is one-way. It disables the consumer, withdraws it from the gateways and re-pushes its API clients as revoked, so the tokens issued before the revoke stop working. The record stays and can still be read and deleted. A revoked consumer answers
409 lifecycleConflict(params.reason = consumerRevoked) toactions/enable,actions/resumeandPUT. Repeating the revoke is safe.
A revoked consumer shows "revoked": true on GET /consumers/{consumerKey} and on the list. The field is absent on every other consumer, so a revoked consumer is not mistaken for a suspended one (enabled is false in both cases).
Suspend and resume
Both actions are idempotent: suspending an already-suspended application, or resuming an already-active one, repeats safely and does not require an Idempotency-Key header — unlike the run-creating calls documented elsewhere on the Management API, there is no second run to deduplicate against, only a state to converge on.
- Suspend disables and un-deploys every consumer of the application that is currently enabled, and records exactly those consumers so resume can restore precisely what suspend paused. Every record — subscriptions, consumers, analytics — is kept; no traffic passes until the application is resumed.
- Resume re-enables and re-deploys only the consumers that the matching suspend disabled, so a consumer that was independently revoked in the meantime stays revoked.
Neither action takes a request body.
Both actions answer with the application and, when they pushed anything to the gateways, a runtimeDeployment field:
| Field | Type | Description |
|---|---|---|
runtimeDeployment | object | What the push told the gateways — the shared Write responses shape (state: IN_SYNC | DEGRADED, environments[] with name, acknowledged and detail). It covers the undeploy or redeploy of the application's credentials and the re-push of its API clients; the state is the worst of them, so one failed push makes it DEGRADED. Absent when nothing was pushed (no running environment, no credentials and no clients), and never present on GET responses |
DEGRADED means the new state is stored but at least one gateway did not acknowledge the change. A suspended application can still pass traffic through that gateway until you redeploy, so do not retry the call; check environments and redeploy. A pipeline that depends on the suspension being effective should test runtimeDeployment.state and not only the HTTP status. See API clients for the same field on client writes.
Application writes
The write step is the same one the consumer endpoints call, so a field, a status or a refusal means the same on both. Every write below stores the change, pushes it to the gateways and reports the push; see Write responses.
Body of POST and PUT /applications. A field the body omits (null) keeps its stored value on PUT; send an empty list or empty string to clear one.
| Field | Type | Meaning |
|---|---|---|
name | string | Required on POST; may change on PUT. A blank name is 400 malformedRequest (params.reason = required) |
applicationKey | string | Assigned by the installation. A POST that names one, or a PUT that changes it, is 400 immutableField |
fullName, email, description | string | |
enabled | boolean | false suspends the application, true resumes it. On POST, false creates it suspended |
ipList | array of string | Allowed IP addresses |
expireDate | timestamp | An absent field keeps the expiry; an explicit null removes it |
roleNameList | array of string | Names of the roles of the application |
metadata | array | When sent, replaces the stored list. Secret values are write-only: a secret entry sent without a value is stored empty, so send the value to keep it. A ${var} or #{var} value stays plain and the gateway resolves it. A duplicate or reserved key is 400, as on a consumer |
trafficLogDisabled | boolean | Silences the traffic log connectors for the application's traffic |
organizationCode | string | POST: the organization to file the application under. PUT: must name the current organization; a different code is 400 malformedRequest (params.reason = useTransferAction) |
A new application starts with the client_credentials grant type. POST answers 201 with a Location header and a write response whose result is the application.
Status. enable and disable repeat resume and suspend; PUT with enabled does the same. On an application pending deletion, each of these steps (enable, disable, actions/resume, actions/suspend, PUT with enabled) settles the deletion request: the request is cleared and the application takes the status you asked for. They do not answer 409. The Management API has no endpoint to approve a deletion request.
Archive. actions/archive disables and withdraws the application's credentials, ends its subscriptions and re-pushes its API clients as revoked, so tokens already issued stop working. It is one-way: an archived application takes no later status change or write (409 lifecycleConflict, params.reason = applicationArchived), only a read or a delete. Use it for an application that cannot be deleted because it was promoted. actions/revoke is the same step as actions/archive. It answers with the application and runtimeDeployment, like suspend.
Delete. DELETE removes the application with its access rows and answers a write response. A promoted application is 409 dependencyConflict (params.reason = notDeletable); archive it instead. An application that still has related records is 409 dependencyConflict with the blocker in params.reason.
Application access
Access is addressed by name, with the same accessList body, environments, disallowedEndpoints and disallowedApiProxies as the consumer access endpoints. A target already granted is 409 lifecycleConflict (params.reason = accessAlreadyGranted); targets apply in order, so a refusal leaves the earlier ones granted and a retry sends only the rest. Revoking a target that was not granted leaves it as it is. A PUT edits only a direct grant, not one an API product subscription made; a target with no direct grant is 404 resourceNotFound (params.reason = accessNotGranted).
A portal application's targets may be in any project. Each of its rows therefore carries projectName, and a request may name a target's project with it (default: the path's project). An unknown project is 404 resourceNotFound (params.reason = projectNotFound). Any other application that sends projectName is refused with 400 malformedRequest (params.reason = accessProjectNotApplicable).
Secret material and token settings
Setting a material points the application at the named object of the project (or, when the project has none by that name, of the administration project) and re-pushes its API clients; an unknown name is 404. Removing one detaches the reference; an application that has none is 404 resourceNotFound (params.reason = materialNotAssigned). GET /secrets returns only names and scopes (PROJECT or ADMIN), never the material. PUT /token-settings sets the fields in the body (a null field keeps its value) and keeps the stored authentication type (an application with none gets secret-manager authentication); DELETE restores the defaults (client_credentials grant, tokens never expire, no refresh tokens, RS256). The request and response shapes are the ones of the consumer secret material and token settings pages.
Administration path and portal roles
A portal application belongs to no project, so it is filed under the administration project and addressed on /apiops/projects/admin/.... The roles that pass there are the ones the Manager asks of a portal application:
| Caller | Applications and their API clients on the admin path |
|---|---|
| System Administrator | Every application, INTERNAL and PORTAL |
| Portal Business, Portal Manager | PORTAL applications and their API clients only. An INTERNAL application, or a client it owns, answers 404 resourceNotFound |
| Portal Developer and every other caller | 404 resourceNotFound |
Two things stay with the System Administrator on that path: creating an application, and the access rows of an application (read and write). They follow the Identity permission of the Manager screen, which the portal roles do not extend. Every other read, write and lifecycle action in this section is open to the three roles above.
The API Clients API answers on the same path for the clients of a PORTAL application. The organization actions have their own rule.
Transfer to another organization
POST .../consumers/{consumerKey}/actions/transfer-organization and POST .../applications/{applicationKey}/actions/transfer-organization move the owner to a different organization of the same project, addressed by code:
{ "organizationCode": "acme-partners" }
Both answer 200 with the owner's own view (the same shape GET .../consumers/{consumerKey} or GET .../applications/{applicationKey} returns), reflecting the new organizationCode. Everything the owner needs to keep working follows in the same operation:
- Consumer: its own API clients, its legacy API/API group access control list rows, and the tokens it holds are re-linked, and the consumer is redeployed.
- Application: the consumers it owns (with their own API clients, legacy access control list rows and tokens), the application's own API clients, and its product subscriptions are re-linked, and its API clients are re-pushed.
Unlike suspend/resume, this action is not idempotent — repeating a transfer to the organization the owner already belongs to is refused with 409 lifecycleConflict (reason: transfer.sameOrganization), not treated as a no-op. It is refused the same way, and nothing is changed, when:
- the consumer is managed by directory synchronization (
reason: transfer.syncManaged), or the application is being deleted or is already archived (reason: transfer.applicationTerminal); - a delete of the owner, or of the organization it is leaving or joining, is currently in progress (
reason: deleteInProgressfor the owner,reason: transfer.organizationDeleteInProgressfor either organization); - the target organization has been merged into another one (
reason: transfer.targetOrganizationTombstoned).
A blank or missing organizationCode is refused with 400 malformedRequest before any of the checks above run; one that does not resolve within the project is 404 resourceNotFound, the same as an unknown consumerKey/applicationKey.
If the target organization is suspended, traffic for the owner (and, for an application, the consumers it owns) stops immediately after the transfer succeeds. Resume the target organization to let traffic flow again.
Response
Consumer — 200
| Field | Type | Meaning |
|---|---|---|
consumerKey | string | Opaque public key — the identifier this surface addresses the consumer by |
username | string | The consumer's username. The deprecated Credentials API addresses the same record by it |
fullName | string | Display name |
email | string | |
description | string | |
enabled | boolean | |
revoked | boolean | true once the consumer was revoked with actions/revoke. Absent on every other consumer, also on the list |
organizationCode | string | The consumer's organization, by code — absent when the consumer belongs to none |
expireDate | timestamp | |
trafficLogDisabled | boolean | The consumer's traffic is not written to any traffic log connector. Read here; write it with Create or Update Consumer. Absent on a record where it was never set. |
ipList | array of string | Allowed IP addresses |
roleNames | array of string | Names of the roles of the consumer |
metadata | array | Metadata entries. A secret entry carries its key and flags but no value |
No secret value, certificate or password ever appears here. You write them with the Consumers API. That API also reads the token settings and the names of the referenced secret material.
trafficLogDisabled coversIt silences the traffic log connectors only — Elasticsearch, Kafka, Syslog, Database and the rest. Trace records, OpenTelemetry spans, Prometheus metrics and the security/access events are unaffected: a privacy preference must not be able to erase a security or audit trail. For zero retention, use the no-persist data retention profile instead.
A consumer counts as silenced when either its own flag or its application's flag is on, so a false here does not on its own mean the consumer is being logged — check the owning application too.
Application — 200
| Field | Type | Meaning |
|---|---|---|
applicationKey | string | Opaque public key — the identifier this surface addresses the application by |
name | string | |
referenceId | string | The API Portal's own portable identity of the same record |
applicationKind | enum | PORTAL (an ordinary consumer application) or INTERNAL (an administrator-created, first-party application, invisible on every portal-facing surface) |
appStatus | enum | ACTIVE | SUSPENDED | PENDING_DELETION | ARCHIVED |
description | string | |
organizationCode | string | The application's organization, by code — absent when the application belongs to none |
created | timestamp | |
lifecycleRevision | integer | Monotonic counter, bumped on every operational state change (suspend, resume, deletion submit/cancel/reject, archive) — lets a caller tell whether anything happened since it last read the application |
trafficLogDisabled | boolean | The traffic of every consumer bound to this application is not written to any traffic log connector. Set it with POST or PUT /applications. Same narrow scope as the consumer field above. Absent on a record where it was never set. |
fullName, email | string | |
expireDate | timestamp | |
ipList | array of string | Allowed IP addresses |
roleNames | array of string | Names of the roles of the application |
metadata | array | Metadata entries. A secret entry carries its key and flags but no value |
Writes other than the status actions return the application as result of a write response; suspend, resume, enable, disable, archive and revoke return the application itself with runtimeDeployment.
API client summary (sub-resource) — 200, array
Returned by GET .../api-clients for either owner. Identity and state only — never secret material. See the API Clients API for the full client shape and its lifecycle.
| Field | Type | Meaning |
|---|---|---|
apiClientKey | string | Opaque public key |
clientId | string | The client's OAuth2 client id |
name | string | |
operationalState | enum | DRAFT | ACTIVE | SUSPENDED | REVOKED — whether the operator currently allows this client to be used at all |
readiness | enum | READY | PENDING_SECRET | UNRESOLVED_DEPENDENCY — whether the client actually has what it needs to authenticate |
validFrom / expiresAt | timestamp | Validity window |
version | integer |
Limit assignment (sub-resource) — 200, array
Returned by GET .../limit-assignments for either owner: every limit assignment whose subject is that consumer or application, in the same portable shape the Limit Assignments Management API and the Bulk Limit Assignment API use — see those pages for the full field reference (target shape, rule content, custom message settings) and for Limit Plans for the underlying concepts. The row's subject.type is CONSUMER or APPLICATION and subject.key echoes the owner's own public key; bindingKey is the assignment's own opaque public key. The row also carries the Counted As choice as assignmentMode (SELF is the owner's own total; DESCENDANTS gives each API client beneath the owner, with no assignment of its own, the plan with its own counter) and inheritTo.
List envelope — 200
GET /consumers and GET /applications both return the shared paged envelope:
{
"status": "SUCCESS",
"items": [],
"page": 0,
"size": 20,
"totalElements": 42,
"totalPages": 3,
"sort": ["username,asc"]
}
size defaults to 20 and is capped at 200 (see Common Contract → Pagination).
Errors
Every error follows the Common Contract's error envelope — branch on errorKey, never on message.
| Error key | When |
|---|---|
malformedRequest | The admin (platform) project was addressed on a consumer path (params.reason = platformScopeNotSupported), a malformed sort value was sent, a transfer's organizationCode was blank or missing, or an application write was malformed (params.reason such as required, bodyRequired, useTransferAction, accessListRequired, accessProjectNotApplicable) |
immutableField | An application write tried to set or change applicationKey |
resourceNotFound | An unknown consumerKey / applicationKey, an unknown organizationCode (as a list filter, a create target or a transfer target), or an unknown access target, secret material, role or project (params.reason = projectNotFound); also a removal with nothing to remove (materialNotAssigned) or an edit of a target with no direct grant (accessNotGranted) |
unsupportedSortField | sort named a field this surface does not sort on |
lifecycleConflict | A transfer was refused: params.reason is one of transfer.syncManaged, transfer.applicationTerminal, transfer.sameOrganization, transfer.targetOrganizationTombstoned, transfer.organizationDeleteInProgress, or deleteInProgress (a delete of the owner itself is in progress) — see Transfer to another organization above |
lifecycleConflict (application writes) | params.reason is applicationArchived (the application is archived) or accessAlreadyGranted |
lifecycleConflict (consumer writes) | params.reason is consumerRevoked: the consumer was revoked, and actions/enable, actions/resume and PUT refuse it |
dependencyConflict | An application delete was refused: notDeletable for a promoted application, otherwise the blocker in params.reason |
permissionDenied | Authenticated, but the caller lacks the Identity permission at View (reads) or Manage (every write) |
See Common Contract → Error keys for the complete closed set and the status-code table.
Permissions & Licensing
- Reads (
GET) require the Identity permission at View; every write on this page — the application writes, the lifecycle actions and the transfer action on either owner — requires it at Manage. - No license module is required for anything on this page.
- Consumers are project-scoped: the admin (platform) project is refused with
400 malformedRequest(params.reason = platformScopeNotSupported). Applications also answer on the admin project's path, where a System Administrator reaches the administration project's applications and everyPORTALapplication, and Portal Business and Portal Manager reachPORTALapplications; any other caller gets404 resourceNotFoundthere. Creating an application and the access rows stay with the System Administrator on that path. See Administration path and portal roles. - A project outside the caller's authorized scope is always
404 resourceNotFound, never403— see Common Contract → Telling 402, 403 and 404 apart.
Example
List a project's enabled consumers in one organization, read one consumer's limit assignments, suspend, resume, create and archive applications, then move a consumer to another organization:
# 1. List enabled consumers in an organization, sorted by full name
curl "https://apinizer.example.com/apiops/projects/finance/consumers?organizationCode=acme-partners&enabledOnly=true&sort=fullName,asc" \
-H "Authorization: Bearer $TOKEN"
# 2. One consumer's limit assignments
curl "https://apinizer.example.com/apiops/projects/finance/consumers/con_8f3a1c/limit-assignments" \
-H "Authorization: Bearer $TOKEN"
# 3. List applications pending deletion review
curl "https://apinizer.example.com/apiops/projects/finance/applications?status=PENDING_DELETION" \
-H "Authorization: Bearer $TOKEN"
# 4. Suspend an application — idempotent, no Idempotency-Key needed
curl -X POST "https://apinizer.example.com/apiops/projects/finance/applications/app_2d7e9b/actions/suspend" \
-H "Authorization: Bearer $TOKEN"
# 5. Resume it once the review is done
curl -X POST "https://apinizer.example.com/apiops/projects/finance/applications/app_2d7e9b/actions/resume" \
-H "Authorization: Bearer $TOKEN"
# 6. Create an application, then archive it when it can no longer be deleted
curl -X POST "https://apinizer.example.com/apiops/projects/finance/applications" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "name": "Partner portal", "organizationCode": "acme-partners" }'
curl -X POST "https://apinizer.example.com/apiops/projects/finance/applications/app_2d7e9b/actions/archive" \
-H "Authorization: Bearer $TOKEN"
# 7. Revoke a consumer for good (one-way)
curl -X POST "https://apinizer.example.com/apiops/projects/finance/consumers/con_8f3a1c/actions/revoke" \
-H "Authorization: Bearer $TOKEN"
# 8. Move a consumer to another organization of the same project
curl -X POST "https://apinizer.example.com/apiops/projects/finance/consumers/con_8f3a1c/actions/transfer-organization" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "organizationCode": "acme-partners" }'