Run your workspace
Integrations
Integrations let your agent learn from data living outside the
Blengi database (Notion, Google Docs) and let leads flow into your
existing systems (CRMs, Slack, webhooks). Open
Settings › Connections › Integrations (/settings/integrations) to manage them.
Notion
Connect once via OAuth. Blengi requests read access to the workspaces you select during the OAuth flow — we never get global access. After connecting:
- The Notion tab of the AI employee's Sources page takes a Notion page URL or id.
- Each added page becomes a Notion source and is ingested via
IngestNotionPageJob. - Indexed Notion sources re-sync every hour (
pitchbar:sync-oauth-sources); the per-source Reindex button re-fetches straight away. - The OAuth token is encrypted at rest using Laravel's
encryptedcast.
Google Docs
Same shape as Notion. OAuth once, then paste a Google Doc URL or id on
the Google Docs tab of the Sources page; it is
ingested via IngestGoogleDocJob and re-synced every hour,
like Google Sheets. Tokens encrypted at rest. See
Disconnecting below for what Disconnect does.
Re-sync vs. Reindex
Both Google Docs and Notion pages dedupe on the document's content hash, scoped to the source — an identical doc under a second source still gets its own copy, so it can be assigned to a different segment and stay retrievable. Two paths refresh a source:
- Routine re-sync (the scheduled OAuth sweep) is cheap: an unchanged doc that already has chunks is left alone, and only changed content is re-embedded. A source that somehow sits indexed with zero chunks is re-indexed automatically on the next sync, so it never stays silently unretrievable.
- Reindex (the per-source button) is a forced rebuild: it re-fetches the doc and re-embeds it even when the content is unchanged. This is the escape hatch when a vector went missing (a dropped upsert, or an index rebuilt to a new embedding model) — a plain re-sync would skip the unchanged doc, but Reindex restores retrieval.
An empty or too-short doc (under 50 characters of extracted text) is
marked failed with "empty or too short to index" rather than
a misleading indexed, so a blank Google Doc surfaces as a
problem instead of a silent zero-result source.
When Google reports the refresh token expired or
revoked (the user revoked access, or a test-mode OAuth app
aged its tokens out), the connection flips to
expired: the Integrations page shows the reconnect
state, adding new Google sources is blocked with a clear message,
and any failing Google Doc source shows "Reconnect Google under
Integrations, then click Reindex" instead of a generic error. After
reconnecting, one Reindex on the source recovers it —
sources are never deleted by an expired connection.
Slack
The Slack integration posts new leads to a channel,
and nothing else. Create an Incoming Webhook in Slack for the
channel you want, paste its URL (it starts with
https://hooks.slack.com/) and save. There is no OAuth
and no digest.
The ping when a visitor asks for a person is separate: it uses the Slack (or Teams) webhook URL under Settings › Human takeover › Availability and alerts (see Live human handoff).
Ticking Send test on save posts a sample "New lead" message to the channel so you can confirm the wiring before a real visitor submits. The test payload uses the authenticated user's name and email (suffixed with "Slack test from <workspace name>") so the alert is obviously a self-test — operators won't panic thinking a real lead came through.
Webhooks (outgoing)
Blengi can POST to your endpoint when events happen. Configure them in
the webhook card on /settings/integrations. Events available:
| Event | Fires when |
|---|---|
lead.captured | The widget lead form was submitted (the only event currently shipped). |
The other event names you might see in older roadmap notes
(conversation.started, conversation.message,
conversation.routed, lead.updated) are
on the roadmap but not yet wired. Add additional events by
extending SignedDispatcher.
Each webhook has a signing secret. Blengi HMACs the body with that
secret and sends the digest in the X-Pitchbar-Signature
header — verify it on receipt. Every delivery is tried once. The
lead-captured dispatcher
(app/Services/Webhooks/SignedDispatcher.php) does not
retry: the lead is already saved, and a failed delivery only writes a
Webhook dispatch failed warning to the application log.
Workflow-step webhooks (DispatchWebhookJob) also run once
($tries = 1), because retrying a POST without an
idempotency key would duplicate records on the receiver; a failure
lands in failed_jobs.
See Outgoing webhooks for the payload shapes.
HubSpot / Salesforce / Zapier
The webhooks above are the universal escape hatch — they work with anything that can receive HTTP POSTs. Native HubSpot and Salesforce integrations are on the roadmap; in the meantime, point a webhook at a Zapier catch-hook and let Zapier route to your CRM.
Disconnecting
Each integration's row has a Disconnect button. It
deletes the stored connection (the integration_connections
row) and does nothing else:
- The token is not revoked with Notion or Google. Remove the app's access in your Notion or Google account if you want that too.
- Sources that use the connection are not deleted, and their indexed content stays usable. Their next sync or Reindex fails with Google is not connected for this workspace. (or the Notion equivalent).
Connecting again and clicking Reindex on those sources brings them back. No data is lost.
Permissions
The Integrations page is for Owners and Admins only: it checks the same rule as managing team members. Editors and Viewers don't see it in the menu and get a 403 if they open the address.