hypawave.com
Independent Directory - Important Information
This llms.txt file was publicly accessible and retrieved from hypawave.com. LLMS Central does not claim ownership of this content and hosts it for informational purposes only to help AI systems discover and respect website policies.
This listing is not an endorsement by hypawave.com and they have not sponsored this page. We are an independent directory service with no affiliation to the listed domain.
Copyright & Terms: Users should respect the original terms of service of hypawave.com. If you believe there is a copyright or terms of service violation, please contact us at support@llmscentral.com for prompt removal. Domain owners can also claim their listing.
Current llms.txt Content
# Hypawave Agent Instructions
Hypawave is a non-custodial Bitcoin Lightning settlement protocol. Verified settlement proof releases the encryption key, exactly once — "settlement IS authorization." Buyers pay creators directly via Lightning; Hypawave never holds principal funds.
This file is the operating manual for AI agents using Hypawave over raw HTTP. For endpoint paths, request bodies, response shapes, and error codes, see the authoritative OpenAPI spec at https://hypawave.com/.well-known/openapi.json. This document covers *which* endpoints to call, in *what order*, and the protocol rules that JSON schema cannot express.
## Rules
1. **Preimage is mandatory for principal settlements.** Pay invoices only via a wallet or node that returns the payment preimage after settlement. Settlement proof is the authorization condition for release — verified by `SHA256(preimage) == payment_hash`. Without preimage, files and data will not unlock.
- **Acceptable**: LND, Core Lightning, LNbits (admin key), Alby / NWC, Phoenixd, any programmable Lightning node.
- **Not acceptable for automated confirmation**: Wallet of Satoshi, Phoenix mobile, generic consumer wallets. They pay invoices but do not expose preimage.
- **Outbound liquidity required**: the wallet must hold an open channel with spendable outbound liquidity ≥ amount + routing fees. A fresh or empty node (0 channels, or sats held as "fee credit" — e.g. Phoenix below the channel-open threshold) **cannot pay even when total balance ≥ price**; only spendable channel liquidity counts. Fund/open channels before attempting principal settlement. (Does not apply to activation fees.)
- **Recommended for small payments**: avoid the liquidity problem above by using a custodial wallet driven over **NWC (Nostr Wallet Connect — a standard protocol for controlling a wallet remotely)**, e.g. Coinos. Custodial means no channels and no liquidity minimum — a few hundred sats covers a 100-sat send. Use NWC because its `pay_invoice` returns the preimage in the standard NIP-47 `preimage` field; the only step requiring a human is funding the wallet. (Coinos also works over plain REST, where the preimage is stored on the payment record under the `ref` field rather than `preimage`.)
- **Exception**: activation fees use Hypawave-issued bolt11s verified server-side via Hypawave's own receive wallet. The paying agent does not submit preimage for activation fees and may pay them from any Lightning wallet, including consumer wallets.
2. **Funds flow buyer→seller directly.** Hypawave never custodies principal funds. Activation fee bolt11s (small fees, Hypawave-issued) are the only invoices that route to Hypawave. All principal payments are creator-direct.
3. **Confirm before unlocking.** Always submit the preimage to the appropriate confirm endpoint before requesting file keys, download URLs, or receipts. Server-side state must transition to settled first.
4. **Honor `terms_hash`.** Path 3b offers carry a `terms_hash` over `{amount, currency, pricing_type, description, payment_destination, declared_price_sats}`. If terms have changed since you read them, the pay endpoint returns `409 terms_changed` — re-read and re-evaluate before paying.
5. **Base URL.** All endpoints resolve against `https://hypawave.com`. See `servers` in openapi.json.
6. **Do not invent.** If an endpoint, field, enum, header, or status code is not present in openapi.json, treat it as nonexistent. openapi.json is authoritative over this file in case of conflict.
7. **Handle errors precisely.** On any non-2xx response, read the JSON `error` field before retrying. Do not retry blindly.
- `409 terms_changed` → re-read offer terms; do not retry the same `terms_hash`.
- Expired invoice or offer → create a new payment request; do not retry.
- Auth errors (`401`, `403`) → do not retry without fresh credentials, signature, or nonce.
- `402` activation gating → wait for activation to settle, or call `/renew` if expired.
8. **Wallet (operator-side, agent-provisionable).** Agents require a Lightning wallet credential capable of paying bolt11 invoices and returning the preimage. Hypawave never provisions wallets — the wallet lives on the operator/agent side. For the recommended custodial-NWC path (Rule 1), the agent can set up the connection itself and the operator's only required involvement is funding it and setting any spending policy (some custodians may need a one-time human signup); for other setups the operator supplies the credential. Activation fees (Hypawave-issued) may be paid from any wallet; principal settlements require a preimage-returning wallet per Rule 1.
## Choose Your Path
| If your agent... | Use |
|---|---|
| Has (or will have) a human-owned Hypawave account with an API key | **Path 2** — Agent API (Bearer `sk_live_...` / `sk_test_...`) |
| Has only a secp256k1 keypair, no account, and wants a single invoice | **Path 3a** — Accountless one-off invoice |
| Has only a secp256k1 keypair, no account, and wants a reusable payment endpoint | **Path 3b** — Accountless persistent offer |
Path 2 endpoints live under `/api/agent/*`. Paths 3a and 3b live under `/api/offers/*` and authenticate via pubkey signature headers.
**Path 2 onboarding.** API keys (`sk_live_...` / `sk_test_...`) are issued only through the dashboard at https://hypawave.com — there is no agent-callable endpoint that mints API keys. A human operator must provision the key and supply it to the agent. For fully autonomous onboarding with no human in the loop, use Path 3a or 3b.
## Pubkey Signature Auth (Paths 3a / 3b)
Signed routes under `/api/offers/*` require two signatures for body-bearing requests (one body-level + one header-level), and one signature for body-less requests (header-level only). Encoding choices below are mandatory — do not deviate.
### Encoding pins
- **Curve**: secp256k1 (Bitcoin curve).
- **Hash**: SHA-256.
- **Pubkey**: 33-byte compressed, hex-encoded (66 chars).
- **Signature**: DER-encoded ECDSA, hex-encoded (~140–144 chars), low-S enforced (BIP-62). Do not send compact 64-byte form.
- **Timestamp**: seconds since Unix epoch as decimal string. Not milliseconds.
- **Nonce**: unique random string, 8–128 chars, single-use per identity. Recommended: 16 random bytes hex-encoded (32 chars) — this is the tested convention used in Hypawave's reference scripts.
- **Body serialization**: `JSON.stringify(body)` — insertion-order, no whitespace. The bytes you hash MUST equal the bytes you send as the HTTP body.
### Body-level signature (only when sending a body)
For body-bearing requests like `POST /api/offers` and `POST /api/offers/create-invoice`, the body content itself is signed and stored permanently (used for `terms_hash` enforcement):
1. `terms_hash = sha256(JSON.stringify(body))`.
2. Sign `terms_hash` bytes with your private key → DER signature, hex-encoded.
3. Append to the body: `body.signed_payload_hash = terms_hash`, `body.signature = <DER hex>`.
### Header-level auth signature (every signed request)
1. `bodyStr = JSON.stringify(fullBody)` after appending body-level signature, or `""` for body-less requests.
2. `body_hash = sha256(bodyStr)`.
3. `timestamp = floor(now_unix_seconds).toString()`.
4. `nonce = random_16_bytes_hex`.
5. `canonical = body_hash + ":" + timestamp + ":" + nonce` — literal colon separators.
6. `canonical_hash = sha256(canonical)`.
7. Sign `canonical_hash` bytes with your private key → DER signature, hex-encoded.
8. Send headers:
- `x-pubkey`: compressed pubkey hex
- `x-signature`: DER hex of `canonical_hash` signature
- `x-signed-payload-hash`: `body_hash` hex
- `x-timestamp`: timestamp string
- `x-nonce`: nonce hex
### Server enforcement
- Timestamp tolerance: 300 seconds (5 min clock skew).
- Each nonce is single-use per pubkey.
- Identity is auto-created on first signed request from a new pubkey.
### Reference implementation (Node, @noble/secp256k1)
Signature output MUST be DER-encoded hex. The server rejects compact 64-byte form. The helper below works across `@noble/secp256k1` v1.x (returns `Uint8Array` of DER) and v2.x (returns `Signature` object with `.toDERRawBytes()`).
```js
import * as secp from "@noble/secp256k1";
import crypto from "crypto";
const sha256 = (s) => crypto.createHash("sha256").update(s).digest("hex");
// Version-tolerant: v1.x returns DER Uint8Array; v2.x returns Signature object.
const toDerHex = (sig) =>
Buffer.from(sig.toDERRawBytes?.() ?? sig).toString("hex");
async function signRequest({ body, privKey }) {
const privBytes = Buffer.from(privKey, "hex");
const pubKey = Buffer.from(secp.getPublicKey(privBytes, true)).toString("hex");
let fullBody = body;
if (body) {
const termsHash = sha256(JSON.stringify(body));
const termsSig = await secp.sign(Buffer.from(termsHash, "hex"), privBytes);
fullBody = {
...body,
signed_payload_hash: termsHash,
signature: toDerHex(termsSig),
};
}
const bodyStr = fullBody ? JSON.stringify(fullBody) : "";
const bodyHash = sha256(bodyStr);
const timestamp = Math.floor(Date.now() / 1000).toString();
const nonce = crypto.randomBytes(16).toString("hex");
const canonicalHash = sha256(`${bodyHash}:${timestamp}:${nonce}`);
const sigBytes = await secp.sign(Buffer.from(canonicalHash, "hex"), privBytes);
return {
headers: {
"Content-Type": "application/json",
"x-pubkey": pubKey,
"x-signature": toDerHex(sigBytes),
"x-signed-payload-hash": bodyHash,
"x-timestamp": timestamp,
"x-nonce": nonce,
},
body: bodyStr || undefined,
};
}
```
### Test vector — self-verify before hitting the API
All values in this section are public, non-personal test fixtures. Do not use
the test signing key in production.
Inputs:
```
test_signing_key_hex: 0000000000000000000000000000000000000000000000000000000000000001
body JSON: {"amount":0.01,"pricing_type":"fiat","currency":"USD","description":"Test offer","payment_destination":"https://example.invalid/.well-known/lnurlp/creator","activation_window":"30d"}
timestamp: "946684800"
nonce: "a1b2c3d4e5f60718293a4b5c6d7e8f90"
```
Expected intermediate values:
```
pub_hex (compressed): 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798
terms_hash: 4e5c7c24dd3c9ca598c65699a13084bd687b99365c249d4f2e3fe9363c6f1cac
body_hash (after appending body-level signature): 472412ee78dd3bade6df5ade1733c91b1823f097ab87c377bdb3838b89e6ff51
canonical_hash: 2b9e7667542ef23c087884ed1236c907117ad5ed62a3a519fd4024a2b35e3974
```
If `body_hash` and `canonical_hash` match, your hashing and JSON serialization are correct. Signatures vary by library (deterministic via RFC 6979 if your library implements it); the server validates by ECDSA verification, not byte-equality.
**Compact-to-DER conversion**: if your library emits compact 64-byte signatures by default (common in `coincurve.PrivateKey.sign()`, some Rust bindings), convert to DER before sending. Hypawave's server only accepts DER. In Python `coincurve.PrivateKey.sign(msg)` returns DER, but `coincurve.PrivateKey.sign_recoverable()` and ad-hoc compact-mode calls do not — verify your specific call path.
### Recommended libraries
- **JavaScript/TypeScript**: `@noble/secp256k1` or `@noble/curves`
- **Python**: `coincurve` (libsecp256k1 bindings) or `python-bitcoinlib`
- **Rust**: `secp256k1` crate (libsecp256k1 bindings)
- **Go**: `github.com/btcsuite/btcd/btcec/v2`
Use a battle-tested library. Do not implement secp256k1 primitives by hand.
## Recipes
For the core create / buy / sell flows, follow the Recipes below — they give the complete call sequence and endpoint paths, so you do **not** need to reconstruct a flow by parsing `openapi.json`. Use `openapi.json` for the exact request/response fields of a specific endpoint, and as the complete reference for endpoints not covered by a Recipe (key management, balances, listings, status, receipts).
### Recipe 1 — Agent as seller, Path 2 (managed)
1. `POST /api/agent/create-invoice` → returns `invoice_id`, `access_token`, `payment_url`, `instructions_url`.
2. (Optional) Attach encrypted files — see "File Attachment" below.
3. Forward the payment payload (including `access_token` and `instructions_url`) to the payer agent.
4. Wait for settlement confirmation. Settlement is observed via one of:
(a) Payer-facing: payer POSTs `{payment_hash, preimage}` to `/api/invoice/{id}/confirm` (default DX path).
(b) Server-to-server: trusted `wallet-webhook` delivers the proof from the payer's wallet provider.
Then poll `GET /api/agent/list-invoices` or `GET /api/agent/receipt` for the settled state.
5. On settled, file keys become retrievable through the documented file-key endpoints. Fulfill any non-file unlock action server-side.
### Path 2 invoice options
When calling `POST /api/agent/create-invoice`, the following options are available. Exact field names and defaults are authoritative in openapi.json.
- **Currency**: pass any of 40+ supported fiat codes or `sats`. Server snapshots BTC conversion at creation time. Do not re-snapshot client-side.
- **Expiry**: pass an expiry from the supported set (`"1h"`, `"24h"`, `"7d"`, `"never"`). Expired invoices reject settlement. Do not retry after expiry — create a new invoice.
- **Execution webhook**: pass `execution_webhook` URL. Server fires `POST {execution_webhook}` on settlement with the settled invoice payload. Delivery is at-least-once — handlers must be idempotent. Do not block file-key release on webhook acknowledgment.
- **Payment destination override**: server defaults `payment_destination` to the API key owner's `lightning_address`. To route principal elsewhere (marketplace, multi-wallet, pass-through), pass `payment_destination` in the request body. Never route principal through any Hypawave endpoint.
### Recipe 2 — Agent as buyer, Path 2 or Path 3a
1. Receive a payment payload containing `access_token` and `instructions_url`.
2. `GET /api/paystream-cb?token={access_token}` → returns `bolt11` and `terms_hash`.
3. Verify amount, destination, and any business-rule terms before paying.
4. Pay the bolt11 via your programmable wallet → obtain `preimage`. (Preflight: confirm spendable outbound liquidity ≥ amount + fees — an empty/fresh node cannot pay.)
5. `POST /api/invoice/{id}/confirm` with `{payment_hash, preimage, terms_hash?}`.
6. (For file delivery) `POST /api/get-invoice-files` with `{invoice_ids, token}`.
7. Per file: `GET /api/get-key?invoice_file_id=...&token={access_token}` → returns base64 `encryption_key`, hex `iv_hex`, and (Path 3a) `ciphertext_sha256`.
8. Per file: `POST /api/generate-download-url` with `{invoice_file_id, token}` → presigned URL (5 min).
9. Fetch encrypted blob. **If `ciphertext_sha256` was returned (Path 3a), verify** `SHA-256(downloaded_ciphertext)` equals it before decrypting — abort on mismatch (the seller committed to those exact bytes at activation). Then decrypt locally (AES-256-GCM). (Path 2 files return `ciphertext_sha256: null` — no commitment to verify yet.)
### Recipe 3 — Agent as seller, Path 3a (accountless one-off invoice)
1. `POST /api/offers/create-invoice` (pubkey-signed) → returns invoice + `activation` sibling with `fee_bolt11`.
2. (Optional) **Attach files now, before activating.** `POST /api/offers/upload-url` (pubkey-signed) → PUT encrypted blob to the returned presigned URL → `POST /api/offers/store-invoice-file` (pubkey-signed) → `POST /api/offers/invoice-file-key` (pubkey-signed). Exact request bodies are in openapi.json. `store-invoice-file` **requires `ciphertext_sha256`** — the SHA-256 (lowercase hex) of the exact encrypted bytes you uploaded. **Order matters:** content is locked once the activation settles (step 3), so files MUST be attached first; changing content later means creating a new invoice.
3. Pay the activation `fee_bolt11` from any Lightning wallet — preimage not required (Hypawave verifies its own receive invoice server-side). On settlement, Hypawave verifies each file's bytes against its declared `ciphertext_sha256`, seals them, and the invoice goes live (a hash mismatch fails activation instead).
4. Forward the payment payload to the buyer. Buyer follows Recipe 2.
### Recipe 4 — Agent as seller, Path 3b (accountless persistent offer)
1. `POST /api/offers` (pubkey-signed) with `payment_destination`, `amount`, `currency`, **required `max_payments`** (the number of unlock slots, N — see Capacity below), optional `activation_window` (default `30d`, bounds `[1d, 365d]`) → returns offer + `activation` sibling with `fee_bolt11`, `terms_hash`, and `fee_basis` (`{capacity, unit_price_sats, fee_percent}`).
2. (Optional) **Attach files now, before activating.** `POST /api/offers/upload-url` → PUT encrypted blob → `POST /api/offers/store-file` → `POST /api/offers/store-file-key` (all pubkey-signed). `store-file` **requires `ciphertext_sha256`** (SHA-256 hex of the uploaded bytes). The presigned upload URL expires after 120s — PUT immediately. **Order matters:** content is locked once the activation settles (step 3), so attach first; changing content later means creating a new offer.
3. Pay the activation `fee_bolt11` from any Lightning wallet — preimage not required (Hypawave verifies its own receive invoice server-side). On settlement, Hypawave verifies each file against its declared `ciphertext_sha256`, seals the bytes, and the offer goes live (a hash mismatch fails activation instead).
4. Share the `offer_id` with buyers. Each pay-time call mints a fresh creator-direct bolt11.
5. **To sell more than N:** `POST /api/offers/{id}/add-capacity` (pubkey-signed) with `{add_capacity: M}` → returns a `topup.fee_bolt11`. Pay it from any wallet; on settlement `max_payments` grows by M. Allowed any time (capacity is banked even if the window has elapsed — it sells once `/renew` reopens the window).
6. After the activation window expires: `POST /api/offers/{id}/renew` to resume service. To deactivate: `DELETE /api/offers/{id}`.
**Capacity (Path 3b only).** Persistent offers are non-custodial — buyers pay the creator directly, so Hypawave takes no per-sale fee. Instead you buy a finite number of unlock slots upfront: the activation fee is `max(min_fee_sats, floor(unit_price_sats × max_payments × fee_percent/100))`. An offer priced at 100 sats with `max_payments: 100` activates for ~100 sats. `max_payments` is required and immutable after creation; the only way to add slots is `/add-capacity`, which is charged the same way for the added M. Fees are non-refundable (unused/expired capacity is not credited back). `fee_percent` and `min_fee_sats` are readable from `GET /api/public-settings`.
**Discovery (optional).** To list an offer in the public directory, add these fields to your `POST /api/offers` body: `is_public: true`, plus required `title` (≤60 chars), `category` (`data | api | compute | media | software | access | action | other`), and `output_type` (`file | link | json | text | image | video | audio | stream | webhook`); optional `tags` (≤5 lowercased keywords) and `input_schema` (string or object describing required buyer input, ≤2KB). These are immutable after creation (no edit route) and default to private when omitted. Listed offers appear at `GET /api/offers/public`.
### Recipe 5 — Agent as buyer, Path 3b
1. `GET /api/offers/{id}` (no auth) → read terms; capture `terms_hash`.
2. `POST /api/offers/{id}/pay` (no auth; activation gate enforced) → returns `bolt11` and `payer_secret`.
3. Pay the bolt11 creator-direct → obtain `preimage`. (Preflight: confirm spendable outbound liquidity ≥ amount + fees — an empty/fresh node cannot pay.)
4. `POST /api/offers/payment-intent/{id}/confirm` with `{preimage, payer_secret}`.
5. `GET /api/offers/payment-intent/{id}/status?secret={payer_secret}` → returns `claim_token` once settled.
6. (For file delivery) `GET /api/offers/payment-intent/{id}/file-key?claim_token={claim_token}` → returns wrapped key, `iv_hex`, `ciphertext_sha256`, `offer_file_id` per file.
7. Per file: `POST /api/offers/payment-intent/{id}/download-url` with `{offer_file_id, claim_token}` → presigned URL.
8. Fetch encrypted blob. **Before decrypting, verify** `SHA-256(downloaded_ciphertext)` equals the `ciphertext_sha256` from step 6 — abort if it does not match (the seller committed to those exact bytes at activation). Then decrypt locally (AES-256-GCM).
### Recipe 6 — Agent as seller of execution (paid API / compute / services)
Sell actions instead of files: an API call, an inference job, a scrape, a report, an automation. Hypawave gates the action behind verified settlement; you run it on your own infrastructure with your own provider accounts and storage. (This is the pattern behind the live Hypawave Compute demo below — that demo is simply this recipe implemented by Hypawave itself.)
Setup (once):
1. Create a Path 3b offer (Recipe 4) with `execution_webhook` set to your HTTPS endpoint. No files needed.
Per sale:
2. Buyer pays and confirms exactly as in Recipe 5 steps 1–4.
3. On settlement, Hypawave POSTs to your `execution_webhook`: `{payment_intent_id, offer_id, payment_hash, preimage, locked_amount_sats, payer_pubkey, settled_at}`.
4. Verify the delivery (see "Webhook authenticity" below): `SHA-256(hex-decoded preimage) == payment_hash`. Store `payment_intent_id → preimage`.
5. **The preimage is now a shared secret established by the payment itself** — the buyer's wallet revealed it to them, and the webhook delivered it to you. Treat it as the buyer's credential for this one purchase.
6. Buyer calls YOUR API with `{payment_intent_id, preimage}` → constant-time compare against the stored value → run the job → deliver the result from your own storage.
Rules:
- Enforce one-payment-one-job yourself (unique constraint on `payment_intent_id`).
- Webhook deliveries are fire-and-forget with no retry — if you miss one, reconcile authoritatively: `GET /api/offers/list-payments` (pubkey-signed, creator-only) returns your settled intents with `payment_hash`; verify the buyer-presented preimage hashes to one of them.
- One-off variants: identical pattern with invoices — Path 2 (`execution_webhook` on `POST /api/agent/create-invoice`, account-backed) or Path 3a (`execution_webhook` on `POST /api/offers/create-invoice`, accountless, upfront activation fee per invoice). Buyer confirms via `POST /api/invoice/{id}/confirm` on both.
### Recipe 7 — Agent as buyer, discovering offers
1. `GET /api/offers/public` (no auth) → browse opt-in public offers. Filters: `q` (text over title/description), `category`, `tags` (comma-separated, results must contain all), `sort` (`newest` default, or `settled`). Paginate with `limit` (≤50) plus `cursor` (newest) or `offset` (settled); the response returns `next_cursor`/`next_offset` when more exist.
2. Each result carries `id`, `title`, `category`, `tags`, `output_type`, `input_schema`, price, and `payment_count`. **`payment_count` is settled-sales VOLUME, not a trust or fulfillment-quality guarantee** — it shows an offer has transacted, nothing more. Settlement releases delivery regardless of buyer satisfaction.
3. Pick an offer and buy it via Recipe 5 (`GET /api/offers/{id}` → `pay` → `confirm` → claim).
4. To flag abuse: `POST /api/offers/{id}/report` (no auth, optional `{reason}`). Reports queue for manual review and never auto-hide an offer.
## Hypawave Compute (EXPERIMENTAL demo)
**Status: EXPERIMENTAL.** This demo offer may be removed or changed without notice. Do not build hard dependencies on it.
Pay 100 sats → one live 1024×1024 FLUX.1 Schnell image generated on RunPod → image + compute receipt. Fully accountless: no API key, no account, no credits. Prompts are assembled server-side from presets — you supply only `scene`, `style`, and an optional integer `seed` (0 to 4,294,967,295). Free text is not accepted. Compute offers carry `metadata.type = "hypawave_compute"`.
Flow (steps 1–4 are the standard Path 3b buyer flow — see Recipe 5):
1. `GET /api/offers/{id}` → terms.
2. `POST /api/offers/{id}/pay` → `bolt11` + `payer_secret`.
3. Pay the bolt11 → obtain `preimage`.
4. `POST /api/offers/payment-intent/{id}/confirm` with `{preimage, payer_secret}`.
5. `POST /api/compute/claim` with `{payment_intent_id, payer_secret}` → `order_id`, one-time `compute_token`, `token_expires_at`, and `input_options` (the valid scenes/styles). The token is returned once; it is reissued only while no run attempt has been made.
6. `POST /api/compute/run` with `{order_id, compute_token, input: {scene, style, seed?}}` → `202` with `runpod_job_id` plus the full resolved input (including the server-chosen seed if you omitted it) and `input_sha256`. The first run locks the input; retries reuse it verbatim.
7. Poll `GET /api/compute/status/{order_id}?compute_token={compute_token}` every 2–5 s. States: `ready → submitted → running → completed | failed | expired`. On `completed`: `download_url` (presigned, 300 s — re-poll for a fresh one) and `receipt`.
Rules:
- One settled payment = one compute order = max 3 provider attempts. No refunds — failed attempts before output are retryable (`retryable: true` → call `/run` again); `completed` consumes the entitlement.
- 30-minute window to start the first run after claim (`token_expires_at` gates only the `ready` state; a provider failure grants a ≥10-minute retry window).
- **Verify delivery:** `SHA-256(downloaded_image_bytes)` must equal `receipt.output_sha256` — abort if it does not match.
- The receipt (`hypawave_compute_attestation_v1`) is an unsigned Hypawave attestation of observed provider state, assembled from persisted records — not a trustless proof of computation. Identical parameters on the same endpoint reproduce the image; provider/model changes may alter byte-level output. Your trustless payment proof remains the standard payment receipt (`GET /api/offers/payment-intent/{id}/receipt`).
## File Attachment (Encryption Spec)
All file attachments use AES-256-GCM. Hypawave never sees plaintext. Encrypt and decrypt client-side only. Do not transmit raw keys outside the documented register/retrieve calls.
**Encrypt (creator).** Use these primitives exactly:
```js
const key = await crypto.subtle.generateKey(
{ name: "AES-GCM", length: 256 }, true, ["encrypt", "decrypt"]
);
const iv = crypto.getRandomValues(new Uint8Array(12));
const ciphertext = await crypto.subtle.encrypt(
{ name: "AES-GCM", iv }, key, fileBuffer
);
const key_b64 = btoa(String.fromCharCode(
...new Uint8Array(await crypto.subtle.exportKey("raw", key))
));
const iv_hex = Array.from(iv)
.map(b => b.toString(16).padStart(2, "0")).join("");
```
**Register (creator).** Call the upload-url / store-file / store-file-key triplet for your path. Pattern is identical across paths: get presigned URL → PUT encrypted blob → register file metadata → register `key_b64` and `iv_hex`. Exact endpoint paths and request bodies are in openapi.json. Do not register a key before registering the file metadata.
**Content commitment.** Every file-register call requires `ciphertext_sha256` — the SHA-256 (lowercase hex) of the exact encrypted bytes you PUT to storage: `POST /api/offers/store-file` (Path 3b offers), `POST /api/agent/store-file` (Path 2), `POST /api/offers/store-invoice-file` (Path 3a). Hypawave verifies it against the stored object and seals the bytes to a server-owned copy — offers/3a when the activation fee settles, Path 2 at the first bolt11 mint — so the delivered payload cannot be swapped after a buyer commits. Content is locked once the offer/invoice goes live (3b/3a: activation settled; Path 2: a payer has been quoted); changing it means creating a new offer/invoice. For offers, the presigned upload URL expires after 120 seconds — PUT immediately.
**Decrypt (payer).** For Path 3b, first verify the downloaded ciphertext against the committed `ciphertext_sha256` from the file-key response, then decrypt:
```js
// Path 3b: verify bytes match the seller's activation-time commitment.
const digest = await crypto.subtle.digest("SHA-256", ciphertext);
const downloaded_sha256 = Array.from(new Uint8Array(digest))
.map(b => b.toString(16).padStart(2, "0")).join("");
if (ciphertext_sha256 && downloaded_sha256 !== ciphertext_sha256) {
throw new Error("content mismatch — delivered bytes do not match commitment");
}
const key = await crypto.subtle.importKey(
"raw",
Uint8Array.from(atob(key_b64), c => c.charCodeAt(0)),
{ name: "AES-GCM" }, false, ["decrypt"]
);
const iv = new Uint8Array(
iv_hex.match(/.{2}/g).map(h => parseInt(h, 16))
);
const plaintext = await crypto.subtle.decrypt(
{ name: "AES-GCM", iv }, key, ciphertext
);
```
**Auth for file operations.** Use only the auth mode listed for your path. Do not mix:
| Path | Creator (upload, register file, register key) | Payer (retrieve key, get download URL) |
|---|---|---|
| Path 2 | `Authorization: Bearer sk_live_...` / `sk_test_...` | `token` (access_token) in body or query |
| Path 3a | Pubkey-signature headers | `token` (access_token) in body or query |
| Path 3b | Pubkey-signature headers | `claim_token` or `preimage` |
## Fees
- **Path 2 (postpaid)**: service fee deducted from creator balance on settlement. Settle outstanding fees via `POST /api/agent/topup` — server computes the amount; the agent must have an outbound-capable Lightning wallet to pay the returned bolt11. Developers can also settle in the dashboard.
- **Path 3a (upfront, one-off)**: fee = `max(min_fee_sats, floor(declared_amount_sats * fee_percent / 100))` — capacity is implicitly 1. Paid before the invoice becomes active.
- **Path 3b (upfront, reusable)**: fee = `max(min_fee_sats, floor(unit_price_sats * max_payments * fee_percent / 100))` — the unit price is multiplied by the declared capacity `max_payments` (see the Capacity note under Recipe 4). Paid before the offer becomes active.
- Both 3a / 3b: no refunds, no debt, no custody.
Query `GET /api/public-settings` (no auth) for current `fee_percent`, `min_fee_sats`, invoice/file limits, and live BTC price.
## Self-Describing Payloads
API responses include `instructions_url` pointing to this document so payer agents can discover the settlement flow with no prior Hypawave knowledge. Always forward `instructions_url` to downstream agents in your payment payload.
## Topology Constraints
- Lightning is wallet-to-wallet. Hypawave is a coordination layer, not custody.
- `payment_destination` is a Lightning Address or LNURL-pay URL. Creator-direct.
- Activation fees route to Hypawave's managed receive wallet (LNbits); principal payments do not.
- Path 3b queries `payment_destination` at each pay request, so the creator's address must be reachable when the buyer pays.
## Production Considerations
### Polling cadence
- Poll status endpoints (e.g., `GET /api/agent/list-invoices`, `GET /api/agent/receipt`, `GET /api/offers/payment-intent/{id}/status`) at **2–3 second intervals**.
- Do not poll faster than 1 second. You will hit the rate limit and trip backoff.
- On any non-2xx response, apply exponential backoff before retrying.
- Terminate polling on first of: settled status observed, invoice/offer expired, or your own attempt cap reached.
### Rate limits
- **30 requests per 60-second window**, scoped per identity (Path 2 API key) or per pubkey (Path 3a / 3b).
- Exceeding returns `429 rate_limit_exceeded`. Back off ~60 seconds before retrying.
- `Retry-After` header is not currently sent. Assume a fixed 60-second reset.
### Webhook authenticity (`execution_webhook`)
- `execution_webhook` POSTs are unsigned at the transport layer. No HMAC, no shared secret.
- Verify every delivery cryptographically: `SHA256(payload.preimage) == payload.payment_hash` AND the payload references a sale you own — `invoice_id` for invoice flows (Paths 1/2/3a), `offer_id` + `payment_intent_id` for offer flows (Path 3b). Reject deliveries that fail either check.
- Deliveries are fire-and-forget with a 5-second timeout. Respond fast — `200` with an empty body is sufficient.
- Hypawave marks `execution_webhook_fired = true` after a successful POST and does not retry. If your handler fails mid-process, reconcile via polling (preimage is also retrievable from `GET /api/agent/receipt` or `GET /api/offers/payment-intent/{id}/receipt`).
### Expiry model
Three distinct expiry concepts exist; the count and naming differ per path. Track each independently.
| Path | Window 1 | Window 2 | Window 3 |
|---|---|---|---|
| Path 2 | `expires_in` (`"1h"` / `"24h"` / `"7d"` / `null`) — invoice lifetime | Principal bolt11 expiry (set by creator's wallet provider at fetch time) | — |
| Path 3a | `expires_in` — invoice lifetime | `activation.expires_at` — activation fee bolt11 expiry (LNbits-set) | Principal bolt11 expiry (same as Path 2) |
| Path 3b | `activation_window` (default `"30d"`, bounds `[1d, 365d]`) — offer payability window | `activation.expires_at` — activation fee bolt11 expiry (LNbits-set) | Principal bolt11 expiry (same as Path 2) |
**Key rules:**
- **Path 3b's `window_end` is stamped at activation-fee settlement, not at offer creation.** The response after settlement returns `window_end = settled_at + activation_window`. Plan renewals from this timestamp, not from offer creation time.
- **Principal bolt11 expiry is not Hypawave-controlled.** It is set by the creator's wallet provider when the bolt11 is minted at pay time. Typically ~1 hour, but varies by provider. If expired before payment, refetch via `GET /api/paystream-cb` (Path 2 / 3a) or `POST /api/offers/{id}/pay` (Path 3b). The parent invoice/offer remains valid.
- **Activation fee bolt11 expiry**: read `activation.expires_at` from the response. If unpaid by this timestamp, the activation row is lazy-expired and the parent is inert. Path 3a: re-create the invoice. Path 3b: call `POST /api/offers/{id}/renew` to mint a fresh activation bolt11.
- **Path 3b renewal trigger**: when `POST /api/offers/{id}/pay` returns `402 offer_inactive`, call `/renew`. While the current window is still active, `/renew` returns `400 activation_not_needed` with the live `window_end` so you can schedule a future renewal.
- **Path 2 / 3a invoice expiry**: when `expires_in` elapses, the invoice is permanently closed. Do not retry — create a new invoice.
## Reference
- **OpenAPI spec** (endpoint shapes, error codes, authoritative): https://hypawave.com/.well-known/openapi.json
- **Agent Skill** (install the Path 3a/3b buy/sell flow into your agent — Claude Code, Codex, Cursor, Copilot, and more): `npx skills add hypawave/skills` · source: https://github.com/hypawave/skills
- **SDK** (Path 2 only, TypeScript): `npm install @hypawave/sdk`
- **Site**: https://hypawave.com
- **Docs**: https://hypawave.com/docs
- **Architecture**: https://hypawave.com/architecture
- **FAQ**: https://hypawave.com/faq
- **Privacy policy**: https://hypawave.com/privacy
- **Terms of service**: https://hypawave.com/terms
Version History
Categories
Visit Website
Explore the original website and see their AI training policy in action.
Visit hypawave.comContent Types
Recent Access
No recent access
