AgentKit
AgentKit integrates GOAT Flow through two complementary plugin sets. Both use the x402 protocol and retain their published package and action identifiers:
x402 plugin (payer side) -- 5 actions enabling your agent to create payments, sign EIP-712 authorization data, transfer tokens, check status, and cancel orders
x402-merchant plugin (merchant side) -- 30 actions for managing a full merchant portal: auth, orders, balance, webhooks, API keys, and more
The payer-side flow uses two adapter interfaces:
MerchantGatewayAdapter -- communicates with the merchant's payment API
PayerWalletAdapter -- signs EIP-712 typed data and transfers tokens
Agent Merchant Gateway Blockchain
│ │ │
│ 1. createPayment -------->│ │
│ <--- paymentId + EIP-712 │ │
│ │ │
│ 2. signTypedData -------->│ (local EIP-712 sign) │
│ 3. submitSignature ------>│ │
│ <--- authorized │ │
│ │ │
│ 4. transferToken ---------│----------------------->│
│ │ │
│ 5. status/cancel -------->│ │
import { ActionProvider } from '@goatnetwork/agentkit/providers' ;
import {
createPaymentAction,
submitSignatureAction,
transferPaymentAction,
paymentStatusAction,
cancelPaymentAction,
HttpMerchantGatewayAdapter,
EvmPayerWalletAdapter,
} from '@goatnetwork/agentkit/plugins' ;
import { Wallet } from 'ethers' ;
// 1. Create the merchant gateway adapter (HTTP client to your merchant)
const merchant = new HttpMerchantGatewayAdapter (
process.env. MERCHANT_API_BASE_URL ! ,
{
headers: {
Authorization: `Bearer ${ process . env . MERCHANT_API_KEY }` ,
},
routes: {
createOrderPath: '/x402/create-order' ,
orderStatusPath: '/x402/order-status/:paymentId' ,
submitSignaturePath: '/x402/submit-signature' ,
cancelOrderPath: '/x402/cancel-order' ,
},
}
);
// 2. Create the payer wallet adapter (signs EIP-712 data)
const signer = new Wallet (process.env. PRIVATE_KEY ! );
const payer = new EvmPayerWalletAdapter (signer);
// 3. Register all x402 actions
const provider = new ActionProvider ();
provider. register ( createPaymentAction (merchant));
provider. register ( submitSignatureAction (merchant, payer));
provider. register ( transferPaymentAction (payer));
provider. register ( paymentStatusAction (merchant));
provider. register ( cancelPaymentAction (merchant));
This example is based on the repo's examples/x402-payment-flow/eip712-full-flow.ts:
import { Wallet } from 'ethers' ;
import { EvmPayerWalletAdapter } from '@goatnetwork/agentkit/plugins' ;
// 1. Create a signer
const signer = Wallet. createRandom ();
const payer = new EvmPayerWalletAdapter (signer);
// 2. Merchant creates a payment intent (returns EIP-712 data to sign)
const created = await merchant. createPaymentIntent ({
to: '0xabc...' ,
asset: 'USDC' ,
amount: '1000000' ,
});
// 3. Agent signs the EIP-712 calldata
const signature = await payer. signCalldataTypedData (
created.calldataSignRequest !
);
// 4. Submit the signature to the merchant
const authorized = await merchant. submitPaymentAuthorization (
created.paymentId,
signature
);
// 5. Check status
const status = await merchant. getPaymentStatus (created.paymentId);
console. log ({ paymentId: created.paymentId, authorized, status });
When executing x402 actions through the runtime, pass confirmed: true since payment actions require confirmation:
const context = {
traceId: 'trace_pay_001' ,
network: 'goat-testnet' ,
now: Date. now (),
caller: 'payment-agent' ,
};
// Create payment
const create = await runtime. run (
provider. get ( 'goat.x402.payment.create' ),
context,
{ to: '0xabc...' , asset: 'USDC' , amount: '10' },
{ confirmed: true , idempotencyKey: 'pay-001' }
);
if ( ! create.ok) {
console. error ( 'Payment failed:' , create.error);
return ;
}
// Submit signature
const sign = await runtime. run (
provider. get ( 'goat.x402.payment.submitSignature' ),
context,
{
paymentId: create.output.paymentId,
calldataSignRequest: create.output.calldataSignRequest,
},
{ confirmed: true }
);
// Transfer tokens
const transfer = await runtime. run (
provider. get ( 'goat.x402.payment.transfer' ),
context,
{
tokenAddress: create.output.tokenAddress,
to: create.output.payToAddress,
amount: '10' ,
},
{ confirmed: true }
);
// Check status
const status = await runtime. run (
provider. get ( 'goat.x402.payment.status' ),
context,
{ paymentId: create.output.paymentId }
);
If you need to integrate with a non-standard merchant API, implement the MerchantGatewayAdapter interface:
interface MerchantGatewayAdapter {
createPaymentIntent (
input : CreatePaymentIntentInput ,
signal ?: AbortSignal
) : Promise < CreatePaymentIntentResult >;
getPaymentStatus (
paymentId : string ,
signal ?: AbortSignal
) : Promise <{ paymentId : string ; status : PaymentStatus }>;
submitPaymentAuthorization (
paymentId : string ,
signature : string ,
signal ?: AbortSignal
) : Promise <{ paymentId : string ; status : 'authorized' | 'failed' }>;
cancelPayment (
paymentId : string ,
signal ?: AbortSignal
) : Promise <{ paymentId : string ; status : 'cancelled' | 'failed' }>;
}
Status Description createdPayment intent created, awaiting authorization authorizedSignature submitted and verified settledOn-chain transfer confirmed failedPayment failed at any stage expiredPayment intent timed out
The merchant plugin provides 30 actions for managing the GOAT Flow merchant portal. It uses the HttpMerchantPortalClient HTTP adapter.
import { HttpMerchantPortalClient } from '@goatnetwork/agentkit/plugins' ;
import {
merchantAuthLoginAction,
merchantAuthRegisterAction,
merchantDashboardStatsAction,
merchantOrdersListAction,
merchantBalanceGetAction,
// ... (30 actions total)
} from '@goatnetwork/agentkit/plugins' ;
const merchantClient = new HttpMerchantPortalClient (
process.env. MERCHANT_PORTAL_BASE_URL ?? 'http://localhost:8080'
);
const provider = new ActionProvider ();
provider. register ( merchantAuthLoginAction (merchantClient));
provider. register ( merchantAuthRegisterAction (merchantClient));
provider. register ( merchantDashboardStatsAction (merchantClient));
provider. register ( merchantOrdersListAction (merchantClient));
provider. register ( merchantBalanceGetAction (merchantClient));
// ... register additional actions as needed
Merchant portal actions use bearer token authentication. The auth actions (login, register) return an access_token. Pass it via ActionContext.accessToken in subsequent calls:
// 1. Login
const loginResult = await runtime. run (
provider. get ( 'x402_merchant.auth.login' ),
context,
{ email: 'merchant@example.com' , password: 'secret' },
{ confirmed: true }
);
// 2. Use the token in subsequent calls
const authedContext = {
... context,
accessToken: loginResult.output.access_token,
};
const stats = await runtime. run (
provider. get ( 'x402_merchant.dashboard.stats' ),
authedContext,
{}
);
The accessToken field on ActionContext is never included in hook events or logs for security. Auth action outputs containing tokens are automatically marked with sensitiveOutputFields.
For multi-tenant deployments, use withAccessToken() to create session-scoped client clones that avoid cross-session token leakage:
const sessionClient = merchantClient. withAccessToken (userToken);
// sessionClient has independent token state
# GOAT Flow payer (merchant gateway)
MERCHANT_API_BASE_URL = https://merchant.example.com
MERCHANT_API_KEY = your-api-key
# Custom route paths (optional — defaults shown)
MERCHANT_CREATE_ORDER_PATH = /x402/create-order
MERCHANT_ORDER_STATUS_PATH = /x402/order-status/:paymentId
MERCHANT_SUBMIT_SIGNATURE_PATH = /x402/submit-signature
MERCHANT_CANCEL_ORDER_PATH = /x402/cancel-order
# GOAT Flow merchant portal
MERCHANT_PORTAL_BASE_URL = http://localhost:8080
# GoatAdapter credentials (published variable names below)
GOAT_X402_BASE_URL = https://x402-api.goat.network
GOAT_X402_API_KEY = your-key
GOAT_X402_API_SECRET = your-secret