# BriefTen — Agent configuration reference

BriefTen is a multi-tenant newsletter platform. Each newsletter is an AI-generated daily briefing: Claude reads articles fetched from specific domains via Tavily, picks the best stories, writes a summary, and sends it to subscribers on a schedule. This page is the machine-readable reference for generating a newsletter's content configuration from a natural-language description.

If you are an AI agent: read the schema, emit a single JSON document, output nothing else. If you are a human: scroll to the bottom for a prompt template you can paste into your LLM of choice.

## Workflow

1. A human user describes the newsletter they want.
2. An AI agent reads this page and emits a valid JSON document matching the schema below.
3. The user pastes the JSON into BriefTen's admin at **Content → Import agent config**.
4. BriefTen validates the JSON, replaces the newsletter's topics, watchlist, sources, tickers, and prompt with the imported values.
5. The user then configures sender identity, schedule, and visual design in BriefTen's admin UI.

Only the five content areas above are imported. Design (template, colors, layout) and delivery (sender address, cron schedule, subscriber list) are not part of this import and are always configured by the human user directly in the admin.

## Schema (version 1)

Emit a single JSON object with this shape:

```json
{
  "$schema": "https://brieften.com/schema/agent-config/v1.json",
  "version": 1,
  "newsletter": {
    "name": "string, optional",
    "prompt": "string, optional",
    "topics": [
      { "name": "string, required", "note": "string, optional" }
    ],
    "watchlist": [
      { "name": "string, required", "note": "string, optional" }
    ],
    "sources": [
      {
        "name":        "string, required",
        "domain":      "string, required",
        "query":       "string, required",
        "days":        "integer 1-30, optional, default 1",
        "max_results": "integer 1-30, optional, default 10",
        "note":        "string, optional"
      }
    ],
    "tickers": [
      {
        "symbol":       "string, required",
        "name":         "string, required",
        "ticker_group": "string, optional",
        "note":         "string, optional"
      }
    ]
  }
}
```

Output only the JSON object. Do not wrap it in Markdown code fences. Do not add commentary.

## Field reference

### `newsletter.name` — string, optional

The display name of the newsletter — "The Daily Fintech Briefing", "Morning Read", "AI Research Weekly". Shown in email headers, subject lines, and the public newsletter directory. Keep it clear and specific. If omitted, BriefTen keeps the name the user picked when they created the newsletter.

### `newsletter.prompt` — string, optional

The prompt is a **literal template string** sent verbatim to Claude at send time. BriefTen substitutes four placeholders right before calling Claude:

- `{TOPICS}` — the topic list, one per line
- `{WATCHLIST}` — watchlist entities, one per line
- `{RECENT_HEADLINES}` — headlines from the last 5 successful runs (for deduplication)
- `{ARTICLES}` — the full fetched article pool Claude has to choose from

Without `{ARTICLES}`, Claude never sees the articles. Without the JSON output contract, Claude doesn't know what shape to return. A voice/tone paragraph alone is not a working prompt — it's a briefing. **Use the canonical template below, filling in only the voice/audience/tone section at the top.** Everything else is required scaffolding.

**Safety net:** if you emit a prompt that omits `{ARTICLES}`, BriefTen automatically wraps whatever you wrote with the default scaffolding so the pipeline doesn't silently fail. But including the full template yourself gives the user more control and makes it clear what Claude will receive.

If you omit the `prompt` field entirely, BriefTen uses its default.

**Canonical template** — copy verbatim, replace only the italicized first paragraph with your voice/audience/tone:

