Campaign Monitor lists and preferences

Discover configured lists, read member state and request selected provider list changes. Public list discovery and member preference operations have different access contexts.

Operation choices

TaskOperationAccess
Discover shown listsDiscover shown listsGuest/resource
Read member list stateRead member list stateActive member/resource
Subscribe selected listsSubscribe selected listsActive member/resource
Unsubscribe selected listsUnsubscribe selected listsActive member/resource

Example clients and synthetic values follow response conventions.

Member and provider context

Use an existing active member and valid resource with custom token and resource headers. Shared guards reject inactive users and locked/suspended resource relationships. The resource must already have Campaign Monitor OAuth configuration. These member operations use API identity resolution, including configured Firebase where applicable; see credential transport.

Public list discovery has a separate guest/resource context. For member reads and changes, use the target-selection rule below. The next section explains result filters and cache effects. Each operation describes its provider writes and errors.

Selecting another member

The following role check applies to user_id in member reads and mutations. It differs from Mailchimp’s global admin/root-only check.

Caller contextCan nonempty user_id select another existing user?
Root callerYes.
Nonroot caller with a relationship to the selected resourceYes only when that relationship’s role is readonly, manager, support, admin, partner or root. An ordinary relationship blocks global-role fallback.
Nonroot caller without a relationship to the selected resourceYes only when the caller’s global role is readonly, manager, support, admin, partner or root.
Other callerNo; user_id is ignored and the current member is used.

Omitting user_id selects the current member even for an eligible caller. The selected target must exist, but this lookup does not require the target to belong to the selected resource. The caller still needs the operation’s active-member/resource context.

Member result selection and cache effects

Ordinary/current-member output includes rows whose show_list is strictly true. With an eligible caller’s nonempty user_id, output also includes rows with show_list, require_list or allowed_list truthy. That output filter is separate from mutation eligibility. Reads force fresh provider queries, bypassing the email-key cache read but writing that cache for 600 seconds. The state reread itself does not change provider membership; its cache write is a local side effect. Mutation calls perform the separate writes described in their own sections.

Discover shown lists

GET /api/v1/integrations/campaignmonitor/get-lists

Read shown list records stored for the selected Wallkit resource. This is a local configuration read, not a provider list refresh or a member-state check.

Before you call

Guest access is permitted, with a valid resource header and existing Campaign Monitor OAuth configuration. No member token is required. This public operation does not apply the member API’s namespace-specific Firebase user-selection branch, even though both use the same base controller. See credential transport.

Request

No request body or action-specific query parameters.

NameLocationType / requirementMeaning
resourceheaderresource public key; requiredSelects stored configured list records.

Result

HTTP 200 with items array, sorted by list_name. Rows whose show_list is false after Boolean conversion are omitted; no pagination or live provider call.

FieldType / presenceMeaning
items[].idstored Wallkit integer / nullLocal resource-list row ID; do not send it as provider list_id.
items[].list_idstored provider identifier string / nullProvider list key used in subscribe/unsubscribe.
items[].list_name, items[].client_namestored string / nullList and provider-client display names.
items[].confirmed_optstored flag / null; no explicit castConfigured opt-in setting, not member confirmation.
items[].unsubscribe_settingsstored flag / null; no explicit castTrue stored for AllClientLists setting; not an executed unsubscription.

Example: Discover a newsletter list

curl "${WALLKIT_API_BASE}/api/v1/integrations/campaignmonitor/get-lists" \
  -H "resource: ${RESOURCE_KEY}"
const response = await fetch(`${process.env.WALLKIT_API_BASE}/api/v1/integrations/campaignmonitor/get-lists`, {
  method: "GET", headers: { "resource": process.env.RESOURCE_KEY }
});
console.log(response.status, await response.json());
import os, json
from urllib.request import Request, urlopen
from urllib.error import HTTPError

request = Request(os.environ["WALLKIT_API_BASE"] + "/api/v1/integrations/campaignmonitor/get-lists",
    headers={"resource": os.environ["RESOURCE_KEY"]}, method="GET")
try:
    with urlopen(request) as response:
        print(response.status, json.load(response))
except HTTPError as error:
    print(error.code, json.load(error))

Synthetic HTTP 200 excerpt:

