Manifest + Bible Source (b2_manifest)
This source pulls structured content from any HTTPS-reachable folder layout:
<base_url>/manifest.json ← index of entries
<base_url>/<slug>/bible_public.json ← the brief / data for that entry
<base_url>/<slug>/portrait.png (+ other assets, referenced inside the bible)
It's brand-agnostic — each source instance points at one bucket. Multiple brands can each have their own source pointing at their own bucket. The schema is permissive: typed fields are extracted into queryable columns, anything else is preserved in metadata for AI prompts.
When to use this source
Use it if you (or your team) already:
- Publish a JSON "brief" file per entry to a public bucket / CDN
- Want a stable, file-backed pipeline (vs database-backed like Supabase)
- Need rich, structured content (multi-language, nested fields) rather than flat title+body
Other source types:
- Supabase — content lives in a Postgres table you control
- Notion — content lives in a Notion database
- GitHub — README/markdown files in a repo
What you need before setting it up
1. A public-readable HTTPS host
Examples that work:
- DigitalOcean Spaces with custom domain (e.g.
cdn.amplicast.io) - Backblaze B2 public bucket
- AWS S3 public bucket / CloudFront distribution
- Any static-file host you control (your own CDN, GitHub Pages, etc.)
The base URL is whatever resolves your manifest. Examples:
https://cdn.example.com ← custom domain
https://my-bucket.nyc3.cdn.digitaloceanspaces.com ← raw DO Spaces CDN
v1 only supports public-read. Private buckets (with access keys) on the roadmap.
2. A manifest.json at the base URL
The manifest is the index — it tells Amplicast which entries exist. Place at <base_url>/manifest.json (or override with the manifest_path config).
Full-URL style (preferred, every asset address resolved up-front):
{
"version": "1.0",
"generated_at": "2026-05-26T19:41:45.836Z",
"character_count": 1,
"characters": [
{
"slug": "entry-001-v1",
"name": "Entry Name (V1)",
"display_name": "Display Name",
"tagline_en": "short EN tagline",
"tagline_de": "kurze DE tagline",
"published_at": "2026-05-24T15:05:57Z",
"updated_at": "2026-05-26T18:41:31Z",
"bible_url": "https://cdn.example.com/entry-001-v1/bible_public.json",
"portrait_url": "https://cdn.example.com/entry-001-v1/portrait.png",
"additional_portraits": [
"https://cdn.example.com/entry-001-v1/portrait-alt-2.png"
]
}
]
}
Placeholder-style (simpler, fewer fields):
{
"version": "1",
"entries": [
{
"slug": "spring-launch",
"updated_at": "2026-05-21T14:00:00Z"
}
]
}
Field reference:
| Field | Required | Notes |
|---|---|---|
characters OR entries (array) | one of them | Array of objects. Either key name is accepted. |
entry.slug | required | Unique identifier; also the folder name where the bible lives |
entry.bible_url | optional | Full URL to the bible JSON. If absent, falls back to <base>/<slug>/<bible_filename> (default bible.json). |
entry.updated_at | recommended | Stored in metadata.bible_updated_at so future incremental sync can detect changes |
entry.display_name / entry.name | optional | Used as fallback for title if bible doesn't have one |
entry.published_at | optional | Used as fallback for publish date |
entry.portrait_url | optional | Used as fallback for featured image |
entry.additional_portraits (array) | optional | Folded into media list |
3. A bible JSON file per entry
Per the manifest's bible_url (Tirida) or by convention at <base>/<slug>/<bible_filename>.
Rich-shape bible (multiple known fields → typed columns + structured RawContent summary):
{
"slug": "entry-001-v1",
"name": "Entry Name (V1)",
"display_name": "Display Name",
"tagline_en": "short EN tagline",
"tagline_de": "kurze DE tagline",
"vibe_personality_en": "<short paragraph: how this entry's personality reads in EN>",
"vibe_personality_de": "<short paragraph: how this entry's personality reads in DE>",
"vibe_core_traits": ["trait-1", "trait-2", "trait-3"],
"vibe_core_traits_de": ["eigenschaft-1", "eigenschaft-2", "eigenschaft-3"],
"vibe_surprise_facts": ["<surprising fact about this entry>"],
"vibe_surprise_facts_de": ["<überraschender Fakt über diesen Eintrag>"],
"tribe_id": "sample-tribe-id",
"tribe_display_name": "Sample Tribe / Group / Series",
"size_category": "<short size descriptor>",
"size_estimate_cm": 0,
"habitat_keywords": {
"climate": "<one-word climate>",
"terrain": "<one-word terrain>",
"signature_features": ["<feature 1>"]
},
"diet_signature_foods": ["<food 1>"],
"primary_color": "#000000",
"color_palette": [
{"hex": "#000000", "name": "Color Name", "part": "body", "role": "primary"}
],
"portrait_url": "https://cdn.example.com/entry-001-v1/portrait.png",
"additional_portraits": ["https://cdn.example.com/entry-001-v1/portrait-alt-2.png"],
"_meta": {
"version": "1.1",
"published_at": "2026-05-24T15:05:57Z",
"updated_at": "2026-05-26T18:41:31Z"
}
}
The bible can be any shape. The fields above are the ones Amplicast knows how to extract into typed columns. Everything else is preserved in
metadata.biblefor AI prompts to read.
Minimal-shape bible (just title + body + tags — no structured fields):
{
"slug": "spring-launch",
"title": "Spring Launch — Day 1",
"body": "Long-form copy used as raw content for AI transforms.",
"tags": ["launch", "spring"],
"brand": "examplebrand",
"assets": [
{ "path": "assets/hero.png", "role": "featured" },
{ "path": "assets/c1.png", "role": "carousel" }
],
"publish_at": "2026-05-25T09:00:00Z"
}
4. Asset files (portraits, scene cards, etc.)
Referenced from the bible by URL (Tirida) or relative path (placeholder). Asset URLs must be on a publicly-fetchable host. Relative paths in the placeholder assets[].path are resolved against <base>/<slug>/.
How bible fields map to a content item
Amplicast extracts specific fields into typed columns (so they're queryable), then stashes the entire bible in metadata.bible so AI transforms have access to everything else.
| Item column | Source (first non-empty wins) |
|---|---|
title | bible.display_name → bible.name → bible.title → manifest entry.display_name/name → slug |
content_subtype (brand) | bible.brand → source config default_brand |
featured_image | bible.portrait_url → bible.featured_image → bible.featured_image_url → bible.cover_image → manifest entry.portrait_url |
media_urls | bible.additional_portraits + bible.media_urls + bible.gallery + bible.assets[].path + manifest entry.additional_portraits (deduped; featured excluded) |
tags | bible.tags + bible.vibe_core_traits + bible.tribe_display_name (deduped) |
published_at | bible.publish_at → bible.published_at → bible._meta.published_at → bible._meta.updated_at → manifest entry.published_at |
raw_content | structured markdown summary auto-built from the most common bible fields (taglines, personalities, traits, surprising facts, tribe, habitat, diet, color). See "RawContent generation" below. |
metadata.bible | the entire bible JSON object — every field preserved for downstream prompts |
metadata.manifest_entry | the matching manifest entry object — preserved |
metadata.slug | the slug |
metadata.bucket_base_url | source's base URL |
metadata.bible_updated_at | manifest entry's updated_at (for incremental change detection) |
metadata.target_platforms | bible.target_platforms → source config default_target_platforms |
RawContent generation
The fetcher builds a structured markdown summary from common bible fields. This is what the AI sees during caption / copy transforms — so it's optimized for readability and to expose multilingual content (EN + DE) side by side.
Example output for a rich-shape bible (placeholders show the structure; real content is whatever the bible holds):
# Display Name
_(Entry Name (V1))_
**Tagline (EN):** <short EN tagline>
**Tagline (DE):** <kurze DE tagline>
## Personality (EN)
<short paragraph: how this entry reads in EN>
## Personality (DE)
<short paragraph: how this entry reads in DE>
**Core traits (EN):** trait-1, trait-2, trait-3
**Core traits (DE):** eigenschaft-1, eigenschaft-2, eigenschaft-3
## Surprising facts (EN)
- <surprising fact>
## Surprising facts (DE)
- <überraschender Fakt>
**Tribe:** Sample Tribe / Group / Series _(id: sample-tribe-id)_
**Size:** <descriptor> (~N cm)
## Habitat
- Climate: <one word>
- Terrain: <one word>
- <signature feature 1>
## Diet
- <food 1>
**Primary color:** #000000
If your bible has a plain body or description field, it's appended at the end. So the placeholder shape ({title, body, …}) also produces a sensible RawContent.
Source config
Settings you pass when creating the source (in the dashboard or via POST /api/sources):
| Config key | Type | Default | Purpose |
|---|---|---|---|
base_url | string | required | HTTPS prefix that resolves to the manifest and asset files |
manifest_path | string | manifest.json | Relative path to the manifest from base_url |
bible_filename | string | bible.json | Default filename to look for at <base>/<slug>/<filename> when a manifest entry has no bible_url. (Some setups use bible_public.json to distinguish from a private/admin shape.) |
default_brand | string | none | Set as content_subtype when the bible has no brand field |
default_target_platforms | array of strings | none | Set as metadata.target_platforms when the bible has no target_platforms. Use platform account_key strings (see Channels). |
limit | number | 100 | Max entries to fetch per sync (in case the manifest grows large) |
Example source config
{
"base_url": "https://cdn.example.com",
"bible_filename": "bible_public.json",
"default_brand": "examplebrand",
"default_target_platforms": ["examplebrand_instagram", "examplebrand_tiktok", "examplebrand_youtube"]
}
How sync works
- Amplicast fetches
<base_url>/<manifest_path>and parses the entries - For each entry, calls
entry.bible_urlif present, else<base>/<slug>/<bible_filename> - Bible content is mapped into a content item; the full bible survives in
metadata.bible - The content item flows through the normal pipeline (AI transforms → review → publish)
Failure handling:
- Missing
manifest.json→ source goes intoerrorstate (check Sources page for the error message) - Missing or malformed bible for a single slug → that slug is skipped, others continue (logged)
Tips
- Use
updated_aton every manifest entry. Without it, Amplicast can't tell what changed and re-processes everything each sync. - Keep slugs URL-safe. They become folder names and may appear in URLs. Stick to
[a-z0-9-]. - Test with one entry first. Set publish mode =
Drafton the source, push one bible, verify the resulting content item looks right, then iterate. - The bible is your source of truth. If you change a field, bump the manifest entry's
updated_atto trigger a re-sync. - Bible can be ANY shape — only specific known field names get extracted into typed columns. Everything else still lands in
metadata.biblefor AI prompts to read.
Roadmap
- Private buckets with access keys (B2 native + S3-compatible)
- Per-platform copy variants inside the bible (
caption_instagram,caption_tiktok, …) - Webhook-driven instant sync (instead of polling on the sync schedule)
- Bulk re-sync UI
- Scene-card / tagline-card auto-discovery from
asset_countmetadata