```
[VOICE / AUDIENCE / TONE paragraph: who the newsletter is for, what voice
to use, how stories should feel. 2–4 sentences. This is the only section
an agent should customize.]

Below is a collection of articles gathered this morning. Select the best stories, prioritized in this order:
{TOPICS}

Stories mentioning these should be bumped ahead of generic coverage of the same topic:
{WATCHLIST}

The following stories have already been covered in recent issues. Do not repeat them or any substantially similar story:
{RECENT_HEADLINES}

For each story, the JSON summary field must contain TWO parts joined by a blank line:
  PART 1 — A 3–5 sentence analysis paragraph with the key facts, your honest read on what it means, and where warranted, a strategic observation about what it signals.
  PART 2 — One line starting with **Why it matters:** followed by a summary of 10 words or less. Be concrete.

Join the two parts with a literal "\n\n" (a blank line) inside the summary string. Do NOT put the "Why it matters" text in a separate JSON field — it must be the last part of the summary string.

Also assign each story:
- A 1–2 word category (e.g. "Markets", "Policy", "Tech", "Deals", "Earnings")
- A punchy, compelling headline (no clickbait)

Also generate a short, compelling subject line for the email (max 60 characters).

If the feed is thin or low quality, do your best with what is available. You MUST always respond with the JSON structure below. Never explain, refuse, or write prose outside the JSON. If there is nothing usable, return an empty stories array.

Your response must be valid JSON only — no markdown fences, no commentary, no other text before or after.

{
  "subject": "Subject line for the email",
  "stories": [
    {
      "headline": "Story Headline Here",
      "summary": "Three to five sentences of analysis.\n\n**Why it matters:** Ten words or less.",
      "url": "https://original-article-url.com",
      "source": "Publication Name",
      "category": "Markets"
    }
  ]
}

Articles:
{ARTICLES}
```

Markdown emphasis in Claude's output is rendered in the final email: `**bold**` becomes bold, `*italic*` becomes italic, and `[text](url)` becomes a link. Paragraph breaks (a blank line, `\n\n`) render as paragraph spacing.

### `newsletter.topics` — array of objects

A priority list. Claude uses this to rank stories — a story touching a top topic is more likely to be included and written about in depth. Order matters; put the most important topic first.

Each item has:

- `name` (required) — the topic itself. Short noun phrase: "Stablecoins", "AI model releases", "Supreme Court decisions".
- `note` (optional) — admin-only context. Not sent to Claude. Use it to remind yourself why a topic is on the list.

Recommended: 3–6 topics, ordered by importance.

### `newsletter.watchlist` — array of objects

Entities that should be bumped ahead of generic coverage of the same topic — specific companies, people, products, agencies, or geographic regions the newsletter's audience cares about. Optional but high-signal when the newsletter is about a specific beat.

Each item has:

- `name` (required) — e.g. "Federal Reserve", "OpenAI", "Ukraine".
- `note` (optional) — admin-only.

Recommended: 5–15 items.

### `newsletter.sources` — array of objects

Where articles come from. Each source is a domain-restricted search run via Tavily. At send time, BriefTen runs every source's query, deduplicates results, and hands the combined pool to Claude.

Each source has:

- `name` (required) — human-readable source name for the email footer: "PYMNTS", "The Block".
- `domain` (required) — bare domain without protocol: "pymnts.com", "theblock.co". No `https://`, no path.
- `query` (required) — Tavily search text. Broad topical terms work better than hyper-specific phrases. For aggregators ("drudgereport.com", "memeorandum.com") use a broad query like "top news today" since they have minimal body text to index on.
- `days` (optional, 1–30, default 1) — how many days back to search. For a daily newsletter, `1`. For a weekly, `7`.
- `max_results` (optional, 1–30, default 10) — max articles returned from this source per run.
- `note` (optional) — admin-only.

Recommended: 4–10 sources. Pick domains that both publish regularly AND have meaningful article body text (not just aggregator link lists).

### `newsletter.tickers` — array of objects

Optional. If the newsletter covers markets, list ticker symbols here to render a market snapshot block at the top of each issue. Data comes from Finnhub. Only shown when the Market snapshot toggle is on in the newsletter's design settings (on by default).

Each ticker has:

