Specification

Wire format

The exact HTTP shape of a paid action — request, 402 challenge, payment, retry, and 200 with receipt.


402 challenge

The first request to an action endpoint MUST omit the Authorization header.

request 1
http
POST /api/actions/extract.structured HTTP/1.1
Host: example.com
Content-Type: application/json

{ "doc_id": "doc.foo" }

The publisher responds with HTTP 402 and the L402 challenge:

response 1
http
HTTP/1.1 402 Payment Required
Content-Type: application/json
WWW-Authenticate: L402 macaroon="<base64>", invoice="lnbc…"

{
  "error":         "payment_required",
  "action_id":     "extract.structured",
  "amount_msats":  1000,
  "invoice":       "lnbc10n1p…",
  "payment_hash":  "9bb6…",
  "token":         "<base64>",
  "expires_at":    1777160234
}

L402 token

The token is opaque to the agent. It is structured for stateless verification on the publisher side; the recommended encoding is:

text
base64url(JSON_BODY) "." base64url(HMAC_SHA256(secret, base64url(JSON_BODY)))

JSON_BODY = {
  "ph":  payment_hash,
  "sc":  action_id ":" sha256(canonical_input),
  "exp": unix_seconds,
  "n":   random_nonce
}

Equivalent macaroon-formatted tokens are accepted. The wire format only cares that the token is opaque, parseable in a known format, and that the publisher can verify it.

Retry with proof

request 2
http
POST /api/actions/extract.structured HTTP/1.1
Host: example.com
Content-Type: application/json
Authorization: L402 <token>:<preimage>

{ "doc_id": "doc.foo" }

The agent supplies the token returned in the 402, plus the Lightning preimage from its wallet. The publisher verifies:

  • ·Token signature (HMAC valid).
  • ·Token scope matches action_id + canonical input hash.
  • ·Token exp in the future.
  • ·Either: sha256(preimage) == payment_hash(cryptographic), OR the publisher's wallet reports the invoice as settled (operational fallback).
  • ·Token has not been previously consumed.

On success the publisher returns:

response 2
http
HTTP/1.1 200 OK
Content-Type: application/json

{
  "output":  { … },
  "receipt": { …signed receipt… }
}

Error responses

StatusCodeWhen
400invalid_inputInput fails the action's input_schema.
401invalid_or_expired_tokenToken signature invalid or expired.
401preimage_mismatchsha256(preimage) ≠ payment_hash.
401token_already_consumedToken was previously redeemed.
402payment_requiredInitial 402 (challenge issuance).
425payment_not_confirmedSettle status not yet visible at publisher's wallet.
503invoice_creation_failedPublisher's wallet temporarily unable to issue invoices.
425 vs 402
425 (Too Early) signals “your payment is in flight, retry the same request shortly.” The agent does NOT need to pay again; it just retries with the same Authorization header.

Timing & retries

  • ·Agents SHOULD wait at least 1 s between 425 retries; back off to 5 s by attempt 3.
  • ·Agents SHOULD give up after 30 s of 425 responses and surface the failure.
  • ·Publishers SHOULD set a token exp of 5–15 minutes after issuance.
  • ·Publishers SHOULD NOT consume a token until they have actually issued the receipt.
Next
Receipt format
Exact JSON shape, canonicalization rules, and signature verification.
agents402.org / 2026
Open protocol · v0.1