# Campaign workflow

V1 creates fixed-video campaigns from authorized, frozen template versions. Recipient records do not personalize the video in this release.

1. **Discover a published template.** Use `templates_list` / the matching REST operation with `templates:read`. Inspect the selected template and its versions. A usable version must report `render_source_status: ready` with supported immutable `flat-video-v1` content. Legacy unavailable versions cannot be used as a generation source.
2. **Create the campaign.** Use `campaigns_create` with the exact fields shown in the [reference](/engage/openapi), including the selected template/version. Supply a unique idempotency value. Keep the returned campaign identifier and revision.
3. **Add recipients.** Use `recipients_upsert` within the campaign. Use synthetic recipients when testing. Storing names or contact details does not personalize the rendered media or send messages.
4. **Generate explicitly.** Read the campaign's current state, then call `campaigns_generate` with the required input, revision and idempotency value. This action may consume the configured generation allowance.
5. **Wait for completion.** Poll `jobs_get` according to its polling guidance until terminal. Distinguish completed, failed and partially failed jobs; see [jobs and retries](/engage/errors).
6. **Read results.** Use `campaign_results_list` and `campaign_results_get` for returned media and status. `campaign_analytics_get` reports available campaign analytics; rendering does not establish message delivery or engagement attribution.


REST operation names, routes and payloads are in the generated reference. MCP uses the tool names above with `operation_id` and, where required, `expected_revision`; REST uses `Idempotency-Key` and `If-Match` headers instead.

For paginated operations, follow each operation's schema. Where `limit` and `cursor` are supported, the default limit is 50 and maximum is 100. Reuse the returned `nextCursor` unchanged for the same operation/workspace until null. Do not infer access to other tenants from their identifiers.