Docs Home Beam MCP APIs

Campaigns API

Create draft campaigns and manage their lifecycle with advanced sequencing

Campaign Management

The Campaigns API lets connected tools create draft campaigns, add leads, list and inspect lifecycle state, pause or end campaigns, use Fast Forward on nearly complete campaigns, and reopen ended campaigns using your Organization API Key.

Create campaigns

Create a draft campaign with leads, sending schedule, and automated email sequence steps.

Add leads

Add leads to an existing campaign while applying the same dedupe and validation rules.

Inspect and manage lifecycle

List by exact status or lifecycle scope, inspect available actions, pause or end campaigns, use Fast Forward on nearly complete campaigns, and reopen ended campaigns.

Base URL and Authentication

All requests use the standard bearer token header.

https://beam.lightmeter.io Authorization: Bearer your_api_key_here

Example Request

POST
/api/v1/campaigns/submit
curl -X POST https://beam.lightmeter.io/api/v1/campaigns/submit \ -H "Authorization: Bearer your_api_key_here" \ -H "Content-Type: application/json" \ -d '{ "campaign_name": "Product Launch Outreach", "schedule": { "timezone": "America/New_York", "days": ["Mon", "Tue", "Wed", "Thu", "Fri"], "daily_start_time": "09:00", "daily_end_time": "17:00" }, "lead_dedupe_scope": "all_campaigns", "leads": [ { "email": "contact@example.com", "first_name": "Alex", "last_name": "Johnson", "company_name": "TechCorp Inc", "contact_linkedin_url": "https://linkedin.com/in/alexjohnson", "account_linkedin_url": "https://linkedin.com/company/techcorp", "timezone": "Pacific Standard Time", "attributes": { "job_title": "VP of Engineering", "industry": "Technology", "custom_note": "Interested in automation tools" } } ], "sequence": [ { "step": 1, "type": "new", "variants": [ { "subject": [ "Quick question for {{first_name}}", "An idea for {{company_name}}", "Worth comparing notes?", "One practical question" ], "body": "Hi {{first_name}}, I noticed your recent work at {{company_name}}..." } ] } ] }'

Key Validation Rules

  • Schedule: valid IANA timezone, unique days, HH:MM time format, and start time before end time.
  • Leads: no duplicate emails; LinkedIn URLs must start with http:// or https:// and contain linkedin.com.
  • Attributes: keys must be alphanumeric with _ or - only, up to 100 characters. Reserved keys include id, customer_id, email, first_name, last_name, company, company_name, linkedin_url, timezone, created_at, and updated_at.
  • Sequence: steps must be sequential with no gaps or duplicates.
  • Variants: variants are labeled by array position. A new-message variant can have up to 26 ordered subject options above one shared body. Enabled subjects are selected uniformly; threaded follow-ups inherit the parent subject and support one body variant.
  • Dedupe policy: optional lead_dedupe_scope must be none, active_campaigns, all_campaigns, or all_audiences. Omitted or empty values default to none.
  • Completion deadline: requests may send an exact RFC3339 timestamp, or a legacy YYYY-MM-DD value when a valid campaign timezone is included in the schedule. Responses keep completion_deadline as date-only for compatibility and expose the exact stored cut-off in completion_deadline_at.
  • Workspace suppression: explicit unsubscribe replies add the recipient email to the workspace email blocklist. Remove that blocklist entry before intentionally contacting the recipient again from a future campaign.

Inspect Readiness and Campaign Setup

Use GET /api/v1/campaigns/{campaign_id}/activation-readiness to see the current Ready state and each blocking action for a campaign in the API key's workspace. The /sequences and /sequences/reorderability reads return saved steps and whether they may be reordered. The /mailbox-allocation-policy read returns only the customer-visible overlap_allowed flag; detailed routing controls remain staff-only. Use PUT /api/v1/campaigns/{campaign_id}/schedule with an IANA time_zone and a sending_schedule map such as {"Mon":{"start_time":"09:00","end_time":"17:00"}}. An empty map clears the sending windows. Readiness is recalculated from current campaign data after a change.

