Integrations & secrets
Tessule can push the people in a table — or the result of a SQL view — into a marketing platform as a synced audience, so the segment you define once in your CRM becomes the list your email platform sends to. Alongside that sits the workspace secret store: a write-only vault for API keys and webhook credentials referenced by features that call other systems on your workspace’s behalf. Both live in Workspace Settings and both handle third-party credentials, so both are admin territory.
If you only remember one thing: audience syncs are a one-way push. Your workspace owns the membership; the provider is told who belongs and is never asked. Nothing flows back — no consent, no engagement, no unsubscribes — and Tessule never deletes a profile or writes an unsubscribe on the provider side.
Who can do what
Section titled “Who can do what”- View connections and syncs — requires
integrations:read, granted to admins and members by default (not readers). The metadata carries no credential material, and a member configuring an email campaign needs to see which connections exist. - Create, edit, test, run, and delete connections and syncs — requires
integrations:manage, granted to workspace admins only. Managing an integration means holding a third-party credential and sending customer data to another vendor, the same trust tier assecrets:manage. - Create, rotate, and delete secrets — requires
secrets:manage, admin-only.
These are ordinary actions in the roles model: a custom role can grant any of them individually.
Connections
Section titled “Connections”A connection is one marketing-platform account: a provider, a name of your
choosing (unique per workspace), and an API key. Manage them in Workspace
Settings → Integrations, or via GET /integrations/connections and
POST /integrations/connections.
Three providers are supported:
| Provider | Credential to paste | An “audience” is called |
|---|---|---|
| Klaviyo | Settings → API keys → Private API key. Needs read and write access to Profiles and Lists. | a list |
| Mailchimp | Account → Extras → API keys. The key ends with a datacenter suffix such as -us14 — paste it whole. |
an audience |
| Omnisend | Store settings → Integrations & API → API keys. Needs the Contacts scope. | a tag |
Credentials are write-only
Section titled “Credentials are write-only”The API key is stored encrypted at rest (with the same cipher as workspace
secrets) and is never returned by any endpoint — not in the connection
list, not in test results, not in error messages. If a key is lost, you don’t
retrieve it; you rotate it: PUT /integrations/connections/{connectionId}
accepts a new name and/or a replacement credential.
Testing a connection
Section titled “Testing a connection”POST /integrations/connections/{connectionId}/test (the test button on
the connection row) makes one authenticated, read-only call to the provider and
records the outcome on the connection:
ok— the credential works. The result includes anaccountLabel(the provider-side account name) so you can confirm you connected the right account, not just an account.error— the provider rejected the call; the failure text is shown and stored as the connection’slastError.
A connection’s status is one of unverified (never proven), ok, or error.
A failed sync run also updates it.
Deleting a connection
Section titled “Deleting a connection”Deletion is refused while any audience sync still references the connection — delete the syncs first, so none is silently orphaned. Anything already pushed to the provider stays there.
Each workspace can hold up to 10 connections by default (an adjustable limit — see organization administration).
Audience syncs
Section titled “Audience syncs”An audience sync activates a table or SQL view as an audience on a connected platform. The source plus its email field is the audience definition: define the segment once in your workspace, and the provider-side list follows it.
Configuring a sync
Section titled “Configuring a sync”Create one from Workspace Settings → Integrations → audience syncs (or
POST /integrations/audience-syncs). You choose:
- Platform connection — which connected account to push to.
- Source — a table, or a SQL view holding the segment. Fixed after creation — moving a sync to a different source would orphan its ledger. Views are usually the right choice: the view is the audience definition.
- Email field — the column holding the address. It’s the identity key: how a row is matched to a provider profile.
- Destination audience — picked from the lists that already exist on the
provider side (
GET /integrations/connections/{connectionId}/audiencespopulates the picker). - Fields to send (optional) — a mapping from source columns to provider-side profile properties (Klaviyo profile attributes, Mailchimp merge fields, Omnisend custom properties).
- Sync is active — a disabled sync refuses to run; queued runs of one are skipped, not executed.
Each workspace can hold up to 25 syncs by default (adjustable limit).
Running a sync
Section titled “Running a sync”There is no automatic schedule. A sync runs when you run it:
POST /integrations/audience-syncs/{syncId}/run (the run button)
enqueues a run and returns immediately with a run id; the work happens in the
background. Only one run per sync may be in flight — starting another while
one is queued or running is refused with a conflict, because two concurrent
runs could push the same profile twice.
Each run is a snapshot-and-diff against the sync’s ledger (what it last pushed):
| Category | Meaning | Provider call |
|---|---|---|
| added | In the source, not yet on the list | Upsert |
| changed | On the list, but a mapped field’s value changed | Upsert |
| unchanged | On the list with identical data | None |
| removed | In the ledger, no longer in the source | Removed from the list only |
| failed | The provider rejected that row | — |
Steady state is free: a run over an unchanged audience makes zero provider calls. “Removed” never means a profile deletion or an unsubscribe — the person simply stops being on that list.
Run status and failures
Section titled “Run status and failures”The sync list shows each sync’s last run: status (queued, running,
completed, failed, cancelled), the added/changed/removed/unchanged/failed
counts, when it ran, and the member count currently tracked in the ledger.
A failed run records its error on the sync (and on the connection when the
credential was the problem).
Failure handling distinguishes two cases:
- Transient failures — provider rate limits, outages — are retried automatically a bounded number of times.
- Authentication failures are never retried: a rejected credential stays rejected, and hammering a provider’s auth endpoint is how an account gets locked. Rotate the key, test the connection, then run again.
Deleting a sync
Section titled “Deleting a sync”DELETE /integrations/audience-syncs/{syncId} removes the sync and its
ledger. Provider-side list membership is left exactly as it is — nobody is
mass-removed from a marketing list because a CRM object was deleted. That kind
of removal is impossible to undo, so it deliberately never happens implicitly.
Workspace secrets
Section titled “Workspace secrets”Secrets are named values — webhook signing keys, third-party API keys —
referenced by name without the value ever
appearing in configuration or error messages. Manage them in
Workspace Settings → Secrets (visible to holders of
secrets:manage), or via the API.
- Naming:
UPPER_SNAKE_CASE, starting with a letter, up to 64 characters, unique per workspace (e.g.HOOK_API_KEY). Values up to 4,096 characters. - Write-only:
GET /secretsreturns metadata only — name, who created it, when it was last updated. No endpoint ever returns a secret’s value. If a value is lost, rotate it:PUT /secrets/{name}replaces the value in place, keeping every reference to the name working. - Referencing: the single-action automations that referenced secrets by name have been replaced by Flows (multi-step automation), which consume the same store; documentation arrives with the new builder.
- Deleting:
DELETE /secrets/{name}removes it immediately. Update anything that still references the name first. - Limit: 20 stored secrets per workspace by default (adjustable).
How credentials are protected
Section titled “How credentials are protected”The same rules apply to integration connection keys and workspace secrets:
- Encrypted at rest with a workspace-scoped cipher.
- Write-only on the wire — no read path exists, by design, so neither a compromised browser session nor an over-broad API token can exfiltrate a stored credential through the API.
- Test results and error messages carry provider text, never credential material.
- Only
integrations:manage/secrets:manageholders (workspace admins by default) can write them, and only the same tier can point them at new destinations.
Reference: API
Section titled “Reference: API”Connections: GET /integrations/connections,
POST /integrations/connections,
PUT /integrations/connections/{connectionId},
DELETE /integrations/connections/{connectionId},
POST /integrations/connections/{connectionId}/test,
GET /integrations/connections/{connectionId}/audiences.
Audience syncs: GET /integrations/audience-syncs,
POST /integrations/audience-syncs,
PUT /integrations/audience-syncs/{syncId},
DELETE /integrations/audience-syncs/{syncId},
POST /integrations/audience-syncs/{syncId}/run.
Secrets: GET /secrets, POST /secrets, PUT /secrets/{name},
DELETE /secrets/{name}.
Q: Can I sync unsubscribes or engagement data back from the platform?
No. The integration is deliberately one-way: membership is pushed out, and consent/engagement never flow back through these syncs. Manage unsubscribes on the provider side, where the legal record of consent lives.
Q: Why can’t I change a sync’s source table?
The ledger — the record of what was pushed for which row — is keyed to the source. Re-pointing the sync would make that history meaningless. Delete the sync and create a new one against the new source.
Q: Why does the provider audience picker fail with a quota error?
Calls to the provider (test, audience listing) are rate-limited per workspace. Wait a moment and retry.
Q: Someone left our company — do they disappear from the mailing list?
Only if your source says so. If the row leaves the table or view (or its email field empties), the next run counts them as removed and takes them off the list. If the row remains, so does the list membership. And if you delete the whole sync, the provider keeps the list as-is.
Q: A member can see our connections — can they run a sync?
No. integrations:read shows metadata only. Creating, editing, running, and
deleting — anything that moves data or touches a credential — requires
integrations:manage, which only admins hold by default.