---
name: phoenixd
description: Interact with a phoenixd Lightning Network node via its HTTP API. Use this skill when the user wants to create Lightning invoices (bolt11), create Bolt12 offers, pay invoices or offers, pay Lightning addresses, send on-chain payments, check payment status, manage channels, decode invoices/offers, use LNURL services, or check node info and balance. Requires PHOENIXD_URL and PHOENIXD_PASSWORD environment variables.
metadata:
  openclaw:
    emoji: "⚡"
    primaryEnv: "PHOENIXD_PASSWORD"
---

# phoenixd Lightning Node Skill

This skill lets you interact with a phoenixd Lightning node via its full HTTP API — creating and paying invoices, managing channels, checking balances, and more.

## Environment Variables

- `PHOENIXD_URL` — The base URL of the phoenixd instance (e.g., `https://phoenixd-1234.nodana.app:9740`).
- `PHOENIXD_PASSWORD` — The HTTP password for phoenixd authentication. This may be the full-access password or the restricted password — see Authentication below.

## Setup

Before making any API calls, check if the environment variables are set:

```bash
echo "PHOENIXD_URL=$PHOENIXD_URL"
echo "PHOENIXD_PASSWORD=$PHOENIXD_PASSWORD"
```

If either is empty, ask the user for:

- The node URL (e.g., `https://phoenixd-1234.nodana.app:9740`)
- The password — ask whether it is the **full-access** or **restricted** password, as this determines what actions are available

Then save them to `~/.bashrc` so they persist across sessions:

```bash
echo 'export PHOENIXD_URL=https://phoenixd-1234.nodana.app:9740' >> ~/.bashrc
echo 'export PHOENIXD_PASSWORD=the-password-here' >> ~/.bashrc
source ~/.bashrc
```

Do NOT echo the password back to the user after saving it.

## Authentication

All requests use HTTP Basic Auth with an empty username and the password from `PHOENIXD_PASSWORD`:

```bash
curl -s -u :"$PHOENIXD_PASSWORD" ...
```

### Password types

phoenixd has two passwords, both found in `~/.phoenix/phoenix.conf`:

**Full-access password** (`http-password`)

- Grants access to all endpoints including sending payments, closing channels, and exporting history.
- Should be treated as highly sensitive. Only use this when the user explicitly needs to send funds or perform destructive actions.

**Restricted password** (`http-password-limited-access`)

- Grants access to read-only and receive-only endpoints: checking balances, listing payments, creating invoices, getting node info, decoding invoices, and estimating fees.
- The following endpoints are **blocked** with the restricted password: `payinvoice`, `payoffer`, `paylnaddress`, `lnurlpay`, `lnurlauth`, `sendtoaddress`, `closechannel`, `export`.
- Safe to use in automated or less trusted contexts (e.g. an agent that only needs to generate and monitor invoices).

### Handling permission errors

If an API call returns a `401 Unauthorized` or permission error when attempting a blocked endpoint:

1. Inform the user that the current password is the restricted password and does not permit that action.
2. Ask whether they want to provide the full-access password to proceed.
3. If yes, save it as `PHOENIXD_PASSWORD` and retry. Do NOT overwrite the restricted password permanently unless the user explicitly asks — offer to save it as a separate variable (e.g. `PHOENIXD_PASSWORD_FULL`) if they want to keep both available.
4. Never assume a 401 means the URL or password is wrong — check which endpoint was called first.

---

## API Endpoints

### Get Node Info

```bash
curl -s -u :"$PHOENIXD_PASSWORD" "$PHOENIXD_URL/getinfo"
```

Returns `nodeId` and a compact list of channels with their state, balance, and liquidity.

---

### Get Balance

```bash
curl -s -u :"$PHOENIXD_PASSWORD" "$PHOENIXD_URL/getbalance"
```

Returns `balanceSat` and `feeCreditSat`.

---

### Create Bolt11 Invoice

```bash
curl -s -u :"$PHOENIXD_PASSWORD" -X POST \
  -d "description=Payment for service" \
  -d "amountSat=1000" \
  -d "externalId=my-label" \
  "$PHOENIXD_URL/createinvoice"
```

Parameters:

- `description` (string, required): Invoice description (max 128 chars). Use `descriptionHash` instead to pass a sha256 hash.
- `amountSat` (number, optional): Amount in satoshis. If omitted, invoice accepts any amount.
- `expirySeconds` (number, optional): Expiry in seconds. Default: 3600.
- `externalId` (string, **required**): A short, lowercase, hyphenated identifier you choose (e.g. `johns-order`, `march-rent`). Always set this — it is the primary way to look up and confirm payment later. See the externalId rules below.
- `webhookUrl` (string, optional): A webhook URL to notify when this specific invoice is paid (authenticated).

