Building on coinos
There are two coinos wallets, and they connect to other software differently. The new one (v3, this site) keeps your keys on your device. The original one at coinos.io is a hosted account with a REST API.
Which one should I build against?
| coinos v3 (v3.coinos.io) | coinos.io hosted accounts | |
|---|---|---|
| Custody | Self-custodial. The wallet runs in the browser or the app; keys never leave the device. | Custodial. coinos holds the balance. |
| Programmatic access | Nostr Wallet Connect (NIP-47), with spending limits you set per connection | REST API with a bearer token, plus a websocket |
| Receive | Lightning address, LNURL-pay, BOLT12, Ark, on-chain | Lightning, on-chain, Liquid, internal |
| Send on-chain from code | No | Yes: /bitcoin/send |
| Good for | Apps that pay or charge a user's own wallet: zaps, tips, small checkouts | Services that run unattended: shops, bots, swaps, POS hardware |
v3 has no server-side API or token. There's no account on our side for one to reach. If your service has to run around the clock without anyone's wallet being open, use a hosted coinos.io account and the REST API.
Receiving: Lightning addresses, LNURL, BIP-353
Every coinos name name@coinos.io is a Lightning address, and it works the same whether the name belongs to a v3 wallet or a hosted account.
LNURL-pay (LUD-06/16)
GET https://coinos.io/.well-known/lnurlp/<name>
This returns a standard payRequest. Call its callback with ?amount=<millisats> to get a BOLT11 invoice. Comments up to 200 characters are accepted (LUD-12). allowsNostr is true, so NIP-57 zaps work and receipts are published.
Nostr identifiers (NIP-05)
GET https://coinos.io/.well-known/nostr.json?name=<name>
BIP-353 (₿name@coinos.io)
v3 names publish a DNSSEC-signed payment instruction:
dig TXT <name>.user._bitcoin-payment.coinos.io
The record is a bitcoin: URI carrying an Ark address and a BOLT12 offer (lno=). Wallets that speak BIP-353 can pay it with no web request at all.
Nostr Wallet Connect (v3)
NWC lets your app ask a user's v3 wallet to pay or create invoices over nostr. Your app never sees the wallet's keys. The user decides how much it may spend, and can cut it off at any time.
Getting a connection string
- In coinos, open Settings → Nostr → Nostr Wallet Connect.
- Add a connection: give it a name, a maximum per payment, and a budget (per day, week or month).
- Copy the
nostr+walletconnect://…string into your app.
Supported methods
| Method | Notes |
|---|---|
get_info | Lists the methods below. |
get_balance | Spendable balance, in millisats. |
get_budget | What's left of this connection's budget, and when it renews. |
pay_invoice | BOLT11. Refused past the per-payment cap or the budget. |
make_invoice | A BOLT11 invoice paid into the wallet. |
lookup_invoice | By payment_hash or invoice; tells you whether it's settled. |
list_transactions | Recent payments in and out. |
NIP-47 has no on-chain method, and coinos doesn't add one. A revoked connection answers UNAUTHORIZED.
Requests and responses go through wss://relay.coinos.io, plus any relays in the connection string.
The coinos REST API (hosted accounts)
The REST API at https://coinos.io/api keeps working for hosted accounts. The full reference is at coinos.io/docs. A short tour:
BASE=https://coinos.io/api
# an account and a token (or copy a token from the coinos.io settings page)
curl -X POST $BASE/register -H 'content-type: application/json' \
-d '{"user":{"username":"myshop","password":"…"}}'
curl -X POST $BASE/login -H 'content-type: application/json' \
-d '{"username":"myshop","password":"…"}' # → {"token": "…"}
AUTH="authorization: Bearer $TOKEN"
curl $BASE/me -H "$AUTH" # balance and profile
# receive: a Lightning invoice (type "bitcoin" or "liquid" for an address)
curl -X POST $BASE/invoice -H "$AUTH" -H 'content-type: application/json' \
-d '{"invoice":{"amount":1000,"type":"lightning"}}' # → {"hash", "text": "lnbc…", …}
curl $BASE/invoice/<hash> # received / pending
# pay a Lightning invoice
curl -X POST $BASE/payments -H "$AUTH" -H 'content-type: application/json' \
-d '{"payreq":"lnbc…"}'
# pay a Lightning address
curl -X POST $BASE/send/alice@coinos.io/1000 -H "$AUTH"
# on-chain: estimate, then send
curl -X POST $BASE/bitcoin/fee -H "$AUTH" -H 'content-type: application/json' -d '{"address":"bc1…","amount":50000}'
curl -X POST $BASE/bitcoin/send -H "$AUTH" -H 'content-type: application/json' -d '{"address":"bc1…","amount":50000}'
curl "$BASE/payments" -H "$AUTH" # history
Don't poll for payments: connect to wss://coinos.io/ws, send {"type":"login","data":"<token>"}, and receive payment events as they happen. Exact request fields can change; coinos.io/docs is authoritative.
v2.coinos.io. We'll announce the date ahead of time. Until then nothing changes, and https://coinos.io/api is the right base URL. v3.coinos.io has no equivalent API.Moving a username to v3
A hosted account's username can move to a v3 wallet. After that, new payments to the hosted account (Lightning, BOLT12, on-chain, Liquid) are forwarded to the v3 wallet automatically. Payments between coinos accounts aren't forwarded. Your API token keeps working, but the money won't stay in the hosted balance. So if your code receives into a hosted account and then spends from it (a swap or a payout), don't migrate that account's username. Use a separate name for your v3 wallet.
Testing without real money
Both wallets have a copy on mutinynet, a test network with 30-second blocks. Its coins are free and worthless.
| What | Where |
|---|---|
| REST API on mutinynet | https://staging.coinos.io/api: the same endpoints as coinos.io/api, with separate accounts. Websocket: wss://staging.coinos.io/api/ws |
| v3 wallet on mutinynet | staging.coinos.io: the same app, defaulting to mutinynet, with its own Ark server and Lightning node |
| Test coins | faucet.mutinynet.com: on-chain coins, or pay a mutinynet Lightning invoice |
| Block explorer | mutinynet.com |
Switch your base URL to https://staging.coinos.io/api and register a fresh account there. Mutinynet addresses start with tb1, and its invoices with lntbs. Liquid is switched off on staging. The test server is reset now and then, so don't keep anything there you'd miss.
Source code
- coinos v3 (this app): github.com/coinos/coinosv3
- The hosted wallet's server: github.com/coinos/coinos-server (its routes are in
index.ts) - Mirrors: git.coinos.io
Questions, or something here that's wrong? Message adam@coinos.io on nostr.