Authentication
The SendSeven API supports three authentication methods. Choose the one that fits your use case:
| Method | Use Case | Lifespan | X-Tenant-ID Required? |
|---|---|---|---|
| API Token | Your own scripts, automations, server-to-server | Up to 365 days | No (tenant is embedded in the token) |
| OAuth App | Third-party apps acting on behalf of a user | Access: 1 hour, Refresh: until revoked | No (tenant is embedded in the token) |
| Bearer Token (JWT) | SendSeven web/mobile app sessions | Short-lived | Yes |
For API integrations, use API Tokens. They are the simplest, most secure option and don't require the X-Tenant-ID header.
1. API Tokens (recommended)
API tokens are the primary authentication method for integrations, scripts, bots, and automation. Each token is scoped to a specific tenant and has fine-grained permissions.
Create tokens at: Settings > API Tokens
Token format
s7_api_<32 hexadecimal characters>
Example: s7_api_a1b2c3d4e5f6789012345678abcdef00
Creating a token
- Go to Settings > API Tokens
- Click Create Token
- Fill in:
- Name: A descriptive name (e.g., "CRM Integration", "Marketing Bot")
- Scopes: Select only the permissions this token needs
- Expiration: Choose a duration (up to 365 days)
- Click Create
- Copy the token immediately - it will only be displayed once
Using the token
Include the token in the Authorization header. No X-Tenant-ID is needed - the tenant is embedded in the token itself:
curl -X GET "https://api.sendseven.com/api/v1/contacts?page=1&page_size=20" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00" \
-H "Content-Type: application/json"
Python
import requests
API_TOKEN = "s7_api_a1b2c3d4e5f6789012345678abcdef00"
BASE_URL = "https://api.sendseven.com/api/v1"
headers = {
"Authorization": f"Bearer {API_TOKEN}",
"Content-Type": "application/json",
}
response = requests.get(f"{BASE_URL}/contacts", headers=headers)
data = response.json()
for contact in data["items"]:
print(contact["name"], contact["email"])
JavaScript (Node.js)
const API_TOKEN = "s7_api_a1b2c3d4e5f6789012345678abcdef00";
const BASE_URL = "https://api.sendseven.com/api/v1";
const response = await fetch(`${BASE_URL}/contacts`, {
headers: {
"Authorization": `Bearer ${API_TOKEN}`,
"Content-Type": "application/json",
},
});
const data = await response.json();
console.log(data.items);
Token lifetime and team membership
A token acts as the team member who created it, with at most that member's permissions. When that member is removed from the workspace or deactivated, all of their API tokens (and OAuth tokens they authorized) stop working immediately and return 401. Create integration tokens with an account that will stay in the workspace, and replace a leaving colleague's tokens before you remove them.
Contact visibility
A workspace API token sees every contact in the workspace, whoever created it. A member's contact visibility level does not limit their API tokens. An OAuth app, on the other hand, acts as the member who authorized it and follows that member's level: contacts hidden from the member are hidden from the app too. The same goes for the MCP server: connected by signing in, it follows the member's level; connected with an API key, it sees all contacts, like any API token.
This has a consequence to plan for: a member whose contact visibility is limited but whose role holds the permission to create API tokens (api:create, shown in the role editor as API > Create) can create a token that sees every contact. Keep that permission off roles whose contact visibility you limit.
Token scopes
Scopes control what actions a token can perform. Always select the minimum scopes needed.
Scopes follow the pattern resource:action:
| Pattern | Meaning |
|---|---|
resource:read | View/list resources |
resource:create | Create new resources |
resource:update | Modify existing resources |
resource:delete | Remove resources |
resource:publish | Put a resource live (flows:publish, automation:publish for Conversational Agents) |
resource:manage | Full administrative access |
resource:* | All actions on a resource |
Common scope combinations
Send messages only:
messages:create, conversations:read, contacts:read
Read-only dashboard:
conversations:read, contacts:read, messages:read, campaigns:read
Marketing automation:
contacts:read, contacts:create, contacts:update, campaigns:read, campaigns:create, campaigns:update, lists:read, lists:update
Full CRM integration:
contacts:*, custom_fields:*, conversations:*, messages:*, tags:*, notes:*
Build and publish Conversational Agents (leave out automation:publish to build and test without going live):
automation:read, automation:create, automation:update, automation:publish, knowledge_base:read, knowledge_base:create, custom_fields:read, custom_fields:create
Workspace scopes
Two scopes act on your billing account rather than on one workspace. Both only work for an owner of the billing account.
| Scope | What it allows | Notes |
|---|---|---|
tenants:create | Create new workspaces (POST /tenants), and with an API token also billing accounts | Included in *:* and tenants:* for API tokens. OAuth apps must request it by name, and can only add workspaces to their own billing account; they can never create a billing account. API tokens created before 27 October 2026 keep this ability without it (replace the token to remove it). See Creating Sub-Accounts. |
tenants:manage | Act on other workspaces of the same billing account with the X-Tenant-ID header | API tokens only, never OAuth apps. Must be listed by name; *:* doesn't include it. See Cross-Account API Access. |
Insufficient scope error
If your token lacks a required scope:
{
"detail": "Missing required permission(s): campaigns:create",
"code": "missing_scope",
"required_scopes": ["campaigns:create"],
"require_all": false,
"reason": "token_scope"
}
The response is a 403. required_scopes lists the scopes the endpoint accepts. reason is token_scope when the token itself lacks the scope (create a new token with it, or edit the token's scopes) and role when the signed-in user's role doesn't grant it. See Error handling.
2. OAuth Apps
OAuth apps allow third-party applications to act on behalf of a SendSeven user. Use this when building integrations that need to access multiple users' accounts (e.g., Zapier, Make.com, CRM connectors).
Manage OAuth apps at: Settings > OAuth Apps
How it differs from API tokens
| API Token | OAuth App | |
|---|---|---|
| Who creates it | The account owner, for their own use | A developer, for any user to authorize |
| Credentials | A single token string | Client ID + Client Secret |
| User interaction | None - token works immediately | User must authorize the app via a consent screen |
| Multi-tenant | Scoped to one tenant | Can be authorized by users across different tenants |
| Best for | Your own integrations | Apps you distribute to other SendSeven customers |
| Contact visibility | All contacts | The authorizing member's level (details) |
OAuth apps cannot create, change, regenerate or delete API tokens, and cannot register or manage OAuth apps. These calls return 403 with the code oauth_not_allowed.
Actions that need a signed-in user
A few account-level actions can only be done by a person signed in to the SendSeven app. API tokens, OAuth apps and embed tokens get 403 with the code token_not_allowed, whatever scopes they carry and even when the token was created by the owner:
| Endpoint | Action |
|---|---|
DELETE /api/v1/tenants/{tenant_id} | Deactivate (delete) a workspace |
DELETE /api/v1/users/me | Delete your own user account |
DELETE /api/v1/billing-accounts/{billing_account_id} | Delete a billing account |
POST /api/v1/billing-accounts/{billing_account_id}/transfer-ownership | Transfer billing-account ownership |
POST, PUT, DELETE on /api/v1/billing-accounts/{billing_account_id}/users… | Add, re-role or remove billing-account users |
POST /api/v1/billing-accounts/{billing_account_id}/cancel-subscription | Cancel the subscription |
POST /api/v1/billing/cancel, /billing/cancel-subscription, /billing/cancel-deletion, /billing/reactivate | Cancel, schedule or undo deletion, reactivate |
POST /api/v1/billing/subscribe (and GET /billing/subscribe/preview) | Only while the account is cancelled or scheduled for deletion |
The /billing/* actions above are also limited to the billing-account owner: other members get 403 with the code billing_owner_required.
{ "detail": { "error": "...", "code": "token_not_allowed" } }
Setting up an OAuth App
- Go to Settings > OAuth Apps
- Click Create App
- Fill in:
- App Name: Your application name (shown on the consent screen)
- Redirect URIs: URLs where users will be redirected after authorization
- Scopes: Permissions your app will request
- Note your Client ID and Client Secret
Authorization Code Flow
Step 1: Redirect the user to the authorization endpoint:
https://api.sendseven.com/api/v1/oauth/authorize?
response_type=code&
client_id=YOUR_CLIENT_ID&
redirect_uri=https://yourapp.com/callback&
scope=contacts:read messages:create&
state=random_state_value&
tenant_id=OPTIONAL_TENANT_ID
The optional tenant_id parameter pre-selects a specific workspace on the consent screen. This is useful when re-authorizing for a known tenant (e.g., after token expiry).
Step 2: User authorizes your application on the SendSeven consent screen.
Step 3: Receive the authorization code at your redirect URI:
https://yourapp.com/callback?code=AUTH_CODE&state=random_state_value
Step 4: Exchange the code for an access token:
curl -X POST "https://api.sendseven.com/api/v1/oauth/token" \
-H "Content-Type: application/json" \
-d '{
"grant_type": "authorization_code",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET",
"code": "AUTH_CODE",
"redirect_uri": "https://yourapp.com/callback"
}'
Response:
{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"refresh_token": "v1.refresh.abc123...",
"expires_in": 3600,
"token_type": "Bearer",
"scope": "contacts:read messages:create",
"tenant_id": "64f3a695-6ce7-4822-b3ae-dc20d2967c26",
"tenant_name": "My Company"
}
The tenant_id and tenant_name fields tell you which workspace was authorized. Use these to store tokens per-tenant when a user belongs to multiple workspaces. See the OAuth guide for details.
Step 5: Use the access token in API requests (no X-Tenant-ID needed):
curl -X GET "https://api.sendseven.com/api/v1/contacts" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
-H "Content-Type: application/json"
Refreshing tokens
Access tokens expire after 1 hour. Use the refresh token to get a new one:
curl -X POST "https://api.sendseven.com/api/v1/oauth/token" \
-H "Content-Type: application/json" \
-d '{
"grant_type": "refresh_token",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET",
"refresh_token": "v1.refresh.abc123..."
}'
The refresh token is not rotated and does not expire; it works until the connection is revoked. Each authorization is its own connection, so connecting the same app again (for example from a second client) does not invalidate the first connection's refresh token. See Several connections.
3. Bearer Tokens (JWT)
Bearer tokens are short-lived JWT tokens issued by Auth0 for authenticated web and mobile app sessions. These are used internally by the SendSeven dashboard and mobile app.
You typically don't need this method for API integrations. Use API Tokens instead.
Bearer tokens are the only authentication method that requires the X-Tenant-ID header, because the JWT itself doesn't contain tenant information:
curl -X GET "https://api.sendseven.com/api/v1/contacts" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..." \
-H "X-Tenant-ID: your_tenant_id" \
-H "Content-Type: application/json"
Only with Bearer (JWT) tokens. API Tokens and OAuth tokens have the tenant embedded, so no X-Tenant-ID header is required. If you're using API Tokens (recommended), you can ignore this header entirely.
Security Best Practices
- Store tokens in environment variables or a secrets manager
- Use the minimum scopes required for your integration
- Rotate tokens regularly
- Revoke tokens that are no longer in use
- Never embed tokens in client-side code
- Commit tokens to version control
- Share tokens via email or chat
- Grant tokens more scopes than necessary
Environment variable storage
# .env file (never commit this!)
SENDSEVEN_API_TOKEN=s7_api_a1b2c3d4e5f6789012345678abcdef00
import os
token = os.environ["SENDSEVEN_API_TOKEN"]
Troubleshooting
| Error | Cause | Solution |
|---|---|---|
401 "Invalid or expired API token" | Token expired, revoked or malformed, or its creator is no longer an active member of the workspace | Verify format s7_api_<32hex>, check expiration, create a new token with an active member |
401 "Invalid or expired token" | OAuth access token expired or revoked, or the user who authorized it left the workspace | Refresh the token, or have an active member authorize again |
401 "OAuth access token expired. Please reconnect your account." | OAuth refresh no longer possible | Run the authorization flow again |
403 "Not authenticated" | No Authorization header | Include Authorization: Bearer <token> |
401 "Invalid authorization header" / "Invalid token format" | Malformed Authorization header or token | Send Authorization: Bearer <token> with the full token |
403 "Missing required permission(s): ..." | Token lacks a required scope | Add the missing scope or create a new token |
503 "Authentication temporarily unavailable, please retry" | Temporary problem checking the token | Retry with backoff |
400 missing tenant | Using a JWT without X-Tenant-ID | Add X-Tenant-ID header, or switch to API Tokens |
Authentication and scope errors carry a message string in detail. For the error shapes other endpoints use, see Error Handling.