> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zespan.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Org-wide policy inventory, grouped by policy id

> Every policy id enforced (or once enforced) anywhere in the organisation, one group per id, with every project/environment occurrence underneath it. Backs the rollup cards and table on [Organisation view](/dashboard/policies#organisation-view).

**Default scope is every environment**, not production — `environment` and `productionOnly` are both opt-in narrowings, never the other way around. They narrow the same axis and are mutually exclusive: passing both is a 400.

`presentIn` / `absentFrom` on each group describe **coverage, not compliance** — the numerator and denominator are both projects "measurable" in the current scope. Under a narrowed scope, a project with no matching environment was never measurable and is excluded from the denominator entirely, rather than counted as absent. There is deliberately no field anywhere in this response naming a project non-compliant, failing, or in violation — no standard exists yet for a project to be measured against.

`lastAppliedAt` on an occurrence is bounded to the trailing 90 days and the most recent 2,000 applies across the organisation (whichever limit is hit first); `applyRecencyTruncated` reports whether the row cap was hit, and `applyRecencyCap` echoes the cap that ran. A `null` `lastAppliedAt` means no apply landed inside that window — it does **not** mean the policy was never applied, which is what `status: "never_applied"` on the same occurrence means instead.

`orgId` in the path selects the scope: it accepts either the organisation's slug or its id, and the caller must be a member of it or the request is refused (403). The handler reads the resolved active organisation rather than re-parsing the path segment itself, but the path segment is exactly what determined it.

**Authenticated with a dashboard session (browser cookie), not `x-api-key`**, and requires the `policy:read` permission, which every role (including viewer) has.




## OpenAPI

````yaml /api-reference/openapi.yaml get /v1/orgs/{orgId}/policies
openapi: 3.1.0
info:
  title: Zespan Public API
  version: 1.0.0
  description: >
    The Zespan Public API covers the endpoints that customers call directly or
    through the Zespan SDKs: trace ingestion (native and OpenTelemetry), prompt
    management, datasets and dataset runs, and the runtime guardrails check.

    All endpoints authenticate with a project API key sent in the `x-api-key`
    header. Create and manage API keys from the project settings in the Zespan
    dashboard.
servers:
  - url: https://api.zespan.com
    description: Zespan production API
security:
  - ApiKeyAuth: []
tags:
  - name: Ingestion
    description: Send traces and events to Zespan.
  - name: OpenTelemetry
    description: OTLP-compatible ingestion endpoints.
  - name: Prompts
    description: Manage versioned prompts and their labels, tags, and folders.
  - name: Datasets
    description: Read datasets and manage dataset runs used for experiments and scoring.
  - name: Guardrails
    description: Runtime guardrail evaluation.
  - name: SDK / CLI support
    description: >
      Small support endpoints consumed by the SDKs and @zespan/cli rather than
      called directly by application code.
  - name: Blast Radius
    description: >
      The prompt/agent/model/policy/evaluator/alert dependency graph backing the
      pre-release impact check and the evaluator-delete gate in the dashboard.
      Session-authenticated (dashboard cookie), not `x-api-key`.
  - name: Outcomes
    description: >
      Report business outcomes (a deflected ticket, an avoided refund, an SLA
      met) attributed to a trace, and read them back summarized by agent or
      model, joined to real trace cost. Backs the Value dashboard page. The
      ingest endpoint is `x-api-key`-authenticated like the rest of ingestion;
      the two read endpoints are session-authenticated (dashboard cookie) like
      Blast Radius.
  - name: Compliance
    description: >
      Generate audit-ready evidence documents (a per-agent Compliance Card, or
      SOC 2 control evidence) from recorded platform data, and re-verify a
      generated document's citations against live data. Session-authenticated
      (dashboard cookie), not `x-api-key`, gated by `compliance:read` /
      `compliance:generate` permissions and the Pro plan or above (the framework
      listing is the one exception — no project scope and no plan gate, since a
      customer deciding whether to upgrade needs to see what they'd get).
  - name: Models
    description: >
      Per-model usage, cost, latency, and error-rate rollups for a project,
      including the lifecycle overlay described under the Model Lifecycle tag.
      Session-authenticated (dashboard cookie), not `x-api-key`, gated by
      `dashboard:read`.
  - name: Model Lifecycle
    description: >
      Findings from the daily model deprecation scan, which matches models a
      project actually calls against a curated, bundled catalogue of
      provider-announced deprecation and retirement dates. Every figure on a
      finding (call volume, cost, affected agents/prompts, cost comparison
      against a named successor) is measured from real trace data — there is no
      quality-delta or regression-comparison endpoint, because nothing in this
      API invokes a model on the caller's behalf. Session-authenticated
      (dashboard cookie), not `x-api-key`: reading findings and the catalogue
      requires `dashboard:read`, dismissing a finding requires `alerts:manage`.
  - name: Policies
    description: >
      The organisation-wide policy rollup backing the top-level **Policies**
      entry in the org admin sidebar: every policy id across every project,
      grouped by id, plus the org's recent apply history. These endpoints report
      what is deployed — they never declare a project non-compliant, because no
      standard exists yet for a project to be measured against. `orgId` in the
      path selects the scope: it accepts either the organisation's slug or its
      id, and both routes 403 if the caller is not a member of it.
      Session-authenticated (dashboard cookie), not `x-api-key`, gated by
      `policy:read`, which every role has.
paths:
  /v1/orgs/{orgId}/policies:
    get:
      tags:
        - Policies
      summary: Org-wide policy inventory, grouped by policy id
      description: >
        Every policy id enforced (or once enforced) anywhere in the
        organisation, one group per id, with every project/environment
        occurrence underneath it. Backs the rollup cards and table on
        [Organisation view](/dashboard/policies#organisation-view).


        **Default scope is every environment**, not production — `environment`
        and `productionOnly` are both opt-in narrowings, never the other way
        around. They narrow the same axis and are mutually exclusive: passing
        both is a 400.


        `presentIn` / `absentFrom` on each group describe **coverage, not
        compliance** — the numerator and denominator are both projects
        "measurable" in the current scope. Under a narrowed scope, a project
        with no matching environment was never measurable and is excluded from
        the denominator entirely, rather than counted as absent. There is
        deliberately no field anywhere in this response naming a project
        non-compliant, failing, or in violation — no standard exists yet for a
        project to be measured against.


        `lastAppliedAt` on an occurrence is bounded to the trailing 90 days and
        the most recent 2,000 applies across the organisation (whichever limit
        is hit first); `applyRecencyTruncated` reports whether the row cap was
        hit, and `applyRecencyCap` echoes the cap that ran. A `null`
        `lastAppliedAt` means no apply landed inside that window — it does
        **not** mean the policy was never applied, which is what `status:
        "never_applied"` on the same occurrence means instead.


        `orgId` in the path selects the scope: it accepts either the
        organisation's slug or its id, and the caller must be a member of it or
        the request is refused (403). The handler reads the resolved active
        organisation rather than re-parsing the path segment itself, but the
        path segment is exactly what determined it.


        **Authenticated with a dashboard session (browser cookie), not
        `x-api-key`**, and requires the `policy:read` permission, which every
        role (including viewer) has.
      operationId: getOrgPolicies
      parameters:
        - name: orgId
          in: path
          required: true
          schema:
            type: string
          description: >
            Selects the scope: an organisation slug or id. The caller must be a
            member of it or the request is refused. See the note above.
        - name: environment
          in: query
          schema:
            type: string
            maxLength: 64
          description: >
            Narrow to one environment slug. Mutually exclusive with
            `productionOnly` — passing both is a 400.
        - name: productionOnly
          in: query
          schema:
            type: string
            enum:
              - 'true'
              - 'false'
          description: >
            Only the literal strings `"true"` and `"false"` are accepted — any
            other value is a 400 schema-validation error, not a silent
            fall-through. `"true"` narrows to environments with `isProduction:
            true`; `"false"` or omitting the parameter leaves the scope at every
            environment. Mutually exclusive with `environment` — passing both
            (with `productionOnly=true`) is also a 400.
      responses:
        '200':
          description: The org's policy inventory in the requested scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrgPolicyInventoryResponse'
        '400':
          description: >-
            Both `environment` and `productionOnly` were passed — they narrow
            the same axis.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error: >-
                  Pass either environment or productionOnly, not both — they
                  narrow the same axis.
                code: CONFLICTING_ENVIRONMENT_FILTER
        '403':
          description: Not authenticated with a dashboard session, or lacks `policy:read`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      security: []
components:
  schemas:
    OrgPolicyInventoryResponse:
      type: object
      description: Response of `GET /v1/orgs/{orgId}/policies`.
      properties:
        environmentFilter:
          type: object
          description: Echoes the scope this response was computed under.
          properties:
            slug:
              type: string
              nullable: true
            productionOnly:
              type: boolean
          required:
            - slug
            - productionOnly
        environmentSlugs:
          type: array
          items:
            type: string
          description: >-
            Every environment slug represented in the org, for populating a
            scope selector.
        projectsInScope:
          type: array
          description: >
            Projects measurable in the current scope — the denominator behind
            every group's `presentIn` / `absentFrom`.
          items:
            type: object
            properties:
              id:
                type: string
              name:
                type: string
            required:
              - id
              - name
        groups:
          type: array
          items:
            $ref: '#/components/schemas/OrgPolicyGroup'
        applyRecencyTruncated:
          type: boolean
          description: >-
            Whether the apply-recency pass behind every `lastAppliedAt` hit its
            row cap.
        applyRecencyCap:
          type: integer
          description: >-
            The row cap the recency pass ran under (currently 2,000, over a
            trailing 90-day window).
      required:
        - environmentFilter
        - environmentSlugs
        - projectsInScope
        - groups
        - applyRecencyTruncated
        - applyRecencyCap
    Error:
      type: object
      properties:
        error:
          type: string
          description: Human-readable error message.
          example: Unauthorized
        code:
          type: string
          description: Machine-readable error code, when present.
          example: rate_limit_exceeded
      required:
        - error
    OrgPolicyGroup:
      type: object
      description: One policy id, aggregated across every project that carries it.
      properties:
        policyId:
          type: string
        name:
          type: string
          description: >-
            The display name most occurrences agree on; ties break
            alphabetically.
        nameConflict:
          type: boolean
          description: >-
            Two or more projects use different display names for this same
            policy id.
        presentIn:
          type: integer
          description: >-
            Distinct projects with at least one occurrence of this policy id, in
            the current scope.
        absentFrom:
          type: array
          description: >
            Projects measurable in the current scope with no occurrence of this
            policy id. A fact about what's deployed, never a verdict — see the
            note on the parent response.
          items:
            type: object
            properties:
              id:
                type: string
              name:
                type: string
            required:
              - id
              - name
        postureCounts:
          type: object
          description: >-
            Occurrence counts, not project counts — one project in two
            environments contributes two.
          properties:
            dryrun:
              type: integer
            warn:
              type: integer
            deny:
              type: integer
          required:
            - dryrun
            - warn
            - deny
        statusCounts:
          type: object
          properties:
            never_applied:
              type: integer
            in_sync:
              type: integer
            unapplied_changes:
              type: integer
            out_of_band:
              type: integer
            detached:
              type: integer
          required:
            - never_applied
            - in_sync
            - unapplied_changes
            - out_of_band
            - detached
        testCounts:
          type: object
          properties:
            tested:
              type: integer
            stale:
              type: integer
            untested:
              type: integer
          required:
            - tested
            - stale
            - untested
        originCounts:
          type: object
          properties:
            git:
              type: integer
            zespan:
              type: integer
          required:
            - git
            - zespan
        occurrences:
          type: array
          items:
            $ref: '#/components/schemas/OrgPolicyOccurrence'
      required:
        - policyId
        - name
        - nameConflict
        - presentIn
        - absentFrom
        - postureCounts
        - statusCounts
        - testCounts
        - originCounts
        - occurrences
    OrgPolicyOccurrence:
      type: object
      description: One policy id, live (or once live) in one project's one environment.
      properties:
        project:
          type: object
          properties:
            id:
              type: string
            name:
              type: string
          required:
            - id
            - name
        environment:
          nullable: true
          description: >
            `null` when this policy is authored but applied nowhere in the
            current scope — it sits in no environment. When present, `id` is
            `null` for a project with no configured `Environment` rows and
            `slug` is `"default"`.
          type: object
          properties:
            id:
              type: string
              nullable: true
            slug:
              type: string
          required:
            - id
            - slug
        docId:
          type: string
          nullable: true
        origin:
          type: string
          enum:
            - git
            - zespan
          description: >
            This policy's **owner** — a file in your repository (`git`) or
            authored in the dashboard (`zespan`). Not the apply entrypoint; see
            `origin` on `OrgPolicyApply` for that.
        posture:
          type: string
          nullable: true
          enum:
            - dryrun
            - warn
            - deny
          description: '`null` when this occurrence has no live rule.'
        ruleCount:
          type: integer
        status:
          type: string
          enum:
            - never_applied
            - in_sync
            - unapplied_changes
            - out_of_band
            - detached
        testStatus:
          type: string
          enum:
            - tested
            - stale
            - untested
        lastAppliedAt:
          type: string
          format: date-time
          nullable: true
          description: >
            `null` means no apply landed inside the 90-day / 2,000-row recency
            window (see `applyRecencyTruncated` on the response) — it does not
            mean the policy was never applied. `status: "never_applied"` is the
            field that carries that meaning.
        lastAppliedBy:
          type: object
          nullable: true
          properties:
            userId:
              type: string
          required:
            - userId
        sourceRef:
          type: string
          nullable: true
        inheritedFrom:
          nullable: true
          description: Reserved for a future org-baseline capability. Always `null` today.
      required:
        - project
        - environment
        - docId
        - origin
        - posture
        - ruleCount
        - status
        - testStatus
        - lastAppliedAt
        - lastAppliedBy
        - sourceRef
        - inheritedFrom
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
      description: >-
        Project API key. Manage keys in the Zespan dashboard under project
        settings.

````