Returns: `amountSat`, `paymentHash`, `serialized` (the bolt11 invoice string).

---

### Create Bolt12 Offer

```bash
curl -s -u :"$PHOENIXD_PASSWORD" -X POST \
  -d "description=Tip jar" \
  -d "amountSat=1000" \
  "$PHOENIXD_URL/createoffer"
```

Parameters:

- `description` (string, optional): Offer description (max 128 chars).
- `amountSat` (number, optional): Amount in satoshis. If omitted, offer accepts any amount.

Returns the bolt12 offer string. Unlike bolt11 invoices, offers are static and reusable — they don't expire and can be paid many times. Well suited for donations or tips.

---

### Get Lightning Address

```bash
curl -s -u :"$PHOENIXD_PASSWORD" "$PHOENIXD_URL/getlnaddress"
```

Returns a BIP-353 Lightning address from the LSP (e.g. `name@phoenixwallet.me`). Only works if a channel exists.

---

### Pay Bolt11 Invoice

```bash
curl -s -u :"$PHOENIXD_PASSWORD" -X POST \
  -d "invoice=lntb..." \
  -d "amountSat=1000" \
  "$PHOENIXD_URL/payinvoice"
```

Parameters:

- `invoice` (string, required): The bolt11 invoice to pay.
- `amountSat` (number, optional): Override amount in satoshis. If omitted, pays the amount in the invoice.

Returns: `recipientAmountSat`, `routingFeeSat`, `paymentId`, `paymentHash`, `paymentPreimage`.

---

### Pay Bolt12 Offer

```bash
curl -s -u :"$PHOENIXD_PASSWORD" -X POST \
  -d "offer=lno1..." \
  -d "amountSat=123" \
  -d "message=Hello!" \
  "$PHOENIXD_URL/payoffer"
```

Parameters:

- `offer` (string, required): The bolt12 offer to pay.
- `amountSat` (number, optional): Amount in satoshis. If omitted, pays the amount in the offer.
- `message` (string, optional): A message for the recipient.

Returns: `recipientAmountSat`, `routingFeeSat`, `paymentId`, `paymentHash`, `paymentPreimage`.

---

### Pay Lightning Address

```bash
curl -s -u :"$PHOENIXD_PASSWORD" -X POST \
  -d "address=user@domain.com" \
  -d "amountSat=123" \
  -d "message=Hello!" \
  "$PHOENIXD_URL/paylnaddress"
```

Parameters:

- `address` (string, required): A BIP-353 or LNURL-based Lightning address.
- `amountSat` (number, optional): Amount in satoshis.
- `message` (string, optional): A message for the recipient.

Returns: `recipientAmountSat`, `routingFeeSat`, `paymentId`, `paymentHash`, `paymentPreimage`.

---

### Pay On-chain (Splice Out)

```bash
curl -s -u :"$PHOENIXD_PASSWORD" -X POST \
  -d "amountSat=100000" \
  -d "address=bc1q..." \
  -d "feerateSatByte=12" \
  "$PHOENIXD_URL/sendtoaddress"
```

Parameters:

- `amountSat` (number, required): Amount in satoshis.
- `address` (string, required): Bitcoin address to send funds to.
- `feerateSatByte` (number, required): Fee rate in satoshis per vbyte.

Returns the transaction ID. The channel remains open after the splice.

---

### Bump Fee (CPFP)

```bash
curl -s -u :"$PHOENIXD_PASSWORD" -X POST \
  -d "feerateSatByte=11" \
  "$PHOENIXD_URL/bumpfee"
```

Parameters:

- `feerateSatByte` (number, required): New fee rate in satoshis per vbyte.

Makes all unconfirmed transactions use a higher fee rate using CPFP. Returns the child transaction ID.

---

### List Incoming Payments

```bash
curl -s -u :"$PHOENIXD_PASSWORD" "$PHOENIXD_URL/payments/incoming?all=true&limit=50"
```

Parameters (query string):

- `all` (boolean, optional): Include unpaid/expired invoices. Default: false.
- `limit` (number, optional): Number of results. Default: 20.
- `offset` (number, optional): Pagination offset. Default: 0.
- `from` (number, optional): Start timestamp in milliseconds from epoch.
- `to` (number, optional): End timestamp in milliseconds from epoch.
- `externalId` (string, optional): Filter by externalId.

If the response contains `"isPaid": true` or a `completedAt` timestamp, the payment has been received. Otherwise it is pending.

