Skip to content
← All services

ProjectX Policy

The decision point and what feeds it: Authorize, Simulate, WhoCan and WhatCan, the tenancy hierarchy, roles, grants and policy packs. Every other service in the platform is a client of the four decision endpoints, and a customer auditing us is entitled to read the shape of the questions we ask about them.

57 operations · https://api.projectx.dev

Generated from the checked-in OpenAPI document for this service. The document itself is the authority, and it is what a generated client is built from.

entitlement 2

  • POST/v1/authorize:what-canRequires iam:entitlement:read

    Everything one principal can reach

    The forward query an entitlement review and an offboarding sweep both start from.

    Body
    WhatCanRequest
    Parameters (2)
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
    • Idempotency-Key · header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
  • POST/v1/authorize:who-canRequires iam:entitlement:read

    Who can do this to this resource

    Computed on every call rather than stored, because a cached answer to a security question is a wrong answer. `truncated` says when the list is a sample.

    Body
    WhoCanRequest
    Parameters (2)
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
    • Idempotency-Key · header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.

grant 9

  • POST/v1/iam/access-requestsRequires iam:grant:request

    Ask for a time-boxed elevation

    Needs an approver who is not the requester, and lapses on its own.

    Body
    AccessRequest
    Parameters (2)
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
    • Idempotency-Key · header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
  • POST/v1/iam/break-glassRequires iam:grant:break_glass

    Take emergency access

    Granted immediately and reviewed afterwards whether or not it was used. The review is the control: break-glass nobody looks at is standing access with a dramatic name.

    Body
    BreakGlassRequest
    Parameters (2)
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
    • Idempotency-Key · header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
  • POST/v1/iam/break-glass/{id}/reviewRequires iam:grant:approve

    Record the after-the-fact review of an emergency grant

    What was done under it is in the audit chain; this records that somebody read it.

    Body
    BreakGlassReview
    Parameters (3)
    • id · path · required
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
    • Idempotency-Key · header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
  • GET/v1/iam/break-glass/reviewsRequires iam:grant:list

    Emergency grants still awaiting review

    The queue that makes break-glass a control rather than a name. A grant appears here whether or not it was ever used.

    Parameters (1)
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
  • GET/v1/iam/grantsRequires iam:grant:list

    List grants

    Expired grants are withheld unless asked for, because a review of who has access should not be buried in a list of who used to.

    Parameters (5)
    • principal · query — Only grants held by this principal URN.
    • kind · query — standing, elevation or break_glass.
    • state · query — pending, active, expired, revoked or rejected.
    • include_expired · query — Set to `true` to include grants that have lapsed.
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
  • POST/v1/iam/grantsRequires iam:grant:create

    Create a standing grant

    The kind is set by the route rather than by the body, so a caller cannot ask for break-glass at a standing grant’s ceiling.

    Body
    GrantRequest
    Parameters (2)
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
    • Idempotency-Key · header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
  • DELETE/v1/iam/grants/{id}Requires iam:grant:revoke

    Revoke a grant before it expires

    The grant record is kept, revoked, so an access review can still see it was held.

    Parameters (3)
    • id · path · required
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
    • Idempotency-Key · header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
  • POST/v1/iam/grants/{id}/approveRequires iam:grant:approve

    Approve a pending grant

    `ttl_seconds` lets an approver grant less time than was asked for. An approver who can only say yes or no will say yes.

    Body
    GrantApproval
    Parameters (3)
    • id · path · required
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
    • Idempotency-Key · header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
  • POST/v1/iam/grants/{id}/rejectRequires iam:grant:approve

    Refuse a pending grant

    Body
    GrantRejection
    Parameters (3)
    • id · path · required
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
    • Idempotency-Key · header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.

organization 2

  • GET/v1/organizationsRequires platform:organization:read

    List organizations in the tenant

    Parameters (1)
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
  • POST/v1/organizationsRequires platform:organization:create

    Create an organization inside the tenant

    Body
    CreateOrganizationRequest
    Parameters (2)
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
    • Idempotency-Key · header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.

