{"agentsJson":"0.1.0","info":{"title":"bitSimp API","version":"1.1.0","description":"Agent-callable flows for bitSimp, a Bitcoin-only anonymous peer-to-peer content marketplace. Creators post content behind a price wall denominated in satoshis; buyers pay via the Lightning Network.\n\nFlow actions reference operations in the bitSimp OpenAPI 3.1 document at https://bitsimp.com/openapi.json.\n\n# Authentication\n\nbitSimp is passwordless. Authentication proves ownership of a secp256k1 key pair via LNURL-auth (Lightning, ECDSA) or NIP-07 (Nostr, BIP-340 Schnorr). Headless clients (scripts, LLM agents) should use the single-call flows `authenticate_lnurl` and `authenticate_nostr`, which return an HttpOnly session cookie; include that cookie in all subsequent requests.\n\n# Per-request authentication (NIP-98)\n\nAn individual request may instead authenticate by carrying a signed NIP-98 event in the `Authorization: Nostr <base64(JSON kind-27235 event)>` header. A NIP-98 request acts as the existing Account that owns the signing npub for that one request only: it does not create an Account, does not issue a session cookie and cannot be reused. The Account must carry an npub — a Lightning-only Account is not eligible. It is available on a fixed subset of the public API (list/create/fetch/edit/archive posts, unlock with invoice or balance, confirm an unlock, mint upload signatures, read the current Account, list comments) and is listed as an alternative security scheme on those operations in /openapi.json. When the header is present it decides the identity; an invalid header fails with 401 instead of being ignored in favor of a session cookie.\n\nOnce signed in, an Account can **link a different sign-in method** to itself (or unlink one) instead of creating a second account: at most one Lightning key and one npub per Account, either key signs in to the same Account, and a key that belongs to another Account is rejected — Accounts are never merged (ADR 0020). See the `link_*` / `unlink_signin_method` flows.\n\n# Content lifecycle\n\nPosts are never hard-deleted. They are archived (`is_archived=true`) and can be unarchived by their owner.\n\n# Payments\n\nAll monetary amounts are integer satoshis. Purchases, tips, deposits and withdrawals are Lightning-Network operations; never spend a user's sats without explicit human approval."},"sources":[{"id":"bitsimp_api","path":"https://bitsimp.com/openapi.json","description":"bitSimp public HTTP API (OpenAPI 3.1) — LNURL-auth, NIP-07 auth and NIP-98 request auth, posts, comments and balance."}],"flows":[{"id":"authenticate_lnurl","title":"LNURL-auth login (headless / LLM agents)","description":"Request a challenge, sign it with LNURL-auth, then exchange `{ pubkey, challenge, sig }` for a session cookie. Prefer the published plugin/skill path for headless clients.\n\nGuide: https://bitsimp.com/docs/sign-in-lightning.md","actions":[{"id":"get_challenge","sourceId":"bitsimp_api","operationId":"lnurlAuthChallenge"},{"id":"log_in","sourceId":"bitsimp_api","operationId":"lnurlAuthLogin"}],"links":[{"origin":{"actionId":"get_challenge","fieldPath":"responses.success.challenge"},"target":{"actionId":"log_in","fieldPath":"requestBody.challenge"}}],"fields":{"parameters":[{"name":"pubkey","description":"33-byte compressed secp256k1 public key as 66-char hex. Not needed for step 1 (challenge issuance); required for step 3 (login). When using the plugin, use the `linkingPubkey` field or the `key` query parameter from `callbackUrl`.","required":false,"type":"string"},{"name":"sig","description":"DER-encoded ECDSA signature over the raw 32-byte challenge, as hex. Only required for step 3 (login). When using the plugin, extract from the `sig` query parameter in `callbackUrl`.","required":false,"type":"string"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"pubkey":{"type":"string"},"challenge":{"type":"string","minLength":64,"maxLength":64},"sig":{"type":"string"}},"required":["pubkey","challenge","sig"]}}},"required":true},"responses":{"success":{"type":"object","properties":{"success":{"type":"boolean","const":true},"id":{"type":"string","format":"uuid"}},"required":["success","id"]}}}},{"id":"authenticate_nostr","title":"NIP-07 Nostr login (headless / LLM agents)","description":"Request a challenge, embed it in a signed Nostr event, then call login with `{ pubkey, challenge, event }` to receive a session cookie.\n\nGuide: https://bitsimp.com/docs/sign-in-nostr.md","actions":[{"id":"get_challenge","sourceId":"bitsimp_api","operationId":"nostrNip07Challenge"},{"id":"log_in","sourceId":"bitsimp_api","operationId":"nostrNip07Login"}],"links":[{"origin":{"actionId":"get_challenge","fieldPath":"responses.success.challenge"},"target":{"actionId":"log_in","fieldPath":"requestBody.challenge"}}],"fields":{"parameters":[{"name":"pubkey","description":"32-byte x-only public key as 64-char hex.","required":true,"type":"string"},{"name":"event","description":"Signed Nostr event (NIP-01) embedding the challenge. Omit on the first invocation (challenge issuance); required on the second.","required":false,"type":"object"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"pubkey":{"type":"string"},"challenge":{"type":"string","minLength":64,"maxLength":64},"event":{"type":"object"}},"required":["pubkey"]}}},"required":true},"responses":{"success":{"type":"object","properties":{"success":{"type":"boolean","const":true},"id":{"type":"string","format":"uuid"}},"required":["success","id"]}}}},{"id":"link_nostr_key","title":"Link a Nostr key to the current Account","description":"Attach the npub to the Account you are signed into, so either key signs in to the same Account (ADR 0020). Additive only: 409 when a Nostr method is already linked, or the key already belongs to another Account (Accounts are never merged). Linking never changes the session — keep using the existing cookie. Sign the returned challenge with the new key: the event content should name the target Account (\"Link this key to bitsimp account <alias | npub | Account ID> — challenge: <challenge>\") and must contain the challenge (content or a [\"challenge\", …] tag).","actions":[{"id":"mint_link_challenge","sourceId":"bitsimp_api","operationId":"linkNostrChallenge"},{"id":"complete_link","sourceId":"bitsimp_api","operationId":"linkNostrComplete"}],"links":[{"origin":{"actionId":"mint_link_challenge","fieldPath":"responses.success.challenge"},"target":{"actionId":"complete_link","fieldPath":"requestBody.challenge"}}],"fields":{"parameters":[{"name":"pubkey","description":"32-byte x-only public key as 64-char hex; must match `event.pubkey`.","required":true,"type":"string"},{"name":"event","description":"Signed Nostr event embedding the link challenge (content or a challenge tag).","required":true,"type":"object"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"pubkey":{"type":"string"},"challenge":{"type":"string","minLength":64,"maxLength":64},"event":{"type":"object"}},"required":["pubkey","challenge","event"]}}},"required":true},"responses":{"success":{"type":"object","properties":{"success":{"type":"boolean","const":true},"id":{"type":"string","format":"uuid"}},"required":["success","id"]}}}},{"id":"link_lightning_key","title":"Link a Lightning key to the current Account","description":"Attach a Lightning key to the Account you are signed into, so either key signs in to the same Account (ADR 0020). Headless clients sign the returned challenge (ECDSA DER over the raw 32 bytes) and complete with `{ challenge, pubkey, sig }`. Interactive wallets scan the returned `lnurl` and the wallet callback stages the key, after which the client completes without `sig`. Additive only: 409 when a Lightning method is already linked, or the key already belongs to another Account (Accounts are never merged). Linking never changes the session.","actions":[{"id":"mint_link_challenge","sourceId":"bitsimp_api","operationId":"linkLightningChallenge"},{"id":"complete_link","sourceId":"bitsimp_api","operationId":"linkLightningComplete"}],"links":[{"origin":{"actionId":"mint_link_challenge","fieldPath":"responses.success.challenge"},"target":{"actionId":"complete_link","fieldPath":"requestBody.challenge"}}],"fields":{"parameters":[{"name":"pubkey","description":"33-byte compressed secp256k1 public key as 66-char hex.","required":true,"type":"string"},{"name":"sig","description":"DER-encoded ECDSA signature over the raw 32-byte challenge, as hex. Required for headless clients; omit on the browser path where the wallet callback already staged the key.","required":false,"type":"string"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"challenge":{"type":"string","minLength":64,"maxLength":64},"pubkey":{"type":"string"},"sig":{"type":"string"}},"required":["challenge","pubkey"]}}},"required":true},"responses":{"success":{"type":"object","properties":{"success":{"type":"boolean","const":true},"id":{"type":"string","format":"uuid"}},"required":["success","id"]}}}},{"id":"unlink_signin_method","title":"Unlink a sign-in method from the current Account","description":"Remove an attached sign-in method with `DELETE /api/auth/link` and `{ \"method\": \"lightning\" | \"nostr\" }` in the body. The Account always keeps at least one sign-in method: 409 when the method is not linked, or when it is the last one. The session survives unlinking the key it was created with (identity is the Account ID).","actions":[{"id":"unlink_method","sourceId":"bitsimp_api","operationId":"unlinkSignInMethod"}],"links":[],"fields":{"parameters":[{"name":"method","description":"The attached sign-in method to remove: \"lightning\" or \"nostr\".","required":true,"type":"string"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"method":{"type":"string","enum":["lightning","nostr"]}},"required":["method"]}}},"required":true},"responses":{"success":{"type":"object","properties":{"success":{"type":"boolean","const":true},"id":{"type":"string","format":"uuid"}},"required":["success","id"]}}}},{"id":"browse_feed","title":"List posts from the feed","description":"Fetch the cursor-paginated marketplace feed, optionally filtered by creator alias, price range, file type, or purchase/unlock state. Archived posts are hidden from everyone except their owner. Anonymous browsing is supported.","actions":[{"id":"list_posts","sourceId":"bitsimp_api","operationId":"listPosts"}],"links":[{"origin":{"actionId":"browse_feed","fieldPath":"parameters.author"},"target":{"actionId":"list_posts","fieldPath":"parameters.author"}},{"origin":{"actionId":"browse_feed","fieldPath":"parameters.price_min"},"target":{"actionId":"list_posts","fieldPath":"parameters.price_min"}},{"origin":{"actionId":"browse_feed","fieldPath":"parameters.price_max"},"target":{"actionId":"list_posts","fieldPath":"parameters.price_max"}},{"origin":{"actionId":"browse_feed","fieldPath":"parameters.includes_file_type"},"target":{"actionId":"list_posts","fieldPath":"parameters.includes_file_type"}},{"origin":{"actionId":"browse_feed","fieldPath":"parameters.purchased"},"target":{"actionId":"list_posts","fieldPath":"parameters.purchased"}},{"origin":{"actionId":"browse_feed","fieldPath":"parameters.unlocked"},"target":{"actionId":"list_posts","fieldPath":"parameters.unlocked"}},{"origin":{"actionId":"browse_feed","fieldPath":"parameters.locked"},"target":{"actionId":"list_posts","fieldPath":"parameters.locked"}},{"origin":{"actionId":"browse_feed","fieldPath":"parameters.cursor"},"target":{"actionId":"list_posts","fieldPath":"parameters.cursor"}},{"origin":{"actionId":"browse_feed","fieldPath":"parameters.page_size"},"target":{"actionId":"list_posts","fieldPath":"parameters.page_size"}}],"fields":{"parameters":[{"name":"author","description":"Filter by creator alias.","required":false,"type":"string"},{"name":"price_min","description":"Minimum price in satoshis.","required":false,"type":"string"},{"name":"price_max","description":"Maximum price in satoshis (0 = only free posts).","required":false,"type":"string"},{"name":"includes_file_type","description":"Comma-separated buckets: images, videos, audio, other.","required":false,"type":"string"},{"name":"purchased","description":"Only posts purchased by the authenticated user.","required":false,"type":"string"},{"name":"unlocked","description":"Only posts already unlocked (free or purchased).","required":false,"type":"string"},{"name":"locked","description":"Only locked/paywalled posts.","required":false,"type":"string"},{"name":"cursor","description":"Created-at cursor of the last post from the previous page.","required":false,"type":"string"},{"name":"page_size","description":"Page size (default 5).","required":false,"type":"integer"}],"responses":{"success":{"type":"array","items":{"type":"object"}}}}},{"id":"fetch_post","title":"Get a single post","description":"Fetch one post by id. Requires a session when the post is paid — locked posts return file entries with empty content so gated media is never leaked; unlocked posts include full files. Archived posts return 404 unless the viewer is the owner.","actions":[{"id":"get_post","sourceId":"bitsimp_api","operationId":"getPost"}],"links":[{"origin":{"actionId":"fetch_post","fieldPath":"parameters.postId"},"target":{"actionId":"get_post","fieldPath":"parameters.postId"}}],"fields":{"parameters":[{"name":"postId","description":"Post id (UUID).","required":true,"type":"string"}],"responses":{"success":{"type":"object"}}}},{"id":"mint_post_uploads","title":"Mint signed upload URLs for a future post","description":"Mint one signed URL per planned file, then upload the file bytes to each returned `signedUrl` out-of-band with HTTP `PUT`. After each upload completes, carry forward only the returned `{ bucket, objectName }` as `{ bucketName, objectName }` into `create_post`.\n\nGuide: https://bitsimp.com/docs/publish-post.md","actions":[{"id":"request_upload_signature","sourceId":"bitsimp_api","operationId":"requestUploadSignature"}],"links":[{"origin":{"actionId":"mint_post_uploads","fieldPath":"parameters.files"},"target":{"actionId":"request_upload_signature","fieldPath":"requestBody.files"}}],"fields":{"parameters":[{"name":"files","description":"Files to mint upload signatures for. Each item uses the upload API shape: `{ mime, filename? }`.","required":true,"type":"array"}],"responses":{"success":{"type":"object"}}}},{"id":"create_post","title":"Create a post from uploaded files","description":"Create the post with already-uploaded `{ bucketName, objectName }` references, then fetch the created post once through `get_post`. If file processing is still pending, call `fetch_post` again later until each file reaches `processing_status=ready` or `failed`.\n\nGuide: https://bitsimp.com/docs/publish-post.md","actions":[{"id":"create_post_request","sourceId":"bitsimp_api","operationId":"createPost"},{"id":"get_post","sourceId":"bitsimp_api","operationId":"getPost"}],"links":[{"origin":{"actionId":"create_post","fieldPath":"parameters.title"},"target":{"actionId":"create_post_request","fieldPath":"requestBody.title"}},{"origin":{"actionId":"create_post","fieldPath":"parameters.body"},"target":{"actionId":"create_post_request","fieldPath":"requestBody.body"}},{"origin":{"actionId":"create_post","fieldPath":"parameters.price"},"target":{"actionId":"create_post_request","fieldPath":"requestBody.price"}},{"origin":{"actionId":"create_post","fieldPath":"parameters.fileRefs"},"target":{"actionId":"create_post_request","fieldPath":"requestBody.files"}},{"origin":{"actionId":"create_post_request","fieldPath":"responses.success.id"},"target":{"actionId":"get_post","fieldPath":"parameters.postId"}}],"fields":{"parameters":[{"name":"title","description":"Post title (3-100 chars).","required":true,"type":"string"},{"name":"body","description":"Optional post body.","required":false,"type":"string"},{"name":"price","description":"Price in satoshis. Use 0 for free posts. Paid posts require files.","required":false,"type":"integer"},{"name":"fileRefs","description":"After uploading bytes to each signed URL, pass `{ bucketName, objectName }` references to `create_post_request`.","required":false,"type":"array"}],"responses":{"success":{"type":"object","properties":{"id":{"type":"string","format":"uuid"}},"required":["id"]}}}},{"id":"edit_post","title":"Edit your post","description":"Owner-only partial update of a Post via `PATCH /api/posts/{postId}`. You can update any subset of `title`, `body`, `price`, and `is_archived` (archive or unarchive). At least one recognized field is required; if the resulting price is > 0 the Post must have files.","actions":[{"id":"update_post","sourceId":"bitsimp_api","operationId":"updatePost"}],"links":[{"origin":{"actionId":"edit_post","fieldPath":"parameters.postId"},"target":{"actionId":"update_post","fieldPath":"parameters.postId"}}],"fields":{"parameters":[{"name":"postId","description":"Post id (UUID).","required":true,"type":"string"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":100},"body":{"type":"string"},"price":{"type":"integer","minimum":0},"is_archived":{"type":"boolean"}}}}},"required":true},"responses":{"success":{"type":"object"}}}},{"id":"archive_post","title":"Archive your post","description":"Owner-only archive via `DELETE /api/posts/{postId}`. Idempotent for retries. This archives only (never hard-deletes); unarchive by calling `edit_post` with `is_archived: false`.","actions":[{"id":"delete_post","sourceId":"bitsimp_api","operationId":"deletePost"}],"links":[{"origin":{"actionId":"archive_post","fieldPath":"parameters.postId"},"target":{"actionId":"delete_post","fieldPath":"parameters.postId"}}],"fields":{"parameters":[{"name":"postId","description":"Post id (UUID).","required":true,"type":"string"}],"responses":{"success":{"type":"object"}}}},{"id":"list_post_comments","title":"List comments on a post","description":"Fetch the newest-first, offset-paginated public comments for a post. Public read — no session required.","actions":[{"id":"list_comments","sourceId":"bitsimp_api","operationId":"listComments"}],"links":[{"origin":{"actionId":"list_post_comments","fieldPath":"parameters.postId"},"target":{"actionId":"list_comments","fieldPath":"parameters.postId"}},{"origin":{"actionId":"list_post_comments","fieldPath":"parameters.offset"},"target":{"actionId":"list_comments","fieldPath":"parameters.offset"}},{"origin":{"actionId":"list_post_comments","fieldPath":"parameters.limit"},"target":{"actionId":"list_comments","fieldPath":"parameters.limit"}}],"fields":{"parameters":[{"name":"postId","description":"Post id to fetch comments for (UUID).","required":true,"type":"string"},{"name":"offset","description":"Number of comments to skip.","required":false,"type":"integer"},{"name":"limit","description":"Maximum number of comments to return (default 10).","required":false,"type":"integer"}],"responses":{"success":{"type":"array","items":{"type":"object"}}}}},{"id":"get_my_balance","title":"Get the current user's balance","description":"Return the authenticated user's account id, npub, alias and full available-balance breakdown. Authenticate with a session cookie (see the authenticate_* flows) or a NIP-98 signed request. Balance formula: availableBalance = earnedAmount − routingFees − pwbSpendings − withdrawnAmount − withdrawalFees.","actions":[{"id":"get_me","sourceId":"bitsimp_api","operationId":"getMe"}],"fields":{"parameters":[],"responses":{"success":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"npub":{"type":["string","null"]},"alias":{"type":["string","null"]},"availableBalance":{"type":"object"}},"required":["id","npub","alias","availableBalance"]}}}},{"id":"unlock_post_invoice","title":"Unlock a paid Post via Lightning invoice","description":"Create a Lightning invoice for an unlock, pay it out-of-band, then confirm with the returned `chargeId`.\n\nGuide: https://bitsimp.com/docs/unlock-post.md","actions":[{"id":"get_post","sourceId":"bitsimp_api","operationId":"getPost"},{"id":"request_unlock","sourceId":"bitsimp_api","operationId":"requestUnlock"},{"id":"confirm_unlock","sourceId":"bitsimp_api","operationId":"confirmUnlock"}],"links":[{"origin":{"actionId":"unlock_post_invoice","fieldPath":"parameters.postId"},"target":{"actionId":"get_post","fieldPath":"parameters.postId"}},{"origin":{"actionId":"unlock_post_invoice","fieldPath":"parameters.postId"},"target":{"actionId":"request_unlock","fieldPath":"parameters.postId"}},{"origin":{"actionId":"unlock_post_invoice","fieldPath":"parameters.postId"},"target":{"actionId":"confirm_unlock","fieldPath":"parameters.postId"}},{"origin":{"actionId":"request_unlock","fieldPath":"responses.success.chargeId"},"target":{"actionId":"confirm_unlock","fieldPath":"requestBody.chargeId"}}],"fields":{"parameters":[{"name":"postId","description":"Post id (UUID) to unlock.","required":true,"type":"string"}],"responses":{"success":{"type":"object","properties":{"success":{"type":"boolean","const":true},"transactionId":{"type":"string"},"postId":{"type":"string"}},"required":["success","transactionId","postId"]}}}},{"id":"unlock_post_balance","title":"Unlock a paid Post using on-platform balance","description":"Unlock immediately from balance by POSTing `{ method: \"balance\" }` to `/unlock` for a post.\n\nGuide: https://bitsimp.com/docs/unlock-post.md","actions":[{"id":"request_unlock_balance","sourceId":"bitsimp_api","operationId":"requestUnlock"}],"links":[{"origin":{"actionId":"unlock_post_balance","fieldPath":"parameters.postId"},"target":{"actionId":"request_unlock_balance","fieldPath":"parameters.postId"}}],"fields":{"parameters":[{"name":"postId","description":"Post id (UUID) to unlock.","required":true,"type":"string"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"method":{"type":"string","const":"balance"}},"required":["method"]}}},"required":true},"responses":{"success":{"type":"object","properties":{"success":{"type":"boolean","const":true},"transactionId":{"type":"string"},"postId":{"type":"string"}},"required":["success","transactionId","postId"]}}}}]}