Skip to Content
LoadersCommanders Act

Commanders Act Loader

The Commanders Act loader pulls data out of the Commanders Act  Config API into your data warehouse: your configuration inventory, the platform audit trail, and the pre-aggregated consent and segment analytics that back the Commanders Act dashboards.

What this loader does — and does not — bring in

Read this before you plan a model on top of it.

It brings in three kinds of data:

  1. Configuration inventory — sources, destinations, segments, Live Report Builder report definitions, the normalized-datalayer event-type catalog, and the cookie-scanner inventory.
  2. Audit logs — the platform modification history: who created, updated, deleted or published which object, and when.
  3. Pre-aggregated analytics — consent-dashboard metrics per privacy banner, and visitor-count statistics for a segment.

It does not bring in any person-level or event-level data. This is a property of the Commanders Act API, not a limitation of the loader:

  • There is no profile or visitor endpoint. The Config API’s Identities tag exists but has zero paths.
  • There is no raw event or hit endpoint, no bulk export, and no asynchronous export-job flow.
  • Segment membership is not readable. The segment stream returns segment definitions (id and label) — never the visitors in them.
  • Consent and audience exports are push-only. Commanders Act sends those out on a schedule you configure in its UI (to email or FTP) or activates them through one of its own destination connectors. There is no pull endpoint for them.

If you need Commanders Act profiles or events in your warehouse, configure a Commanders Act destination that writes to your cloud storage or warehouse, and read that with the Zeotap Google Cloud Storage, S3, or SFTP loader. This loader is the wrong tool for that job.

Prerequisites

  • A Commanders Act account with Config API access.
  • A Config API access token — see Authentication. Onboarding is gated on this: there is no self-service way to create one.
  • Your Commanders Act site ID (the numeric identifier in your platform URL).
  • Platform rights on the token’s user for the streams you want. In particular, the activity-log stream requires the “Reports > Modification history — See and export report” right, and the consent statistics stream requires access to the consent product.
  • A connected Warehouse with write permissions on the target schema.

Authentication

The loader authenticates with a single API token, sent as Authorization: Bearer <token> on every request.

Obtaining the token

The token is issued by your Commanders Act account manager. Commanders Act’s own developer documentation says so directly: “To use config API, you will need an access_token. Please ask it to your account manager.” There is no token endpoint, no self-service key page, and no OAuth flow — so plan for a request-and-wait step during onboarding.

Consequences worth knowing up front:

  • No documented expiry, so no refresh. The token is documented as a JWT bearer token, but no lifetime is published and no refresh endpoint exists. Zeotap therefore treats it as a static secret and never attempts to refresh it. If the token is rotated or revoked, the loader fails with an authentication error and you paste the new token into the loader’s credentials.
  • The token is also sent as a query parameter. Two endpoints (/privacy/statistics and /dms/segment-statistics) declare the token as a required query parameter rather than a header, so Zeotap sends it as a Bearer header on every request, and additionally as a token query parameter on only the three endpoints that document one. Zeotap never writes a request URL into a run log or an error message, so the token does not leak into your logs.

Finding the site ID

Every Config API path is scoped to a site: https://api.commander1.com/v2/{siteId}/.... The siteId is the numeric identifier visible in your Commanders Act platform URL. A wrong value does not produce a “not found” — it produces HTTP 403 SITE_ACCESS_FORBIDDEN, which reads like a permissions problem but almost always means the site ID is wrong.

Configuration

SettingDescriptionDefault
API TokenConfig API access token, from your account manager. Stored encrypted.— (required)
Site IDNumeric site ID — the {siteId} path segment on every request.— (required)
Segment IDInternal numeric segment ID. Required only by the Segment Statistics stream.
Start DateOptional earliest date (YYYY-MM-DD) for the date-windowed streams. Leave blank for a rolling window.
Lookback (days)Size of the trailing window read on every run when no Start Date is set (1–730).30

A note on the Segment ID

