Developer reference

Build for any Nostr store.

Your storefront owns its catalog and checkout. BitUnlock provides a store-neutral, signed API for Lightning payment verification and buyer-private delivery.

Production identity

APIhttps://api.bitunlock.network
Service npubnpub1v6lxrn6m355x8y0hkxnjl7nu0x0v9hzfalcgxrzqmuwvje8x0p7sha5mt4
Service hex66be61cf5b8d286391f7b1a72ffa7c799ec2dc49eff0830c40df1cc964e6787d
Policy30078:66be61cf5b8d286391f7b1a72ffa7c799ec2dc49eff0830c40df1cc964e6787d:bitunlock:service-policy:v1

Pin the hex pubkey. Verify signed discovery before sending secrets or trusting quotes.

Available today

Browser SDKPublic ESM bundle
HTTP clientGenerate from OpenAPI 3.1
npm packageNot public yet
Hosted sandboxNot public yet

Broad NWC links are accepted when they support invoice creation and status lookup, including links that advertise pay_invoice. BitUnlock never invokes extra methods. Use small amounts and a dedicated low-balance seller wallet; restricted permissions remain safest.

Start with the contract

No package-registry guesswork and no placeholder endpoints.

Browser ESM

import {
  createBitUnlockClient,
  buildBitUnlockProductEventTemplate
} from "/assets/bitunlock-sdk.js";

Signed product contract

Publish a seller-signed kind 30078 event to a relay listed in verified discovery. BitUnlock reads only the exact service-facing tags; event content remains storefront-owned public metadata.

TagValueMeaning
dunique identifierCompletes the product coordinate.
pricepositive sats, SATSigned seller price used for the quote.
statusactiveOnly active products can quote.
br:deliveryseller-unlock-service-v1Selects this protocol.
br:unlock-servicehttps://api.bitunlock.networkBinds the endpoint.
br:unlock-service-pubkeypinned service hex keyBinds the service identity.
br:unlock-policysigned policy coordinateBinds fee and service policy.
br:claim-epochpositive integerBinds the registered payload revision.

Store-neutral boundary: BitUnlock does not require or interpret BitRoad soft-gate fields. BitRoad's soft gate is a separate store feature for listings that do not use BitUnlock.

Payment modes and delivery modes

Two independent axes, both set at product registration. Neither changes the quote/refresh/unlock contract a buyer follows.

Payment mode

direct-split (default): two legs, seller and BitUnlock, per the fee policy (100bps, 10-sat minimum). single-recipient: one leg, the full price, straight to the seller's own wallet — gated to tenants an operator has explicitly granted this for. Micro-ticket tenants on accrual billing keep the single leg while the platform fee accrues to a ledger, settled as one batched invoice via GET /v1/fee-ledger; overdue settlements suspend quoting only, never a buyer's paid order — and the service DMs the tenant's configured npubs when a settlement opens, three days before its deadline, and the day quoting pauses. Never hard-code a leg count; render whatever the quote returns.

Delivery mode: execution

A product doesn't have to be a static payload. deliveryMode: "execution" sells a bounded, server-run call to a third-party provider instead — pay, then get a real result computed on demand, billed per completed purchase. A Provider Connection can use Routstr conventions or an OpenAI-compatible API such as OpenRouter; its key is validated live, encrypted at rest, and never returned. Execution products are operator-gated.

Execution input, checked at quote time

An execution-mode quote takes an extra executionInput: {schema, ciphertextSha256, serviceEnvelope}, a NIP-44 payload (buyer → service pubkey) whose plaintext must match the product's own inputSchema. BitUnlock decrypts it, enforces the product's limits, and — for a blob-bearing input — actually fetches the referenced blob before minting an invoice. An unreachable host, a bad content type, a hash mismatch or an oversized input is refused with the specific cause — free, with no order to recover. A text-only input carries no blob, so that fetch and its failure modes do not apply.

Input schemas are implemented, not declared

inputSchema and outputSchema name a validator BitUnlock implements. They are not free-form identifiers: the platform parses and bounds every execution input and output itself, and a schema it does not know is refused at quote time with input_schema_unknown — so a product registered against an unrecognised name can never take a quote. That is what makes size and shape enforceable before an invoice exists, which is what keeps a bad input a free 422 instead of a paid order that fails. Adding a schema is a small platform change; ask for one before building against it.

Execution failure, at unlock time

What is left after payment is what could not be known before it: a provider error, a timeout, or a host that died between quote and unlock. Those surface as a generic execution_failed_permanently, execution_retryable_failure, or execution_retry_window_exhausted (the last two 503 with a retry-after, and neither terminal — a spent attempt budget refills). The specific internal cause is never exposed to the buyer.

Image outputs, bundles, and tenant secrets

Execution products are not limited to text results, single results, or hard-coded configuration.

Image-output products

A product whose outputSchema has a registered output transform is an image product: the provider is called with modalities: ["image","text"], the returned image is decoded by its actual magic bytes (PNG and JPEG both accepted — declared subtypes are not trusted), and the transform reduces it server-side to a compact JSON payload, e.g. a run-length-encoded 48×48 pixel-art object. The NIP-44 fulfillment envelope caps plaintext at 64KB; the transform is what makes an image deliverable at all. Buyer input for these products is text-only.

Companion stages: one invoice, two results

A product can bundle a second, image-producing execution into the same order — one quote, one invoice at the primary price, one envelope. After the primary output validates, a short text input is derived from it and the companion product runs with its own model, encrypted prompt, and provider connection; the result rides along as an extra payload field. Best-effort by contract: a companion failure never fails the paid order.

Generic tenant secrets

