Cashu Redeem API
A production-grade API for redeeming Cashu tokens (ecash) to Lightning addresses using the cashu-ts library and LNURLp protocol.
Features
- Decode Cashu tokens - Parse and validate token content, check spendability at the mint
- Redeem to Lightning addresses - Convert ecash to Lightning payments via LNURLp with exact fee calculation
- Lightning address validation - Verify addresses and query LNURLp capabilities
- Duplicate detection - In-memory tracking prevents double-spend attempts
- Security - Domain restrictions, rate limiting, input validation, CORS protection
- Interactive API docs - Complete Swagger/OpenAPI documentation at
/docs
API Documentation
Interactive Swagger documentation is available at /docs when the server is running.
- Local:
http://localhost:3000/docs - OpenAPI spec:
http://localhost:3000/openapi.json
API Endpoints
POST /api/decode
Decode a Cashu token and return its content. Supports both v3 (cashuB) and v1 (cashuA) token formats. Checks spendability against the mint.
Request:
{
"token": "cashuB..."
}
Response:
{
"success": true,
"decoded": {
"mint": "https://kashu.me",
"totalAmount": 16,
"numProofs": 5,
"denominations": [8, 4, 2, 1, 1],
"format": "cashuB",
"spent": false
},
"mint_url": "https://kashu.me"
}
POST /api/redeem
Redeem a Cashu token to a Lightning address. The Lightning address is optional if a default is configured.
The redemption process:
- Token validation and parsing
- Spendability check at the mint
- Melt quote to determine exact fees
- Invoice creation for the net amount (token amount minus fees)
- Token melting and Lightning payment
Request:
{
"token": "cashuB...",
"lightningAddress": "user@twentyone.tips"
}
Request (using default address):
{
"token": "cashuB..."
}
Success Response:
{
"success": true,
"paid": true,
"amount": 16,
"invoiceAmount": 14,
"to": "user@twentyone.tips",
"fee": 2,
"netAmount": 14,
"mint_url": "https://kashu.me",
"format": "cashuB",
"preimage": "d5af74a3..."
}
Response fields:
| Field | Description |
|---|---|
amount |
Total token amount in sats |
invoiceAmount |
Amount sent to the Lightning address (after fees) |
fee |
Exact fee charged by the mint (from melt quote) |
netAmount |
Net amount received |
preimage |
Lightning payment preimage (proof of payment) |
usingDefaultAddress |
Present and true when the default address was used |
POST /api/validate-address
Validate a Lightning address without performing a redemption. Tests LNURLp resolution and returns address capabilities.
Request:
{
"lightningAddress": "user@twentyone.tips"
}
Response:
{
"success": true,
"valid": true,
"domain": "twentyone.tips",
"minSendable": 1,
"maxSendable": 1000000,
"commentAllowed": 2000
}
GET /api/health
Health check endpoint returning server status, uptime, and memory usage.
Response:
{
"status": "ok",
"timestamp": "2026-02-17T03:36:40.987Z",
"uptime": 170.23,
"memory": { "rss": 83873792, "heapTotal": 20520960, "heapUsed": 17170824 },
"version": "2.0.0"
}
GET /openapi.json
Returns the full OpenAPI 3.0 specification as JSON.
Setup & Installation
Prerequisites
- Node.js >= 18.0.0
- npm >= 8.0.0
Installation
- Clone and install dependencies:
git clone https://github.com/Michilis/cashu-redeem-api.git
cd cashu-redeem-api
npm install
- Configure environment variables:
cp env.example .env
Edit .env:
# Server Configuration
PORT=3000
NODE_ENV=development
# API Domain/IP Configuration (for Swagger docs and CORS)
API_DOMAIN=localhost:3000
# Security Configuration
ALLOW_REDEEM_DOMAINS=*
# Default Lightning Address (used when no address is provided in redeem requests)
DEFAULT_LIGHTNING_ADDRESS=admin@your-domain.com
# Rate Limiting (requests per minute per IP)
RATE_LIMIT=30
# Logging
LOG_LEVEL=info
# CORS Configuration
ALLOWED_ORIGINS=*
- Start the server:
# Development (with auto-reload)
npm run dev
# Production
npm start
Configuration
Environment Variables
| Variable | Description | Default | Required |
|---|---|---|---|
PORT |
Server port | 3000 |
No |
NODE_ENV |
Environment (development / production) |
development |
No |
API_DOMAIN |
Domain for Swagger docs and CORS | localhost:3000 |
No |
ALLOW_REDEEM_DOMAINS |
Comma-separated allowed Lightning address domains (* for all) |
All allowed | No |
DEFAULT_LIGHTNING_ADDRESS |
Default Lightning address for redemptions | None | No |
RATE_LIMIT |
Requests per minute per IP | 100 |
No |
LOG_LEVEL |
Logging level | info |
No |
ALLOWED_ORIGINS |
CORS allowed origins (* for all) |
http://localhost:3000 |
No |
Domain Restrictions
Restrict redemptions to specific Lightning address domains:
ALLOW_REDEEM_DOMAINS=twentyone.tips,getalby.com,walletofsatoshi.com
If set to * or not set, all domains are allowed.
Default Lightning Address
Set a fallback address used when no address is provided in redemption requests:
DEFAULT_LIGHTNING_ADDRESS=admin@your-domain.com
If no default is configured, the lightningAddress field becomes required for all redemption requests.
Project Structure
cashu-redeem-api/
server.js # Express app, middleware, route mounting
swagger.config.js # OpenAPI/Swagger configuration and schemas
components/
cashu.js # Token parsing, spendability checks, melt operations
lightning.js # LNURLp resolution, invoice creation, address validation
redemption.js # Orchestrates the full redemption flow
routes/
cashu.js # POST /api/decode
redemption.js # POST /api/redeem
lightning.js # POST /api/validate-address
health.js # GET /api/health
Redemption Flow
- Token Parsing - Validate and decode the Cashu token structure
- Spendability Check - Verify proofs are unspent at the mint
- Melt Quote - Get exact fee from the mint before creating the invoice
- Address Resolution - Resolve Lightning address via LNURLp
- Invoice Generation - Create a Lightning invoice for the net amount (total minus fee)
- Token Melting - Melt the token at the mint to pay the invoice
Internal Status Codes
| Status | Description |
|---|---|
processing |
Redemption initiated |
parsing_token |
Validating and parsing the token |
checking_spendability |
Checking proofs at the mint |
getting_melt_quote |
Requesting fee quote from mint |
resolving_invoice |
Creating Lightning invoice via LNURLp |
melting_token |
Performing the melt operation |
paid |
Successfully completed |
failed |
Redemption failed (see error details) |
Security Features
- Input validation - All inputs are sanitized and validated
- Rate limiting - Configurable per-IP request limits
- Domain restrictions - Limit allowed Lightning address domains
- CORS protection - Configurable allowed origins
- Duplicate detection - Prevents resubmission of already-redeemed tokens
- Error handling - Comprehensive error messages without sensitive data leaks
Testing
The easiest way to test the API is using the interactive Swagger documentation:
- Start the server:
npm run dev - Visit
http://localhost:3000/docs - Click "Try it out" on any endpoint
- Fill in the request parameters and execute
Or use curl:
# Health check
curl http://localhost:3000/api/health
# Decode a token
curl -X POST http://localhost:3000/api/decode \
-H "Content-Type: application/json" \
-d '{"token": "cashuB..."}'
# Validate a Lightning address
curl -X POST http://localhost:3000/api/validate-address \
-H "Content-Type: application/json" \
-d '{"lightningAddress": "user@twentyone.tips"}'
# Redeem a token
curl -X POST http://localhost:3000/api/redeem \
-H "Content-Type: application/json" \
-d '{"token": "cashuB...", "lightningAddress": "user@twentyone.tips"}'
Contributing
- Fork the repository
- Create a feature branch
- Make your changes
- Submit a pull request
License
MIT License - see LICENSE file for details.