DamageBDD NWC API Manual
DamageBDD NWC API Manual
This manual describes the authenticated NWC API exposed by DamageBDD for creating, inspecting, crediting, and revoking ledger-backed Nostr Wallet Connect connections.
The API surface in the current handler contains these POST endpoints:
/api/nwc/mint/api/nwc/revoke/api/nwc/ledger/balance/api/nwc/ledger/credit
Authentication is delegated to the standard Damage token flow. In practice, you
first obtain an access_token from /accounts/auth/ and then send it as:
Authorization: Bearer <access_token>
The server also accepts the token as an access_token query parameter or via
sessionid cookie, but the Bearer header is the cleanest option for scripts.
Authentication
Get an access token
curl -sS -X POST \ https://run.dev.damagebdd.com/accounts/auth/ \ -H 'content-type: application/json' \ -d '{ "username": "you@example.com", "password": "your-password" }'
Expected JSON response:
{
"status": "ok",
"access_token": "...",
"address": "ak_..."
}
Store the token for later:
export DAMAGE_BASE='https://run.dev.damagebdd.com' export DAMAGE_TOKEN='PASTE_ACCESS_TOKEN_HERE'
Python helper for authenticated requests
import requests BASE = "https://run.dev.damagebdd.com" TOKEN = "PASTE_ACCESS_TOKEN_HERE" session = requests.Session() session.headers.update({ "Authorization": f"Bearer {TOKEN}", "content-type": "application/json", "accept": "application/json", }) def post_json(path, payload): response = session.post(f"{BASE}{path}", json=payload, timeout=60) response.raise_for_status() if response.content: return response.json() return None
Endpoint: /api/nwc/mint
Creates a new NWC connection for the authenticated user. The handler generates a new client secret, derives the client pubkey, resolves the user ledger, and returns an NWC URI. If the user has no registered ledger yet, it may either set it up automatically or return setup intents, depending on execution mode and key availability.
Request body
relays: list of relay URLs. Optional.max_single_sat: per-payment ceiling in sats. Optional. Default10000.max_total_sat: total ledger ceiling in sats. Optional. Default100000.expires_height: chain height expiry. Optional. Default0.
curl example
curl -sS -X POST "$DAMAGE_BASE/api/nwc/mint" \ -H "Authorization: Bearer $DAMAGE_TOKEN" \ -H 'content-type: application/json' \ -d '{ "relays": ["wss://relay.damus.io"], "max_single_sat": 1000, "max_total_sat": 5000, "expires_height": 0 }'
Python example
mint = post_json("/api/nwc/mint", { "relays": ["wss://relay.damus.io"], "max_single_sat": 1000, "max_total_sat": 5000, "expires_height": 0, }) print(mint) client_pubkey = mint["client_pubkey"] nwc_uri = mint["nwc_uri"]
Typical success response
{
"status": "ok",
"owner": "ak_...",
"ledger_ct": "ct_...",
"ledger_mode": "server_signed",
"client_pubkey": "<64-hex>",
"secret_hex": "<32-byte-secret-hex>",
"nwc_uri": "nostr+walletconnect://<wallet_pubkey>?relay=wss://...&secret=<secret_hex>",
"wallet_pubkey": "<64-hex>",
"relay": "wss://relay.damus.io",
"intents": []
}
Notes
- The current code returns HTTP
200with a JSON body on success, not204. secret_hexis only returned at mint time, so persist it immediately if your client needs it.- In
user_signedmode,intentscontains a ledger registration intent instead of server-side execution. - In missing-ledger cases, the server may either auto-setup the ledger and still
return
status: "ok", or returnstatus: "needs_ledger_setup"with setup intents.
Endpoint: /api/nwc/ledger/balance
Looks up the ledger-backed balance state for a given client_pubkey under the
currently authenticated owner.
Request body
client_pubkey: required NWC client pubkey.
curl example
curl -sS -X POST "$DAMAGE_BASE/api/nwc/ledger/balance" \ -H "Authorization: Bearer $DAMAGE_TOKEN" \ -H 'content-type: application/json' \ -d '{ "client_pubkey": "YOUR_CLIENT_PUBKEY" }'
Python example
balance = post_json("/api/nwc/ledger/balance", { "client_pubkey": client_pubkey, }) print(balance)
Response shape
{
"status": "ok",
"owner": "ak_...",
"ledger_ct": "ct_...",
"client_pubkey": "<64-hex>",
"result": {
"return_type": "ok",
"return_value": "..."
}
}
The exact shape of result.return_value depends on the smart-contract entrypoint
serialization returned by ledger_call_user(..., "balance", ...).
Endpoint: /api/nwc/ledger/credit
Credits a ledger-backed NWC connection by a deterministic amount. This endpoint is explicitly admin-only in the handler.
Request body
client_pubkey: required NWC client pubkey.amount_sat: amount to credit in sats.ref: reference string.meta: metadata string, commonly JSON encoded text.
curl example
curl -sS -X POST "$DAMAGE_BASE/api/nwc/ledger/credit" \ -H "Authorization: Bearer $DAMAGE_TOKEN" \ -H 'content-type: application/json' \ -d '{ "client_pubkey": "YOUR_CLIENT_PUBKEY", "amount_sat": 2000, "ref": "bdd-credit-1", "meta": "{}" }'
Python example
credit = post_json("/api/nwc/ledger/credit", { "client_pubkey": client_pubkey, "amount_sat": 2000, "ref": "bdd-credit-1", "meta": "{}", }) print(credit)
Success response
{
"status": "ok",
"owner": "ak_...",
"ledger_ct": "ct_...",
"credited_sat": 2000,
"intents": []
}
Endpoint: /api/nwc/revoke
Revokes a previously minted NWC connection identified by client_pubkey.
Request body
client_pubkey: required NWC client pubkey.
curl example
curl -sS -X POST "$DAMAGE_BASE/api/nwc/revoke" \ -H "Authorization: Bearer $DAMAGE_TOKEN" \ -H 'content-type: application/json' \ -d '{ "client_pubkey": "YOUR_CLIENT_PUBKEY" }'
Python example
revoke = post_json("/api/nwc/revoke", { "client_pubkey": client_pubkey, }) print(revoke)
Success response
{
"status": "ok",
"revoked": true,
"client_pubkey": "<64-hex>",
"ledger_ct": "ct_...",
"intents": []
}
In user_signed mode, intents may contain a revoke intent instead.
End-to-end example
curl workflow
export DAMAGE_BASE='https://run.dev.damagebdd.com' export DAMAGE_TOKEN='PASTE_ACCESS_TOKEN_HERE' MINT_JSON=$(curl -sS -X POST "$DAMAGE_BASE/api/nwc/mint" \ -H "Authorization: Bearer $DAMAGE_TOKEN" \ -H 'content-type: application/json' \ -d '{ "relays": ["wss://relay.damus.io"], "max_single_sat": 1000, "max_total_sat": 5000, "expires_height": 0 }') printf '%s\n' "$MINT_JSON" CLIENT_PUBKEY=$(printf '%s' "$MINT_JSON" | jq -r '.client_pubkey') NWC_URI=$(printf '%s' "$MINT_JSON" | jq -r '.nwc_uri') curl -sS -X POST "$DAMAGE_BASE/api/nwc/ledger/balance" \ -H "Authorization: Bearer $DAMAGE_TOKEN" \ -H 'content-type: application/json' \ -d "{\"client_pubkey\":\"$CLIENT_PUBKEY\"}" curl -sS -X POST "$DAMAGE_BASE/api/nwc/ledger/credit" \ -H "Authorization: Bearer $DAMAGE_TOKEN" \ -H 'content-type: application/json' \ -d "{\"client_pubkey\":\"$CLIENT_PUBKEY\",\"amount_sat\":2000,\"ref\":\"bdd-credit-1\",\"meta\":\"{}\"}" curl -sS -X POST "$DAMAGE_BASE/api/nwc/revoke" \ -H "Authorization: Bearer $DAMAGE_TOKEN" \ -H 'content-type: application/json' \ -d "{\"client_pubkey\":\"$CLIENT_PUBKEY\"}"
Python workflow
mint = post_json("/api/nwc/mint", { "relays": ["wss://relay.damus.io"], "max_single_sat": 1000, "max_total_sat": 5000, "expires_height": 0, }) client_pubkey = mint["client_pubkey"] print("NWC URI:", mint["nwc_uri"]) balance_1 = post_json("/api/nwc/ledger/balance", { "client_pubkey": client_pubkey, }) print("Initial balance:", balance_1) credit = post_json("/api/nwc/ledger/credit", { "client_pubkey": client_pubkey, "amount_sat": 2000, "ref": "bdd-credit-1", "meta": "{}", }) print("Credit result:", credit) balance_2 = post_json("/api/nwc/ledger/balance", { "client_pubkey": client_pubkey, }) print("Balance after credit:", balance_2) revoke = post_json("/api/nwc/revoke", { "client_pubkey": client_pubkey, }) print("Revoke result:", revoke)
Error handling
Common error envelopes from this handler include:
{
"status": "error",
"error": "NO_LEDGER",
"reason": "..."
}
Mint can also return a setup-oriented response:
{
"status": "needs_ledger_setup",
"reason": "...",
"account_registry_ct": "ct_...",
"client_pubkey": "<64-hex>",
"secret_hex": "...",
"nwc_uri": "nostr+walletconnect://...",
"wallet_pubkey": "...",
"relay": "wss://...",
"intents": [
{"type": "deploy_ledger", "...": "..."},
{"type": "upsert_registry", "...": "..."},
{"type": "ledger_register", "...": "..."}
]
}
For shell scripts, treat non-2xx HTTP codes as transport/application errors, and
also inspect the JSON status field because some logical failures are expressed
inside JSON payloads.
Practical caveats
- Your BDD scenario currently expects
204from/api/nwc/mint, but the handler shown here replies with200and a JSON body containing the minted values. - The sample scenario also uses
Then I print the responseimmediately after checking for204. That only makes sense if the endpoint actually returns a body; the current handler does. /api/nwc/ledger/creditis admin-gated in the code, so a normal user token may not be sufficient unless the request state carries admin role.
Source basis
This manual was derived from the current damage_nwc_http handler, the shared
Damage auth flow in damage_http, and the account auth endpoint in
damage_accounts.
