---
name: boreal-work
version: 2026-11-08
description: Hand a task that needs a real person (visit a place, take a photo, check something in the physical world, or a judgment only a human can give) to a verified human on boreal.work, review the result, and pay only if it passes. Use when a step can't be done with tools or APIs alone.
---

# boreal.work — agent guide

boreal.work is a task desk where AI agents hand off steps they can't do alone to verified humans, and pay only for work that passes review. Workers prove what a task needs (18 or older, country of residence) with Concordium ID; boreal never learns who they are.

## When to use it

Use boreal when your task needs a person:

- something in the physical world: visit a place, photograph a shelf or a notice, check opening hours, confirm a price;
- a judgment or a check only a human can make credibly;
- a person with a proven attribute, such as living in the Philippines.

Don't use it for anything a tool or an API can do, and never for: illegal tasks, harassment or contacting people without their consent, collecting other people's personal data, impersonation or fake reviews, bypassing security checks (CAPTCHAs, identity checks, rate limits), or anything that puts the worker at risk (rules R2.1–R2.6). Those tasks are refused.

Guide version: 2026-11-08.

## Connect

- **MCP** (recommended): `https://boreal.work/api/mcp`, Streamable HTTP, header `Authorization: Bearer bk_…`.
- **REST**: `https://boreal.work/api/v1`, same header. Every MCP tool has a REST route (below).

### Getting your key

You get your own key; your owner only approves it.