---

### Get Incoming Payment by paymentHash

```bash
curl -s -u :"$PHOENIXD_PASSWORD" "$PHOENIXD_URL/payments/incoming/$PAYMENT_HASH"
```

### Get Incoming Payment by externalId

```bash
curl -s -u :"$PHOENIXD_PASSWORD" "$PHOENIXD_URL/payments/incoming?externalId=my-label"
```

---

### List Outgoing Payments

```bash
curl -s -u :"$PHOENIXD_PASSWORD" "$PHOENIXD_URL/payments/outgoing?all=true&limit=50"
```

Parameters (query string):

- `all` (boolean, optional): Include failed payments. Default: false.
- `limit` (number, optional): Number of results. Default: 20.
- `offset` (number, optional): Pagination offset. Default: 0.
- `from` (number, optional): Start timestamp in milliseconds from epoch.
- `to` (number, optional): End timestamp in milliseconds from epoch.

---

### Get Outgoing Payment by paymentId

```bash
curl -s -u :"$PHOENIXD_PASSWORD" "$PHOENIXD_URL/payments/outgoing/$PAYMENT_ID"
```

### Get Outgoing Payment by paymentHash

```bash
curl -s -u :"$PHOENIXD_PASSWORD" "$PHOENIXD_URL/payments/outgoingbyhash/$PAYMENT_HASH"
```

---

### Export Payment History (CSV)

```bash
curl -s -u :"$PHOENIXD_PASSWORD" -X POST \
  "$PHOENIXD_URL/export"
```

Parameters (query string, optional):

- `from`: Start timestamp in milliseconds from epoch.
- `to`: End timestamp in milliseconds from epoch.

Returns the path of the generated CSV file on the server's filesystem. Requires the full (non-limited) password.

---

### List Channels

```bash
curl -s -u :"$PHOENIXD_PASSWORD" "$PHOENIXD_URL/listchannels"
```

Returns a detailed list of all channels including state, balances, fees, and channel parameters.

---

### Close Channel

```bash
curl -s -u :"$PHOENIXD_PASSWORD" -X POST \
  -d "channelId=abc123..." \
  -d "address=bc1q..." \
  -d "feerateSatByte=10" \
  "$PHOENIXD_URL/closechannel"
```

Parameters:

- `channelId` (string, required): The channel ID to close.
- `address` (string, required): Bitcoin address where balance will be sent.
- `feerateSatByte` (number, required): Fee rate in satoshis per vbyte.

Returns the closing transaction ID. **This is irreversible.**

---

### Decode Invoice

```bash
curl -s -u :"$PHOENIXD_PASSWORD" -X POST \
  -d "invoice=lntb..." \
  "$PHOENIXD_URL/decodeinvoice"
```

Parameters:

- `invoice` (string, required): A bolt11 invoice to decode.

Returns decoded fields including `amount`, `paymentHash`, `description`, `timestampSeconds`, and feature flags.

---

### Decode Offer

```bash
curl -s -u :"$PHOENIXD_PASSWORD" -X POST \
  -d "offer=lno1..." \
  "$PHOENIXD_URL/decodeoffer"
```

Parameters:

- `offer` (string, required): A bolt12 offer to decode.

Returns decoded offer fields including chain, path, and blinded node details.

---

### Estimate Liquidity Fees

```bash
curl -s -u :"$PHOENIXD_PASSWORD" -X POST \
  -d "amountSat=2000000" \
  "$PHOENIXD_URL/estimateliquidityfees"
```

Parameters:

- `amountSat` (number, required): The liquidity amount in satoshis.

Returns `miningFeeSat` and `serviceFeeSat`. Note: the estimate does not account for any existing fee credit.

---

### LNURL Pay

```bash
curl -s -u :"$PHOENIXD_PASSWORD" -X POST \
  -d "lnurl=LNURL1..." \
  -d "amountSat=100" \
  -d "message=Hello!" \
  "$PHOENIXD_URL/lnurlpay"
```

Parameters:

- `lnurl` (string, required): The LNURL-pay resource.
- `amountSat` (number, required): Amount to pay in satoshis.
- `message` (string, optional): A comment for the recipient.

Returns: `recipientAmountSat`, `routingFeeSat`, `paymentId`, `paymentHash`, `paymentPreimage`.

---

### LNURL Withdraw

```bash
curl -s -u :"$PHOENIXD_PASSWORD" -X POST \
  -d "lnurl=LNURL1..." \
  "$PHOENIXD_URL/lnurlwithdraw"
```

Parameters:

- `lnurl` (string, required): The LNURL-withdraw resource.

