This NIP defines a general-purpose A2A (Agent-to-Agent) Payment Channel protocol. It provides a mechanism for establishing, managing, and settling bilateral payment channels between participants in the AI Agent ecosystem. These channels are designed to facilitate efficient, low-cost, off-chain micropayments, particularly suited for automated or high-frequency protocol-level payments, such as fees for A2A message routing, API gateway access (e.g., as per NIP-9), or other foundational agent interactions. This protocol aims to provide a core infrastructure component that can be leveraged by various higher-level NIPs or applications requiring a standardized off-chain payment solution.
Many interactions within a decentralized agent ecosystem, especially at the protocol or infrastructure level, involve frequent, small-value exchanges. Performing each of these as a separate on-chain transaction would be prohibitively expensive and slow. A standardized A2A Payment Channel protocol is needed to:
This NIP specifies:
The detailed mechanisms for application-level service payments (e.g., a User Agent paying an AI Agent for a specific skill like content generation or data analysis, which typically involves service discovery, quotation, and explicit user confirmation) are considered out of scope for this NIP. Such application-specific payment flows will be defined in a separate NIP (e.g., a future NIP-X for Agent Service Payments). However, that NIP-X may optionally reference and utilize the payment channel mechanisms defined herein for settling payments for those services, especially for recurring or high-frequency service interactions with the same provider.
Interfacing with Non-A2A Services: While this NIP defines an A2A protocol, it is recognized that payment channels may be desired with services that are not native A2A agents (e.g., traditional HTTP services). For such services to participate in NIP-4 payment channels, they would need to interact via an A2A-compatible interface. This could be achieved by the service provider running a dedicated A2A agent (or a lightweight adapter component) or by utilizing a trusted intermediary A2A gateway. The core NIP-4 protocol messages remain A2A; the specific architecture of such an interface or gateway is an implementation choice or can be further detailed in complementary documents.
This protocol relies on NIP-1 for Agent identity and NIP-2 for secure authenticated communication channels for exchanging payment channel messages. It is assumed that for a given payment channel, both the Payer and Payee Agents operate with respect to a common, pre-agreed underlying blockchain or ledger system where the channel is anchored and settled. The specific on-chain mechanism (e.g., smart contract type) used on this common system is also assumed to be known or discoverable by participating agents.
The protocol supports one primary model for managing payments:
This model is suited for frequent, low-value interactions, minimizing on-chain transactions by conducting most payment updates off-chain.
ChannelOpenRequest
.ChannelOpenResponse
, agreeing to terms or proposing alternatives. This includes on-chain channel contract details or multi-sig setup.ChannelFundNotification
with proof of funding.ChannelActiveNotification
to confirm the channel is ready for off-chain transactions.ChannelStateUpdatePropose
A2A message.ChannelStateUpdatePropose
for this specific transaction.ChannelStateUpdatePropose
A2A message or within an HTTP response header):
ChannelStateUpdateConfirm
A2A message back to the Proposing Agent.confirmation_data
within the X-Payment-Channel-Data
header of the subsequent HTTP request to the service (see “HTTP Interface for Channel Payments” section).ChannelStateUpdateConfirm
A2A message or the confirmation_data
in a subsequent HTTP request), constitutes the off-chain micro-payment. Both parties retain this latest agreed state.ChannelFundNotification
to the Payee Agent, with funded_amount
specifying the amount of the top-up. The funding_transaction_proof
would point to this new top-up transaction.ChannelStateUpdatePropose
messages will reflect balances based on this new, larger total collateral (see note under ChannelStateUpdatePropose
balances).ChannelCloseRequest
containing the latest mutually signed channel state.ChannelCloseConfirmation
.ChannelOpenRequest
(Payer Agent -> Payee Agent)message.parts
content):
{
"type": "ChannelOpenRequest",
"proposed_channel_id": "unique_proposed_id_by_payer", // Temporary ID for negotiation
"payer_did": "did:example:payer_agent_did",
"payee_did": "did:example:payee_agent_did",
"initial_funding_amount": { "amount": "1000", "currency": "USD" } // Currency as token symbol
}
ChannelOpenResponse
(Payee Agent -> Payer Agent)message.parts
content):
{
"type": "ChannelOpenResponse",
"proposed_channel_id": "unique_proposed_id_by_payer", // From request
"channel_id": "confirmed_channel_id_by_payee", // Payee confirms or assigns a new persistent ID
"status": "accepted" | "rejected" | "alternative_proposed",
"payer_did": "did:example:payer_agent_did",
"payee_did": "did:example:payee_agent_did",
"agreed_funding_amount": { "amount": "1000", "currency": "USD" }, // Currency as token symbol
"rejection_reason": "...", // If rejected
"alternative_terms": { ... } // If alternative proposed
}
ChannelFundNotification
(Payer Agent -> Payee Agent)message.parts
content):
{
"type": "ChannelFundNotification",
"channel_id": "confirmed_channel_id_by_payee",
"funding_transaction_proof": {
"transaction_hash": "0x_funding_tx_hash",
},
"funded_amount": { "amount": "1000", "currency": "USD" } // Currency as token symbol
}
ChannelActiveNotification
(Payee Agent -> Payer Agent)message.parts
content):
{
"type": "ChannelActiveNotification",
"channel_id": "confirmed_channel_id_by_payee",
"status": "active" | "funding_issue",
"message": "Channel is now active." // Or error message
}
ChannelStateUpdatePropose
(Proposing Agent -> Counterparty Agent)message.parts
content):
{
"type": "ChannelStateUpdatePropose",
"channel_id": "confirmed_channel_id_by_payee", // Should be the agreed channel_id
"sequence_number": 123, // Monotonically increasing
"balances": { // Balances from the perspective of the Payer. These balances should always sum to the total acknowledged on-chain collateral (initial funding plus any acknowledged top-ups).
"payer_balance": "990", // Remaining balance for Payer
"payee_earned_total": "10" // Total amount earned by Payee in this channel up to this state
},
"signature_proposer": "signature_of_hash(channel_id, sequence_number, balances)_by_proposing_agent"
}
ChannelStateUpdateConfirm
(Counterparty Agent -> Proposing Agent)message.parts
content):
{
"type": "ChannelStateUpdateConfirm",
"channel_id": "confirmed_channel_id_by_payee",
"sequence_number": 123,
"balances": {
"payer_balance": "990",
"payee_earned_total": "10"
},
"signature_confirmer": "signature_of_hash(channel_id, sequence_number, balances)_by_counterparty_agent"
// Both agents now have a doubly-signed state.
}
ChannelCloseRequest
(Initiating Agent -> Other Agent)message.parts
content):
{
"type": "ChannelCloseRequest",
"channel_id": "confirmed_channel_id_by_payee", // Should be the agreed channel_id
"final_signed_state": { // The latest mutually agreed and signed state
"sequence_number": 150,
"balances": {
"payer_balance": "850",
"payee_earned_total": "150"
},
"signature_proposer": "signature_of_proposer_on_this_state", // Generalized signature
"signature_confirmer": "signature_of_confirmer_on_this_state" // Generalized signature
},
"reason": "User request" // Optional
}
ChannelCloseConfirmation
(Responding Agent -> Initiating Agent)message.parts
content):
{
"type": "ChannelCloseConfirmation",
"channel_id": "confirmed_channel_id_by_payee", // Should be the agreed channel_id
"status": "acknowledged" | "disputed",
"message": "Acknowledged. Proceeding with on-chain settlement." // Or dispute reason
}
While NIP-4 itself defines A2A messages for payment channel management, services (e.g., traditional HTTP APIs) that are not native A2A agents can participate in these payment channels via A2A-compatible interfaces (adapters/gateways). When a client interacts with such an HTTP service and wishes to pay for a specific request using an established NIP-4 payment channel, a single HTTP header, X-Payment-Channel-Data
, is used to carry Base64 encoded JSON payloads for coordinating the payment.
The A2A adapter on the client’s side typically constructs and adds the request header. This header includes details for the current request and may also include confirmation data for a payment proposal received from a previous HTTP response. The A2A adapter on the service’s side interprets the request header, processes any confirmation data, and after the service logic determines the outcome and cost for the current request, constructs and adds the response header containing a new payment proposal.
X-Payment-Channel-Data
HeaderThe JSON object, once Base64 decoded from the X-Payment-Channel-Data
request header, has the following structure:
{
"channel_id": "confirmed_channel_id_by_payee", // Required: The NIP-4 payment channel ID.
"max_amount": "50", // Optional: Max amount client authorizes for this request.
"currency": "USD", // Optional: Currency (as token symbol) for max_amount. Assumed channel currency if omitted.
"client_tx_ref": "client-ref-001", // Optional: Client-generated reference for this HTTP transaction.
"confirmation_data": { // Optional: Data to confirm a previous payment proposal from the service.
"confirmed_sequence_number": 123, // Required: The sequence_number of the state update being confirmed.
"confirmed_balances": { // Required: The balances being confirmed.
"payer_balance": "990",
"payee_earned_total": "10"
},
"signature_confirmer": "signature_of_hash(channel_id, confirmed_sequence_number, confirmed_balances)_by_payer_adapter"
// Required: Payer's signature over the confirmed state.
}
}
The JSON object, once Base64 decoded from the X-Payment-Channel-Data
response header, has the following structure. This payload contains the core elements of a ChannelStateUpdatePropose
.
{
"channel_id": "confirmed_channel_id_by_payee", // Required: The NIP-4 payment channel ID.
"sequence_number": 124, // Required: The new sequence number for this channel state update.
"balances": { // Required: The new proposed balances.
"payer_balance": "990",
"payee_earned_total": "10"
},
"amount_debited": "5", // Required: Actual amount debited for this HTTP transaction.
"currency_debited": "USD", // Required: Currency (as token symbol) for amount_debited.
"service_tx_ref": "service-ref-abc", // Optional: Service-generated reference for this transaction.
"signature_proposer": "signature_of_hash(channel_id, sequence_number, balances)_by_payee_adapter"
// Required: Signature from the Payee\'s A2A adapter/gateway.
// The signature is over the canonical form of channel_id,
// sequence_number, and balances, identical to how it\'s
// done for the ChannelStateUpdatePropose A2A message.
}
Example Flow:
channel-123
is open with the service’s A2A gateway. Current agreed sequence_number
is 123, payer_balance
is “1000”, payee_earned_total
is “0”.{"channel_id": "channel-123", "client_tx_ref": "user-req-007"}
It Base64 encodes this JSON and adds it to the X-Payment-Channel-Data
header of the HTTP GET request to https://api.service.com/data
.X-Payment-Channel-Data
header. The HTTP API processes the request and determines the cost is 5 units.sequence_number: 124
, balances: {payer_balance: "995", payee_earned_total: "5"}
), signs it to get signature_proposer
, and other details:
{"channel_id": "channel-123", "sequence_number": 124, "balances": {"payer_balance": "995", "payee_earned_total": "5"}, "amount_debited": "5", "currency_debited": "USD", "service_tx_ref": "srv-tx-456", "signature_proposer": "..."}
It Base64 encodes this JSON and adds it to the X-Payment-Channel-Data
header of the HTTP 200 OK response.X-Payment-Channel-Data
header. It now has the signed proposal for state 124.signature_proposer
against the received channel_id
, sequence_number
(124), and balances
. It also checks if amount_debited
is acceptable.confirmation_data
for state 124, including its own signature_confirmer
for that state.
Let’s say the next request is for another 7 units of service.
The request payload for the second call would be:
{"channel_id": "channel-123", "client_tx_ref": "user-req-008", "confirmation_data": {"confirmed_sequence_number": 124, "confirmed_balances": {"payer_balance": "995", "payee_earned_total": "5"}, "signature_confirmer": "payer_sig_for_state_124"}}
This is Base64 encoded and sent in the X-Payment-Channel-Data
header of the next HTTP request.confirmation_data
:
signature_confirmer
for state 124.{"channel_id": "channel-123", "sequence_number": 125, "balances": {"payer_balance": "988", "payee_earned_total": "12"}, "amount_debited": "7", "currency_debited": "USD", "service_tx_ref": "srv-tx-457", "signature_proposer": "..."}
This is Base64 encoded and returned in the X-Payment-Channel-Data
header of the HTTP response.This mechanism allows HTTP services to participate in NIP-4 payment channels by embedding the payment proposal and confirmation directly within the HTTP exchange, followed by a standard A2A confirmation.
sequenceDiagram
participant Payer as Payer Agent
participant Payee as Payee Agent
participant OnChain as On-Chain Contract/Ledger
Payer->>Payee: X-DID-Signature Header (NIP-2 Auth)<br>Body: ChannelOpenRequest
activate Payee
Payee-->>Payer: X-DID-Signature Header (NIP-2 Auth)<br>Body: ChannelOpenResponse (with on-chain details)
deactivate Payee
Payer->>OnChain: Fund Channel (e.g., deploy/fund contract)
Payer->>Payee: X-DID-Signature Header (NIP-2 Auth)<br>Body: ChannelFundNotification (with tx_proof)
activate Payee
Payee->>OnChain: Verify Funding (optional, or relies on Payer proof)
Payee-->>Payer: X-DID-Signature Header (NIP-2 Auth)<br>Body: ChannelActiveNotification
deactivate Payee
Note over Payer, Payee: Channel is Active for Off-Chain Payments
loop Multiple Protocol Payments & Off-Chain State Updates
Note over Payer, Payee: A protocol event triggers a payment
Note over Payer, Payee: One Agent (Proposer, e.g., Payee) initiates state update
Payee->>Payer: X-DID-Signature Header (NIP-2 Auth)<br>Body: ChannelStateUpdatePropose (Payee signs new state as Proposer)
activate Payer
Payer-->>Payee: X-DID-Signature Header (NIP-2 Auth)<br>Body: ChannelStateUpdateConfirm (Payer counter-signs new state as Counterparty)
deactivate Payer
Note over Payer, Payee: Both parties store latest doubly-signed state
end
Note over Payer, Payee: Decision to close channel (e.g., Payer initiates)
Payer->>Payee: X-DID-Signature Header (NIP-2 Auth)<br>Body: ChannelCloseRequest (with final doubly-signed state)
activate Payee
Payee-->>Payer: X-DID-Signature Header (NIP-2 Auth)<br>Body: ChannelCloseConfirmation
deactivate Payee
Payer->>OnChain: Submit Final State for Settlement
OnChain->>OnChain: Process Settlement (handles disputes if any)
OnChain-->>Payer: Settlement Confirmed
OnChain-->>Payee: Settlement Confirmed
This protocol is designed to be extensible to various payment methods for funding and settling channels. The initial focus is on on-chain transactions. Other methods like Layer-2 payments (Lightning Network) or even traditional payment gateways (with appropriate tokenization of proof for funding/settlement) could be integrated by defining new on_chain_details
for ChannelOpenResponse
and funding_transaction_proof
structures for ChannelFundNotification
.
The design choices in this NIP aim to provide a flexible and secure payment channel framework for A2A interactions.
on_chain_details
and funding_transaction_proof
structures are designed to be adaptable to future payment technologies and methods for channel operations.Alternative designs considered:
This NIP introduces a new protocol for A2A payment channels. It does not alter or replace any existing NIPs in a way that would cause backwards incompatibility. Agents not implementing this NIP will simply be unable to participate in these specific payment channel flows.
Test cases are highly recommended and should cover:
(Specific test vectors and scenarios to be detailed in a companion document or repository.)
A reference implementation is planned to demonstrate the protocol in action.
nonce
and timestamp
mechanisms in NIP-2 protect against replay attacks for the payment messages themselves. Channel state updates rely on monotonically increasing sequence numbers to prevent replay of old states.This NIP provides a foundational layer for A2A payment channels. Further enhancements and specific on-chain mechanism integrations can be proposed in subsequent NIPs or extensions.
Copyright and related rights waived via CC0.