pdp 3

  • POST/v1/authorizeRequires platform:pdp:authorize

    Ask whether a principal may take an action on a resource

    The one decision endpoint every other service in the platform calls. Unaudited on purpose: the caller writes the audit event for the action the decision was about, and a second event here would double the platform’s audit volume to record the same fact.

    Body
    AuthorizeRequest
    Returns
    AuthorizeResponse
    Parameters (2)
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
    • Idempotency-Key · header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
  • POST/v1/authorize:batchRequires platform:pdp:authorize

    Ask many authorization questions in one call

    Answered in order, one decision per request. What a console uses to decide which of forty buttons to render enabled.

    Body
    AuthorizeBatchRequest
    Parameters (2)
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
    • Idempotency-Key · header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
  • POST/v1/iam/authorizeRequires platform:pdp:authorize

    Ask whether a principal may take an action on a resource

    The documented alias of `/v1/authorize`. Same handler, so the two paths cannot drift.

    Body
    AuthorizeRequest
    Returns
    AuthorizeResponse
    Parameters (2)
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
    • Idempotency-Key · header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.

policy 11

  • POST/v1/authorize:simulateRequires iam:policy:simulate

    Evaluate a decision without taking the action

    Returns every policy considered and whether it matched, which is what makes a policy editor able to explain a denial rather than merely report one.

    Body
    SimulateRequest
    Parameters (2)
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
    • Idempotency-Key · header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
  • GET/v1/iam/permissionsRequires iam:policy:list

    The permission catalog, grouped by pillar

    Frozen in code rather than configured per tenant, so a permission means the same thing everywhere. A role naming a permission that is not here is refused, which is what stops a role appearing to grant something nothing checks.

    Parameters (1)
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
  • GET/v1/iam/policiesRequires iam:policy:list

    List policies

    Parameters (2)
    • environment · query — draft, staging or published. Absent means every environment.
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
  • POST/v1/iam/policies:dry-runRequires iam:policy:simulate

    Report what a promotion would change

    Names every change and whether any of them broadens access. Nothing is written.

    Body
    PolicyDryRunRequest
    Parameters (2)
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
    • Idempotency-Key · header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
  • POST/v1/iam/policies:promoteRequires iam:policy:publish

    Promote policies between draft, staging and published

    `force` overrides an unsafe dry-run verdict and is annotated on the audit event, because that is the decision somebody will be asked about.

    Body
    PolicyPromotionRequest
    Parameters (2)
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
    • Idempotency-Key · header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
  • DELETE/v1/iam/policies/{name}Requires iam:policy:delete

    Delete a policy from one environment

    Parameters (4)
    • name · path · required
    • environment · query — draft, staging or published. Defaults to draft.
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
    • Idempotency-Key · header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
  • GET/v1/iam/policies/{name}Requires iam:policy:read

    Fetch one policy

    Carries the compiled Cedar as well as the structured definition, so a decision can be replayed exactly as it was made.

    Parameters (3)
    • name · path · required
    • environment · query — draft, staging or published. Defaults to draft.
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
  • PUT/v1/iam/policies/{name}Requires iam:policy:write

    Create or replace a policy

    Saving produces a new version and a digest over the compiled Cedar, which is the unit of “did this change”. Written to the draft environment; promoting it is a separate, dry-runnable step.

    Body
    PolicyDocumentInput
    Parameters (3)
    • name · path · required
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
    • Idempotency-Key · header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
  • PUT/v1/iam/policy-testsRequires iam:policy:write

    Replace the policy test suite

    Run against an environment before a promotion, which is what turns “we think this change is safe” into an answer.

    Body
    PolicyTestSuite
    Parameters (2)
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
    • Idempotency-Key · header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
  • POST/v1/iam/policy-tests:runRequires iam:policy:simulate

    Run the policy test suite

    Answers 409 when any case fails, so a pipeline step can branch on the status rather than parse the body.

    Parameters (3)
    • environment · query — Which policy set to run against. Defaults to published.
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
    • Idempotency-Key · header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
  • POST/v1/iam/simulateRequires iam:policy:simulate

    Evaluate a decision without taking the action

    The documented alias of `/v1/authorize:simulate`.

    Body
    SimulateRequest
    Parameters (2)
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
    • Idempotency-Key · header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.