Withdraws the maximum amount authorized by the service. Returns the URL, min/max withdrawable amounts, and the generated invoice.

---

### LNURL Auth

```bash
curl -s -u :"$PHOENIXD_PASSWORD" -X POST \
  -d "lnurl=lnurl1..." \
  "$PHOENIXD_URL/lnurlauth"
```

Parameters:

- `lnurl` (string, required): The LNURL-auth resource.

Authenticates with a remote service. The signing key is derived from the wallet key per the service's domain. **Do not use this if multiple users share the same phoenixd instance** — they will share the same key and signature.

Returns: `authentication success`.

---

### Payments WebSocket

```bash
WS_URL="${PHOENIXD_URL/https:\/\//wss://}"
WS_URL="${WS_URL/http:\/\//ws://}"
websocat --basic-auth :"$PHOENIXD_PASSWORD" "$WS_URL/websocket"
```

Streams JSON events for received payments in real time. Each event has a `type` field (currently `payment_received`) plus `amountSat`, `paymentHash`, and optionally `externalId`, `payerKey`, and `payerNote`.

Authentication can be done via Basic Auth or the `Sec-WebSocket-Protocol` header.

---

## Workflows

### externalId rules

The `externalId` is the primary handle for tracking invoices after creation. Follow these rules every time:

- **Always set it.** Never create an invoice without an `externalId`.
- **Derive it from context.** Use a short, lowercase, hyphenated label that reflects what the payment is for (e.g. `johns-order`, `march-rent`, `website-design-deposit`, `api-credits-2026-03`).
- **Make it unique per invoice.** If the same purpose generates multiple invoices, append a counter or date (e.g. `consulting-2026-03`, `consulting-2026-04`).
- **Tell the user the label.** After creating an invoice always say: "I've created the invoice labelled `<externalId>`. Ask me anytime to check if it's been paid."
- **Remember it.** Keep the `externalId` in mind for the rest of the conversation so you can check status without asking the user to repeat it.

### Creating an invoice

1. Decide on a meaningful `externalId` using the rules above. If there isn't enough context, ask the user for a short label before proceeding.
2. Create the invoice with `POST /createinvoice`, always including the `externalId`.
3. Share the `serialized` bolt11 string with the user.
4. Confirm the label: "I've created the invoice labelled `<externalId>`. Ask me anytime to check if it's been paid."

### Checking if an invoice has been paid

When the user asks whether an invoice has been paid (e.g. "has my invoice been paid?", "did I receive the payment?", "check if X paid me"):

1. **If you know the `externalId`** from earlier in the conversation, look it up directly:

```bash
curl -s -u :"$PHOENIXD_PASSWORD" "$PHOENIXD_URL/payments/incoming?externalId=<externalId>"
```

2. **If you don't know the `externalId`**, ask the user for the label, or list recent invoices and find a match by description or amount:

```bash
curl -s -u :"$PHOENIXD_PASSWORD" "$PHOENIXD_URL/payments/incoming?all=true&limit=50"
```

3. **Interpret the result:**
   - `"isPaid": true` or `completedAt` present → paid. Tell the user the amount received (`receivedSat`) and when (`completedAt`).
   - `"isPaid": false`, no `completedAt` → not yet paid. Check `isExpired` — if `true`, the invoice has expired and can no longer be paid; offer to create a new one.
   - Empty result → the `externalId` doesn't match any invoice. Double-check the label or search the full list.

### Paying someone

- If given a bolt11 invoice string (`lntb...` or `lnbc...`) → use `POST /payinvoice`
- If given a bolt12 offer string (`lno1...`) → use `POST /payoffer`
- If given an email-style address (e.g. `user@domain.com`) → use `POST /paylnaddress`
- If given a `LNURL1...` string → use `POST /lnurlpay`
- If given a Bitcoin address (`bc1...`, `tb1...`) → use `POST /sendtoaddress`

### Checking node health

Use `GET /getinfo` for node ID and channel overview, and `GET /getbalance` for spendable balance.

---

## Important Notes

- All amounts use satoshis (`amountSat`). 1 sat = 1000 millisatoshis.
- A 0.4% routing fee applies to Lightning payments (`payinvoice`, `payoffer`, `paylnaddress`, `lnurlpay`).
- phoenixd manages liquidity automatically — no need to manually manage channels.
- **Never poll or wait for payment.** Invoices may take hours or days to be paid. Create the invoice, give it to the user, and move on. Only check status when the user asks.
- Never share or log the `PHOENIXD_PASSWORD`.
- `closechannel` is irreversible — always confirm with the user before calling it.
