Home / Developers / Management API Reference

Reference

Management API Reference

Create and update flags, targets, and targeting rules, and read the audit trail programmatically with scoped Bearer tokens.

Overview

The Management API lets you create and update feature flags, read and roll out targets, manage targeting rules, and read your account's audit trail — all at https://api.zenmanage.com under the /management/v1 path.

This is a distinct API from the public evaluation API: it authenticates with scoped Bearer tokens instead of platform keys, and it manages configuration rather than evaluating flags for a context. Use it to build internal tooling, CI/CD gates, or automation around your flag configuration.

Prerequisites

  • A management API token generated in the Zenmanage admin app, with the scopes your integration needs.
  • The project (and environment, for target/rule endpoints) key you're operating on.
  • A plan for handling 429s and 422s — write traffic should back off and re-validate rather than retry blindly.

Authentication and scopes

Every request must include a management token as a Bearer token:

Authorization headerhttp
GET /management/v1/projects/your-project/flags HTTP/1.1
Host: api.zenmanage.com
Authorization: Bearer mgt_your_token_here

Tokens are scoped. A token missing the scope an operation requires gets a 403, even if the token itself is valid.

Scope Grants
flags:readList and read flags.
flags:writeCreate, update, and delete flags.
targets:writeRead and update targets and targeting rules — there is no separate read scope for these.
audit:readRead the account audit trail.

Rate limits

Each token is rate limited per minute; the limit depends on the token's tier.

Tier Requests / minute
standard60
elevated300
unlimitedNo enforced ceiling

Every response carries X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers. Exceeding the limit returns a 429 with a Retry-After header and body {"status":"error","message":"Rate limit exceeded."}. Contact hello@zenmanage.com if your token needs a higher tier.

Error envelope

Most errors — auth failures, missing scopes, not-found resources, rate limits — share one envelope:

Standard errorjson
{
  "status": "error",
  "message": "Flag not found."
}

A missing-scope 403 adds a required_scope field alongside status/message.

422s on flag and targeting-rule bodies are the exception — those are request-validation failures, and instead return Laravel's field-keyed shape:

Validation errorjson
{
  "message": "The key field is required.",
  "errors": {
    "key": ["The key field is required."]
  }
}

Two operations don't follow that split: audit log query-parameter errors always use the standard envelope, and rollout updates can return either shape depending on what failed — a bad rollout_percentage uses the field-keyed shape, while "target isn't published" or "target has no active rollout" use the standard one.

Idempotency keys

POST and PUT requests accept an optional Idempotency-Key header, up to 255 characters. Replaying the same key with the same request body and token returns the original response instead of repeating the operation, marked with X-Idempotent-Replayed: true. Replays are honored for 24 hours; only successful and client-error responses are cached, so a 500 is never replayed.

Pagination

The flags list and audit logs list are cursor-paginated: pass the previous response's meta.next_cursor as the cursor query parameter to fetch the next page. A null cursor means there are no more results. per_page defaults to 25 and is capped at 100. Targets and targeting rules are returned as full, unpaginated arrays — each is a bounded set scoped to one target.

OpenAPI specification

Zenmanage publishes an OpenAPI v3 document for the Management API at https://api.zenmanage.com/management/v1/api-docs. Use it for client generation, schema validation, and endpoint exploration. This page mirrors that spec — if the two ever disagree, treat the spec as authoritative.

Endpoint catalog

Method Path Scope Purpose
GET/projects/{projectKey}/flagsflags:readList flags in a project, paginated.
GET/projects/{projectKey}/flags/{flagKey}flags:readRead a single flag.
POST/projects/{projectKey}/flagsflags:writeCreate a flag.
PUT/projects/{projectKey}/flags/{flagKey}flags:writeUpdate a flag's name, description, or platform inclusion. Key and type are immutable.
DELETE/projects/{projectKey}/flags/{flagKey}flags:writeDelete a flag.
GET.../targetstargets:writeList targets for a flag in an environment.
GET.../targets/{targetUlid}targets:writeRead a single target.
PUT.../targets/{targetUlid}/rollouttargets:writeUpdate a published target's rollout percentage.
GET.../targets/{targetUlid}/rulestargets:writeList targeting rules for a target, in evaluation order.
POST.../targets/{targetUlid}/rulestargets:writeCreate a targeting rule.
PUT.../targets/{targetUlid}/rules/{ruleUlid}targets:writeReplace a targeting rule's value, description, and criteria.
DELETE.../targets/{targetUlid}/rules/{ruleUlid}targets:writeDelete a targeting rule.
GET/audit-logsaudit:readList audit log entries for the account, paginated and filterable.

