Menu
Nostr Intermediate 6 min read

Lightning Zaps

Send and receive Lightning payments through Nostr with NIP-57 Zaps. Social micropayments for agents.

zaps lightning micropayments NIP-57 LNURL tips

Lightning Zaps

Zaps are Lightning payments sent through Nostr. They connect social interaction with value transfer—you can tip a note, support a creator, or receive payments for your agent’s services.

How Zaps Work

1. Alice wants to zap Bob's note
2. Alice's client fetches Bob's Lightning address (LNURL)
3. Alice creates a zap request (kind 9734)
4. Alice sends request to Bob's LNURL provider
5. Provider returns a Lightning invoice
6. Alice pays the invoice
7. Provider publishes a zap receipt (kind 9735)
8. Bob (and everyone) sees the zap

Zap Flow Diagram

┌─────────┐     ┌─────────────┐     ┌──────────────┐
│  Alice  │     │ LNURL Server│     │   Relays     │
└────┬────┘     └──────┬──────┘     └──────┬───────┘
     │                 │                   │
     │─── Zap Request ─►│                  │
     │   (kind 9734)    │                  │
     │                  │                  │
     │◄── Invoice ──────│                  │
     │                  │                  │
     │─── Pay Invoice ──►│                 │
     │                  │                  │
     │                  │─── Zap Receipt ──►│
     │                  │    (kind 9735)   │
     │                  │                  │

Setting Up Zap Receiving

1. Configure Lightning Address

Add a Lightning address to your profile:

{
  "kind": 0,
  "content": "{\"name\":\"Agent\",\"lud16\":\"agent@getalby.com\",\"lud06\":\"LNURL...\"}"
}
FieldFormatExample
lud16Email-likeuser@provider.com
lud06LNURL stringlnurl1dp68...

Either works; lud16 is more readable.

2. Use a Zap-Compatible Provider

ProviderSetupNotes
Albygetalby.comEasy, custodial option
Wallet of Satoshiwalletofsatoshi.comMobile-friendly
LNbitsSelf-hostedFull control
Zeuszeusln.appSelf-custodial

3. Enable Zap Receipts

Your LNURL provider must support NIP-57 to publish zap receipts.

Sending Zaps

Step 1: Get Recipient’s LNURL

import httpx
import json

async def get_lnurl_from_profile(pubkey: str, relays: list) -> str | None:
    """Fetch user's Lightning address from their profile."""
    # Query for kind 0 (metadata)
    profile = await query_profile(pubkey, relays)
    if not profile:
        return None

    metadata = json.loads(profile["content"])

    # Check for lud16 (Lightning Address) or lud06 (LNURL)
    lud16 = metadata.get("lud16")
    lud06 = metadata.get("lud06")

    if lud16:
        # Convert Lightning Address to LNURL endpoint
        name, domain = lud16.split("@")
        return f"https://{domain}/.well-known/lnurlp/{name}"
    elif lud06:
        # Decode LNURL
        return decode_lnurl(lud06)

    return None

Step 2: Create Zap Request

def create_zap_request(
    sender_pubkey: str,
    recipient_pubkey: str,
    amount_msats: int,
    relays: list,
    event_id: str = None,  # Optional: zap specific note
    content: str = ""      # Optional: zap comment
) -> dict:
    """Create a NIP-57 zap request (kind 9734)."""
    tags = [
        ["relays", *relays],
        ["amount", str(amount_msats)],
        ["p", recipient_pubkey]
    ]

    # If zapping a specific note
    if event_id:
        tags.append(["e", event_id])

    return {
        "kind": 9734,
        "pubkey": sender_pubkey,
        "created_at": int(time.time()),
        "tags": tags,
        "content": content
    }

Step 3: Request Invoice

async def request_zap_invoice(
    lnurl_endpoint: str,
    zap_request: dict,
    amount_msats: int
) -> str:
    """Request a Lightning invoice for a zap."""
    # Sign the zap request
    signed_request = sign_event(zap_request, private_key)
    request_json = json.dumps(signed_request)

    # URL-encode the request
    encoded_request = urllib.parse.quote(request_json)

    # Call LNURL callback
    async with httpx.AsyncClient() as client:
        # First, get the callback URL
        lnurl_response = await client.get(lnurl_endpoint)
        lnurl_data = lnurl_response.json()

        callback = lnurl_data["callback"]

        # Request invoice with zap request
        params = {
            "amount": amount_msats,
            "nostr": encoded_request
        }
        invoice_response = await client.get(callback, params=params)
        invoice_data = invoice_response.json()

        return invoice_data["pr"]  # BOLT11 invoice

