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:
- Traffic: every agent visit with its protocol, the agent's identity, and one verdict in three states: verified, rejected, or could not check. A check we could not complete is never shown as a rejection.
- Settings: the default discount, the maximum, and the identity-only tier (what an agent earns when it proves who it is but carries no buyer mandate; the default is nothing).
- Policies: per-platform allow, challenge, or block rules with discount caps, exported and imported as JSON. A platform you have not written a rule for gets the most restrictive treatment of any rule you have. The same JSON loads into the WooCommerce plugin.
- Discount codes: minted one at a time, single use, only for agents your policy approves, and used to attribute the later order back to the agent platform.
- Storefront banner: optional, off until you enable it in the theme editor. It confirms a verified visit and the code your policy applied. Human shoppers never see it.
- A test button: Settings has "Send test agent visit," which signs a demo credential server side and runs it through the real pipeline, so you can see a verified row within a minute of installing. Test visits are labeled as such in Traffic and never counted as organic.
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
-
Verification is single-digit milliseconds; the response carries an
x-ava-verify-msheader if you want to watch it. -
ttlSecondslets you cache a verdict rather than verifying every asset request. Verify page and cart requests; skip images. -
The SDK is on npm as
@ava-pay/agent, version 0.3.0 or later. 0.3.0 carries security fixes to the RFC 9421 and Web Bot Auth parsers, see the package changelog. MIT licensed. You do not need it to call the API; it is there if you want typed helpers or to sign requests for testing. - Everything is open source at github.com/AVA-PAY/ava-pay, including the test suite, so you can read exactly what a verdict means before you trust one.
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.