Handle webhook from agent (async task status updates and completions)
Accepts webhook payloads from both MCP and A2A protocols:
The method normalizes both formats so handlers receive the unwrapped AdCP response data (AdCPAsyncResponseData), not the raw protocol structure.
Protocol-specific webhook payload (MCPWebhookPayload | Task | TaskStatusUpdateEvent)
Task type (e.g create_media_buy) from url param or url part of the webhook delivery
Operation id (e.g used for client app to track the operation) from the param or url part of the webhook delivery
Optionalsignature: stringX-ADCP-Signature header (format: "sha256=...")
Optionaltimestamp: string | numberX-ADCP-Timestamp header (Unix timestamp)
OptionalrawBody: stringWhether webhook was handled successfully
app.post('/webhook/:taskType', async (req, res) => {
const signature = req.headers['x-adcp-signature'];
const timestamp = req.headers['x-adcp-timestamp'];
try {
const handled = await client.handleWebhook(req.body, signature, timestamp, req.params.taskType);
res.status(200).json({ received: handled });
} catch (error) {
res.status(401).json({ error: error.message });
}
});
Generate webhook URL using macro substitution
Type of task (e.g., 'get_products', 'media_buy_delivery')
Operation ID for this request
Full webhook URL with macros replaced
// With template: "https://myapp.com/webhook/{task_type}/{agent_id}/{operation_id}"
const webhookUrl = client.getWebhookUrl('sync_creatives', 'op_123');
// Returns: https://myapp.com/webhook/sync_creatives/agent_x/op_123
// With template: "https://myapp.com/webhook?agent={agent_id}&op={operation_id}"
const webhookUrl = client.getWebhookUrl('sync_creatives', 'op_123');
// Returns: https://myapp.com/webhook?agent=agent_x&op=op_123
Create an HTTP webhook handler that automatically verifies signatures
This helper creates a standard HTTP handler (Express/Next.js/etc.) that:
HTTP handler function compatible with Express, Next.js, etc.
Verify webhook signature using HMAC-SHA256 per AdCP spec.
HMAC is computed over the raw HTTP body bytes — the exact bytes received on the wire, before JSON parsing. This ensures cross-language interop since different JSON serializers may produce different byte representations of the same logical payload.
For backward compatibility, a parsed object is still accepted but will be re-serialized with JSON.stringify, which may not match the sender's bytes. Always prefer passing the raw body string.
Signature format: sha256={hex_signature} Message format: {timestamp}.{raw_body}
Raw HTTP body string (preferred) or parsed payload object (deprecated)
X-ADCP-Signature header value (format: "sha256=...")
X-ADCP-Timestamp header value (Unix timestamp)
true if signature is valid
Discover available advertising products
Product discovery parameters
OptionalinputHandler: InputHandlerHandler for clarification requests
Optionaloptions: TaskOptionsTask execution options
List available creative formats
Format listing parameters
OptionalinputHandler: InputHandlerHandler for clarification requests
Optionaloptions: TaskOptionsTask execution options
Create a new media buy
Media buy creation parameters
OptionalinputHandler: InputHandlerHandler for clarification requests
Optionaloptions: TaskOptionsTask execution options
Update an existing media buy
Media buy update parameters
OptionalinputHandler: InputHandlerHandler for clarification requests
Optionaloptions: TaskOptionsTask execution options
Sync creative assets
Creative sync parameters
OptionalinputHandler: InputHandlerHandler for clarification requests
Optionaloptions: TaskOptionsTask execution options
List creative assets
Creative listing parameters
OptionalinputHandler: InputHandlerHandler for clarification requests
Optionaloptions: TaskOptionsTask execution options
Preview a creative
Preview creative parameters
OptionalinputHandler: InputHandlerHandler for clarification requests
Optionaloptions: TaskOptionsTask execution options
Get media buy status, creative approvals, and optional delivery snapshots
Request parameters
OptionalinputHandler: InputHandlerHandler for clarification requests
Optionaloptions: TaskOptionsTask execution options
Get media buy delivery information
Delivery information parameters
OptionalinputHandler: InputHandlerHandler for clarification requests
Optionaloptions: TaskOptionsTask execution options
Provide performance feedback
Performance feedback parameters
OptionalinputHandler: InputHandlerHandler for clarification requests
Optionaloptions: TaskOptionsTask execution options
Get audience signals
Signals request parameters
OptionalinputHandler: InputHandlerHandler for clarification requests
Optionaloptions: TaskOptionsTask execution options
Activate audience signals
Signal activation parameters
OptionalinputHandler: InputHandlerHandler for clarification requests
Optionaloptions: TaskOptionsTask execution options
Sync campaign plans to a governance agent. Plans define authorized parameters: budget, channels, flight dates, markets, policies, delegations.
Uses the governance agent from config.governance.campaign.agent by default. Pass an explicit agent via options.agent to override.
OptionalinputHandler: InputHandlerOptionaloptions: TaskOptions & { agent?: AgentConfig }Get governance audit logs for one or more plans. Returns budget state, channel allocation, per-campaign breakdown, and audit trail.
Uses the governance agent from config.governance.campaign.agent by default. Pass an explicit agent via options.agent to override.
Optionaloptions: TaskOptions & { agent?: AgentConfig }Report a governance outcome for an async task that has resolved.
Use this when a task returned status 'submitted' or 'working' and later resolves via polling or webhooks. The checkId is available on the original TaskResult at result.governance.checkId.
OptionalgovernanceContext: stringOptionalsellerResponse: Record<string, unknown>Optionalerror: { code?: string; message: string }Get AdCP capabilities
Capabilities request parameters
OptionalinputHandler: InputHandlerHandler for clarification requests
Optionaloptions: TaskOptionsTask execution options
Build a creative from a format and brand context
OptionalinputHandler: InputHandlerOptionaloptions: TaskOptionsList accounts
OptionalinputHandler: InputHandlerOptionaloptions: TaskOptionsSync accounts
OptionalinputHandler: InputHandlerOptionaloptions: TaskOptionsSync audiences
OptionalinputHandler: InputHandlerOptionaloptions: TaskOptionsCreate a property list
OptionalinputHandler: InputHandlerOptionaloptions: TaskOptionsGet a property list
OptionalinputHandler: InputHandlerOptionaloptions: TaskOptionsUpdate a property list
OptionalinputHandler: InputHandlerOptionaloptions: TaskOptionsList property lists
OptionalinputHandler: InputHandlerOptionaloptions: TaskOptionsDelete a property list
OptionalinputHandler: InputHandlerOptionaloptions: TaskOptionsList content standards
OptionalinputHandler: InputHandlerOptionaloptions: TaskOptionsGet content standards
OptionalinputHandler: InputHandlerOptionaloptions: TaskOptionsCalibrate content against standards
OptionalinputHandler: InputHandlerOptionaloptions: TaskOptionsValidate content delivery
OptionalinputHandler: InputHandlerOptionaloptions: TaskOptionsGet an SI offering
OptionalinputHandler: InputHandlerOptionaloptions: TaskOptionsInitiate an SI session
OptionalinputHandler: InputHandlerOptionaloptions: TaskOptionsSend a message in an SI session
OptionalinputHandler: InputHandlerOptionaloptions: TaskOptionsTerminate an SI session
OptionalinputHandler: InputHandlerOptionaloptions: TaskOptionsExecute any task by name with type safety
Name of the task to execute
Task parameters
OptionalinputHandler: InputHandlerHandler for clarification requests
Optionaloptions: TaskOptionsTask execution options
Resume a deferred task using its token
Deferred task token
Handler to provide the missing input
Continue an existing conversation with the agent
Message to send to the agent
Conversation context ID to continue
OptionalinputHandler: InputHandlerHandler for any clarification requests
const agent = new ADCPClient(config);
const initial = await agent.getProducts({ brief: 'Tech products' });
// Continue the conversation — use the server-returned contextId, not
// the client-minted correlation taskId.
const refined = await agent.continueConversation(
'Focus only on laptops under $1000',
initial.metadata.contextId!
);
Clear conversation history for a task
Get the agent configuration with normalized protocol
Returns the agent config with:
For guaranteed canonical URL, use getResolvedAgent() instead.
Get the fully resolved agent configuration
This async method ensures the agent config has the canonical URL resolved:
Promise resolving to agent config with canonical URL
Get the agent ID
Get the agent name
Get the agent protocol (may be normalized from original config)
Get the canonical base URL for this agent
Returns the canonical URL if already resolved, or computes it synchronously from the configured URL. For the most accurate canonical URL (especially for A2A where the agent card contains the authoritative URL), use resolveCanonicalUrl() first.
The canonical URL is:
The canonical base URL (synchronous, may not be fully resolved)
Resolve and return the canonical base URL for this agent
This async method ensures the canonical URL is properly resolved:
The result is cached, so subsequent calls are fast.
Promise resolving to the canonical base URL
Check if this agent is the same as another agent
Compares agents by their canonical base URLs. Two agents are considered the same if they have the same canonical URL, regardless of:
Another agent configuration or SingleAgentClient to compare
true if agents have the same canonical URL
Async version of isSameAgent that resolves canonical URLs first
This provides more accurate comparison for A2A agents since it fetches the agent card to get the authoritative canonical URL.
Another agent configuration or SingleAgentClient to compare
Promise resolving to true if agents have the same canonical URL
Get active tasks for this agent
Get detailed information about a specific task
ID of the task to get information for
Promise resolving to task information
Subscribe to task notifications for this agent
Function to call when task status changes
Unsubscribe function
Subscribe to all task events (create, update, complete, error)
Event callbacks for different task events
Unsubscribe function
Register webhook URL for receiving task notifications
URL to receive webhook notifications
OptionaltaskTypes: string[]Optional array of task types to watch (defaults to all)
Unregister webhook notifications
Get comprehensive agent information including name, description, and available tools/skills
Works with both MCP (tools) and A2A (skills) protocols to discover what the agent can do.
Auth resolution: this method forwards agent.headers as customHeaders
to the MCP transport so header-only auth (HTTP Basic via gateways like
Apigee/Kong, x-api-key, custom tenant routing) reaches the precheck
path. The invariant — basic-auth credentials live entirely on
headers.Authorization; do not also set auth_token — is documented
at docs/guides/BASIC-AUTH.md. See #1864 for the failure mode if it's
violated.
Promise resolving to agent information including tools
Get agent capabilities, including AdCP version support
For v3 servers, calls get_adcp_capabilities tool. For v2 servers, builds synthetic capabilities from available tools.
Promise resolving to normalized capabilities object
Detect server AdCP version
'v2' or 'v3' based on server capabilities
Whether the seller's capabilities are synthesized from tools/list
with no authoritative get_adcp_capabilities response — i.e. the
dispatcher routes through the v2 adapter and idempotency-TTL is
unknown. Use this to gate retry behavior for sellers whose retry
safety can't be derived from declared capabilities (lower attempt
caps, longer backoff, or fall back to natural-key recovery).
Returns false for declared v2 sellers, declared v3 sellers, and
synthetic v3 sellers (which advertise the v3 discovery tool even
when the call itself failed).
Caveat for synthetic v3: the predicate returns false, but TTL is
still unknown for those sellers — getIdempotencyReplayTtlSeconds()
throws until the agent's capabilities endpoint is fixed (issue
#1217). Retry-policy consumers that need a complete "TTL unknown"
gate should additionally check getCapabilities()._synthetic.
Check if server supports a specific AdCP major version
Return the seller's declared adcp.idempotency.replay_ttl_seconds.
BYOK callers use this to compare the age of persisted keys against the seller's replay window — past the window, the safe recovery is a natural-key lookup rather than reusing the key.
Fails closed when the seller is v3 but does not declare the field: the
spec makes the declaration REQUIRED, and silently defaulting to 24h
would mislead buyers about retry safety. Callers on v2 servers get
undefined instead of a throw — v2 pre-dates the idempotency envelope.
Check if the seller supports a feature.
Feature names resolve as follows:
Absent features return false.
Require that the seller supports all listed features. Throws FeatureUnsupportedError if any are missing.
Call this before making feature-dependent task calls to fail fast with an actionable error message.
Force-refresh cached capabilities from the server. Useful when seller capabilities may have changed.
Assert that the seller's capabilities corroborate the major this client
is pinned to (per getAdcpVersion()).
A self-reported version: 'v3' is not enough — a hostile or
misconfigured seller can just string-claim the version. For sellers
that return an authoritative get_adcp_capabilities response, the
guard requires:
capabilities.majorVersions.includes(<this client's major>)capabilities.idempotency.replayTtlSeconds present (spec-required
for real major-3+ sellers)Sellers whose capabilities are synthesized from tools/list (no
authoritative get_adcp_capabilities response) are treated as v2: a
compliant v3 seller would declare itself, so absence of a declaration
is taken as evidence of v2. The dispatcher routes the request through
the v2 wire-shape adapter. A one-time warning surfaces the routing
decision so adopters can audit it; retry-safety (idempotency TTL) is
unknown for these sellers and BYOK callers should treat them as such.
Per-client allowV2: true or, when that's undefined,
ADCP_ALLOW_V2=1 in the environment bypasses the guard entirely.
Throws VersionUnsupportedError with the specific reason on failure.
Deprecated alias for requireSupportedMajor. Original name from
the AdCP v2/v3 split; the function generalized in Stage 3 to check the
client's per-instance major instead of hardcoded 3, and requireV3
stopped reflecting what the function actually does.
StaticdiscoverQuery a creative agent to discover available creative formats
This is a static utility method that allows you to query any creative agent (like creative.adcontextprotocol.org) to discover what formats are available before creating a media buy.
URL of the creative agent (e.g., 'https://creative.adcontextprotocol.org/mcp')
Protocol to use ('mcp' or 'a2a'), defaults to 'mcp'
Promise resolving to the list of available formats
// Discover formats from the standard creative agent
const formats = await SingleAgentClient.discoverCreativeFormats(
'https://creative.adcontextprotocol.org/mcp'
);
// Find a specific format
const banner = formats.find(f => f.format_id.id === 'display_300x250_image');
// Use the format in a media buy
await salesAgent.createMediaBuy({
packages: [{
format_ids: [{
agent_url: banner.format_id.agent_url,
id: banner.format_id.id
}]
}]
});
Returns the AdCP protocol version this client is configured to speak.
Defaults to ADCP_VERSION (the GA version the SDK ships against) unless overridden via
new SingleAgentClient(agent, { adcpVersion }).Plumbing surface — Stage 2 of the multi-version refactor exposes the configured value but does not yet vary validator/schema selection by version. Wire-shape adapters key off this method in subsequent stages.