DELETE /v1/projects/:projectId/ads/:platform/campaigns/:campaignId
Soft-delete a campaign on the platform.
/v1/projects/:projectId/ads/{platform}/campaigns/:campaignId- Auth
- Bearer
- Scope
- ads:write:lifecycle
Marks the campaign deleted on the platform (Meta DELETED, TikTok DELETE, Apple REMOVED). Most platforms preserve historical metrics for the deleted campaign indefinitely; Layers' GET …/campaigns?status=deleted continues to return it.
Same shape applies to adsets and ads:
DELETE …/adsets/:adsetIdDELETE …/ads/:adId
Authority
Delete is gated by the structural axis. With structural: "off" (the default for manual campaigns), every delete is denied.
Request
curl -X DELETE https://api.layers.com/v1/projects/$PROJECT_ID/ads/meta/campaigns/$CAMPAIGN_ID \
-H "Authorization: Bearer $LAYERS_API_KEY"Response
200 with {entityId, platform, partnerStatus, platformStatus, verdictId, occurredAt} — the same envelope the pause / resume endpoints return. partnerStatus is deleted; platformStatus is the platform-native terminal Layers translated to (DELETED on Meta, DELETE on TikTok, REMOVED on Apple).
The response is an acknowledgement, not a completion: the platform write is dispatched asynchronously through the gate, and settlement surfaces through the ads.write.executed / ads.write.denied webhooks. verdictId is the audit id for this dispatch, and is null when no audit row was written.
Repeating a delete
Delete is idempotent, as Idempotency promises for every DELETE: send it twice and the second call returns the same 200 envelope the first did. One entity has one deletion, so the repeat is answered from the first one rather than sent to the platform again — whether that first delete has already been carried out or is still on its way.
The one case we can't answer that way is a first delete that reached the platform and whose outcome we can't establish. We don't send a second one and we don't pretend it's done; you get a CONFLICT carrying details.reason: "delete_outcome_unconfirmed". Check the platform's own console for the entity, then reconcile. Retrying the call won't change the answer.
Errors
| Code | When |
|---|---|
AUTHORITY_DENIED | structural axis is off. |
FORBIDDEN_SCOPE | Key lacks ads:write:lifecycle. |
CONFLICT | details.reason: "delete_outcome_unconfirmed" — an earlier delete of this entity reached the platform and we can't confirm what it did. Nothing was sent a second time. Not retryable; check the platform console. |
See also
POST /v1/projects/:projectId/ads/:platform/campaigns/:campaignId/{pause,resume}
Pause or resume a campaign. Layers translates the verb to the platform's status enum.
GET / PATCH /v1/projects/:projectId/ads/:platform/campaigns/:campaignId/authority
Read or update a campaign's bucket-mode authority block (creative / tactical / structural axes).