Ana içeriğe geç

Common Contract

Scope

Newer Management API endpoints share one contract. Whatever the resource is, the shape of an error, the way a page is requested, the way a write reports what happened to the running gateways, and the caching rules are the same. This page is that contract; the individual endpoint pages describe only what is specific to them.

Note

Endpoints that existed before this contract keep their original status / resultMessage envelope, described in Error Handling. Both styles are live at the same time. You can tell them apart from the response body: a response that carries an errorKey follows the contract on this page.

Base path and naming

/apiops/projects/{projectName}/{collection}

Every address is scoped to a project, and the project name is part of the path — never a query parameter and never a body field. Collection segments are lower case with hyphens (api-proxies, ip-groups).

Collection URLs accept GET (list) and POST (create). PUT, PATCH and DELETE always address a single item and therefore carry the item key in the path. A DELETE never takes a request body: what is being deleted is always readable from the URL alone.

Objects are addressed by the key you already know them by — a name, a code, or a public identifier. Internal database identifiers are not part of the contract and are not returned.

Authentication

These endpoints accept only a personal API access token, as described in Authentication.

Warning

A Manager session token obtained by signing in to the Manager user interface is not an API access token and is rejected with 401 and the error key invalidToken, even while it is still valid for the user interface. Create a personal API access token for automation.

Correlation ID

Every request is given a correlation identifier, carried in this header on both the request and the response:

APINIZER-CORRELATION-ID: 7f1c9e2a-8f43-4d2e-9a11-6b5d0c3e2f77

Send your own value to trace a call end to end through your pipeline logs; if you do not send one, Apinizer generates it. The same value appears in the response header, in the correlationId field of an error body, and in the Apinizer server logs for that request. Quote it in a support request — it is the fastest way to find the exact call.

The value is sanitized before use: only letters, digits and the characters . _ : - are kept, and it is truncated at 64 characters.

Note

This header name is fixed for the Management API. The correlation header name configurable under gateway settings applies to API traffic passing through the gateway, not to Management API calls.

Error response

Every error returns the same envelope:

{
"status": "FAILURE",
"errorKey": "resourceNotFound",
"message": "error.resourceNotFound",
"params": {
"project": "finance"
},
"correlationId": "7f1c9e2a-8f43-4d2e-9a11-6b5d0c3e2f77"
}
FieldTypeDescription
statusstringAlways FAILURE on error
errorKeystringStable, machine-readable reason. Branch on this.
messagestringTranslation key for the reason, always error. followed by the error key
paramsobjectValues that belong to this specific failure, such as the project name or the list of supported sort fields. Present only when the failure has them. On a malformedRequest, lifecycleConflict or dependencyConflict from one of the domain surfaces below, params.reason narrows the cause — see Domain reason keys.
correlationIdstringThe identifier for this request

Empty fields are omitted from the body.

Warning

Branch on errorKey, never on the text. The message field is a translation key, not a sentence, and the human-readable wording behind it may change between versions. Error keys come from a closed list and do not change.

Error bodies never contain a stack trace, an exception class name, or any text taken from an internal failure. They are also sent with Cache-Control: no-store.

Error keys

400 Bad Request

Error keyMeaning
malformedRequestThe request could not be understood: a malformed body, an illegal value, or a missing required field
immutableFieldA field that can only be set when the object is created was changed
unsupportedSortFieldThe sort parameter named a field this endpoint does not sort on. params lists the supported fields.
projectNameRequiredThe project name was empty
idempotencyKeyRequiredThe endpoint requires an Idempotency-Key header and it was missing or blank

401 Unauthorized

Error keyMeaning
invalidTokenThe API access token is missing, unknown, expired — or a Manager session token was offered in its place

402 Payment Required

Error keyMeaning
licenseInvalidOrExpiredNo valid Apinizer license is installed, or the installed license has expired

403 Forbidden

Error keyMeaning
permissionDeniedAuthenticated, but the user lacks the permission this operation requires
licenseModuleNotEnabledThe license is valid, but it does not include the module this operation belongs to
managementApiDisabledThe Management API is switched off for this installation

404 Not Found

Error keyMeaning
resourceNotFoundThe addressed object is not visible to this caller

409 Conflict

Error keyMeaning
duplicateNameAnother object in this project already uses this name or code
versionConflictThe object changed after you read it — see Optimistic concurrency
lifecycleConflictThe object's current state does not allow this operation
dependencyConflictOther objects still depend on this one
idempotencyKeyReusedThis Idempotency-Key was already used with a different request
idempotencyReplayExpiredThis exact request already completed; its response is not replayed
idempotencyInProgressA request with this key is still running

503 Service Unavailable

Error keyMeaning
temporarilyUnavailableA dependency the operation needs is momentarily unavailable. The call may be retried.

Domain reason keys

