Skip to main content
Glama

pay_l402_challenge

Pay Lightning invoices to obtain L402 or MPP authorization tokens for API access and transactions.

Instructions

Manually pay an L402 or MPP invoice and receive the authorization token. Use this if you need to handle the L402/MPP flow yourself. Omit macaroon for MPP (Machine Payments Protocol) mode.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
invoiceYesBOLT11 Lightning invoice string
macaroonNoBase64-encoded macaroon from the L402 challenge. Omit for MPP mode (preimage-only authentication).
max_satsNoMaximum satoshis allowed for this payment

Implementation Reference

  • The core implementation of the pay_l402_challenge tool, responsible for paying a Lightning invoice and returning an L402 or MPP token.
    async def pay_l402_challenge(
        invoice: str,
        macaroon: str | None = None,
        max_sats: int = 1000,
        wallet: "NWCWallet | None" = None,
        budget_manager: "BudgetManager | None" = None,
    ) -> str:
        """
        Manually pay an L402 or MPP invoice and receive the authorization token.
    
        This is useful when you want to handle the L402/MPP flow yourself rather than
        using access_l402_resource which does it automatically.
    
        When macaroon is provided, uses L402 protocol.
        When macaroon is omitted, uses MPP (Machine Payments Protocol) — preimage only.
    
        Args:
            invoice: BOLT11 Lightning invoice string
            macaroon: Base64-encoded macaroon from the L402 challenge (optional; omit for MPP mode)
            max_sats: Maximum satoshis allowed for this payment
            wallet: NWC wallet instance
            budget_manager: Budget manager for tracking spending
    
        Returns:
            JSON with L402/MPP token or error message
        """
        if not wallet:
            return json.dumps(
                {"success": False, "error": "Wallet not initialized. Check NWC connection."}
            )
    
        if not invoice:
            return json.dumps({"success": False, "error": "Invoice is required"})
    
        # Normalize invoice: strip whitespace/newlines that could cause decode or payment failures
        invoice = invoice.strip()
    
        # Normalize macaroon: strip whitespace and treat empty/whitespace-only as None
        if macaroon is not None:
            macaroon = macaroon.strip()
            if not macaroon:
                macaroon = None
        is_mpp = macaroon is None
    
        try:
            # Parse invoice to get amount
            decoded = decode_bolt11(invoice)
            amount_msat = None
            amount_sats = None
    
            if hasattr(decoded, "amount_msat") and decoded.amount_msat:
                amount_msat = decoded.amount_msat
                amount_sats = -(-amount_msat // 1000)  # ceil division: sub-sat amounts round up to 1
            elif hasattr(decoded, "amount") and decoded.amount:
                amount_sats = decoded.amount
    
            # Reject no-amount invoices (security: could bypass budget checks)
            if amount_sats is None or amount_sats <= 0:
                return json.dumps(
                    {
                        "success": False,
                        "error": "Invoice has no amount specified. For security, only invoices with explicit amounts are supported.",
                    }
                )
    
            # Check against max_sats
            if amount_sats > max_sats:
                return json.dumps(
                    {
                        "success": False,
                        "error": f"Invoice amount {amount_sats} sats exceeds maximum {max_sats} sats",
                        "amount_sats": amount_sats,
                    }
                )
    
            # Check budget
            if budget_manager and amount_sats:
                try:
                    budget_manager.check_payment(amount_sats, max_sats)
                except Exception as e:
                    return json.dumps(
                        {"success": False, "error": sanitize_error(str(e)), "amount_sats": amount_sats}
                    )
    
            # Pay the invoice
            protocol = "MPP" if is_mpp else "L402"
            logger.info(f"Paying {protocol} invoice for {amount_sats} sats")
            preimage = await wallet.pay_invoice(invoice)
    
            # Record payment
            if budget_manager and amount_sats:
                budget_manager.record_payment(
                    url=f"manual_{protocol.lower()}_payment",
                    amount_sats=amount_sats,
                    invoice=invoice,
                    preimage=preimage,
                    status="success",
                )
    
            # Construct authorization header based on protocol
            if is_mpp:
                authorization_header = f'Payment method="lightning", preimage="{preimage}"'
            else:
                l402_token = f"{macaroon}:{preimage}"
                authorization_header = f"L402 {l402_token}"
    
            result = {
                "success": True,
                "preimage": preimage,
                "amount_sats": amount_sats,
                "protocol": protocol,
                "usage": {
                    "headerName": "Authorization",
                    "headerValue": authorization_header,
                    "protocol": protocol,
                    "description": "Include this header in subsequent requests to the same endpoint",
                },
                "message": (
                    f"Payment successful ({protocol}). Use the authorization header value "
                    f"to access the protected resource."
                ),
            }
    
            # Include token and authorization_header for backward compatibility across protocols
            if is_mpp:
                # For MPP, the token is just the preimage
                result["token"] = preimage
            else:
                # For L402, preserve existing macaroon:preimage token format
                result["token"] = f"{macaroon}:{preimage}"
    
            # Always include the full authorization header
            result["authorization_header"] = authorization_header
    
            return json.dumps(result, indent=2)
    
        except Exception as e:
            logger.exception("Error paying L402/MPP challenge")
            return json.dumps({"success": False, "error": sanitize_error(str(e))})
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden. It mentions the outcome ('receive the authorization token') and a behavioral note about omitting macaroon for MPP mode, but it lacks critical details: it doesn't specify whether this is a read-only or destructive operation, what happens on failure, rate limits, or authentication requirements. For a payment tool with no annotations, this is insufficient.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise with two sentences that are directly relevant. The first sentence states the purpose and outcome, and the second provides a usage note. There's no fluff, but it could be slightly more structured by front-loading key distinctions from siblings.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given no annotations and no output schema, the description is incomplete. It covers the basic purpose and a usage hint but misses behavioral traits (e.g., side effects, error handling) and output details. For a payment tool with 3 parameters and no structured safety hints, this leaves significant gaps for an AI agent to understand full context.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents all parameters well. The description adds minimal value: it reiterates that macaroon should be omitted for MPP mode (which is also in the schema) and implies the tool handles both L402 and MPP flows. No additional syntax, format, or contextual details beyond the schema are provided, meeting the baseline for high coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'Manually pay an L402 or MPP invoice and receive the authorization token.' It specifies the verb ('pay'), resource ('invoice'), and outcome ('receive the authorization token'). However, it doesn't explicitly distinguish this from sibling tools like 'pay_invoice' or 'verify_l402_payment', which reduces clarity about when to choose this specific tool.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides some usage context: 'Use this if you need to handle the L402/MPP flow yourself.' This implies it's for manual handling of these protocols, but it doesn't explicitly state when to use this versus alternatives like 'pay_invoice' or 'access_l402_resource'. No exclusions or prerequisites are mentioned, leaving gaps in guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/refined-element/lightning-enable-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server