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

# Managing your credentials

> Revoke SD-JWT credentials from your issuer service, serve per-credential revocation status, and optionally publish an IETF Token Status List.

<Info>
  Every route on this page runs on **your issuer service** at `{ISSUER_ORIGIN}`, not on the AIR API. The examples follow the SD-JWT branch of the [AIR issuer service](https://github.com/MocaNetwork/air-issuer-service/tree/main). Revocation needs a database (the reference issuer service uses PostgreSQL); see [Issuer backend hosting](/products/identity/backend-hosting).
</Info>

Revoking a credential is one-way. Once revoked, verifiers that check status reject it. Handle deletion of your own source records separately under your retention policy.

## Routes

| Method | Path | Called by | Auth |
| - | - | - | - |
| `GET` | `/admin/issuance-history` | Your servers | `x-admin-api-key` |
| `POST` | `/admin/revoke` | Your servers | `x-admin-api-key` |
| `POST` | `/admin/publish-token-status-list` | Your servers, when the status list is enabled | `x-admin-api-key` |
| `GET` | `/revocation-status/:nonce` | Verifiers and AIR | None |
| `GET` | `/statuslist/:partition` | Verifiers, when the status list is enabled | None |

Keep `ADMIN_API_KEY` on your servers. Never call admin routes from a browser.

## Revoke a credential

<Steps>
  <Step title="Find the revocation nonce">
    Each issued credential has a `revocationNonce`. Look it up in the issuance history, filtered by holder or schema:

    ```bash theme={null}
    curl -s \
      -H "x-admin-api-key: $ADMIN_API_KEY" \
      "$ISSUER_ORIGIN/admin/issuance-history?holderDid=did:air:...&schemaId=<SCHEMA_ID>" \
      | jq .
    ```
  </Step>

  <Step title="Revoke it">
    ```bash theme={null}
    curl -X POST "$ISSUER_ORIGIN/admin/revoke" \
      -H "x-admin-api-key: $ADMIN_API_KEY" \
      -H "content-type: application/json" \
      -d '{"nonce":"<revocationNonce>"}'
    ```

    The credential is marked revoked in your database immediately.
  </Step>

  <Step title="Confirm the status">
    ```bash theme={null}
    curl "$ISSUER_ORIGIN/revocation-status/<revocationNonce>"
    ```

    ```json theme={null}
    { "isRevoked": true }
    ```
  </Step>
</Steps>

`GET /revocation-status/:nonce` reflects a revocation as soon as you make it. Register this route with AIR as `revocationStatusApiUrl` so verification programs that enable issuer revocation checks can use it.

To change a credential, such as a tier upgrade, revoke the old one and issue a new one.

## Token status list

The token status list is optional and off by default. It publishes revocation as one compressed bit array per partition, following the [IETF Token Status List](https://datatracker.ietf.org/doc/draft-ietf-oauth-status-list/) draft. Verifiers download a whole partition, so the issuer never learns which credential they checked, and they can cache the list. Revocation works without it; the status list improves privacy and caching.

Each credential issued with the list enabled carries a pointer to its bit:

```json theme={null}
{
  "status": {
    "status_list": {
      "idx": 12345,
      "uri": "https://issuer.example.com/statuslist/0"
    }
  }
}
```

A verifier fetches `uri`, decompresses the list, and reads the bit at `idx`: `0` is valid, `1` is revoked. The partition is served as a JWT (`application/statuslist+jwt`) signed with your partner key, and verifiers resolve the key from `/.well-known/jwt-vc-issuer`.

### Enable it

1. Apply migrations so the status list table exists (`npx mikro-orm migration:up`).
2. Set `SD_JWT_TSL_PARTITION_SIZE` to the number of credentials each partition covers. Use a multiple of 8.
3. Make sure `ISSUER_ORIGIN` is your stable public origin. It is written into the `uri` of every credential issued from then on.

<Warning>
  The partition size cannot be changed once you start issuing. `idx` and `uri` are embedded in credentials already in holders' accounts, so changing the size points them at the wrong bits. Pick it with room to grow.
</Warning>

Credentials issued before you enable the list have no `status` claim and keep relying on `GET /revocation-status/:nonce`.

### Choose a partition size

* **Larger partitions** give better privacy, because the anonymity set is everyone in the partition. They also mean fewer URLs to publish and cache, and they compress well. Every verifier downloads the whole partition to check one credential.
* **Smaller partitions** keep each download small, but narrow the anonymity set and slow down publishing as the partition count grows.

Aim for a handful of partitions over the lifetime of your credentials, not hundreds. For reference, 80,000 bits is 10 KB uncompressed and typically a few hundred bytes compressed when few credentials are revoked. If your credentials are short-lived, a size that covers about one credential lifetime of issuance works well.

### Publish

Revoking a credential does not change the published list. Publish to rebuild every partition from your database:

```bash theme={null}
curl -X POST "$ISSUER_ORIGIN/admin/publish-token-status-list" \
  -H "x-admin-api-key: $ADMIN_API_KEY"
```

Batch revocations and publish on a schedule that matches how fast revocation must take effect:

| Situation | Suggested cadence |
| - | - |
| Urgent revocation (fraud, account compromise, access credentials) | Every few minutes, or right after a revocation batch |
| Routine revocation (membership lapses, tier changes) | Hourly or nightly |
| Short-lived credentials | Daily; expiry already does most of the work |

Tell your verifiers that revocation takes up to your publish interval plus their cache time to reach them.

### Serve the list

`GET /statuslist/:partition` needs no API key. It returns `501 Not Implemented` when the status list is disabled, and `404` for a partition that has never been published. Responses are signed per request, so put the route behind a CDN or reverse-proxy cache, with a TTL no longer than your publish interval.

### Limitations

* Only valid and revoked are represented. Suspension is not supported.
* Publishing rebuilds all partitions and must not run concurrently with itself. Run it from a single scheduler.
* The status list JWT has no `exp` or `ttl` claim, so verifiers apply their own cache policy.
* There is no way to un-revoke a credential.

## Iden3 credentials

The Iden3 issuer service has `/admin/revoke` and `/revocation-status/:nonce` too, plus `/credential-status/:nonce` for non-revocation proofs. It has no token status list. See [Iden3 credentials](/products/identity/iden3-credentials).

## Related

* [Verify SD-JWT on your backend](/products/identity/verify-sd-jwt#revocation)
* [Issuance API reference](/api-reference/issuance-api)
* [Issuer backend hosting](/products/identity/backend-hosting)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.