{
  "items": [
    {
      "id": 1001,
      "list_id": "list-example",
      "list_name": "Technology",
      "client_name": "Example publication",
      "confirmed_opt": true,
      "unsubscribe_settings": false
    }
  ]
}

Keep local ID 1001 separate from provider list-example. The result does not say this member is subscribed.

Consequential alternate

Synthetic HTTP 200 excerpt:

{
  "items": []
}

No shown stored rows are available. Other require_list/allowed_list rows may still be eligible for member mutations; this public list is not the full write allowlist.

Recovery

HTTP statusCode / shapeCauseNext action
500integration_error / Campaignmonitor integration not configuratedResource lacks OAuth configuration.Ask the integration owner to check the existing integration.
404resource_not_exists / Incorrect resource keyResource resolution failed.Check the resource public key.
200items:[]No stored rows pass show_list.Handle no displayed choices; ask the owner about intended shown configuration.
Shared failureNo dedicated action error shapeLocal record lookup throws.Have the owner inspect the resource configuration.

Next task

With existing member context, use read member state to see provider flags for shown lists. Supply provider list_id, not local id, when constructing a mutation.

Read member list state

GET /api/v1/integrations/campaignmonitor/interests

Read current provider state for the selected member across visible configured lists. The GET performs synchronous provider reads and writes a local cache; it is separate from public stored-list discovery.

Before you call

Use the member/provider context and target-selection role check; omission selects the current member.

Request

No request body.

NameLocationType / requirementMeaning
resourceheaderresource public key; requiredSelects the existing integration.
tokenheadermember session token; requiredSelects caller identity for member operations.
user_idqueryoptional integer-filtered Wallkit IDHonored only when the caller passes the target-selection role check; otherwise ignored.

Result

HTTP 200 with lists, an array that can be empty. This combines stored list configuration with synchronous provider state, not raw subscriber data.

FieldType / presenceMeaning
lists[].list_idstored provider identifier string / nullProvider list key; use this in mutation lists.
lists[].list_namestored string / nullConfigured display name.
lists[].confirmed_optstored flag / null; no explicit castStored confirmed-opt-in setting, not evidence of this member’s consent/confirmation.
lists[].unsubscribe_settingsstored flag / null; no explicit castTrue is stored for provider AllClientLists setting; not proof that this request changed other lists.
lists[].subscribedBooleanTrue only when returned state is Active; false for other or blank states.
lists[].stateprovider string / null; blank possibleReturned provider State; no complete enum guarantee. Blank can follow a caught provider client error, not just absence.
lists[].updatingBoolean falseFixed value in this projection; not a queue/progress tracker.

See member result selection and cache effects for the returned-list filter and state reread.

Example: Read the current member’s active list

curl "${WALLKIT_API_BASE}/api/v1/integrations/campaignmonitor/interests" \
  -H "resource: ${RESOURCE_KEY}" \
  -H "token: ${USER_TOKEN}"
const response = await fetch(`${process.env.WALLKIT_API_BASE}/api/v1/integrations/campaignmonitor/interests`, {
  method: "GET", headers: { "resource": process.env.RESOURCE_KEY, "token": process.env.USER_TOKEN }
});
console.log(response.status, await response.json());
import os, json
from urllib.request import Request, urlopen
from urllib.error import HTTPError

request = Request(os.environ["WALLKIT_API_BASE"] + "/api/v1/integrations/campaignmonitor/interests",
    headers={"resource": os.environ["RESOURCE_KEY"], "token": os.environ["USER_TOKEN"]}, method="GET")
try:
    with urlopen(request) as response:
        print(response.status, json.load(response))
except HTTPError as error:
    print(error.code, json.load(error))

Synthetic HTTP 200 excerpt:

{
  "lists": [
    {
      "list_id": "list-example",
      "list_name": "Technology",
      "confirmed_opt": true,
      "unsubscribe_settings": false,
      "subscribed": true,
      "state": "Active",
      "updating": false
    }
  ]
}

Active maps to subscribed:true. Fixed updating:false does not mean no other work exists.

Consequential alternate

Synthetic HTTP 200 excerpt with blank provider state:

{
  "lists": [
    {
      "list_id": "list-example",
      "subscribed": false,
      "state": "",
      "updating": false
    }
  ]
}

A provider ClientException can become this blank state. Keep unknown/failed lookup distinct from a confirmed unsubscription.

Recovery

