Skip to content

Webflow connector

A standalone Node connector that streams a Webflow site's CMS content to the bAInquet ingestion API, mapping each CMS collection item by its field schema 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

Each CMS item is mapped using its collection schema (field types from the Webflow Data API v2):

Webflow field typeMapping
Rich textitem html (plus derived text); also json.fields (plain)
Plain text / optionjson.fields[<slug>]
Reference / multi-referencejson.relations[<slug>]: related item ids, never stringified into prose
Imagejson.media[] (url plus alt)
Numberjson.fields[<slug>]; typed price {value,unit,currency} when the collection's priceField is configured

Collection to SourceType is configurable per collection slug (WEBFLOW_COLLECTION_MAP), with a deterministic heuristic default: blog, post, and news collections map to post (not article, which is not in the enum); product and shop collections map to product; everything else maps to collection.

  • Stable id: <collectionSlug>#<itemId>.
  • url: <siteOrigin>/<collectionSlug>/<itemSlug> (host equals X-Site-Domain).
  • Draft, archived, and never-published items are excluded.

References become structured relations, never prose, which keeps the relationship graph machine-readable downstream.

Install and build

bash
npm install
npm run build

Requires Node 20.11 or newer.

Configuration

VariablePurpose
BAINQUET_API_URLdefault https://api.bainquet.online/v1
BAINQUET_CONNECTOR_TOKENconnectorId.secret
BAINQUET_WEBSITE_IDscoped website id
BAINQUET_SITE_DOMAINsite host, for example acme.com
WEBFLOW_WEBHOOK_SECRETWebflow app client secret, for inbound webhook signature
WEBFLOW_API_TOKENWebflow Data API token (read-only)
WEBFLOW_SITE_IDthe Webflow site id
WEBFLOW_SITE_ORIGINhttps://acme.com
WEBFLOW_DEFAULT_LANGUAGEdefault en
WEBFLOW_COLLECTION_MAPoptional JSON: { "<slug>": { "sourceType": "...", "bodyField": "...", "priceField": "...", "currency": "..." } }

Two separate secrets

  • WEBFLOW_WEBHOOK_SECRET verifies inbound Webflow webhooks: x-webflow-signature = hex HMAC-SHA256("<timestamp>:<rawBody>"), with a plus-or-minus 300s timestamp window (replay guard).
  • BAINQUET_CONNECTOR_TOKEN signs the outbound HMAC to bAInquet.

WARNING

The two secrets sign in opposite directions. The Webflow secret verifies what Webflow sends you; the connector token authenticates what you send to bAInquet.

Mapping pattern

Mapping is field-type driven and implemented in mapper.ts. For each item the connector fetches the collection schema (cached), then maps each field by its type per the table above. Override the SourceType, body field, price field, or currency per collection slug with WEBFLOW_COLLECTION_MAP.

Incremental sync

Subscribe to collection_item_created, collection_item_changed, collection_item_deleted, collection_item_unpublished, and site_publish, and point them at the bundled webhook server:

bash
node dist/scripts/webhook-server.js   # :8474 /webhooks/webflow
  • created or changed fetches the collection schema (cached), maps the item, and POSTs to POST /v1/ingest/item.
  • deleted or unpublished POSTs a tombstone to POST /v1/ingest/delete.
  • site_publish cues a reconciliation backfill to catch any dropped webhooks.

The inbound signature is verified before any processing.

Backfill

bash
npm run backfill

Lists the site's collections, loads each schema, pages all items (offset paging, honoring 429), maps published items, and batch-POSTs in chunks of 500. Idempotent.

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: webflow. Retries on 5xx / 429 / network errors reuse the same Idempotency-Key. See Ingestion and signing.

Out of scope

This package is the content mapper, signer, webhook glue, and backfill. The full Webflow App (OAuth install, hosted connection UI, encrypted secret store, Postgres outbox, polling fallback, llms.txt and ai.json emission) wraps this core and is out of scope. No LLM anywhere.

Verifying the signer

bash
npm test

Runs the vendored-signer parity check (known-good vector) plus mapper tests (field-type mapping, reference to relations, typed price, draft exclusion, webhook-signature verify and replay).

Owner-controlled structured data for AI.