Docs

Revoke a release

POST
/releases/{release_id}/revoke

Marks the release revoked, so that its content is never served again. From then on the release cannot be deployed and is refused on every path that would serve it, including to a build that pins its digest, with rel.revoked.

A release that any origin still serves cannot be revoked. An origin serves the release of the newest deployment to it, so the call answers 409 with rel.in_use, naming each such origin and that deployment. Deploy another release there or roll back first, then revoke. Revoking therefore never takes an origin down. Deployments that named the release earlier stay in the history.

Idempotent: revoking a release that is already revoked answers 200 with the release unchanged, revoked_at still naming the first revocation. There is no un-revoke; build a new release instead.

Takes no body. The lookup is scoped to the project in project_id, so a release id of another project answers not found exactly as an unknown id does.

Authorization

headerAuthorizationBearer <token>

Scope: release.write

Path Parameters

release_id*string

The unique identifier of the release.

Match^rel_[a-zA-Z0-9_-]+$

Query Parameters

project_id*string

The unique identifier of the project

Match^[a-zA-Z0-9_-]+$

Response Body

The release is revoked.

application/json
  1. response

An immutable, project-scoped snapshot that pins one revision of every resource it includes.

A release owns pointers and metadata, not content — it records which revisions belong together. Callers that need the resource bytes resolve each revision_id through the per-kind read endpoints (GET /schemas/{id}, GET /flow_definitions/{id}, …).

A release exists on its own and is not tied to an origin. Deploying it is a separate call.

Two identifiers: id is the one paths and release_id fields take; content_hash is the digest of the content the release pins, printed by the CLI and pinned by builds, and looked up through fields that name it.

id*release-id

The opaque, immutable resource id, assigned at construction.

Match^rel_[a-zA-Z0-9_-]+$
project_id*project-id

The project this release belongs to.

Match^[a-zA-Z0-9_-]+$
content_hash*ReleaseContentHash

The digest of the pinned content. Identical for two releases with the same content, which is why a project never holds two such releases.

Match^sha256:[0-9a-f]{64}$
Length71 <= length <= 71
metadata*

Who assembled the release, when, and from what source. Set when the release is assembled and never mutated afterwards.

revoked_at*|

When the release was revoked, or null while it is not. A revoked release cannot be deployed and is refused on every path that would serve it, including to a build that pins its digest. No origin serves a revoked release, since a release an origin still serves cannot be revoked; earlier deployments that named it stay in the history.

pointers*array<>

The revisions this release pins, one entry per (kind, handle). Never empty: a release must contain at least one resource, and never longer than the set POST /releases accepts.

Items1 <= items <= 50
Example Request
import { configureZitadel, getApi } from "@zitadel/api/config";import { setApiCsrfToken } from "@zitadel/api/runtime/auth";// In the browser, through the SDK proxy: the session cookie authenticates the call.const client = getApi(configureZitadel({ projectId: "proj_01KWH1P4MYS7F0N9WD3P2E4YM5" }));// A write the session cookie authenticates carries the session's CSRF token.setApiCsrfToken((await client.getMySessionCsrfToken()).csrf_token);const release = await client.revokeRelease(  "rel_01KX3RG8A7F0N9WD3P2E4YM5C1",  { project_id: "proj_01KWH1P4MYS7F0N9WD3P2E4YM5" },);
Example Response

Only revoked_at changed; the pinned set and the metadata are immutable and stay as they were.

{  "id": "rel_01KX3RG8A7F0N9WD3P2E4YM5C1",  "project_id": "proj_01KWH1P4MYS7F0N9WD3P2E4YM5",  "content_hash": "sha256:9f2c1a7b4e83d05f6c2b19ae7d430f821c6b5de90a4f73829f2c1a7b4e83d05f",  "metadata": {    "message": "add phone_number to human-user",    "git_sha": "4a5b6c7d8e9f0a1b2c3d4e5f60718293a4b5c6d7",    "git_dirty": false,    "created_at": "2026-07-14T09:11:00Z",    "created_by": "user_01KWH3B72K7M7F0N9WD3P2E4YM",    "created_by_type": "human"  },  "revoked_at": "2026-07-16T16:20:00Z",  "pointers": [    {      "kind": "schema",      "handle": "human-user",      "revision_id": "sch_01KWHF18816ZQE7F0N9WD3P2E4"    },    {      "kind": "flow_definition",      "handle": "default-login",      "revision_id": "flowdef_01KWHG09JXA7F0N9WD3P2E4YM5"    },    {      "kind": "branding",      "handle": "default",      "revision_id": "brnd_01KWH1P4MYS7F0N9WD3P2E4YM5"    }  ]}

Was this helpful?