Channel Connect Links
A channel connect link is a secure, time-limited URL you generate and send to a customer. They open it, authenticate with the provider (e.g. Meta for WhatsApp), and the connected channel lands in the tenant you designate — without the customer ever seeing your dashboard or credentials.
This is the self-serve onboarding primitive for platform partners: instead of collecting a customer's WhatsApp/Telegram credentials yourself, you hand them a link.
Where the channel lands
The channel created through a connect link belongs to the tenant that issued the token. Combine this with X-Tenant-ID to place a customer's channel directly into their sub-account:
Parent token + X-Tenant-ID: tenant_acme_123
│
└─ POST /channel-connect-tokens → link onboards the channel INTO tenant_acme_123
Generate a connect link
POST /api/v1/channel-connect-tokens
Requires the channels:create scope. To create the link for a sub-account, add the X-Tenant-ID header (see Cross-Account API Access).
Request body
| Field | Type | Required | Description |
|---|---|---|---|
allowed_channel_types | array | Yes | Which channel types the link may connect. Any of: telegram, whatsapp, instagram, messenger, gmail, smtp_imap, sendgrid_byok, mailgun_byok, sendgrid_managed, sms. |
allowed_channel_modes | object | No | Optional per-channel restriction on which connect mode the link may use. See Restrict connect modes. Omit it (or a given channel's key) to allow every mode for that channel. |
name | string | No | Internal label for the token (e.g. "Acme Corp Setup"). |
expires_in_hours | integer | No | Validity window in hours (1–168, default 24). |
max_uses | integer | No | Maximum successful connections (1–100, default 10). Set to 1 for a single customer. |
use_window_minutes | integer | No | Grace period in minutes after first use (5–1440, default 30). |
partner_name | string | No | Your brand/company name shown on the connect page (white-label). |
partner_redirect_url | string | No | HTTPS URL to send the customer back to after connecting. See Send the customer back to your app. |
partner_redirect_mode | string | No | button (default when partner_redirect_url is set) or auto_after_first. Ignored, and returned as null, when there is no partner_redirect_url. |
curl
curl -X POST "https://api.sendseven.com/api/v1/channel-connect-tokens" \
-H "Authorization: Bearer s7_api_PARENT_TOKEN" \
-H "X-Tenant-ID: tenant_acme_123" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Corp WhatsApp Setup",
"allowed_channel_types": ["whatsapp"],
"expires_in_hours": 48,
"max_uses": 1,
"partner_name": "Your Platform",
"partner_redirect_url": "https://yourapp.com/onboarding/done?customer=acme",
"partner_redirect_mode": "auto_after_first"
}'
Response (201 Created)
{
"id": "cct_9a8b7c6d",
"token_prefix": "s7_cc_a1",
"connect_url": "https://app.sendseven.com/connect/s7_cc_a1b2c3d4e5f6g7h8...",
"allowed_channel_types": ["whatsapp"],
"partner_name": "Your Platform",
"partner_redirect_url": "https://yourapp.com/onboarding/done?customer=acme",
"partner_redirect_mode": "auto_after_first",
"expires_at": "2026-03-06T09:00:00Z",
"max_uses": 1,
"current_uses": 0,
"remaining_uses": 1,
"created_at": "2026-03-04T09:00:00Z"
}
Send the connect_url to your customer. The raw token is embedded in that URL and is shown only once — persist the connect_url if you need to resend it.
Restrict connect modes
Some channels can be connected in more than one way. allowed_channel_modes lets a link pin the exact mode(s) a customer may pick — for example, force a WhatsApp link to the standard API onboarding and hide Coexistence, or offer only messaging (no social/comments) for Facebook and Instagram.
It is an object keyed by channel type, each mapping to a list of allowed modes:
| Channel | Modes |
|---|---|
instagram | messaging, social |
messenger | business_messaging, business_social, personal_social |
whatsapp | classic, coexistence |
{
"allowed_channel_types": ["whatsapp", "instagram"],
"allowed_channel_modes": {
"whatsapp": ["classic"],
"instagram": ["messaging"]
}
}
Rules:
- Absent = all allowed. If
allowed_channel_modesis omitted entirely, or a given channel's key is absent, every mode for that channel is allowed. Links created before this field existed keep working exactly as before. - List at least one mode per channel. A channel key must name one or more modes. To allow all modes, omit the key rather than passing an empty list — an empty list is rejected (
422). - Unknown channels or modes are rejected (
422). - Enforcement is the intersection of the link's modes and your account's feature availability, and it fails closed. A mode is offered to the customer, and accepted server-side, only when the link permits it and the feature is enabled for the issuing account. Restricting a link can therefore only ever narrow what is offered — it never unlocks a mode the account doesn't have. Requesting a mode the link forbids returns a
4xxerror.
The public connect page reads the effective (already-intersected) mode list, so customers only ever see the modes they can actually complete.
The customer's experience
- The customer opens
connect_url. - They see a branded connect page (using
partner_name) scoped to theallowed_channel_types. - They authenticate with the provider (e.g. Meta login for WhatsApp, bot token for Telegram, mailbox credentials for email).
- On success, the channel is created in the target tenant and the connect page shows a confirmation. If the link has a
partner_redirect_url, the customer is sent back to it as described below.
Send the customer back to your app
Set partner_redirect_url to bring the customer back to your product after they connect.
Requirements
- The URL must use
https://and include a host name.http://URLs, URLs with a username or password (https://user@host), spaces or control characters are rejected with422. Maximum 500 characters. - Your URL may already contain query parameters and a
#fragment. SendSeven keeps them, adds its own parameters to the query string and keeps the fragment at the end. - The redirect URL and mode are set when the link is created. To change them, create a new link.
Button or automatic
partner_redirect_mode | What the customer sees |
|---|---|
button (default) | After each successful connection the confirmation screen shows a Continue to partner_name button. The customer can connect another channel first, or click to go back to you. |
auto_after_first | After the first successful connection the customer is sent back to you automatically, after a short confirmation. |
If the connection fails or the customer cancels the provider's login, the connect page offers to try again and also lets the customer return to you with status=error or status=cancelled.
Query parameters SendSeven adds
| Parameter | When | Value |
|---|---|---|
status | always | success, error or cancelled |
channel_type | success | Type of the (first) connected channel, e.g. whatsapp, telegram, gmail, sendgrid_byok |
channel_id | success | ID of the (first, most recent) connected channel. For email providers (sendgrid_byok, mailgun_byok, sendgrid_managed) this is the email provider ID |
channels | success with more than one channel | Comma-separated type:id pairs, e.g. messenger:ch_1,messenger:ch_2 |
error_code | error (sometimes cancelled) | Machine-readable reason, e.g. connection_failed, invalid_credentials, token_expired, email_already_connected |
connect_token_id | always | The id of the connect link (never the secret token) |
No other data is added. In particular the customer's name, email address, phone number, the tenant ID and the raw link token are never put in the URL. If your own URL already uses one of the parameter names above, SendSeven's value replaces yours.
Example, for partner_redirect_url = https://yourapp.com/onboarding/done?customer=acme#step-3:
https://yourapp.com/onboarding/done?customer=acme&status=success&channel_type=whatsapp&channel_id=0f1e2d3c-4b5a-6978-8a9b-0c1d2e3f4a5b&connect_token_id=cct_9a8b7c6d#step-3
The query string is not signed. Anyone who holds the link, or who simply types the parameters into a browser, can produce a URL with status=success, status=error or any channel_id. Use the parameters only to decide what to check, never as proof:
- Match
connect_token_idagainst theidof a connect link you created. - Look the channel up with your API token (with the
X-Tenant-IDof the sub-account if you issued the link for one) and check that it exists:GET /api/v1/channels/{channel_id}for messaging channels,GET /api/v1/email-integrations/{channel_id}forgmail,smtp_imapandproxy,GET /api/v1/email-providers/{channel_id}forsendgrid_byok,mailgun_byokandsendgrid_managed. - For messaging channels, the
channel.createdwebhook carriescreated_via_connect_token_id; matching it againstconnect_token_idis the most reliable signal.
Treat status=error and status=cancelled as "the customer came back without finishing", not as a confirmed failure.
Links created before these rules existed keep working; links that have a redirect URL but no mode behave as button.
Know the moment a channel connects
Rather than polling, subscribe to the channel.created webhook event. When a customer completes a connect link, you receive an event whose data.channel.created_via_connect_token_id matches the token you issued — so you can correlate the connection back to the exact customer and link:
{
"type": "channel.created",
"tenant_id": "tenant_acme_123",
"data": {
"channel": {
"id": "ch_new",
"platform": "whatsapp",
"status": "connected",
"created_via_connect_token_id": "cct_9a8b7c6d"
}
}
}
Pair this with channel.updated to be alerted if that channel later disconnects (e.g. the customer's token is revoked), so you can prompt them to reconnect.
Manage connect tokens
| Action | Endpoint |
|---|---|
| List tokens | GET /api/v1/channel-connect-tokens |
| Get one | GET /api/v1/channel-connect-tokens/{id} |
| Revoke | DELETE /api/v1/channel-connect-tokens/{id} (or the revoke endpoint) |
Revoking a token immediately invalidates its link; connections already completed are unaffected.
sms in allowed_channel_types is gated per-account until SMS is enabled for the issuing tenant. If SMS isn't enabled, the request is rejected with guidance.
Next steps
- Cross-Account API Access — issue connect links into any sub-account
- Channel webhook events — react to connect/disconnect in real time