The Segment Statistics endpoint filters on filter[segment_id], documented as the internal segment ID. That is not the 8-character public identifier (for example BBFAC450) that the segment inventory and the Commanders Act UI show, and Commanders Act publishes no mapping between the two. Because the two identifier spaces cannot be reconciled from public documentation, Zeotap does not offer a segment dropdown here — a dropdown built from the public IDs would hand the endpoint a value it rejects. Take the internal numeric ID from the Engage UI or ask your account manager.

Start Date vs Lookback

  • Lookback only (recommended). Leave Start Date blank and every run reads a rolling window ending on the last fully-completed UTC day: [today − Lookback, yesterday]. This keeps each run’s work constant no matter how old the account is.
  • Start Date set. Every run reads from that date to the last complete day. Use it for a one-off backfill, then clear it — a permanently-set Start Date means the window (and the run time) grows every day.

Available Streams

StreamEndpointKey columnsIncremental cursor
Activity Logs (activity_logs)GET /activity-logsid (int), log_name, action, description, origin, subject_id (int), subject_type, causer_id (int), properties (json), created_at (timestamp), raw (json)— (date-windowed full refresh)
Sources (sources)GET /sourcesid (int), label, status, occurence_type, integration_key, cron, timezone, the *_at / *_date timestamps, parameters (json), health (json), connector_id, environment_id, raw (json)— (none available)
Destinations (destinations)GET /destinationsSame shape as Sources — both are JSON:API integrations resources— (none available)
Segments (segments)GET /api/dms/segmentation/segments/listid (public segment ID), label, raw (json)— (none available)
Segment Statistics (segment_statistics)GET /dms/segment-statisticssegment_id, bucket_id, start_date (timestamp), nb_visitors_added (int), nb_visitors_removed (int), nb_visitors_total (number), window_from / window_to (date), raw (json)— (restated; re-read each run)
Consent Statistics (consent_statistics)GET /privacy/statisticsbanner_id, stat_date (date), 15 nb_* counters (int), optin_rate / optout_rate (number), raw (json)— (restated; re-read each run)
Event Types (event_types)GET /normalized-datalayer/event-typesid (int), name, label, stored (bool), is_custom (bool), created / updated (timestamp), raw (json)— (none available)
Reports (reports)GET /ams/reportsid (int), public_id, label, description, status, archived (bool), is_system (bool), created_at / updated_at (timestamp), owner_id, raw (json)— (none available)
Cookies (cookies)GET /privacy/cookie-scanner/cookiesid (int), name, detection_status, storage_type, storage_domain, storage_duration, origin, status (int), is_live / is_compliant / is_missing (bool), score (int), website_status (json), before_consent (json), url (json), sample (json), vendor_id, category_id, raw (json)— (none available)

Default streams: Activity Logs, Sources, Destinations, Reports, Event Types, Cookies.

Three streams are off by default:

  • Segments — it is the only stream that does not call the Config API. It calls a different Commanders Act API (the Engage/Data API), whose documented credential is a query-string security token rather than the Config API JWT. It may therefore need a separate token from your account manager, and it can return 401/403 while every other stream succeeds. The connection test probes it and reports a warning rather than failing, so this shows up at setup instead of mid-run.
  • Segment Statistics — needs an internal numeric Segment ID.
  • Consent Statistics — issues one request per day of the window.

Every stream carries a raw column

Each row includes a raw column holding the untouched JSON of the resource it came from. Commanders Act’s configuration objects are extensible — parameters on a destination is a whole smart-mapping tree, health is a nested metrics object with chart datasets, website_status and before_consent on a cookie are maps keyed by website domain — and none of that can be flattened into a fixed column list. Anything the loader does not promote to a typed column is still queryable in raw with your warehouse’s JSON functions.

Streams deliberately left out

Not a streamWhy
GET /ams/reports/{id}/dataReport results. Every report has a different column set defined by the report author, so there is no stable schema to declare. Read report definitions from the reports stream and pull results ad hoc if you need them.
GET /monitoring/sources-data-qualityA monitoring view, not an inventory; its shape depends on the monitoring configuration.
GET /integrations/{id}/delivery/healths, /trends, /issuesPer-destination and require an id per call. They also retain only 30 days, so they cannot backfill. The health json column on the destinations stream already carries the delivery health summary.