POST /api/v1/campaigns/{campaign_id}/duplicate copies reusable content into a new unsent draft. Mailboxes and routing are not copied unless you explicitly choose reviewed infrastructure sharing. DELETE /api/v1/campaigns/{campaign_id}?confirm=true soft-deletes a campaign after it has stopped sending, and POST /api/v1/campaigns/{campaign_id}/restore restores a deleted campaign subject to name and domain ownership checks. POST /api/v1/campaigns/{campaign_id}/test-email sends from an active physical mailbox assigned to this campaign and workspace to the specified test addresses only when the request includes confirm_send: true. It accepts at most 50 addresses and 100 total messages per request; variant_label requires step_number. It supports the same creative snapshot fields as the browser's test-send action. The campaign's click-tracking choice also applies to test emails: tracking On rewrites eligible links on the selected mailbox's sending domain and requires fresh readiness; tracking Off retains ordinary original links and resolves existing Beam redirect links to their final destinations so copied lead claims cannot collect test engagement. Test-link clicks do not enter campaign lead analytics. An unavailable tracking domain is reported in the per-email failed results without sending an untracked fallback.

If mail maintenance interrupts a test batch after any message was accepted, the response is HTTP 200 with code: sending_maintenance_partial, retryable: false, and explicit sent and deferred lists. Never replay the full batch. Retry each recipient in one request using retry.messages with the deferred to, step and variant values, and retry.sent with that recipient's accepted receipts (including message_id and subject). Accepted parents provide threading context and are not resent; deferred steps run in order. HTTP 503 with code: sending_maintenance and Retry-After: 15 is used only when no message was accepted.

Share Campaign Infrastructure

All workspace users and workspace-scoped API keys can deliberately share eligible sending infrastructure between campaigns in that workspace. Sharing starts off. Spam complaints from one campaign can affect the reputation of other campaigns using the same domain, even with different mailboxes. Sharing a physical mailbox also shares its send limits, cooldown and pacing; concurrent campaigns, including the original, may send more slowly. Sharing adds no capacity and does not launch or resume sending. See the manual for manual sequential sending.

First call GET /api/v1/campaigns/{campaign_id}/mailboxes/sharing-preview. Review the named owners, eligible mailboxes, warnings, and review_fingerprint. For creation, read this on the source campaign; for assignment, read it on the destination campaign. Pass that exact value as infrastructure_sharing_review_fingerprint with acknowledge_shared_infrastructure_risks: true and the explicit sharing flag on the write. If ownership or availability changes, the write rejects the old review; refresh and review again.

Sharing mutation requests are limited to 1 MiB of JSON. These operations keep mailbox lifecycle, health, ownership and continuity validation. A sharing choice does not override those checks or grant access to another workspace. Duplication can trigger configured campaign-created workspace webhooks after commit. Lead move and follow-up actions schedule contact-analysis jobs after commit. If a creation response is lost, inspect the campaign list before retrying; retrying can create another draft.

Prepare the Creative Working Copy

Begin with GET /api/v1/campaigns/{campaign_id}/creative. It returns target-labelled Default and adaptation documents plus the server-owned adaptations_activated capability; the capability is not a client request flag or response filter. Use the Creative endpoints to save one or more numbered subjects above each shared body, plus planned subject eligibility, variants, step delays, and threading settings before approval. Updating Creative saves immutable snapshot history but does not change the Ready Creative used for future sends.

Creative update requests accept a JSON body of up to 1 MiB (1,048,576 bytes), counting escaping and whitespace inside the JSON value. Larger requests return HTTP 413 with a message asking you to shorten the copy and try again. API clients can also send fewer documents in one request. A Creative update accepts at most 140,400 documents (50 steps, 26 variants, 26 subjects plus one body, across four targets) and 50 step settings. These are per-request limits, not a total size limit for a campaign. REST generated-draft apply retains its existing 64 KiB JSON limit and HTTP 400 error for an unreadable or oversized draft.

Template audits block launch and resume when Creative exceeds 1,000 distinct template variables, 10,000 template variable uses, 20,000 fallback alternatives across distinct template variables, or 2 MiB of template variable text repeated across its uses. These budgets count matching template variable text at the same position once across boxhost targets; an edited adaptation that shifts positions counts as new uses. The audit returns one blocking invalid_syntax issue with syntax_kind: audit_limit_exceeded, naming the limit and a field where it was crossed. Shorten or remove the indicated template content, then mark Creative Ready again. A partial audit does not permit sending to start. Mark Ready does not run these budgets; a campaign can pass Mark Ready and then be blocked by the launch audit.

Draft subjects and bodies larger than 64 KiB can still be saved within the request limit. Mark Ready rejects active fields larger than 65,536 raw UTF-8 bytes; shorten the named Step, Variant, and field before marking Creative Ready. Campaign submit retains its stricter existing limits: subjects up to 200 bytes and bodies up to 10,000 characters.

