ActiveCampaign ExecModule
Overview
ActiveCampaignModule connects ValkyrAI workflows to the tenant-scoped ActiveCampaign API v3 for contacts, accounts, deals, lists, and tags. It exposes 14 bounded operations through the native map I/O ExecModule ABI and keeps the user API token in a READY IntegrationAccount.
The module accepts only HTTPS ActiveCampaign tenant origins, sends the token in the Api-Token header, validates record IDs, filters, tenant hosts, and write fields, caps JSON requests at 256 KiB and responses at 5 MiB, returns one offset page per execution, retries transient failures only for safe GET operations, and never automatically retries writes.
The official n8n ActiveCampaign node informed the resource and operation selection. ValkyrAI adds the generated IntegrationAccount security boundary, trusted-host enforcement, bounded payloads, explicit retry safety, normalized outputs, response identity checks, and credential redaction.
Usage
- Create or select a least-privilege ActiveCampaign user with access only to the intended CRM and marketing records.
- In ActiveCampaign, open Settings > Developer and copy the account API URL and API key.
- Store the API key only in the encrypted
apiKeyfield of a ValkyrAIIntegrationAccount, set the account toREADY, and bind it throughExecModuleConfig.authConfig.integrationAccount. - Set
accountUrlto the HTTPS tenant origin, such ashttps://acme.api-us1.com, without/api/3. - Choose one operation and provide its conditional fields.
- For list operations, pass
nextOffsetinto a later execution whenhasMoreistrue.
Inputs
| Name | Type | Required | Description | Constraints |
|---|---|---|---|---|
operation | string | Yes | Operation listed below. | Exact allowlist of 14 values. |
accountUrl | string | Yes | ActiveCampaign tenant API origin. | HTTPS only; clean origin on a supported api-<region><number>.com or activehosted.com tenant host; no credentials, query, fragment, port, or /api/3 path. |
recordId | integer | Conditional | Provider ID for get_* and update_*. | Positive integer. |
record | object | Conditional | Allowlisted fields for create_* and update_*. | Non-empty, at most 50 top-level fields and 256 KiB serialized; credential-like fields rejected. |
query | object | No | Resource-specific list filters. | At most 20 allowlisted scalar fields; values at most 2,048 characters. |
offset | integer | No | Zero-based collection offset. | 0–1,000,000; default 0. |
limit | integer | No | Provider page size. | 1–100; default 100. |
Create requirements:
create_contact: non-emptyrecord.email.create_account: non-emptyrecord.name.create_deal: non-emptyrecord.titleandrecord.currency; positiverecord.contact,record.group, andrecord.stage; non-negative numericrecord.value.
Contact records allow email, firstName, lastName, phone, and fieldValues. Account records allow name, accountUrl, and fields. Deal records allow title, description, contact, organization, group, stage, value, currency, owner, percent, and status.
List-query fields are resource-specific. Contacts support search, email, status, tag/list filters, time filters, id_greater, and documented ordering. Accounts support search and deal-count inclusion. Deals support search, status, owner, contact, pipeline group, stage, and tag filters. Lists and tags accept only their documented name/search filters.
Outputs
| Name | Type | When present | Description |
|---|---|---|---|
status | string | Always | success or error. |
operation | string | Always | Normalized operation. |
resource | string | After validation | contacts, accounts, deals, lists, or tags. |
data | object | Single-record success | Verified provider record. |
id | integer | Single-record success | Positive provider identity. |
items | array | List success | One bounded API page. |
count | integer | List success | Items returned in this page. |
total | integer | List success | Provider-reported meta.total, or the bounded page end if omitted. |
hasMore | boolean | List success | Whether another offset page exists. |
nextOffset | integer | When another page exists | Offset for the next execution. |
attempts | integer | Provider call or error | HTTP attempts consumed. |
error | object | Failure | Redacted code, message, and retryable fields. |
CRM and marketing responses are classified restricted. Tokens, provider bodies, and record values are never copied into progress logs.
IntegrationAccount Requirements
| Setting | Requirement |
|---|---|
| Provider | ActiveCampaign API v3 |
| Authentication | User API key in the Api-Token request header |
| Status | Exactly READY |
accountName | Human-readable tenant/user identity |
apiKey | Encrypted API token, 16–2,048 non-whitespace characters |
| Relationship | ExecModuleConfig.authConfig.integrationAccount |
ActiveCampaign API keys inherit the associated user’s permissions and should be treated as secrets. Use a dedicated least-privilege user, rotate the key deliberately, and never place it in record, query, URLs, examples, logs, or WorkflowState. OAuth application lifecycle and token refresh are outside this version’s contract.
Configuration
Illustrative persisted configuration:
{
"version": "1.0.0",
"authConfig": {
"authStrategy": 1,
"integrationAccount": "integration-account:activecampaign-revenue-automation"
},
"payloadConfig": {
"parameters": "{\"accountUrl\":\"https://acme.api-us1.com\",\"operation\":\"list_deals\",\"limit\":100,\"query\":{\"status\":0}}"
}
}
The relationship is symbolic. The generated IntegrationAccount record owns the encrypted token.
Operations
| Resource | Create | Get | List | Update |
|---|---|---|---|---|
| Contacts | create_contact | get_contact | list_contacts | update_contact |
| Accounts | create_account | get_account | list_accounts | update_account |
| Deals | create_deal | get_deal | list_deals | update_deal |
| Lists | — | — | list_lists | — |
| Tags | — | — | list_tags | — |
Create uses POST /api/3/{resource}, get uses GET /api/3/{resource}/{id}, list uses GET /api/3/{resource}, and update uses PUT /api/3/{resource}/{id}. Contact-list and contact-tag associations, deal notes, e-commerce connections/orders/customers, custom-field administration, automations, webhooks, deletion, and OAuth lifecycle management are deferred.
Errors and Failure Modes
| Failure | Cause | Retry guidance |
|---|---|---|
VALIDATION_ERROR | Missing field, invalid tenant/ID/filter/bound, unsafe write key, invalid success shape, or invalid provider total. | Correct the request; local validation sends no provider call. Invalid provider responses require compatibility review. |
INTEGRATION_ACCOUNT_REQUIRED | No bound secure account. | Bind the intended account. |
INTEGRATION_ACCOUNT_NOT_READY | Account status is not READY. | Repair or reauthorize the account. |
ACTIVECAMPAIGN_HTTP_400/404 | Provider validation failure or missing record. | Correct IDs, field shapes, pipeline/stage values, or filters. |
ACTIVECAMPAIGN_HTTP_401/403 | Invalid token, plan restriction, or insufficient user permission. | Rotate the key or correct the ActiveCampaign user’s access. |
ACTIVECAMPAIGN_HTTP_429 | API rate limit. | Reads use bounded retry and numeric Retry-After; writes remain single-attempt. |
ACTIVECAMPAIGN_HTTP_5xx / NETWORK_ERROR | Provider or transport failure. | Reads retry up to three total attempts. Reconcile create/update state before manually retrying. |
RESPONSE_TOO_LARGE | Provider response exceeded 5 MiB. | Narrow filters or page size. |
Provider errors are bounded to 500 characters and pass through token replacement plus the shared sensitive-data policy. A failed write may have reached ActiveCampaign, so the module refuses automatic replay.
Example
Create an opted-in contact:
{
"accountUrl": "https://acme.api-us1.com",
"operation": "create_contact",
"record": {
"email": "buyer@example.com",
"firstName": "Ada",
"lastName": "Lovelace",
"phone": "+15550123456"
}
}
Expected normalized result:
{
"status": "success",
"operation": "create_contact",
"resource": "contacts",
"id": 123,
"data": {
"id": "123",
"email": "buyer@example.com",
"firstName": "Ada",
"lastName": "Lovelace"
},
"httpStatus": 201,
"attempts": 1
}
Notes
- Pagination: one execution returns one page. Reuse
nextOffset; no silent all-pages loop occurs. ActiveCampaign reportsmeta.total, uses a default page size of 20, and allows at most 100 records. Large contact collections should preferorders[id]=ASCwithid_greateras recommended by the provider. - Rate limits: the module treats HTTP
429as transient, honors bounded numericRetry-After, and caps retries to three safe-read attempts. - API limits: offset is locally capped at 1,000,000, request JSON at 256 KiB, and response JSON at 5 MiB. The provider may enforce stricter plan- or endpoint-specific limits.
- Idempotency:
GETcalls are retry-safe. Creates can duplicate records and updates can complete ambiguously after a timeout, so writes are single-attempt. - Destructive behavior: deletion is intentionally not exposed. Creates and updates can still trigger ActiveCampaign automations, messages, scoring, and sales processes and must be governed as outbound changes.
- Consent: creating a contact does not prove marketing consent. Workflows must establish and preserve lawful consent before subscription or campaign activity.
- External verification: deterministic tests cover validation, routing, JSON envelopes,
Api-Tokenauthentication, offset pagination, retries, write non-retry, redaction, identity checks, trusted tenant hosts, response shapes, and annotation discovery. Live provider execution is deferred until a separately authorized ActiveCampaign account is supplied.
See the official ActiveCampaign authentication guide, pagination guide, contact reference, deals reference, and n8n ActiveCampaign node for the upstream functional reference.