Sync Modes

Full refresh only. Every stream replaces the contents of its warehouse table on each run, and no stream declares an incremental cursor. That is a deliberate reading of the API, not a shortcut:

  • Of roughly 70 read endpoints, only /activity-logs has a filterable monotonic field (filter[created]). Its granularity is a date, its inclusivity is undocumented, and the endpoint declares no sort parameter — not enough to checkpoint a high-watermark without risking silent gaps. It is bounded by a date window instead.
  • The aggregate streams take a required from/to range and Commanders Act restates them: late-arriving data changes yesterday’s numbers. Re-reading a trailing window is therefore a correctness requirement, not an optimization — an incremental cursor would freeze the first (wrong) version of each day.
  • No other endpoint exposes an updated_at filter, a sort parameter, or a change feed.

Rather than fake a cursor, Zeotap bounds the work with the Lookback window and lets the full refresh replace the table.

Data freshness

The date window always ends on the last fully-completed UTC day. Because filter[created] and the aggregate range filters are date-grained with undocumented inclusivity, and the aggregates for the current day are still being written, a partial day cannot be read reliably. The practical effect: a change made today appears after tomorrow’s run, so allow up to 48 hours of latency on the activity-log and aggregate streams. The configuration streams (sources, destinations, segments, reports, event types, cookies) are not windowed and are always current as of the run.

How It Works

  • Bounded memory on unpaginated endpoints. Commanders Act documents JSON:API pagination (page[number], page[size]) but declares it on only 2 of about 70 read endpoints. Every other endpoint returns its entire collection in a single response body. The loader therefore stream-decodes those bodies element by element and emits rows in bounded sub-batches, instead of unmarshalling a whole collection into memory. Loader runs have a fixed memory budget, and this is what keeps a large cookie or destination inventory inside it.
  • /activity-logs is windowed, never read whole. It has no pagination at all and grows forever, so the filter[created] window is the only thing bounding the response. Long ranges are split into 30-day sub-windows, each fetched and emitted separately.
  • Consent statistics are read one day at a time. The endpoint returns a single aggregate per banner for whatever range you ask for. Asking day by day gives each row a stable (banner_id, stat_date) grain that survives restatement, rather than a pile of overlapping range totals.
  • Page URLs are rebuilt, never followed. On the paginated endpoints the loader increments page[number] against your configured host. It deliberately ignores the links.next URL in the response: Commanders Act’s own documented response examples emit https://api-internal.commander1.com/..., an internal host that does not resolve from outside their network. The loop terminates on the signals in priority order — meta.pagination.total_pages first, then links.next, and a short page only when neither exists (a server that clamps page[size] must not be mistaken for the end of the data) — meta.pagination.total_pages reached, a short page, or no next link — because not every response carries all three.
  • Explicit type coercion on every non-text column. Several counters come back from the API as quoted strings ("nb_visitors_added": "33632"), so the loader parses them into real integers rather than pushing a string into an integer column. nb_visitors_total is a fractional average (for example 540287.75) and is declared as a floating-point column, not an integer. The genuine JSON booleans (stored, is_custom, archived, is_system, is_live, is_compliant, is_missing) land as real booleans. Large numeric values are decoded exactly, so a 13-digit millisecond timestamp is never rendered in scientific notation.
  • Batching, retries and writes are the pipeline’s job. The connector fetches and shapes rows; Zeotap’s loader pipeline handles batching into the warehouse, retries, heartbeats, and table replacement.

Rate Limits

Commanders Act publishes its limits:

  • 30 requests per second.
  • 30 concurrent connections.

The loader paces its requests below that ceiling and backs off on HTTP 429. Note that Commanders Act sends no Retry-After header, so the loader uses its own exponential backoff (starting at 2 seconds) rather than a server-suggested delay. 5xx responses are retried the same way.

