Summary and recommendation
ActiveCampaign's REST API (v3, base URL: `https://{youraccountname}.api-us1.com/api/3`) supports full user lifecycle management: create, read, update, and delete users, plus group retrieval and assignment. Authentication is via a per-user API key passed as the `Api-Token` HTTP header - there is no account-level default key.
The rate limit is 5 requests per second, account-wide and shared across all API keys on the account. Pagination uses offset/limit (default 20, max 100); the users list endpoint does not reliably return a `meta.total` count, so callers must iterate until an empty array is returned.
There is no native SCIM 2.0 support on any plan. For teams managing ActiveCampaign alongside a broader SaaS portfolio, Stitchflow's MCP server with ~100 deep IT/identity integrations provides a structured alternative to building and maintaining tailored polling logic against this API.
API quick reference
| Has user API | Yes |
| Auth method | API Key (Http Header) |
| Base URL | Official docs |
| SCIM available | No |
| SCIM plan required | Enterprise |
Authentication
Auth method: API Key (Http Header)
Setup steps
- Log in to your ActiveCampaign account.
- Click the Settings gear icon in the left sidebar.
- Click 'Developer' in the Account Settings menu.
- Copy the API URL (your base URL) and the API Key.
- Pass the key on every request as the HTTP header: Api-Token:
.
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| id | string | Auto-assigned unique user ID. | read-only (returned) | read-only | Use in URL path for GET/PUT/DELETE /users/{id}. |
| username | string | Unique login username for the user. | required | read-only (cannot be changed after creation) | Must be unique across the account. |
| string | User's email address. | required | optional | Used for login and notifications. | |
| firstName | string | User's first name. | required | optional | |
| lastName | string | User's last name. | required | optional | |
| password | string | User's account password. | required | optional | Write-only; never returned in responses. |
| group | integer | ID of the permission group to assign the user to. | required | optional | Permissions in ActiveCampaign are set at the group level, not the user level. |
| phone | string | User's phone number. | optional | optional | |
| signature | string|null | User's email signature. | optional | optional | Returned as null if not set. |
| lang | string | User's language preference (e.g., 'english'). | optional | optional | Returned in create response; not always present in list response. |
| localZoneid | string | User's timezone (e.g., 'America/New_York'). | optional | optional | IANA timezone string. |
| userGroup | string | ID of the user's current group (returned in update response). | read-only (derived from group param) | read-only (derived from group param) | Linked resource; see links.userGroup for full object URL. |
| cdate | string (ISO 8601) | Date/time the user was created. | read-only (returned) | read-only | |
| udate | string (ISO 8601) | Date/time the user was last updated. | read-only (returned) | read-only | |
| links | object | Hypermedia links to related resources (lists, userGroup, dealGroupTotals, dealGroupUsers, configs). | read-only (returned) | read-only | Use these URLs to fetch related sub-resources. |
Core endpoints
Create a user
- Method: POST
- URL:
https://{youraccountname}.api-us1.com/api/3/users - Watch out for: A paid user seat must be available on the account before a new user can be created. Creating a user does not automatically send an invitation email.
Request example
{
"user": {
"username": "jdoe",
"firstName": "John",
"lastName": "Doe",
"email": "jdoe@example.com",
"password": "myPa$$w0rd",
"group": 4
}
}
Response example
{
"user": {
"id": "3",
"username": "jdoe",
"email": "jdoe@example.com",
"firstName": "John",
"lastName": "Doe",
"lang": "english",
"localZoneid": "America/New_York",
"cdate": "2022-02-02T16:01:44-06:00"
}
}
List all users
- Method: GET
- URL:
https://{youraccountname}.api-us1.com/api/3/users - Watch out for: Response does not include a meta.total field for users - iterate with offset until an empty array is returned.
Request example
GET /api/3/users?limit=20&offset=0
Header: Api-Token: <key>
Response example
{
"users": [
{
"id": "1",
"username": "admin",
"firstName": "John",
"lastName": "Doe",
"email": "admin@example.com",
"phone": "",
"signature": null
}
]
}
Retrieve a user
- Method: GET
- URL:
https://{youraccountname}.api-us1.com/api/3/users/{id} - Watch out for: Use GET /api/3/users/me to retrieve the currently authenticated user without knowing their ID.
Request example
GET /api/3/users/1
Header: Api-Token: <key>
Response example
{
"user": {
"id": "1",
"username": "admin",
"firstName": "John",
"lastName": "Doe",
"email": "admin@example.com",
"phone": "",
"signature": null
}
}
Retrieve the authenticated user (self)
- Method: GET
- URL:
https://{youraccountname}.api-us1.com/api/3/users/me - Watch out for: Useful for validating API key ownership and resolving the caller's user ID.
Request example
GET /api/3/users/me
Header: Api-Token: <key>
Response example
{
"user": {
"id": "1",
"username": "admin",
"email": "admin@example.com"
}
}
Update a user
- Method: PUT
- URL:
https://{youraccountname}.api-us1.com/api/3/users/{id} - Watch out for: username cannot be changed after creation. Full PUT - omitting optional fields may clear them.
Request example
{
"user": {
"firstName": "John",
"lastName": "Doe",
"email": "jdoe2@example.com",
"password": "newPa$$w0rd",
"group": 3
}
}
Response example
{
"user": {
"id": "3",
"username": "user",
"email": "jdoe2@example.com",
"userGroup": "3"
}
}
Delete a user
- Method: DELETE
- URL:
https://{youraccountname}.api-us1.com/api/3/users/{id} - Watch out for: Deleting a user who owns lists, deals, accounts, or tasks requires reassigning those resources first. Deleting a user whose API key is used in integrations will break those integrations immediately - there is no account-default API key.
Request example
DELETE /api/3/users/3
Header: Api-Token: <key>
Response example
HTTP 200 OK
{}
List all user groups
- Method: GET
- URL:
https://{youraccountname}.api-us1.com/api/3/groups - Watch out for: Permissions are managed at the group level. Retrieve group IDs here before assigning users.
Request example
GET /api/3/groups?limit=20&offset=0
Header: Api-Token: <key>
Response example
{
"groups": [
{ "id": "1", "title": "Admin", "descript": "" },
{ "id": "2", "title": "Marketing" }
]
}
Retrieve a user's group
- Method: GET
- URL:
https://{youraccountname}.api-us1.com/api/3/users/{id}/userGroup - Watch out for: A user belongs to exactly one group. Use PUT /users/{id} with a new group value to reassign.
Request example
GET /api/3/users/3/userGroup
Header: Api-Token: <key>
Response example
{
"userGroup": {
"userid": "3",
"groupid": "2",
"id": "3"
}
}
Rate limits, pagination, and events
- Rate limits: 5 requests per second per account on all hosted plans. No documented daily cap. Exceeding the limit results in throttling (HTTP 429). Custom rate limit solutions available by contacting ActiveCampaign.
- Rate-limit headers: No
- Retry-After header: No
- Rate-limit notes: HTTP 429 is returned when the rate limit is exceeded. No official Retry-After header documented. Implement exponential backoff. Each user's API key shares the same account-level bucket.
- Pagination method: offset
- Default page size: 20
- Max page size: 100
- Pagination pointer: limit / offset
| Plan | Limit | Concurrent |
|---|---|---|
| All hosted plans (Starter, Plus, Pro, Enterprise) | 5 requests/second | 0 |
| Custom (contact sales) | Negotiated | 0 |
- Webhooks available: Yes
- Webhook notes: ActiveCampaign supports outbound webhooks that fire on contact and deal activity events. Webhooks are managed via the API (POST/GET/PUT/DELETE /api/3/webhooks). Delivery is guaranteed at-least-once; webhooks are never retried on failure. A webhook is automatically deactivated if the receiving endpoint returns HTTP 410.
- Alternative event strategy: No native webhook events exist for account-user lifecycle actions (user created, user deleted, user role changed). Poll GET /api/3/users on a schedule to detect changes, or use SSO JIT provisioning for onboarding.
- Webhook events: subscribe, unsubscribe, update, bounce, click, open, forward, reply, sent, share, contact_tag_added, contact_tag_removed, subscriber_note, list_add, deal_add, deal_update, deal_note_add, deal_pipeline_add, deal_stage_add, deal_task_add, deal_task_complete, deal_tasktype_add, sms_sent, sms_reply, sms_unsub
SCIM API status
- SCIM available: No
- SCIM version: Not documented
- Plan required: Enterprise
- Endpoint: Not documented
Limitations:
- No native SCIM 2.0 support.
- SSO (SAML) is available on Enterprise plans with Okta and Microsoft Entra ID.
- User provisioning on first SSO login is handled via JIT (Just-In-Time) provisioning only.
- New SSO users are added to a configurable 'SSO Users' permission group.
- No automatic deprovisioning - users must be deleted manually via the UI or DELETE /api/3/users/{id}.
- No SCIM-based group/role sync.
Common scenarios
Three core automation scenarios are supported by the API. Provisioning: verify seat availability, retrieve group IDs via GET /api/3/groups, then POST /api/3/users with username, email, firstName, lastName, password, and group.
note that username is immutable after creation and no invite email is sent automatically.
Deprovisioning: locate the user via GET /api/3/users, reassign any owned lists, deals, accounts, or tasks (the delete will fail if owned resources are not reassigned first), rotate any integrations using that user's API key to a different key, then DELETE /api/3/users/{id}.
the purchased seat count is not reduced by deletion and requires a separate Billing & Upgrade action. Directory sync: paginate `GET /api/3/users?
limit=100&offset=0`, incrementing offset until an empty array is returned, diff against your directory, and POST or DELETE accordingly on a scheduled poll - no user-lifecycle webhook events exist, so polling is the only available sync mechanism.
Provision a new team member
- Verify a user seat is available (check Billing & Upgrade page or account plan).
- GET /api/3/groups to retrieve available permission group IDs.
- POST /api/3/users with username, email, firstName, lastName, password, and group (group ID).
- Store the returned user id for future updates or deletion.
- Share login credentials with the new user out-of-band (no invite email is sent automatically).
Watch out for: If no seats are available, the POST will fail. Seats must be purchased before provisioning. username cannot be changed later.
Deprovision a departing user
- GET /api/3/users to find the user's id by email or username.
- Reassign any owned lists, deals, accounts, and tasks to another user via the UI or relevant API endpoints (e.g., PUT /api/3/deals/{id} to change owner).
- If the user's API key is used in any integrations, update those integrations with a different user's API key first.
- DELETE /api/3/users/{id} to remove the user.
- Note: deleting the user does not reduce the number of purchased seats - the seat remains available for reassignment.
Watch out for: Deleting a user whose API key powers active integrations will immediately break those integrations. There is no grace period or account-default fallback key.
Sync user roster to an external directory
- GET /api/3/users?limit=100&offset=0 to fetch the first page of users.
- Increment offset by 100 and repeat until an empty users array is returned (no meta.total is reliably provided).
- Compare the fetched list against your directory to identify additions and removals.
- For new users in the directory not in ActiveCampaign, POST /api/3/users to create them.
- For users in ActiveCampaign not in the directory, DELETE /api/3/users/{id} after reassigning owned resources.
- Schedule this poll at an interval appropriate for your org (e.g., hourly) - no user-lifecycle webhooks exist to trigger real-time sync.
Watch out for: No webhook events exist for user create/update/delete. Polling is the only sync mechanism. Rate limit is 5 req/sec account-wide - batch carefully on large rosters.
Why building this yourself is a trap
Several API behaviors create silent failure modes that are worth flagging explicitly. The per-user API key model means any integration authenticated with a deleted user's key breaks immediately with no grace period and no account-default fallback - this is the highest-severity operational risk in the API.
The account-wide 5 req/sec rate limit is shared across all keys simultaneously; concurrent integrations using different user keys still draw from the same bucket, and HTTP 429 responses carry no Retry-After header, requiring manual exponential backoff implementation.
No webhook events fire on user create, update, or delete, making event-driven sync architecturally impossible without a polling layer. The PUT /api/3/users/{id} endpoint is a full replacement - omitting optional fields may clear existing values.
Finally, SCIM is entirely absent: SSO JIT provisioning (Enterprise only) handles onboarding on first login but provides no deprovisioning signal and no IdP-driven group assignment, leaving offboarding fully dependent on manual API calls or UI actions.
Automate ActiveCampaign workflows without one-off scripts
Stitchflow builds and maintains identity workflows for your exact setup. We cover every app, including the ones without APIs, and run deterministic trigger-to-report workflows with human approvals where they matter.