HTTP statusCode / shapeCauseNext action
500integration_error / Campaignmonitor integration not configuratedResource lacks OAuth configuration.Ask the integration owner to check the selected resource’s existing integration.
404user_not_found / User not foundEligible caller’s selected user ID did not resolve.Check the intended existing Wallkit user ID.
Shared failureNo stable action-specific provider error shapeForced reread/adapter construction throws outside a handler catch.Inspect the reported provider condition; account for earlier writes on mutation calls.
200lists:[] / blank stateNo selected visible rows, or a caught provider client error.Handle empty/unknown state and ask the owner to inspect unexpected provider results.

Next task

Use subscribe or unsubscribe only when a selected provider change is intended. Treat blank state as unknown before choosing.

Subscribe selected lists

POST /api/v1/integrations/campaignmonitor/subscribe

Request provider subscribe changes for selected lists. IDs outside the resource’s show_list OR require_list OR allowed_list allowlist are silently skipped, and omitted lists are untouched. This allowlist is broader than public shown-list discovery.

Before you call

Use the member/provider context and target-selection role check; omission selects the current member.

For each eligible list, build member name/email and configured custom fields, then read provider state and add or update the subscriber. Existing state uses an update path. Missing mapped values become empty strings; mapped values over 250 characters fail that list. The adapter sends Resubscribe:true, RestartSubscriptionBasedAutoresponders:true and ConsentToTrack:Yes. These are adapter defaults, not caller-supplied consent evidence. This body does not supply arbitrary merge fields.

Request

Send a JSON object.

NameLocationType / requirementMeaning
resourceheaderresource public key; requiredSelects the existing integration.
tokenheadermember session token; requiredSelects caller identity for member operations.
listsJSONnonempty array of provider list IDs; requiredIDs from existing configured lists, not local row IDs. No deduplication; duplicate IDs can repeat calls.
user_idJSONoptional integer-filtered Wallkit IDSelects another user only when the caller passes the target-selection role check; default current member.

Email/name come from the selected member; direct email, custom-field or consent overrides are not parsed from this body.

Result

HTTP 200 with lists, an array that can be empty. This combines stored list configuration with synchronous provider state, not raw subscriber data.

FieldType / presenceMeaning
lists[].list_idstored provider identifier string / nullProvider list key; use this in mutation lists.
lists[].list_namestored string / nullConfigured display name.
lists[].confirmed_optstored flag / null; no explicit castStored confirmed-opt-in setting, not evidence of this member’s consent/confirmation.
lists[].unsubscribe_settingsstored flag / null; no explicit castTrue is stored for provider AllClientLists setting; not proof that this request changed other lists.
lists[].subscribedBooleanTrue only when returned state is Active; false for other or blank states.
lists[].stateprovider string / null; blank possibleReturned provider State; no complete enum guarantee. Blank can follow a caught provider client error, not just absence.
lists[].updatingBoolean falseFixed value in this projection; not a queue/progress tracker.

See member result selection and cache effects for the returned-list filter and state reread.

Mutations run sequentially and then force a state reread. A caught per-list exception adds error/error_description/req_guid while final status stays HTTP 200 and other lists can continue. A later reread failure can happen after earlier changes. No rollback, deduplication, provider delivery, consent or membership guarantee. A provider ClientException in the add path can return false without an outward error; rely on returned state with its blank-state limitation.

Extra fieldType / presenceMeaning
errorstring; conditionalerror_synchronization after caught per-list failure.
error_descriptionstring; conditionalCM subscribe error.
req_guidstring; with error or shared debug dataCorrelation value, not a provider transaction ID.

Example: subscribe selected lists for the current member

curl -X POST "${WALLKIT_API_BASE}/api/v1/integrations/campaignmonitor/subscribe" \
  -H "resource: ${RESOURCE_KEY}" \
  -H "token: ${USER_TOKEN}" \
  -H "Content-Type: application/json" \
  --data-raw '{"lists": ["list-example"]}'
const response = await fetch(`${process.env.WALLKIT_API_BASE}/api/v1/integrations/campaignmonitor/subscribe`, {
  method: "POST", headers: { "resource": process.env.RESOURCE_KEY, "token": process.env.USER_TOKEN, "Content-Type": "application/json" },
  body: JSON.stringify({"lists": ["list-example"]})
});
console.log(response.status, await response.json());
import os, json
from urllib.request import Request, urlopen
from urllib.error import HTTPError

