Sign inTry it

Guides

Contributing recipes

How public recipes are written, checked and reviewed.

A recipe teaches Wield one task on one website: "search train connections on bahn.de", "list my Doctolib appointments". It is a JSON file that describes the HTTP calls the site's own pages make. It contains no code. Wield runs it inside a tab of that site in the user's own browser, with the user's own session, and sends requests only to the origins the recipe lists.

Every recipe runs on other people's machines with their logins. So every recipe passes automated checks and a human review before it is public. This page lists both.

Three ways to make a recipe

  1. Record it (easiest). In the Wield extension, click Record, do the task once, click Stop. The extension removes cookies, tokens, personal data and the values you typed on your device, and the builder drafts the recipe. Your own browser then runs the draft's test plan. Hosted builds are part of the Pro plan.
  2. From a HAR file (free, local). Record the task in Chrome DevTools (Network → Preserve log → Save all as HAR). Keep HAR files out of any repository: they contain your cookies. Then: ``bash node packages/builder/dist/cli.js trace flow.har --task "search used bikes" --input "Suchbegriff=fahrrad" -o flow.trace.json node packages/builder/dist/cli.js derive flow.trace.json --id example.de/search-bikes -o draft.json # needs ANTHROPIC_API_KEY, or --llm file:<dir> ` trace` sanitizes on your machine; only the trace goes to the model.
  3. By hand. Study the site's calls in DevTools and write the JSON against the recipe format. Then run wield-build lint and wield-dev run.

Whichever way you choose, the result must pass the checks below.

Before you submit

  • [ ] wield-build lint recipes/<site>/<slug>.json reports no errors. Warnings have been read, and fixed or explained in provenance.notes.
  • [ ] The recipe ran live and passed its verify.checks: in your browser (extension test plan, or wield-dev run for public reads), with the date and how it was checked in your submission.
  • [ ] The task is something people would ask an agent to do, and the output answers it: typed values (numbers, booleans, ISO dates, absolute links), no internal ids the user can't use, no fields "just in case".
  • [ ] Ads, sponsored and promoted results are dropped by default (a parameter may bring them back, marked).
  • [ ] task is a short user-facing headline. description says what comes back and what is filtered. keywords cover the site's language and English.
  • [ ] provenance says how each call was found. Every host outside the site's own domain is named and explained.
  • [ ] tos.notes says what the site's terms say about automated access, if known, and how the recipe stays polite.

Automated checks

These run on every submission (wield-build lint runs them all). Errors block publishing.

Schema and lint

The JSON Schema (contracts/recipe.schema.json) and the format lint: template references exist, JMESPath compiles, ids are unique, step URLs stay on site.origins, no Cookie/Host headers, output never references vars. It also warns about two JMESPath traps: params.x inside [? … ], and !a.b.

Safety rules

