Skip to content

API Reference

WoompaLoompa edited this page Jul 12, 2026 · 1 revision

HTTP Endpoints

All store-scoped endpoints are prefixed with /plugins/{storeId}/clink.

Admin Endpoints

GET /plugins/{storeId}/clink/configure

Returns the admin configuration page.

  • Auth: Store settings view permission
  • Response: HTML (Razor view)

POST /plugins/{storeId}/clink/configure

Save CLINK configuration.

  • Auth: Store settings modify permission
  • Request Body: ClinkSettings form data
  • Response: Redirect to configure page

Validation:

  • Noffer must start with noffer1 or be empty

GET /plugins/{storeId}/clink/validate-noffer

Validate the current store's offer string format.

  • Auth: Store settings view permission
  • Response:
{
  "valid": true,
  "message": "Offer string looks valid."
}

Payment Endpoints

GET /plugins/{storeId}/clink/payment-status

Returns the CLINK payment status for a store.

  • Auth: None
  • Response:
{
  "enabled": true,
  "noffer": "noffer1qyz3unz..."
}

GET /plugins/{storeId}/clink/{invoiceId}/status

Check if a specific invoice has been paid.

  • Auth: None
  • Response:
{
  "paid": true
}

POST /plugins/{storeId}/clink/{invoiceId}/notify-payment

Record a payment notification for an invoice.

  • Auth: None
  • Request Body:
{
  "InvoiceId": "string",
  "Bolt11": "lnbc...",
  "AmountSats": 1000,
  "Paid": true
}
  • Response:
{
  "status": "recorded"
}

Subscription Endpoints

POST /clink/store-ndebit

Store an nDebit string for a customer email. If an invoice ID is provided, attempts auto-payment.

  • Auth: None
  • Request Body:
{
  "InvoiceId": "string (optional)",
  "Ndebit": "ndebit1...",
  "BuyerEmail": "customer@example.com (optional)",
  "StoreId": "string (optional)"
}
  • Response:
{
  "status": "paid" | "stored",
  "autoPayError": "string (optional)"
}

GET /clink/ndebit-setup

Standalone nDebit setup page for customers.

  • Auth: None
  • Query Parameters:
    • portalSessionId (optional) — BTCPay portal session ID
    • storeId (optional) — Store ID
    • email (optional) — Customer email
  • Response: HTML (Razor view)

GET /clink/check-subscription-invoice

Check if an invoice is a subscription invoice (determines whether to show nDebit UI).

  • Auth: None
  • Query Parameters:
    • invoiceId — The BTCPay invoice ID
  • Response:
{
  "isSubscription": true
}

Nostr Bridge Protocol

The Nostr bridge (clink-bridge.bundle.mjs) communicates via stdin/stdout JSON.

Request Format

node clink-bridge.bundle.mjs <command> <<< '{"key": "value"}'

Commands

request-invoice

Request a BOLT11 Lightning invoice from a CLINK node.

Input:

{
  "noffer": "noffer1...",
  "amountSats": 1000,
  "description": "Order #1234",
  "expiresInSeconds": 600,
  "timeoutSeconds": 60,
  "additionalRelays": ["wss://relay.example.com"]
}

Output (success):

{
  "bolt11": "lnbc10...",
  "eventId": "abc123...",
  "fromPub": "pubkey...",
  "privkeyHex": "hex..."
}

Output (error):

{
  "error": "No response from noffer"
}

Nostr Events:

  • Sends: kind 21001 (NIP-44 encrypted offer request)
  • Receives: kind 21001 (NIP-44 encrypted BOLT11 response)

check-payment

Check if an invoice has been paid via Nostr.

Input:

{
  "noffer": "noffer1...",
  "eventId": "abc123...",
  "fromPub": "pubkey...",
  "privkeyHex": "hex...",
  "timeoutSeconds": 30,
  "additionalRelays": []
}

Output:

{
  "paid": true
}

Nostr Events:

  • Subscribes to: kinds 21001, 21002 (from merchant, tagged with customer pubkey)
  • Looks for: NIP-44 encrypted { "res": "ok" }

pay-invoice

Pay a Lightning invoice using an nDebit string.

Input:

{
  "ndebit": "ndebit1...",
  "bolt11": "lnbc10...",
  "amountSats": 1000,
  "timeoutSeconds": 45,
  "additionalRelays": []
}

Output (success):

{
  "res": "ok",
  "preimage": "hex..."
}

Output (error):

{
  "error": "Payment failed"
}

Nostr Events:

  • Sends: kind 21002 (nDebit payment request via SendNdebitRequest)

Timeouts

Command Bridge Timeout Process Timeout
request-invoice 60s (configurable) 120s
check-payment 30s (configurable) 120s
pay-invoice 45s (configurable) 120s

The .NET side enforces a hard 120-second timeout on all bridge processes. If the bridge doesn't exit within this time, the process is killed.

ILightningClient Interface

ClinkLightningClient implements BTCPay Server's ILightningClient interface:

Method Supported Notes
CreateInvoice Min 10 sats, auto-pays via nDebit if configured
GetInvoice Polls Nostr for payment status
ListInvoices Returns all invoices from the event store
GetPayment Returns payment details if paid
ListPayments Returns only paid invoices
Listen Polls every 3 seconds
GetInfo Returns static info (alias: "CLINK nOffer Node")
GetBalance Returns empty balances
Pay Pays via nDebit if configured
CancelInvoice Removes from event store
ConnectTo Always returns Ok
OpenChannel Throws NotSupportedException
GetDepositAddress Throws NotSupportedException
ListChannels Returns empty array

Connection String Handler

ClinkConnectionStringHandler parses connection strings of the format:

type=clink-noffer;noffer=noffer1...;ndebit=ndebit1...
Key Required Description
type Yes Must be clink-noffer
noffer Yes CLINK offer string
ndebit No nDebit string for auto-pay

Returns null if the type doesn't match clink-noffer (allows other handlers to process).

Clone this wiki locally