body = {'lists': ['list-example']}
request = Request(os.environ["WALLKIT_API_BASE"] + "/api/v1/integrations/campaignmonitor/subscribe",
    data=json.dumps(body).encode("utf-8"), headers={"resource": os.environ["RESOURCE_KEY"], "token": os.environ["USER_TOKEN"], "Content-Type": "application/json"}, method="POST")
try:
    with urlopen(request) as response:
        print(response.status, json.load(response))
except HTTPError as error:
    print(error.code, json.load(error))

Synthetic HTTP 200 excerpt:

{
  "lists": [
    {
      "list_id": "list-example",
      "subscribed": true,
      "state": "Active",
      "updating": false
    }
  ]
}

This excerpt omits stored display/settings fields. Read the provider state rather than interpreting updating:false as completion.

Consequential alternate

Synthetic HTTP 200 excerpt after a caught list failure:

{
  "error": "error_synchronization",
  "error_description": "CM subscribe error",
  "req_guid": "example-request",
  "lists": [
    {
      "list_id": "list-example",
      "subscribed": false,
      "state": "",
      "updating": false
    }
  ]
}

HTTP 200 can include a failure. Other lists may already have changed; inspect each returned state and ask the owner about the failed list before repeating. Blank state is not proof of unsubscription.

Recovery

HTTP statusCode / shapeCauseNext action
500integration_error / Campaignmonitor integration not configuratedResource lacks OAuth configuration.Ask the integration owner to check the selected resource’s existing integration.
404user_not_found / User not foundEligible caller’s selected user ID did not resolve.Check the intended existing Wallkit user ID.
Shared failureNo stable action-specific provider error shapeForced reread/adapter construction throws outside a handler catch.Inspect the reported provider condition; account for earlier writes on mutation calls.
406incorrect_data / Body must be json formatNo truthy parsed JSON body.Send a JSON object with Content-Type: application/json.
406incorrect_data / Subscribe lists is emptylists absent/empty, including for unsubscribe.Supply the intended provider list IDs.
200error_synchronization / CM subscribe errorPer-list operation threw.Handle partial state; inspect the failed list before another write.
200No change / blank stateIDs skipped or provider result unconfirmed.Check eligibility and provider state; do not equate HTTP success with mutation success.

Next task

Inspect each returned state and any error before displaying a successful preference change. A later member state read can refresh the display; it does not repair a failed write.

Unsubscribe selected lists

POST /api/v1/integrations/campaignmonitor/unsubscribe

Request provider unsubscribe changes for selected lists. IDs outside the resource’s show_list OR require_list OR allowed_list allowlist are silently skipped, and omitted lists are untouched. This allowlist is broader than public shown-list discovery.

Before you call

Use the member/provider context and target-selection role check; omission selects the current member.

For each eligible list, read provider state first. Missing/blank, Unsubscribed, Unconfirmed and Bounced states skip the unsubscribe POST. Other states trigger a provider unsubscribe call. No deletion of the subscriber record or all-list atomicity is promised.

Request

Send a JSON object.

NameLocationType / requirementMeaning
resourceheaderresource public key; requiredSelects the existing integration.
tokenheadermember session token; requiredSelects caller identity for member operations.
listsJSONnonempty array of provider list IDs; requiredIDs from existing configured lists, not local row IDs. No deduplication; duplicate IDs can repeat calls.
user_idJSONoptional integer-filtered Wallkit IDSelects another user only when the caller passes the target-selection role check; default current member.

Email/name come from the selected member; direct email, custom-field or consent overrides are not parsed from this body.

Result

HTTP 200 with lists, an array that can be empty. This combines stored list configuration with synchronous provider state, not raw subscriber data.

FieldType / presenceMeaning
lists[].list_idstored provider identifier string / nullProvider list key; use this in mutation lists.
lists[].list_namestored string / nullConfigured display name.
lists[].confirmed_optstored flag / null; no explicit castStored confirmed-opt-in setting, not evidence of this member’s consent/confirmation.
lists[].unsubscribe_settingsstored flag / null; no explicit castTrue is stored for provider AllClientLists setting; not proof that this request changed other lists.
lists[].subscribedBooleanTrue only when returned state is Active; false for other or blank states.
lists[].stateprovider string / null; blank possibleReturned provider State; no complete enum guarantee. Blank can follow a caught provider client error, not just absence.
lists[].updatingBoolean falseFixed value in this projection; not a queue/progress tracker.

