Credentials API
The operations of this section are deprecated. They still work. Use the Consumers API instead.
Every response carries a Deprecation: @1792368000 header and a Link header with rel="successor-version".
Endpoints
CRUD Operations
- List Credentials - Get all consumers for a project
- Create Credential - Create a new consumer
- Update Credential - Update an existing consumer
- Change Credential Password - Change consumer password
- Delete Credential - Delete a consumer
Credential Organizations
- Credential Organizations - Manage organizational groupings of consumers
- Create Credential Organization - Create a new organization
- Get Credential Organization - Retrieve an organization by code
- Update Credential Organization - Update an organization
- Delete Credential Organization - Delete an organization
Access Management
- Get Granted Access List - Get list of API Proxies/Groups granted to consumer
- Grant Access - Grant access to API Proxy or API Proxy Group
- Revoke Access - Revoke access from API Proxy or API Proxy Group
Secrets Management
- Secrets Management - Manage secret keys, certificates, keys, keystores, and truststores for consumers
JWK Settings
- JWK Settings - Manage JWK settings for validation, signing, decryption, and encryption
Token Settings
- Token Settings - Configure token expiration, refresh tokens, grant types, and JWT signature algorithms
Credential Identity Fields
A consumer is identified by two values, and only one of them takes part in the payload of these endpoints.
| Field | In the payload | Description |
|---|---|---|
username | Yes, writable | Login and display identity. Unique across ALL projects of the installation, not only the target project. It may be changed after creation. |
consumerKey | No | Permanent, URL-safe public identity assigned by the server when the consumer is created. Unique across the installation, never editable, and never carried over to another record: a duplicated consumer, or one brought in with Import as New, is assigned a new key. It does not change when username changes. |
consumerKey is stored on the consumer, but it is not part of the request or response body of the endpoints on this page in this version: it can neither be set nor read through them, and a value sent under that name is ignored. Keep using username to address a consumer here.
The Consumers API addresses a consumer by its consumerKey and returns it, read-only. The endpoints on this page do not carry the field; keep using username here.
API Client Fields
A consumer (and an application) can own API clients — machine identities with their own client id and versioned client secrets, issued from /credential/token and /credential/jwt. They are not managed through the endpoints on this page. Create, read, edit and run the lifecycle of a client (suspend, resume, revoke) and its secret generations on the API Clients Management API; its issuance settings (grant types, token lifetime, refresh, signature algorithm) are fields of that API. Read the clients of one consumer with GET /consumers/{consumerKey}/api-clients (Consumers & Applications).
One consequence reaches this API: deleting a consumer that still owns API clients not yet revoked is refused with an HTTP 409 response — revoke the clients first, on the API Clients Management API or from the manager interface.
API Client Secret Migration (Manager REST API)
This endpoint lives on the manager's own REST surface (/api/api-clients/**), not on /apiops/**. It gives an API client created by the legacy-record migration the exact secret its legacy consumer already uses, so the same client id and password keep authenticating without a forced reset. The startup migration runs this same carry-over itself, installation-wide, the first time the Manager starts on this version (see Automatic Migration at Upgrade); this endpoint is its scripted form, for re-running the step against one project after the fact. This section covers only the request and response shape.
POST /api/api-clients/migration/secrets/carry-over
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
projectId | string | (none) | Scopes the run to one project. Omitted or blank runs over the whole installation and is refused unless the caller has system administrator permission. |
apply | boolean | false | false classifies every candidate and writes nothing. true creates a secret for each eligible client. |
activate | boolean | false | Only takes effect together with apply=true. Additionally moves a Draft client whose secret was just carried — or that already held one — to Active. Ignored on a dry run. |
Response Shape
{
"apply": true,
"activate": false,
"recoverySupported": true,
"scanned": 42,
"counts": { "CARRIED": 38, "ALREADY_HAS_MATERIAL": 2, "SKIPPED_BLANK_SECRET": 2 },
"activatedCount": 0,
"rows": [
{
"apiClientKey": "...",
"clientId": "legacy-service-1",
"projectId": "...",
"migratedFromCredentialId": "...",
"operationalState": "DRAFT",
"outcome": "CARRIED",
"detail": "A secret generation was derived from the legacy password; the legacy row is unchanged.",
"secretVersionId": "...",
"recoveryState": "AVAILABLE",
"activated": false
}
]
}
| Field | Description |
|---|---|
apply / activate | Echo the request's own switches. activate reads back false whenever apply was false, regardless of what was sent, since nothing is activated on a dry run. |
recoverySupported | Whether the installation holds a recovery key (KEK) for sealing a recovery envelope. It does not determine whether a carried-over secret can be revealed — see the note below. |
scanned | How many candidate clients — those created by the legacy migration — were evaluated. |
counts | Number of rows for each outcome value present in this run (see the table below); an outcome with zero rows is omitted. |
activatedCount | How many clients this run itself moved from Draft to Active. |
rows | One entry per candidate client. operationalState reflects the client's lifecycle state as observed at the start of this row's evaluation, not after; activated is the field that tells you whether this run additionally activated it. secretVersionId and recoveryState are present when outcome is CARRIED, CARRIED_RAW, or CARRIED_REFERENCE. |
Outcome Values
outcome | Meaning |
|---|---|
WOULD_CARRY | Dry run only: a secret would be created from the legacy password. |
CARRIED | A secret was created from the legacy password and deployed; the legacy consumer is unchanged. |
CARRIED_RAW | The stored password was not a ciphertext of this installation's key — plain text, or sealed under a different installation's key — so it was carried over as-is, byte for byte, matching what the legacy runtime itself expected. Rotating the client's secret afterward is recommended. |
CARRIED_REFERENCE | The legacy password was a variable or expression reference (a value starting with ${ or #{); the reference itself is carried as a secret-reference generation and is resolved by the gateway on every request, exactly as it was resolved on the legacy password field. |
ALREADY_HAS_MATERIAL | The client already has a secret of its own; running again changes nothing. |
SOURCE_MISSING | The legacy consumer this client was created from no longer exists. |
SKIPPED_EXTERNAL_IDENTITY_SOURCE | The legacy consumer authenticates through LDAP, a database, an API, or an external identity provider rather than a locally stored password — there is no password to carry. |
SKIPPED_BLANK_SECRET | The legacy consumer has no password stored, or it is empty once read. |
SKIPPED_REVOKED | The client has been revoked and takes no new material. |
FAILED_DECRYPT | The stored password looked like a ciphertext of this installation's key but decrypted to nothing — a corrupt value; nothing is carried for this row. |
FAILED_ISSUE | The secret could not be created — for example a concurrent change to the client — and nothing was written for this row. |
SKIPPED_REFERENCE_NOT_SUPPORTED is no longer produced: a variable or expression password is now carried over as CARRIED_REFERENCE instead of being left on the legacy path.
A carried-over secret is always stored in Encrypted mode, so recoveryState reads AVAILABLE for every CARRIED row regardless of recoverySupported — a platform-encrypted copy is kept even without a recovery key. recoverySupported only reports whether a recovery key is additionally available to seal the envelope Reveal prefers. A CARRIED_REFERENCE row is different: there is no local secret material to recover — the value is resolved by the gateway at request time — so its recoveryState reads UNAVAILABLE, since the material belongs to whatever resolves the reference, not to this installation.
Permissions for This Endpoint
isSystemAdmin() or identity management manage permission in the target project, for both the dry run and the real carry-over — a dry run is authorized as the write it could become — and an installation-wide run (blank projectId) always requires system administrator permission. The sibling GET /api/api-clients/migration/inventory (what each legacy record would become) needs identity management view permission; POST /api/api-clients/migration/backfill?projectId=&apply= (derive the API clients; apply defaults to false = dry run) follows the same manage rule as this endpoint.
Portal API Clients (Portal Management API)
Most of this page documents /apiops/** (and the Manager REST section just above, under /api/api-clients/**). The five endpoints below are a third surface, /apiportal/management/** — the base path the API Portal's own backend calls on behalf of a signed-in developer to run the API Clients tab of an application — see Application Promote → API Clients for the portal-facing behavior this documents the wire shape of. For automation outside the portal, use the API Clients Management API under /apiops/**.
An application can own one or more typed API clients — see API Client Fields above for the entity these endpoints operate on, scoped here to one portal application (ownerType=APPLICATION). Every endpoint below is scoped to one appId, and a apiClientKey that exists but is not owned by that application answers the same "not found" error as a key that does not exist at all — this surface never confirms or denies whether a key belongs to a different application.
Endpoints
GET /apiportal/management/portal/api-clients/get-by-app-id/{appId}— list the application's api clients (revoked ones included, as history), without secretsPOST /apiportal/management/portal/api-clients?appId={appId}— create a new api client, body{"name": "..."}POST /apiportal/management/portal/api-clients/{apiClientKey}/rotate?appId={appId}— issue a new secret for an existing clientPOST /apiportal/management/portal/api-clients/{apiClientKey}/suspend?appId={appId}&reason={reason}— suspend a client (reasonoptional)POST /apiportal/management/portal/api-clients/{apiClientKey}/resume?appId={appId}— resume a suspended client
List — Response Shape
GET .../get-by-app-id/{appId} returns an array of:
[
{
"apiClientKey": "...",
"clientId": "...",
"name": "Billing integration",
"operationalState": "ACTIVE",
"readiness": "READY",
"migratedFromLegacy": false,
"version": 3,
"secretVersions": [
{
"versionId": "...",
"label": "Portal",
"status": "ACTIVE",
"createdAt": "2026-08-01T10:00:00Z",
"validFrom": "2026-08-01T10:00:00Z",
"expiresAt": null
}
]
}
]
| Field | Description |
|---|---|
apiClientKey | The client's management-reference key. Used to address it in the rotate/suspend/resume paths above. |
clientId | The OAuth client_id presented at authentication. For a client created by the legacy-record migration, this is the migrated consumer's old username. |
operationalState | DRAFT, ACTIVE, SUSPENDED, or REVOKED — whether the client is currently allowed to authenticate. A client this surface creates is always born ACTIVE; DRAFT is only seen on a client the legacy-record migration created but has not yet been activated. |
readiness | READY, PENDING_SECRET, or UNRESOLVED_DEPENDENCY — whether the client actually has usable secret material, independent of operationalState. |
migratedFromLegacy | true when this client was derived from the application's older, single-key credential by the legacy-record migration. |
secretVersions | Every secret generation's calendar (version id, label, status, created/valid-from/expires timestamps) — never the secret value itself. What a generation's status reads while a rotation's previous secret is still inside its grace window is covered in Grace Period and Scheduled Rotation; it is not repeated here. |
Never present in this DTO: a secret value, a verifier, a recovery envelope, or a trailing hint of any kind — this is the deliberate difference from the manager's own ApiClientDTO. The only response that ever carries a secret value is the create/rotate response below, and only once.
Create and Rotate — Response Shape
Both POST .../api-clients?appId=... (create) and POST .../api-clients/{apiClientKey}/rotate?appId=... (rotate) answer with:
{
"client": { "...": "the PortalApiClientDTO shape above, after the write" },
"secretVersionId": "...",
"clientSecretOnce": "the plaintext secret — present in this response only"
}
clientSecretOnce is the only place this API ever returns a secret value; neither the list endpoint above nor any other call echoes it again. It is never logged and never appears in an audit trail. Both responses are served with Cache-Control: no-store and Pragma: no-cache.
On rotate, the previous secret generation keeps authenticating through its grace window rather than stopping immediately — see Suspending, Resuming and Revoking an API Client for the underlying mechanism this reuses.
Suspend and Resume — Response Shape
Both answer with the client's PortalApiClientDTO (the list-response shape above) as it now stands, after the transition. reason on suspend is optional; when omitted, a default reason is stored. Suspend and resume follow the same operationalState transition rules as every other api client — see API Client Fields above — so, for example, resuming a client that is not currently SUSPENDED is refused.
Errors
| Condition | Response |
|---|---|
appId does not resolve to an application | 400, error key portal.apiClient.applicationNotFound |
apiClientKey does not resolve to a client owned by that application (including a key that belongs to a different application) | 400, error key portal.apiClient.notFoundInApplication |
Create with a blank or missing name | 400, error key portal.apiClient.nameRequired |
Authentication and Permissions for This Section
These endpoints authenticate the same way as the rest of this page: a Personal API Access Token, sent as the Authorization header value — see Authentication below. What is different here is whose token is accepted and what else is sent alongside it: the token's owner must hold a portal role (or be a system administrator), and the caller — the Portal backend, never the developer's own portal session — also sends the acting portal's id in the X-Portal-Id header; see API Portal Settings → Security for the Management API Key Users list a Manager installation can use to further restrict which keys are accepted for a given portal. Authorization for which application and client a given call may touch is enforced one layer up, by the Portal backend itself, before it ever reaches this API — see Application Promote → API Clients.
That outer check is not the same for every one of the five endpoints: listing an application's clients (GET .../get-by-app-id/{appId}) only requires the caller's signed-in developer to be a member of the application's organization, any role included. Creating, rotating, suspending or resuming a client additionally requires a write role in that organization — Owner or Developer — so a Viewer's session is refused by the Portal backend before it ever issues the corresponding request to this API.
Authentication
All endpoints require authentication using a Personal API Access Token.
Permissions
GET Operations (List Credentials, Get Granted Access List)
- User must have
IDENTITY+VIEWpermission in the project - If the asset category does not exist, user must have at least one permission in the project
POST/PUT/DELETE Operations (Create/Update/Delete Credential, Grant/Revoke Access)
- User must have
IDENTITY+MANAGEpermission in the project - For deployment operations (when granting/revoking access with deploy), user must also have
IDENTITY+DEPLOY_UNDEPLOYpermission
Permission Requirements
| Operation | Required Permission |
|---|---|
| List Credentials, Get Granted Access List | IDENTITY + VIEW (or any permission if category doesn't exist) |
| Create/Update/Delete Credential | IDENTITY + MANAGE |
| Grant/Revoke Access (with deploy) | IDENTITY + DEPLOY_UNDEPLOY |
Related Documentation
- Authentication Guide - How to obtain and use API tokens
- Error Handling - Error response formats