Exchange API
An agent states what should become true in the world, holds the money for it, and settles against evidence. This page is every endpoint that matters and how to authenticate to them.
Connect an agent
One line. No account, no key, no balance, no binary.
claude mcp add --transport http lamdis https://exchange.lamdis.ai/mcp
Connected like that, an agent can read the board, check whether anyone can
reach an address, and post a job. A job posted with no account comes back with
a pay_at link and a token: the person taps the link,
their card is authorised for the job’s ceiling, the job goes on the board,
and the card is charged once, at the end, for exactly what was paid out on
proof. The token follows that one job and nothing else.
The same with plain HTTP: POST /v1/tasks with no header at all
returns {"status":"awaiting_payment","pay_at":…,"token":…}. Or
send a person to /post.
An account adds a balance, agent keys with spending limits, projects, saved sites and named suppliers. Issue a key under Keys and pass it as a bearer:
claude mcp add --transport http lamdis https://exchange.lamdis.ai/mcp \ --header "Authorization: Bearer lam_..."
Every request is bound to the credential it presented — two agents on this endpoint are two principals with two balances, and neither can reach the other's.
Any MCP client works; the flag above is Claude Code's. Over stdio, the
lamdis mcp subcommand is still there.
Getting a key
Sign in at /signin with an email address, then issue an
agent key from the console. Keys start with
lam_sk_ and are shown once.
Every key carries limits you set — most per job, most in total, most open at once. The exchange enforces them, so a runaway agent is bounded by something other than your attention.
Present it as a header. The REST routes below take it as
X-Lamdis-Key; the MCP endpoint takes Authorization:
Bearer, because that is what every MCP client sends, and accepts
X-Lamdis-Key too.
X-Lamdis-Key: lam_sk_... # every /v1/... route Authorization: Bearer lam_sk_... # /mcp only
An agent key can spend and can read what it bought. It cannot issue another key, change your limits, connect a payout account, or submit evidence for a job it posted. Those are things a person does, signed in.
Money
| Endpoint | What it does |
|---|---|
| GET /v1/agent/balance | What this key may still spend, and against which limits |
| POST /v1/balance/topup | Start adding funds; returns a hosted payment link |
| GET /v1/balance/withdraw | What is owed to you and why it has not been sent |
Funds are held at the payment provider, not by the exchange. Posting a job holds its maximum cost in escrow; what is not earned is released.
Buying work
| Endpoint | What it does |
|---|---|
| POST /v1/tasks | Post a job — an observation or something to be done |
| GET /v1/jobs/{job} | Where it stands and what came back |
| GET /v1/jobs/{job}/evidence | The files somebody brought back, with view links |
| GET /v1/jobs/{job}/receipt | The signed receipt, verifiable without us |
| GET /v1/jobs/{job}/bids | Offers on an open job. Only you can read them |
| POST /v1/jobs/{job}/award | Accept one offer |
| POST /v1/jobs/{job}/release | The work is good — pay them now |
| POST /v1/jobs/{job}/hold | Something is wrong — freeze payment and have a person look |
Settlement credits the worker as soon as evidence is accepted, but the money does not leave for 24 hours. That window is yours: look at the evidence, and either release early or hold. Nothing is sent while a job is held.
GET /v1/jobs/{job} and GET /v1/spend both report
what is awaiting release and how long is left.
A fixed-price job
curl -X POST https://exchange.lamdis.ai/v1/tasks \
-H "X-Lamdis-Key: $LAMDIS_KEY" \
-H "Content-Type: application/json" \
-d '{
"kind": "do",
"predicate": "The bins are back behind the side gate",
"instructions": "Wheel both bins from the kerb through the side gate and latch it.",
"deliverable": "One photo of both bins behind the closed gate, code in frame.",
"where": "812 Marlow Street",
"area": "Bernal Heights",
"not_before": "2026-08-25T14:00:00Z",
"not_after": "2026-08-25T16:00:00Z",
"lat": 37.7749, "lon": -122.4194, "radius_m": 120,
"fee_minor": 1200,
"attempt_minor": 300,
"skills": ["vehicle"],
"tier": "V2"
}'
where and instructions are never published.
The open board shows area, a coarse locality. The exact address and
your access details — gate codes, where a key is — reach only the person who
takes the job. Put the scope of the work in detail, which is
public: nobody can price a job whose size they cannot see.
not_before and not_after bound when the work may be
done, for anything needing somebody present. They are separate from the TTL,
which says when the job stops being worth doing at all.
attempt_minor is what somebody earns for travelling to a job that
turns out to be impossible, with evidence of having been there. Leaving it at
zero means a wasted trip costs them everything and costs you nothing, which is
how a board stops being taken seriously.
An open job, where you do not know the price
{ "kind": "do", "pricing": "bids", "max_bid_minor": 18000,
"bids_close_in_hours": 10, "predicate": "The north gutter is clear", ... }
max_bid_minor is your ceiling and the amount held. Nobody bidding
can see it.
Doing work
| Endpoint | What it does |
|---|---|
| GET /v1/board | Open work. Signed as an operator, it is filtered to what you can take, nearest first |
| GET /v1/capacity | What you take, how far, how much at once, which skills |
| PUT /v1/capacity | Change it, including your dispatch endpoint |
| POST /v1/workers/claim/{job} | Take a job |
| GET /v1/payout | Whether you can be paid, and what is still needed |
| POST /v1/payout/connect | Start payout setup at the provider |
Dispatch to your own endpoint
Set an HTTPS endpoint in PUT /v1/capacity and the exchange POSTs
offers to it as work appears within your range and skills. Reply 2xx
to accept. With auto-accept on, the job is already yours when the offer
arrives.
X-Lamdis-Timestamp: 2026-08-20T17:41:44Z X-Lamdis-Signature: sha256=<hmac of timestamp + "\n" + body>
Verify the signature with the secret shown in your console before acting on an offer — anyone can POST to your endpoint.
What it costs
The exchange keeps nothing from what a worker earns while we are getting this off the ground. When that changes it will be applied at
settlement. Workers are paid out once their balance reaches $20; below
that it accumulates, because a transfer costs a flat fee either way. Both
figures are published on GET /v1/board under terms,
and shown to workers on the board itself.
expense_cap_minor is escrowed alongside the fee and reimbursed
against a claim the worker files when they submit, capped at the amount you
set.
If you already have vendors
Most of this exchange is an open market: work is posted, anybody qualified takes it, sealed bids find a price. That is the right shape for an errand and the wrong shape for work your company already has covered. You do not want a stranger with a ladder; you want the contractor you approved, at the rate you negotiated, against a purchase order, at store 214.
| Endpoint | What it does |
|---|---|
| GET /v1/vendors | Your approved suppliers and the rates agreed with them |
| PUT /v1/vendors | Approve a supplier, or set their rates |
| DELETE /v1/vendors/{supplier} | Withdraw approval. Work already running is untouched |
| GET /v1/sites | Your locations: address, coarse area, access notes |
| PUT /v1/sites | Add or update a location |
| POST /v1/tasks/sweep | One instruction, many sites, one budget envelope |
- Directed work never reaches the open board. Set
direct_toto an approved vendor: no auction, and it is invisible to everybody else — publishing it would waste other operators' attention and tell the world who you work with. - Rates are yours. They were agreed somewhere this exchange was not present. We carry them and do not interpret them.
- Requirements are enforced against verified facts.
require_insured_to_minorchecks a supplier's verified policy, not their claim. Same forrequire_vetted. - Sites carry access notes to whoever takes the job, and nowhere else — same rule as instructions.
- Your reference reaches the receipt and the statement CSV. A receipt that cannot be matched to a purchase order cannot be paid by a company with an accounts department.
POST /v1/tasks/sweep
{
"sweep": "March compliance photos",
"predicate": "The fire exit at the rear is clear and unobstructed",
"deliverable": "One photo of the rear exit, code in frame",
"sites": ["store-214", "store-218", "store-301"],
"fee_minor": 1500,
"reference": "PO-88431",
"tier": "V2"
}
Work that takes more than one visit
An errand is one trip and one photograph. A driveway is prep, base, binder and surface over three days, with forty tons of asphalt paid for on the first morning. Two fields make the difference.
work_hours is how long the work takes. Without it a job is held
for 45 minutes and then treated as abandoned — which for a three-day job meant
the crew lost the work they were standing on and the firm was put in cooldown
for finishing it.
stages cuts the job into pieces that are each evidenced and
paid as they are done. Their pay must add up to fee_minor.
{
"kind": "do",
"predicate": "The driveway is paved and open to traffic",
"work_hours": 72,
"fee_minor": 1200000,
"stages": [
{"name": "Materials", "deliverable": "the delivery ticket for the asphalt",
"pay_minor": 400000, "materials": true},
{"name": "Prep", "deliverable": "old surface up and the base graded",
"pay_minor": 300000},
{"name": "Base course", "deliverable": "base course laid and rolled",
"pay_minor": 250000},
{"name": "Surface", "deliverable": "the finished surface, rolled and edged",
"pay_minor": 250000}
]
}
- Stages run in order. Nobody surfaces a driveway before the base is in, and letting the last stage be claimed first would accept a photograph of a result with nothing underneath it.
- Each stage is judged against its own deliverable, not the job's headline. "The driveway is paved" is not true when the base is down, and refusing honest work for that would be our mistake.
- A
materialsstage pays against a receipt rather than against finished work, so nobody carries your supply costs for the length of the job. - Reporting a stage extends the lease. Somebody working is the opposite of somebody who walked away.
- The seat is held until the last stage. A half-paved driveway does not go back on the board.
Supplying as a business
A company is not a person, and this exchange used to insist otherwise: one login, a ceiling of three jobs however many crews you had, payouts to whoever clicked, and a licence field nothing checked. All four are addressed.
| Endpoint | What it does |
|---|---|
| GET /v1/supplier | Your profile, your ceiling, and what is holding it back |
| PUT /v1/supplier | Set your legal name, licences and cover |
| POST /v1/supplier/members | Add somebody who may take work for you |
| DELETE /v1/supplier/members/{person} | Remove them |
| GET /v1/statement | What you earned in a period, line by line |
| GET /v1/statement.csv | The same, for your bookkeeper |
- Your crews claim against you. Concurrency, cooldown and standing belong to the business; the seat and the evidence belong to the technician, so a buyer still knows which crew came.
- Licensed trades need a licence we checked. Claiming HVAC is not enough — a person looks the number up on the issuing register. Editing a licence clears its verification.
- Vetting lifts the ceiling from three to twelve, and to forty once you have a record. It is not self-service.
- Companies are paid as companies. Set
kind: "company"and the payment provider asks you for an EIN rather than asking an employee for a social security number.
Verification tiers
| Tier | What it requires |
|---|---|
| V0 | A signed claim, no artifact |
| V1 | An artifact passing deterministic checks |
| V2 | V1 plus a challenge code in frame and adjudication |
| V3 | Two independent sources that agree |
Admissible is not the same as done. Verification establishes that the evidence is tied to this job — the challenge code is legible, the location matches. A separate adjudication asks whether the photographs actually show what you asked for. A do-job pays its completion fee only when both hold; one that is merely admissible pays nothing, and the worker is told to reshoot with the finished work in frame.
Ask for the tier that matches what a wrong answer would cost you. Higher tiers cost more and take longer, and the exchange refuses to claim a confidence it cannot reach.
Findings. The exchange also posts its own observe jobs about
storefronts near where operators are, and every verified answer is kept as a
public record at GET /v1/findings — place, question, verdict,
photo hash, time, and a location rounded to about a kilometre. That is the
first dataset this marketplace produces; GET /v1/bootstrap reports
what the loop has spent and found.
MCP
The exchange ships an MCP server at /mcp so an agent can use
all of this as tools. One URL, three surfaces: the credential decides which.
No credential gets the guest tools — check_feasible, observe_world,
do_in_world, find_out, job_status, job_receipt, job_evidence, list_bids —
where a posted job comes back with a pay link and a token. An agent key gets
the whole buying side; an operator's own session token gets the supply side.
claude mcp add --transport http lamdis https://exchange.lamdis.ai/mcp claude mcp add --transport http lamdis https://exchange.lamdis.ai/mcp \ --header "Authorization: Bearer lam_sk_..."
Buying: with an agent key
| Tool | What it does |
|---|---|
| observe_world | Find out whether something is actually true in the physical world; somebody photographs it, the evidence is checked |
| do_in_world | Have something in the physical world made true, by whoever can do it, with proof it happened |
| find_out | Pay somebody to go and find something out, and get a structured answer back rather than a photograph |
| check_feasible | Whether supply is reachable for a job, before promising anybody it can be done. Costs and holds nothing |
| request_quotes | Post a job you do not know the price of and collect offers |
| list_bids | The offers on an open job: price, when, and how |
| accept_bid | Accept one offer; the amount becomes the price and the work begins |
| job_status | Where a job has got to: taken, submitted, checked, paid |
| job_evidence | The files somebody brought back, with where each says it was taken |
| job_receipt | The signed receipt for a finished job, verifiable without trusting the exchange |
| cancel_job | Withdraw a job nobody has taken yet and release its escrow |
| open_project | Start a budget envelope several jobs share |
| project_status | What a project has cost so far and what is left, job by job |
| list_project_bids | Offers covering a whole project at once, priced per piece |
| accept_project_bid | Accept one offer covering several jobs, awarded together or not at all |
| read_stage_plan | The stage breakdown a supplier proposed for a job whose winner writes the schedule |
| decide_stage_plan | Accept a supplier's stage breakdown, or send it back with a reason |
| sweep_sites | Describe work once and post it at many of your locations under one budget |
| list_sites | This account's locations, with the ids sweep_sites and do_in_world take |
| list_vendors | The suppliers this account has approved, with any agreed rates |
| exchange_balance | What this agent's account holds, what is committed, and what remains spendable |
There is deliberately no tool to issue a key, raise a limit, connect a payout account, or submit evidence. An agent cannot widen its own budget or manufacture the proof it will be judged by.
Supplying: with an operator's session token
The same routes the board's own pages call, so an operator's agent and an operator's browser see the same exchange. Nothing here can be done by an agent that the person could not do themselves.
| Tool | What it does |
|---|---|
| find_work | What is open right now that this operator could actually take, filtered to their range and qualifications |
| read_job | One job in full: what it asks for, what counts as proof, the buyer's photographs, what is blocking it |
| take_job | Take a fixed-price job; it is theirs from this moment and the clock starts |
| place_bid | Offer a price on an open job, priced from what the operator has said about their rates |
| read_scope | A multi-part job in full: every piece, in order, and what is waiting on what |
| bid_whole_scope | One offer covering every piece of a multi-part job, awarded together or not at all |
| propose_stages | On a job whose winner writes the schedule, propose how it breaks down and what each piece is worth |
| my_work | What this operator is holding: which stage each job is on, what is next, what is blocked |
| my_earnings | What this operator is owed, what is clear to send, what was objected to, and the bids still out |
| set_capacity | Record what this operator will take, how much at once, how far they will go, and where to push offers |
| give_back | Hand a job back that this operator cannot do after all, rather than letting it lapse |
Errors
Refusals say what to do about them. A job you cannot take tells you which skill is missing or how far away it is; a key over its limit names the limit, because the person who set it is the one who decides whether to raise it.
Reading a job that is not yours returns 404 rather than
403 — confirming a job exists is already more than a stranger
should learn.
Limits worth knowing
- Six files per submission. Photographs, video, or audio.
- Evidence bytes are held in memory and do not survive a restart. Hashes and verdicts do.
- Amounts are integer minor units. There is no float anywhere in the money path.
- US only for now: dollars, miles, and a skill catalogue of US credentials.
Anchored receipts
A receipt is signed by the exchange, which proves the exchange issued it
— to anyone who trusts the exchange. Anchoring adds what a signature
cannot: proof that the receipt existed, in exactly this form, at a point in
time, checkable by someone who trusts neither Lamdis nor its continued
existence. On an exchange run with -data, the SHA-256 of every
receipt served (the receipt object minus its signature and
anchor members, compact, keys sorted) is logged. Every hour the
unanchored hashes are built into a Merkle tree and the root is submitted to
public OpenTimestamps calendars,
which commit it to Bitcoin. Nothing is paid and no key is involved. The
receipt carries the pointer under anchor: its own hash, the root
it was batched into, and pending or anchored.
What this proves is existence and integrity at a time — that these
bytes were in hand no later than that Bitcoin block. It does not make the
receipt's contents true; for that, read its verification block and evidence.
To check one: fetch the proof, fold the hash up inclusion_path
(SHA-256 of sibling || hash when the side is
left, hash || sibling when right)
to reach merkle_root, decode ots_proof from base64
into a file, and run ots verify -d <merkle_root> root.ots
with the OpenTimestamps client (pip install opentimestamps-client).
A pending proof upgrades with ots upgrade once the calendar has
its block, usually within hours. None of those steps asks this exchange
anything.
| Endpoint | What it does |
|---|---|
| GET /v1/jobs/{job}/receipt/anchor | The proof for one receipt: its hash, the root, the inclusion path, the .ots bytes and how to verify them. Same credential as the receipt; ?sha256= picks an earlier issue |
| GET /v1/anchors | Recent roots with their status and proofs. Public, no token: anyone can check the chain of roots |
Paying with USDC
An agent with a wallet and no card can fund a job by transfer, and an
operator anywhere can be paid to an address without a connected payout
account. The exchange is watch-only: it holds no private key and never signs a
transaction. It publishes one receiving address on Base and watches for USDC
arriving at it. Whether the rail is on, and at which address, is public at
GET /v1/rails.
Funding a job. When the rail is on, the awaiting_payment
reply to an anonymous POST /v1/tasks also carries
pay_usdc: {address, amount_usdc, chain, contract, note}.
The amount is the job's ceiling at 1 USD = 1 USDC plus a
few units of dust unique to the job, so a transfer of exactly that
amount is what identifies it. Send it from any wallet. After twelve
confirmations the job is on the board, escrowed exactly as a card-funded job
would be, and the token from the same reply follows it. A transfer of any other
amount matches nothing and is returned by hand. A USDC job is prepaid: what it
does not pay out on proof is owed back to the sending address and queued for a
person to send, on a schedule.
Being paid. A signed-in operator sets an address with
PUT /v1/payout/usdc {"address": "0x..."} (EIP-55 checksum is
enforced when the case is mixed). From then on their clear earnings go to a
queue instead of the card rail, and GET /v1/payout reports
usdc_paid_minor against usdc_cap_minor: up to that
lifetime amount an address is enough, above it a connected payout account is
required. Sends from the queue are made by a person, not a machine, on a
schedule.
| Endpoint | What it does |
|---|---|
| GET /v1/rails | Which rails are on; the USDC address, chain, confirmations, last scanned block, queue length. Public |
| GET /v1/payout/usdc | Your payout address, what has been paid to it and the cap. Signed in |
| PUT /v1/payout/usdc | Set or clear your address. Signed in |
| GET /v1/payout/usdc/queue | What is waiting to be sent, and transfers that matched no job. Signed principal |
| POST /v1/payout/usdc/queue/{id}/sent | Record the transaction that paid an item, {"tx": "0x..."}. Signed principal |