See member result selection and cache effects for the returned-list filter and state reread.

Mutations run sequentially and then force a state reread. A caught per-list exception adds error/error_description/req_guid while final status stays HTTP 200 and other lists can continue. A later reread failure can happen after earlier changes. No rollback, deduplication, provider delivery, consent or membership guarantee. The response does not return each skipped-list reason; subscribed:false alone cannot distinguish all outcomes.

Extra fieldType / presenceMeaning
errorstring; conditionalerror_synchronization after caught per-list failure.
error_descriptionstring; conditionalCM unsubscribe error.
req_guidstring; with error or shared debug dataCorrelation value, not a provider transaction ID.

Example: unsubscribe selected lists for the current member

curl -X POST "${WALLKIT_API_BASE}/api/v1/integrations/campaignmonitor/unsubscribe" \
  -H "resource: ${RESOURCE_KEY}" \
  -H "token: ${USER_TOKEN}" \
  -H "Content-Type: application/json" \
  --data-raw '{"lists": ["list-example"]}'
const response = await fetch(`${process.env.WALLKIT_API_BASE}/api/v1/integrations/campaignmonitor/unsubscribe`, {
  method: "POST", headers: { "resource": process.env.RESOURCE_KEY, "token": process.env.USER_TOKEN, "Content-Type": "application/json" },
  body: JSON.stringify({"lists": ["list-example"]})
});
console.log(response.status, await response.json());
import os, json
from urllib.request import Request, urlopen
from urllib.error import HTTPError

body = {'lists': ['list-example']}
request = Request(os.environ["WALLKIT_API_BASE"] + "/api/v1/integrations/campaignmonitor/unsubscribe",
    data=json.dumps(body).encode("utf-8"), headers={"resource": os.environ["RESOURCE_KEY"], "token": os.environ["USER_TOKEN"], "Content-Type": "application/json"}, method="POST")
try:
    with urlopen(request) as response:
        print(response.status, json.load(response))
except HTTPError as error:
    print(error.code, json.load(error))

Synthetic HTTP 200 excerpt:

{
  "lists": [
    {
      "list_id": "list-example",
      "subscribed": false,
      "state": "Unsubscribed",
      "updating": false
    }
  ]
}

This excerpt omits stored display/settings fields. Read the provider state rather than interpreting updating:false as completion.

Consequential alternate

Synthetic HTTP 200 excerpt after a caught list failure:

{
  "error": "error_synchronization",
  "error_description": "CM unsubscribe error",
  "req_guid": "example-request",
  "lists": [
    {
      "list_id": "list-example",
      "subscribed": false,
      "state": "",
      "updating": false
    }
  ]
}

HTTP 200 can include a failure. Other lists may already have changed; inspect each returned state and ask the owner about the failed list before repeating. Blank state is not proof of unsubscription.

Recovery

HTTP statusCode / shapeCauseNext action
500integration_error / Campaignmonitor integration not configuratedResource lacks OAuth configuration.Ask the integration owner to check the selected resource’s existing integration.
404user_not_found / User not foundEligible caller’s selected user ID did not resolve.Check the intended existing Wallkit user ID.
Shared failureNo stable action-specific provider error shapeForced reread/adapter construction throws outside a handler catch.Inspect the reported provider condition; account for earlier writes on mutation calls.
406incorrect_data / Body must be json formatNo truthy parsed JSON body.Send a JSON object with Content-Type: application/json.
406incorrect_data / Subscribe lists is emptylists absent/empty, including for unsubscribe.Supply the intended provider list IDs.
200error_synchronization / CM unsubscribe errorPer-list operation threw.Handle partial state; inspect the failed list before another write.
200No change / blank stateIDs skipped or provider result unconfirmed.Check eligibility and provider state; do not equate HTTP success with mutation success.

Next task

Inspect each returned state and any error before confirming unsubscription. For Wallkit membership tasks use subscriptions; provider list unsubscription does not cancel that membership.

Follow the task guide

Read the 3rd party integration flows for request order, context choices and response decisions.

Full diagram

Use the arrow keys to scroll. Escape closes this view.

Search documentation

Enter at least 2 characters.

    ↑ ↓ move through results · Enter opens · Escape closes