The Lexe Sidecar SDK presents a simple JSON API for developers to control their
self-custodial, always-online Lexe node which can send and receive
payments over the Lightning Network. Running the lexe-sidecar binary spins up
a local webserver (the "sidecar") at http://localhost:5393 which accepts REST
requests and proxies them to your Lexe node. By making simple HTTP requests like
GET http://localhost:5393/v2/node/node_info
POST http://localhost:5393/v2/node/create_invoice
POST http://localhost:5393/v2/node/pay_invoice
GET http://localhost:5393/v2/node/payment
you can programmatically control your Lexe node. Your app can be written in any
language, and is portable to any environment where the lexe-sidecar can run.
This repo contains public-facing docs for the Lexe Sidecar SDK.
The lexe-sidecar source code can be found in the Lexe monorepo at:
https://github.com/lexe-app/lexe-public/tree/master/sdk-sidecar
To get started, you'll need a Lexe wallet. Download the Lexe mobile app for:
- iOS (App Store): https://lexe.app/ios
- Android (Play Store): https://lexe.app/android
After installing the app, select "Create a new wallet", optionally connect your Google Drive (for backups), and set a backup password. This will create a new self-custody Bitcoin+Lightning wallet that runs in a secure enclave in the cloud; you can now send and receive Lightning payments 24/7!
The sidecar supports two credential types:
ClientCredentials: control an existing wallet created in the Lexe app.RootSeed: auth for an existing wallet whose seed you manage.
Option 1: Default ClientCredentials via env (single-wallet use)
To control a wallet created in the app, export client credentials:
- Open the Lexe app > Menu sidebar > "SDK clients" > "Create new client"
- Copy the client credentials string
Set LEXE_CLIENT_CREDENTIALS in your environment or in a .env file:
# Option 1A: Set directly in environment
$ export LEXE_CLIENT_CREDENTIALS="eyJsZXhlX2F1dGhfdG9rZ...TA0In0"
# Option 1B: Use a .env file which the sidecar loads automatically
$ git clone https://github.com/lexe-app/lexe-sidecar-sdk.git
$ cd myproject
$ cp ../lexe-sidecar-sdk/.env.example ./.env
$ chmod 600 .env
# Then edit .env and set LEXE_CLIENT_CREDENTIALSOption 2: Default RootSeed via env (advanced, single-wallet use)
Use this if your backend already stores root seeds. The sidecar does not perform signup or provisioning. For signup and provisioning flows, use the Rust or Python SDKs.
Set LEXE_ROOT_SEED or LEXE_ROOT_SEED_PATH instead:
# Option 2A: Set directly in environment
$ export LEXE_ROOT_SEED="0123...abcd"
# Option 2B: Load from a file containing your hex root seed
$ export LEXE_ROOT_SEED_PATH="/Users/satoshi/secrets/lexe_root_seed.txt"Option 3: Per-request Authorization header (multi-wallet use,
ClientCredentials only)
Alternatively, you can pass credentials per-request via the Authorization
header, with the value formatted as Bearer <credentials>.
This is useful for multi-wallet scenarios where different requests control
different Lexe wallets.
$ curl http://localhost:5393/v2/node/node_info \
-H "Authorization: Bearer eyJsZXhlX2F1dGhfdG9rZ...TA0In0"If both default credentials (env) and per-request credentials (auth header) are provided, the per-request credentials take precedence.
The Lexe Sidecar SDK is distributed as a single self-contained binary called
lexe-sidecar.
It runs a local webserver that accepts HTTP requests
and manages the connection to your Lexe node.
Official install script
The easiest way to install lexe-sidecar is using the official install script:
curl -fsSL https://raw.githubusercontent.com/lexe-app/lexe-sidecar-sdk/master/install.sh | shThis will install the lexe-sidecar binary at ~/.local/bin and add it to your
PATH.
To use the sidecar in your current shell, you may need to run:
# For sh/bash/zsh
source ~/.local/bin/env
# For fish
source ~/.local/bin/env.fishAlternative installation methods
If you prefer manual installation or need to build from source:
- Download a precompiled binary from the Releases page
- Build the binary from Rust source: see BUILD.md for instructions
Using the lexe-sidecar as a Rust library
If you're working in Rust, you may also use the lexe-sidecar as a Rust
library, which provides:
- A
Sidecarwebserver which can be.run()within your async Rust app. - A
SidecarClientwhich fully implements the sidecar API.
See rust-example for an example which implements both a sidecar client and server.
Run the sidecar from your project directory which contains the .env file:
$ lexe-sidecarOr if you downloaded the binary manually:
# Assumes the binary was downloaded to ./bin/lexe-sidecar
$ ./bin/lexe-sidecarMake your first request to the sidecar:
$ curl http://localhost:5393/v2/node/node_info | jq .
{
"version": "0.7.9",
"measurement": "6d6ae19f2a82167abecd7bbe834e417a1b3c9c8971d08bd05b24533de21bf3f1",
"user_pk": "63ad1661bfc23ad25f5bcc6f610f8fd70d7426de51be74766c24e47f4b4fcfca",
"node_pk": "02e4d8f86591eb2ce59a787e2a5abb83278c86198ac22854e9e3cf365cf8d9730f",
"balance": "95507",
"lightning_balance": "40000",
"lightning_sendable_balance": "40000",
"lightning_max_sendable_balance": "39824.777",
"onchain_balance": "55507",
"onchain_trusted_balance": "55507",
"num_channels": 1,
"num_usable_channels": 1
}You're all set - it's time to build your Lightning app! Here's a prompt which
allowed us to "vibe code" a fully functional Lightning tip page in less than 4
minutes with Claude Sonnet 3.7 and goose -
Cursor, Windsurf, etc work well too. Since this prompt tells the AI to run the
sidecar as a subprocess of your app, you should stop the running sidecar first.
Also be sure to change the project names and paths to match your own machine.
Build a tip page powered by the Lexe API. This will be a python web service that
renders a simple tip page for visitors to our site. When a visitor taps the tip
button, the backend will query the Lexe API for a new Lightning invoice and
render it as a QR code on the frontend. The frontend will repeatedly poll the
backend for the invoice status until it's paid. Once the invoice is paid,
it will go the success page and show a confetti animation.
The backend should spawn the Lexe sidecar as a subprocess. The sidecar will
expose the Lexe API at `http://localhost:5393`. You can find the sidecar binary
at </Users/satoshi/.local/bin/lexe-sidecar>. You can find the Lexe API docs
at: </Users/satoshi/dev/lexe-sidecar-sdk/README.md>. Make sure you read this
first so you understand how all the pieces fit together.
We're running on macOS and already have python 3.12 installed. You're already
in the project directory </Users/satoshi/dev/myproject>. Try to use the python
standard library as much as possible. Create a new python virtual environment to
keep all the dependencies isolated. The frontend can be a simple HTML page
rendered by the backend with some basic JS+CSS for polling and rendering the QR
code. Serve the backend on port 8080.
Don't ask for permission, just start coding.
If you're building on Replit, you can quickly set up the sidecar in your environment by pasting the setup prompt below.
Recommended: Prompted Setup with Replit Agent
Before starting the Replit Agent, make sure the Agent is set to "Build" (not "Plan") and the application type is NOT set to "Automation":
Then paste this prompt into the Replit Agent:
Set up the Lexe Sidecar for my Replit project:
1. Download and read the Lexe Sidecar SDK docs for reference.
$ curl -fsSL https://raw.githubusercontent.com/lexe-app/lexe-sidecar-sdk/master/README.md -o LEXE_SIDECAR_DOCS.md
$ cat LEXE_SIDECAR_DOCS.md
2. Install the `unzip` system dependency using the Replit tool.
(This is required for the install script below.)
3. Install the `lexe-sidecar` binary.
$ curl -fsSL https://raw.githubusercontent.com/lexe-app/lexe-sidecar-sdk/master/install.sh | sh
4. Prompt me for my `LEXE_CLIENT_CREDENTIALS` value and configure it as a Replit secret.
5. Ask me to update the `.replit` file to run the sidecar, then confirm it has been configured correctly before proceeding.
When showing me what to change, provide the exact old and new TOML blocks so I can copy-paste without needing to understand TOML syntax.
Make these TWO changes:
A. Add a new "Lexe Sidecar" workflow that runs in parallel with the app.
Add this workflow definition (do NOT add waitForPort - it causes timeouts):
```
[[workflows.workflow]]
name = "Lexe Sidecar"
author = "agent"
mode = "sequential"
[[workflows.workflow.tasks]]
task = "shell.exec"
args = "./.local/bin/lexe-sidecar"
```
Then add a task to run this workflow in parallel with the main app workflow:
```
[[workflows.workflow.tasks]]
task = "workflow.run"
args = "Lexe Sidecar"
```
B. Update the `[deployment]` section's `run` array to prepend the sidecar.
Wrap the command in a shell invocation.
For example, change:
`run = ["npm", "run", "server:prod"]`
to:
`run = ["sh", "-c", "./.local/bin/lexe-sidecar & npm run server:prod"]`
6. Restart the workflow using the `restart_workflow` tool, then wait a few seconds for it to restart.
7. Confirm sidecar is up and running.
- Check the health endpoint: curl http://localhost:5393/v2/health
- Test the credentials config: curl http://localhost:5393/v2/node/node_info
The sidecar will now run automatically alongside the application at http://localhost:5393.
Once we've confirmed that the sidecar runs smoothly with client credentials configured correctly, return to me and we'll begin working on our app. Don't stop until we've confirmed that the node-info endpoint works correctly.
The Replit Agent will configure everything, prompt you for your credentials, and verify the setup is working.
Manual Setup
If you prefer to configure manually:
- Install
lexe-sidecar:curl -fsSL https://raw.githubusercontent.com/lexe-app/lexe-sidecar-sdk/master/install.sh | sh - Configure your
LEXE_CLIENT_CREDENTIALSas a Replit secret - Update
.replitto add a "Lexe Sidecar" workflow that runs./.local/bin/lexe-sidecarin parallel with your app - Update the
[deployment]section'srunto prepend./.local/bin/lexe-sidecar & - Restart the workflow
The sidecar will now run automatically alongside your application at
http://localhost:5393.
Note: This is an unofficial, community-made tool not developed by Lexe. Use at your own risk.
Python users may be interested in using the
lexe-wrapper
Python package developed by @matbalez which
simplifies binary management, the subprocess lifecycle, and connection setup -
simply pip install lexe-wrapper to get started.
See the lexe-wrapper repo for
further instructions.
Watch us vibe code a fully functional Lightning tip page using the above prompt in less than 3 minutes:
The Lexe SDK sidecar exposes the following REST API endpoints:
GET /v2/health
GET /v2/node/node_info
POST /v2/node/create_invoice
POST /v2/node/pay_invoice
GET /v2/node/payment
Payments are uniquely identified by their index value string. Fetch the status
of a payment using the GET /v2/node/payment?index=<index> endpoint.
amount values are fixed-precision decimal values denominated in satoshis
(1 BTC = 100,000,000 satoshis) and serialized as strings. The representation
supports up-to millisatoshi precision (1 satoshi = 1,000 millisatoshis).
All timestamps indicate the number of milliseconds since the UNIX epoch.
Prefer longer request timeouts (e.g. 15 seconds) since your node may need time to startup and sync if it hasn't received any requests in a while.
Get the health status of the Lexe SDK sidecar. Returns HTTP 200 once the sidecar is running and ready to accept requests.
Response:
status:"ok"if configuration is OK, otherwise a warning or error message.
Examples:
$ curl http://localhost:5393/v2/health
{ "status": "ok" }Fetch information about the node and wallet balance.
Request:
Empty.
Response:
-
version: The node's current semver version, e.g.0.6.9. -
measurement: The hex-encoded SGX 'measurement' of the current node. The measurement is the hash of the enclave binary. -
user_pk: The hex-encoded ed25519 user public key used to identify a Lexe user. The user keypair is derived from the root seed. -
node_pk: The hex-encoded secp256k1 Lightning node public key; thenode_id. -
balance: The sum of ourlightning_balanceand ouronchain_balance, in sats. -
lightning_balance: Total Lightning balance in sats, summed over all of our channels. -
lightning_sendable_balance: An estimated upper bound, in sats, on how much of our Lightning balance we can send to most recipients on the Lightning Network, accounting for Lightning limits such as our channel reserve, pending HTLCs, fees, etc. You should usually be able to spend this amount. -
lightning_max_sendable_balance: A hard upper bound on how much of our Lightning balance can be spent right now, in sats. This is always >=lightning_sendable_balance. Generally it is only possible to spend exactly this amount if the recipient is a Lexe user. -
onchain_balance: Total on-chain balance in sats, including unconfirmed funds. -
onchain_trusted_balance: Trusted on-chain balance in sats, including only confirmed funds and unconfirmed outputs originating from our own wallet. -
num_channels: The total number of Lightning channels. -
num_usable_channels: The number of channels which are currently usable, i.e.channel_readymessages have been exchanged and the channel peer is online. Is always less than or equal tonum_channels.
Examples:
$ curl http://localhost:5393/v2/node/node_info | jq .
{
"version": "0.9.2",
"measurement": "e9cc14a630c8c6973be2f7cfdfe1baac5d997e907485f4f21fd1ba179b0a0cb9",
"user_pk": "b484a4890b47358ee68684bcd502d2eefa1bc66cc0f8ac2e5f06384676be74eb",
"node_pk": "0203e73be064cc91d5e3c96d8e2f2f124f3196e07e9916b51307b6ff5419b59f6e",
"balance": "924823",
"lightning_balance": "461071",
"lightning_sendable_balance": "442478.190",
"lightning_max_sendable_balance": "445995.804",
"onchain_balance": "463752",
"onchain_trusted_balance": "463752",
"num_channels": 4,
"num_usable_channels": 4
}Create a new BOLT11 Lightning invoice to receive Bitcoin over the Lightning network.
Request:
The request body should be a JSON object with the following fields:
expiration_secs: Int(optional): The number of seconds until the invoice expires. If not specified, defaults to 86400 (1 day).amount: String(optional): The amount to request in satoshis, as a string. If not specified, the payer will decide the amount.description: String(optional): The payment description that will be presented to the payer.payer_note: String(optional): An optional note received from the payer out-of-band via LNURL-pay that is stored with this inbound payment. If provided, must be non-empty and ≤200 chars / ≤512 UTF-8 bytes.
Response:
The response includes the encoded invoice string, which should be presented to
the payer to complete the payment.
The index is a unique identifier for the invoice, which can be used to track
the payment status via GET /v2/node/payment.
index: Identifier for this inbound invoice payment.invoice: The string-encoded BOLT 11 invoice.description: The description encoded in the invoice, if one was provided.amount: The amount encoded in the invoice, if there was one. Returningnullmeans we created an amountless invoice.created_at: The invoice creation time, in milliseconds since the UNIX epoch.expires_at: The invoice expiration time, in milliseconds since the UNIX epoch.payment_hash: The hex-encoded payment hash of the invoice.payment_secret: The payment secret of the invoice.
Examples:
$ curl -X POST http://localhost:5393/v2/node/create_invoice \
--header "content-type: application/json" \
--data '{ "expiration_secs": 3600 }' \
| jq .
{
"index": "0000001772349163844-ln_003dd23aec576e7d0d85aa991ff9c2dc471fd7c863ff31b93bfbb02836eb56b5",
"invoice": "lnbc1p568ehtdqqpp5qq7aywhv2ah86rv942v3l7wzm3r3l47gv0lnrwfmlwczsdht266scqpcsp5a3v0skathghudyaszdze77dnuh7pnza7phagq5c7ke5mqra3nuqs9qyysgqxqrrssnp4qgp7wwlqvnxfr40re9kcute0zf8nr9hq06v3ddgnq7m074qekk0kurzjqv22wafr68wtchd4vzq7mj7zf2uzpv67xsaxcemfzak7wp7p0r29wzxnguqq2qsqqcqqqqqqqqqqhwqqfq73dfkaqrghzc0lpgeandl5zfjxh2z6fhk47sfph40dqv72tefmw9j4a7c8w0f0l7uyjfa9dzwpy7ypllmvmxd4n2ggfufd593yh5v7cq5uaa9s",
"description": null,
"amount": null,
"created_at": 1772349163000,
"expires_at": 1772352763000,
"payment_hash": "003dd23aec576e7d0d85aa991ff9c2dc471fd7c863ff31b93bfbb02836eb56b5",
"payment_secret": "ec58f85babba2fc693b013459f79b3e5fc198bbe0dfa80531eb669b00fb19f01"
}
$ curl -X POST http://localhost:5393/v2/node/create_invoice \
--header "content-type: application/json" \
--data '{ "expiration_secs": 3600, "amount": "1000", "description": "Lunch" }' \
| jq .
{
"index": "0000001772349167284-ln_16ec06bf32459f0bd3fbbf2fdaace70b60dcadbbfb170de097edd5e515d7ac18",
"invoice": "lnbc10u1p568eh0dqgf36kucmgpp5zmkqd0ejgk0sh5lmhuha4t88pdsdetdmlvtsmcyhah2729wh4svqcqpcsp5red8hqrl6wx8zvufsgfwge2chcrgd9cmxeu8kdl5u5egf2wqhjvq9qyysgqxqrrssnp4qgp7wwlqvnxfr40re9kcute0zf8nr9hq06v3ddgnq7m074qekk0kurzjqv22wafr68wtchd4vzq7mj7zf2uzpv67xsaxcemfzak7wp7p0r29wzxnguqq2qsqqcqqqqqqqqqqhwqqfqd9lunvz89ed636ymga55aypfgdx9g0fgxga4edcxatwzzac2ssqpt6gyuy73ezyav7gsg2tvj92cg9wvzlrrh7jhc76he6r8pllq0qqquygrtn",
"description": "Lunch",
"amount": "1000",
"created_at": 1772349167000,
"expires_at": 1772352767000,
"payment_hash": "16ec06bf32459f0bd3fbbf2fdaace70b60dcadbbfb170de097edd5e515d7ac18",
"payment_secret": "1e5a7b807fd38c7133898212e46558be0686971b36787b37f4e53284a9c0bc98"
}Pay a BOLT11 Lightning invoice.
Request:
The request body should be a JSON object with the following fields:
invoice: String: The encoded invoice string to pay.fallback_amount: String(optional): For invoices without an amount specified, you must specify a fallback amount to pay.note: String(optional): A personal note to attach to the payment. The receiver will not see this note.payer_note: String(optional): An optional note sent to the receiver out-of-band via LNURL-pay. Unlikenote, this is visible to the recipient. If provided, must be non-empty and ≤200 chars / ≤512 UTF-8 bytes.
Response:
The response includes the index of the payment, which can be used to track the
payment status via GET /v2/node/payment.
index: Identifier for this outbound invoice payment.created_at: When we tried to pay this invoice, in milliseconds since the UNIX epoch.
Examples:
$ curl -X POST http://localhost:5393/v2/node/pay_invoice \
--header "content-type: application/json" \
--data '{ "invoice": "lnbc100n1p5qz7z2dq58skjqnr90pjjq4r9wd6qpp5u8uw073l8dp7ked0ujyhegwxx6yxx6aq5ganqyt3pepnk5dm87dqcqpcsp5nrs44f3upgxysnylrrpyrxs96mgazjjstuykyew74zv0najzkdeq9qyysgqxqyz5vqnp4q0w73a6xytxxrhuuvqnqjckemyhv6avveuftl64zzm5878vq3zr4jrzjqv22wafr68wtchd4vzq7mj7zf2uzpv67xsaxcemfzak7wp7p0r29wz5ecsqq2pgqqcqqqqqqqqqqhwqqfqrpeeq5xdys8vcfcark45w992h6j5nhajc62wet0q25ggxjwhtcfn8c3qx30fqzq8mqxfdtks57zw25zp0z2kl9yrfwkkthxclawxpfcqtdcpfu" }' \
| jq .
{
"index": "0000001744926842458-ln_e1f8e7fa3f3b43eb65afe4897ca1c63688636ba0a23b3011710e433b51bb3f9a",
"created_at": 1744926842458
}Use this endpoint to query the status of a payment or invoice. Payments will transition
through the following status states: "pending" -> "completed" or "pending" -> "failed".
Once a payment is finalized (either completed or failed), you do not need to query
the payment any more.
Request:
The request should include the index of the payment query as a query string
parameter.
Response:
The response includes the payment details. If the payment is not found, the endpoint returns HTTP 404.
index: Unique payment identifier, ordered bycreated_at.rail: The technical payment mechanism:"onchain","invoice","offer","spontaneous".kind: The application-level payment kind, e.g."onchain","invoice","offer","spontaneous","waived_channel_fee","waived_liquidity_fee".direction: The payment direction:"inbound","outbound", or"info".txid: (Onchain payments only) The hex-encoded Bitcoin txid.amount: The payment amount in satoshis, ornullfor pending amountless invoices.fees: Fees paid in satoshis.status: The status of this payment:"pending","completed","failed".status_msg: The payment status as a human-readable message. These strings are customized per payment type, e.g. "invoice generated", "timed out".address: (Onchain send only) The destination Bitcoin address.invoice: (Invoice payments only) The BOLT11 invoice string.tx: (Onchain payments only) The raw Bitcoin transaction.note: An optional personal note attached to this payment.payer_name: (Offer payments only) The payer's self-reported name.payer_note: (Offer payments, LNURL-pay invoices) A payer-provided note.priority: (Onchain send only) The confirmation priority:"high","normal","background".expires_at: The invoice or offer expiry time, in milliseconds since the UNIX epoch.finalized_at: If this payment is finalized, meaning it is "completed" or "failed", this is the time it was finalized, in milliseconds since the UNIX epoch.created_at: When this payment was created, in milliseconds since the UNIX epoch.updated_at: When this payment was last updated, in milliseconds since the UNIX epoch.
Examples:
$ curl 'http://localhost:5393/v2/node/payment?index=0000001772349163844-ln_003dd23aec576e7d0d85aa991ff9c2dc471fd7c863ff31b93bfbb02836eb56b5' \
| jq .
{
"index": "0000001772349163844-ln_003dd23aec576e7d0d85aa991ff9c2dc471fd7c863ff31b93bfbb02836eb56b5",
"rail": "invoice",
"kind": "invoice",
"direction": "inbound",
"txid": null,
"amount": null,
"fees": "0",
"status": "pending",
"status_msg": "invoice generated",
"address": null,
"invoice": "lnbc1p568ehtdqqpp5qq7aywhv2ah86rv942v3l7wzm3r3l47gv0lnrwfmlwczsdht266scqpcsp5a3v0skathghudyaszdze77dnuh7pnza7phagq5c7ke5mqra3nuqs9qyysgqxqrrssnp4qgp7wwlqvnxfr40re9kcute0zf8nr9hq06v3ddgnq7m074qekk0kurzjqv22wafr68wtchd4vzq7mj7zf2uzpv67xsaxcemfzak7wp7p0r29wzxnguqq2qsqqcqqqqqqqqqqhwqqfq73dfkaqrghzc0lpgeandl5zfjxh2z6fhk47sfph40dqv72tefmw9j4a7c8w0f0l7uyjfa9dzwpy7ypllmvmxd4n2ggfufd593yh5v7cq5uaa9s",
"tx": null,
"note": null,
"payer_name": null,
"payer_note": null,
"priority": null,
"expires_at": 1772352763000,
"finalized_at": null,
"created_at": 1772349163844,
"updated_at": 1772349163844
}
# Example of missing payment (returns HTTP 404)
$ curl -i 'http://localhost:5393/v2/node/payment?index=0000000000000000000-ln_0000000000000000000000000000000000000000000000000000000000000000'
HTTP/1.1 404 Not Found
content-type: application/json
content-length: 68
date: Thu, 11 Sep 2025 22:26:53 GMT
{
"code": 107,
"msg": "Payment not found",
"data": null,
"sensitive": false
}The Lexe Sidecar can send webhook notifications when payments are finalized (completed or failed). This is useful for tracking payment status without polling.
Set LEXE_WEBHOOK_URL in your environment or pass --webhook-url to the
sidecar binary:
# Via environment variable
$ export LEXE_WEBHOOK_URL="https://example.com/webhooks/lexe"
# Via CLI argument
$ ./bin/lexe-sidecar --webhook-url https://example.com/webhooks/lexeWhen a payment is finalized, the sidecar will POST a JSON payload to your webhook URL:
{
"user_pk": "63ad1661bfc23ad25f5bcc6f610f8fd70d7426de51be74766c24e47f4b4fcfca",
"index": "0000001744926519917-ln_9be5e4e3a0356cc4a7a1dce5a4af39e2896b7eb7b007ec6ca8c2f8434f21a63a",
"rail": "invoice",
"kind": "invoice",
"direction": "inbound",
"txid": null,
"amount": "1000",
"fees": "0",
"status": "completed",
"status_msg": "completed",
"address": null,
"invoice": "lnbc10n1p5qz7z2dq...",
"tx": null,
"note": null,
"payer_name": null,
"payer_note": null,
"priority": null,
"expires_at": 1744930119917,
"finalized_at": 1744926857989,
"created_at": 1744926519917,
"updated_at": 1744926857989
}The webhook payload uses the same response schema as GET /v2/node/payment, with an
additional user_pk field to identify the wallet (useful for multi-wallet setups).
Your endpoint should return a 200 status code to indicate success.
NOTE: Make sure only the sidecar can make requests to your webhook URL! Otherwise, someone could trick your server into thinking a payment was finalized by sending a fake webhook to it. We recommend running your webhook endpoint on the same machine as the sidecar, so notifications can be received via localhost.
NOTE: All webhook handling must be idempotent. In rare occasions, you might receive a payment notification more than once.
The Lexe Sidecar persists all webhook tracking state into a .lexe/ directory
in the current working directory. You can override this default directory by using
the LEXE_DATA_DIR environment variable or the --data-dir CLI argument.
# Via environment variable
$ export LEXE_DATA_DIR="$HOME/.lexe"
# Via CLI argument
$ ./bin/lexe-sidecar --data-dir ~/.lexe