A separate write-only configuration vault for future tenant features: PUT /v1/tenant-secrets/{name} creates or rotates, GET lists names and timestamps only, DELETE removes. Values are AEAD-encrypted with the tenant id and secret name bound into the encryption context, capped at 8KB each and 64 per tenant, and no endpoint ever returns one. No production runtime feature consumes these values yet; execution API keys belong in Provider Connections.

End-to-end flow

Seller

  1. Verify signed discovery and pin the service key.
  2. Accept policy and register a compatible NWC wallet.
  3. Build, sign, and publish the product event.
  4. NIP-44 encrypt the delivery payload to BitUnlock.
  5. Register the coordinate, epoch, type, and ciphertext.

Buyer

  1. NIP-98 sign a quote request.
  2. Pay every returned invoice — one or two, depending on payment mode.
  3. Poll the order; refresh only expired unpaid legs.
  4. Call unlock once every leg has settled.
  5. NIP-44 decrypt and verify the fulfillment envelope.

Entitlements: recovery, term access, revocation

Every completed purchase also produces an entitlement — a service-signed receipt that a buyer pubkey owns a specific purchase. It verifies offline against the pinned service key, so a relying party (the seller's own backend, or an unrelated service like a chat room, game server, or API gateway) never has to call BitUnlock just to check a perpetual entitlement.

Perpetual or time-boxed, per product

Registering a product is perpetual access by default — one purchase, access forever. Set accessTermSeconds to sell a rental or manually renewable term: access expires after that many seconds, and another completed purchase renews it. BitUnlock does not automatically or recurringly charge the buyer.

Recovery across devices

Authenticated buyers call GET /v1/access-entitlements to recover every entitlement they own — the signed receipt, an accessPolicy (perpetual or a term with its expiry), and an effective status: active, expired, or revoked.

Seller-initiated revocation

Sellers can revoke a specific buyer's entitlement with POST /v1/entitlements/:id/revoke. Revocation is never embedded in the signed receipt itself — changing signed content would invalidate its own signature — so a relying party that must honor revocation checks current status from the API rather than trusting a cached receipt indefinitely.

Endpoints

Protected calls use NIP-98 kind 27235 authorization. Mutation retries reuse their idempotency key with a fresh signature.

MethodPathActorPurpose
GET/healthpublicLiveness + signed-policy status.
GET/.well-known/bitunlock.jsonpublicSigned service policy for discovery.
GET/openapi.jsonpublicOpenAPI 3.1 contract.
GET/entitlements/:fulfillmentId/statuspublicOpaque-ID revocation status for a relying party.
POST/v1/tenantssellerCreate or update the store account; accept policy + fee cap.
GET/v1/storesellerRead the signed-in seller's onboarding and receiving-wallet status.
POST/v1/wallet-connectionssellerRegister a compatible NWC connection (encrypted at rest).
POST/v1/productssellerRegister an encrypted delivery payload against a signed product.
POST/v1/products/:coord/revokesellerDisable or rotate a delivery payload.
GET/v1/tenant/overviewsellerRead exact tenant-scoped financial metrics and operational alerts.
GET/v1/tenant/orderssellerList sanitized tenant orders with stable cursor pagination.
POST/v1/provider-connectionssellerRegister an execution-provider API key (validated live, encrypted at rest).
GET/v1/provider-connectionssellerList a tenant's provider connections.
POST/v1/provider-connections/:id/rotatesellerReplace and re-validate a provider connection's API key.
POST/v1/provider-connections/:id/disablesellerDisable a provider connection.
GET/v1/provider-connections/:id/healthsellerProvider health, low-balance, and credential-staleness summary from recorded executions.
GET/v1/tenant-secretssellerList stored secret names and timestamps — never values.
PUT/v1/tenant-secrets/:namesellerCreate or rotate a write-only tenant secret (encrypted at rest, never returned).
DELETE/v1/tenant-secrets/:namesellerRemove a tenant secret.
GET/v1/fee-ledgersellerPlatform-fee balance, entries and settlements; reading it also maintains them.
GET/v1/notification-settingssellerWho is DM'd when a fee settlement opens.
PUT/v1/notification-settingssellerReplace the additional settlement-DM recipients (up to 5 npubs).
POST/v1/quotesbuyerCreate an immutable payment plan — two legs (seller, BitUnlock) by default, one for a single-recipient product.
GET/v1/orders/:idbuyer-or-sellerAuthorized order + settlement status.
POST/v1/orders/:id/refreshbuyerReplace only expired, unpaid legs.
POST/v1/orders/:id/unlockbuyerAfter all legs settle, return the idempotent NIP-44 envelope.
POST/v1/orders/:id/execution-inputbuyerSubmit a corrected execution input after a buyer-input-attributable permanent failure.
POST/v1/orders/:id/execution-retrysellerRetry a provider-side permanent execution failure (e.g. after topping up a balance).
POST/v1/orders/:id/execution-resetbuyerReset a permanently-failed execution once, without waiting for the seller to act.
GET/v1/access-entitlementsbuyerRecover every signed entitlement receipt the buyer owns, with current status.
POST/v1/entitlements/:id/revokesellerRevoke a specific buyer's entitlement for a product you own.
GET/v1/admin/overviewsuper-adminRead sanitized service-wide commercial and operational totals.
GET/v1/admin/tenantssuper-adminList sanitized tenant summaries with bounded pagination.
GET/v1/admin/tenants/:tenantIdsuper-adminInspect one tenant's sanitized reporting and connection metadata.
GET/v1/admin/orderssuper-adminList sanitized cross-tenant orders for support and incident response.
GET/v1/admin/seller-allowlistsuper-adminList Nostr identities approved to create a seller tenant.
POST/v1/admin/seller-allowlistsuper-adminApprove a Nostr identity for seller onboarding.
DELETE/v1/admin/seller-allowlist/:pubkeysuper-adminRemove a Nostr identity from the seller onboarding allowlist.