> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hubtalk.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# The campaign object, blockers and warnings

> What comes back about a campaign, and how to read launch blockers and warnings.

# The campaign object, blockers and warnings

## The campaign object

Returned by `POST /v1/campaigns`, `GET /v1/campaigns/{id}`, `POST …/launch`, `POST …/pause`, `POST …/resume`, `POST …/cancel`.

| Field                                                       | Type           | Description                                                                                                                                                                                  |
| ----------------------------------------------------------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `campaign_id`                                               | string         | Identifier (`cam-…`).                                                                                                                                                                        |
| `status`                                                    | string         | `draft` \| `scheduled` \| `running` \| `paused` \| `completed` \| `cancelled`.                                                                                                               |
| `created_via`, `launched_via`                               | string         | `api` \| `web` \| `""`. `launched_via` is stamped on the launch transition and stays `""` for a draft.                                                                                       |
| `name`, `agent_id`                                          | string         | As stored (name trimmed).                                                                                                                                                                    |
| `timezone`                                                  | string         | The **effective** IANA zone.                                                                                                                                                                 |
| `timezone_source`                                           | string         | `request` when you named a zone, `organization` when it was inherited. Identical on every path (create, read, list).                                                                         |
| `starts_at`, `ends_at`                                      | number \| null | Epoch seconds.                                                                                                                                                                               |
| `accepts_call_additions`                                    | boolean        |                                                                                                                                                                                              |
| `calls_per_minute`                                          | integer        | The value **after** clamping.                                                                                                                                                                |
| `call_strategy`, `classification`                           | string         |                                                                                                                                                                                              |
| `consent_tier`                                              | string         | `pewc` \| `pec` — derived from `classification`, never stored; the tier whose wording your attestation is signed against.                                                                    |
| `retry_config`                                              | object         | Normalized form; rule ids assigned.                                                                                                                                                          |
| `duplicate_action`, `use_contact_timezone`, `working_hours` |                | Normalized echoes of the settings.                                                                                                                                                           |
| `binding`                                                   | object         | `{"trunk_id", "route_id", "from_number"}` — the binding in force; `route_id` is `""` when it is resolved from the trunk at launch rather than stored.                                        |
| `import`                                                    | object \| null | The upload summary of **this request's** `planned_calls` (creation only). `null` when `planned_calls` was empty and on every read path — the summary lives at `GET /v1/imports/{import_id}`. |
| `dnc_screen`                                                | object \| null | Screen of the campaign's current (launch) import: live while a draft, frozen after launch. `null` when the campaign has no import.                                                           |
| `blockers`, `warnings`                                      | array          | Present on create, read and `launch`; **absent** on `pause`/`resume`/`cancel`.                                                                                                               |

**How to read the creation response**

1. `status` — `running` (dialing), `scheduled` (window is in the future) or `draft` (did not start). Nothing else in the body means "it started".
2. A non-empty `blockers` on **creation** ⟹ `status: "draft"`. The campaign and its import report are **kept**. Fix what the blockers name and call `POST /v1/campaigns/{id}/launch`.
3. `import` is `null` when `planned_calls` was empty; `blockers` then carries `no_pending_contacts` (unless the campaign is open to call additions).
4. `warnings` are non-blocking. Two of them are present on practically every campaign: `starts_immediately` (no future `starts_at`) and `frequency_policy_active` (the per-number frequency caps in force) — the latter replaced by `frequency_limits_disabled` when those caps are switched off.
5. `timezone_source` is `request` when you named a zone and `organization` when it was inherited. It reads the same on every path — the create response, `GET /v1/campaigns/{id}` and the list all agree.

## Blockers and warnings

`blockers` and `warnings` are computed by the same launch check the dashboard runs. Each item is an object with a machine-readable `code`; most blockers also carry `field` — the request field the finding is about.

**Shape depends on the path — read only what is guaranteed:**

| Where                                                                                                         | Item shape                              | Notes                                                                                                                                                                                          |
| ------------------------------------------------------------------------------------------------------------- | --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `2xx` campaign object (`POST /v1/campaigns`, `GET /v1/campaigns/{id}`, `POST …/launch`, `GET …/launch-check`) | `{"code", "field"?, "message", …extra}` | `message` is a **Russian, human-oriented diagnostic and not part of the contract** — never match on it or show it to end users. Extra context fields per code are listed below and are stable. |
| `409 launch_blocked` (`error.blockers`)                                                                       | `{"code", "field", "dnc_screen"?}`      | Codes and fields only; do-not-call blockers additionally carry the refreshed `dnc_screen`.                                                                                                     |
| `201` of `POST …/calls`                                                                                       | `{"code", "field"}`                     | Codes and fields only (`field` may be `""`).                                                                                                                                                   |

<Warning>
  **Reading `blockers` on `GET /v1/campaigns/{id}` and `launch-check`.** On the read paths the list answers "what would a launch check report right now", computed **without** an attestation and regardless of the current status. Consequences: a running campaign always shows `attestation_required` (the signature is given per launch request, not stored as a standing state), and a running campaign whose queue has drained shows `no_pending_contacts`. So on read paths a non-empty `blockers` does **not** imply `status: "draft"` — use `status` for that, and read `blockers` as the checklist a future `launch` / `resume` would have to pass.
