POP_ALLOWED_CATEGORIES is your agent's spending policy — set it once, and your agent
operates autonomously within it without asking for per-transaction approval.
This cookbook explains how matching works, common patterns, and the tradeoffs you need
to understand before choosing a configuration strategy.
When your agent calls request_virtual_card or request_purchaser_info, Point One Percent
runs two independent checks before approving any payment:
The agent-provided target_vendor string is matched against your POP_ALLOWED_CATEGORIES
list. This is lexical (token-based), not semantic. The system does not know that
"Maker Faire" is a type of "event" — you have to tell it.
Three sub-checks run in order, first match wins:
| Sub-check | Example | Passes? |
|---|---|---|
| Exact match | "aws" in ["aws"] |
Yes |
| Token match | "AWS" → token "aws" in ["aws"] |
Yes |
| Token-subset | "Maker Faire Bay Area 2026" tokens ⊇ "Maker Faire" tokens |
Yes |
What does NOT work: semantic labels like
"Event"or"SaaS"will only match if those exact words appear in the vendor name the agent sends."Event"does NOT match"Maker Faire Bay Area 2026"because "event" is not a token in that string.
After the policy gate passes, Point One Percent checks that the browser's current page domain actually belongs to the approved vendor. This prevents a compromised or misdirected agent from spending money on a lookalike site.
For known vendors (AWS, Cloudflare, GitHub, OpenAI, Stripe, Wikipedia, etc.),
strict domain suffix matching is used — wikipedia.attacker.com will never satisfy
vendor "wikipedia".
For unknown vendors, a fallback runs: vendor name tokens are matched against domain labels,
including substring matching for compound names (e.g. "maker" found inside "makerfaire.com").
Add the brand's most recognizable token — the part that actually appears in its domain.
POP_ALLOWED_CATEGORIES='["aws", "cloudflare", "openai", "github", "stripe", "Wikipedia", "Maker Faire", "PyCon"]'How to pick the right token:
- Find the vendor's official domain (e.g.
makerfaire.com) - Extract the meaningful part, ignoring TLD:
makerfaire - Pick the token from your intended vendor name that is a substring of that domain label:
Maker✓
Worked examples:
| You want to buy | Add to categories | Official domain | Why it works |
|---|---|---|---|
| Maker Faire Bay Area tickets | "Maker Faire" |
makerfaire.com |
"maker" ⊂ "makerfaire" |
| PyCon Taiwan registration | "PyCon" |
tw.pycon.org |
"pycon" = domain label |
| AWS compute | "aws" |
amazonaws.com |
Known vendor, strict match |
| Wikipedia donation | "Wikipedia" |
wikipedia.org |
Known vendor, strict match |
| Stripe Atlas | "Stripe" |
stripe.com |
Known vendor, strict match |
| DigitalOcean droplet | "DigitalOcean" |
digitalocean.com |
Known vendor, strict match |
Semantic labels like "Event", "donation", or "SaaS" only work if the agent
passes that exact word as (part of) the target_vendor argument — which it typically
will not do for a real vendor name.
When semantic labels DO work:
"donation" works well in practice because agents often call:
target_vendor="Wikipedia donation" → token "donation" matches category "donation"
"Event" works only if the agent passes something like target_vendor="Event ticket".
For real event names like "Maker Faire Bay Area 2026", it does nothing.
Implication: Do not rely on broad labels to cover an entire class of vendors. Use them only as a supplement for cases where the agent actually uses that word.
If you are testing and want to skip vendor restrictions temporarily, you can
set POP_ALLOWED_CATEGORIES very broadly and also disable the domain guard:
POP_ALLOWED_CATEGORIES='["*"]'
POP_TOCTOU_DISABLED=trueWarning: This removes both the policy gate and the domain binding. Your agent can spend on any vendor on any page. Only use this in a sandboxed test environment with a low
POP_MAX_PER_TXlimit.
POP_ALLOWED_CATEGORIES='["aws", "cloudflare", "github", "openai", "anthropic", "vercel", "netlify", "digitalocean"]'
POP_MAX_PER_TX=200.0
POP_MAX_DAILY=500.0POP_ALLOWED_CATEGORIES='["Maker Faire", "PyCon", "DEF CON", "Black Hat", "NeurIPS", "WWDC"]'
POP_MAX_PER_TX=500.0
POP_MAX_DAILY=1000.0POP_ALLOWED_CATEGORIES='["Wikipedia", "Wikimedia", "donation", "Internet Archive", "EFF"]'
POP_MAX_PER_TX=50.0
POP_MAX_DAILY=100.0POP_ALLOWED_CATEGORIES='["aws", "cloudflare", "openai", "github", "donation", "Wikipedia", "Wikimedia", "Maker Faire", "Event"]'
POP_MAX_PER_TX=100.0
POP_MAX_DAILY=500.0Many vendors outsource their checkout to a third-party payment processor. When this happens, the final payment page domain will not match the vendor's own domain — which would normally trigger a TOCTOU block.
pop-pay ships with a built-in allowlist of known-safe processors. If a checkout page redirects to any of these domains, the domain guard passes automatically (the vendor intent was already verified by the policy gate):
| Processor | Domain | Used by |
|---|---|---|
| Stripe | stripe.com |
Countless SaaS, e-commerce |
| Stripe Elements | js.stripe.com |
Embedded card forms |
| Zoho Payments | zohosecurepay.com |
Maker Faire, Zoho Commerce merchants |
| Square | squareup.com, square.com |
Retail, events |
| PayPal | paypal.com |
General |
| Braintree | braintreegateway.com |
PayPal subsidiary |
| Adyen | adyen.com |
Enterprise e-commerce |
| Checkout.com | checkout.com |
Enterprise |
| Paddle | paddle.com |
SaaS subscriptions |
| FastSpring | fastspring.com |
Software / digital goods |
| Gumroad | gumroad.com |
Creators, digital products |
| Recurly | recurly.com |
Subscription billing |
| Chargebee | chargebee.com |
Subscription billing |
| Eventbrite | eventbrite.com |
Events & ticketing |
| Tito | ti.to |
Tech conferences (RailsConf, etc.) |
| Luma | lu.ma |
Tech meetups & events |
| Universe | universe.com |
Ticketing |
| 2Checkout | 2checkout.com |
Software & digital |
| Authorize.net | authorize.net |
Hosted payment forms |
If your vendor uses a processor not on this list, add it to your .env:
POP_ALLOWED_PAYMENT_PROCESSORS='["checkout.mybank.com", "pay.myprocessor.io"]'Want to add a processor to the built-in list? Open a PR at
github.com/agentpayorg/project-aegis— include the processor name, domain, and one or two example vendors that use it.
Semantic categories are not supported without LLM guardrails.
The default keyword engine cannot infer that "Maker Faire" is an "Event".
If you need true semantic category enforcement (e.g. "approve any ticket purchase under $200"),
enable the LLM guardrail engine:
POP_GUARDRAIL_ENGINE=llm
POP_LLM_API_KEY=sk-...With the LLM engine enabled, the POP_ALLOWED_CATEGORIES list still acts as a fast
pre-filter, but the LLM layer can apply semantic reasoning for edge cases.
Multi-word categories require all tokens to appear in the vendor name.
"Maker Faire" matches "Maker Faire Bay Area 2026" (both tokens present).
"San Francisco Event" would require all three tokens — likely too specific to be useful.
Prefer the shortest unambiguous name fragment: "Maker Faire" not "Maker Faire Bay Area".
Adding a vendor does not guarantee injection will succeed. The domain guard is a second, independent layer. Even if your category list matches, injection will be blocked if the current page domain does not correspond to the approved vendor. This is intentional — it protects against prompt injection attacks that redirect your agent to a lookalike checkout page.
If a payment is blocked unexpectedly, check which layer rejected it:
-
Policy gate rejected → your
POP_ALLOWED_CATEGORIESdoes not match the vendor name the agent passed. Fix: add a token that appears in both the vendor name and the domain. -
Domain guard rejected → the page domain did not match the approved vendor. Fix: make sure the agent passes
page_urlto the tool, and confirm the official domain contains one of your category tokens as a substring. -
Budget rejected →
POP_MAX_PER_TXorPOP_MAX_DAILYexceeded. Fix: raise the limit in~/.config/pop-pay/.envand restart the MCP server.