Summary and recommendation
Klaviyo exposes a REST user management API at `https://a.klaviyo.com/api` using private API key authentication (Bearer token). Every request requires both an `Authorization: Klaviyo-API-Key <key>` header and a `revision` header (e.g., `revision: 2024-02-15`). Omitting the revision header or using a stale value can produce deprecated behavior.
The API follows JSON:API spec - PATCH requests must include resource `type` and `id` in the request body. A separate SCIM 2.0 endpoint (`https://a.klaviyo.com/scim/v2`) is available on paid plans with SSO enabled; its bearer token is generated independently from REST API keys.
API quick reference
| Has user API | Yes |
| Auth method | API Key (Bearer token via private API key) |
| Base URL | Official docs |
| SCIM available | Yes |
| SCIM plan required | Paid plans (Email plan or higher; SSO must be enabled first) |
Authentication
Auth method: API Key (Bearer token via private API key)
Setup steps
- Log in to Klaviyo and navigate to Account > Settings > API Keys.
- Click 'Create Private API Key' and assign a name and the required scopes.
- Copy the generated private key (shown only once).
- Include the key in all API requests as an HTTP header: 'Authorization: Klaviyo-API-Key your-private-api-key'.
- Include the required API revision header: 'revision: 2024-02-15' (or current stable revision).
Required scopes
| Scope | Description | Required for |
|---|---|---|
| accounts:read | Read account information | GET /api/accounts/ |
| users:read | Read user records within the account | GET /api/users/, GET /api/users/{id}/ |
| users:write | Create and update user records | POST /api/users/, PATCH /api/users/{id}/ |
| roles:read | Read role definitions | GET /api/roles/ |
| roles:write | Assign or modify roles for users | POST /api/role-assignments/, DELETE /api/role-assignments/{id}/ |
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| id | string | Unique Klaviyo-assigned user identifier | system-generated | immutable | UUID format; used in all user-specific endpoint paths. |
| string | User's email address; used as login credential | required | optional | Must be unique within the account. | |
| full_name | string | User's full display name | optional | optional | |
| role | string (enum) | User's role within the account (e.g., 'owner', 'admin', 'manager', 'analyst', 'campaign_manager', 'content_creator', 'support_specialist') | required | optional | Role assignment is managed via the role-assignments sub-resource. When SCIM is active, role changes should be managed from the IdP. |
| status | string (enum) | Account status of the user: 'active' or 'pending' | system-set (pending until invite accepted) | read-only via REST; managed via SCIM deactivation | Newly invited users remain 'pending' until they accept the email invitation. |
Core endpoints
List Users
- Method: GET
- URL:
https://a.klaviyo.com/api/users/ - Watch out for: Returns only users within the authenticated account. Pagination is cursor-based; do not use offset. Pending (uninvited) users are included.
Request example
GET /api/users/ HTTP/1.1
Host: a.klaviyo.com
Authorization: Klaviyo-API-Key pk_xxxx
revision: 2024-02-15
Accept: application/json
Response example
{"data":[{"type":"user","id":"abc123","attributes":{"email":"user@example.com","full_name":"Jane Doe","role":"admin","status":"active"}}],"links":{"self":"...","next":"..."}}
Get User
- Method: GET
- URL:
https://a.klaviyo.com/api/users/{id}/ - Watch out for: The {id} must be the Klaviyo internal user UUID, not the email address.
Request example
GET /api/users/abc123/ HTTP/1.1
Host: a.klaviyo.com
Authorization: Klaviyo-API-Key pk_xxxx
revision: 2024-02-15
Response example
{"data":{"type":"user","id":"abc123","attributes":{"email":"user@example.com","full_name":"Jane Doe","role":"admin","status":"active"}}}
Invite / Create User
- Method: POST
- URL:
https://a.klaviyo.com/api/users/ - Watch out for: Creating a user sends an email invitation; the user status is 'pending' until accepted. You cannot set a password via the API. Only one role can be assigned at creation.
Request example
POST /api/users/ HTTP/1.1
Host: a.klaviyo.com
Authorization: Klaviyo-API-Key pk_xxxx
revision: 2024-02-15
Content-Type: application/json
{"data":{"type":"user","attributes":{"email":"newuser@example.com","full_name":"John Smith","role":"analyst"}}}
Response example
{"data":{"type":"user","id":"xyz789","attributes":{"email":"newuser@example.com","full_name":"John Smith","role":"analyst","status":"pending"}}}
Update User
- Method: PATCH
- URL:
https://a.klaviyo.com/api/users/{id}/ - Watch out for: PATCH requires the resource 'id' in both the URL and the request body. Email cannot be changed via PATCH. When SCIM is enabled, role changes made via REST API may be overridden by the IdP on next sync.
Request example
PATCH /api/users/xyz789/ HTTP/1.1
Host: a.klaviyo.com
Authorization: Klaviyo-API-Key pk_xxxx
revision: 2024-02-15
Content-Type: application/json
{"data":{"type":"user","id":"xyz789","attributes":{"full_name":"John A. Smith","role":"manager"}}}
Response example
{"data":{"type":"user","id":"xyz789","attributes":{"email":"newuser@example.com","full_name":"John A. Smith","role":"manager","status":"pending"}}}
Delete User
- Method: DELETE
- URL:
https://a.klaviyo.com/api/users/{id}/ - Watch out for: Deleting a user is permanent and cannot be undone via the API. If SCIM is active, prefer deactivating via the IdP to maintain sync state.
Request example
DELETE /api/users/xyz789/ HTTP/1.1
Host: a.klaviyo.com
Authorization: Klaviyo-API-Key pk_xxxx
revision: 2024-02-15
Response example
HTTP 204 No Content
List Roles
- Method: GET
- URL:
https://a.klaviyo.com/api/roles/ - Watch out for: Role IDs are string enums, not UUIDs. The available roles are fixed by Klaviyo and cannot be customized.
Request example
GET /api/roles/ HTTP/1.1
Host: a.klaviyo.com
Authorization: Klaviyo-API-Key pk_xxxx
revision: 2024-02-15
Response example
{"data":[{"type":"role","id":"admin","attributes":{"name":"Admin"}},{"type":"role","id":"analyst","attributes":{"name":"Analyst"}}]}
Get Account
- Method: GET
- URL:
https://a.klaviyo.com/api/accounts/{id}/ - Watch out for: A private API key only has access to the single account it was created under. There is no multi-account listing endpoint for standard keys.
Request example
GET /api/accounts/ACCT123/ HTTP/1.1
Host: a.klaviyo.com
Authorization: Klaviyo-API-Key pk_xxxx
revision: 2024-02-15
Response example
{"data":{"type":"account","id":"ACCT123","attributes":{"contact_information":{"organization_name":"Acme Corp"},"industry":"Retail","timezone":"America/New_York","locale":"en-US","currency":"USD"}}}
Rate limits, pagination, and events
- Rate limits: Klaviyo enforces per-endpoint rate limits using a burst + steady-state model. Limits vary by endpoint category. When a limit is exceeded, the API returns HTTP 429.
- Rate-limit headers: Yes
- Retry-After header: Yes
- Rate-limit notes: Klaviyo returns 'Retry-After' header on 429 responses indicating seconds to wait. The 'X-RateLimit-Limit', 'X-RateLimit-Remaining', and 'X-RateLimit-Reset' headers are included in responses. Exact per-endpoint limits are documented in the developer portal and may differ by endpoint family.
- Pagination method: cursor
- Default page size: 20
- Max page size: 100
- Pagination pointer: page[cursor]
| Plan | Limit | Concurrent |
|---|---|---|
| All plans (default REST endpoints) | Varies by endpoint; commonly 75 requests/second burst, 700 requests/minute steady-state for profile/list endpoints | 0 |
| All plans (user/account management endpoints) | Typically lower; ~10 requests/second for account/user endpoints | 0 |
- Webhooks available: Yes
- Webhook notes: Klaviyo supports webhooks for marketing/customer data events (e.g., profile updates, list subscriptions, metric events). However, webhooks are not available for account user-management events such as user creation, role changes, or deactivation.
- Alternative event strategy: For user-management event notifications, use SCIM provisioning with an IdP that supports event logging (e.g., Okta System Log, Azure AD Audit Logs). Alternatively, poll the GET /api/users/ endpoint on a schedule.
- Webhook events: profile.created, profile.updated, profile.merged, list.created, list.deleted, segment.created, segment.deleted, metric.created, campaign.created, campaign.updated, flow.created, flow.updated
SCIM API status
SCIM available: Yes
SCIM version: 2.0
Plan required: Paid plans (Email plan or higher; SSO must be enabled first)
Endpoint: https://a.klaviyo.com/scim/v2
Supported operations: Create Users (POST /Users), Read User (GET /Users/{id}), List Users (GET /Users), Update User Attributes (PATCH /Users/{id}), Deactivate / Deprovision Users (PATCH /Users/{id} with active=false), Get Service Provider Config (GET /ServiceProviderConfig)
Limitations:
- SSO must be fully configured and enabled before SCIM provisioning can be activated.
- Supported IdPs: Okta, Microsoft Entra ID (Azure AD), OneLogin. Google Workspace is not supported for SCIM.
- SCIM token is generated in Klaviyo Settings > Integrations > SCIM; it is shown only once.
- Group/team provisioning is not supported via SCIM; only individual user provisioning.
- When SCIM is active, role assignments are controlled by the IdP; manual role changes in Klaviyo UI or REST API may be overridden on next IdP sync.
- Deactivating a user via SCIM sets them to inactive but does not permanently delete the user record.
- JIT (Just-in-Time) provisioning via SSO is available as an alternative to SCIM but does not support deprovisioning.
- SCIM is often an add-on feature at the IdP level (e.g., Okta SCIM requires Okta Lifecycle Management license).
Common scenarios
Provision a new user: POST to /api/users/ with users:write scope. Klaviyo sends an invitation email automatically - this cannot be suppressed.
The user's status is pending until they accept; poll GET /api/users/{id}/ to confirm activation. If the email already has a Klaviyo account, they are added without a new invite.
Deprovision via SCIM (Okta): Configure the Klaviyo SCIM app in Okta with base URL https://a.klaviyo.com/scim/v2 and the SCIM bearer token from Klaviyo Settings → Integrations → SCIM. Unassigning the user in Okta sends PATCH /Users/{id} with active=false. This deactivates but does not delete the user record. Note: Okta SCIM requires a Lifecycle Management license - not included in all Okta plans.
Bulk user audit: GET /api/users/?page[size]=100 with cursor-based pagination. Follow links.next exactly as returned - do not construct cursors manually. Implement exponential backoff on HTTP 429 responses; user/account endpoints are rate-limited to approximately 10 requests/second.
Critical gap across all scenarios: Removing or deactivating a user via REST or SCIM does not revoke API keys that user created. Those must be audited and revoked separately under Settings → API Keys.
Provision a new team member via REST API
- Generate a private API key with 'users:write' and 'roles:read' scopes in Klaviyo Settings > API Keys.
- Call GET /api/roles/ to retrieve valid role IDs for your account.
- Call POST /api/users/ with the new user's email, full_name, and desired role in the request body.
- Klaviyo sends an invitation email to the new user; their status is 'pending'.
- Poll GET /api/users/{id}/ to check when status changes to 'active' after the user accepts the invite.
Watch out for: The invitation email is sent automatically and cannot be suppressed. If the user already has a Klaviyo account with that email, they will be added to the account without a new invitation.
Deprovision a user via SCIM (Okta)
- Ensure SSO is configured and active in Klaviyo Settings > Integrations > SSO.
- Generate a SCIM token in Klaviyo Settings > Integrations > SCIM and copy it immediately.
- In Okta, configure the Klaviyo SCIM app with base URL 'https://a.klaviyo.com/scim/v2' and the SCIM bearer token.
- Enable 'Push Users' and 'Deactivate Users' in the Okta SCIM provisioning settings.
- To deprovision, unassign the user from the Klaviyo app in Okta. Okta sends a PATCH /Users/{id} with active=false to Klaviyo.
- Verify the user is deactivated in Klaviyo Settings > Account > Users.
Watch out for: Deactivating via SCIM does not delete the user record in Klaviyo; it only sets them inactive. If you later re-assign the user in Okta, they will be reactivated. Ensure the Okta SCIM license (Lifecycle Management) is active, as SCIM is not included in all Okta plans.
Bulk list and audit all account users
- Generate a private API key with 'users:read' scope.
- Call GET /api/users/?page[size]=100 with the revision header.
- Check the response 'links.next' field; if present, call that URL to retrieve the next page.
- Continue following 'links.next' until it is null, collecting all user records.
- For each user, inspect the 'role' and 'status' attributes to build an audit report.
Watch out for: Do not attempt to reconstruct cursor values manually; always use the exact 'next' URL from the response links. Large accounts with many users may require many paginated requests; respect rate limits and implement exponential backoff on 429 responses.
Why building this yourself is a trap
Klaviyo's user API is well-structured but carries several sharp edges that make a DIY integration expensive to maintain. The revision header requirement means Klaviyo can evolve the API schema under a versioned contract - your integration must track and adopt new revisions or risk silent behavioral drift.
When SCIM is active alongside the REST API, role changes made via REST can be silently overwritten by the IdP on the next sync cycle, creating state divergence that is difficult to detect.
There are no webhooks for user-management events (creation, role change, deactivation), so any real-time sync requires polling with proper pagination and backoff logic.
Across ~100 deep integrations, each with its own auth model, pagination scheme, rate limit behavior, and deprovisioning edge cases, the maintenance surface of a self-built solution grows faster than the team that owns it.
Automate Klaviyo workflows without one-off scripts
Stitchflow builds and maintains end-to-end IT automation across your SaaS stack, including apps without APIs. Built for exactly how your company works, with human approvals where they matter.