CodeSeverityRuleWhyHow to fix
S1 origin-unusederrorEvery origin in site.origins is called by some stepEach listed origin widens what the executor permitsRemove the unused origin
S2 origin-unjustifiederrorA host outside the site's registrable domain (also a sister domain such as .com for a .de site) is named and explained in description or provenance.notesRequests to other sites must be deliberate and reviewableName the host and say what it is ("the site's own search API on api.example-cdn.com")
S3 dynamic-hostwarning (error for start_path starting with //)The destination is not decided at run time from a templateThe executor still blocks unlisted origins, but reviewers should see where calls goUse a fixed origin and a templated path
S4 secret-to-third-partyerror, warning when the host is justifiedValues derived from the session (vars.*: cookies, tokens) are not sent to another siteThat is how a recipe would exfiltrate a sessionKeep tokens on the site's own origins
S5 data-to-third-partyerror for login recipes, else warningData read from the site is not passed to another siteLogged-in pages hold personal dataJustify the host, or don't send the data
S6 credentials-third-partywarningCross-site calls use "credentials": "omit" unless they need cookiesDon't attach the user's cookies for another site without needSet omit
S7 write-unmarkederrorPUT, PATCH, DELETE, GraphQL mutations and POSTs to booking/ordering/sending endpoints are "write": trueWrites must go through the Allow windowMark the step, and set effect: "write"
S7 post-readwarningA read-only POST (search APIs) says so in the step descriptionReviewers need to know why a POST doesn't change anything"search API, read-only POST"
S8 write-looperrorA write step never loops (foreach.max must be 1)One Allow must cover exactly what is sentSplit into one write
S9 write-no-confirmerrorWrite recipes have confirm.summary, with template values (warning if static)The user must see what will be sent, to whom, when"Book {{params.slot}} with {{steps.practitioner.name}}"
S10 write-no-loginwarningWrites normally require loginAnonymous writes are unusualDouble-check
S11 secret-literalerrorNo token, API key or session value is written into the recipeLiterals leak and expire; tokens must come from the user's session at run timeUse auth.derive (meta tag, inline script, cookie) or extract from an earlier step. A public key that the site ships to every visitor may stay if provenance.notes says so and names it (then it is only a warning). Persisted GraphQL query hashes are fine
S12 personal-literalerrorNo email, phone number, IBAN or card number anywhere, including verify.example_paramsRecipes are publicUse neutral examples
S13 output-varserroroutput.value never reads varsVars can hold tokens; output goes to the agentReturn only page data
S14 login-check-missingerrorrequires_login recipes have a login_check: a cheap GET that passes only when logged inWield must tell "not logged in" apart from "broken"Add one, with expect.login_required_if
S15 request-budgetwarning above 15, error above 40 requests per runRuns stay smallSites allow about 30 requests per hour per userFewer steps, smaller loops
S16 pacingwarningLoops of more than 5 requests have delay_msBursts trip bot protection and burden the siteAdd a delay, or shrink the loop
S17 account-datawarningCalls to account-looking paths (/account, /orders, /my) set requires_loginA recipe that needs a session must say soSet requires_login and add a login check
S18 provenance / toswarningprovenance and tos.notes are filled inReviewers and users need to know where the recipe came fromFill them in

Live verification

Lint can't tell whether a recipe works. Every recipe is run live before it is public:

  • Public reads: the test plan (example params, your recorded values, a variant, a zero-results search) passes in a browser, or with wield-dev run where the site answers servers.
  • Login reads: the author runs it logged in; the login check passes, and fails when logged out.
  • Writes: the preview stops at the Allow window and shows the right summary; then the author runs it once for real, with their own data, only if they want the effect. Until that has happened the recipe stays a draft.

After publishing, health checks run every public read recipe daily from our servers where the site allows bots (robots.txt) and answers them. Login, write and browser-only recipes are judged by opt-in run telemetry. A recipe that fails twice in a row is marked degraded on its page and in find_recipe, and the builder tries a repair from a fresh public sample. Repairs come back as new versions for review.

What we accept, and what we don't

We accept recipes for tasks people do on a site with their own account or in public view: searching, comparing, checking their own bookings, orders and messages, and, with the user's Allow, booking, ordering and messaging.

We don't accept recipes that:

  • sign in, store or ask for passwords, or capture credentials in any way. Users log in themselves in their browser;
  • solve, skip or work around CAPTCHAs, bot checks, rate limits or paywalls. If a site blocks automated requests, the recipe is not for us;
  • collect other people's personal data (profiles, contact details), or download whole catalogues;
  • send requests anywhere other than the site and the services it uses itself;
  • contain code, obfuscated values, or requests the description does not explain.

Review checklist (maintainers)

  1. Destinations. Every origin belongs to the site, or is the site's own service and is explained. No templated hosts without reason.
  2. What is sent. Read every step: URL, query, headers, body. Nothing from the session goes anywhere but the site. Only needed headers.
  3. Reads really read. No POST that changes state without write: true. Check GraphQL operation names.
  4. Writes. confirm.summary names exactly what happens (who, what, when, how much). One write per Allow. Login check present.
  5. Output. Only what the task needs. No tokens, no internal ids that leak, no other people's personal data beyond what the user is looking at (for example a practice's public address is fine; other customers' names are not).
  6. Politeness. Request count per run, loops, delays. One run should look like one person using the site.
  7. Verification evidence. Date, method (browser, wield-dev), the checks that passed. For writes, who ran the live test.
  8. Terms. tos.notes is honest; sites that forbid automated access in their terms, or actively block it, are declined.
  9. Naming and format. id is <site>/<task>, task is a user-facing headline, params have clear names, descriptions and examples.

Versions

Never change a published version in place. A fix is a new version: the old file stays as recipes/<site>/<slug>.v<N>.json (pinned), and <slug>.json holds the latest one with a higher version. Keep id, params and output shape stable unless the change is unavoidable, because agents depend on them. Say what changed in provenance.notes.

Reporting problems

  • A recipe that doesn't work: use "Report" on its page, or request_recipe from your agent with the site and what failed.
  • A security issue (a recipe that sends data where it shouldn't, a way around the Allow window): write privately to the contact on the privacy page. Please don't open a public issue.