Integration — building a Hunt (التحدي) with an AI agent Markdown Back to the dashboard

Integration — building a Hunt (التحدي) with an AI agent

Read this first, AI agent. A Gamemaster (القيم ماستر) has handed you this document. Your job is to interview them, build a Hunt (التحدي), let them check it, and produce one ZIP file they upload at letstreasurehunt.com. You never talk to the server. The Gamemaster does the uploading.

Written for a machine to read. The Markdown original is the source — prefer it to any PDF rendering.

0. Terms — المسميات

The JSON field names are English and fixed. The words you write for people to read — titles, clues, stems, choices, and anything you say to the Gamemaster — use this vocabulary. Both columns name the same thing.

English (field names use these) بالعربية (نصوص اللاعبين تستخدم هذه)
Hunt التحدي
Stage المرحلة
Clue / location_hint الدليل
Box الصندوق
Team الفريق
Gamemaster القيم ماستر
Question السؤال
Reward / reward_text الجائزة
A Hunt is «التحدي», never «صيد». If you are generating Arabic content — a title, an intro, a location_hint, a finish_text — write التحدي for the whole thing and المرحلة for one stop in it. «صيد» is wrong and will read as hunting animals. Do not translate the website's name.

1. What you are producing

One ZIP, containing one JSON file and every picture it names.


my-hunt.zip
├── hunt.json          the whole hunt
└── images/
    ├── resistor-10k.jpg
    └── meter-face.png

For a single stage added to an existing hunt, the same shape with stage.json instead:


new-stage.zip
├── stage.json
└── images/

Images are referenced by bare filename only. No URLs, no Drive links, no paths. "image": "resistor-10k.jpg" means images/resistor-10k.jpg inside the ZIP. A file named in the JSON but missing from the ZIP fails the import and names itself.

2. hunt.json


{
  "format": "treasurehunt/hunt@1",
  "title": "Ohm's Law",
  "intro": "Four stops around the lab.",
  "finish_text": "Collect your badge from the teacher.",
  "duration_min": 0,
  "stages": [
    {
      "name": "The Bench",
      "location_hint": "Start at the soldering bench — the box is under the stool.",
      "reward_text": "A yellow sticker",
      "n_questions": 3,
      "pass_threshold": 100,
      "duration_min": 0,
      "shuffle": 1,
      "max_attempts": 0,
      "questions": [ ... ]
    }
  ]
}
Field Meaning
title The Hunt's (التحدي) name. Required.
intro Shown on the join screen.
finish_text Shown when every Stage (المرحلة) is cleared.
duration_min Minutes the whole Hunt runs from the start. 0 = no limit.
location_hint The Clue (الدليل). The sentence that sends players to this Stage. Required for every Stage after the first.
reward_text Shown on the Box (الصندوق) and the phone the moment it opens.
n_questions How many of the Stage's Questions each Team (الفريق) is served.
pass_threshold Percent needed to open the Box. 100 is normal.
shuffle 1 to shuffle the order per Team.
max_attempts 0 = unlimited retries.

stage.json is exactly one stage object, with the same fields, at the top level.

3. Question types

3.1 Multiple choice


{
  "type": "mcq",
  "stem": "Which of these resistors is 10 kΩ?",
  "stem_image": "band-chart.png",
  "choices": ["4.7 kΩ", "10 kΩ", "100 kΩ"],
  "choice_images": [null, "r-10k.jpg", null],
  "correct_index": 1
}

3.2 Match to labels — the word bank

The one to reach for when you want "choose the right word". On screen the player drags; on paper it becomes a word bank and numbered blanks.


{
  "type": "match",
  "stem": "Complete each line.",
  "pairs": [
    { "label": "Unit of resistance", "answer": "Ohm" },
    { "label": "Unit of current",    "answer": "Ampere" }
  ],
  "decoys": ["Watt", "Farad"]
}

3.3 Drag onto a picture

Regions cut out of a picture, dragged back into place. Harder to author blind — prefer 3.1 and 3.2 unless the gamemaster specifically wants this, and tell them it is easier to build in the dashboard's own editor where they can draw the regions with a mouse.


