API reference
Content
Reading and writing your own content types with the /v1/content and /v1/schema endpoints.
The endpoints for the types you define. For the blog, knowledge base and authors, see Built-in modules.
Fetch a type
/v1/content/{type} {type} is the type slug, exactly as content_types[].slug reports it in
introspection.
A singleton answers with its values object. A collection answers with a paginated array.
| Parameter | |
|---|---|
limit | Collections only. 1–100, default 20. Refused outside that, not clamped. |
cursor | Collections in their default order. Opaque. |
offset | Collections when sort is used. |
where[…] | Filter by the type’s own fields. See filtering. |
sort | A field key, or created_at / published_at / updated_at. - prefixes descending. |
ids | Comma-separated ent_… ids, at most 50. Returns exactly those, in the sort order. |
release | A rel_… id. Omit for the default release. |
media_urls | permanent to refuse expiring image URLs. |
curl "https://api.zevcontent.net/v1/content/team-member\
?where[role]=engineer&sort=-joined&limit=10" \
-H "Authorization: Bearer $ZEVCONTENT_KEY"
{
"data": [
{
"id": "ent_2c8RkXqGn4wVtYbMhJpDs",
"values": { "name": "Ada Lovelace", "role": "engineer", "joined": "2024-03-01" },
"published_at": "2026-08-14T09:00:00.000Z",
"updated_at": "2026-08-20T11:12:00.000Z"
}
],
"meta": {
"type": "team-member",
"kind": "collection",
"release": { "id": "rel_…", "name": "Launch", "default": true },
"limit": 10,
"has_more": false,
"next_cursor": null,
"next_offset": null
}
}
Fetch one entry
/v1/content/{type}/{entryId} By its ent_… id. Use this for a detail page when you already hold the id;
filter the listing instead when you hold a slug-like field value.
Takes release and media_urls.
Create an entry
/v1/content/{type}/entries Secret key only. Collections only.
{
"values": { "name": "Ada Lovelace", "role": "engineer" },
"publish": true
}
values is keyed by field key and validated against the type: a missing
required field is missing_required_field, a wrong type is
invalid_field_value, an unknown key is unknown_field naming the ones that
exist.
Omitting publish creates a draft. That is the right default for a
migration script or an agent, because content generated by a machine should
usually be reviewed before it is public.
Save a singleton
/v1/content/{type} Secret key only. Singletons only.
Replaces the singleton’s draft with values, creating its one entry on first
save, and publishes it in the same call with "publish": true.
It is a replace, not a merge: values is the whole object. A key you omit is
cleared, not left alone.
The schema itself
/v1/schema/content-types Secret key only. Every type definition with its fields. These are the same definitions introspection embeds, on their own endpoint for a client that wants the schema without the rest of the map.
/v1/schema/content-types/{type} Secret key only. Create or update a type and its fields. This is how a project is modelled from code rather than in the dashboard, and it is what lets an agent set one up end to end.
Two things are immutable once set, because changing them breaks every frontend already reading:
- A type’s kind (
content_type_kind_immutable). A singleton becoming a collection changes the response from an object to an array. - A field’s type (
field_type_immutable). Astringbecoming anumberchanges what every reader receives.
Everything else is editable: labels, options, order, and adding or removing fields.
Only published content is served
/v1/content serves published entries. A draft is invisible here and visible
in the dashboard, which is what lets somebody work on a page for a week without
it appearing on your site.
An entry that exists but is not published answers entry_not_published rather
than pretending it does not exist.
Updated at, Wednesday, September 2, 2026