---
title: Introspection
description: "One request that returns your project's entire contract. The most useful call in the API."
---

```http
GET /v1/introspection
```

```bash
curl https://api.zevcontent.net/v1/introspection \
  -H "Authorization: Bearer $ZEVCONTENT_KEY"
```

Works with any key kind.

## Why it exists

Documentation goes stale. This does not: it is generated from the same code
that serves every other request, and it describes **your project** rather than
the product in general.

If you are an AI agent, or writing one, this replaces reading the rest of this
site to learn *what to send*. These pages explain *why*.

## What comes back

```json
{
  "data": {
    "project":        { "name": "…", "slug": "…" },
    "release":        { "id": "rel_…", "name": "Launch", "default": true },
    "releases":       [ /* secret keys only */ ],
    "core_endpoints": [ /* /v1/content, /v1/schema, /v1/ping */ ],
    "content_types":  [ /* your types, with fields and response_shape */ ],
    "media":          { "access": "public", "urls": "permanent" },
    "rich_text":      { /* every node, mark and variant */ },
    "conventions":    { /* auth, pagination, filtering, rate limits, errors */ },
    "modules":        [ /* blog, kb, authors: endpoints, shapes, counts */ ]
  }
}
```

### `content_types`

Each type with its fields **and** a `response_shape`, which is what a response
actually contains, key by key:

```json
{
  "slug": "team-member",
  "kind": "collection",
  "fields": [ { "key": "photo", "type": "image", "required": false } ],
  "response_shape": {
    "photo": {
      "url": "string",
      "alt": "string|null",
      "width": "number|null",
      "height": "number|null",
      "content_type": "string",
      "url_expires_at": "string (ISO 8601). This URL stops working then."
    }
  }
}
```

Scalar fields map to a type string. `image` and `gallery` map to an object:
five keys you would otherwise discover by fetching content and guessing, plus
the sixth that only appears on a private project.

### `modules`

Each built-in module with its endpoints, its query parameters, its shapes, the
notes that matter, and `counts`, which is how much is in it. Modules are always
present, because their endpoints exist on every project whether or not anything
has been written.

### `conventions`

The rules that hold everywhere, stated once rather than repeated per module:
authentication, pagination, filtering (including the operator list), rate
limits (with the real numbers), and the error envelope.

Every number in it is read from the code that enforces it, so it cannot drift.

### `rich_text`

The complete document contract: block nodes, inline nodes, marks, heading
levels, callout variants, image sources, embed providers, and the rules for
each. It is static and identical for every project, and it is here anyway,
because "write a richtext value" is unanswerable without it and the caller this
endpoint exists for has no documentation open.

## Secret and publishable keys see different things

> **WARNING: `releases` is secret-key only**
>
> A non-default release's `rel_…` id is its only address, which is staging by
> obscurity, and a publishable key ships in page source. Listing release ids to it would
> hand every visitor an index of your unreleased content.
>
> Publishable keys still see `release`, the one serving the request. The
> `releases` key is **absent** rather than empty, so a client can tell "not
> allowed to see" from "there are none".

Everything else is identical. The schema is not a secret to someone already
holding a key to the project, and hiding it would make every publishable-key
integration start with a guessing game.

## Counts and privacy

Module counts report **published** content only, and the knowledge base counts
only **visible** collections and sections.

This response is readable with a publishable key. "This project has fourteen
unpublished articles" is a fact about work in progress that a stranger reading
a page source should not learn, and counting a hidden collection would
advertise navigation nobody can reach.

## Reading a specific release

```bash
curl "https://api.zevcontent.net/v1/introspection?release=rel_…" \
  -H "Authorization: Bearer $ZEVCONTENT_KEY"
```

`content_types` then describes **that** release's schema, which is the point of
a staging release: the new shape, before it is live.

The built-in modules are release-independent, so their part of the response
does not change.