- `symbol` (required) — ticker symbol: "COIN", "AAPL", "BTC-USD".
- `name` (required) — human label: "Coinbase", "Apple", "Bitcoin".
- `ticker_group` (optional) — subheading for grouped rendering: "Crypto", "Big Tech", "Indices". When Group tickers by category is enabled, tickers are organized under these subheaders.
- `note` (optional) — admin-only.

If the newsletter isn't market-focused, omit the field or pass an empty array.

## Required fields and minimums

BriefTen enforces two minimums so an imported newsletter is runnable on save:

- `newsletter.sources` must contain at least **1** item. Without sources, Tavily has nothing to fetch and the pipeline fails.
- `newsletter.topics` must contain at least **1** item. Without topics, Claude has no priority guidance for story selection.

`newsletter.watchlist`, `newsletter.tickers`, `newsletter.prompt`, and `newsletter.name` are all optional. Pass empty arrays or omit keys you don't want to populate.

## Example configurations

### Daily fintech briefing

```json
{
  "version": 1,
  "newsletter": {
    "name": "The Daily Fintech Briefing",
    "topics": [
      { "name": "Payments and stablecoins" },
      { "name": "Crypto regulation" },
      { "name": "Digital banks and BaaS" },
      { "name": "Market infrastructure" },
      { "name": "Venture funding" }
    ],
    "watchlist": [
      { "name": "Circle" },
      { "name": "Coinbase" },
      { "name": "Stripe" },
      { "name": "SEC" },
      { "name": "Federal Reserve" },
      { "name": "Ethereum" }
    ],
    "sources": [
      { "name": "PYMNTS",       "domain": "pymnts.com",       "query": "payments and fintech news",          "days": 1, "max_results": 10 },
      { "name": "The Block",    "domain": "theblock.co",      "query": "crypto and DeFi markets",            "days": 1, "max_results": 10 },
      { "name": "CoinDesk",     "domain": "coindesk.com",     "query": "crypto regulation and stablecoins", "days": 1, "max_results": 10 },
      { "name": "Fintech News", "domain": "fintechnews.org",  "query": "fintech news today",                 "days": 1, "max_results": 10 },
      { "name": "Axios Fin",    "domain": "axios.com",        "query": "fintech banking payments",           "days": 1, "max_results": 8 }
    ],
    "tickers": [
      { "symbol": "COIN",    "name": "Coinbase",     "ticker_group": "Crypto exchanges" },
      { "symbol": "MSTR",    "name": "MicroStrategy","ticker_group": "Crypto exchanges" },
      { "symbol": "BTC-USD", "name": "Bitcoin",      "ticker_group": "Digital assets" },
      { "symbol": "ETH-USD", "name": "Ethereum",     "ticker_group": "Digital assets" }
    ],
    "prompt": "You are writing The Daily Fintech Briefing for founders, operators, and investors in payments, crypto, and digital banking. Voice: sharp, concise, quietly skeptical of hype. Each story should include enough context that a reader outside the specific sub-domain understands the stakes, plus a strategic take on what it signals for the industry.\n\nBelow is a collection of articles gathered this morning. Select the best stories, prioritized in this order:\n{TOPICS}\n\nStories mentioning these should be bumped ahead of generic coverage of the same topic:\n{WATCHLIST}\n\nThe following stories have already been covered in recent issues. Do not repeat them or any substantially similar story:\n{RECENT_HEADLINES}\n\nFor each story, the JSON summary field must contain TWO parts joined by a blank line:\n  PART 1 — A 3–5 sentence analysis paragraph with the key facts, your honest read on what it means, and where warranted, a strategic observation about what it signals.\n  PART 2 — One line starting with **Why it matters:** followed by a summary of 10 words or less.\n\nJoin the two parts with a literal \"\\n\\n\" inside the summary string. Do NOT put the Why-it-matters text in a separate JSON field.\n\nAlso assign each story a 1–2 word category and a punchy headline. Also generate a subject line (max 60 characters). If the feed is thin, do your best with what's available.\n\nYour response must be valid JSON only — no markdown fences, no commentary.\n\n{\n  \"subject\": \"Subject line\",\n  \"stories\": [\n    { \"headline\": \"...\", \"summary\": \"Analysis.\\n\\n**Why it matters:** Ten words.\", \"url\": \"...\", \"source\": \"...\", \"category\": \"Markets\" }\n  ]\n}\n\nArticles:\n{ARTICLES}"
  }
}
```

