DOCS / JOBS

Jobs: one intent, every step guarded

A job is a compound intent — “bridge, then deposit, then long, then protect it” — compiled into a step plan the runner executes with you: it builds and guard-checks each transaction only when it’s offered, your wallet signs, and settlement waits are verified server-side between signatures. External agents get the same rails through one endpoint: POST /api/jobs.

Try it for $0 right now

dryRun compiles your ask and builds + guard-checks step 1 against live venues — real quotes, real balances, real refusals — but creates nothing, signs nothing, and costs nothing. It’s the test mode and the docs playground:

curl -s https://www.yeetful.com/api/jobs \
  -H "authorization: Bearer $YF_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "ask": "swap 5 usdc from base to arbitrum, then deposit 4 usdc to hyperliquid, then long $12 of eth on hyperliquid, then protect my eth long with a 5% stop",
    "dryRun": true
  }'

Mint the yf_ key at /dashboard/keys — or skip the key entirely and run the repo’s demo script, which can also authenticate as your wallet via SIWE:

npx tsx scripts/jobs-api-demo.ts            # dryRun against prod
npx tsx scripts/jobs-api-demo.ts --live     # actually creates the job

What comes back

Real output (a funded test wallet, live NEAR Intents quote — trimmed for width). Note what’s in it: the compiled plan, and step 1 as an actual transaction with its guard report and priced value — not a description of one.

{
  "dryRun": true,
  "title": "Bridge 5 USDC (base) → USDC (arbitrum) → Deposit 4 USDC to
            Hyperliquid → Long $12 of ETH on Hyperliquid → Arm stop-loss on ETH (5%)",
  "steps": [
    { "seq": 0, "kind": "sign", "builder": "native-cross-chain", "title": "Bridge 5 USDC …" },
    { "seq": 1, "kind": "wait", "builder": "wait", "waitPredicate": { "kind": "oneclick" } },
    { "seq": 2, "kind": "sign", "builder": "native-hl-exec", "title": "Deposit 4 USDC …" },
    { "seq": 3, "kind": "wait", "builder": "wait", "waitPredicate": { "kind": "hl-credit" } },
    { "seq": 4, "kind": "sign", "builder": "native-hl-exec", "title": "Long $12 of ETH …" },
    { "seq": 5, "kind": "auto", "builder": "native-hl-guardian", "title": "Arm stop-loss …" }
  ],
  "firstSignPreview": {
    "step": 0,
    "artifact": {
      "txRequest": { "to": "0x833589…2913", "data": "0xa9059cbb…", "chainId": 8453 },
      "summary": "Send 5.0 USDC on Base to NEAR Intents deposit address 0xA046…917E —
                  solvers then deliver ~4.99 USDC on Arbitrum (ETA ~34s)"
    },
    "guardReport": { "ok": true, "warnings": [] },
    "valueUsd": 5
  },
  "note": "Nothing was created or signed — re-POST without dryRun to run it."
}

If the build can’t clear its guards, you get the reason instead of the artifact — same shape, refused instead of artifact. A real one: “Wallet holds only 2.52 USDC on Arbitrum — bridge funds there first (cross-chain swap).” The layer refuses honestly; it never guesses.

Step kinds

KindWho actsWhat happens
signYour walletBuilt + guard-checked at offer time (fresh quotes, current balances), then offered for signature. Artifacts expire; stale ones are rebuilt, not reused.
waitThe runnerSettlement verification — solver fill, Hyperliquid credit — polled server-side. Waits verify; they don’t trust the previous step’s word.
autoThe runnerServer-side under an existing consent — e.g. arming a Guardian stop once the position exists.

dryRun vs live

dryRun: truedryRun: false
Compile + plan preview
Step 1 built + guarded live
Rows creatednonethe job + its steps
Can be signednostep by step, from the chat JobCard or your own UI
Cost$0whatever you sign — and only that

Auth, and the rest of the surface

  • Two auth paths, full parity: a SIWE session (the browser) or Authorization: Bearer yf_… (a headless agent). Same compile, same builds, same list at GET /api/jobs.
  • Watch a job: GET /api/jobs/:id returns the job + steps with artifacts. Replies that compile a job also carry a per-job capability token — it’s how the embedded chat’s JobCard follows a job without a session, scoped to exactly that job.
  • Cancel: DELETE /api/jobs/:id. Unfilled swaps auto-refund; waits time out closed, never open.
  • Bad asks refuse honestly: a single-step ask belongs to the native layer(400 tells you so), and a compound ask with an uncompilable segment names the exact step it couldn’t build instead of improvising one.

Where jobs show up

The same compiler answers everywhere: the first-party chat, the embed on your site, and this API. A user typing the ask above gets a JobCard with a sign button per step; your agent POSTing it gets the same steps as JSON. Signed value lands in your dashboard either way — the receipt is the product.