CRM Integration Guide
Practical patterns for syncing WinFactor with HubSpot, Salesforce, or any CRM/ERP
Patterns for the four things every CRM integration wants: new business in, contact data in sync, quotes pushed out, and IDs linked both ways. Each pattern works in ActivePieces, in your own services, or as a scheduled script.
Self-configure your connector
Start every integration by reading the organization profile — locale, unit system, and currency drive how you format everything downstream:
curl -s "$WINFACTOR_URL/api/v1/organization" -H "Authorization: Bearer $WINFACTOR_KEY"Pattern 1 — Submission intake (deals/opportunities)
Trigger: submission.created webhook. Enrich: fetch the full submission — the API adds resolvedConfiguration, the human-readable version of what the customer built (component names, option names, colors, dimensions), perfect for a deal description:
curl -s "$WINFACTOR_URL/api/v1/submissions/$SUBMISSION_ID" \
-H "Authorization: Bearer $WINFACTOR_KEY"{
"id": "k57…",
"customer": { "name": "Jane Doe", "email": "[email protected]", "phone": "+31 6 …" },
"dimensions": { "width": 2400, "height": 1800 },
"resolvedConfiguration": {
"componentSelections": [
{ "componentName": "Lift-slide door", "componentType": "door",
"width": 1185, "height": 1740,
"selectedOptions": [{ "code": "HRPP", "name": "HR++ glass", "optionGroup": "glass" }] }
],
"insideColor": { "name": "Anthracite" }, "outsideColor": { "name": "White" },
"selectedOptions": [{ "code": "HRPP", "name": "HR++ glass" }], "notes": "…"
},
"externalPricing": { "subtotal": 1390.5, "…": "your frozen line items, if the live loop priced it" },
"latestQuoteId": null,
"externalRef": null
}Write back the link: stamp your CRM record ID onto the submission so the relationship survives in both systems:
curl -X PATCH "$WINFACTOR_URL/api/v1/submissions/$SUBMISSION_ID" \
-H "Authorization: Bearer $WINFACTOR_KEY" -H "Content-Type: application/json" \
-d '{"externalRef": "hubspot:deal:9912345"}'Backfill / reconcile: GET /api/v1/submissions?createdAfter=<ms> pages through history — your safety net if a webhook was ever missed.
Pattern 2 — Two-way contact sync
WinFactor auto-creates contacts from submissions. Keep them aligned with your CRM:
-
CRM → WinFactor: upsert by email (idempotent — safe to run on every CRM change):
curl -X POST "$WINFACTOR_URL/api/v1/contacts" \ -H "Authorization: Bearer $WINFACTOR_KEY" -H "Content-Type: application/json" \ -d '{"email": "[email protected]", "name": "Jane Doe", "companyName": "Doe BV", "externalRef": "hubspot:contact:3314"}' -
WinFactor → CRM: on
submission.created, look the contact up (GET /api/v1/contacts?email=…); if it has noexternalRef, create it in your CRM andPATCHthe ref back.
submissionCount and lastUsedAt on each contact tell you who your repeat customers are.
Pattern 3 — Quote lifecycle out
Mirror the quote pipeline into CRM stages using the quote events (quote.created → sent → viewed → accepted/rejected/expired) — each maps naturally to a deal stage.
You can also drive quotes from the CRM/ERP:
# Create a draft quote — with YOUR breakdown in one call (no intermediate wrong-priced draft)
curl -X POST "$WINFACTOR_URL/api/v1/submissions/$SUBMISSION_ID/quotes" \
-H "Authorization: Bearer $WINFACTOR_KEY" -H "Content-Type: application/json" \
-d '{"lineItems": [
{"description": "Lift-slide door (SKU WW-451)", "quantity": 2, "unitPrice": 512.0, "itemType": "door"},
{"description": "Mounting", "quantity": 1, "unitPrice": 250.0, "itemType": "labor"}
],
"reference": "ERP order 5512"}'
# Revise after sending (versions the quote, mirrors in-app editing)
curl -X PUT "$WINFACTOR_URL/api/v1/quotes/$QUOTE_ID/line-items" \
-H "Authorization: Bearer $WINFACTOR_KEY" -H "Content-Type: application/json" \
-d '{"revise": true, "lineItems": [ … ]}'
# Send it to the customer (same email path the app uses)
curl -X POST "$WINFACTOR_URL/api/v1/quotes/$QUOTE_ID/send" \
-H "Authorization: Bearer $WINFACTOR_KEY" -H "Content-Type: application/json" \
-d '{"recipients": ["[email protected]"]}'Omit lineItems on create and WinFactor calculates them — preferring any frozen external pricing from the live loop. GET /api/v1/quotes/{id} exposes pdf.status and a download url once the PDF has been generated (PDFs are produced when the quote is prepared or sent in the app) — archive it on the CRM deal.
Pattern 4 — Leads
Contact-form leads from your public page (lead.created event) are fetchable too: GET /api/v1/leads?status=new — route them into your CRM's lead inbox and mark progress in WinFactor from the dashboard.
Operational notes
- Idempotency: dedup webhooks on
X-WinFactor-Delivery; contact upsert is idempotent by email; quote line-item PUT is a full replace. - Recovery: the delivery log + redelivery replays anything your endpoint missed; list endpoints with
createdAfterreconcile bulk gaps. - Linking:
externalRefexists on submissions, quotes, contacts, and individual adjustment line items — use a consistentsystem:type:idconvention. - Security: verify webhook signatures; use a read-only key for anything that only reads.