AI Agents — Read Guide
A self-contained guide for an AI agent (or any read-only client) to fetch Blobify content directly from a customer's public bucket. Bucket reads are the canonical Blobify read path — the API is for writes.
1. The one auth call
Before reading the bucket, an agent makes one authenticated request to the Blobify API to get the read recipe:
GET /v1/orgs/{orgId}
Authorization: Bearer <api-key>The response includes everything needed to construct read URLs:
{
"id": "org_...",
"name": "Acme",
"publicBucketUrl": "https://pub-xxxx.r2.dev/acme",
"rootPrefix": "acme",
"spaces": [
{ "id": "main", "contentPrefix": "b134c4670082cad614ac2576" },
{ "id": "staging", "contentPrefix": "94f81d22c4ae3e7f1b58a0d1" }
],
"defaultLocale": "en",
"locales": [
{ "code": "en", "label": "English" },
{ "code": "is", "label": "Íslenska" }
]
}After this, the agent reads the bucket directly. No further API calls needed for navigation or content fetches.
2. Building bucket URLs
Two kinds of paths exist in the bucket:
- Unprefixed paths (schemas, asset media): live directly under
rootPrefix. - Hardened paths (content, summaries, catalogs, lookups, list-indexes): live under
_blobify/{contentPrefix}/inside the space, so they can't be enumerated without knowing the space'scontentPrefix.
unprefixed(path) = {publicBucketUrl}/{rootPrefix}/{path}
hardened(spaceId, path) = {publicBucketUrl}/{rootPrefix}/spaces/{spaceId}/_blobify/{contentPrefix}/{path}If rootPrefix is empty, omit it. If a space has no contentPrefix, omit _blobify/{contentPrefix}/.
3. Path map
Schemas (org-wide, unprefixed)
| What | Path |
|---|---|
| List of model IDs | schemas/models/index.json |
| One model's schema | schemas/models/{model}.json |
| List of block IDs | schemas/blocks/index.json |
| One block's schema | schemas/blocks/{blockId}.json |
Summaries (per space, hardened)
Lightweight per-content-item records used for listing and finding. Sharded.
| What | Path (relative to space root) |
|---|---|
| State manifest (entry point) | summaries/{state}/index.json |
| Per-model summary shard | summaries/{state}/{model}/{shard}.json |
{state} is published (default) or draft.
Content (per space, hardened)
| What | Path (relative to space root) |
|---|---|
| Published content doc | content/{model}/{contentId}/published.json |
| Draft content doc | content/{model}/{contentId}/draft.json |
| Version history index | content/{model}/{contentId}/history/index.json |
Catalogs (per space, hardened — split shared + per-locale)
| What | Path |
|---|---|
| Shared (non-localizable fields) | catalogs/{model}.{state}.json |
| Per-locale (localizable fields) | catalogs/{model}.{locale}.{state}.json |
Lookups (fast field-index shards)
| What | Path |
|---|---|
| Non-localized field shard | lookups/{state}/{model}/{field}/{shard}.json |
| Localized field shard | lookups/{state}/{model}/{field}/{locale}/{shard}.json |
List indexes (sorted/paginated lists)
| What | Path |
|---|---|
| Manifest | list-indexes/{state}/{model}/{indexId}/{locale}/index.json |
| Page | list-indexes/{state}/{model}/{indexId}/{locale}/p00000.json |
Assets
| What | Path | Hardened? |
|---|---|---|
| Asset catalog | spaces/{spaceId}/assets/catalog.json | yes |
| Asset media file | spaces/{spaceId}/assets/media/{assetId}.{ext} | no |
4. Common agent flows
Find a content item by title
- Fetch
summaries/{state}/index.json. - For each candidate model, fetch the listed summary shards for the relevant locale.
- Substring-match on the localized
title(orname/slug) field.
Find a content item by slug
If slug is an indexed field, use the lookup shard directly — O(1) instead of scanning summaries:
GET {space-root}/lookups/published/page/slug/en/{shard}.jsonThe lookup shard maps slug → contentId.
Get full content
Once you have a contentId:
GET {space-root}/content/{model}/{contentId}/published.jsonResolve a reference
Reference fields store { model, id }. To resolve, fetch the referenced summary (preferred, smaller) or the full content doc.
5. Localization rules
- Translatable fields are stored as
{ locale: value }objects. - Non-translatable fields are stored as direct values.
- The
blocksfield itself is never localized — but fields inside a block instance can be. - Plain-text rich text values are strings, not localized objects, even inside translatable rich-text trees (localization is done at the field level, with one full AST per locale).
When in doubt, fetch the model schema and check translatable: true.
6. Draft vs published
- Default agent reads should target
state: "published". - Drafts (
state: "draft") require the same API key (private bucket) or a deliberately exposed staging deployment. - The API key passed to
getOrganizationdoesn't affect what state you read — that's chosen by the URL you construct.
7. Caching
- All JSON files served by Blobify are mutable (
Cache-Control: no-store, must-revalidate) — fetch fresh. - Asset media is immutable (
max-age=31536000, immutable) — safe to cache aggressively. - For serverless/SSG, tag fetches by content ID / model / locale and invalidate via the webhook payload's
revalidationTags.
8. Worked example
Goal: read the published English version of the page with slug map-of-westfjords in space main.
# 1. Recipe
GET /v1/orgs/{orgId} (auth'd)
→ publicBucketUrl, rootPrefix, spaces[].contentPrefix
# 2. Resolve slug → contentId via lookup
GET {bucket}/{rootPrefix}/spaces/main/_blobify/{contentPrefix}/lookups/published/page/slug/en/{shard}.json
→ { "map-of-westfjords": "pg_abc123", … }
# 3. Fetch full content
GET {bucket}/{rootPrefix}/spaces/main/_blobify/{contentPrefix}/content/page/pg_abc123/published.json
→ full content doc with resolved references and assetsThat's the whole read flow — one auth call, then plain HTTPS GETs against a public CDN.