Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Wix Voice AI — AI Voice Agent for Wix eCommerce | Telenow

Turn your Wix store events into automated AI phone calls — COD confirmation, abandoned cart recovery, order confirmation, delivery updates, win-back and instant lead callback — with every call's outcome written back onto the Wix order or contact.

License: MIT Platform: Wix Node Powered by Telenow

Wix Voice AI is a free, open-source Wix app that connects your Wix eCommerce / Wix Stores site to Telenow, an AI voice-agent service, so an AI voice agent automatically calls your shoppers at the moments that matter — order confirmation calls, COD confirmation to cut RTO (return-to-origin), abandoned cart recovery, delivery updates, win-back re-engagement, and instant lead callback (speed-to-lead) — in a natural, multilingual voice including Hindi and other Indian and global languages. Each store event (new order, fulfillment, new contact, abandoned checkout) becomes a real phone conversation, and the call outcome is written straight back onto the Wix order (timeline activity) or contact (label + note) so nothing lives in a spreadsheet. The app itself is free; it connects to Telenow with your own account and vai_live_… API key, and Telenow bills for call usage on its own platform (no charges go through Wix). Bring your existing Telenow agents, toggle the automations you want, and your store starts "talking to" your customers.

Free app — requires a Telenow account. The app is free to install. Telenow is a separate third-party voice-AI service that bills for call usage on its own platform; you connect it with your own vai_live_… API key (Telenow → Developers → API Keys). No charges go through Wix.

Table of Contents

✨ Features

Install once and Telenow places automated AI voice calls to your shoppers on store events, then writes the result back onto the Wix order or contact. Every automation is independently toggleable, with its own agent, call delay, quiet-hours window and filters.

  • 📞 Order confirmation calls — a warm thank-you / confirm-details call goes out the moment an order is placed (eCommerce Order Created), reducing address errors and support tickets. Outcome logged as an order activity.
  • 💰 COD confirmation & RTO reduction — every cash-on-delivery / pay-offline order gets a confirmation call before you fulfill, so you ship orders the customer actually wants. The result labels the contact telenow-cod-confirmed / telenow-cod-cancelled / telenow-cod-no-response and adds an order activity. (COD-cancelled does not auto-cancel the order — it only flags it for your review.)
  • 🛒 Abandoned cart recovery — when a checkout is abandoned (eCommerce Abandoned Checkout), Telenow can call the shopper with a friendly nudge, the recovery URL and an optional discount to bring the sale back (best-effort / plan-dependent — see below).
  • 🚚 Delivery & fulfillment updates — on Fulfillments Updated, the AI agent reads out the tracking number / URL and carrier so "where is my order?" gets answered before it becomes a complaint. Delivered / failed-delivery (NDR retry) are stubs you wire to your carrier/3PL.
  • ⚡ Instant lead callback (speed-to-lead) — every new Wix CRM Contact Created (site signup, lead form, contact box) triggers an instant AI callback while interest is high. New leads appear in the Recent leads view in the settings page with the call result and a contact note.
  • 🔁 Win-back / re-engagement — a scheduled sweep finds customers whose most recent order is older than N days and calls them back, with an optional discount code.
  • ✍️ Outcome write-back — results land where you work: an order activity (timeline note), a contact label for COD outcomes, and a contact note for lead callbacks.
  • 🌍 Natural multilingual voice — Telenow speaks in a natural AI voice across English, Hindi and other Indian languages plus global languages, and can look up an order, product or customer live during the call, so the conversation is genuinely two-way.
  • 🎛️ Per-automation controls — independent on/off toggle, Telenow agent ID, call delay, timezone-aware quiet-hours window, and per-store filters (e.g. extra COD payment-method matchers, minimum order value, discount code).
  • 🔒 Secure by design — RS256-JWT verification of Wix webhooks, HMAC-SHA256 verification of Telenow result webhooks, signed session-token auth for the settings API, E.164 phone normalization, secret/PII masking in logs, stable-id dedupe, and lead-PII purge on uninstall.

