Developer brief

AVA Pay: a one-page brief for your developer

What this does

AI shopping agents are arriving at storefronts, and most sites cannot tell one from a scraper, so they either block everything or trust everything. AVA Pay verifies the cryptographic signature an agent carries and returns a single verdict your code can branch on. You decide what a verified agent earns.

It replaces nothing. It sits in front of the decision you are already making about automated traffic.

On Shopify

If your store is on Shopify, skip the rest of this page. Install AVA Pay from the Shopify App Store: apps.shopify.com/ava-pay. It is free.

The app runs the same verifier described below through Shopify's App Proxy, so there is nothing to host and nothing to code. What you get in the admin:

What leaves your store: the signed agent request's method, URL, and headers, with cookies, authorization, and session headers stripped by the app before forwarding. No customer data, no order contents. The scopes the app asks for are the ones it needs for the features above: orders for attribution, discounts for minting, and the online store surface for the optional banner. The listing's data-access panel is the authoritative list, and it shows two more entries that come with those scopes rather than from anything the app collects: "device and activity data" under customer data, because Shopify's order and checkout records carry the buyer's IP address and browser details and the orders scope exposes them, and "store owner," because every installed app can read the store's contact record. The app reads the discount code and the verified marker from those records for attribution and stores no IP address, no browser details, and nothing about the owner beyond which store it is talking to.

The app fails closed. If the verification service is unreachable, agents are not admitted, the visit is recorded as could not check, and nothing changes for human shoppers.

Support: help@avalayer.com. Source: github.com/AVA-PAY/ava-pay (MIT).

The whole integration

One POST, server side, from wherever you handle incoming storefront requests.

POST https://pay.avalayer.com/verify
Content-Type: application/json

{
  "method": "GET",
  "url": "https://yourstore.com/products/widget",
  "headers": { "signature": "...", "signature-input": "...", "signature-agent": "..." },
  "body": null
}

Forward the incoming request's method, its full URL, and its headers. Strip cookie, authorization, and any session headers before forwarding; the verifier needs the signature headers and nothing private. Include the raw body only if the request had one.

What comes back

A discriminated union on trusted, plus one flag that keeps you honest. The branch worth writing is three-way:

const r = await verify(incoming);

if (r.trusted) {
  // r.agent.id        e.g. "https://chatgpt.com"
  // r.agent.binding   "domain" (origin-bound key discovery) | "url-only"
  // r.protocol        "web-bot-auth" | "visa-tap" | "ava-tap" | "ap2"
  // r.mandate         present ONLY for payment protocols
  // r.ttlSeconds      how long you may cache this decision
} else if (r.conclusive === false) {
  // We could NOT complete the check (e.g. a key directory was unreachable).
  // This is an infrastructure condition, not a judgment about the agent.
  // r.reason tells you what we could not do, e.g. "directory_unavailable".
} else {
  // We checked, and this request failed verification.
  // r.reason          a typed string, not prose
  // r.message         human-readable detail, safe to log
}

conclusive is true whenever the verdict is a real determination, on both branches. If the field is absent (older cached responses), read it as true. The distinction matters because "we could not look" and "we looked and found nothing" deserve different logs, different alerts, and eventually different retries, even though both fail closed today.

binding is a trust grade on the identity itself: "domain" means the key was discovered through an origin-bound path a domain operator stands behind; "url-only" means key continuity at an arbitrary URL with no origin claim. Treat them the same to start; the field exists so your policy can price the difference when you are ready.

The one rule that matters

A verified identity is not spending authority. r.agent tells you which operator sent the request. It does not tell you that a customer authorized a purchase. Only a mandate does that, and mandates arrive only on the payment protocols.

So: admit verified agents, log them, personalize for them. Do not unlock discounts, offers, or checkout on identity alone unless you have decided that is your policy. Treating trusted: true as permission to spend is the one mistake that matters, and it is easy to make because it reads like a yes.

Failure handling

Fail closed. If the call errors, times out, or returns trusted: false, treat the request as unverified and fall back to whatever you do today. Never let a verification outage become an open door, and never let it become a wall either: unverified is your current normal, not a block.

reason values are stable typed strings, so branch on them if you want different handling for, say, replay_detected versus an expired signature. And route conclusive: false to your operational logs rather than your security logs: it is telling you about the world, not about the visitor.

Practical notes

What to ask us

Free while in developer preview. If your platform is BigCommerce, Magento, or custom, this brief is the whole integration path today; a native app for your platform does not exist yet, and we would rather say so than imply otherwise.

Questions to help@avalayer.com. A developer's questions get a developer's answer, usually same day.