Developer docs
Customer context
Bring each customer's orders, subscriptions, and licenses into the conversation sidebar — live from your store (EDD, WooCommerce, FluentCart, Shopify) or any custom app.
Customer context pulls a customer’s commerce history — orders, subscriptions, licenses, sites, and payment method — into the conversation sidebar, fetched live from your store when an agent opens the ticket. No tab-switching, no stale copies.
Supported platforms
| Platform | How | Status |
|---|---|---|
| Easy Digital Downloads | ReplyPlex WordPress plugin | Live |
| WooCommerce | ReplyPlex WordPress plugin | Live |
| FluentCart | ReplyPlex WordPress plugin | Live (pending live-store smoke test) |
| Custom app (pull) | Your own endpoint serving the context contract | Live |
| Custom app (push) | PUT /public/v1/customers/{email}/context — no endpoint needed | Live |
| Shopify | Hosted OAuth + Admin API | Coming |
WordPress carts (EDD / Woo / FluentCart) are served by one companion plugin that detects which carts are installed and returns already-normalized data — so adding Woo or FluentCart is plugin work, not a new ReplyPlex integration.
How it works
- Per-org connections — one set of store connections is shared across all the org’s inboxes.
- On-demand, nothing stored — context is fetched when the sidebar opens (
GET /v1/conversations/:id/commerce), resolved in parallel across only the connected providers, with a short cache (~60s) and a per-call timeout (~15s). Each connection renders one card; a failing lookup is isolated to its own card. The one deliberate exception is pushed context, which is stored with a TTL and clearly captioned as pushed. - Read-only first — write-actions (refund / resend / cancel) are planned behind the same seam.
Connect a WordPress store
- Install the ReplyPlex plugin. Build the zip with
pnpm build:wp-plugin(→integrations/replyplex-wp/dist/replyplex.zip) and install it in WordPress. - Generate a key in WP-admin and copy your site URL.
- In ReplyPlex, go to Manage → Integrations, paste the site URL + key, and connect. ReplyPlex
calls
GET {site}/wp-json/replyplex/v1/pingwith the key to verify before saving.
The plugin contract
The companion plugin exposes a small, versioned REST API. ReplyPlex calls it server-side with the paired key:
GET {site}/wp-json/replyplex/v1/customer?email=<email>
Authorization: Bearer <apiKey>
The plugin does all per-cart mapping and returns the normalized shape:
{
"found": true,
"summary": { "name": "James Carter", "ordersCount": 12, "totalSpent": "1,234.00 USD" },
"carts": [
{
"provider": "edd",
"label": "Easy Digital Downloads",
"sections": [
{
"title": "Purchases",
"items": [
{
"title": "Pro License",
"subtitle": "#1234",
"status": "completed",
"amount": "99.00 USD",
"date": "2026-01-02",
"url": "https://site/wp-admin/edit.php?post=1234"
}
]
},
{ "title": "Licenses", "items": [] }
]
}
]
}
found: false (with empty carts) renders a “No customer found” state for that connection.
Connect a custom app
Your product doesn’t have to be a WordPress cart. Any service that can host two HTTPS endpoints can feed the sidebar — subscriptions, licenses, plans, account status, whatever your customers ask about.
- Serve the two endpoints below from a base URL of your choice.
- In ReplyPlex, go to Manage → Integrations → Connect a custom app and enter a label, the endpoint
URL, and the API key your endpoint checks. ReplyPlex calls
GET {base}/pingto verify before saving.
The contract
Both endpoints receive your key as a Bearer token — compare it in constant time and reply with JSON.
GET {base}/ping
Authorization: Bearer <apiKey>
Respond { "ok": true } when the key is valid.
GET {base}/customer?email=<urlencoded email>
Authorization: Bearer <apiKey>
Respond with the same normalized shape the WordPress plugin uses — cards and carts are both accepted
as the array key:
{
"found": true,
"summary": { "name": "James Carter", "ordersCount": 3, "totalSpent": "297.00 USD" },
"cards": [
{
"label": "My App",
"sections": [
{
"title": "Subscription",
"items": [
{
"title": "Agency plan",
"status": "active",
"amount": "99.00 USD / year",
"date": "2026-01-02",
"url": "https://admin.myapp.com/customers/123"
}
]
}
]
}
]
}
found: false(or an empty array) renders a “No customer found” state for that connection.- Sections that don’t match the shape are dropped individually — one malformed row never takes down the card.
- Items support the full shape from the plugin contract:
subtitle,status,amount,date,url, plusmeta(labeled key/value rows, optionally linked) andlinks(a labeled list, e.g. activated sites).
Push context (no endpoint required)
If your product can’t host a public endpoint, push the cards instead: send them to the developer API and they render in the sidebar beside live store lookups — captioned “Pushed <time>” so agents always know they’re looking at stored context, not a live lookup. Pushed cards feed AI drafting the same way pull cards do.
Requires an API key with the write scope (see Authentication). The email in the path is URL-encoded.
PUT /public/v1/customers/{email}/context
Authorization: Bearer rp_live_…
Content-Type: application/json
{
"source": "Templately",
"ttlSeconds": 604800,
"cards": [
{
"label": "Subscription",
"summary": "Pro — annual",
"sections": [
{ "title": "Plan", "items": [ { "title": "Pro", "status": "active" } ] }
]
}
]
}
sourceis your product’s name — it attributes the cards and is the replacement key: each PUT replaces that source’s cards for that customer, so push the full current set each time.- TTL:
ttlSecondsis optional — default 7 days, maximum 30 days. Cards disappear at the returnedexpiresAtunless refreshed; push on change or on a timer. - Limits: at most 10 cards and 32 KB serialized per push.
sections/itemsuse the same shapes as the pull contract above. - Response:
{ "ok": true, "expiresAt": "…" }.
To remove your cards immediately (uninstall, disconnect):
DELETE /public/v1/customers/{email}/context?source=Templately
Authorization: Bearer rp_live_…
Idempotent — deleting an already-clear slice succeeds.
Security
- Credentials encrypted at rest (AES-256-GCM, key from
INTEGRATIONS_SECRET); never returned to the client, redacted from logs. - Outbound calls are server-side only, https-only, SSRF-guarded (private/loopback hosts rejected), time-bounded (~15s), and cached (~60s).
- Per-org isolation; pairing verified via
/pingbefore the connection is saved.