YAML Schema Reference¶
Complete reference for the community config.yaml file format.
Top-Level Fields¶
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
id | string | Yes | - | Unique identifier (kebab-case: [a-z0-9]+(-[a-z0-9]+)*) |
name | string | Yes | - | Display name |
description | string | Yes | - | Short description of the community/tool |
status | enum | No | available | One of: available, beta, coming_soon |
system_prompt | string | No | Built-in default | Custom system prompt template |
cors_origins | list[string] | No | [] | Allowed CORS origins for widget embedding |
anthropic_api_key_env_var | string | No | Platform key | Env var name for the community's own Claude Platform key |
openrouter_api_key_env_var | string | No | Platform key | Env var name for the community's own OpenRouter key |
default_model | string | No | claude-haiku-4-5 | Default model: one of the offered models |
default_model_provider | string | No | None | OpenRouter-only provider routing hint; ignored on the Claude Platform |
reasoning_effort | enum | No | high | How hard the assistant thinks: none, low, medium, high, xhigh or max |
model_instructions | map | No | {} | Extra system-prompt text for particular models |
enable_page_context | boolean | No | true | Enable page context tool for widget embedding |
maintainers | list[string] | No | [] | Community maintainer GitHub usernames |
documentation | list | No | [] | Documentation sources |
github | object | No | null | GitHub sync configuration |
citations | object | No | null | Paper/citation search config |
public_feeds | object | No | null | Opt-in public read-only JSON feeds (FAQ, citations) |
docstrings | object | No | null | Code docstring extraction config |
mailman | list | No | [] | Mailing list archive configs |
faq_generation | object | No | null | LLM-based FAQ generation config |
sync | object | No | null | Per-type cron sync schedules |
budget | object | No | null | Per-community spending limits |
discourse | list | No | [] | Discourse forum configs |
widget | object | No | null | Widget display configuration (title, greeting, suggestions) |
extensions | object | No | null | Extension points (plugins, MCP) |
links | object | No | null | External links (homepage, documentation, repository, demo) |
id¶
The community identifier used for API routing, storage, and CLI commands.
- Must be kebab-case: lowercase letters, numbers, and hyphens
- No leading or trailing hyphens
- Examples:
hed,bids,eeg-lab,my-tool-v2
id: hed # Valid
id: my-tool # Valid
id: MyTool # Invalid - uppercase
id: my_tool # Invalid - underscore
id: -my-tool # Invalid - leading hyphen
system_prompt¶
Template string for the LLM system prompt. Supports placeholders that are substituted at runtime:
| Placeholder | Substitution |
|---|---|
{name} | Community display name |
{description} | Community description |
{repo_list} | Formatted list of GitHub repos (from github.repos) |
{paper_dois} | Formatted list of tracked DOIs (from citations.dois) |
{preloaded_docs_section} | Content of preloaded documents |
{available_docs_section} | List of on-demand documents available via tool |
{page_context_section} | Page context instructions (if enable_page_context: true) |
{additional_instructions} | Extra instructions passed at creation time |
system_prompt: |
You are a technical assistant for {name}.
{description}
{preloaded_docs_section}
{available_docs_section}
{page_context_section}
If omitted, a built-in default prompt is used that includes the community name, description, and standard tool usage instructions.
cors_origins¶
Allowed CORS origins for widget embedding. Required if you plan to embed the widget on your website.
- Must include scheme (
https://orhttp://) - Supports wildcard subdomains (
*.example.org) - No path, query, or fragment allowed
- Max 255 characters per origin
cors_origins:
# Production website
- https://myproject.org
- https://www.myproject.org
# Development/preview environments
- https://*.myproject.pages.dev # Cloudflare Pages previews of your project
- http://localhost:3000 # Local development
# Subdomain wildcard
- https://*.myproject.org # Matches docs.myproject.org, etc.
CORS Best Practices
- Only add origins you control
- Never put a wildcard on a shared hosting domain:
https://*.pages.devorhttps://*.vercel.appauthorizes every site on that platform, including anyone else's - Use specific domains when possible
- Wildcards only for preview environments
- Never use
*alone
anthropic_api_key_env_var¶
Environment variable name holding your community's own Claude Platform key. This enables per-community cost attribution.
Setup on the server:
Without a community key, costs are billed to the platform's shared key with shared rate limits.
Your Anthropic key pays for Claude models only
The Amazon Bedrock models (GPT-6 Luna, Qwen3 Next and gpt-oss-120b) always run on the platform's Bedrock key, because a community's Anthropic key is not valid for them. A community that wants Bedrock spend attributed to itself would need a Bedrock key of its own, which OSA does not support yet.
openrouter_api_key_env_var¶
Environment variable name holding your community's OpenRouter API key, for a community that funds itself through OpenRouter rather than the Claude Platform.
Setup on the server:
This is only reached when anthropic_api_key_env_var is unset: the Claude Platform key is checked first. Setting both is not an error, but the OpenRouter one will never be used.
OpenRouter answers carry no inline citations
Source citations are produced by the Claude Platform's native search_result blocks. A community routed through OpenRouter falls back to a prompt instruction asking the model to link its sources, which is a request rather than a guarantee. See the citations response field.
default_model¶
The model this community's assistant runs when a request names none. It must be one of the offered models, given by its id. Legacy OpenRouter-style identifiers such as anthropic/claude-haiku-4.5 are still accepted and normalized, so an older config.yaml keeps working, but new configs should use the ids below.
# Cost-effective, with extended thinking on by default (recommended)
default_model: claude-haiku-4-5
# More capable, with adaptive thinking
default_model: claude-sonnet-5-5
# A lower-cost model served from Amazon Bedrock
default_model: openai.gpt-6-luna
| Model | Runs on | Input / output per 1M tokens | Use case |
|---|---|---|---|
claude-haiku-4-5 | Claude Platform on AWS | $1.00 / $5.00 | General Q&A; the platform default |
claude-sonnet-5-5 | Claude Platform on AWS | $2.00 / $10.00 | Harder reasoning, longer synthesis |
openai.gpt-6-luna | Amazon Bedrock | $0.11 / $0.55 | Documentation Q&A at a lower cost |
qwen.qwen3-next-80b-a3b | Amazon Bedrock | $0.14 / $1.20 | Documentation Q&A at a lower cost |
openai.gpt-oss-120b | Amazon Bedrock | $0.15 / $0.60 | Documentation Q&A at a lower cost |
Any other value is rejected at startup with the list of offered models, rather than failing later at request time. Claude Opus, for example, is not offered.
- A reader can pick any other offered model in the widget;
default_modelis what they get without choosing. - The Bedrock models run on the platform's Amazon Bedrock key, and a caller's own Anthropic key cannot select them. When a request cannot have the community's Bedrock default (the caller brought their own Anthropic key and named no model, or the deployment has no Bedrock key), it runs the deployment's Claude default instead, and an error naming the community is logged.
osa validatewarns about a default like that. - The models do not all take the same request: GPT-6 Luna and Claude Sonnet 5.5 reject a
temperature, and the Bedrock models take no images.
default_model_provider¶
An OpenRouter-only routing hint, kept for communities still on the OpenRouter path. It is ignored on the Claude Platform, which has no provider routing layer.
default_model: anthropic/claude-haiku-4.5
default_model_provider: Cerebras # only meaningful with an OpenRouter key
Common providers: Cerebras (ultra-fast), Together (balanced). Availability varies by model.
reasoning_effort¶
How hard the assistant thinks before it answers, on one scale that works the same on every provider (the Claude Platform on AWS, Amazon Bedrock and OpenRouter). Each model turns the level into its own provider's request field, so a community sets it once.
The scale, lowest to highest: none, low, medium, high, xhigh, max.
If you set nothing, every model runs at high. The key applies to whichever model a request runs, not only default_model: a reader who picks another model in the widget gets that model's nearest level.
Each model keeps its own predetermined levels, and a level a model does not accept is never sent. It is lowered to the highest level the model accepts at or below the one you asked for, or raised to its lowest when you asked for less than the model can do.
| Model | Levels it accepts | What a level does |
|---|---|---|
claude-sonnet-5-5 | none, low, medium, high | Sets the model's effort. xhigh and max give high: Sonnet is never run above high. none is no up-front thinking at effort low. |
claude-haiku-4-5 | none, low, medium, high | Haiku has no effort setting, so a level is its thinking budget: low 1,024 tokens, medium 2,048, high 4,096, and none no thinking. xhigh and max give high. |
openai.gpt-6-luna | none, low, medium, high, xhigh, max | Sets the model's reasoning effort. |
openai.gpt-oss-120b | low, medium, high | Sets the model's reasoning effort. none gives low; xhigh and max give high. |
qwen.qwen3-next-80b-a3b | none | Has no reasoning control, so the key is ignored. |
Choosing a level
Higher levels answer more carefully and take longer before the first word, and the thinking tokens are billed as output. On one NWB documentation question, GPT-6 Luna's median time to first text was about 5 seconds at high, 11 seconds at xhigh and 48 seconds at max, and at the two higher levels it sometimes answered without searching the documentation, so the answer had no citations. For a faster assistant, lower the level (medium or low) before reaching for a different model.
When the level cannot be honored
If your default_model cannot run at the level you set (max on Claude Sonnet, or any level on Qwen3 Next), the config still loads, and a warning says what it will run at. osa validate lists that warning. Changing the level of a live community changes its requests once, so the provider's prompt cache is refilled after the change.
model_instructions¶
Extra system-prompt text for particular models, keyed by offered model id. Models differ in what they need to be told: GPT-6 Luna and gpt-oss-120b, for example, keep searching unless they are asked to stop after a couple of searches, which the platform already adds for them. Text here is appended after any such built-in note, and only on requests that run that model, so a community can tune one model without touching the others.
Keys are model ids or aliases from the offered models. An unknown key is an error, not a silent no-op.
documentation¶
List of documentation sources. Each entry creates a retrievable document.
DocSource Fields¶
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
title | string | Yes | - | Human-readable title |
url | URL | Yes | - | HTML page URL (shown to users in responses) |
source_url | string | No | null | Raw content URL for fetching (required if preload: true) |
preload | boolean | No | false | Embed in system prompt at startup |
category | string | No | general | Category for organization |
type | enum | No | html | Format: sphinx, mkdocs, html, markdown, json |
source_repo | string | No | null | GitHub repo for raw markdown sources (e.g., 'org/repo') |
description | string | No | null | Short description of document content |
Preloaded vs On-Demand¶
Preloaded (preload: true): Content is fetched at startup and embedded directly in the system prompt. Use for core documentation the assistant always needs (2-3 docs max, keep total under 15k tokens).
On-demand (preload: false or omitted): Available via the auto-generated retrieve_docs tool. The assistant fetches these when relevant to the user's question.
documentation:
# Preloaded - always in context
- title: Core Concepts
url: https://my-tool.org/docs/concepts
source_url: https://raw.githubusercontent.com/org/repo/main/docs/concepts.md
preload: true
category: core
description: Fundamental concepts and terminology
# On-demand - fetched when needed
- title: Advanced Features
url: https://my-tool.org/docs/advanced
source_url: https://raw.githubusercontent.com/org/repo/main/docs/advanced.md
category: advanced
description: Advanced usage patterns and configuration
Preload Requirement
Documents with preload: true must have a source_url for fetching raw content. Validation will fail otherwise.
github¶
GitHub repository configuration for syncing issues, pull requests, and discussions into the knowledge database.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
repos | list[string] | No | [] | Repos in org/repo format |
github:
repos:
- hed-standard/hed-specification
- hed-standard/hed-python
- hed-standard/hed-javascript
Repos are validated to match the org/repo format and deduplicated automatically.
citations¶
Paper search and citation tracking configuration for the knowledge database.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
queries | list[string] | No | [] | Search queries for OpenALEX |
dois | list[string] | No | [] | Core paper DOIs to track (format: 10.xxxx/yyyy) |
live_search | bool | No | false | Expose an on-demand live paper-search tool |
paper_labels | map[string, string] | No | {} | Human-readable label per DOI for the citations dashboard legend |
aliases | map[string, list[string]] | No | {} | Version DOIs (preprint, etc.) to merge into a primary DOI's citation count |
citations:
queries:
- "HED annotation"
- "Hierarchical Event Descriptors"
dois:
- "10.1016/j.neuroimage.2021.118766"
- "10.1007/s12021-023-09628-4"
# Labels shown in the public citations chart legend
paper_labels:
"10.1016/j.neuroimage.2021.118766": "HED-NeuroImage (2021)"
# Merge a preprint into the published paper's citation count (deduplicated)
aliases:
"10.1016/j.neuroimage.2021.118766":
- "10.1101/2021.01.01.000000"
DOIs are validated against the 10.xxxx/yyyy pattern. Common URL prefixes (https://doi.org/, https://dx.doi.org/) are automatically stripped. The same normalization and validation apply to paper_labels keys and aliases keys/values; every aliases primary key must also appear in dois.
aliases — why: OpenAlex keeps separate records for a paper's preprint and published versions and splits citations across them. Listing the version DOIs under aliases makes the citation sync query them together (OR-joined, deduplicated) and attribute the merged per-year counts to the primary DOI. Counts are floored at the earliest version's publication year, so no paper reports citations before it existed.
paper_labels and aliases feed the public citations dashboard and the GET /{community}/citations endpoint.
public_feeds¶
Opt-in flags that expose community data as public, read-only JSON feeds so communities can build their own frontends. Both are off by default.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
faq | bool | No | false | Expose generated FAQ entries at GET /{community}/faq |
citations | bool | No | false | Expose citation counts at GET /{community}/citations |
faq: truerequires a configured FAQ pipeline (mailman+faq_generation) with synced entries; otherwise the feed is empty.citations: truerequirescitations.dois; counts populate on the next citation sync.
See the API Reference for the endpoint shapes, query parameters, and examples.
maintainers¶
List of community maintainer GitHub usernames. Used for scoped admin access and budget alert notifications.
docstrings¶
Configuration for extracting code docstrings from GitHub repositories. Enables the search_{community}_code_docs tool.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
repos | list | Yes | - | Repositories to extract docstrings from |
DocstringRepoConfig¶
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
repo | string | Yes | - | Repository in org/repo format |
branch | string | No | main | Branch to extract from |
languages | list[string] | Yes | - | Languages to extract: matlab, python |
docstrings:
repos:
- repo: sccn/eeglab
branch: develop
languages: [matlab, python]
- repo: sccn/ICLabel
branch: master
languages: [matlab]
mailman¶
List of Mailman mailing list archives to sync. Enables the mailing list sync and FAQ generation pipeline.
MailmanConfig¶
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
list_name | string | Yes | - | Mailing list name (used in archive URL) |
base_url | URL | Yes | - | Base URL of the Mailman pipermail archive |
display_name | string | No | null | Human-readable name for the list |
start_year | int | No | null | Earliest year to sync |
mailman:
- list_name: eeglablist
base_url: https://sccn.ucsd.edu/pipermail/eeglablist/
display_name: EEGLAB Mailing List
start_year: 2004
faq_generation¶
Configuration for the two-stage LLM pipeline that generates FAQ entries from mailing list threads. The pipeline first scores thread quality (cheap), then summarizes high-quality threads (quality).
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
evaluation_agent | object | No | - | Model config for scoring thread quality |
summary_agent | object | No | - | Model config for generating FAQ summaries |
quality_threshold | float | No | 0.7 | Minimum quality score to create FAQ (0.0-1.0) |
sources | object | No | - | Source-specific settings |
Agent Config¶
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
model | string | Yes | - | claude-haiku-4-5 or claude-sonnet-5-5, or a legacy alias of either |
provider | string | No | - | Deprecated OpenRouter routing hint; accepted and ignored |
temperature | float | No | 0.1 | Sampling temperature, honored only by claude-haiku-4-5 |
enable_caching | bool | No | true | Enable prompt caching |
Both agents run on the Claude Platform, using the community's own key when it has one and the platform key otherwise. Two things to know before choosing models here:
- The evaluation agent scores every thread, so it is most of the bill;
claude-sonnet-5-5there costs roughly twice whatclaude-haiku-4-5does and defeats the point of splitting the pipeline in two.osa validatewarns if you do it anyway. claude-sonnet-5-5accepts only its default temperature. Atemperatureset alongside it is dropped rather than sent, which is also a warning fromosa validate.
faq_generation:
evaluation_agent:
model: claude-haiku-4-5
temperature: 0.0 # deterministic scoring
enable_caching: true
summary_agent:
model: claude-haiku-4-5
temperature: 0.1 # slightly creative FAQ phrasing
enable_caching: true
quality_threshold: 0.7
sources:
mailman:
enabled: true
min_messages: 2
min_participants: 2
sync¶
Per-community sync schedules. Each sync type can have its own cron expression (APScheduler format, UTC timezone).
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
github | object | No | - | GitHub sync schedule |
papers | object | No | - | Paper sync schedule |
docstrings | object | No | - | Docstring sync schedule |
mailman | object | No | - | Mailing list sync schedule |
faq | object | No | - | FAQ generation schedule |
beps | object | No | - | BEP sync schedule |
Each schedule object has a single cron field.
sync:
github:
cron: "0 2 * * *" # daily at 2am UTC
papers:
cron: "0 3 * * 0" # weekly Sunday at 3am UTC
docstrings:
cron: "0 4 * * 1" # weekly Monday at 4am UTC
mailman:
cron: "0 5 * * 1" # weekly Monday at 5am UTC
faq:
cron: "0 6 1 * *" # monthly 1st at 6am UTC
budget¶
Per-community spending limits for cost management.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
daily_limit_usd | float | No | - | Maximum daily spend in USD |
monthly_limit_usd | float | No | - | Maximum monthly spend in USD |
alert_threshold_pct | float | No | 80.0 | Alert when this percentage of budget is reached |
discourse¶
Discourse forum configurations for community Q&A search.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
url | URL | Yes | - | Base URL of Discourse instance |
tags | list[string] | No | [] | Tags to filter topics by |
extensions¶
Extension points for specialized tools that cannot be auto-generated from YAML.
Python Plugins¶
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
module | string | Yes | - | Python module path |
tools | list[string] | No | null | Specific tools to import (null = all) |
extensions:
python_plugins:
- module: src.assistants.hed.tools
tools:
- validate_hed_string
- suggest_hed_tags
- get_hed_schema_versions
See Extensions for how to write Python plugin tools.
MCP Servers¶
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name | string | Yes | - | Server identifier |
command | list[string] | No | null | Command for local server |
url | URL | No | null | URL for remote server |
Exactly one of command or url must be provided.
extensions:
mcp_servers:
- name: my-validator
command: ["node", "path/to/server.js"]
- name: remote-service
url: https://mcp.my-tool.org
widget¶
Widget display configuration for the embedded chat widget. These values provide defaults that the widget loads from the /communities API endpoint, so embedders only need to set communityId in their JavaScript.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
title | string | No | Community name | Widget header title (max 100 chars) |
initial_message | string | No | null | Greeting message shown when chat opens (max 1000 chars) |
placeholder | string | No | "Ask a question..." | Input field placeholder text (max 200 chars) |
suggested_questions | list[string] | No | [] | Clickable suggestion buttons (max 10) |
theme_color | string | No | null | Primary theme color as hex #RRGGBB (e.g., #008a79) |
logo_url | string | No | null | URL for custom logo/icon image |
theme_color¶
Primary color applied to the widget button, header background, and accent elements. Must be a 6-digit hex color code.
When not set, the widget uses the platform default blue (#2563eb). The widget JS also accepts themeColor via setConfig() for per-page overrides.
logo_url¶
URL to a custom logo or icon image displayed in the widget header avatar (replacing the default brain icon).
- Must use
http://,https://, or start with/(relative path) - Max 500 characters
Convention-based detection: If logo_url is not set in the YAML, the API automatically checks for a logo.* file in the community's folder (src/assistants/{id}/). Supported formats: SVG, PNG, JPG, JPEG, WEBP. SVG is preferred over other formats when multiple exist. The file is served via the GET /{community_id}/logo endpoint.
Precedence (highest to lowest):
- Widget JS
setConfig({ logo: 'url' })-- embedder override - YAML
widget.logo_url-- community maintainer sets explicit URL - Convention file --
logo.pngorlogo.svginsrc/assistants/{id}/ - Default brain icon (built into the widget)
Example¶
widget:
title: HED Assistant
theme_color: "#1a365d"
initial_message: "Hi! I'm the HED Assistant. I can help with HED annotation, validation, and related tools."
placeholder: Ask about HED...
suggested_questions:
- What is HED and how is it used?
- How do I annotate an event with HED tags?
- What tools are available for working with HED?
- Explain this HED validation error.
When the widget is embedded on a page, it fetches community defaults from GET /communities and applies them automatically. Embedders can still override any field via setConfig() in JavaScript; see the Widget Deployment Guide.
If widget is omitted, the widget falls back to generic defaults (title = community name, placeholder = "Ask a question...", platform blue theme, brain icon).
enable_page_context¶
When true (default), the assistant includes a fetch_current_page tool that retrieves content from the web page where the widget is embedded. This allows the assistant to provide contextually relevant answers based on what the user is currently reading.
Set to false if the assistant will only be used via CLI or API (not embedded in a web page).
links¶
External links for the community, exposed via the /communities API endpoint. Only populated links are included in the response.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
homepage | URL | No | null | Primary community website |
documentation | URL | No | null | Documentation or tutorials URL |
repository | URL | No | null | Source code repository (GitHub org or repo) |
demo | URL | No | null | Live demo page URL |
links:
homepage: https://www.fieldtriptoolbox.org
documentation: https://www.fieldtriptoolbox.org/tutorial/
repository: https://github.com/fieldtrip/fieldtrip
demo: https://demo.osc.earth/fieldtrip
All fields are optional. If no links are provided, the links field is null in the API response.
Complete Example¶
id: hed
name: HED (Hierarchical Event Descriptors)
description: Event annotation standard for neuroimaging research
status: available
enable_page_context: true
system_prompt: |
You are a technical assistant specialized in HED.
...
{preloaded_docs_section}
{available_docs_section}
{page_context_section}
documentation:
- title: HED annotation semantics
url: https://www.hedtags.org/hed-resources/HedAnnotationSemantics.html
source_url: https://raw.githubusercontent.com/.../HedAnnotationSemantics.md
preload: true
category: core
description: Fundamental annotation rules
- title: Basic annotation
url: https://www.hedtags.org/hed-specification/04_Basic_annotation.html
source_url: https://raw.githubusercontent.com/.../04_Basic_annotation.md
category: specification
description: Essential guidelines for creating annotations
github:
repos:
- hed-standard/hed-specification
- hed-standard/hed-python
citations:
queries:
- HED annotation
- Hierarchical Event Descriptors
dois:
- "10.1016/j.neuroimage.2021.118766"
widget:
title: HED Assistant
theme_color: "#1a365d"
initial_message: "Hi! I'm the HED Assistant. I can help with HED annotations."
placeholder: Ask about HED...
suggested_questions:
- What is HED and how is it used?
- How do I annotate an event with HED tags?
links:
homepage: https://www.hedtags.org
documentation: https://www.hed-resources.org
repository: https://github.com/hed-standard
discourse:
- url: https://neurostars.org
tags:
- hed
extensions:
python_plugins:
- module: src.assistants.hed.tools
tools:
- validate_hed_string
- suggest_hed_tags
- get_hed_schema_versions
Validation¶
The configuration is validated using Pydantic models. Common validation errors:
| Error | Cause | Fix |
|---|---|---|
Community ID must be kebab-case | Uppercase or underscore in ID | Use lowercase with hyphens |
DocSource has preload=True but no source_url | Missing fetch URL | Add source_url field |
Repository must be in 'org/repo' format | Invalid repo format | Use organization/repository |
Invalid DOI format | Malformed DOI | Use 10.xxxx/yyyy format |
McpServer must have either 'command' or 'url' | Missing server address | Add one of the fields |
extra fields not permitted | Typo or unknown field | Check field names |
The extra="forbid" setting on all models means typos in field names will be caught as validation errors rather than silently ignored.