How the connection works
FoxFlow connects to Blackbaud in two parts:
- Mailchimp — a built-in, one-click integration for email.
- A Blackbaud bridge — FoxFlow sends every lead and conversion event to a URL you control via outbound webhooks, and a small connector on that end writes the data into Blackbaud through the Blackbaud SKY API. You provide the connector — Zapier, Make, or a simple script.
Together these give you a working, duplicate-safe pipeline from first inquiry to enrolled constituent.
1. The big picture
Lead (pipeline) → convert → Contact
FlowSend SMS — replies route back to the lead by phone
┌──────────────────────────── FoxFlow ───────────────────────────┐
│ │
Inquiry ──► │ Lead (pipeline) ──► convert ──► Contact (org database) │
(form, ad, │ │ │ │
SMS reply) │ │ FlowSend SMS/iMessage │ │
│ ▼ ┌───────┴────────┐ │
│ (replies route back │ auto-sync │ webhook on │
│ to the lead by phone) ▼ ▼ events │
└─────────────────────────┌───────────┐ ┌──────────────────────┘
│ Mailchimp │ │ Your connector
│ (email) │ │ (Zapier/Make/script)
└───────────┘ ▼
┌────────────┐
│ Blackbaud │
│ (SKY API) │
└────────────┘- FoxFlow captures and works the inquiry (top of funnel).
- Mailchimp sends email to those contacts (built-in sync).
- Blackbaud is the system of record for students and families. Your connector receives FoxFlow events and creates/updates the matching constituent.
2. Before you start (prerequisites)
- FoxFlow organisation on the Pro plan — required for both Mailchimp and webhooks.
- You are an owner or admin of the FoxFlow organisation.
- A Mailchimp account (for the email piece).
- Blackbaud SKY API access: a developer account, a registered app (Client ID / Secret / Redirect URI), and a subscription key. Your Blackbaud admin usually arranges this.
- A connector that can receive an HTTPS webhook and call the Blackbaud API:
- No-code: Zapier or Make (Integromat) — "Webhook → Blackbaud" scenario.
- Low-code: a small cloud function / serverless endpoint your IT writes.
- Agreement on your matching rule (how to decide a lead is an existing constituent — email first, then phone; details in Section 6).
3. A quick note on Blackbaud's vocabulary
Blackbaud is precise about wording, and mismatched terms are a common source of confusion. Line up the language before you line up the data.
| In FoxFlow | In Blackbaud | Note |
|---|---|---|
| Lead | (no equivalent) | A prospect being worked; lives only in FoxFlow until converted. |
| Contact | Constituent / Individual | Blackbaud assigns a unique ID to every individual. |
| (no equivalent) | Student | Has its own Student ID, separate from the constituent record. |
| (no equivalent) | Household / Relationship | Families = linked individuals. FoxFlow has no household concept. |
| Tag | Custom field / list | FoxFlow contact tags travel in the webhook so you can map them. |
4. Step 1 — Connect Mailchimp (email)
This one is fully built in — no connector required.
- Open Org Settings → Integrations → Mailchimp.
- Click Connect and authorize via Mailchimp (OAuth). Tokens are stored securely server-side; no keys are exposed in the browser.
- Choose the audience to sync into.
- Done. From now on, when a lead converts into a Contact, FoxFlow automatically:
- Upserts the subscriber in Mailchimp, and
- Syncs tags (adds new ones, removes ones you've cleared).
Why this stays clean: Mailchimp subscribers are keyed by the email address — the exact same key FoxFlow uses for contacts — so the same person never lands in Mailchimp twice.
5. Step 2 — Connect Blackbaud via the webhook bridge
FoxFlow can POST a signed JSON event to any HTTPS URL you control whenever a lead is created, changes status, or converts. Your connector receives it and writes to Blackbaud.
5.1 Add the webhook endpoint in FoxFlow
- Open Org Settings → Integrations → API & Webhooks.
- Click Add endpoint and enter:
- URL: your connector's HTTPS address (e.g. your Zapier/Make webhook URL, or your function URL). Must be
https://and publicly reachable. - Events: pick what to send. For Blackbaud most schools use:
lead.converted— the main one: a family has become a real enrollment prospect, create/update the constituent.- (optional)
lead.created— if you want every raw inquiry mirrored into Blackbaud. - (optional)
lead.status_changed— to track pipeline movement.
- Flows: all flows, or scope to specific ones (e.g. just your "Admissions" flow).
- URL: your connector's HTTPS address (e.g. your Zapier/Make webhook URL, or your function URL). Must be
- Save. FoxFlow shows the signing secret (
whsec_…) exactly once — copy it now and store it in your connector. (You can rotate it later if needed.) - Use the "Send test" button to fire a sample event at your connector and confirm it's wired up.
5.2 What FoxFlow sends (payload)
A POST with JSON like this:
{
"id": "del_abc123",
"event": "lead.converted",
"created_at": "2026-06-20T11:30:00.000Z",
"data": {
"lead": {
"id": "lead_xyz",
"flow_id": "flow_admissions",
"flow_name": "Admissions",
"name": "Jane Parent",
"email": "jane@example.com",
"phone": "+15715551234",
"status": "enrolled",
"status_label": "Enrolled",
"source": "public_form",
"created_at": "2026-06-18T09:00:00.000Z",
"form_data": { "grade_applying_for": "5th", "student_name": "Sam Parent" }
},
"previous_status": "touring",
"previous_status_label": "Touring"
}
}Notes:
emailandphoneare best-effort extracted from the lead; phone is normalized to E.164.form_datacarries the raw inquiry fields (great place to pull student name, grade, Student ID, etc. into Blackbaud custom fields).previous_statusonly appears onlead.status_changed/lead.converted.
5.3 Headers and verifying authenticity
Every delivery includes:
| Header | Purpose |
|---|---|
X-FoxFlow-Event | the event name (e.g. lead.converted) |
X-FoxFlow-Delivery-Id | unique delivery ID — use it as your idempotency key |
X-FoxFlow-Signature | t={unix_timestamp},v1={hmac} |
Verify the signature so you only accept genuine FoxFlow calls. The signature is a Stripe-style HMAC-SHA256:
v1 = HMAC_SHA256(secret = whsec_…, message = "{t}.{raw_request_body}")Recompute v1 from the timestamp t and the exact raw body; compare to the header value. (In Zapier/Make you can do this in a code step, or rely on a shared secret path/header for a simpler setup.)
5.4 What your connector should do with Blackbaud
This is where the match-before-create logic lives (see next section for why it matters):
- Read
email(andphone) from the payload. - Search Blackbaud for an existing constituent by email, then phone.
- Exactly one match → update it, filling blanks only.
- No match → create a new constituent.
- Two or more matches → don't guess; route to a human to resolve (e.g. a "needs review" list or an email to admissions).
- Map FoxFlow fields → Blackbaud fields (suggested mapping):
| FoxFlow payload | Blackbaud constituent |
|---|---|
name / form_data.*name | First / last name |
email | Primary email |
phone (E.164) | Primary phone |
form_data.grade_applying_for | Custom field / attribute |
form_data (student info) | Linked student record / custom fields |
| FoxFlow tags | Custom field or constituent list |
- Store the resulting Blackbaud constituent ID on your side, keyed by email, so repeat events for the same family always update the same record.
- Respond HTTP 2xx quickly. Anything else is treated as a failure and retried (below).
5.5 Delivery reliability (built in)
You don't have to build retry logic — FoxFlow handles it:
- Timeout: 5 seconds per attempt (respond fast; do heavy work asynchronously if needed).
- Retries: up to 5 attempts with backoff (~5 min → 15 min → 1 hr → 6 hr).
- Idempotency: the same
X-FoxFlow-Delivery-Idmay arrive more than once — dedupe on it so a retry never creates a second constituent. - Auto-disable: an endpoint that fails 20 times in a row is disabled (check the deliveries log in the same settings tab to diagnose).
6. Keeping records clean (no duplicates)
Keeping a single clean record per person — especially when texting is involved — is handled at three layers:
Layer 1 — Inside FoxFlow (automatic)
- Contacts are keyed by email, so the same email can't create two contacts — it updates.
- Lead import / re-engagement can skip duplicates by email.
- Phone numbers are normalized to E.164 everywhere, so the same number is always stored identically.
Layer 2 — SMS / FlowSend (automatic)
FoxFlow's built-in texting (FlowSend) is designed so messaging never creates duplicates:
- An inbound reply is matched back to the existing lead by phone number — it does not create a new lead or contact.
- If a text matches no lead, it goes to an unmatched queue for someone to review — never auto-created as a duplicate.
- FoxFlow guards against sending the same message twice.
Result: texting a family and getting replies will not spawn duplicate leads/contacts — so it won't push duplicate constituents to Blackbaud either.
Layer 3 — At Blackbaud (your connector — Section 5.4)
The match-before-create step is the final guard: search before you create, fill blanks instead of overwriting, store the Blackbaud ID so future updates hit the same record, and send ambiguous matches to a human. This is what honors Blackbaud's "one unique ID per individual" model.
7. The full journey (verify all the pieces are connected)
Trace one family end-to-end — this is the checklist to walk through with your team:
- Inquiry — a parent submits a form / Meta lead ad → a Lead appears on the Kanban board in real time.
- Worked — the team texts via FlowSend; replies attach to the same lead (no duplicates); the lead moves through stages.
- Email — if synced to Mailchimp, the family receives email campaigns; tags keep segments accurate.
- Converted — the lead hits the conversion status → FoxFlow stamps it converted and (if enabled) adds it to Contacts.
- Mailchimp sync — the contact is upserted into Mailchimp with tags. (Automatic.)
- Blackbaud bridge — FoxFlow fires
lead.convertedto your connector → it matches or creates the constituent, fills blanks, and remembers the Blackbaud ID. - Enrollment — your team links/creates the student record in Blackbaud and proceeds as usual.
Checkpoints
- Form submission creates a lead within seconds.
- Inbound + outbound SMS both attach to the one lead.
- Conversion creates exactly one Contact.
- Contact appears in Mailchimp with correct tags.
- Connector links to exactly one Blackbaud constituent.
- Re-submitting the same email does not create a second Blackbaud record.
8. Troubleshooting
| Symptom | Likely cause | What to do |
|---|---|---|
| Mailchimp/webhooks options missing | Free plan, or not an admin | Confirm a Pro plan and owner/admin role. |
| Contact not appearing in Mailchimp | Mailchimp not connected, or no email on contact | Reconnect Mailchimp; ensure the lead has an email. |
| Connector never receives events | Wrong URL, endpoint disabled, or non-2xx responses | Check the deliveries log; use Send test; verify your URL is public HTTPS. |
| Signature check fails | Hashing the wrong string, or wrong secret | Sign "{t}.{rawBody}" with the exact whsec_…; use the raw body, not re-serialized JSON. |
| Duplicate constituent in Blackbaud | Connector created instead of matched, or retry not deduped | Match by email→phone before creating; dedupe on X-FoxFlow-Delivery-Id; store the Blackbaud ID. |
| Endpoint got auto-disabled | 20 consecutive failures | Fix the connector, re-enable the endpoint, re-send. |
| SMS reply created a "new" lead | Inbound phone matched no existing lead | It lands in the unmatched queue; confirm the original lead's phone is stored. |
| Student vs parent confusion | Treating them as one record | Match the parent as the constituent; link the student (separate Student ID) separately. |
| Some inquiry fields missing in Blackbaud | Not mapped from form_data | Pull extra fields (student name, grade, Student ID) from data.lead.form_data. |
9. Who does what
| Task | Responsibility |
|---|---|
| Connect Mailchimp | Org admin — click Connect, pick the audience |
| Add the webhook endpoint | Org admin — add endpoint, store the signing secret |
| Build & host the connector (webhook → Blackbaud) | Your IT, or a Zapier/Make scenario |
| Blackbaud SKY API app + keys | Your Blackbaud account admin |
| Field mapping decisions | Your team — you know your data |
| Matching rules (email → phone) | Your team — implement in the connector |
| Verifying the end-to-end journey | Your team — confirm with real data |
| Ongoing record merges in Blackbaud | Your team |
Questions?
Start with the API & Webhooks deliveries log to see exactly what FoxFlow sent and how your connector responded — it's the fastest way to see which piece needs attention.
Book a Demo