PATCH /v1/projects/:projectId/ads-content/:id
Force-include or force-exclude a creative from ad rotation. The override is one input to the eligibility decision - the facts outrank it.
/v1/projects/{projectId}/ads-content/{id}- Auth
- Bearer
- Scope
- ads:write
Pins a creative as include or exclude. There is no score gate behind it (the creative score is deprecated, see GET /ads-content). Use exclude to pull a creative off the eligible list immediately. Use include to record an explicit pin; a creative with no override is already eligible by default.
exclude always wins. include does not: it skips no fact, and Layers evaluates the same rules here, on GET /ads-content, on top-performers and at the moment it builds the ad — override → safety review → the source container's ad review → moderator flag → TikTok Spark authorization window → whether the media behind the creative still exists. A creative whose eligibility.reason is safety_failed, flagged, spark_authorization_not_live, spark_window_unknown or source_unavailable stays ineligible whatever the override says, and the response below reports that.
This is the only ads-content mutation available today. Everything else about the creative - paid performance, promotions - is derived. The override field is the single human-writable lever.
projectIdstring (UUID)requiredProject containing the creative.idstringrequiredadsContentId - the prefixed adc_<UUID> value returned by GET /ads-content. The bare UUID is also accepted.
overridestring | nullrequiredSet the override. null clears it; the creative is then eligible by default.One of:include,exclude,notestringoptionalShort context saved alongside the override. Visible in the audit log; max 280 chars.
Example request
curl -X PATCH https://api.layers.com/v1/projects/prj_254a4ce1-f4ca-42b1-9e36-17ca45ef3d39/ads-content/adc_6f5d4c3b-2a19-4e87-b0c5-1d2e3f4a5b6c \
-H "Authorization: Bearer lp_..." \
-H "Content-Type: application/json" \
-d '{ "override": "exclude", "note": "Legal flagged the background music" }'const res = await fetch(
`https://api.layers.com/v1/projects/${projectId}/ads-content/${adsContentId}`,
{
method: "PATCH",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ override: "exclude", note: "Legal flagged background music" }),
},
);
const updated = await res.json();import httpx
r = httpx.patch(
f"https://api.layers.com/v1/projects/{project_id}/ads-content/{ads_content_id}",
headers={"Authorization": f"Bearer {api_key}"},
json={"override": "exclude", "note": "Legal flagged background music"},
)
updated = r.json()Response
{
"adsContentId": "adc_6f5d4c3b-2a19-4e87-b0c5-1d2e3f4a5b6c",
"sourceType": "content_container",
"sourceId": "cnt_9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
"platformPostId": null,
"scoringPool": "generated",
"organicScore": 0,
"organicPerformance": null,
"adPerformance": null,
"scoredAt": "2026-04-18T12:00:00Z",
"scoringVersion": 3,
"eligibility": { "isEligible": false, "reason": "excluded by override" },
"override": "exclude",
"overrideNote": "Legal flagged background music",
"overrideSetBy": "api_key",
"overrideSetAt": "2026-04-18T19:22:05Z",
"activePromotions": []
}{
"error": {
"code": "VALIDATION",
"message": "Invalid patch body.",
"requestId": "req_..."
}
}What override does
override is the eligibility decision in every place Layers picks creatives to promote:
"include"- an explicit pin, recorded and honoured only when every fact already allows the creative to run. It does not clear a safety failure, a moderator flag, a failed container ad review, a Spark authorization that can no longer launch, or missing source media."exclude"- ineligible. Use this to retire a creative from future ad selection. Ads currently running against it are not paused - ad pause decisions are made from live performance, not from override. Pause them in the platform's ad manager.null- clears the override. The creative is eligible by default;eligibility.reasonreadseligible by default; no override set.
Notes
- Idempotent: PATCHing the same override twice is a no-op with respect to downstream selection.
:idis theadsContentIdthe list endpoint returns, in its prefixedadc_<UUID>form. Pass it back exactly as received; the bare UUID is also accepted, per the tolerance rule.overrideSetBystores the API key prefix and the api_key id that made the change ("<prefix> (<api_key_id>)"). Audit who-did-what viaGET /v1/auditwithsubjectType=ads_content.- Override does not change any other field. Clearing the override returns the creative to the default state as if you had never touched it - eligible, unless one of the facts above refuses it.
organicScoreis deprecated and always0. - Excluding a creative on one platform excludes it across all platforms. There is no per-platform override.
See also
GET /v1/projects/:projectId/ads-content- creative inventory and current overridesGET /v1/projects/:projectId/top-performers- ranked list that respects overrides- Publish to learn - the feedback loop override plugs into
GET /v1/projects/:projectId/ads-content
Creative inventory - generated content, UGC and manual uploads with provenance, override state and explicit eligibility for ad promotion.
Ads
Read and write across Meta, TikTok, and Apple Search Ads — campaigns, ad sets, ads, creatives, budgets, optimizer triggers, pending queue, audit log, and metrics. BYO ad accounts; the customer's bucket-mode authority gate is the source of truth on every write.