Assess draft copy before saving with POST /api/v1/creative/quality/assess. Submit a subject and body string, using an empty subject for a threaded step. The response contains the Creative editor's six advisory metrics, their guidance and descriptions, and text findings with UTF-16 offsets. RANDOM options use expected word counts and the worst option's spam-trigger score; findings still identify each matching option. Each field is limited to 64 KiB, the complete JSON request to 769 KiB including escaping and whitespace, template nesting to 32 levels, total RANDOM alternatives to 1024, and the result to 256 findings. Oversized or overly dense copy is rejected instead of returning a partial assessment. This read-only call does not save, approve, or send Creative.

POST
/api/v1/creative/quality/assess
curl -X POST https://beam.lightmeter.io/api/v1/creative/quality/assess \ -H "Authorization: Bearer your_api_key_here" \ -H "Content-Type: application/json" \ -d '{"subject":"Follow up","body":"Hi {{firstName}}, open to comparing notes?"}'
PUT
/api/v1/campaigns/{campaign_id}/creative
curl -X PUT https://beam.lightmeter.io/api/v1/campaigns/550e8400-e29b-41d4-a716-446655440000/creative \ -H "Authorization: Bearer your_api_key_here" \ -H "Content-Type: application/json" \ -d '{ "overwrite_existing": true, "documents": [ { "step_number": 1, "variant_number": 1, "field_type": "subject", "subject_number": 1, "plain_text": "Quick question for {{first_name}}" }, { "step_number": 1, "variant_number": 1, "field_type": "subject", "subject_number": 2, "is_disabled": true, "plain_text": "An idea for {{company_name}}" }, { "step_number": 1, "variant_number": 1, "field_type": "body", "subject_number": 1, "plain_text": "Hi {{first_name}}, noticed your work at {{company_name}}..." } ], "step_settings": [ { "step_number": 2, "wait_days": 3, "is_threaded": true } ] }'

Creative Approval

Whole-step pause state is returned as paused_step_numbers in Creative reads and is_step_paused on sequences. Mark Creative Ready keeps each paused step at its number when Default body document IDs match or body copy is byte-identical for the same variants. Moving or replacing paused copy requires resuming the step or undoing the change first. A deleted tail loses its pause only after unsettled sends clear, with an attributed Activity event. Step numbers outside 1–50 return 400; a missing configured step returns 404; state conflicts return 409.

Call POST /api/v1/campaigns/{campaign_id}/creative/approve when the current Creative working copy should become an immutable approved snapshot and be marked Ready for future sends. Existing Creative fields are protected from accidental overwrite unless overwrite_existing is true. New subject numbers must leave each body with a contiguous set starting at 1; one request may provide a complete batch in any array order. Optional is_disabled on subject documents plans eligibility for the next Ready Creative. Use PATCH /api/v1/campaigns/{campaign_id}/subjects/{subject_id} to change one current Ready subject without editing or reapproving the shared body.

Generation, History, and Comments

POST /api/v1/campaigns/{campaign_id}/creative/preview renders a templates object (use body for the email body) with optional data and pick_first. It reads current campaign tracking: On returns working anonymous redirect URLs; Off keeps ordinary original links and resolves existing Beam redirects to their final destinations, without allocating links or requiring redirect readiness. The response contains rendered, audit, tracking_enabled, and the selected sending_domain. An optional owned sending_domain selects the host; otherwise an assigned domain is preferred, then an existing ready workspace domain. Unavailable tracking returns 409 with retry guidance. Identical domain/destination pairs reuse immutable anonymous mappings. No Creative, approval, lead, send or engagement identity is created. These links are previews, not an individual lead's authorized send. Use preserve_templates: true for Ready display to retain the approved template text and change only its eligible URL representation. Requests are limited to 256 KiB and 1 to 51 templates totaling at most 128 KiB. Regenerated RANDOM choices return an optional render_seed; this only replays rendering choices and authorizes no send. A text-only preview needs no ready redirect host.

To test the selected current draft, provide the body and subject document IDs with their current version numbers, step, variant and adaptation target. This works before Mark Creative Ready and does not replace the approved snapshot. A threaded draft test sends its current parent draft first. Optional preview_rendering: {"pick_first": true} selects the first RANDOM choices; use {"pick_first": false, "seed": render_seed} to replay a regenerated preview. Optional variables.email, sender_first_name and sender_last_name preserve preview personalization. Omit these additive fields to retain existing test-recipient substitution and rendering. Stale versions return 409 before sending.

GET /api/v1/campaigns/{campaign_id}/creative/generation-capability returns the current workspace_revision, model, source limits, and remaining quota. Generate a transient draft with POST /api/v1/campaigns/{campaign_id}/creative/generations. Provide a fresh UUID operation_id, mode: "initial" or "regenerate", the current base_revision, audience, offer, and exactly one of source_text or source_pdf_base64. Regeneration also requires the current draft, target_step, and target_field. Text is limited to 14 KiB; decoded PDFs are limited to 20 MiB. Generation uses the same included workspace and campaign quota as the Creative editor. A completed operation ID cannot replay its transient result, so retain the returned draft until you apply or discard it.