### Weekly AI research roundup

```json
{
  "version": 1,
  "newsletter": {
    "name": "AI Research Weekly",
    "topics": [
      { "name": "Foundation model releases and benchmarks" },
      { "name": "Interpretability and alignment research" },
      { "name": "Robotics and embodied AI" },
      { "name": "Open-source model ecosystems" },
      { "name": "AI policy and regulation" }
    ],
    "watchlist": [
      { "name": "OpenAI" }, { "name": "Anthropic" }, { "name": "Google DeepMind" },
      { "name": "Meta AI" }, { "name": "Mistral" }, { "name": "Hugging Face" },
      { "name": "NIST" }, { "name": "EU AI Act" }
    ],
    "sources": [
      { "name": "Anthropic blog",      "domain": "anthropic.com",         "query": "research safety interpretability", "days": 7, "max_results": 8 },
      { "name": "OpenAI blog",         "domain": "openai.com",            "query": "research releases",                 "days": 7, "max_results": 8 },
      { "name": "Import AI",           "domain": "importai.substack.com", "query": "AI news",                           "days": 7, "max_results": 10 },
      { "name": "MIT Tech Review",     "domain": "technologyreview.com",  "query": "artificial intelligence",           "days": 7, "max_results": 10 },
      { "name": "arXiv announcements", "domain": "arxiv.org",             "query": "large language models",             "days": 7, "max_results": 12 }
    ]
  }
}
```

## Prompt template for human users

If you are a human reading this page: copy the block below into Claude, ChatGPT, or your AI assistant of choice. Replace the bracketed line with your newsletter description. The agent should output a single JSON document you can paste into BriefTen's import UI.

```
You are generating a BriefTen newsletter configuration.

Read the schema and field reference at https://brieften.com/for-agents.md

The user wants this newsletter:
[describe your newsletter in 1-3 sentences: topic, tone, audience,
 and delivery frequency if relevant]

Output a single valid JSON document matching schema version 1. Include:
- A clear, specific name
- 3-6 prioritized topics (ordered by importance)
- 5-15 watchlist items (specific entities, companies, people, or regions)
- 4-10 sources with real domain-restricted queries — prefer domains that
  publish meaningful article text, not just aggregator link lists
- Tickers only if the newsletter covers markets
- A prompt built from the canonical template in the "Field reference"
  section. Replace only the voice/audience/tone paragraph at the top;
  keep every placeholder ({TOPICS}, {WATCHLIST}, {RECENT_HEADLINES},
  {ARTICLES}) and the JSON output contract verbatim. Or omit the prompt
  entirely to use BriefTen's default.

Do not wrap the JSON in Markdown code fences. Do not add commentary.
Output only the JSON object.
```

## Common validation errors

| Error | Fix |
|---|---|
| "At least one source is required" | Add at least one object to `newsletter.sources`. |
| "At least one topic is required" | Add at least one object to `newsletter.topics`. |
| "Source N is missing 'domain'" | Every source needs a bare domain like `"example.com"`. No `https://`, no path. |
| "Unsupported schema version X" | Set `"version": 1`. Later schema versions will be announced on this page. |
| "Missing 'newsletter' object" | Wrap all content fields in a top-level `"newsletter"` key. |
| "Newsletter name cannot be empty" | Either omit `name` entirely, or provide a non-empty string. Empty strings are rejected so imports can't accidentally clear a name. |

---

**For agents:** this page's schema and field reference is the canonical source of truth for BriefTen newsletter imports. If the user describes changes to an existing newsletter, emit a complete new configuration rather than a patch — BriefTen imports are full replacements of the five content areas, not merges.
