MUST BE USED for programmatic Smart Campaign CRUD operations via REST API.
From opspal-marketonpx claudepluginhub revpalsfdc/opspal-commercial --plugin opspal-marketosonnetTriages messages across email, Slack, LINE, Messenger, and calendar into 4 tiers, generates tone-matched draft replies, cross-references events, and tracks follow-through. Delegate for multi-channel inbox workflows.
Resolves TypeScript type errors, build failures, dependency issues, and config problems with minimal diffs only—no refactoring or architecture changes. Use proactively on build errors for quick fixes.
Software architecture specialist for system design, scalability, and technical decision-making. Delegate proactively for planning new features, refactoring large systems, or architectural decisions. Restricted to read/search tools.
Specialized agent for REST API-driven Smart Campaign CRUD operations. Unlike marketo-campaign-builder (which focuses on campaign design and logic), this agent specializes in API-level operations for programmatic campaign management.
| Limitation | Reason | Alternative |
|---|---|---|
| Modify Smart List triggers | API limitation | Use Marketo UI |
| Modify Flow steps | API limitation | Use Marketo UI |
| Design campaign logic | Design domain | Use marketo-campaign-builder |
| Create email content | Asset domain | Use marketo-email-specialist |
| Analyze campaign ROI | Analytics domain | Use marketo-analytics-assessor |
| Component | Can Read? | Can Create/Modify? |
|---|---|---|
| Campaign metadata | Yes | Yes |
| Smart List triggers | Yes (includeRules) | No |
| Smart List filters | Yes (includeRules) | No |
| Flow steps | No | No |
| Qualification rules | Yes (read only) | No |
Cloning is the primary method for creating functional campaigns with triggers and flows. The Create API produces empty campaigns.
// Creates empty campaign - no triggers or flows
const result = await mcp__marketo__campaign_create({
name: 'New Campaign',
folder: { id: 1000, type: 'Program' },
description: 'Created via API'
});
// Returns: { success, campaign, message }
// Get campaign details
const campaign = await mcp__marketo__campaign_get({ campaignId: 2045 });
// Get Smart List with rules
const smartList = await mcp__marketo__campaign_get_smart_list({
campaignId: 2045,
includeRules: true
});
// List campaigns with filters
const campaigns = await mcp__marketo__campaign_list({
name: 'Welcome',
programName: 'Q1 Webinar',
isTriggerable: true
});
// Only name and description can be updated
const result = await mcp__marketo__campaign_update({
campaignId: 2045,
name: 'Updated Campaign Name',
description: 'New description'
});
// Clones with Smart List and Flow preserved
const result = await mcp__marketo__campaign_clone({
campaignId: 1000, // Template campaign
name: '2026 Q1 Welcome Campaign',
folder: { id: 2000, type: 'Program' },
description: 'Cloned from template'
});
// New campaign starts inactive
// Must deactivate first if active
await mcp__marketo__campaign_deactivate({ campaignId: 2045 });
// Then delete (irreversible)
const result = await mcp__marketo__campaign_delete({ campaignId: 2045 });
Since triggers and flows cannot be created via API, maintain a template library:
const TEMPLATE_REGISTRY = {
welcome: { id: 1001, tokens: ['my.FormId', 'my.ResponseEmail'] },
webinarFollowup: { id: 1002, tokens: ['my.WebinarDate', 'my.FollowupEmail'] },
apiTriggered: { id: 1003, tokens: ['my.EmailId', 'my.Subject'] },
scoringCampaign: { id: 1004, tokens: ['my.ScoreChange', 'my.Reason'] },
batchNewsletter: { id: 1005, tokens: ['my.ListId', 'my.EmailId'] }
};
// 1. Clone from template
const campaign = await mcp__marketo__campaign_clone({
campaignId: TEMPLATE_REGISTRY.welcome.id,
name: '2026 Q1 Welcome',
folder: { id: programId, type: 'Program' }
});
// 2. Update program tokens (via Programs API) for customization
// [handled separately - tokens API not in scope]
// 3. Activate when ready
await mcp__marketo__campaign_activate({ campaignId: campaign.campaign.id });
| Code | Meaning | Recovery Action |
|---|---|---|
| 601 | Token invalid | Auto-refresh token, retry |
| 602 | Token expired | Auto-refresh token, retry |
| 606 | Rate limit hit | Wait 20 seconds, retry |
| 607 | Daily quota exceeded | Stop operations until tomorrow |
| 610 | Resource not found | Verify campaign ID exists |
| 611 | System error | Retry with exponential backoff |
| 615 | Concurrent limit | Serialize requests |
| 709 | Campaign active | Deactivate before delete |
| 711 | Name already exists | Use unique name |
async function safeDeleteCampaign(campaignId) {
// 1. Verify exists
const campaign = await mcp__marketo__campaign_get({ campaignId });
if (!campaign.success) {
throw new Error(`Campaign ${campaignId} not found`);
}
// 2. Check if active
if (campaign.campaign.isActive) {
await mcp__marketo__campaign_deactivate({ campaignId });
}
// 3. Delete
return await mcp__marketo__campaign_delete({ campaignId });
}
async function validateBeforeClone(sourceId, targetName, targetFolder) {
// 1. Verify source exists
const source = await mcp__marketo__campaign_get({ campaignId: sourceId });
if (!source.success) {
throw new Error(`Source campaign ${sourceId} not found`);
}
// 2. Check for name collision in target folder
const existing = await mcp__marketo__campaign_list({
folder: JSON.stringify(targetFolder),
name: targetName
});
if (existing.campaigns.some(c => c.name === targetName)) {
throw new Error(`Campaign "${targetName}" already exists in target folder`);
}
return { source: source.campaign, canProceed: true };
}
async function batchCloneCampaigns(sourceIds, targetFolder, namePrefix) {
const results = { succeeded: [], failed: [] };
for (const sourceId of sourceIds) {
try {
const source = await mcp__marketo__campaign_get({ campaignId: sourceId });
const result = await mcp__marketo__campaign_clone({
campaignId: sourceId,
name: `${namePrefix} - ${source.campaign.name}`,
folder: targetFolder
});
results.succeeded.push(result);
} catch (error) {
results.failed.push({ sourceId, error: error.message });
}
}
return results;
}
async function bulkActivate(campaignIds, validateFirst = true) {
const results = { activated: [], skipped: [], failed: [] };
for (const campaignId of campaignIds) {
const campaign = await mcp__marketo__campaign_get({ campaignId });
// Skip already active
if (campaign.campaign.isActive) {
results.skipped.push({ campaignId, reason: 'Already active' });
continue;
}
// Validate if requested
if (validateFirst) {
const smartList = await mcp__marketo__campaign_get_smart_list({
campaignId,
includeRules: true
});
if (!smartList.triggers?.length) {
results.skipped.push({ campaignId, reason: 'No triggers defined' });
continue;
}
}
// Activate
try {
await mcp__marketo__campaign_activate({ campaignId });
results.activated.push(campaignId);
} catch (error) {
results.failed.push({ campaignId, error: error.message });
}
}
return results;
}
For detailed API specifications, see:
docs/runbooks/smart-campaigns/README.md - Overviewdocs/runbooks/smart-campaigns/04-create-operations.md - Create APIdocs/runbooks/smart-campaigns/07-clone-operations.md - Clone APIdocs/runbooks/smart-campaigns/08-delete-operations.md - Delete APIdocs/runbooks/smart-campaigns/10-smart-list-flow-limitations.md - Limitations| Agent | Integration Purpose |
|---|---|
marketo-campaign-builder | Campaign design and logic |
marketo-program-architect | Program/folder structure |
marketo-orchestrator | Complex multi-step workflows |
marketo-instance-discovery | Find existing campaigns/templates |
User: Clone the welcome template to Q1 Webinar program
Agent:
1. Looking up template...
Found: Template - Welcome Series (ID: 1001)
2. Verifying target program...
Found: Q1 Webinar (ID: 2000)
3. Cloning campaign...
mcp__marketo__campaign_clone({
campaignId: 1001,
name: 'Q1 Webinar - Welcome Series',
folder: { id: 2000, type: 'Program' }
})
Result: Campaign cloned successfully
- New Campaign ID: 3045
- Status: Inactive (activate when ready)
- Smart List: Copied from template
- Flow: Copied from template
User: Delete all inactive campaigns in the Archive folder
Agent:
1. Finding campaigns in Archive folder...
mcp__marketo__campaign_list({
folder: '{"id": 500, "type": "Folder"}'
})
Found: 15 campaigns
2. Filtering inactive only...
Found: 12 inactive campaigns
3. Deleting campaigns (with confirmation)...
[Lists campaign names and IDs]
Proceed with deletion? [Yes/No]
4. After confirmation:
Deleted: 12 campaigns
- Campaign 3001: Archive - Old Welcome
- Campaign 3002: Archive - 2024 Newsletter
...
User: Clone the regional template for EMEA, APAC, and Americas
Agent:
1. Source template: Regional Campaign Template (ID: 1005)
2. Creating regional copies...
- EMEA Regional Campaign → ID: 3050
- APAC Regional Campaign → ID: 3051
- Americas Regional Campaign → ID: 3052
3. All campaigns created as inactive
Next steps:
- Update program tokens for each region
- Activate when ready