# Authoring a walking adventure — agent guide (API v1)

You are creating an actual outdoor walking game, not a list of quiz questions.
Use this guide with `/api/v1/openapi.json` and `/api/v1/trail.schema.json`.
Discover these URLs at `/.well-known/agent.json`. Content is plain Dutch by
convention, but all narrative strings belong to the trail rather than the app.

## Start here: a short request is enough

A request such as “Maak een tocht met een [thema] voor [gebied] met behulp van
Speurtocht; zie deze handleiding” is a complete instruction to carry
out the workflow below. Research, create the content and map, save a draft,
validate, repair and publish the finished adventure. Agents **may publish**;
do not ask for a separate publication approval when the request is to create a
playable trail. Do not stop after describing a plan or producing a JSON file.

### An API key is required

Before using the management API, obtain the API key for this deployment. If you
already have it in your environment or secret configuration, use it. **If the key
is not known, ask the user to provide it securely.** Send it as
`Authorization: Bearer <API_KEY>` with every trail-management or asset-upload
request. Never include the key in a URL, trail content or your final report.
You can read this guide and research the adventure while waiting for the key.

### This deployment and its entry points

Use the deployment serving this guide:

- Platform origin: https://speurtocht.aersoftware.org
- Management API base: https://speurtocht.aersoftware.org/api/v1
- API reference: [OpenAPI](https://speurtocht.aersoftware.org/api/v1/openapi.json)
- Complete content contract: [JSON Schema](https://speurtocht.aersoftware.org/api/v1/trail.schema.json)
- Available UI styles: [Theme catalog](https://speurtocht.aersoftware.org/api/v1/themes)
- Available POI icons: follow the selected theme’s `icons_url` from that catalog.
- Existing trails: https://speurtocht.aersoftware.org/api/v1/trails
- Management overview: https://speurtocht.aersoftware.org/admin

Resolve all API and asset paths against this origin. A platform name or domain
mentioned in a brief is not an instruction to switch deployments: when this guide
is read on localhost, create the trail on that local deployment. Use another
origin only when the user explicitly selects it as the API target. Never switch
to production automatically if a local request fails. A localhost URL can only be
used by an agent that can reach the same machine; report an access failure rather
than pretending to have created a trail.

### Fill in ordinary choices yourself

Use explicit user requirements first. Otherwise use these starting defaults:

- Language: Dutch. Audience: mixed families with children aged 6–12, one `family`
  audience and no age-selection step.
- Route: aim for 3–5 km and about 20 question destinations, plus a separate
  story-only entrance. Adapt the distance and destination count to the actual
  terrain and access rules; do not add unsafe or repetitive detours to hit a quota.
- Questions: four times the destination count per audience (80 for 20 destinations).
  For 20 destinations, use an easy/medium/challenging mix of 5/9/6, with enough
  questions in each pool. For other counts, choose a sensible integer mix totaling
  that count. Include questions that adults and children can solve together.
- UI style: fetch the catalog; choose `fairy_tale` for a fairy-tale brief, `sports`
  for sport, or `rescue` for a rescue-team adventure. The narrative is original
  content; do not create a new application UI theme.
- Collection: use nouns matching that style (fireflies/jar, footballs/trophy,
  stars/badge); start at 0, award 1 for a first-try correct answer and lose up to
  1 for a wrong answer. Use up to 20 optional moving collectibles along the route
  when suitable; `[]` is supported when wild collecting does not fit the brief.
- GPS rules: arrival radius 20 m, maximum accuracy 30 m, location lifetime 30 s.
- Duration: provide `estimated_duration_minutes`, an integer estimate of the
  **whole adventure**, not just walking time. Include time for the intended age
  group to walk the verified route, read the story, discuss and answer questions,
  collect optional finds, and take short breaks. As a starting point for families,
  use about 3 km/h plus 1–2 minutes per question destination and 10–15 minutes for
  the story and exploring; adjust for hills, terrain, crossings and the brief.
  Round to a sensible 5- or 15-minute estimate. Do not present it as field-tested
  or derive it from the straight-line distance between destinations. The app
  shows this separately from distance in the directory and before starting.
- Map: a richly illustrated storybook adventure map grounded in the verified OSM
  geometry, optimized for mobile. The requested narrative theme must be visible
  throughout the map itself, through characters and story scenes. A clean park
  diagram with themed colors, repeated tree symbols and a decorative border is
  not a finished themed map. Follow the art-direction workflow below; include a
  smaller matching preview and preserve GPS registration.
- Supporting art: two original illustrations in addition to the map: a wide
  `artwork.hero` and a portrait `artwork.story`. Follow the illustration contract
  below; map crops do not count as supporting art.

Do not interview the user about each of these optional choices. State the choices
in the final handover. Ask only when an essential ambiguity cannot be resolved
from reliable sources, for example which of two different places with the same
name the user intends. A location or named theme alone is not a reason to pause.

### Write an invitation into the adventure

Write for the family imagining their day out. Let `summary` be a short,
story-led invitation: evoke the place, introduce the mystery or characters, and
give the family a reason to join in. A few vivid, natural sentences should make
this particular adventure feel worth discovering.

Carry that voice through the opening, story and ending. Let characters, events
and the family's mission bring the adventure to life. Practical planning details
belong in the dedicated configuration fields and directions/access notes; the
app presents those facts and explains how to play. Read the visitor-facing copy
aloud before publishing: it should sound like a storyteller welcoming the family,
rather than an author describing how the trail was assembled.

### Your deliverable and stopping conditions

Create a new trail unless the user explicitly asks to update an existing one.
Choose a descriptive unique slug; list existing trails first and do not overwrite
an unrelated trail with a matching name. For an explicit update, create a new
version of that trail, preserving existing published versions and group progress.

Supply every required field in the JSON Schema: title/area/summary,
`estimated_duration_minutes`, theme choice
and story nouns, audiences and question mixes, collection rules, GPS rules,
start point, registered map and preview, both supporting illustrations and their
alt text, opening/ending/share/photo-frame copy,
access sources, ordered destinations with walking geometry, and question pools.
`flights` is optional. Obtain local image-generation, mapping or file tools as
needed; the content API stores the finished assets and does not generate artwork
or research a walking route for you. Do not reuse artwork or coordinates from a different real-world area. Never fabricate an asset URL or claim an upload,
source check, validation or publication that did not succeed.

Before publication, check the complete walking route against access sources,
check several known map points against the artwork, perform the visual acceptance
review below, proofread the questions and explanations, then validate through the API. Fix errors and repeat validation;
use the current ETag when replacing or publishing a draft. Once valid and
source-checked, publish and verify the public directory, returned trail URL and
its `/trail-config`. Do not claim the route has been walked or field-tested.

If essential tools, map registration or public access cannot be verified, save the
available content as an **unpublished draft** and report the specific missing work.
Do not mark `access.reviewed` true or publish merely to get past validation.
If the API is unreachable, report that blocker without claiming a saved draft.

Finish in the user's language with: title, area, chosen theme and ages, route
length and destination/question counts, source links and access restrictions,
trail slug/version, actual validation/publication status, and the playable URL
(or draft reference and remaining blockers). All operating instructions belong in
this guide; the human should not have to repeat the API workflow in their prompt.

## The experience to design

A family reads an introduction at home, travels to the start and chooses a group
name and, if needed, an age group. GPS unlocks the opening story at a **separate
start point without a question**. Start the timer after the family accepts the
story. Reveal only the next destination and the walking path to it. Within the
configured arrival radius its question appears. Only a correct answer reveals
the following destination. Questions are drawn without repetition from an age-
and difficulty-specific pool. Answers and explanations are never sent before
they are needed. Celebrations wait for the family to press Continue.

Each first-try correct answer can award collectibles. Wrong answers remove the
configured number, floored at zero; there is no waiting penalty. Optional moving
collectibles fly near the route, with smooth client-side movement. A fresh,
accurate GPS fix is required to collect. Flight proximity is client-authoritative
by design, so scores are a casual family game, not a fraud-proof competition.

The ending, story, theme, POI nouns, collectibles, audience, map, frame text,
scoreboard and share invitation are specific to the trail. The public photo link
invites another family to start on that trail's own subdomain. Existing groups
and photographs remain bound to their original published version when a new
version is published. Records are scoped to that same trail version.

## Resolve the brief before authoring

Use the request, source research and the defaults above; this is not a questionnaire.
Establish: real area and entrance; audience (including mixed families); theme;
walking distance and available time; destination count; finish point; accessibility
needs; collection object/container; narrative goal; land manager and access rules.
For example: a fairy-tale adventure collecting fireflies in a jar, or a rescue
adventure collecting stars for a badge, adapted to the requested area.
These are briefs, not pre-verified walking routes.

## Build a coherent and reachable route

1. Consult OpenStreetMap **and authoritative land-manager access information**.
   Respect private roads, water, barriers, opening hours, permits, protected areas
   and temporary closures. Never guess a shortcut across a road or through water.
2. Choose a distinct start and ordered, publicly reachable POIs. The final POI is
   a satisfying finish and need not return to the entrance. Keep groups off roads
   and away from hazards while reading questions. Prefer adequate GPS reception.
3. Supply `approach` as real walking-path geometry for **every** leg: from start
   to POI 1, then from the preceding POI to each next POI. Coordinates are WGS84
   `[latitude, longitude]`, never GeoJSON's reversed coordinate order. The server
   computes length; do not invent a straight segment and call it a footpath.
4. Explain access limitations in `access.notes`, cite an HTTPS source URL and
   supply proper OSM attribution. Set `access.reviewed: true` only after actually
   checking those sources. Do not claim a field survey unless one happened.
   Structural validation cannot establish real-world access; an on-site walk is
   still needed before recommending the trail to visitors.

## Write the story and question bank

Use `story.intro_title` and `intro` for the at-home introduction; `title` and
`paragraphs` for the GPS-unlocked opening. Explain the goal, characters, collection
mechanic and finish. Write `completion_title` and `completion` as a payoff to that
same story. `directions` describes the entrance and practical access.
`share_title` invites a new family; `share_text` describes the achievement.
`frame_title` and the two short `frame_lines` appear over the group's photograph.
All copy is **plain text**. No HTML, scripts, CSS, instructions for the application,
external analytics or credentials belong in content.

For each POI you may include a short `description` (shown with its question) and
a practical `navigation_hint` (shown while walking there). Use plain text and
keep the story consistent. After choosing `theme.id`, call
`GET https://speurtocht.aersoftware.org/api/v1/themes/{theme_id}/icons` (replace `{theme_id}` with
the chosen ID). The public response contains `icons` with their `id` and `symbol`,
and a `default_icon`. Select a returned ID for `stops[].icon`, or omit the field
to use that theme's default. Discover the current choices through this endpoint;
icon names are application capabilities, not names to invent from the story.
The discovery response itself is not a configuration block. Unknown icons are
reported at their `stops[index].icon` path during validation and prevent publishing.

Give each question a stable `pool_key`, an `audience` key and difficulty
`easy`, `medium` or `challenging`. Supply exactly four different plausible choices,
zero-based `answer_index` and an explanation that teaches something. Avoid obvious
joke distractors, ambiguous answers and answer-length clues. Mix observation,
logic, language, basic mathematics and theme knowledge. Families should sometimes
need to think together; avoid making every question trivial. Questions are sampled
across the whole route; do not rely on a question occurring at a particular POI.
A useful starting pool is **four times the destination count per audience**.
Each audience's `question_mix` counts must sum to the destination count and the
pool must contain enough questions of each requested difficulty.

## Two supporting illustrations for every adventure

Create **two original narrative illustrations in addition to the navigation map**.
They introduce the adventure before the family starts walking. The app uses:

| Field | Composition | Where it appears | Suggested export |
| --- | --- | --- | --- |
| `artwork.hero` | Wide scene, about 2:1 | Trail catalog and the opening screen, including visitors still at home | 1600×800 or 2000×1000 WebP |
| `artwork.story` | Portrait scene, about 3:4 | “Het verhaal”, the opening story and the public invitation page | 1200×1600 WebP |

Use one coherent art direction across both scenes and the map: the same cast,
clothing, palette, lighting, illustrated landscape and motifs from this story.
Design different compositions for their roles. The hero invites the family into
the setting; the portrait shows the protagonist, central mystery or quest object
in a story scene. Show an adventure that families can imagine themselves joining.
Adapt this to the chosen narrative and UI theme; sport and rescue adventures need
their own scenes, not fairy-tale imagery by default. Do not reuse another trail's
illustrations or introduce unrelated characters merely to decorate the page.

These are **not maps, screenshots, enlarged map crops, photo frames or collages**.
Do not paint a parchment scroll, UI buttons, title text, labels, waypoint numbers
or a border into them. The app places the title and copy separately. The scenes
can be full-bleed and do not need GPS registration. Fictional scenery illustrates
the story; do not imply that an invented castle or bridge physically exists there.

### Safe composition for responsive layouts

Design both illustrations to survive resizing and center cropping. The source
image's outer edge is **not a safe place for essential content**. Use the following
safe areas in every adventure, regardless of location, characters or theme.
Coordinates are percentages of the full source image, measured from its top-left:

| Illustration | Horizontal safe area | Vertical safe area | What belongs outside it |
| --- | --- | --- | --- |
| Hero, about 2:1 | x = 25–75% (middle 50% of width) | y = 30–70% (middle 40% of height) | Expendable scenery, atmosphere and supporting decoration |
| Story, about 3:4 | x = 15–85% | y = 10–90% | Background and secondary details that are not needed to understand the scene |

Keep the **entire visible extent** of important faces, heads (including helmets,
hair and horns), quest objects and story-critical gestures inside the relevant
safe area. Putting only an object's center inside it is insufficient. A hand
holding the quest object must remain readable together with that object. Feet,
foliage or a decorative wing tip can extend farther when losing them would not
change the scene's meaning. Avoid placing important elements against the safe
area boundary; leave a little additional breathing room.

For example, in a 2000×1000 hero, essential content belongs between x = 500–1500
and y = 300–700 pixels. In a 1200×1600 portrait, use x = 180–1020 and y = 160–1440.
These are composition guides, not boxes to draw into the illustration. Continue
natural scenery to every edge: no blank bars, artificial padding, borders or
painted safe-area guides. Group the essential subjects around one central focal
scene rather than spreading them across the full width or height.

The hero may appear as a 2:1 card or a wider banner. For the usual 2:1 source,
a centered 4:1 crop removes the top and bottom quarters. Plan for that loss before
generating the image. The portrait is normally shown at its native 3:4 ratio;
its margins also protect against rounded corners and small future layout changes.
These margins improve resilience, but no composition can survive every possible
crop. Inspect the actual delivered images rather than trusting the prompt alone.

Include the aspect ratio, safe-area percentages, focal subjects and forbidden
edge placements explicitly in each image-generation prompt. Do not fix a bad
composition by distorting the image, adding white space or stretching it to fit.
Recompose/regenerate it when necessary. Create two separate compositions rather
than supplying the same image twice or cropping the hero into a portrait.

Before uploading/publishing:

1. Review the hero at its full ratio and as centered 3:1, 3.4:1 and 4:1 crops.
   Check that whole heads, essential objects and gestures remain visible. Treat
   4:1 as a conservative stress check, not a promise about the current app layout.
2. View the hero at about 360 pixels wide and the portrait at 300–360 pixels wide.
   The main characters, quest object and visual action must still be recognizable
   without zooming. Use clear silhouettes and contrast; tiny decorative details
   must not carry essential story information.
3. Inspect the actual catalog, opening screen and story view on a phone and a
   small tablet. Check for awkward cuts, unexpected subject placement, stretching
   and an image that only works at desktop size. A simulated crop alone does not
   replace checking the app.
4. Confirm that both images share the map's cast, palette and illustration style,
   contain no generated lettering/UI, and have alt text describing the actual
   scene. If any check fails, repair the image before publication.

The API checks dimensions and asset references; it cannot certify safe placement,
readability, composition or artistic quality. Do not report those visual checks
as passed unless you actually inspected the pixels and views.

Upload both using the existing authenticated `POST /api/v1/assets` endpoint and
use its returned URLs. Supply short, descriptive Dutch alt text for each scene:

```json
"artwork": {
  "hero": { "image": "<hero upload URL>", "alt": "Een beschrijving van de brede avonturenscène." },
  "story": { "image": "<portrait upload URL>", "alt": "Een beschrijving van de held en het raadsel." }
}
```

The placeholders above are not literal asset URLs. Publication requires both
illustrations and alt texts (1–240 characters), two different uploaded assets,
and assets distinct from `map.image`/`map.preview`. Hero: ratio 1.8–2.2 and at least
1200×600 pixels. Story: ratio 0.70–0.80 and at least 900×1200 pixels. Each supporting
illustration must be at most 2 MB; prefer WebP and aim for 500 KB or less for fast
mobile loading. No extra preview uploads are needed for these two illustrations.

Inspect the generated pixels and the actual catalog, opening screen and story
view before publishing. Check visual consistency, recognizable characters, the
hero crop and whether each alt text describes the actual image. Structural
validation does not judge artistic quality. If you cannot create these assets,
save an unpublished draft and name the missing work; do not publish placeholders.
Older published trails without this field keep running with an app-owned neutral
theme visual. When updating one, supply both illustrations in the new draft;
do not copy that placeholder or a derived map URL into `artwork`.

## Map registration and artwork

### Art direction: make the map part of the adventure

The application UI theme and the map illustration are separate responsibilities.
Selecting `theme.id: "fairy_tale"` styles the interface, jar and photo frame and
supplies the shared parchment map frame (`map_frame: "parchment"` in the theme
catalog). The app lays your illustration onto that paper and softly fades its
outer edge into the parchment along a softly blurred, uneven contour, with curled
ends, irregular edges and a transparent
exterior. **Do not draw or bake a second border, scroll, curled ends, frame or
drop shadow into the uploaded artwork.** The paper and its border belong to the
application theme; the content author creates the illustrated landscape and story
scenes. The theme does **not** generate those illustrations for you.
The map should feel like a page from this particular adventure,
with a recognizable real landscape underneath it.

For a fairy-tale brief, use hand-drawn or painterly illustrated scenery with depth,
varied trees and clearings, warm paper, gentle magical light and a coherent visual
language. Include substantial, recognizable story illustrations: an enchanted gate,
a sleeping dragon, a fairy among glowing mushrooms, a knight or princess, a tiny
woodland dwelling, a dream-filled tower or a magical pond, as appropriate to the
actual story. Choose a coherent cast and motifs rather than adding unrelated
fantasy clip art. Integrate the illustrations into the landscape, with a scale,
lighting and palette that belong together. A crown in the border and gold path
lines alone do not meet this requirement.

Derive the scenes from the requested theme, the story you have written and the
verified landscape of the requested area. Choose story characters, objects and
visual motifs that belong to that adventure, then associate them with suitable
real landmarks and destinations. Do not copy a location-specific cast, plot or
set of illustrations from an existing trail. Adapt the scenery to the actual
terrain: woodland, dunes, water, open fields or urban greenery should remain
recognizable rather than being turned into a generic forest.

Fictional characters and structures are story illustrations beside verified real
places, not claims that those features physically exist there. Retain the real
entrance, paths, clearings, terrain and route geometry. Apply this principle to
every theme: sporting characters and trophy scenes for sport, or a friendly
rescue team and mission scenes for a rescue story, integrated with that area's
real geography.

For a route of around 20 destinations, aim for 6–8 clearly readable major story
scenes and smaller discoveries throughout the rest of the area. Each destination
should relate to an illustrated landmark or motif; several destinations may belong
to one larger scene. Scale this to the route and available space. The illustrations
must remain recognizable at normal phone zoom; tiny decorations only visible in
the full-resolution file do not count as the main scenes. Keep paths and junctions
clear, with fewer and more subtle strokes than the live route overlay. Use labels
sparingly. Do not bake waypoint numbers, the active route, questions, GPS dots,
score controls or UI buttons into the artwork; the application supplies those.

### Produce the geography and illustration together

1. Establish the verified OSM path network, bounds, entrance and POI coordinates
   first. Draw **at least 150 metres of real landscape beyond the entire walking
   route on every side**, including the start, finish and every approach segment,
   not just the destination coordinates. Also keep all required route geometry,
   navigation junctions, pins and important story scenes **outside the outer 8%
   of each canvas edge** (inside the central 84% in both directions). Both margins
   apply: use whichever requires wider bounds. Measure the geographic margin in
   ground metres, accounting for latitude; do not treat a degree of longitude as
   a fixed distance. Add extra room for large labels or illustrations as needed.
   Build a registered reference canvas with known junctions and landmarks.
   Decide which real places support the story scenes and write an art brief for
   the requested adventure before producing the final image.
2. Use an image-generation or illustration tool for the original themed raster
   artwork when available. Give it the registered reference map and request
   detailed storybook scenery and the selected scenes, with the exact framing,
   orientation and geography preserved. Explicitly forbid shifted junctions,
   invented navigable paths, water crossings, missing roads or cropped terrain.
   Do not treat a procedural diagram with uniform tree stamps, a parchment border
   or a recolored OSM screenshot as the completed illustration.
3. Keep the verified path geometry authoritative. If an illustration tool distorts
   paths or landmarks, repair the artwork or composite the exact registered path
   layer back over it, integrating its colors and line weights into the drawing.
   Place characters and architectural fantasies alongside navigable paths, never
   over a junction where they obscure the route or imply a new walkable shortcut.
   Do not change bounds or POI coordinates to make distorted artwork seem aligned.
4. Check `map_frame` in the selected theme catalog entry. For `parchment`, export
   the illustrated terrain alone: no decorative border or surrounding paper.
   Use warm parchment-compatible colors (rather than a white rectangle); the
   drawing may fade naturally near its edges. The app adds the shared scroll
   outside your canvas and feathers its edges along an irregular, softly blurred
   contour within the outer 8%. This is expendable scenery: no required walking
   leg, entrance, destination, label or important illustration belongs there.
   Follow the 150-metre and 8% safety margins above before generating artwork,
   then register the entire expanded landscape to those verified bounds. Include
   these margins and the unchanged path coordinates explicitly in the art brief.
   Never stretch existing artwork or merely enlarge its geographic bounds to
   claim extra coverage; extend the verified reference and draw the added terrain.
   Never move paths to create room. Do not bake a straight rectangular fade into
   the illustration: the app supplies the same organic edge for map and preview.
   For `none`, the uploaded canvas is displayed as supplied, without a theme
   frame or automatic feathering. Do not invent another application frame style.
5. Inspect the generated pixels, then export the full map and a matching preview.
   Deterministic compositing, resizing and WebP compression are appropriate for
   finishing an illustration; do not request a new generative redraw just to
   resize, compress or assemble already suitable artwork. The upload API does not
   provide an image generator. If no available tool can produce the required
   artwork, save a draft and report the missing art instead of publishing a plain
   diagram as a finished themed map.

### Visual acceptance before publishing

Actually open and inspect the finished map and preview, not just the drawing
script, image dimensions or a successful upload. Review the whole composition,
several detailed crops and a phone-sized view with a sample walking leg and pin.
Ask yourself:

- Without reading the title or seeing the app chrome, is the requested theme
  unmistakable in the map's interior? Can I point to several substantial story
  scenes and explain how they belong to this adventure?
- Does this look like an illustrated adventure rather than a clean cartographic
  diagram with decorative trim? Are the characters and scenic landmarks large
  enough to recognize on a phone, with visual variety across the route?
- Do the verified entrance, multiple dispersed junctions, clearings and the finish
  still align with the known coordinates? Can a family read the walking route
  clearly without mistaking a decorative feature for a real path?
- Does the entire route have at least 150 metres of illustrated landscape beyond
  it on every side? Are route lines, pins, navigation junctions and important story
  scenes outside the outer 8%, with no essential content lost in the irregular
  fade? Check the framed overview and the route's outermost points on a phone.
- Are there no clipped scenes, unreadable generated text, accidental UI elements,
  stray white edges or differences between map and preview? For a parchment
  theme, inspect the app's framed result as well as the uploaded illustration:
  does it blend naturally onto the paper without a second border or visible seam?

If the map is too neutral, revise the art direction and regenerate or enrich the
illustration before publication. If registration fails, repair that too. Passing
`/validate` checks structure and geometry constraints; it does **not** certify
illustration quality, theme fidelity or actual alignment of the painted pixels.
In the handover, briefly name the main illustrated scenes and describe the visual
and registration checks actually performed. Do not claim a visual inspection that
you did not perform.

### Upload and registration contract

Upload a **PNG or extended WebP (VP8X)** using `POST /api/v1/assets` with a JSON
`data_base64` string. Images must be complete, static files (no APNG/animated WebP). Maximum decoded
size is 8 MB and 16 million pixels; both
image dimensions must be 100–8192 pixels. Use the returned same-origin `url` in
`map.image` or `map.preview`. No server-side URL fetching, SVG, remote scripts,
credentials or arbitrary local filesystem paths are supported.

For the parchment theme, upload the finished **illustration only**, without the
theme's paper or border. An opaque PNG/WebP is fine: the app applies the edge fade.
Use `paper_width = width` and `paper_height = height` for new parchment maps.
`map.bounds` is `[[south,west],[north,east]]` of the **registered inner map**,
not a lat/lon bounding box guessed from the artwork. The projection is Mercator in
latitude and linear in longitude. Preserve OSM geography when illustrating:
landmarks and paths must not move. The painted image is decoration; the overlaid
walking geometry is the navigation source.

`width` and `height` are the logical registered inner canvas. `paper_width` and
`paper_height` describe the full image canvas, with the inner canvas centered.
These describe your uploaded canvas, **excluding the app-owned frame**. Use equal
dimensions for new parchment maps; unequal dimensions are supported for historical
assets that already have centered padding. `image_pixel_ratio` relates logical
coordinates to exported pixels (usually 2). Example: registered canvas 1200×1400,
`paper_width: 1200`, `paper_height: 1400`, full upload 2400×2800 pixels.
The app expands the displayed paper dimensions and supplies framed image URLs in
`/trail-config`; never copy these derived values back into an authoring document.
The preview is a smaller export of exactly the same composition and aspect ratio.
Use WebP for a reasonable mobile download. Do not request a new AI redraw when
only deterministic compositing or compression is needed.

Set reasonable `max_zoom`, `route_max_zoom` and `start_max_zoom` (zoom levels are
powers of two relative to the logical canvas). The map overview fits the full
border. Trackpad/pinch is continuous. Do not alter registration to change zoom.

Optional `flights` have consecutive IDs starting at 1 and 2+ path points each,
inside bounds and preferably near walking paths, away from the outer map edge.
They are not extra mandatory POIs. `[]` disables moving collectibles.
Choose `theme.id` from the public `GET /api/v1/themes` catalog:
- `fairy_tale`: forest green/gold, sparkles in a glass jar, botanical photo frame.
- `sports`: blue/yellow, football tokens in a trophy, striped photo frame.
- `rescue`: teal/orange, stars in a rescue badge, shield photo frame.

These are application-owned UI styles, tested independently of individual trails.
Do not submit colors, CSS, hero symbols, collection symbols, `visual` or
`container_style`; publication rejects these fields. New styles require app
code and theme tests, never a content API request. Use the closest existing style.
A narrative may feature knights, football or a rescue team without adding a style.
`theme.name`, `stop_singular`, `stop_plural`, collectible nouns and container name
are content. Match these nouns to the selected visual (e.g. balls/trophy for sport,
stars/badge for rescue). Map artwork and stories remain trail-specific.

Example: `"theme": {"id":"sports", "name":"Het kampioensteam",
"stop_singular":"coach", "stop_plural":"coaches"}` and
`"collection": {"singular":"bal", "plural":"ballen", "container":"beker",
"initial":0, "first_try_bonus":1, "wrong_answer_loss":1}`.

Historical published exports may use the former color-based format. For updates
to the **same trail**, clone with `source_version_id` to adapt to the current theme
contract. That parameter cannot clone across trails. For a new trail, submit a new
configuration object, using the reference only to understand its structure.

## API workflow

Base URL for this deployment: `https://speurtocht.aersoftware.org/api/v1`.
All trail-management and asset-upload requests require the owner's fixed API key:
`Authorization: Bearer <API_KEY>`. This includes reading trail/version exports,
creating drafts, uploading assets, validation and publication. Obtain the key from
the operator through the agent's environment or secret configuration, for example
`TRAILS_API_TOKEN`; never invent a key, print it, put it in a URL, save it in trail
content or include it in the final report. Do not try the owner's admin passkey or
browser session as API authentication. Ask the user to supply the key securely
if it is unavailable; independent research may continue, but do not claim an API
write succeeded. A 401 means the key is missing or invalid; a 503 can mean the API
key has not been configured on the server.

The guide, OpenAPI, JSON Schema, theme and icon catalogs, discovery and visitor
gameplay remain public. The admin UI uses the owner's passkey and has no public signup.
Authentication does not change publication validation, immutable versions or
`If-Match` requirements. Agents with the key may publish without a separate human
approval. Public game config still excludes the question bank.

1. `POST /trails` with `{ "slug": "voorbeeld-avontuur", "title": "Een nieuw avontuur" }`.
   These names are examples; choose a slug and title for the requested adventure.
   Slugs are lowercase DNS labels, immutable, unique, at most 50 characters. A
   reserved or existing slug returns an error; inspect it before creating another.
2. `POST /assets` for the full map, matching map preview, wide hero illustration
   and portrait story illustration (four uploads). Save the returned URLs in
   `map.image`, `map.preview`, `artwork.hero.image` and `artwork.story.image`.
3. `POST /trails/voorbeeld-avontuur/versions` with `{ "configuration": { ... } }`.
   An incomplete document may be saved as a draft. For updates to an existing
   trail, use `{ "source_version_id": 123 }` to copy its content to a **new draft**.
4. `GET /trails/voorbeeld-avontuur/versions/124` to read the complete editable document and
   its `ETag` / numeric `revision`. `PUT` to that URL replaces the whole document;
   send `{ "configuration": { ... } }` and `If-Match: "0"` (use the actual ETag).
   A stale revision returns 412; missing If-Match returns 428. Re-read and reconcile
   concurrent edits rather than overwriting them. Drafts are invisible to visitors.
5. `POST /trails/voorbeeld-avontuur/versions/124/validate`. 200 means valid; 422 gives
   `errors` with configuration paths. Resolve all errors. Use the JSON Schema for
   preflight checks and this endpoint for geometric/question-bank checks.
6. `POST /trails/voorbeeld-avontuur/versions/124/publish` with the current `If-Match`.
   Agents **may publish** with the API key. Publication validates
   again and atomically creates the playable POIs/questions, freezes this version
   and switches the trail's current version. Failed validation leaves the current
   public version unchanged. A published version cannot be overwritten; edit a
   new draft. Repeating publication of the same version is harmless with its current
   ETag. There is deliberately no destructive delete endpoint.
7. Verify the directory on this deployment and the trail URL returned by the API.
   Unknown or unpublished subdomains return 404. On a local deployment, use the
   returned `http://<slug>.localhost:<port>/` URL. On a hosted deployment, use its
   returned public subdomain URL. Do not construct URLs for another deployment.
   Wildcard DNS, TLS and proxy routing must be configured by the operator first.

`GET /trails` lists IDs, titles, publication and URLs. `GET /trails/:slug` lists
versions. Management version exports include the answer bank; keep these out of
the public game interface.
An existing published trail may serve as a **structural reference** through these
endpoints. It is not a content template: create the requested area’s own route,
artwork, coordinates, story and questions. Never reuse another area’s map
registration or assume that the reference trail’s characters fit the new brief.

Errors are JSON with `error` and usually `details` (validation endpoint: `errors`).
Typical statuses: 401 invalid/missing API key, 404 unknown resource, 409 immutable version, 412 stale
revision, 413 oversized body, 422 invalid content, 428 missing revision, 503 API not configured or database busy.