project 3

  • GET/v1/projectsRequires platform:project:read

    List projects in the tenant

    Parameters (1)
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
  • POST/v1/projectsRequires platform:project:create

    Create a project

    What usage is attributed to. A project that charges nowhere makes the showback wrong for every other project in the tenant, which is why the cost centre is not optional in practice.

    Body
    CreateProjectRequest
    Parameters (2)
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
    • Idempotency-Key · header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
  • GET/v1/projects/{name}Requires platform:project:read

    Fetch one project

    Parameters (2)
    • name · path · required
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.

quota 3

  • GET/v1/quotasRequires platform:quota:read

    List quotas with their current consumption

    Parameters (2)
    • scope · query — Only quotas at this scope URN.
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
  • PUT/v1/quotasRequires platform:quota:set

    Set a quota on one dimension at one scope

    A soft limit warns and the hard limit refuses. Both are needed: a quota that only refuses gives an operator no notice, and one that only warns is not a quota.

    Body
    SetQuotaRequest
    Parameters (2)
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
    • Idempotency-Key · header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
  • POST/v1/quotas:checkRequires platform:quota:read

    Ask whether an amount would fit under the quota

    Consumes nothing. Called before a provision, so the refusal arrives before the side effects.

    Body
    CheckQuotaRequest
    Parameters (2)
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
    • Idempotency-Key · header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.

role 5

  • GET/v1/iam/rolesRequires iam:role:list

    List roles, built-in and custom

    Parameters (1)
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
  • POST/v1/iam/rolesRequires iam:role:create

    Create a role

    Every permission must exist in the catalog. A role naming a permission nothing checks is a role that appears to grant something and does not.

    Body
    CreateRoleRequest
    Parameters (2)
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
    • Idempotency-Key · header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
  • DELETE/v1/iam/roles/{name}Requires iam:role:delete

    Delete a role

    Refused while anybody still holds it, because the alternative is silently removing their access.

    Parameters (3)
    • name · path · required
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
    • Idempotency-Key · header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
  • GET/v1/iam/roles/{name}Requires iam:role:read

    Fetch one role with its effective permissions

    `effective_permissions` is the closure over inheritance, which is the only number that answers what holding this role actually lets somebody do.

    Parameters (2)
    • name · path · required
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
  • PATCH/v1/iam/roles/{name}Requires iam:role:update

    Change a role

    Absent fields are left alone. Supplying `permissions` replaces the list entire.

    Body
    UpdateRoleRequest
    Parameters (3)
    • name · path · required
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
    • Idempotency-Key · header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.

share 3

  • GET/v1/sharesRequires platform:share:read

    List cross-tenant shares, in both directions

    What this tenant has let out and what has been let in to it, because a review needs both.

    Parameters (1)
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
  • POST/v1/sharesRequires platform:share:create

    Share one resource with another tenant for a bounded time

    The only sanctioned way across a tenant boundary, and it expires on its own: a cross-tenant grant with no expiry is a merged tenancy nobody agreed to.

    Body
    CreateShareRequest
    Parameters (2)
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
    • Idempotency-Key · header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
  • DELETE/v1/shares/{urn}Requires platform:share:revoke

    Revoke a share before it expires

    The share record is kept, revoked, rather than removed: who let whom in and who closed it is the evidence.

    Parameters (3)
    • urn · path · required — The resource URN, used verbatim.
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
    • Idempotency-Key · header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.

