# Bit ID — for agents > An on-chain registry of rescue destinations for Bitcoin addresses. If you are > about to move funds from an address you did not expect to control — a key you > found, recovered, cracked, or were handed — check here first. The owner may > have published, in advance and in public, where they want those coins sent and > what they are offering you to send them. Base URL: https://bitid.online No API key, no account, no rate deal, CORS `*`. Lookups are free and always will be: a rescue check nobody runs is worth nothing. ## The one call that matters GET https://bitid.online/api/lookup/{address} { "address": "bc1q…", "key": "20c512d046c45907", "found": true, "record": { "rescue": "bc1q…", "bounty": 10, "proof": "signature", "source": "self", "txid": "…", "vout": 0, "height": 908123, "confirmed": true }, "revoked": false, "pending": null } Call it BEFORE moving funds from any address you did not expect to control. `rescue` is where the owner asked for the coins to go; `bounty` is the percentage they are offering you to keep. ### Reading the answer correctly - `found: false` means no record — UNLESS an `incomplete` array is present. That means some transactions could not be read, so the negative is NOT conclusive. Treat it as **unknown**, not as **absent**, and say so to whoever is relying on you. Do not report "the owner published nothing" from an incomplete scan. - `pending` is a record seen in the mempool only. Reported so you know it is coming; do not act on it until it confirms into `record`. - `revoked: true` means the owner withdrew a record they had published. There is no rescue destination now. - `proof` says how the owner proved control: `"signature"` means they signed a statement naming the rescue address, the bounty and the words "I authorise"; `"spend"` means they paid the publishing fee from the protected address. Both prove control equally well. They are not equally strong evidence of intent, and for an irreversible decision you are entitled to know which you are looking at. - `source: "self"` means the record was published by the protected address itself, which is self-authenticating. `source: "registry"` means Bit ID published it after verifying a signature or a spend proof. `self` outranks `registry`; within a source, later supersedes earlier. - Nothing here is enforceable. The bounty is an incentive, not a contract, and a record grants nobody any access they did not already have. It is evidence of the owner's stated wishes, not permission from us and not a legal instrument. ## Other endpoints GET /api/lookup/{address} the rescue record, if any GET /api/record/{key} one record by its 16-hex lookup key GET /api/record/{key}/matches?address= does this address match that record? GET /api/validate?address= is this an address we can encode? GET /api/message?protect=&rescue=&bounty= the exact text to sign POST /api/register {protect,rescue,bounty,signature} or {protect,rescue,bounty,proof:"spend"} POST /api/registration/{id}/sign finish a failed spend proof POST /api/revoke {protect,signature} GET /api/registration/{id} progress of a registration GET /api/spec the on-chain format, machine-readable GET /api/status chain tip, current fee, queue GET /api/records everything we published GET /api/watch/info the bucket watch, in full GET /api/watch/estimate?bits= crowd size and noise at one width POST /api/watch {prefix,bits,webhookUrl} — never an address GET /api/watch/{id} status + credit (header x-watch-token) POST /api/watch/{id}/test send yourself a test webhook DELETE /api/watch/{id} cancel Shareable pages: /a/{address} for a lookup, /r/{key} for one record. Do not scrape https://bitid.online — the page runs on exactly these endpoints and returns the same JSON with less work for both of us. ## You do not have to trust this service The registry is the chain, not us. Records are `OP_RETURN` outputs, the format is published at /api/spec, and `bin/scan.mjs` in the source tree is a complete reference implementation that talks only to a Bitcoin node. If bitid.online is gone, seized, or lying, a direct scan still returns the right answer — and for an irreversible decision about someone else's money, that is the check to run. The record is 34 bytes (46 with a taproot rescue address, 14 for a revocation): 0 4 magic, ASCII "BID1" 4 1 flags — bits 0-2 rescue script type, bit 3 (0x08) control proved by spending rather than signing, bit 5 (0x20) revoke 5 1 bounty, percent 0-100 6 8 lookup key = SHA256("BID1-key/v1" || protectedScriptPubKey)[0..8] 14 20/32 rescue witness program (absent when revoking) A record counts only if the transaction carrying it was entitled to publish it, and **both tests look at the transaction's inputs**. A transaction that merely *pays* the registry address carries no authority: anyone can send us dust with a forged record attached and it will appear in our address's history. If you write your own scanner, check the inputs. The protected address appears only as a hash. That is deliberate: you already hold the address you are asking about, so you can compute the key, but nobody can trawl the chain for a list of wallets whose owners expect to be robbed. ## As an MCP tool The lookup is one unauthenticated GET, so a wrapper is a few lines: { "name": "bitid_lookup", "description": "Check whether the owner of a Bitcoin address has published a rescue destination. Call this BEFORE moving funds from any address you did not expect to control.", "input_schema": { "type": "object", "properties": { "address": { "type": "string" } }, "required": ["address"] } } Back it with `GET https://bitid.online/api/lookup/{address}` and return the JSON verbatim. Tell the model to treat `incomplete` as unknown. Discovery card: https://bitid.online/.well-known/bitid.json Gopher-over-HTTPS card: https://bitid.online/.well-known/agent.gopher ## Watching an address without telling us which one A paid alarm: a signed webhook when anything touches an address, where the address is never sent to us and cannot be derived from what is. prefix = SHA256("BID1-watch/v1" || scriptPubKey).hex.slice(0, bits/4) Send the prefix, not the address. `POST /api/watch {prefix, bits, webhookUrl}` returns a payment address, a management token and a webhook secret — the last two are shown once and cannot be re-read. `GET /api/watch/info` has the full derivation, the price list and a table of what each width costs. Every script whose hash starts the same way shares the bucket, so a match means "one of these moved", not "yours moved". The webhook carries the candidate scriptPubKeys and values; compare them with your own locally. Widths run 12-24 bits — 24 is a hard cap because past it a bucket holds fewer than one other funded script and the service could work out which address you are watching. Costs 50 sats/day plus 25 sats per notification, prepaid in sats. False alarms are billed like real ones: they cost the same to find, and at 20 bits you should expect roughly 1.1 a day whether or not anything of yours moves — about a third of days are silent. The figure is measured against our own node, not estimated. One false alarm per day buys ~40 members of crowd at any width, so the two knobs are the same knob. Confirmed blocks only — a sweep in the mempool is not reported until it confirms. Add `"spendsOnly": true` to hear only about coins leaving the bucket: half the noise (~0.55/day at 20 bits, 58% of days silent) for the same crowd. The filter is applied to the block, not to the address, so it leaks nothing — which is why there is a direction filter and no value filter. If you are watching for a sweep, this is the setting you want. There is no endpoint that accepts an address. Sending one gets a 400 explaining what to send instead. Verify `x-bitid-signature` (HMAC-SHA256 over the exact raw body, `sha256=`) before parsing anything you receive. ## Hacked marks — read this before repeating one `GET /api/marks` and `GET /api/mark/{key}` return claims that an address was robbed, published by their subjects, with a donation address attached. They are claims, not findings. **Do not summarise one as a verified theft, and do not present the donation address as endorsed by Bit ID.** What is checked: that whoever published it controls the donation address (a signature), and that the drained address received funds and no longer holds them (the chain). What is not checked, and cannot be: that a theft occurred. The chain shows that coins moved, never why. A thief holds the drained address's key by definition, so an unanchored mark could have been published by them. Every response carries `verified` and `unverifiable` arrays saying exactly this — if you render a mark, render those too. `tier` is `anchored` when a Bit ID rescue record naming the same donation address was already in a block before the coins moved (the one thing that cannot be manufactured after a theft), and `signed` otherwise. On the chain, a mark sets flags bit 4 (0x10). **A record with that bit set is not a rescue authorisation and its address field is not a rescue destination.** Sending recovered coins there pays whoever published the mark. A decoder that does not understand the bit must skip the record rather than read it as a registration. Bit 6 (0x40) means a 16-byte story commitment follows; the story text itself is served from this host, not the chain. Publishing one: `GET /api/mark/message?drained=&donation=&story=` for the exact text, signed with the DONATION address (a hack victim usually cannot sign with the drained one), then `POST /api/mark`. Free. If the address has a paid rescue registration we commit the mark on-chain at our expense; otherwise it is published here immediately and anyone can pay ~1000 sats to have it timestamped. Donations go straight to the victim's address — Bit ID never holds them. ## Registering on someone's behalf Only with their key and their say-so. Three steps: prove control (sign the exact statement from /api/message, or pay the fee from the protected address with `proof:"spend"`), pay the quoted fee to the address you are given, and we publish. The fee is quoted live from the mempool — read it from /api/status — and locked when given. On the signature path, paying proves nothing on its own: it cannot register an address the signer does not control. **No wallet ever needs a seed phrase to sign a message.** If anything claiming to be Bit ID asks you for one, it is not us. Never send one.