🚀 Installation

git clone https://github.com/TelenowAI/wix-voice-ai.git
cd wix-voice-ai
npm install
cp .env.example .env      # fill in the values below
npm start                 # or: npm run dev  (node --watch)

You need a public HTTPS URL (Wix and Telenow both call you). In dev use a tunnel:

ngrok http 3000           # then set HOST=https://<id>.ngrok-free.app in .env

Requirements: Node.js 18+ (uses the global fetch), a Wix Dev Center app, and a Telenow account with a vai_live_… API key.

⚙️ Configuration

Copy .env.example to .env and fill in the values:

Var Required Description
HOST Public HTTPS base URL of this app (no trailing slash). Builds the OAuth redirect URL and the Telenow webhook target.
PORT Listen port (default 3000).
WIX_APP_ID / WIX_APP_SECRET From your app in the Wix Dev Center → your app → OAuth (App ID + App Secret Key). The secret also signs the settings-UI session token.
WIX_PUBLIC_KEY ✅ (for webhooks) Your app's webhook public key (PEM), from the Wix Dev Center → your app → Webhooks (or View ID & Keys). Wraps multi-line — wrap in quotes and use \n, e.g. "-----BEGIN PUBLIC KEY-----\n…\n-----END PUBLIC KEY-----". If missing, the app boots but webhook verification is disabled (loud warning) and inbound webhooks are rejected with 503 so you can finish OAuth first.
TELENOW_API_BASE Telenow API base (default https://api.telenow.ai).
DATA_DIR Where the file store persists (default ./data).
DEFAULT_PHONE_COUNTRY ISO-2 country for E.164 normalization of local numbers (default IN).
SWEEP_INTERVAL_MS / SWEEP_RUN_ON_BOOT Win-back scheduler cadence (default 6h) / run the sweep once at boot for testing.

The Telenow API key is not an env var — each merchant pastes their own vai_live_… key in the settings page (/app), stored per instance.

🧩 How it works

Wix site ──webhook(signed JWT)──▶  this app  ──POST /api/sessions/initiate-call──▶  Telenow
   ▲                                  │   ▲                                              │
   └──REST API (order/contact)────────┘   └──────POST /telenow/webhook (HMAC) ◀──────────┘
                                                  (call.ended / call.analyzed)

A Wix store event arrives as a signed JWT webhook; the app verifies it, builds the call context, and asks Telenow to place an AI voice call. When the call ends, Telenow posts the result (with disposition / transcript) back to the app, which verifies the HMAC signature and writes the outcome onto the originating Wix order or contact.

Tech: Node 18+ (global fetch), Express, jsonwebtoken (to verify Wix's RS256 webhook JWTs). No DB — a file-based store stub you swap in production.

Wix Dev Center setup (one-time, per app)

  1. Create the app at dev.wix.comCreate New App. Copy the App ID and App Secret Key into WIX_APP_ID / WIX_APP_SECRET.
  2. OAuth → Redirect URL: set it to HOST/wix/oauth/callback. Set the App URL (what opens after install) to HOST/app (Wix appends context; the page also accepts ?instanceId=).
  3. Permissions: request Wix Stores / eCommerce — Read & Manage Orders and Wix CRM — Read & Manage Contacts (read orders/contacts, add order activities, add contact labels/notes).
  4. WebhooksCreate Webhook and point the Callback URL at HOST/webhooks/wix for each event:
    • eCommerce → Order Created (order confirmation + COD)
    • eCommerce → Order Approved (optional — fires when an online payment is received or a COD order is auto-approved)
    • eCommerce → Fulfillments Updated (delivery / tracking)
    • CRM → Contact Created (lead callback)
    • eCommerce → Abandoned Checkout (optional / plan-dependent — abandoned cart)
    • App → App Removed (so the app purges all data, including lead PII, on uninstall)
  5. Public key: copy the webhook public key shown on the Webhooks page (or View ID & Keys) into WIX_PUBLIC_KEY. This is what the app uses to verify every inbound webhook (RS256).

OAuth / install flow

  1. The merchant adds the app from the Wix App Market. Wix redirects to HOST/wix/oauth/callback?code=….
  2. The app exchanges the code at POST https://www.wixapis.com/oauth/access (grant_type:"authorization_code") → a long-lived refresh_token (+ a short-lived access_token). It persists the refresh_token per instance and captures the instanceId via GET https://www.wixapis.com/apps/v1/instance.
  3. Access tokens are minted on demand from the refresh token (grant_type:"refresh_token") and cached in memory. All Wix REST calls send Authorization: <access_token> (Wix uses the raw token, not Bearer).
  4. The app mints a short-lived signed session token for the now-verified instance and redirects the merchant to /app#t=… (the token rides in the URL fragment, never the query string or logs). There they paste their Telenow API key (validated via GET /api/v1/me), pick an agent ID per automation, toggle automations, and set delays/quiet-hours. Saving a new key (re)subscribes the Telenow result webhook via POST /api/v1/hooks.

Webhooks (inbound from Wix)

Wix delivers webhooks as a signed JWT in the raw request body, signed with your app's RSA key pair. We verify and decode it exactly as Wix documents:

const rawPayload = jwt.verify(body, WIX_PUBLIC_KEY, { algorithms: ['RS256'] });
const event      = JSON.parse(rawPayload.data);  // { eventType, instanceId, data }
const eventData  = JSON.parse(event.data);        // the entity event

We dispatch on event.eventType (wix.ecom.v1.order_created, wix.ecom.v1.order_approved, wix.ecom.v1.fulfillments_updated, wix.contacts.v4.contact_created, wix.ecom.v1.checkout_abandoned, wix.app_market.v1.app_removed) and extract the affected entity from createdEvent.entity / updatedEvent.entity / actionEvent.body. We ACK Wix with 200 immediately and process automations in the background so retries never double up.

COD detection

A Wix order is treated as Cash-on-Delivery when its paymentStatus is unpaid (NOT_PAID / PENDING) and an offline/manual payment method is present, or when the payment method itself matches a COD/offline matcher. Add extra matcher substrings per-store via the COD confirmation automation's filters.codMethodsExtra.

Lead / contact mapping

A CRM Contact Created is the lead signal: name from info.name.first/last, email from info.emails.items[], phone from info.phones.items[] (normalized to E.164). The lead is stored first (so it shows in Recent leads even if the call is skipped), then an instant callback is placed, then the row is patched with the result.

Automations & the data written back

Each automation builds a variables object and calls POST /api/sessions/initiate-call (note: not /api/v1) with the agent, the E.164 number, an identifier (order:<id> / lead:<id> / cart:<id> / customer:<id>), and machineDetection:"hangup". The response is enveloped — we read data.sessionId. We persist sessionId → entity so the result webhook can find the order/lead.

Automation Wix trigger Variables passed to the agent Write-back on result
Order confirmation Order Created customer_name, order_number, order_items, order_total, payment_status order activity
COD confirmation Order Created (COD only) customer_name, order_number, order_items, order_total, payment_method, shipping_city contact label telenow-cod-confirmed / -cancelled / -no-response + order activity
Fulfillment / delivery Fulfillments Updated customer_name, order_number, tracking_number, tracking_url, carrier order activity
Lead callback Contact Created customer_name, email, source, labels lead row updated + contact note
Win-back scheduled customer_name, days_since_last_order, discount_code? logged / activity when an order id is known
Abandoned cart Abandoned Checkout (best-effort) customer_name, cart_items, cart_total, recovery_url, discount_code? logged (no order yet)

COD cancelled does NOT auto-cancel the order — we only label + note it so the merchant reviews first. There's a clearly-marked TODO in src/webhooks/telenow.js to optionally cancel via the eCommerce Orders API.

Telenow webhooks (inbound from Telenow)

We subscribe with POST /api/v1/hooks (events: ["call.ended","call.analyzed"], source: "wix", includeTranscript: true) and store the returned signing secret per instance. Telenow then POSTs results to HOST/telenow/webhook with:

X-VoiceAI-Signature: sha256=<hex HMAC-SHA256 of the raw body>
X-VoiceAI-Event:     call.ended | call.analyzed
X-VoiceAI-Delivery:  <uuid>

We verify by recomputing the HMAC over the raw body with that secret (constant-time compare; hex canonical, base64 fallback), resolve the entity via the persisted sessionId (falling back to the identifier), and — lead branch first — patch the lead row, else write back to the order. COD confirmed-vs-cancelled is read from analysis.disposition (with a summary/transcript keyword fallback).

Security notes

  • Three verification schemes. Inbound Wix webhooks are verified as an RS256 JWT against WIX_PUBLIC_KEY; inbound Telenow webhooks are verified as a hex HMAC-SHA256 against the per-hook signing secret; the settings API (/api/*) is authenticated with a signed session token (HMAC-SHA256 over <instanceId>.<exp>, keyed by WIX_APP_SECRET), minted only at the verified OAuth entry point and sent as Authorization: Bearer <t>. The non-secret ?instanceId= query is never trusted to authenticate data routes (that would be an IDOR exposing another tenant's leads/key). Both webhook routes receive the raw body (mounted before the JSON parser) so the bytes/signature match exactly. Bad signatures get 401.
  • E.164 normalization. Phone numbers from Wix orders (billingInfo.contactDetails.phone) and contacts (info.phones.items[]) are normalized to E.164 before dialing (src/util/phone.js); un-normalizable numbers are skipped, never dialed.
  • Never log the API key or full phone. The Telenow X-API-Key and Wix tokens are never logged or sent to the browser — the settings page only ever sees a masked key hint. Phone numbers are masked in logs and in the leads table.
  • Dedupe on stable Wix ids. We dedupe per (automation, instance, STABLE entity id) — order id / contact id — not a freshly-generated local id, so webhook redeliveries never double-call. The mark is released if placement fails so a genuine retry can go through.
  • Lead PII is purged on uninstall. Lead rows hold name/email/phone; the App Removed webhook calls deleteInstance(), which purges instances, settings, hooks, call map, dedupe attempts and leads.
  • Quiet hours. Calls are suppressed inside each automation's local quiet-hours window (re-checked at fire time for delayed calls).

Production checklist

  • Swap the file store for a real DB. src/store.js is an in-memory + JSON-file stub (no locking, last-write-wins, single-process). Move instances/settings/callMap/hooks/attempts/leads to Postgres/MySQL/DynamoDB. The logical stores are documented at the top of the file.
  • Durable scheduling for delays + sweeps. Delayed calls use setTimeout and win-back uses setInterval — neither survives a restart or scales across instances. Use a job queue (BullMQ/Redis, SQS) or node-cron with a leader lock. (Marked TODO in _base.js and winBack.js.)
  • Shared access-token cache. src/wix.js caches minted access tokens in process memory. For multi-instance prod, move it to a shared store (Redis) or mint per request.
  • Host on HTTPS with a stable HOST. Update the OAuth redirect URL + webhook callback URLs in the Dev Center if it changes.
  • Confirm the abandoned-checkout webhook is available for the target site plans and verify the payload field names; src/automations/abandonedCart.js is wired best-effort behind a toggle.
  • Verify delivered / failed-delivery by wiring a carrier/3PL webhook (Shippo, AfterShip, Delhivery, Shiprocket…) — stubs in src/automations/orderUpdates.js.
  • Replace the win-back last-order scan. winBack.js pages the Orders search and keeps the most-recent order per buyer; for large stores maintain a true last_order_at index from the Order Created webhook.
  • Per-customer call frequency caps / suppression list (don't call the same shopper repeatedly across automations).
  • Data-subject requests. Lead PII is purged on uninstall; for in-life GDPR/erasure also forward redaction to Telenow for the actual voice recordings/transcripts.

Local round-trip test

A self-contained harness proves the entire integration chain end-to-end on your machine — no real Wix site, no real Telenow backend, and no hosting required. It drives the app's real modules (the wired Express app, the Wix RS256-JWT webhook verifier, placeCall, the Telenow client, the result-webhook receiver and the lead store), with an in-process mock Telenow API.

npm run roundtrip

What it exercises (the lead-callback path, whose write-back is to the app's own lead store, so no Wix REST API is needed):

  1. Generates a throwaway RSA key pair, sets WIX_PUBLIC_KEY to the public key, and seeds an installed instance + Telenow hook + settings directly via store.js (bypassing OAuth), enabling the leadCallback automation with an agent id and a Telenow API key.
  2. POSTs a real RS256-signed JWT to /webhooks/wix (signed with the matching private key), shaped as Wix delivers it — the double-encoded envelope for a wix.contacts.v4.contact_created event with a contact phone — which the app's own jwt.verify accepts.
  3. Asserts the mock Telenow received an initiate-call with the expected E.164 number and a lead:<id> identifier, and that a lead row was stored, linked to the Wix contact id, and moved to placed.
  4. Fires a call.analyzed result webhook back at /telenow/webhook, signed with the mock's hook secret (X-VoiceAI-Signature: sha256=<hex>), and asserts the lead is updated to completed / disposition confirmed.
  5. Asserts a result webhook with a wrong signature is rejected with 401 and leaves the lead unchanged.

One step is not exercised locally: when the result is resolved via the session→call map, the lead branch makes a best-effort Wix REST call (addContactNote) to note the outcome on the underlying contact. To keep the harness fully offline, the result webhook is resolved via the lead:<id> identifier instead (same signature check + same updateLead write-back, zero outbound calls), so that contact-note write-back to the live Wix API is the only part of the chain not driven here. Everything else runs end-to-end in-process.

It prints PASS/FAIL per check and exits non-zero on any failure. It uses a throwaway temp DATA_DIR (removed on exit) and dummy credentials, so it needs no real keys and touches no network. Test files live in test/ (test/mock-telenow.mjs, test/roundtrip.mjs).

Project layout

src/
  server.js              Express app: routers, body parsers, settings API, scheduler
  wix.js                 Wix OAuth + token refresh + REST client + instance lookup + write-back
  auth.js                OAuth /wix/oauth/callback; persists refresh token, subscribes Telenow hook
  session.js             Signed session tokens (HMAC) for the settings-UI /api/* auth
  telenow.js             Telenow API client (me, initiateCall, createHook, listHooks, deleteHook)
  settings.js            Per-instance settings model + defaults + redaction
  store.js               Persistence STUB (file JSON) — instances/settings/callMap/hooks/attempts/leads
  webhooks/
    wix.js               Wix webhook receiver (RS256 JWT) + dispatch + app-removed cleanup
    telenow.js           Telenow result receiver (hex HMAC) + write-back (lead branch first) + hook lifecycle
  automations/
    _base.js             placeCall(): gating (enabled/key/agent/quiet/delay) + stable-id dedupe + dial + map
    orderConfirmation.js codConfirmation.js orderUpdates.js leadCallback.js winBack.js abandonedCart.js
  util/
    phone.js             E.164 normalization (Wix order/contact shapes)
    quietHours.js        timezone-aware quiet-hours check
  public/app.html        embedded settings UI + Recent-leads card + legal footer

📞 About Telenow

Telenow is an AI voice-agent platform that places and answers natural, multilingual phone calls — English, Hindi and other Indian and global languages — for eCommerce and customer operations. Build an agent once, then drive it from your store events to confirm orders, cut COD returns, recover carts, send delivery updates, and call back leads, with full transcripts and call analysis. Learn more at telenow.ai, read the documentation, or see pricing.

📄 License

Released under the MIT License.

About

Free open-source Wix eCommerce app connecting your store to Telenow voice AI: AI voice agent calls for COD confirmation, RTO reduction, abandoned cart recovery, order confirmation, delivery updates, win-back & instant lead callback — outcomes written back to the Wix order/contact. Multilingual incl. Hindi.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages