Expresso Store AI
Let AI shopping agents browse your CartThrob store and send buyers straight to your own checkout.
Buy NowAgentic commerce for Expresso Store 6 on ExpressionEngine 7. Current release: 1.0.0-rc.1 (release candidate) · Add-on slug: es_ai · By tripleNERDscore, LLC
Expresso Store AI lets AI agents shop your Store catalog and lets your own merchant agents help run the store, without handing payment or store control to a third party.
- Buyer agents can search products, build carts, prepare checkouts and read the orders they placed. They connect over UCP REST, MCP or A2A.
- Shoppers always pay on your site. When an agent completes a checkout, the shopper gets a secure link to your native Store checkout. Your existing gateways, shipping and tax rules handle the rest.
- Merchant agents get 25 scoped tools for products, variants, inventory, promotions, orders, fulfillment, refunds and reports. Every tool is covered by policies, approvals, an audit trail and snapshot revert where it applies.
- Order webhooks send signed, encrypted, retried status, fulfillment and refund updates to the platform that placed the order.
- Natural-language shopping is optional. It is off by default and supports Anthropic or OpenAI-compatible providers.
- Licensing works offline. It never contacts a license server, and license status never interrupts checkout.
Contents#
- How it works
- Requirements
- Installation
- Web server routing
- Configuration walkthrough
- Endpoints
- Buyer integration guide
- Checkout handoff
- Orders and webhooks
- Merchant agents
- Approvals, activity and revert
- Control panel reference
- Scheduled maintenance and CLI
- Optional natural-language shopping
- Licensing
- Security
- Privacy and data retention
- Troubleshooting
- Limitations and scope
- Upgrading and uninstalling
- Changelog
- Support
How it works#
Buyer agent ──REST / MCP / A2A──▶ Expresso Store AI ──native pricing──▶ Store 6
(platform) │ catalog index (rolled back,
│ carts & checkouts no shadow orders)
│
└─ complete ─▶ secure handoff link ─▶ Shopper pays in
your native checkout
│
Platform webhook ◀── signed order events (outbox) ◀── order linked ◀───────────┘
Merchant agent ──MCP / A2A──▶ scoped tools ──▶ policy ──▶ auto / approval / deny ──▶ native write + audit
- Catalog. You choose which channels are exposed. The add-on keeps a public index of visible products, prices and variants, and reconciles it on a schedule.
- Pricing. Cart and checkout quotes run through Store's own pricing, tax and shipping inside a database transaction that is always rolled back. No persistent shadow orders are created, listed or reported.
- Payment. Agents never submit payment credentials. Completing a checkout returns a browser handoff link. The shopper finishes and pays in your normal Store checkout.
- Orders. When a handed-off checkout becomes a native order, the add-on links it to the originating platform. It then delivers signed order events through a durable outbox.
- Merchant operations. Merchant tools run as a configured EE service member, so native revisions and authorship are real. Each call is checked against client scopes and policy before anything changes.
Normal human checkout is not affected. It creates no agent links and sends no webhooks.
Requirements#
| Component | Requirement |
|---|---|
| PHP | 64-bit PHP 8.1 or later (validated on 8.1 and 8.3) |
| ExpressionEngine | EE 7 (validated on 7.5.26) |
| Store | Expresso Store 6 (validated on 6.0.2) |
| Database | MySQL/MariaDB with InnoDB for native Store and add-on tables (validated on MariaDB 11.4) |
| PHP extensions | bcmath, sodium, openssl, curl, mbstring, json, pdo_mysql, plus everything EE/Store need |
| Web server | Apache or Nginx (both validated), with HTTPS and TLS 1.3 on a canonical, publicly reachable origin |
| EE | A working EE encryption key and a writable EE file cache |
| Cron | Ability to run eecli.php commands on a schedule |
Store configuration constraints
- The Store currency's display decimals must equal the ISO currency exponent. For example, USD needs 2 and JPY needs 0.
- Each site supports one currency and guest pricing. Member-only offers and per-customer-limited discounts are not exposed to agents.
Coexistence: only one commerce AI dispatcher can own the /.well-known/ucp and agent card paths. If another commerce AI add-on is installed, such as CartThrob AI on a CartThrob site, disable it before you activate this add-on. The installer detects this conflict from registrations alone and does not touch the other add-on.
The installer runs a preflight check and reports any missing capability before it changes anything.
Installation#
Back up first. Back up the site database, the
system/user/addonsdirectory and your EE encryption key before you install or update. Encrypted add-on data cannot be recovered without that key.
- Verify the download. Check the archive against its published checksum:
sha256sum -c es_ai-1.0.0-rc.1.zip.sha256 - Extract the archive into your site root, keeping the path
system/user/addons/es_ai. The archive already contains the prefixed, production-ready dependencies, so don't run Composer inside the add-on. - Install in the control panel: Developer → Add-ons → Expresso Store AI → Install. The installer creates the add-on's 19 tables and registers its native Store hooks.
- Add trusted configuration to
system/user/config/config.php:// Canonical public HTTPS origin, with trailing slash. $config['es_ai_public_origin'] = 'https://shop.example/'; // Base path for protocol routes. Must match CP → Runtime settings. $config['es_ai_base_path'] = '/ucp'; - Route the discovery and protocol paths to EE. See Web server routing.
- Configure the add-on by following the configuration walkthrough.
Safe defaults after install: all transports are off, checkout handoff is off and merchant agent writes are paused. Nothing is exposed until you turn it on.
Web server routing#
Send these paths to EE's front controller (index.php):
/.well-known/ucp/.well-known/agent-card.json/ucp/*, or your custom base path
These requests must arrive unchanged:
- Keep the original HTTP method, URI and raw body.
- Pass through the
Authorization,X-API-Key,UCP-Agent,Signature,Signature-Input,Content-Digest,Request-IdandIdempotency-Keyheaders. - Exempt these paths from any HTML, login, caching or "maintenance mode" middleware.
- Allow request bodies of at least 2 MiB at the proxy. The add-on can then reject oversized requests with a structured JSON error instead of a proxy error page.
Apache
# If PHP runs as CGI/FastCGI, Authorization is otherwise stripped
CGIPassAuth On
RewriteEngine On
RewriteRule ^\.well-known/(ucp|agent-card\.json)$ index.php [L,QSA]
RewriteRule ^ucp(/.*)?$ index.php [L,QSA]
Nginx
location = /.well-known/ucp { try_files $uri /index.php$is_args$args; }
location = /.well-known/agent-card.json { try_files $uri /index.php$is_args$args; }
location ^~ /ucp/ { try_files $uri /index.php$is_args$args; }
client_max_body_size 2m;
Proxies and TLS: the add-on works out HTTPS and the hostname from the configured origin and the actual connection. It does not trust public X-Forwarded-* headers. Terminate TLS 1.3 in a way that EE sees as HTTPS, or keep the proxy on a trusted private hop.
Logs and analytics: handoff links (/ucp/continue/…) and invoice permalinks contain bearer tokens. Remove them from access logs, analytics, error trackers and support exports.
Configuration walkthrough#
All settings live under Add-ons → Expresso Store AI. Work through these steps in order and check the Dashboard after each one.
1. Catalog mapping#
On the Mapping page, choose which channels agents can see. For each channel, set:
- the assigned Store field
- the public HTTPS product URL pattern
- description and media fields
- visibility
- the physical or digital flag
Native SKU, price and stock come from Store directly and need no mapping. Then Reindex.
- Variation, Single-SKU and Text modifiers follow Store's native data.
- Text modifiers, such as engraving, are completed by the shopper on your site during checkout. Template
input:modifiers are not supported. Use real Store Text modifiers instead. - Guest-visible native sales apply. Exact stock counts stay hidden unless you enable them.
2. Checkout handoff#
On the Handoff page, set:
- Checkout URL: your existing Store checkout page, on the same origin.
- Privacy and Terms links, which are shown to agents and shoppers.
- Cart behavior: whether the agent cart replaces or merges with the shopper's existing Store cart.
- Attribution field (optional): an order custom field that records the originating platform.
- Invoice permalink: a path containing
ORDER_HASH. Leave it blank to inherit Store's Order Invoice Path.
Add the notice tag to your native checkout template:
{exp:es_ai:handoff_notice}
Run a test purchase through your checkout, then enable handoff.
3. Order mapping#
On the Orders page, map your Store statuses to shipped, delivered and canceled. Optionally map a status-message field and custom tracking fields. These mappings drive the order events sent to platforms.
Canceling an order only changes its status. It does not refund or restock.
4. Buyer platforms and access#
- Under Runtime settings, choose a buyer authentication mode:
- Platform signature or registered key (
signature_or_key, default) - Registered platform key only (
key_only)
- Platform signature or registered key (
- Under Buyer access, add platform-profile allow and deny lists and set separate rate capacities. Deny always wins.
- Register API clients for platforms that use keys. Keys look like
esai_live_…. Copy a new key immediately. Only a hash is stored, so a lost key can't be shown again.
5. Merchant agents#
- Under Agents, set a valid EE service member. Merchant writes run as this member, so revisions and authorship are real.
- Create merchant API clients with the minimum scopes they need, a policy and optional allowed IP ranges.
- Try tools in the Test console in preview mode first.
- When you're satisfied, resume agent writes. They start paused.
6. License, transports and health#
- Paste your license on the License page.
- Enable the transports you want (REST, MCP, A2A, orders) under Runtime settings.
- Confirm the Dashboard shows healthy discovery, TLS, cron freshness, payment and shipping methods.
- Set up the scheduled tasks.
Endpoints#
Paths assume the default base path /ucp.
| Surface | Method and path |
|---|---|
| UCP discovery (business profile) | GET /.well-known/ucp |
| A2A Agent Card | GET /.well-known/agent-card.json?version=0.3 or ?version=1.0 |
| Catalog search | POST /ucp/v1/catalog/search |
| Catalog lookup | POST /ucp/v1/catalog/lookup |
| Product detail | POST /ucp/v1/catalog/product |
| Create cart | POST /ucp/v1/carts |
| Read or update cart | GET / PUT /ucp/v1/carts/{id} |
| Cancel cart | POST /ucp/v1/carts/{id}/cancel |
| Create checkout | POST /ucp/v1/checkout-sessions |
| Read or update checkout | GET / PUT /ucp/v1/checkout-sessions/{id} |
| Complete checkout (returns handoff) | POST /ucp/v1/checkout-sessions/{id}/complete |
| Cancel checkout | POST /ucp/v1/checkout-sessions/{id}/cancel |
| Read owned order | GET /ucp/v1/orders/{id} |
| Buyer MCP | POST /ucp/mcp |
| Merchant MCP | POST /ucp/admin/mcp |
| A2A JSON-RPC (buyer and merchant) | POST /ucp/a2a |
| Shopper handoff (browser) | GET /ucp/continue/{token} |
Protocol versions
| Protocol | Supported versions |
|---|---|
| UCP | 2026-08-25 |
| MCP | 2026-07-28, plus the documented legacy revisions |
| A2A | 0.3.0 (default) and 1.0.0, each with its own wire format |
All buyer transports share the same services, so a cart created over REST behaves the same over MCP or A2A.
Buyer integration guide#
This section is for platform developers who connect a buyer agent to a store running Expresso Store AI.
Discovery#
Fetch GET /.well-known/ucp to get the business profile: capabilities, endpoints, policies and public signing keys. A2A clients fetch the versioned Agent Card instead.
Authentication#
Every buyer request carries a UCP-Agent header that names your canonical HTTPS platform profile. The store fetches and negotiates against that profile. Then authenticate in one of these ways:
- RFC 9421 HTTP message signature: the
Signature,Signature-InputandContent-Digestheaders, covering method, authority, path, query, required headers and a digest of the body. This works insignature_or_keymode. - Registered API key:
Authorization: Bearer esai_live_…orX-API-Key: esai_live_…. This works in both modes.
esai_test_… keys are rejected on production sites.
Required REST headers#
| Header | When | Format |
|---|---|---|
UCP-Agent | Every request | Platform profile reference |
Request-Id | Every request | UUID |
Idempotency-Key | Every mutation (create, update, complete, cancel) | UUID |
Content-Type: application/json | Requests with a body | — |
Query strings are not accepted on commerce endpoints. Reads must not send a body.
Idempotency and retries#
- Retry with the same
Idempotency-Keyand identical body. You get the stored result back and nothing runs twice. - Use a new key only for a genuinely new operation.
- If the store reports an operation as still in progress or its outcome as unknown, the reservation stays held so it can't repeat. Wait, then read the resource. Don't resend it under a new key.
Buyer tools (MCP and A2A)#
The buyer MCP server and A2A buyer skills cover the same operations as REST:
| Tool | Purpose |
|---|---|
search_catalog | Search visible products |
lookup_catalog | Look up products or variants by identifier |
get_product | Full product and variant detail |
create_cart, get_cart, update_cart, cancel_cart | Cart lifecycle |
create_checkout, get_checkout, update_checkout, cancel_checkout | Checkout lifecycle: addresses, fulfillment, totals |
complete_checkout | Finalize and receive the shopper handoff link |
get_order | Read an order your platform placed |
Ownership#
Carts, checkouts and orders belong to the platform profile and client identity that created them. A resource that belongs to someone else, has expired or doesn't exist gets the same "not found" response. This keeps resource IDs from leaking information.
Errors#
Errors are structured JSON with a stable code, for example https_required, invalid_idempotency_key, request_too_large, method_not_allowed, unsupported_media_type or capabilities_incompatible. Rate-limited responses include Retry-After.
Checkout handoff#
- The agent builds a cart and checkout and calls complete.
- The response contains a short-lived, single-purpose handoff link.
- The shopper opens the link. The add-on loads the agent's cart into the shopper's native Store cart, replacing or merging it as you configured, and redirects to your checkout URL.
{exp:es_ai:handoff_notice}shows the shopper a notice that an agent prepared the cart.- The shopper enters payment details and pays through your normal Store gateway.
- Store creates the native order. The add-on links it to the platform and queues an order event.
Details
- Handoff cookies are host-only,
Secure,HttpOnly,SameSite=Laxand last 15 minutes. Handoff responses areno-storeandno-referrer. - Expired or canceled checkouts can't be revived. The shopper sees an accessible expiry page.
- With Manual authorization, an order can be complete but still unpaid, and events report it as unpaid. A Manual purchase reports as paid.
- If a gateway callback was missed, reconcile it without charging again. See
es_ai:reconcile_payment.
Orders and webhooks#
- Platforms read their own orders with
GET /ucp/v1/orders/{id}orget_order. - Status, fulfillment and refund changes become order events. They're stored encrypted in an outbox and delivered to the platform's webhook, signed with your business signing key.
- Each event keeps a stable identity across retries. Delivery reserves an attempt before sending, so a crash can't cause duplicate sends.
- Webhook destinations must use public DNS, a trusted certificate and TLS 1.3. Private, metadata or redirecting destinations are refused.
- The order data sent to platforms includes items, totals, fulfillment and adjustments. It excludes buyer identity, addresses, free-text customization and gateway credentials.
Watch failed-delivery counts on the Dashboard. Don't reset attempt counters. Fix the destination and let the outbox retry.
Merchant agents#
Merchant agents connect to Merchant MCP (POST /ucp/admin/mcp) or A2A with a merchant API key. A platform signature never grants merchant authority. Extended Agent Cards list only the tools a client's scopes allow.
Tools#
| Tool | Scope | Writes | Default |
|---|---|---|---|
store_summary | reports:read | — | auto |
store_sales_report | reports:read | — | auto |
store_search_products | catalog:read | — | auto |
store_get_product | catalog:read | — | auto |
store_create_product | catalog:write | ✓ | auto (created with a non-open status) |
store_update_product | catalog:write | ✓ | auto |
store_set_product_status | catalog:write | ✓ | approval |
store_manage_variants | catalog:write | ✓ | auto |
store_set_price | pricing:write | ✓ | auto within threshold, otherwise approval |
store_get_inventory | inventory:read | — | auto |
store_list_low_stock | inventory:read | — | auto |
store_set_inventory | inventory:write | ✓ | auto |
store_adjust_inventory | inventory:write | ✓ | auto |
store_list_orders | orders:read | — | auto |
store_get_order | orders:read | — | auto |
store_update_order_status | orders:write | ✓ | auto (approval if it sends customer email) |
store_cancel_order | orders:write | ✓ | approval |
store_add_fulfillment | fulfillment:write | ✓ | auto |
store_refund_order | refunds:write | ✓ | always approval |
store_create_discount | discounts:write | ✓ | approval |
store_update_discount | discounts:write | ✓ | approval |
store_create_sale | pricing:write | ✓ | always approval |
store_update_sale | pricing:write | ✓ | always approval |
store_create_coupon | discounts:write | ✓ | Alias of store_create_discount |
store_update_coupon | discounts:write | ✓ | Alias of store_update_discount |
Write calls must include either:
"dry_run": true, which previews the real native before/after diff and the policy decision without changing anything, or"idempotency_key": "<uuid>", which performs a live write.
Money is always sent in integer minor units, for example price_minor: 1999 for 19.99. Percentages are decimal strings.
All tools return { ok, data, meta, errors }.
Policies#
Each merchant client has a policy (JSON). Policies can narrow site-wide limits but never widen them:
{
"tools": {
"store_set_price": "approval",
"store_adjust_inventory": "auto",
"store_cancel_order": "deny"
},
"allowed_channels": [1, 3],
"allowed_hours": [8, 9, 10, 11, 12, 13, 14, 15, 16, 17],
"price_change_percent": 10,
"refund_cap_minor": 5000,
"daily_refund_cap_minor": 20000,
"max_writes_per_hour": 60
}
| Key | Meaning |
|---|---|
tools | Sets a tool to auto, approval or deny. Read tools accept only auto or deny. |
allowed_channels | Channel IDs the client may touch |
allowed_hours | Hours of the day (0–23, UTC) when the client may act |
price_change_percent | Regular price changes within this percentage (0–100) run automatically. Larger changes wait for approval. |
refund_cap_minor | Maximum single refund, in minor units |
daily_refund_cap_minor | Maximum refunds per day, in minor units |
max_writes_per_hour | Write rate limit |
signature_required | true requires signed requests as well as the key |
profile_url | Binds the client to a signing profile URL |
Unknown keys are rejected.
- Refunds and sales always need approval, whatever the override says.
- Refund caps use exact minor units and add up across all clients. A client override can't raise them.
- Status changes that trigger native customer email default to approval, and the preview names the email templates.
The pause switch#
The global pause stops all agent writes, including approvals that are already queued. Human revert stays available while writes are paused.
Approvals, activity and revert#
Approvals. Each pending item shows the client, proposed arguments, before/after diff, the policy rule that triggered it and any email templates involved.
- Approve runs the operation immediately, after rechecking authorization, limits and fresh native state.
- If the product, transaction or template changed since the preview, the approval is stale. Ask the agent for a new preview.
- Reject records your note.
- Pending approvals expire after 72 hours by default.
- A2A clients see
input-requiredwhile an approval is pending, then the final result. - To approve refunds, a user also needs CP access to Store's Orders module.
Refunds
- Partial refunds are verified with Store's Dummy and Manual gateways. Manual only records the adjustment.
- Other gateways keep Store's native full-refund behavior until they're separately verified.
- If a refund's outcome is uncertain, check the native transaction in Store before trying again. Never retry a refund that might have run under a new idempotency key.
Activity. Filter the audit log by operation, client, result and transport.
Revert. Eligible product, inventory and promotion writes show Review revert. A revert restores the saved native snapshot only if the current data still matches what the agent left. It warns you about orders placed since the change. Revert can't undo refunds, sent customer emails, order status changes or external shipments.
Control panel reference#
| Page | What it's for |
|---|---|
| Dashboard | Health: Store and currency configuration, discovery, TLS, cron freshness, payment and shipping methods, failed notifications, alerts |
| Mapping | Catalog channels and fields; reindex and reconcile |
| Handoff | Checkout URL, legal links, cart behavior, attribution, invoice permalink |
| Orders | Status and tracking-field mapping |
| Buyer access | Platform allow and deny lists, rate capacities, platform API clients |
| Agents | Merchant clients, scopes, policies, IP ranges, service member, pause switch |
| Approvals | Pending agent operations |
| Activity | Audit log, revert review |
| Signing keys | Business signing key rotation with overlap |
| Test console | Preview or run buyer and merchant tools as a chosen client |
| Runtime settings | Transports, auth mode, base path, TTLs, retention |
| Language | Optional natural-language provider settings |
| License | Activate, check or remove your commercial license |
The Test console previews by default. Merchant previews show the real native diff and policy decision, while buyer previews only check the schema. It doesn't test network signatures or IP restrictions.
Scheduled maintenance and CLI#
Run these from the site root with the correct PHP binary and site ID:
| Schedule | Command | Purpose |
|---|---|---|
| Every minute | php system/ee/eecli.php es_ai:outbox --site=1 --limit=25 | Reconcile linked orders and deliver due webhooks |
| Every 5 minutes | php system/ee/eecli.php es_ai:reconcile --site-id=1 --batch-size=100 | Catalog reconciliation |
| Daily | php system/ee/eecli.php es_ai:prune --site=1 --limit=500 | Bounded retention cleanup |
| After mapping changes | php system/ee/eecli.php es_ai:reindex --site-id=1 --batch-size=100 | Full catalog rebuild |
| As needed | php system/ee/eecli.php es_ai:keys list --site=1 | Show public business signing keys |
| As needed | php system/ee/eecli.php es_ai:keys rotate --site=1 | Rotate signing keys (the old public key stays published during the grace period) |
| As needed | php system/ee/eecli.php es_ai:reconcile_payment --order=123 --site=1 [--refresh-profile] | Recover a missed native payment callback without charging |
Example crontab:
* * * * * cd /var/www/shop && php system/ee/eecli.php es_ai:outbox --site=1 --limit=25
*/5 * * * * cd /var/www/shop && php system/ee/eecli.php es_ai:reconcile --site-id=1 --batch-size=100
15 3 * * * cd /var/www/shop && php system/ee/eecli.php es_ai:prune --site=1 --limit=500
Notes
--ordertakes the native Store order primary key, not the order number.- Prune processes a bounded batch. Run it again to clear a large backlog. It never deletes native Store orders or payments.
es_ai:keysrotates webhook and business signing keys only. It has nothing to do with your license.
Optional natural-language shopping#
Off by default. When enabled, signed-in buyer traffic can send plain text, and the add-on turns it into validated buyer actions.
Configure under Language:
- Provider: Anthropic or any OpenAI-compatible Chat Completions endpoint
- Model name and API key. The key is stored encrypted.
- Output and context token budgets, plus a daily spend cap
- Current input and output prices in USD cents per million tokens. Round up. No model names or prices are built in.
- A system prompt with your shipping, returns and contact policies
Guardrails
- Only buyer tools are offered, and
complete_checkoutis never one of them. Merchant tools are never available. - Provider output must pass schema validation and the normal ownership checks. Prompts can't override the tool allowlist.
- Each call reserves its maximum cost against the daily budget first. Malformed output, timeouts and spent budgets fall back to structured instructions.
- Raw prompts, provider errors and keys are not logged.
Your responsibilities
- Shopper text is sent to the provider you chose. Say so in your privacy policy.
- Spend accounting uses the prices you enter. Set spending limits in your provider account as well if you need a guaranteed billing ceiling.
Licensing#
Expresso Store AI uses offline, Ed25519-signed licenses that cover es_ai only. A CartThrob AI license is separate.
Activate: paste your license key on the License page. The add-on checks:
- the issuer and signature
- the product
- the exact hostname
- the issue and expiry dates
- any local revocation list
The key is stored encrypted and never shown again.
What license status affects: a missing, expired, revoked or wrong-host license shows a status in the control panel. It never stops buyer checkout, merchant operations or order events.
Common situations
| Situation | What to do |
|---|---|
| Renewal | Activate the replacement key you receive. It keeps the same license ID with a new expiry. |
| Moving domains | Request a replacement key for the new hostname. Until you have it, status shows wrong_host. |
| Staging site | Licenses can list several exact production and staging hostnames. Ask when you purchase. |
| Removing a license | Use Remove on the License page. No commerce data is affected. |
No license server is contacted and no telemetry is sent.
Security#
Authentication and ownership
- Every buyer request is authenticated before anything changes. Ownership is tied to the platform profile plus the client identity.
- Merchant authority needs a merchant key. Platform signatures can't grant it.
- API keys are shown once and stored only as peppered hashes. You can restrict merchant keys by IP, using the real peer address.
- Signatures cover method, authority, path, query, required headers and the body digest. The add-on checks the raw body exactly as received.
Writes
- Every mutation reserves durable idempotency state before it acts. An interrupted operation stays reserved, so it can never turn into a repeat charge or repeat write.
- Merchant writes are serialized per resource. Native writes compare fresh snapshots under database locks.
- Approvals store arguments, diffs and notes encrypted, and recheck everything right before running.
Outbound requests (profile fetches, webhooks, language providers)
- Requests refuse URLs with embedded credentials, private and metadata addresses, unsafe DNS answers, redirects and proxies.
- Connections pin the resolved public address and verify the TLS certificate.
- Size, header and time limits apply to every request.
Money uses exact decimal strings and integer minor units with bcmath, never floats.
Control panel
- EE's native forms supply CSRF protection.
- Add-on admin permissions control access to configuration, secrets, approvals and audit records.
- Secret fields are never shown again, and sensitive pages are sent
no-store.
Your checklist
- ☐ TLS 1.3 on the public origin
- ☐ Authorization and signature headers reach EE unchanged
- ☐ Handoff and invoice URLs removed from logs and analytics
- ☐ EE encryption key backed up securely
- ☐ Merchant keys use minimum scopes, and IP ranges where possible
- ☐ Third-party shipping, payment and item extensions tested on staging. A rollback can't undo their external emails or network calls.
- ☐ Manual refund work in Store's CP coordinated with agents. The native CP doesn't take the add-on's locks.
Privacy and data retention#
What's stored (all scoped to the site):
- Catalog index: mapped, visible product content, prices and variants.
- Carts and checkouts: buyer identity, addresses, choices and priced snapshots. Encrypted with the EE key.
- Order links: native and public order IDs, platform and webhook destination, snapshot hash and sequence. Invoice URLs, event payloads and tracking are encrypted.
- Merchant audit: snapshots, arguments, diffs and notes. Encrypted, and visible to add-on admins.
- Secrets: API keys are stored as hashes. Provider keys and license tokens are encrypted settings.
- Logs: IDs and failure codes only. No secrets, raw prompts or raw gateway errors.
Default retention, configurable in Runtime settings and enforced by es_ai:prune:
| Data | Default |
|---|---|
| Finished private resources and delivered notifications | 30 days |
| Buyer audit records | 90 days |
| Merchant audit records | 730 days |
| Pending approvals | 72 hours |
After the retention period, old linked checkouts are minimized: personal and priced details are removed, but a small encrypted ownership record stays so order reads and order updates keep working. Unresolved payments, uncertain writes and undelivered notifications are kept for manual recovery. Retention is deliberately not a blind deletion deadline.
Your responsibilities: native Store orders follow your own retention policy. You're also responsible for legal disclosures, data-subject requests across providers and backups, and protecting the EE encryption key. Losing that key makes encrypted add-on data unrecoverable.
Troubleshooting#
| Symptom | Likely cause and fix |
|---|---|
/.well-known/ucp returns 404 or HTML | The path isn't routed to EE, or the transport is disabled. Check routing and Runtime settings. |
https_required | EE doesn't see the request as HTTPS. Fix TLS termination; forwarded headers aren't trusted. |
| Authentication fails with a valid key | The Authorization header is being stripped. On Apache CGI/FastCGI, add CGIPassAuth On. |
| Signature verification fails | A proxy changed the body, path or signed headers. Pass the request through unchanged. |
invalid_idempotency_key / invalid_request | Idempotency-Key or Request-Id is missing or isn't a UUID, or a query string was sent. |
request_too_large (or proxy 413) | Raise the proxy body limit to at least 2 MiB so the add-on can respond. |
| Products missing from search | The channel isn't mapped, the entry isn't visible or open, or a reindex is needed. |
| Prices differ from the storefront | The offer is member-only or per-customer-limited and isn't exposed to agents. |
| Handoff link says expired | The link or checkout passed its TTL. The agent should create a new checkout. |
| Order paid but platform not updated | Cron isn't running es_ai:outbox, or a payment callback was missed. Check cron freshness, then run es_ai:reconcile_payment. |
| Webhooks failing | The destination isn't public, uses an untrusted certificate, lacks TLS 1.3 or redirects. Fix it and let retries continue. |
| Merchant writes do nothing | Writes are paused, or the service member isn't set. Check the Agents page. |
| Approval marked stale | Native data changed after the preview. Ask the agent for a new dry run. |
| Install blocked by a dispatcher conflict | Another commerce AI add-on owns the discovery paths. Disable it first. |
License shows wrong_host | The hostname doesn't exactly match the license. Request a replacement key. |
Limitations and scope#
This is a release candidate. Not included in v1:
- Agent-completed payment or payment-capture tools. Shoppers always pay in the browser.
- Identity or account linking
- Multiple currencies, or member-specific pricing
- Server-Sent Events (SSE) and A2A push notifications
Verification status
- Validated locally on PHP 8.1 and 8.3 with Apache and Nginx, with independent UCP SDK, MCP Inspector and official A2A SDK clients.
- Deployed third-party platform acceptance is not yet verified. Confirm with a specific platform before claiming support for it.
- The official UCP conformance suite hasn't run successfully against this build. Independent SDK checks are separate evidence.
- Partial refunds are verified only for the Dummy and Manual gateways.
- Language-provider adapters are contract-tested but haven't been tested live against a merchant's own account.
- Accessibility has automated WCAG 2.2 AA checks on covered CP views. No manual audit has been done.
Upgrading and uninstalling#
Upgrade
- Back up the database, add-on files and EE encryption key.
- Replace
system/user/addons/es_aiwith the new release. - Run the update in Add-ons. Keys, settings and data are kept.
- Check the Dashboard and the changelog for any new settings.
Uninstall
- Export any audit or order-link records you need to keep.
- Uninstall through EE's normal add-on flow and confirm data removal.
This removes the add-on's tables and hooks. Native Store products, orders and payments are untouched. Pricing never created persistent shadow orders, so there's nothing extra to clean up. Remove the {exp:es_ai:handoff_notice} tag and the routing rules afterward.
Changelog#
1.0.0-rc.1#
- Buyer REST, MCP (current and legacy) and A2A 0.3 and 1.0 over shared UCP services; native browser handoff.
- Owned order reads; status, fulfillment and refund projection; signed, encrypted outbox with reconciliation.
- 25 scoped merchant tools with exact native writes, live policy checks, approvals and tasks, and snapshot revert.
- Operations control panel: health dashboard, client policies, buyer access rules, rate controls, signing-key rotation, retention and test console.
- Optional natural-language buyer layer with Anthropic and OpenAI-compatible adapters, encrypted credentials and spend reservations.
- Offline commercial license activation.
Support#
- Security issues: report privately through the support channel included with your purchase. Include your version and a redacted reproduction. Never send production API keys, encryption keys or buyer data.
- General support: use the support channel included with your purchase, or visit triplenerdscore.com.
© tripleNERDscore, LLC. Expresso Store AI is commercial software, licensed per hostname.