POST
/api/v1/campaigns/{campaign_id}/creative/generations
{ "operation_id": "d4d4aefe-58d4-4b56-b763-a0823e89df17", "mode": "initial", "base_revision": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "audience": "Operations leaders at growing SaaS teams", "offer": "Send a short implementation checklist", "source_text": "Relevant product and customer context with at least 50 readable characters" }

POST /api/v1/campaigns/{campaign_id}/creative/generated-draft/apply takes generation_request_id, base_revision, and the returned draft. It replaces the editable working copy only if Creative has not changed, and creates a recovery snapshot. It does not mark Creative Ready or send email. GET /api/v1/campaigns/{campaign_id}/creative/workspace returns documents, step settings, checkpoints, comment threads, and the current revision. POST /api/v1/campaigns/{campaign_id}/creative/checkpoints creates a labelled checkpoint for the current documents; POST .../checkpoints/{checkpoint_id}/restore restores one document using expected_revision. GET /api/v1/campaigns/{campaign_id}/creative/workspace-snapshots lists the three most recent retained recovery snapshots; POST /api/v1/campaigns/{campaign_id}/creative/workspace-snapshots/{snapshot_id}/restore requires expected_revision and creates a pre-restore snapshot.

Collaborate through POST /api/v1/campaigns/{campaign_id}/creative/comments with creative_document_id and body, optionally adding an anchor range and selected text. Use POST .../comments/{thread_id}/replies with body, POST .../comments/{thread_id}/resolve and .../reopen, or DELETE .../comments/{thread_id} to soft-delete a thread. API and MCP comments display an explicit agent author name. Real-time browser editing sessions and cursor locks remain part of the signed-in UI collaboration protocol.

Import an audience into a campaign

Use POST /api/v1/campaigns/{campaign_id}/leads/from-audience/preview with {"audience_id":"..."} to inspect active audience members, dedupe conflicts, blocklist matches, and send eligibility. Apply with POST /api/v1/campaigns/{campaign_id}/leads/from-audience and the same audience ID. Preview and apply return a lifecycle conflict when the campaign no longer accepts leads. Requests are limited to 64 KiB. Audiences above 100,000 active leads are rejected by preview and apply without importing a partial audience. The campaign and audience must belong to the API key's workspace. To change the campaign's dedupe policy, pass the same leadDedupeScope in both preview and apply; omit it from both to keep the current policy.

Review Existing Lead Corrections

Submit the complete campaign or add-leads request with preview_only: true. The response names other affected campaigns and compares current and imported values. When confirmation_required is true, obtain an explicit choice, then resend the unchanged request with preview_only: false, review_token from review.token, and decision: "overwrite" or "preserve". A stale review must be reviewed again. Current-campaign-only and finished-only changes do not require an extra decision.

Successful apply responses can contain failed rows. Inspect row_outcomes for every input row: conflicting identities are left unapplied while independently valid rows proceed. Do not retry successful rows. Blank imported values do not erase existing details, and choosing overwrite never merges different people. The user manual explains shared values and finished campaigns.

Manage Campaign Leads

Use an Organization API Key to list and filter campaign leads with GET /api/v1/campaigns/{campaign_id}/leads. The response includes page, page size, total count, status counts, and each lead's current campaign state. GET /leads/removal-reasons returns active reason codes and text requirements. All routes require access to the campaign's workspace.

For a bulk removal, send the same scope (selected or filtered), lead IDs or filters, and exclusions to POST /leads/remove/preview, inspect the removable count, then call POST /leads/remove with an active reason_code and confirm_removal: true. The preview is read-only and does not reserve the selection; review again if the campaign changes. POST /leads/{campaign_lead_id}/remove removes one lead with a reason and the same explicit confirmation. Removed membership and history cannot be restored by these routes.

PATCH /leads/{campaign_lead_id}/personalization updates allowed identity and custom-attribute fields through the same validation as the browser. Successful edits record the API key actor and changed field categories in the journal, without copying field values into the audit entry. POST /leads/top-up-from-csv accepts a multipart CSV file and optional column mappings, review token, and preserve/overwrite decision. Use preview_only=true first; an apply that changes shared lead values may require the returned review token and explicit decision. The public multipart request is limited to 10 MiB. Inspect row outcomes for partial success. The matching MCP tools are documented in the MCP guide.