tenant 7

  • GET/v1/tenantsRequires platform:tenant:read

    List tenants

    Everybody may ask; what differs is what the question is about. Staff are answered with the register, a customer with themselves — authorizing both against the caller’s own tenant and then returning every row would hand the customer list to anyone holding a permission their own tenant-admin role includes by default.

    Parameters (1)
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
  • POST/v1/tenantsRequires platform:tenant:create

    Register a tenant

    `isolation` is the commercial decision made technical: a dedicated tenant gets its own schema. The key appears in every URN of theirs and cannot be changed afterwards.

    Body
    CreateTenantRequest
    Parameters (2)
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
    • Idempotency-Key · header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
  • POST/v1/tenants:provisionRequires platform:tenant:provision

    Create a company that signed up, with its first administrator

    Self-service sign-up (A5). Creates the tenant and its default workspace, issues the service grants every customer tenant needs, and gives the first user the built-in `tenant-admin` role in that tenant. Idempotent: a retried sign-up finds the tenant and issues only what is missing. Callable from the platform tenant only — a customer administrator holding wildcard permissions in their own tenant still cannot create another company.

    Body
    ProvisionTenantRequest
    Parameters (2)
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
    • Idempotency-Key · header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
  • GET/v1/tenants/{key}Requires platform:tenant:read

    Fetch one tenant

    Reading your own record is a same-tenant read; reading somebody else’s is a read of the register, which lives in the platform tenant. So the operator console reaches it with the grant it already holds and a customer is refused by tenant isolation rather than by a check written into the handler.

    Parameters (2)
    • key · path · required
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
  • GET/v1/tenants/{key}/exportRequires platform:tenant:export

    Export a tenant’s configuration

    The portability half of the exit story: what a customer takes with them, in a format they can read without us.

    Parameters (2)
    • key · path · required
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
  • POST/v1/tenants/{key}/lifecycleRequires platform:tenant:lifecycle

    Suspend, terminate or restore a tenant

    Suspension stops new spend and keeps the data. Termination starts the clock on deletion, which is a separate, evidenced operation rather than a state change.

    Body
    TenantLifecycleRequest
    Parameters (3)
    • key · path · required
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
    • Idempotency-Key · header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
  • PUT/v1/tenants/{key}/residencyRequires platform:tenant:update

    Set which substrates a tenant’s resources may live on

    The residency floor. Enforced at every provision and every query plan, not only at creation — a rule applied once is a rule a migration can break.

    Body
    ResidencyRequest
    Parameters (3)
    • key · path · required
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
    • Idempotency-Key · header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.

workspace 9

  • DELETE/v1/membershipsRequires platform:workspace:manage_members

    Remove a membership

    Identified by all three of principal, scope and role, in the query string rather than a body: the same person may hold two roles at one scope, and dropping the wrong one is silent.

    Parameters (5)
    • principal · query · required — Whose membership.
    • scope · query · required — At which scope.
    • role · query · required — Which role.
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
    • Idempotency-Key · header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
  • GET/v1/membershipsRequires platform:workspace:read

    List memberships

    Parameters (2)
    • principal · query — Only memberships held by this principal URN.
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
  • POST/v1/membershipsRequires platform:workspace:manage_members

    Grant a principal a role at a scope

    Body
    CreateMembershipRequest
    Parameters (2)
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
    • Idempotency-Key · header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
  • GET/v1/workspacesRequires platform:workspace:read

    List workspaces in the tenant

    Parameters (1)
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
  • POST/v1/workspacesRequires platform:workspace:create

    Create a workspace

    The unit of collaboration and of cost. `cloud_mode` decides whether resources here are ordered from ProjectX or provisioned onto the customer’s own substrate, and on a customer-owned substrate no infrastructure margin is metered.

    Body
    CreateWorkspaceRequest
    Parameters (2)
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
    • Idempotency-Key · header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
  • GET/v1/workspaces/{name}Requires platform:workspace:read

    Fetch one workspace

    Parameters (2)
    • name · path · required
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
  • POST/v1/workspaces/{name}/archiveRequires platform:workspace:archive

    Archive a workspace

    Stops new work in it and keeps everything. Not a delete: the resources and their history remain.

    Parameters (3)
    • name · path · required
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
    • Idempotency-Key · header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
  • POST/v1/workspaces/{name}/cloneRequires platform:workspace:clone

    Copy a workspace’s configuration under a new name

    Memberships and resources are not copied: a clone that carried the original’s access list would be a way to hand somebody a second copy of data they were granted once.

    Body
    CloneWorkspaceRequest
    Parameters (3)
    • name · path · required
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
    • Idempotency-Key · header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
  • PUT/v1/workspaces/{name}/residencyRequires platform:workspace:update

    Narrow a workspace’s substrate allowance

    May narrow the tenant’s allowance and never widen it.

    Body
    ResidencyRequest
    Parameters (3)
    • name · path · required
    • X-Request-Id · header — Echoed on the response and recorded on the audit event.
    • Idempotency-Key · header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
ProjectX Policy · API reference · ProjectX