All target and target-rule paths are prefixed with /projects/{projectKey}/flags/{flagKey}/environments/{environmentKey}, shortened above for width. All paths shown are relative to https://api.zenmanage.com/management/v1.

Example requests

POST /projects/your-project/flags

Create a flag. key must be lowercase alphanumeric with hyphens/underscores, unique within the project. include_in_mobile/include_in_client both default to true.

Request bodyjson
{
  "key": "new-checkout",
  "name": "New Checkout",
  "type": "boolean",
  "description": "Rolls out the redesigned checkout flow."
}
200 responsejson
{
  "status": "success",
  "data": {
    "ulid": "flg_01hzy9m2qj1q1jz3sd4xg9n1vb",
    "key": "new-checkout",
    "name": "New Checkout",
    "type": "boolean",
    "description": "Rolls out the redesigned checkout flow.",
    "permanent": false,
    "include_in_mobile": true,
    "include_in_client": true,
    "created_at": "2026-07-28T12:00:00+00:00",
    "updated_at": "2026-07-28T12:00:00+00:00"
  }
}
GET /projects/your-project/flags?per_page=25&cursor=eyJrZXkiOiJuZXctY2hlY2tvdXQifQ

List flags. Also supports type and search filters.

200 responsejson
{
  "status": "success",
  "data": [
    { "ulid": "flg_01hzy9m2qj1q1jz3sd4xg9n1vb", "key": "new-checkout", "name": "New Checkout", "type": "boolean", "description": null, "permanent": false, "include_in_mobile": true, "include_in_client": true, "created_at": "2026-07-28T12:00:00+00:00", "updated_at": "2026-07-28T12:00:00+00:00" }
  ],
  "meta": {
    "per_page": 25,
    "next_cursor": null,
    "prev_cursor": null
  }
}
PUT .../targets/{targetUlid}/rollout

Update a rollout percentage. The target must already be published and have an active rollout — otherwise this returns a 422 with the standard error envelope rather than a validation error.

Request bodyjson
{ "rollout_percentage": 25 }
422 — target not publishedjson
{
  "status": "error",
  "message": "Rollout can only be updated on published targets."
}
POST .../targets/{targetUlid}/rules

Create a targeting rule. comparer depends on selector: context and segment selectors only accept equal, notequal, in, notin; attribute additionally accepts gt, gte, lt, lte, isnull, notnull, contains, notcontains, startswith, endswith, notstartswith, notendswith. Sending a comparer that doesn't match the selector returns a validation-shape 422. values is required unless comparer is isnull/notnull.

Request bodyjson
{
  "value_ulid": "val_01hzy9m2qj1q1jz3sd4xg9n1vb",
  "description": "US enterprise accounts",
  "criteria": {
    "selector": "attribute",
    "comparer": "equal",
    "sub_selector": "country",
    "values": ["US"]
  }
}
GET /audit-logs?result=denied_scope&per_page=25

List audit log entries. Filterable by from/to, result, subject_type/subject_ulid, management_token_ulid, and actor_ulid. action filtering currently only covers auth and flag actions (not target/target-rule actions) — those entries still appear in unfiltered results.

200 responsejson
{
  "status": "success",
  "data": [
    {
      "ulid": "aud_01hzy9m2qj1q1jz3sd4xg9n1vb",
      "actor": { "type": "user", "ulid": "usr_...", "name": "Jane Doe", "email": "jane@example.com" },
      "token": { "ulid": "mgt_...", "name": "CI deploy token" },
      "http_method": "PUT",
      "http_path": "/management/v1/projects/your-project/flags/new-checkout/environments/staging/targets/.../rollout",
      "action": "target.rollout_updated",
      "subject_type": "target",
      "subject_ulid": "tgt_...",
      "subject_label": "new-checkout / staging",
      "result": "success",
      "status_code": 200,
      "created_at": "2026-07-28T12:05:00+00:00"
    }
  ],
  "meta": {
    "per_page": 25,
    "sort": "created_at_desc",
    "next_cursor": null
  }
}

Common errors

400 Bad Request: the Idempotency-Key header exceeds 255 characters.

401 Unauthorized: the Bearer token is missing, invalid, revoked, or expired.

403 Forbidden: the token is valid but lacks the required scope, or lacks a grant for the project/environment.

404 Not Found: the project, environment, flag, target, or rule in the path doesn't exist, or the token has no grant for it.

422 Unprocessable Entity: request body or query parameters failed validation — see the error envelope section above for the exact shape.

429 Too Many Requests: the token's rate limit was exceeded — back off using the Retry-After header.

Next step

Take the next integration step in your own stack.

Start with the quickstart that matches your runtime, then return to the reference pages when you need exact request and payload details.