Sending invites (email API)
Trigger personalized survey-invite emails from your own systems.
The invite API turns BAI Analytics into the sending arm of your own workflow: your system decides who to email and when (a closed support ticket, a delivered order, a finished onboarding), and BAI Analytics renders the invite template, sends it, tracks delivery, and joins the responses back to your contacts.
The split of responsibilities is deliberate: design lives in the dashboard, triggering lives in the API. The email template, branding, and survey are built in the platform where they can be previewed and reviewed; the API only supplies recipients and variables. That keeps a compromised key from ever rewriting what your customers receive.
Prerequisites
Three things must be true before the first send works. All three are inspectable via GET /sources/{id}/invite-template, so wire that into your setup checks:
A
send-scoped API key. Emailing humans is scoped separately from pushing data;allincludes it. See Authentication & API keys.Your organisation's own verified sending domain. API sends never use the shared BAI Analytics sender: automated volume rides your sending reputation, not ours. Until the domain is verified you'll get a
SENDER_DOMAIN_NOT_VERIFIEDerror. Set it up under Organisation details → Email sending (Settings).An invite template on the source, designed in the platform. The template check returns its available variable keys (say,
first_name,order_id) so your integration can validate its data before sending.
The sending flow
1. Check readiness
curl https://boundaryai-ingest-279197672085.europe-west9.run.app/api/input/sources/9021/invite-template \
-H "Authorization: Bearer $BAI_API_KEY"Returns whether a template is configured, its variables, sender_domain_verified, sending_enabled, and the day's quota usage.
2. Preview with dry_run
curl -X POST https://boundaryai-ingest-279197672085.europe-west9.run.app/api/input/sources/9021/invites \
-H "Authorization: Bearer $BAI_API_KEY" -H "Content-Type: application/json" \
-d '{
"dry_run": true,
"recipients": [
{"email": "jamie@example.com",
"external_id": "cus_310",
"variables": {"first_name": "Jamie", "order_id": "A-1042"}}
]
}'A dry run renders the emails and reports what would happen (would_accept, skipped_already_invited, invalid) without sending anything. Use it in CI and before the first production send.
3. Send
Drop dry_run and the same call returns 202 with a distribution_id. Sends are transactional: 1 to 1,000 recipients per call, each with their own variables. Recipients are appended to the source's respondent list, and each gets a personalized survey link, which is what joins their eventual response back to your external_id.
4. Track the outcome
GET /invites/{distribution_id}: overall status and counts (total,sent,failed,skipped).GET /invites/{distribution_id}/recipients: per-recipient outcomes, including the delivery status reported by the mail provider (delivered, bounced, deferred) and yourexternal_id, cursor-paginated.Or skip polling: the
invites.completedwebhook fires when the distribution finishes, andinvite.bouncedfires per bounce or spam complaint. See Webhooks.
Safety rails (and why they're not optional)
These exist so an automation bug can't damage your sending reputation, which takes months to rebuild:
Never-double-invite by default. An address that already received this survey is skipped; pass
resend: trueonly when you explicitly mean it.Suppression is always enforced. Unsubscribed, bounced, or complaining addresses are never emailed again, even if your system keeps sending them.
Rolling 24-hour quota per organisation (default 2,000 invites; raisable on request). The template check shows today's usage, and over-quota sends fail cleanly with
INVITE_QUOTA_EXCEEDEDrather than queueing.Complaint-rate auto-pause. If spam complaints spike over a 30-day window, sending pauses before mailbox providers penalise your domain.
1,000 recipients per call. Batch bigger lists into multiple calls; each returns its own
distribution_id.
Joining responses back to your data
The external_id you set per recipient flows through the whole loop: it's on the recipient outcome rows, on the invite.bounced payload, and, because the survey link is personalized, on the response itself. Use the same identifier you use in feedback pushes (customer_id) and you can connect invited → responded → what they said entirely in your own warehouse.
Last updated