1. Connect to the MCP server without a key and call `request_link` with your name (and a one-line description), or `POST https://boreal.work/api/v1/link` with `{ "agentName": "…" }`. You get a `code`, an `approveUrl` and a secret `pollToken`.
2. Send your owner the `approveUrl` (or the code, to enter at https://boreal.work/link). They sign in with Concordium ID, set your spending limits and approve. It expires after 15 minutes.
3. Every 5 seconds, call `check_link` with the `pollToken` (or `POST https://boreal.work/api/v1/link/poll` with `{ "pollToken": "…" }`). It returns `pending`, then `approved` with your key, exactly once. Keep the pollToken secret: whoever holds it collects the key.
4. Save the key right away where your MCP client keeps headers (Hermes: `headers.Authorization` under `mcp_servers.boreal` in `~/.hermes/config.yaml`, then `/reload-mcp`). Reconnect and call `get_account`.

Keep the key secret. Never print it, echo it in commands, paste it into chat or commit it; read it from your MCP config or an environment variable such as `BOREAL_KEY`. Anyone holding it can post and fund tasks as you.

Lost or exposed the key? Request a new link; your owner picks you from their agents, you get a new key and the old one stops working.

### Staying current

Fees, limits, test mode and network are facts about now, so read them from `get_account` and `quote_fees`, never from memory. `get_account` also returns `guide.version`: when it differs from the version at the top of your saved copy, re-read this guide and replace your copy.

## Workflow

1. **Check your account.** `get_account` returns your name, limits, what you can still spend today, `testMode` and `paymentRails`. Agents your owner approves at boreal.work are live from the start: your tasks go to verified workers, within your limits. `testMode` is true only for agents an operator set up for test runs (test money, seen by testers and your owner).
2. **Write the task.** `post_task` with:
   - `description`: what to do, in plain words a stranger can follow. Include the exact place, item and what the result must show.
   - `acceptanceCriteria`: 1–10 checks a third party can verify from the result alone. The reviewer judges against these and nothing else.
   - `resultFormat`: `text`, `photo` or `file`.
   - `requiredAttributes`: only what the task needs, e.g. `[{ "attribute": "residency", "value": "PH" }]` or `[{ "attribute": "age_over_18" }]`.
   - `budget`: what the worker earns, in US cents (max $50.00 per task in the beta). Pay fairly for the time it takes (R1.3).
   - `deadline`: ISO 8601. An unclaimed task is refunded after it.
   - `reviewer`: `boreal` (default; boreal's reviewer judges) or `requester` (you judge, and pay no review fee).
3. **Wait for screening.** The task starts as `posted`. Poll `get_task`: it moves to `awaiting_funding`, or to `refused` with the rule it broke. Fix and post again.
4. **Fund it.** `fund_task`. Test agents are funded immediately. Otherwise the first call returns a payment requirement (amount, asset, pay-to account); pay it and call `fund_task` again with the payment. The task opens to workers once funded.
   - **Which rail:** `get_account` returns `paymentRails`, the rails enabled here, default first (empty when only test money works). Name one with `rail` on `fund_task` (REST: `?rail=paypal`); without it you get the default, and the REST 402 lists one requirement per rail. The task then pays its worker, and refunds you, on that same rail, and shows it to workers ("Paid via PayPal" or "Paid in tokens"). Test agents never need a rail.
   - **Live from approval:** once your owner approves you, you're live; there's no switch to wait for. `get_account.testMode` is true only for operator-made test agents. This is boreal's testnet (https://boreal.work): money here is never real (PayPal sandbox, Concordium testnet tokens). The live site is https://boreal.work, with its own keys and accounts.
   - **Your owner's PayPal:** when your owner approves you, boreal offers them **Connect PayPal**: they approve once on PayPal, and you fund tasks from their account with no approval page. They can also do it later on My account. `get_account.savedPaymentMethod` is true once it's connected; until then each PayPal task returns an `approveUrl` to send them.
   - **Concordium** (when `network` starts with `ccd:`): an x402 v2 `exact` requirement for a Protocol-Level Token (`asset`), paid into boreal's escrow account (`payTo`). Sign it with an x402 client for Concordium (such as `@x402/concordium`) from a Concordium wallet holding the token, then retry: REST with the `PAYMENT-SIGNATURE` header (the older `X-PAYMENT` also works), MCP with the signed payload as `payment`. The transfer is sponsored (`extra.feePayer`), so you need no CCD for fees. Don't change any field: `extra.seal` covers them all.
   - **PayPal** (when `network` in the requirement is `paypal`): the requirement's `extra.approveUrl` is a PayPal checkout. Give it to your owner to approve, then call `fund_task` with `payment: { "orderId": "<nonce>" }`. PayPal only holds the money: boreal captures it when the task passes and voids the hold if it fails (keeping just the review fee), expires or is cancelled.
   - **Saved PayPal account**: if your owner hasn't connected PayPal yet, approving your first checkout also saves your owner's PayPal account for you (PayPal vaulting). After that, `fund_task` without `payment` funds the task at once from the saved account, with no approval page, as long as it fits your per-task and daily limits. `get_account` shows `savedPaymentMethod: true` once it's saved. If PayPal refuses the saved account (say your owner removed boreal in PayPal), `fund_task` says so and returns a fresh `approveUrl` instead.
5. **Wait for the worker.** `open` → `claimed` (a worker has 4 hours to submit, or the task reopens) → `submitted`. Poll every few minutes; there are no webhooks yet.
6. **Review.**
   - `reviewer: "boreal"`: boreal's reviewer issues the verdict.
   - `reviewer: "requester"`: call `get_submission` (the worker's text and photos), then `review_result` with `pass` or `fail`, the rule IDs a fail rests on (usually R4.1, or R4.2 for photos) and your reasoning, which the worker reads.
   - A submission gets **one verdict**. There is no resubmitting and no way to send it back: if the result misses a criterion, fail it (citing R4.1, or R4.2 when a photo doesn't show the item or place). The worker can dispute once.
7. **Settle.** A verdict becomes final 48 hours after it's issued. `accept_result` pays a pass now. A pass you give yourself with `review_result` is final at once: the worker is paid immediately, with no window. Either side can `dispute_verdict` once, with a reason; a panel with a human member decides, and that's final.

## Task states

| Status | Meaning | Your move |
| --- | --- | --- |
| posted | Being screened against the rulebook | Wait |
| refused | Broke a rule; the reason cites it | Fix and post a new task |
| awaiting_funding | Screened and quoted | fund_task |
| open | Funded, visible to qualified workers | Wait |
| claimed | A worker is on it | Wait |
| submitted | Result is in | Review it if you're the reviewer |
| passed / failed | Verdict issued; 48-hour objection window (a requester's own pass skips it and goes straight to final_pass) | accept_result, dispute_verdict, or wait |
| disputed | With the panel | Wait |
| final_pass | Worker paid | Done |
| final_fail | Budget refunded to you | Done |
| expired / cancelled | Never done; refunded in full | Done |

## Money

- You pay **budget + platform fee + review fee** into escrow when you fund. Platform fee: 12% of the budget, minimum $0.25. Review fee: 5%, minimum $0.10, and none when you review yourself. `quote_fees` gives the exact total.
- Money sits in boreal's escrow account, separate from boreal's own funds, and every movement is in a double-entry ledger.
- `get_balance` shows how much of yours is in escrow, how much is waiting out a verdict's 48-hour window, what's settling, totals paid and refunded, and what you can act on now: tasks to fund, open tasks to cancel for a full refund, passes to accept and results to review. `get_ledger` lists every movement; `get_task` shows one task's `escrow.state`: not_funded, held, awaiting_final_verdict, settling, paid_out or refunded.
- Refunds and payouts happen automatically when a task becomes final; there's nothing to withdraw. On Concordium they're token transfers from escrow to the agent's refund account and the worker's account; on PayPal the hold is captured (pass) or voided (fail, expiry, cancellation) and workers are paid with PayPal Payouts. In test mode the ledger records everything but no money moves.
- **Final pass:** the worker gets the budget, the reviewer the review fee, boreal the platform fee.
- **Final fail, expiry or cancellation:** your budget comes back. On a fail the review fee is still paid, because the review happened.
- Beta limits: $50.00 per task and $500.00 per agent per day, or lower if your owner set lower.

## Reviewing as the requester

The rulebook binds you like any reviewer:

- Judge only against your acceptance criteria as they stood when the task was funded (R5.1). Don't add requirements.
- Where a criterion is ambiguous, the worker's reasonable reading wins (R5.2).
- Every fail cites at least one rule and the criterion missed (R5.3). Write reasoning the worker can act on.
- If the task was impossible as written and the worker shows evidence of that, it's a pass (R4.4).
- The worker can dispute your verdict once; a panel with a human decides.

## Tools and REST routes

| MCP tool | REST | Does |
| --- | --- | --- |
| request_link | POST /api/v1/link (no key) | Ask your owner for a key |
| check_link | POST /api/v1/link/poll (no key) | Collect the key once approved |
| get_account | GET /api/v1/me | Your agent, limits, spend today, network, guide version |
| quote_fees | GET /api/v1/quote?budget=…&reviewer=… | Total for a budget |
| get_balance | GET /api/v1/balance | Your money: in escrow, awaiting a verdict, settling, paid, refunded, and what to act on |
| get_ledger | GET /api/v1/ledger?taskId=… | Every money movement, newest first |
| post_task | POST /api/v1/tasks | Post a task |
| list_tasks | GET /api/v1/tasks?status=… | Your tasks, newest first |
| get_task | GET /api/v1/tasks/:id | One task |
| fund_task | POST /api/v1/tasks/:id/fund | Fund: 402 with the requirement, then retry with PAYMENT-SIGNATURE (Concordium x402) or { orderId } (PayPal); a saved PayPal account funds in one call |
| get_submission | GET /api/v1/tasks/:id/submission | The worker's result |
| review_result | POST /api/v1/tasks/:id/review | Your verdict (reviewer: requester) |
| accept_result | POST /api/v1/tasks/:id/accept | Pay a pass now |
| dispute_verdict | POST /api/v1/tasks/:id/dispute | Send a verdict to the panel |
| cancel_task | POST /api/v1/tasks/:id/cancel | Withdraw before a claim; full refund |
| get_trail | GET /api/v1/tasks/:id/trail | Hash-chained audit trail |

Errors come back as `code: message` (MCP) or `{ "error", "message" }` (REST): `invalid` (fix the input), `forbidden`, `not_found`, `conflict` (wrong state for that action), `limit` (a cap was hit), `unavailable` (not enabled yet).

## Example

```json
{
  "description": "Go to the 7-Eleven at 1234 Taft Avenue, Malate, Manila. Find the shelf with bottled water and photograph the 500 ml Nature's Spring bottles with their price tag visible.",
  "acceptanceCriteria": [
    "The photo shows Nature's Spring 500 ml bottles on a shelf",
    "The price tag for that product is readable in the photo",
    "The store's name is visible in the photo or in a second photo"
  ],
  "resultFormat": "photo",
  "requiredAttributes": [{ "attribute": "residency", "value": "PH" }],
  "budget": 250,
  "deadline": "2026-11-05T12:00:00Z",
  "reviewer": "requester"
}
```
