Edit campaign details
Update supplied fields on an existing campaign. This endpoint edits stored records; it does not replace the dedicated scheduling or lifecycle actions.
Before you start
Read the campaign in the intended authorized brand, retain its current fields/variants, and use a credential with app_topolo_socialize.calendar:write and the workspace grant. Review every change, including any full variant replacement, before confirming the mutation.
Request
PUT /api/campaigns/{id}
id is required. Other fields are optional: name, description, status, goals, tags, platforms, window dates, timezone/cadence and variants. Resource binding stays outside the body.
name: non-empty after the route trim-check, up to 100 supplied characters; the value itself is stored as supplied.platforms: records containingplatform,enabledandsettings, unlike the string list used by create.- Window dates can be strings or null. Editing them does not schedule posts; use the scheduling guide for actual operational constraints.
- Empty goals/tags clear those lists. An empty description can be omitted from the subsequent optional response projection.
Worked examples
These examples are synthetic and unrun; the ids, content and timestamps do not identify live records.
Rename an existing campaign
Source-derived synthetic and unrun. Assumes the illustrated existing draft belongs to the verified workspace and only the approved name is changed. SQL CURRENT_TIMESTAMP formatting is illustrative, not a captured response.
Request
{
"id": "campaign_example",
"name": "Reviewed campaign title"
}
Response illustration
{
"id": "campaign_example",
"brandId": "ws_socialize_example",
"name": "Reviewed campaign title",
"createdBy": "user_example",
"status": "draft",
"windowStartDate": null,
"windowEndDate": null,
"paused": false,
"pausedAt": null,
"resumedAt": null,
"variants": [],
"createdAt": "2026-10-21T00:00:00.000Z",
"updatedAt": "2026-10-21 13:00:00",
"platforms": [
{
"platform": "linkedin",
"enabled": true,
"settings": {}
}
],
"timezone": "UTC"
}
Re-read the same campaign; check its owning brand, new name and preserved creator/status/variants. Do not infer a scheduling or publishing effect.
Replace content variants on an intended draft
Source-derived synthetic and unrun. Assumes the campaign is already an authorized intended draft and replacement of all variant links is approved; status=draft is deliberate, not an unapproved status workaround.
Request
{
"id": "campaign_example",
"status": "draft",
"variants": [
{
"name": "Control",
"weight": 100,
"content": {
"text": "A replacement draft example.",
"hashtags": [
"example"
]
}
}
]
}
Response illustration
{
"id": "campaign_example",
"brandId": "ws_socialize_example",
"name": "Example campaign",
"createdBy": "user_example",
"status": "draft",
"windowStartDate": null,
"windowEndDate": null,
"paused": false,
"pausedAt": null,
"resumedAt": null,
"variants": [
{
"id": "campaign_example_Control",
"name": "Control",
"weight": 100,
"postId": "post_new_draft_example",
"hashtags": [
"example"
]
}
],
"createdAt": "2026-10-21T00:00:00.000Z",
"updatedAt": "2026-10-21 13:00:00",
"platforms": [
{
"platform": "linkedin",
"enabled": true,
"settings": {}
}
],
"timezone": "UTC"
}
Verify the replacement variant id and newly created draft post in the same workspace. Prior draft posts are not deleted automatically. Do not replay after uncertainty; repetition can create another post.
Variant replacement
A non-empty variants array replaces all existing variant links. Content text creates new draft posts; old drafts are not automatically deleted. Empty or omitted variants leave existing links in place. A failure after link deletion can leave only some replacements, so preserve the before-state and inspect actual records before repair.
Variant names must be unique (at most 10), with weights totalling 100 within 0.01. Content-only variants without a post id require an explicitly intended status: draft in the request. Do not change an active campaign to draft just to satisfy that validator. Use content.hashtags for stored variant hashtags; this draft-post INSERT does not persist media URLs merely because the schema accepts those optional fields.
Lifecycle and verification
Changing a status string here does not run scheduling, activation, completion or cancellation hooks. It does not materialize occurrence posts or emit a lifecycle notification. Prefer the dedicated action when that is the intended operation.
Re-read the campaign and check its owning brand, changed fields, retained creator, current variant links and linked posts. The current caller and the stored creator are different facts. An id-only edit can return the unchanged record without a timestamp update; do not interpret that as a new content write.
Failure and recovery
Campaign field edits, old-link deletion and new draft/link inserts are sequential and have no enclosing rollback here. After a timeout/error, reconcile current state before another operation. Repeating content replacement can create extra drafts; cleanup is a separate reviewed mutation.
Name/variant validation uses HTTP400 problem details, access failures retain their actual refusal, and generic update failures can return HTTP500 with detail/debug fields. Preserve the actual request evidence; do not infer nothing changed or invent a uniform error.code envelope.
Change log
- 2026-10-21: Added metadata and intended-draft replacement examples, create/update field differences, lifecycle distinctions, persistence limits and partial-write recovery.
Source
Reviewed route, brand access, campaign service and error source at f8e30bad6947f868302f24e9cb31b6a2bdd1fbf6, with the current declaration refreshed. No business request or provider test ran; serving-source association is unknown.