Draft and approve a plan
Campaigns require unified v2 campaign routes and account access. createCampaign / create_campaign creates a draft. Review its objective, stages, and deliverables before approval; save the review_digest of that exact reviewed plan. A stale digest returns conflict. These snippets assume a validated plan and an authenticated client.
typescript
const draft = await client.createCampaign(plan, { idempotencyKey: 'discovery-001' });
// Present draft to your reviewer; persist draft.id and draft.review_digest.
// After approval of that exact plan:
const started = await client.approveCampaign(reviewedId, reviewedDigest);
const result = await client.waitCampaign(started.id);python
draft = client.create_campaign(plan, idempotency_key="discovery-001")
# Present draft to your reviewer; persist draft["id"] and draft["review_digest"].
# After approval of that exact plan:
started = client.approve_campaign(reviewed_id, reviewed_digest)
result = client.wait_campaign(started["id"])waitCampaign / wait_campaign returns immediately for a draft; it never approves work. It also returns on completed, failed, cancelled, or needs_attention. Inspect status and stages to decide the next action.
Define a campaign agent
Create an agent with run_mode: 'campaign', a campaign_plan, and its input_form. Template strings use {{field_label}} to insert input values. The backend validates the template and refuses undeclared placeholders or a campaign mode without a plan. getAgent returns the configuration; updateAgent can replace it. To return to ordinary runs, update with { run_mode: 'job', campaign_plan: null }.
typescript
const agent = new Agent({
apiKey: process.env.AGENTRUN_API_KEY!,
name: 'Company discovery',
instructions: 'Find verifiable company information and cite evidence.',
run_mode: 'campaign',
input_form: [{ label: 'goal', type: 'text', required: true }],
campaign_plan: {
name: 'Company discovery', objective: '{{goal}}',
stages: [
{ key: 'discover', title: 'Find companies', op: 'agent',
instruction: 'Find companies matching this goal: {{goal}}',
entity_key_field: 'company_website', records_field: 'companies' },
{ key: 'inspect', title: 'Verify each company', op: 'agent',
instruction: 'Verify this company against the goal: {{goal}}. Cite evidence.',
fanout: 'per_entity', entity_key_field: 'company_website', depends_on: ['discover'],
output_schema: { type: 'object', properties: { evidence: { type: 'string' } } } },
{ key: 'join', title: 'Combine findings', op: 'join', depends_on: ['discover', 'inspect'] },
{ key: 'export', title: 'Export results', op: 'export', depends_on: ['join'],
filename: 'companies.xlsx', entity_key_field: 'company_website',
columns: ['company_website', 'company', 'evidence'], requested: 10 },
],
deliverables: [{ key: 'companies', title: 'Verified companies', kind: 'dataset', from_stage: 'export' }],
},
});
await agent.create();
const campaign = await agent.campaign({ goal: 'Find UK cloud infrastructure companies' }, {
idempotencyKey: 'company-discovery-001',
});This call renders and starts the configured campaign immediately. Use getCampaign(campaign.id) to inspect stage progress and download a ready artifact with downloadCampaignArtifact(campaign.id, 'companies'). The same configuration fields are accepted by MCP create_agent.
Python campaign agents
Use client.create_agent with run_mode="campaign", campaign_plan=plan, and input_form to save the same template structure shown above. Then:
python
agent = client.agent(saved_agent_id)
campaign = agent.campaign(
{"goal": "Find UK cloud infrastructure companies"},
records=[{"company_website": "example.com"}],
idempotency_key="discovery-002",
)agent.campaign in both languages renders and approves the stored template immediately. Use the draft workflow when your application requires a separate review step. records supplies optional seed records; it is separate from template inputs. Goal-to-plan generation is not implemented by these SDK methods.
Read results and download artifacts
getCampaignPortfolio / get_campaign_portfolio takes the campaign ID and stage key, with cursor, limit, status, and search filters. It returns coverage, items, and next_cursor. Follow run_id for full reports. Download by a ready deliverable's key, not its display title.
typescript
const page = await client.getCampaignPortfolio(campaign.id, 'inspect', { limit: 50 });
const blob = await client.downloadCampaignArtifact(campaign.id, 'companies');
const bytes = new Uint8Array(await blob.arrayBuffer());python
page = client.get_campaign_portfolio(campaign["id"], "inspect", limit=50)
data = client.download_campaign_artifact(campaign["id"], "companies")
with open("companies.xlsx", "wb") as output:
output.write(data)cancelCampaign / cancel_campaign stops pending work. Local wait deadlines do not cancel work. Artifacts preserve their original binary bytes.