</Warning>

**Blocker codes**

| Code                      | `field`          | Extra fields                     | Meaning                                                                                                                                                     |
| ------------------------- | ---------------- | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `no_pending_contacts`     | `contacts`       |                                  | No contacts waiting to be dialed. Not raised for a campaign with `accepts_call_additions: true`.                                                            |
| `agent_not_published`     | `agent_id`       |                                  | The agent has no published version.                                                                                                                         |
| `binding_invalid`         | `trunk_id`       |                                  | Trunk, subgroup or caller ID of the binding is unavailable (agent not in the subgroup, empty caller pool, trunk grant missing…).                            |
| `starts_date_past`        | `starts_at`      |                                  | The start **date** (in the campaign zone) is in the past. A past *time* on today's date is only the `starts_immediately` warning.                           |
| `ends_before_starts`      | `ends_at`        |                                  | `ends_at` ≤ `starts_at`.                                                                                                                                    |
| `ends_at_past`            | `ends_at`        |                                  | `ends_at` already passed.                                                                                                                                   |
| `policy_unavailable`      | `market_profile` |                                  | The dialing policy (market package) of your organization is not installed; contact support.                                                                 |
| `contacts_missing_geo`    | `contacts`       |                                  | US market: the upload's contacts carry no geolocation.                                                                                                      |
| `contacts_geo_rejected`   | `contacts`       | `rejected`, `filename`           | US market: rows of the current upload rejected for unusable location. Lifted only by a new upload.                                                          |
| `dnc_screen_required`     | `contacts`       | `dnc_screen`                     | The current import has no recorded do-not-call source-row projection: upload the contacts again.                                                            |
| `dnc_matches`             | `contacts`       | `dnc_screen`, `registry_changed` | Matches with the registry and no valid `skip` decision for this exact screen. `registry_changed: true` means a skip existed but the registry changed since. |
| `all_contacts_suppressed` | `contacts`       | `dnc_screen`                     | Every pending contact is on the registry; cannot be skipped.                                                                                                |
| `attestation_required`    | `attestation`    |                                  | No consent attestation in this request.                                                                                                                     |
| `attestation_stale`       | `attestation`    |                                  | `attestation.import_id` is not the campaign's current import.                                                                                               |
| `rules_empty`             | `retry_config`   |                                  | `expert` strategy with no rules.                                                                                                                            |
| `rule_permanent_retry`    | `retry_config`   | `rule_id`                        | A rule retries a permanent outcome (`invalid_destination`, `spam_blocked`; `user_declined` under the US profile).                                           |
| `campaign_paused`         | `status`         |                                  | **`POST …/calls` only**: the batch is accepted but nothing dials until `resume`.                                                                            |

**Warning codes**

| Code                            | Extra fields                                                                   | Meaning                                                                                                                                                                                             |
| ------------------------------- | ------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `starts_immediately`            |                                                                                | No future `starts_at`: dialing begins at launch.                                                                                                                                                    |
| `frequency_policy_active`       | `rolling_7d_cap`, `daily_cap`, `source` (`organization` \| `platform_default`) | The per-number frequency caps in force for this campaign. Present whenever the limits are on — that is, on every campaign except the case below.                                                    |
| `frequency_limits_disabled`     | `source` (`campaign` \| `organization`)                                        | The configurable frequency limits are switched off, and `source` says by which setting. Comes **instead of** `frequency_policy_active`, never alongside it. Reg F and the state caps keep applying. |
| `working_hours_law_only`        | `field: "working_hours"`                                                       | US market, empty schedule: only the legal window applies.                                                                                                                                           |
| `working_hours_org_window_only` | `field: "working_hours"`, `start`, `end`, `source`                             | Empty schedule: the organization's calling window `start`–`end` applies.                                                                                                                            |
| `retry_schedule_exceeds_end`    | `lost`                                                                         | The retry ladder is longer than what is left of the campaign window; `lost` retries will not happen.                                                                                                |
| `variable_uncovered`            | `variable`                                                                     | The agent uses a variable the rows do not carry; the engine substitutes an empty string.                                                                                                            |
| `variable_case_mismatch`        | `variable`, `column`                                                           | A column was matched to a variable ignoring case/separators.                                                                                                                                        |
| `variable_partial`              | `variable`                                                                     | The variable is filled for some rows only.                                                                                                                                                          |
| `rule_action_unsupported`       | `rule_id`                                                                      | An `expert` rule uses an action the engine does not execute (`retry_in`); the general outcome table decides.                                                                                        |
| `rule_goal_without_criteria`    | `rule_id`                                                                      | A `goal_not_achieved` rule while the agent defines no success criterion — the rule never fires.                                                                                                     |
| `rule_budget_masked`            | `rule_id`                                                                      | A rule's `max_matches` is not below the campaign's attempts — unreachable.                                                                                                                          |
