API 參考
Campaigns
Management API: GET /sites/{id}/campaigns, POST /sites/{id}/campaigns, PATCH /sites/{id}/campaigns/{campaignId}
List email campaigns (newest updated first) with status, audience segment and delivery stats for sent ones. Errors: FORBIDDEN, PLAN_REQUIRED, INVALID_CURSOR.
Parameters
idstring · pathrequiredWebsite id. Get it from sites.list or from sites.create.
limitinteger · queryMaximum number of items to return, 1–100. Defaults to 20.
cursorstring · queryOpaque cursor from the previous page's nextCursor. Omit for the first page.
Response 200
dataobject[]requireddata[].idstringrequireddata[].namestringrequireddata[].channelstringrequireddata[].statusstringrequiredDRAFT, SCHEDULED, SENDING, PAUSED, SENT or CANCELED. Agents can only edit DRAFT.
data[].pausedReasonstring | nullrequireddata[].segmentNamestring | nullrequireddata[].scheduledAtstring | nullrequireddata[].startedAtstring | nullrequireddata[].completedAtstring | nullrequireddata[].updatedAtstringrequireddata[].recipientCountinteger | nullrequireddata[].statsobject | nullrequireddata[].stats.recipientsintegerrequireddata[].stats.pendingintegerrequireddata[].stats.sentintegerrequireddata[].stats.deliveredintegerrequireddata[].stats.bouncedintegerrequireddata[].stats.complainedintegerrequireddata[].stats.notSentintegerrequireddata[].stats.openedintegerrequireddata[].stats.clickedintegerrequireddata[].stats.unsubscribedintegerrequiredhasMorebooleanrequiredTrue when another page exists; pass nextCursor to fetch it.
nextCursorstring | nullrequiredCursor for the next page, or null on the last page.
Errors
FORBIDDEN403PLAN_REQUIRED403INVALID_CURSOR400
MCP tool campaigns_list
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/campaigns" \
-H "Authorization: Bearer $KANORIO_TOKEN"{
"data": [
{
"id": "camp_example01",
"name": "October seasonal bake",
"channel": "EMAIL",
"status": "DRAFT",
"pausedReason": null,
"segmentName": "Ordered in the last 90 days",
"scheduledAt": null,
"startedAt": null,
"completedAt": null,
"updatedAt": "2026-10-06T08:00:00.000Z",
"recipientCount": 320,
"stats": null
}
],
"hasMore": false,
"nextCursor": null
}Create an email campaign as a DRAFT. Agents cannot send or schedule campaigns; the owner reviews and sends from the console. content is optional; when provided it must match the email design-template content shape (subject, previewText, blocks). Audience defaults to the all-subscribers segment. Reference images as asset:<assetId> (media.upload, media.import_stock). Errors: FORBIDDEN, PLAN_REQUIRED, VALIDATION, INVALID_CONTENT (details.detail lists issues), CHANNEL_UNAVAILABLE, ASSET_NOT_FOUND, ASSET_NOT_ALLOWED.
Parameters
idstring · pathrequiredWebsite id. Get it from sites.list or from sites.create.
Request body
namestringrequiredchannel"EMAIL"Defaults to EMAIL.
contentobject
Response 201
idstringrequirednamestringrequiredchannelstringrequiredstatusstringrequiredcontentobjectrequiredChannel content (email: subject, previewText, blocks...). Media-library images appear as asset:<assetId>.
assetsobjectrequiredThe media-library images the content uses, by assetId.
audienceobject | nullrequiredaudience.kind"segment"requiredaudience.segmentIdstringrequiredscheduledAtstring | nullrequiredrecipientCountinteger | nullrequiredupdatedAtstringrequiredtimeZonestringrequired
Errors
FORBIDDEN403PLAN_REQUIRED403VALIDATION400INVALID_CONTENT400 · details.detail lists issuesCHANNEL_UNAVAILABLE422ASSET_NOT_FOUND404ASSET_NOT_ALLOWED422
MCP tool campaigns_create_draft
curl -X POST "https://app.kanorio.com/api/v1/sites/{id}/campaigns" \
-H "Authorization: Bearer $KANORIO_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"October seasonal bake","channel":"EMAIL"}'{
"id": "camp_example01",
"name": "October seasonal bake",
"channel": "EMAIL",
"status": "DRAFT",
"content": {
"subject": "October special: chestnut croissants are here",
"body": "This month we're baking..."
},
"assets": {},
"audience": {
"kind": "segment",
"segmentId": "seg_example01"
},
"scheduledAt": null,
"recipientCount": 320,
"updatedAt": "2026-10-06T08:00:00.000Z",
"timeZone": "Asia/Taipei"
}Update draft
/sites/{id}/campaigns/{campaignId}Update a DRAFT campaign's name, content and/or audience segment. Only drafts can be changed by agents (NOT_EDITABLE otherwise); scheduling and sending stay in the console. Errors: FORBIDDEN, PLAN_REQUIRED, CAMPAIGN_NOT_FOUND, NOT_EDITABLE, INVALID_CONTENT, INVALID_AUDIENCE, SEGMENT_NOT_FOUND, ASSET_NOT_FOUND, ASSET_NOT_ALLOWED.
Parameters
idstring · pathrequiredWebsite id. Get it from sites.list or from sites.create.
campaignIdstring · pathrequiredCampaign id. Get it from campaigns.list.
Request body
namestringcontentobjectaudienceobjectWho receives it: a segment id from segments.list. New drafts default to the all-subscribers segment.
audience.kind"segment"requiredaudience.segmentIdstringrequired
Response 200
idstringrequirednamestringrequiredchannelstringrequiredstatusstringrequiredcontentobjectrequiredChannel content (email: subject, previewText, blocks...). Media-library images appear as asset:<assetId>.
assetsobjectrequiredThe media-library images the content uses, by assetId.
audienceobject | nullrequiredaudience.kind"segment"requiredaudience.segmentIdstringrequiredscheduledAtstring | nullrequiredrecipientCountinteger | nullrequiredupdatedAtstringrequiredtimeZonestringrequired
Errors
FORBIDDEN403PLAN_REQUIRED403CAMPAIGN_NOT_FOUND404NOT_EDITABLE409INVALID_CONTENT400INVALID_AUDIENCE400SEGMENT_NOT_FOUND404ASSET_NOT_FOUND404ASSET_NOT_ALLOWED422
MCP tool campaigns_update_draft
curl -X PATCH "https://app.kanorio.com/api/v1/sites/{id}/campaigns/{campaignId}" \
-H "Authorization: Bearer $KANORIO_TOKEN" \
-H "Content-Type: application/json" \
-d '{"audience":{"kind":"segment","segmentId":"seg_example01"}}'{
"id": "camp_example01",
"name": "October seasonal bake",
"channel": "EMAIL",
"status": "DRAFT",
"content": {
"subject": "October special: chestnut croissants are here",
"body": "This month we're baking..."
},
"assets": {},
"audience": {
"kind": "segment",
"segmentId": "seg_example01"
},
"scheduledAt": null,
"recipientCount": 320,
"updatedAt": "2026-10-06T08:00:00.000Z",
"timeZone": "Asia/Taipei"
}