Skip to main content

SellerConfig

The central configuration object for Key0. Pass it to any framework integration to stand up payment-gated endpoints:
  • Expresskey0Router(config)
  • Honokey0App(config)
  • Fastifykey0Plugin(config)
  • FactorycreateKey0(config)
import type { SellerConfig } from "@key0ai/key0";

Identity

Fields that populate the A2A agent discovery card.
FieldTypeRequiredDefaultDescription
agentNamestringYesDisplay name shown in the agent card.
agentDescriptionstringYesShort description of the agent’s capabilities.
agentUrlstringYesPublic base URL of your server (used for endpoint discovery).
providerNamestringYesOrganization or developer name.
providerUrlstringYesOrganization URL.
versionstringNo"1.0.0"Semantic version exposed in the agent card.

Payment

FieldTypeRequiredDefaultDescription
walletAddress`0x${string}`YesAddress that receives USDC payments.
network"testnet" | "mainnet"Yes"testnet" targets Base Sepolia (chain 84532). "mainnet" targets Base (chain 8453).

Product Catalog

FieldTypeRequiredDefaultDescription
plansreadonly Plan[]YesOne or more pricing plans. See Plan below.

Challenge

FieldTypeRequiredDefaultDescription
challengeTTLSecondsnumberNo900How long (in seconds) a challenge remains valid before expiring.

Credential Issuance

FieldTypeRequiredDefaultDescription
fetchResourceCredentials(params: IssueTokenParams) => Promise<TokenIssuanceResult>YesCalled after on-chain payment is verified. Return a token (JWT, API key, etc.) that grants the buyer access to the resource. See IssueTokenParams and TokenIssuanceResult.
tokenIssueTimeoutMsnumberNo15000Maximum time (ms) to wait for fetchResourceCredentials before timing out.
tokenIssueRetriesnumberNo2Number of retries on transient failures within fetchResourceCredentials.

Lifecycle Hooks

Optional callbacks fired during the challenge lifecycle. Both are fire-and-forget — failures are logged but do not affect the payment flow.
FieldTypeRequiredDefaultDescription
onPaymentReceived(grant: AccessGrant) => Promise<void>NoFired after a payment is verified and credentials are issued.
onChallengeExpired(challengeId: string) => Promise<void>NoFired when a challenge passes its TTL without payment.

MCP

FieldTypeRequiredDefaultDescription
mcpbooleanNofalseWhen true, mounts MCP discovery (/.well-known/mcp.json) and Streamable HTTP (POST /mcp) endpoints alongside A2A routes.

Customization

FieldTypeRequiredDefaultDescription
basePathstringNo"/a2a"Prefix for all A2A endpoints.
resourceEndpointTemplatestringNoauto-generatedURL template containing {resourceId} that appears in the access grant. Example: "https://api.example.com/photos/{resourceId}".

Settlement

Control how on-chain settlement is performed. By default, settlement is routed through the Coinbase Developer Platform facilitator.
FieldTypeRequiredDefaultDescription
gasWalletPrivateKey`0x${string}`NoProvide a private key to enable self-contained gas wallet settlement instead of the facilitator.
facilitatorUrlstringNoCDP defaultOverride the default facilitator URL from CHAIN_CONFIGS.
redisIRedisLockClientNoRedis client for distributed locking when using gas wallet settlement across multiple instances. Without this, an in-process serial queue is used (single-instance only).

Supporting Types

Plan

Defines a pricing tier that buyers can select.
type Plan = {
  readonly planId: string;
  readonly unitAmount: string; // e.g. "$0.10"
  readonly description?: string;
};
FieldTypeRequiredDescription
planIdstringYesUnique identifier for this plan.
unitAmountstringYesDollar-formatted price string (e.g. "$0.10", "$5.00").
descriptionstringNoHuman-readable label for the plan.

IssueTokenParams

Passed to fetchResourceCredentials after payment verification.
type IssueTokenParams = {
  readonly requestId: string;
  readonly challengeId: string;
  readonly resourceId: string;
  readonly planId: string;
  readonly txHash: string;
};
FieldTypeDescription
requestIdstringOriginal access request ID.
challengeIdstringChallenge that was fulfilled.
resourceIdstringResource the buyer is purchasing access to.
planIdstringSelected plan from the catalog.
txHashstringOn-chain transaction hash of the USDC transfer.

TokenIssuanceResult

Returned from fetchResourceCredentials.
type TokenIssuanceResult = {
  readonly token: string;
  readonly tokenType?: string; // Default "Bearer"
};
FieldTypeRequiredDescription
tokenstringYesThe credential (JWT, API key, etc.) the buyer will use to access the resource.
tokenTypestringNoToken scheme. Defaults to "Bearer".

Minimal Example

A working configuration with only the required fields:
import { key0Router } from "@key0ai/key0/express";

const config = {
  agentName: "Photo API",
  agentDescription: "High-resolution stock photos via A2A.",
  agentUrl: "https://photos.example.com",
  providerName: "Example Inc.",
  providerUrl: "https://example.com",

  walletAddress: "0x1234567890abcdef1234567890abcdef12345678" as `0x${string}`,
  network: "mainnet" as const,

  plans: [
    { planId: "single", unitAmount: "$0.10" },
  ],

  async fetchResourceCredentials({ resourceId, planId }) {
    const token = await generateJwt({ resourceId, planId });
    return { token };
  },
};

app.use(key0Router(config));

Full Example

All optional fields included:
import { key0Router } from "@key0ai/key0/express";
import Redis from "ioredis";

const redis = new Redis(process.env.REDIS_URL);

const config = {
  // Identity
  agentName: "Photo API",
  agentDescription: "High-resolution stock photos via A2A.",
  agentUrl: "https://photos.example.com",
  providerName: "Example Inc.",
  providerUrl: "https://example.com",
  version: "2.1.0",

  // Payment
  walletAddress: "0x1234567890abcdef1234567890abcdef12345678" as `0x${string}`,
  network: "mainnet" as const,

  // Product catalog
  plans: [
    { planId: "single", unitAmount: "$0.10", description: "One photo download" },
    { planId: "bulk-50", unitAmount: "$3.50", description: "50 photo downloads" },
  ],

  // Challenge
  challengeTTLSeconds: 600,

  // Credential issuance
  async fetchResourceCredentials({ resourceId, planId, txHash }) {
    const token = await generateJwt({ resourceId, planId, txHash });
    return { token, tokenType: "Bearer" };
  },
  tokenIssueTimeoutMs: 10_000,
  tokenIssueRetries: 3,

  // Lifecycle hooks
  async onPaymentReceived(grant) {
    await analytics.track("payment", { challengeId: grant.challengeId });
  },
  async onChallengeExpired(challengeId) {
    await analytics.track("challenge_expired", { challengeId });
  },

  // MCP
  mcp: true,

  // Customization
  basePath: "/v1/a2a",
  resourceEndpointTemplate: "https://photos.example.com/api/photos/{resourceId}",

  // Settlement
  gasWalletPrivateKey: process.env.GAS_WALLET_KEY as `0x${string}`,
  facilitatorUrl: "https://custom-facilitator.example.com",
  redis,
};

app.use(key0Router(config));