---
title: "List delegations"
method: GET
path: "/api/v1/delegations"
tags: ["Delegations"]
---

# List delegations

`GET /api/v1/delegations`

## Request

- Base URL: `https://api.proof.holdings`
- URL: `https://api.proof.holdings/api/v1/delegations`
- Auth: one of:
  - HTTP bearer
  - API key in cookie `auth_token`

List YOUR delegations, newest first. There is deliberately no public enumeration over anyone else's delegations — the public read shape is a point lookup of a handle you already hold (`GET /api/v1/proofs/{id}/status` accepts `ph_dlg_*`). Every item carries the DERIVED `effective_status` and `is_valid` beside its stored `status`, the same pair the detail returns: a delegation whose domain control proof was revoked, suspended or expired — or whose OWN lifetime has run out — reports that here even though its own `status` is untouched. Render `effective_status`. Returns 503 `delegation_status_unavailable` if that cascade cannot be resolved — a page is never served with stored statuses standing in for derived ones.

## Query parameters

- `page` string: Page number (default 1)
- `limit` string: Page size (default 20, max 100)
- `status` 'active' | 'revoked': Filters the delegation's OWN stored record — whether you revoked it yourself. It deliberately does NOT filter on the derived `effective_status`: that cascade spans two collections and cannot be expressed in the query, and filtering it after the fact would punch holes in both the page and its total. `?status=active` therefore still returns a delegation whose control proof died; read `effective_status` on each item.

## Response `200`

Your delegations

- DelegationListResponse: The caller's own delegations (no public enumeration exists)
  - `data` DelegationListItem[], required
    - `id` string, required: MongoDB ObjectId
    - `delegation_id` string, nullable, required: Stable public handle (`ph_dlg_<32 hex>`) — also the token's `sub`, and accepted by the proof status endpoint
    - `principal` string, required: The proven domain that authorized the delegate — derived from the control proof, never client input
    - `delegate` DelegateIdentifier, required: Typed delegate identifier
      - `type` 'url' | 'purl', required: Delegate identifier type: `purl` for a package (`pkg:npm/postmark-mcp`), `url` for an https endpoint
      - `value` string, required: The identifier. Canonicalized per type at creation: purl version/qualifiers/subpath are rejected (delegate the artifact, not a version); urls must be https with no credentials, query or fragment
    - `scope` string[], required
    - `control_proof` string, required: The control proof handle this delegation points down to
    - `status` 'active' | 'revoked', required: The owner's OWN record — whether they revoked this delegation themselves. Nothing else writes it: neither the control proof dying nor this delegation's own lifetime running out. Liveness is DERIVED and reported separately as `effective_status`. This stored field is also what `?status=` filters on
    - `revoked_at` string, date-time, nullable, required
    - `revoked_reason` string, nullable, required
    - `environment` 'production' | 'test', required
    - `issued_at` string, date-time, required
    - `expires_at` string, date-time, required: Never later than the control proof's own expiry at issuance (effective expiry is min of the two)
    - `created_at` string, date-time, required
    - `effective_status` 'active' | 'revoked' | 'expired' | 'suspended', required: DERIVED liveness, computed at read time from BOTH clocks that govern a delegation: the control proof it stands on (cascade rule a — revoked, suspended or expired flows through) and its own `expires_at` (rule b — effective expiry is the earlier of the two). Either one running out reports `expired` here while the delegation's own `status` stays untouched. This is the field to render — `status` alone would show a live badge on a delegation every verifier already rejects
    - `is_valid` boolean, required: True only when `effective_status` is `active`. It is a restatement of that field, not a second opinion: expiry is decided once, inside the derivation above
  - `pagination` object, required
    - `page` integer, required
    - `limit` integer, required
    - `total` integer, required

## Other responses

- `503`: Delegation status could not be resolved (registry unreachable). The page fails closed rather than reporting stored statuses as if they were derived — retry

---

[API](https://skmtc.dev/proof/apis/proof-holdings-api.md) · [All operations](https://skmtc.dev/proof/apis/proof-holdings-api/llms.txt) · [OpenAPI document](https://skmtc.dev/proof/apis/proof-holdings-api/revisions/b15f506de696?raw)
