Schema: /v2/schema
Your project's models, fields and relations as JSON, and as ready-made TypeScript types, for code generation.
Two routes describe the content model of the project your key belongs to. GET /v2/schema returns every model, field and relation as JSON, with a checksum. GET /v2/schema/types returns the same models as TypeScript interfaces.
capa-codegen reads these routes when you run it with a legacy key. See Codegen.
Keys and hosts
Both routes take a legacy key (pk_, sk_, or an older key with no prefix) in x-api-key. Any permission reads them, read included. A cap_ key is refused with 401 {"error":"Invalid API key"}, as on every legacy route. See Keys and scopes.
Send them to https://api.capacms.com. They are not served through the CDN, unlike /v2/api and /v3/api.
| Status | Body | When |
|---|---|---|
| 401 | {"error":"API key required"} | no x-api-key header |
| 401 | {"error":"Invalid API key"} | an unknown, inactive or cap_ key |
| 402 | {"success":false,"error":"…"} | the project has no active subscription |
| 500 | {"error":"Failed to fetch schema","message":"…"} | something failed on our side |
GET /v2/schema
curl https://api.capacms.com/v2/schema -H "x-api-key: $CAPA_KEY"{
"models": [
{
"id": "2c8b0a11-7f4e-4d2a-9c31-51a2b3c4d5e6",
"namespace": "blog_post",
"modelName": "Blog Post",
"isSingleInstance": false,
"allowDynamicFields": false,
"category": { "id": "6a1d2c3b-4e5f-4a7b-8c9d-0e1f2a3b4c5d", "name": "Editorial" },
"fields": [
{
"id": "41a2b3c4-d5e6-4f70-8192-a3b4c5d6e7f8",
"namespace": "title",
"name": "title",
"type": "string",
"arrayType": null,
"required": true,
"relationRef": null,
"enumValues": null,
"directEmbed": false,
"sortOrder": 0
},
{
"id": "9e8d7c6b-5a4f-4e3d-2c1b-0a9f8e7d6c5b",
"namespace": "author",
"name": "Author",
"type": "relation",
"arrayType": null,
"required": false,
"relationRef": "author",
"enumValues": null,
"directEmbed": false,
"sortOrder": 1
}
]
}
],
"relations": [
{ "fromModel": "blog_post", "fromField": "author", "toModel": "author", "type": "one" }
],
"checksum": "3f6c1b4e2a714d0e",
"generatedAt": "2026-10-01T14:02:11.004Z"
}models holds every model of the project that is not deleted, sorted by modelName. Each model's fields are in their sortOrder.
category is null for a model with no category. arrayType is set only on an array field, relationRef only on a relation, and enumValues only on an enum with values.
relations lists every relation field once. type is one for a relation field and many for an array of relations. toModel is the target model's namespace, or null when the target no longer exists.
The checksum
checksum is 16 hexadecimal characters, a hash of models and relations. It changes whenever anything in models or relations does. The same value is the response's ETag, so you can ask whether anything moved without downloading the schema again:
curl -i https://api.capacms.com/v2/schema \
-H "x-api-key: $CAPA_KEY" \
-H 'If-None-Match: "3f6c1b4e2a714d0e"'
# HTTP/1.1 304 Not ModifiedThe response is sent with Cache-Control: private, max-age=60. It is specific to your key's project, so no shared cache stores it.
GET /v2/schema/types
curl https://api.capacms.com/v2/schema/types -H "x-api-key: $CAPA_KEY" -o capa-types.tsThe body is TypeScript, sent as text/plain with Content-Disposition: attachment; filename="capa-types.ts". It starts with four shared interfaces, CapaImage, CapaVideo, CapaFile and CapaInstance, then one interface per model, named after its namespace in PascalCase:
export interface BlogPost {
title: string;
author?: Author;
tags?: string[];
views?: number;
}A required field has no ?. Field types map like this:
| Field type | TypeScript |
|---|---|
string, markdown, html, code, color, date | string |
number | number |
true_false | boolean |
image, video, file | CapaImage, CapaVideo, CapaFile |
enum with values | a union of the values, such as 'draft' | 'live' |
relation | the target model's interface, or unknown when the target no longer exists |
array | the item type followed by [] |
| anything else | unknown |
Which to use
For new code on /api/, run capa-codegen with a cap_ key instead. It writes the same interfaces from the key's GraphQL schema, with no project id. See Codegen.