Wix connector
A standalone Node connector that streams a Wix site's content to the bAInquet ingestion API, mapping each Wix solution to the canonical IngestItem shape, signing every request with the bq.connector.hmac.v1 HMAC scheme, and POSTing idempotent items and batches.
Stable HMAC parity-tested: it vendors the SDK signer (src/bainquet-sign.ts, a self-contained copy proven byte-for-byte against the project-wide known-good vector).
What it maps
The SourceType enum has 13 fixed values; Wix solutions map onto them with no new enum values invented:
| Wix solution | type | Stable id | Notes |
|---|---|---|---|
| Pages | page | wixPage#<id> | excludes hidden / unpublished |
| Stores products | product | wixStores.product#<id> | variants plus typed price {value,unit,currency} |
| Bookings services | custom | wixBookings.service#<id> | attributes.subtype = "service" |
| Restaurants menu items | custom | wixRestaurants.item#<id> | attributes.subtype = "menu_item" |
| Events | event | wixEvents.event#<id> | typed date and location, lowest ticket price |
| Blog posts | post | wixBlog.post#<id> | not article (not in the enum) |
| Business profile | profile | wixBusiness.profile#<siteId> | |
| Locations | location | wixLocations.location#<id> | one entity per location |
The two custom solutions advertise their subtype field mapping via POST /v1/ingest/schema once per connection, before their first batch (see src/schema.ts, customSubtypeMappings). All prices are typed objects, never concatenated strings, and url is always the public site URL whose host equals X-Site-Domain.
Install and build
npm install
npm run buildRequires Node 20.11 or newer.
Configuration
| Variable | Purpose |
|---|---|
BAINQUET_API_URL | default https://api.bainquet.online/v1 |
BAINQUET_CONNECTOR_TOKEN | connectorId.secret |
BAINQUET_WEBSITE_ID | scoped website id |
BAINQUET_SITE_DOMAIN | site host, for example acme.com |
WIX_WEBHOOK_PUBLIC_KEY | Wix app webhook RSA public key (PEM), for inbound JWT verification |
WIX_API_TOKEN | Wix API access token (read-only) for backfill |
WIX_SITE_ORIGIN | https://acme.com |
WIX_CURRENCY | default USD |
WIX_DEFAULT_LANGUAGE | default en |
WIX_SOLUTIONS | comma-separated list limiting which solutions sync (default all) |
Two separate secrets
WIX_WEBHOOK_PUBLIC_KEYverifies inbound Wix webhooks, which Wix signs as an RS256 JWT.BAINQUET_CONNECTOR_TOKENsigns the outbound HMAC to bAInquet.
WARNING
Keep the two secrets distinct. The webhook public key only verifies what Wix sends you; the connector token authenticates what you send to bAInquet.
Mapping pattern
Mapping is per-solution, governed by the resolution table above and implemented in mapper.ts. A record's solution determines its type and stable id; structured data (variants, prices, dates, locations) goes into typed json objects. The two custom solutions describe their subtype attribute to the server via /ingest/schema before sending items, so the server knows how to interpret the custom field.
Incremental sync
Wix delivers webhooks as a signed JWT. Subscribe to per-solution created / updated / deleted events (and app-removed), and point them at the bundled webhook server:
node dist/scripts/webhook-server.js # :8473 /webhooks/wix- created or updated maps the record and POSTs to
POST /v1/ingest/item. - deleted or unpublished POSTs a tombstone to
POST /v1/ingest/deleteby stable id. - app or instance removed sends a final
idleheartbeat for a clean disconnect.
The JWT signature is verified with WIX_WEBHOOK_PUBLIC_KEY before any processing; an invalid JWT is rejected with no side effects.
Backfill
npm run backfillPages each enabled Wix solution (cursor paging, honoring 429), maps records, and batch-POSTs in chunks of 500. Idempotent: unchanged records return skipped.
Signing
signingKey = HKDF-SHA256(secret, salt = websiteId, info = "bq.connector.hmac.v1", 32 bytes)
canonical = METHOD\npath\nsha256(body)\ntimestamp\nnonce\nwebsiteId
X-Signature = hex HMAC-SHA256(signingKey, canonical)Headers include X-Connector-Type: wix. Retries on 5xx / 429 / network errors reuse the same Idempotency-Key with a fresh nonce and timestamp. See Ingestion and signing.
Out of scope
This package is the content mapper, signer, webhook glue, and backfill. The full Wix App surface (OAuth install, embedded dashboard, encrypted token store, Postgres outbox and BullMQ, polling fallback, DNS or meta verification helper) wraps this core and is out of scope. The endpoint paths used in backfill.ts follow the documented Wix REST query endpoints and may need per-app permission adjustment. No LLM anywhere.
Verifying the signer
npm testRuns the vendored-signer parity check (known-good vector, byte-for-byte) plus mapper unit tests (SourceType resolution, typed prices, draft exclusion).