Step 4: Pay Invoice

async def pay_zap(invoice: str, lnbits_url: str, admin_key: str):
    """Pay the zap invoice."""
    async with httpx.AsyncClient() as client:
        response = await client.post(
            f"{lnbits_url}/api/v1/payments",
            headers={"X-Api-Key": admin_key},
            json={"out": True, "bolt11": invoice}
        )
        return response.json()

Complete Zap Flow

async def send_zap(
    recipient_pubkey: str,
    amount_sats: int,
    event_id: str = None,
    comment: str = ""
):
    """Send a complete zap."""
    # 1. Get recipient's LNURL
    lnurl = await get_lnurl_from_profile(recipient_pubkey, RELAYS)
    if not lnurl:
        raise ValueError("Recipient has no Lightning address")

    # 2. Create zap request
    amount_msats = amount_sats * 1000
    zap_request = create_zap_request(
        sender_pubkey=MY_PUBKEY,
        recipient_pubkey=recipient_pubkey,
        amount_msats=amount_msats,
        relays=RELAYS,
        event_id=event_id,
        content=comment
    )

    # 3. Get invoice
    invoice = await request_zap_invoice(lnurl, zap_request, amount_msats)

    # 4. Pay invoice
    payment = await pay_zap(invoice, LNBITS_URL, ADMIN_KEY)

    return payment

Receiving Zaps

Monitor Zap Receipts

async def monitor_zaps(my_pubkey: str):
    """Subscribe to incoming zaps."""
    filter = {
        "kinds": [9735],  # Zap receipts
        "#p": [my_pubkey]
    }

    async for event in subscribe(RELAYS, filter):
        zap = parse_zap_receipt(event)
        print(f"Received {zap['amount']} sats from {zap['sender']}")

Parse Zap Receipt

def parse_zap_receipt(event: dict) -> dict:
    """Parse a zap receipt (kind 9735)."""
    # Extract tags
    tags = {t[0]: t[1:] for t in event["tags"]}

    # Get the embedded zap request
    description_tag = tags.get("description", [""])[0]
    zap_request = json.loads(description_tag) if description_tag else None

    # Get bolt11 to extract amount
    bolt11 = tags.get("bolt11", [""])[0]
    amount_msats = decode_bolt11_amount(bolt11) if bolt11 else 0

    return {
        "receipt_id": event["id"],
        "amount_msats": amount_msats,
        "amount_sats": amount_msats // 1000,
        "sender": zap_request["pubkey"] if zap_request else None,
        "recipient": tags.get("p", [None])[0],
        "zapped_event": tags.get("e", [None])[0],
        "comment": zap_request["content"] if zap_request else "",
        "timestamp": event["created_at"]
    }

Zap Splits

NIP-57 supports splitting zaps among multiple recipients:

{
  "kind": 9734,
  "tags": [
    ["p", "pubkey1", "wss://relay1", "0.7"],
    ["p", "pubkey2", "wss://relay2", "0.3"]
  ]
}

The LNURL provider handles splitting the payment.

Agent Use Cases

Accept Zaps for Services

# Monitor for zaps with specific content
async def handle_service_request(zap):
    if zap["comment"].startswith("/query"):
        query = zap["comment"][7:]
        result = await process_query(query)
        await post_reply(zap["zapped_event"], result)

Zap-Gated Content

# Check if user has zapped before revealing content
async def has_zapped(user_pubkey: str, my_pubkey: str, min_sats: int):
    zaps = await query_zaps_from(user_pubkey, my_pubkey)
    total = sum(z["amount_sats"] for z in zaps)
    return total >= min_sats

Automated Tipping

# Auto-zap quality content
async def auto_zap_if_quality(event):
    score = await analyze_content(event["content"])
    if score > 0.8:
        await send_zap(event["pubkey"], amount_sats=21)

Machine-Readable Summary

{
  "topic": "nostr-zaps",
  "audience": "ai-agents",
  "prerequisites": ["lightning-basics", "nostr-events", "lnurl"],
  "key_concepts": [
    "zap-request-kind-9734",
    "zap-receipt-kind-9735",
    "lnurl-integration",
    "zap-splits"
  ],
  "code_examples": ["python"],
  "event_kinds": {
    "zap_request": 9734,
    "zap_receipt": 9735
  },
  "related": [
    "/learn/nostr/specs/nip-57",
    "/learn/lightning/api-lnurl",
    "/learn/lightning/invoices"
  ]
}