malformedRequest, lifecycleConflict and dependencyConflict are deliberately generic — many resources share them. Where a caller needs to tell causes apart, the response carries an additional params.reason, a stable key of its own that is never translated and never changes shape. Branch on errorKey first; narrow on params.reason only when it is present.

A reason key that carries a dotted prefix (transfer., purge., legalHold., limit.bulk.) names the subsystem that raised it; a small number of reasons, such as platformScopeNotSupported, are shared across surfaces and carry no prefix. The full narrative for each — which request shape triggers it, what to do about it — lives on the endpoint page linked below; this table is the index.

transfer.*Import/Export Transfer

Error keyMeaning
transfer.exportDigestMismatchThe export selection resolves differently than it did when expectedExportDigest was captured. Returned as 409 dependencyConflict.
transfer.preflightDriftThe target project changed since the preflight this import's options.preflightDigest was taken from. Returned as 409 dependencyConflict.
transfer.formatVersionUnsupportedThe package's formatVersion is not 1. Returned as 400 malformedRequest.
Error keyMeaning
purge.legalHoldThe subject has an active legal hold; preview, approve and execute all refuse while it stands. Returned as 409 lifecycleConflict.
purge.activeRunExistsAnother non-terminal purge run already targets this subject. Returned as 409 lifecycleConflict.
purge.deleteInProgressA delete of this subject is already in progress through another surface. Returned as 409 lifecycleConflict.
purge.subjectNotFoundThe subject reference on a preview does not resolve, or its type/id was left blank. Returned as 400 malformedRequest.
purge.modeNotApplicableReturned as 400 malformedRequest when mode itself is not one of the four supported values, and as 409 lifecycleConflict when the mode is valid but does not apply to the subject's current state (for example CRYPTO_SHRED on a subject with nothing to shred).
purge.correctionNotEligibleCORRECTION_PURGE was requested on a subject that is not eligible for one; params also carries the blockers. Returned as 409 lifecycleConflict.
purge.retentionNotReachedRETENTION_PURGE was requested before the subject's retention age was reached. Returned as 409 lifecycleConflict.
purge.digestMismatchThe digest submitted to approve does not match the run's current manifest. Returned as 409 lifecycleConflict.
purge.sameActorThe approver is the same user who ran the preview (waived for a system administrator). Returned as 409 lifecycleConflict.
purge.invalidStateThe run is not in the state this action requires (approve, execute, or cancel called out of order). Returned as 409 lifecycleConflict.
purge.cannotCancelAfterCommitThe run has already passed its point of no return. Returned as 409 lifecycleConflict.
Error keyMeaning
legalHold.subjectRequiredA hold was placed with no subject type/id. Returned as 400 malformedRequest.
legalHold.reasonRequiredA hold was placed, or released, with no reason. Returned as 400 malformedRequest.
legalHold.alreadyHeldThe subject already has an active legal hold. Returned as 409 lifecycleConflict.
legalHold.notActiveThe hold being released is not currently active. Returned as 409 lifecycleConflict.

limit.bulk.*Limit Assignment Runs

Error keyMeaning
limit.bulk.selectorRequiredThe request carries no subject selector. Returned as 400 malformedRequest.
limit.bulk.subjectTypeNotSupportedThe selector's subject type is not one of consumer, API client, application or organization. Returned as 400 malformedRequest.
limit.bulk.contentRequiredThe request carries no limit content (family, target and mode) to assign. Returned as 400 malformedRequest.
limit.bulk.conflictPolicyInvalidconflictPolicy is not KEEP_EXISTING or REPLACE. Returned as 400 malformedRequest.
limit.bulk.includeListTooLargeAn include/exclude id list on the selector exceeds 5,000 entries. Returned as 400 malformedRequest.
limit.bulk.fingerprintMismatchThe selected population changed since the preview previewFingerprint was taken from. Returned as 409 lifecycleConflict.
limit.bulk.runAlreadyActiveAnother bulk assignment run is already in progress in this project. Returned as 409 lifecycleConflict.
limit.bulk.runNotCancellableThe run is no longer running and cannot be cancelled. Returned as 409 lifecycleConflict.
limit.bulk.runNotRollbackableThe run has nothing to roll back (still live, already a rollback, or it assigned nothing). Returned as 409 lifecycleConflict.
limit.bulk.runNotRetryableThe run has no failed or license-skipped subject to retry, or is still live. Returned as 409 lifecycleConflict.

Unprefixed, shared across surfaces

Error keyMeaning
platformScopeNotSupportedThe admin (platform) project was addressed on a surface whose subjects are always project-owned — legal holds, lifecycle purge, and bulk limit assignment runs. Returned as 400 malformedRequest.
Note

This index covers the reason keys the domain services above define as their own stable vocabulary. A malformedRequest can also carry a handful of endpoint-local, unprefixed reasons for shape errors that are specific to one request body (a missing names list, an unrecognized type) — those are documented on the endpoint page that returns them, not repeated here.

