Ana içeriğe geç

Credentials API

Deprecated

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​

Credential Organizations​

Access Management​

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.

FieldIn the payloadDescription
usernameYes, writableLogin and display identity. Unique across ALL projects of the installation, not only the target project. It may be changed after creation.
consumerKeyNoPermanent, 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.

Note

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​

ParameterTypeDefaultDescription
projectIdstring(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.
applybooleanfalsefalse classifies every candidate and writes nothing. true creates a secret for each eligible client.
activatebooleanfalseOnly 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
}
]
}
FieldDescription
apply / activateEcho 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.
recoverySupportedWhether 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.
scannedHow many candidate clients — those created by the legacy migration — were evaluated.
countsNumber of rows for each outcome value present in this run (see the table below); an outcome with zero rows is omitted.
activatedCountHow many clients this run itself moved from Draft to Active.
rowsOne 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​

outcomeMeaning
WOULD_CARRYDry run only: a secret would be created from the legacy password.
CARRIEDA secret was created from the legacy password and deployed; the legacy consumer is unchanged.
CARRIED_RAWThe 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_REFERENCEThe 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_MATERIALThe client already has a secret of its own; running again changes nothing.
SOURCE_MISSINGThe legacy consumer this client was created from no longer exists.
SKIPPED_EXTERNAL_IDENTITY_SOURCEThe 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_SECRETThe legacy consumer has no password stored, or it is empty once read.
SKIPPED_REVOKEDThe client has been revoked and takes no new material.
FAILED_DECRYPTThe 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_ISSUEThe 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.

Note

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)​

This is the Portal Management API, not APIops

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 secrets
  • POST /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 client
  • POST /apiportal/management/portal/api-clients/{apiClientKey}/suspend?appId={appId}&reason={reason} — suspend a client (reason optional)
  • 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
}
]
}
]
FieldDescription
apiClientKeyThe client's management-reference key. Used to address it in the rotate/suspend/resume paths above.
clientIdThe OAuth client_id presented at authentication. For a client created by the legacy-record migration, this is the migrated consumer's old username.
operationalStateDRAFT, 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.
readinessREADY, PENDING_SECRET, or UNRESOLVED_DEPENDENCY — whether the client actually has usable secret material, independent of operationalState.
migratedFromLegacytrue when this client was derived from the application's older, single-key credential by the legacy-record migration.
secretVersionsEvery 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​

ConditionResponse
appId does not resolve to an application400, 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 name400, 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 + VIEW permission 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 + MANAGE permission in the project
  • For deployment operations (when granting/revoking access with deploy), user must also have IDENTITY + DEPLOY_UNDEPLOY permission

Permission Requirements​

OperationRequired Permission
List Credentials, Get Granted Access ListIDENTITY + VIEW (or any permission if category doesn't exist)
Create/Update/Delete CredentialIDENTITY + MANAGE
Grant/Revoke Access (with deploy)IDENTITY + DEPLOY_UNDEPLOY