{
  "type": "hotspots",
  "image": "diagram.png",
  "fill": "#ffffff",
  "regions": [
    { "label": "the ammeter", "x": 0.21, "y": 0.37, "w": 0.31, "h": 0.07 }
  ]
}

x, y, w, h are fractions of the picture, 0 to 1, from the top-left. The importer cuts each region out, paints the hole in fill, and the cut piece becomes the card to drag back.

4. Rules that are enforced, not advisory

These are checked on import, all of them before anything is written. A bundle either imports whole or not at all — there is no half-created hunt to clean up, and the importer reports every problem at once rather than one per attempt.

Entry names inside the ZIP are never used as paths. Every file is stored under a name the server mints, so a folder structure, an absolute path or a ../../ traversal in the archive is harmless — and ignored.

5. How to run the conversation

Do these in order. Do not skip to generating.

  1. Ask what the Hunt (التحدي) is about, and for whom. Subject, age, language, how long it should take. If they answer in Arabic, write the player-facing text in Arabic — using §0's vocabulary.
  2. Ask how many Stages (المراحل) and where they are. Each Stage is a physical place. You cannot invent the Clue (الدليل) — only the Gamemaster knows the room. Ask for a one-line description of each spot, then write the Clue from it.
  3. Propose the Question plan before writing any. "Stage 1: four multiple-choice on units. Stage 2: a word bank of six terms." Get agreement.
  4. Write the Questions. Match the stated level. Prefer 3.1 and 3.2.
  5. Ask about pictures. If any Question needs one, ask the Gamemaster to put the files in a folder — Google Drive, a USB stick, anywhere — and give you the exact filenames. You will reference those names; they will put the files in images/.
  6. Build the preview (§6) and have them read it.
  7. Produce the ZIP and tell them exactly where to click.
Ask one question at a time and wait. A gamemaster who is handed twelve questions at once answers three of them.

6. The preview — build this before the ZIP

Produce a single self-contained HTML file that renders every question the way a player would see it: the stem, the choices, the pictures, the word banks. Dark background, one question per block, the correct answer marked in a collapsed "answer key" section at the end.

This is how the gamemaster checks your work without uploading anything. Typos, a wrong correct_index, a question pitched at the wrong level — all of it is visible here and expensive later.

If you cannot produce a file, print the whole hunt as readable text instead. Do not skip this step.

7. Delivering

Tell them, in these words:

  1. Put the images in a folder called images.
  2. Put hunt.json beside that folder — not inside it.
  3. Select both and compress them into one ZIP. (Zipping the enclosing folder is fine too; the importer looks for the JSON anywhere in the archive.)
  4. In the dashboard, Hunts tab, press ⬆ Import hunt — or, on the Stages tab with the right hunt selected, ⬆ Upload a stage.
  5. Choosing the file only checks it. They get a report: how many stages and questions, which types, which stages have a clue, and every problem or warning. The Import button stays disabled until the bundle is valid, so nothing reaches their hunt list unseen.
  6. The hunt arrives as a draft. Nothing is live until they press Go live.

If the report lists problems, fix them and give them a new ZIP. Each message names the stage and question it came from.

8. What you must not do

9. A complete minimal example


{
  "format": "treasurehunt/hunt@1",
  "title": "Units and Symbols",
  "intro": "Three stops. Bring a pen.",
  "stages": [
    {
      "name": "The Bench",
      "location_hint": "Start at the soldering bench — look under the stool.",
      "n_questions": 2,
      "pass_threshold": 100,
      "questions": [
        {
          "type": "mcq",
          "stem": "What is the unit of resistance?",
          "choices": ["Ohm", "Ampere", "Volt"],
          "correct_index": 0
        },
        {
          "type": "match",
          "stem": "Match each quantity to its unit.",
          "pairs": [
            { "label": "Current", "answer": "Ampere" },
            { "label": "Voltage", "answer": "Volt" }
          ],
          "decoys": ["Watt"]
        }
      ]
    }
  ]
}

10. For developers — the live API

Everything above describes the file. If you are writing software that talks to the server directly instead, the per-endpoint reference lives in AUTHORING-API.md: authentication, hunt.create, stage.create, question.create, the media endpoint, and the rules an authoring tool has to respect.

The file route exists because it needs no credentials and no network access from the agent. Prefer it.