Agent commerce (ACP / AP2)
Why
Agent checkouts (ACP from OpenAI+Stripe, AP2 from Google/FIDO) settle without a browser. The ad that sent the shopper has no conversion signal unless you fire one server-side. These adapters turn the protocol payload into the same event you already pass to ads.conversions.track().
ACP checkout session
Amounts are integer minor units (cents). The adapter divides by 100. ACP carries no timestamp — stamp eventTime at receipt, or let it default to now. Buyer email and phone stay plaintext; adscapi hashes them per platform at dispatch.
import { createAdscapi, fromAcpCheckoutSession } from 'adscapi';
const ads = createAdscapi();
const event = fromAcpCheckoutSession(session, { eventTime: Date.now() });
await ads.conversions.track(event);transactionId is session.order.id when present, otherwise session.id. Value comes from the totals row with type === 'total'. Currency is uppercased.
ACP order webhook
Use this for the async settle path (order_create / order_update). Returns a purchase only when status is confirmed, shipped, or fulfilled. Anything else (created, manual_review, canceled, …) returns null — not a conversion yet. The envelope has no buyer and no currency.
import { fromAcpOrderWebhook } from 'adscapi';
const event = fromAcpOrderWebhook(webhook);
if (event) await ads.conversions.track(event);transactionId is checkout_session_id. Value is set only when totals are present.
AP2 payment mandate
Amounts are already major units (for example 19.50) — no division. AP2 carries an ISO 8601 timestamp; a bad one falls back to now. Payer identity is opt-in and often absent. When payer_name is present it splits into firstName / lastName.
import { fromAp2PaymentMandate } from 'adscapi';
const event = fromAp2PaymentMandate(mandate);
await ads.conversions.track(event);transactionId is payment_mandate_id.
MCP and relay
From an agent, call adscapi_fire_agent_conversion with { protocol: 'acp' | 'ap2', payload }. It maps the payload and fans out. Pass dryRun: true first.
Non-JS backends map the payload themselves, then hit the relay. The Worker accepts a canonical event, not a raw ACP/AP2 payload. Convert first.
curl -X POST https://<your-worker>/track \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"purchase","value":19.5,"currency":"USD","transactionId":"order_xyz789","consent":{"adUserData":true,"adPersonalization":true}}'Notes
- Default consent is
{ adUserData: true, adPersonalization: true }— a settled purchase implies transaction consent. Override viaopts.consent. - Never pre-hash email or phone. Pass them raw.
- ACP webhooks do not include identity or currency. Match quality is weaker than a checkout-session event that has a buyer.
- The MCP tool only maps ACP checkout sessions and AP2 mandates, not ACP order webhooks. Use
fromAcpOrderWebhookin code for the async settle path.
Related
- conversions.track() — fan the mapped purchase to every configured platform
- Governance — gate an unsupervised agent before it fires
- MCP / agents guide