The two 429 codes you may see in a failure message are REQUEST_LIMIT_REACHED (the per-second rate) and CONNECTION_LIMIT_REACHED (too many simultaneous connections — usually another integration of yours holding them open).

Troubleshooting

IssueSolution
HTTP 403 SITE_ACCESS_FORBIDDENThe Site ID is almost certainly wrong. This is the signature error for a bad site_id — it reads like a permissions failure but is really “this token has no access to that site”. Confirm the numeric site ID in your Commanders Act platform URL. If the site ID is definitely right, the token’s user has not been granted access to the site.
HTTP 401 MISSING_AUTHORIZATION_HEADER / UNKNOWN_TOKEN / INVALID_TOKEN_TYPEThe token is missing, unknown, or was revoked. There is no refresh path — request a fresh token from your Commanders Act account manager and update the loader’s credentials.
HTTP 403 on one stream only, while others workStreams are gated by per-user platform rights. Activity Logs needs “Reports > Modification history — See and export report”; Consent Statistics needs consent-product access. Ask your Commanders Act administrator to grant the right to the token’s user, or deselect that stream.
The segments stream 401s or 403s while every other stream worksExpected: it calls the Engage/Data API, not the Config API, and wants that API’s security token. Ask your account manager for it, or deselect the stream.
the response carried no \data` member`The endpoint returned HTTP 200 with something that is not a JSON:API success document — an empty body, an error body served with 200, or a changed response shape. The run fails rather than replacing your table with zero rows.
”the segment_statistics stream requires the segment_id config field”That stream needs an internal numeric segment ID. It is not the 8-character public ID from the segments table — see A note on the Segment ID.
Segment Statistics returns nothing, or an error, for a valid-looking IDYou have most likely supplied the public segment ID. The endpoint expects the internal numeric ID and Commanders Act does not publish the mapping — get it from the Engage UI or your account manager.
HTTP 429 REQUEST_LIMIT_REACHED / CONNECTION_LIMIT_REACHEDYou are at the documented 30 requests/second or 30 concurrent connections, usually because another integration is running at the same time. The loader retries with backoff; if runs keep failing, stagger the loader’s schedule or shrink the Lookback window so it issues fewer requests.
Yesterday’s activity or consent numbers are missingThe window ends on the last fully-completed UTC day, so today’s data appears on tomorrow’s run. Allow up to 48 hours of latency on the windowed streams.
Aggregate numbers changed for a day that already loadedExpected — Commanders Act restates recent days as late data arrives. That is exactly why the Lookback window re-reads them. Widen Lookback if your restatements land later than the current window.
Runs get slower every dayYou left Start Date set, so every run re-reads from that fixed date. Clear it to go back to a rolling Lookback window.
The consent stream issues a lot of requestsIt reads one day per request by design, so a 30-day Lookback is 30 requests. Shrink Lookback, or run it on its own less-frequent schedule.
parameters, health, website_status or before_consent look like unstructured textThey are stored as JSON text (Snowflake keeps VARIANT). Their keys are dynamic — per connector, or per website domain — so they cannot become fixed columns. Query them with your warehouse’s JSON functions.
Only some consent counters are populatedCommanders Act returns only statistics greater than zero (opt-in and opt-out rates are the exception), so absent counters land as NULL. Treat NULL as zero in your models.
No profiles, visitors or events in any streamExpected — see What this loader does and does not bring in. The Config API has no person-level or event-level endpoints. Route that data out through a Commanders Act destination to storage and load the files instead.
A timestamp column is NULL but raw shows a valueThe value did not match any documented ISO-8601 layout, so it was not coerced rather than being coerced wrongly. The original text is preserved in raw.

Next Steps

  • Create a model over the sources and destinations tables to keep an auditable inventory of every integration and its delivery health.
  • Join activity_logs.subject_type / subject_id back to sources, destinations and reports to build a change-history view of your Commanders Act configuration.
  • Track consent performance over time from consent_statistics, keyed on (banner_id, stat_date).
  • Use the cookies table to report on cookies detected before consent (before_consent) and on cookies that are declared but no longer present (is_missing).
Last updated on