# Zaps The lightning-zap flow (NIP-57) and zap goals (NIP-75) are modeled by three kinds: `ZapRequest` (what a sender publishes to ask for a zap), `ZapReceipt` (what the recipient's LN service publishes as proof of payment), and `ZapGoal` (a fundraising target). All three are plain `EventReader` / `EventBuilder` subclasses — see [Readers & Builders](./readers-and-builders) for the base pattern. ## Zap request (kind 9734) The zap request you send to a recipient's LNURL callback. The comment is the event content; everything else is tags. ```typescript import {ZapRequest, ZapRequestBuilder} from "@welshman/domain" const req = await ZapRequest.fromEvent(event) req.amount() // millisats as an int, or undefined ("amount" tag) req.lnurl() // string | undefined req.recipient() // p-tag value req.eventId() // e-tag value (the zapped event) req.urls() // the "relays" tag, sliced past the key req.comment() // event.content const template = await new ZapRequestBuilder() .setAmount(21000) // millisats .setRecipient(recipientPubkey) .setLnurl(lnurl) .setEventId(zappedNoteId) .setUrls(["wss://relay.example"]) // ["relays", ...urls] .setComment("great post") .toTemplate() ``` `buildTags` always emits a `relays` tag (bare if you never called `setUrls`). ## Zap receipt (kind 9735) The receipt is generated by the recipient's lightning service, so it is effectively **read-only** in practice. `parse` decodes the embedded zap request out of the `description` tag into `plain`, which the getters read through. ```typescript import {ZapReceipt} from "@welshman/domain" const receipt = await ZapReceipt.fromEvent(event) receipt.bolt11() // the invoice receipt.invoiceAmount() // amount parsed from bolt11, or undefined on parse failure receipt.request() // the embedded zap-request event (TrustedEvent | undefined) receipt.sender() // request.pubkey receipt.recipient() // p-tag value receipt.eventId() // e-tag value receipt.comment() // the embedded request's content receipt.preimage() // string | undefined ``` The important method is `verify(zapper)`, which validates the receipt against a `Zapper` (the recipient's LNURL zapper info from `@welshman/util`). It checks that the request is present, that the invoice amount matches the requested amount, that the sender is not the zapper itself, and that the recipient / lnurl / nostr pubkey are consistent. It returns a boolean. ```typescript import type {Zapper} from "@welshman/util" const ok: boolean = receipt.verify(zapper) ``` A `ZapReceiptBuilder` exists (`setBolt11`, `setDescription`, `setRecipient`, `setEventId`, `setPreimage`) for completeness — e.g. tests or a service generating receipts — but you rarely construct these by hand. ## Zap goal (kind 9041) A fundraising target. The title is the content; the goal amount and relays are tags. ```typescript import {ZapGoal, ZapGoalBuilder} from "@welshman/domain" const goal = await ZapGoal.fromEvent(event) goal.title() // event.content (or "") goal.summary() // "summary" tag value goal.amount() // millisats as an int, default 0 goal.urls() // "relays" tag values const template = await new ZapGoalBuilder() .setTitle("Fund the relay") .setSummary("keeps the lights on") .setAmount(1000000) // millisats .setUrls(["wss://relay.example"]) // one ["relays", url] per url .toTemplate() ``` `validate()` requires a title (throws `ZapGoal requires a title`), and `buildTags` always emits an `amount` tag (defaulting to `["amount", "0"]`). ## See also - [Readers & Builders](./readers-and-builders) — the base `EventReader`/`EventBuilder` pattern.