Sign inTry it

How to write a Wield recipe, step by step

A Wield recipe is one JSON file that teaches Wield one task on one website: for example, searching train connections or listing a site's search results. It describes the HTTP requests the site's own pages make, the parameters they take, how to read the responses, and how to check the output. It contains no code. Wield runs it in a tab of the site, in the user's own browser, and sends requests only to the origins the recipe lists.

Updated

Anatomy of a recipe

A recipe has these top-level parts:

PartWhat it holds
siteThe site's id, name, origin, the origins a request may use, country and language
task, description, keywordsWhat the recipe does, what comes back, and words that help agents find it
effect and requires_loginread or write, and whether a logged-in session is needed
paramsA JSON Schema for the inputs the agent may pass
stepsThe requests, in order, with how to read each response
outputThe shape of the result, with a JMESPath expression that builds it
verifyExample parameters and checks the output must pass

The format is documented in the recipe format reference. Use it as the source of truth.

Declare the site and the params

Set site.origin to the tab the recipe runs in. List every origin a request may use in site.origins. Requests to any other host are blocked, and redirects are not followed.

Write params as a JSON Schema object. Give every property a description with examples, because agents read them. Wield checks the params before anything is sent and fills in defaults. Keep additionalProperties set to false, so unexpected input is rejected.

Write the steps

Each step has an id and a request. Use {{ params.name }} to insert a parameter into a URL, query or body. Keep to these rules:

  • Up to 12 steps per recipe, and at most 40 requests per run.
  • Cookie and Host headers are not allowed. The browser sends the site's cookies itself.
  • Use expect to check the status, for example "status": [200].
  • Use extract to read values from the response, for example with a JMESPath expression on json.
  • A GET step starts as soon as the steps it references are done, so independent lookups run in parallel.

Here is the small example from the recipe format reference:

json
{
  "schema": "https://wield.run/schema/recipe/v1.json",
  "id": "example.de/search-items",
  "version": 1,
  "site": {
    "id": "example.de",
    "name": "Example",
    "origin": "https://www.example.de",
    "origins": ["https://www.example.de", "https://api.example.de"],
    "country": "DE",
    "language": "de"
  },
  "task": "Search items by keyword and maximum price",
  "description": "What comes back, and what is filtered (e.g. sponsored results are dropped).",
  "keywords": ["suche", "artikel", "search", "items"],
  "effect": "read",
  "requires_login": false,
  "params": {
    "type": "object",
    "properties": {
      "query": { "type": "string", "minLength": 2, "maxLength": 100 },
      "max_price": { "type": "integer", "minimum": 1 },
      "limit": { "type": "integer", "default": 20, "maximum": 50 }
    },
    "required": ["query"],
    "additionalProperties": false
  },
  "steps": [
    {
      "id": "search",
      "request": {
        "method": "GET",
        "url": "https://api.example.de/v2/search",
        "query": { "q": "{{ params.query }}", "price_to": "{{ params.max_price }}", "size": "{{ params.limit }}" }
      },
      "expect": { "status": [200] },
      "extract": { "json": { "total": "meta.total", "items": "results[?!sponsored]" } }
    }
  ],
  "output": {
    "schema": { "type": "object", "required": ["items"] },
    "value": "{total: steps.search.total, items: steps.search.items[].{title: title, price_eur: price.amount, url: join('', ['https://www.example.de', path])}}"
  },
  "verify": {
    "example_params": { "query": "fahrrad", "max_price": 300 },
    "checks": ["length(output.items) > `0`", "output.items[0].price_eur <= `300`"]
  }
}

Describe the output and verify it

output.schema is a JSON Schema for the result. output.value is a JMESPath expression that builds it from the steps. Use typed values: numbers, booleans, ISO dates and absolute links. Drop ads, sponsored and promoted results by default.

verify.checks are JMESPath expressions that must return true on the output of verify.example_params. Write checks that would fail if the site changed its answer, such as a minimum result count or a price limit.

Writes

A recipe that changes data must set effect to write, and mark the write step with write: true. Before the first write request, Wield stops and shows the user an Allow window with every request and body. Deny is the default. Write recipes are not tested automatically, because testing would send real data. Run one under supervision first.

Check it

  1. Run wield-build lint on the file. Read every warning and fix it, or explain it in provenance.notes.
  2. Run the recipe live with its example parameters. For public read recipes, wield-dev run works from the command line. For anything that needs a login, run it in your own browser through the extension.
  3. Confirm that every check in verify.checks passes.
  4. Write down how each request was found, and name every host outside the site's own domain.
  5. Note what the site's terms say about automated access, in tos.notes.

Contributions are reviewed by a maintainer before they become public. The contributing guide lists the full checklist.

FAQ

Can a recipe run JavaScript on the page?

No. Recipes are data, not code. They describe requests and how to read responses. They cannot run scripts in the page or read anything outside the responses they asked for.

What are the limits?

A run allows 12 steps, 20 loop iterations, 40 requests, 5 MB per response and 60 seconds in total. These limits protect the site and the user's browser.

Where do I start if I am not sure what the site sends?

Capture the task in your browser's developer tools, then read the JSON request it makes. See how to find a site's internal API in DevTools.