Status codes

StatusWhen
200 OKThe request succeeded
201 CreatedThe object was created
202 AcceptedThe request was accepted and completes asynchronously; the Location header points at the result
400 Bad RequestThe request itself is wrong
401 UnauthorizedThe token is missing, invalid, expired, or the wrong kind of token
402 Payment RequiredThere is no valid license at all
403 ForbiddenLicensed and authenticated, but not allowed
404 Not FoundNothing visible to this caller at that address
409 ConflictThe request is valid, but the current state refuses it
503 Service UnavailableA temporary failure; retrying is reasonable

Telling 402, 403 and 404 apart

These three are the cases automation most often misreads, so they are deliberately distinct:

SituationStatusError keyWhat to do
No license installed, or it expired402licenseInvalidOrExpiredContact your Apinizer representative — no retry will help
License valid, module not included403licenseModuleNotEnabledAsk for that module to be added to the license
Management API switched off403managementApiDisabledAsk an administrator to enable the Management API
User lacks the permission403permissionDeniedGrant the permission to the user the token belongs to
Object absent, or outside this user's scope404resourceNotFoundCheck the name and the user's project access
Note

404 is returned both when nothing exists at that address and when something exists but this caller may not see it. The two are answered identically, on purpose: distinguishing them would tell an unauthorized caller what exists. A 404 is therefore not proof that an object is absent — it means "not visible to you".

Idempotency

Endpoints that create something, or that produce credentials, require an idempotency key:

Idempotency-Key: 9d0c8f4a-2f1e-4c7b-9c2f-8b3a1d5e7c40

Use a fresh value for each distinct operation, at most 255 characters. A retry of the same operation — a network timeout, a pipeline re-run — reuses the same key, and Apinizer will not perform the work twice.

The key is matched together with a fingerprint of the request, so:

SituationResult
New keyThe operation runs
Same key, same request, first call still running409 idempotencyInProgress — wait, then read the object
Same key, same request, first call finished409 idempotencyReplayExpired — the work was already done; read the object instead
Same key, different request409 idempotencyKeyReused — use a new key
Header missing400 idempotencyKeyRequired
Warning

The original response is never replayed. A completed request returns 409 idempotencyReplayExpired, not the first response — so a secret that was generated once is never handed out a second time. Store what you receive from the first successful call; it cannot be retrieved again.

Keys are remembered for 24 hours.

Optimistic concurrency

Objects that support concurrent editing report a resourceVersion when you read them and when you change them. Send it back on the next write to make sure you are changing the version you actually read:

If-Match: 7

The body field expectedVersion does the same thing when a header is inconvenient. If the object changed in the meantime, the write is refused with 409 versionConflict; read the object again, re-apply your change, and retry.

Omitting both means "last write wins".

Pagination

List endpoints are always paged.

ParameterDefaultDescription
page0Zero-based page index
size20Rows per page, capped at 200
sortendpoint-specificfield,asc or field,desc

A size above the cap is reduced to 200 rather than rejected. A negative page, or a sort that is malformed, is rejected with 400 malformedRequest; a sort that names a field the endpoint does not support is rejected with 400 unsupportedSortField, and params then carries the supported field names. Field names are case-sensitive.

A stable secondary sort is always appended, so a row never moves between pages while you are walking them.

{
"status": "SUCCESS",
"items": [],
"page": 0,
"size": 20,
"totalElements": 137,
"totalPages": 7,
"sort": ["createdAt,desc", "name,asc"]
}

Write responses

A successful write reports both what was stored and what reached the running gateways:

{
"status": "SUCCESS",
"resourceVersion": 8,
"result": {},
"runtimeDeployment": {
"state": "DEGRADED",
"revision": 42,
"environments": [
{ "name": "production", "acknowledged": true },
{ "name": "test", "acknowledged": false, "detail": "gateway did not respond" }
]
}
}
FieldDescription
statusAlways SUCCESS — this envelope is only used for a successful write
resourceVersionThe new version, to send back as If-Match on your next write
resultThe stored object
runtimeDeploymentWhat happened to the running gateways, when the write triggers a deployment

runtimeDeployment.state is one of:

StateMeaning
IN_SYNCEvery environment acknowledged the change
DEGRADEDStored, but at least one environment did not acknowledge it. Read environments to see which.
PENDINGDelivery is still in progress
Warning

DEGRADED arrives with a success status, and that is intentional: the change is saved. Treating it as a failure and retrying the write will not help. Check environments, resolve the unreachable environment, and redeploy. A pipeline that must not continue on a partial rollout should test runtimeDeployment.state explicitly rather than only the HTTP status.

Caching

Error responses, responses that contain credentials, and downloads of raw key or certificate material are sent with:

Cache-Control: no-store
Pragma: no-cache

Make sure any proxy or client cache between your automation and Apinizer honours these.

Next Steps