otto-ads
Skill1 starsChanged 10 days agoMITView on GitHub
Use when the user wants to run OTTO Sponsored Product Ads through Backlane: list or create campaigns, change budgets or bids, pause campaigns, manage advertised products (targets) and keywords, or pull campaign, product and keyword performance reports. Requires the Backlane MCP server (tools `list_integrations`, `search`, `execute`) with an OTTO Market connection.
In this repository9 other files it gives its agents.
- AGENTS.mdAGENTS.md
- .claude/CLAUDE.mdCLAUDE.md
- backlaneSkill
- customer-serviceSkill
- daily-checkSkill
- get-startedSkill
- listing-healthSkill
- mirakl-product-dataSkill
- sales-analysisSkill
The file
We split this skill into the rules it gives an agent
10 rules. About 0 are common, 1 is distinctive, and 9 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
9
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 25
Use search to read the exact request schema before the first write.
- Distinctive: line 28
Ads spend real money: Before creating or changing a campaign, budget or bid, state the exact change (amounts in euros) and get the user's …
- Very specific: line 31
If OTTO answers 401 or 403, the connection lacks the advertising permission.
- Very specific: line 36
All amounts are euro cents: { "amount": 1099, "currency": "EUR" } is 10.99 EUR.
- Very specific: line 57
Keep each execute call short: start the write in one call and poll in the next one, rather than looping for minutes in a single call.
- Very specific: line 69
Create body (all fields required except endDate):
- Very specific: line 96
Create: POST with { "targets": [{ "sku": "…", "bid": { "amount": 50, "currency": "EUR" } }] }.
- Very specific: line 101
Remove: DELETE …/targets with the IDs as a query array: query: { targetIds: ["…", "…"] } (no body).
- Very specific: line 114
Create: POST with { "keywords": [ … ] } (up to 2,500 at once).
- Very specific: line 116
Remove: DELETE with query: { keywordIds: [ … ] }.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
OTTO Sponsored Product Ads
OTTO's advertising API runs through Backlane's execute tool like every other
OTTO call. Load the backlane skill first if you have not checked the
connection yet.
Before you start
- Find the OTTO connection name in the
executetool description (e.g.otto-main). Paths are/otto/<connection>/v1/sponsored-product-ads/...and/otto/<connection>/v1/spa-reporting/.... - Use
searchto read the exact request schema before the first write. It is the source of truth; this skill only covers the rules that are easy to get wrong. - Ads spend real money. Before creating or changing a campaign, budget or bid, state the exact change (amounts in euros) and get the user's confirmation.
- If OTTO answers 401 or 403, the connection lacks the advertising
permission. Call
list_integrationsand give the user the reconnect link.
Money and names
- All amounts are euro cents:
{ "amount": 1099, "currency": "EUR" }is 10.99 EUR. The minimum budget or bid is 20 (0.20 EUR). Convert when talking to the user. - Campaign names must not contain
$ § " # *or parentheses, except a trailing number likeSummer (2).
Every write is asynchronous
Creating, updating or deleting campaigns, targets and keywords returns
202 with a change request, not the result:
{ "changeRequest": { "requestId": "…", "status": "PENDING", "entityIds": null } }Poll GET /v1/sponsored-product-ads/change-requests/{requestId} every few
seconds until status is no longer PENDING. ACCEPTED means it worked and
entityIds holds the new or changed IDs. Any other status means it failed:
show the user the details. Never report a write as done before it is
ACCEPTED.
Keep each execute call short: start the write in one call and poll in the
next one, rather than looping for minutes in a single call.
Campaigns
| Task | Call |
| List | GET /v1/sponsored-product-ads/campaigns (limit up to 1000; follow nextCursor via cursor) |
| Get one | GET /v1/sponsored-product-ads/campaigns/{campaignId} |
| Create | POST /v1/sponsored-product-ads/campaigns |
| Change | PATCH /v1/sponsored-product-ads/campaigns/{campaignId} with only the fields to change |
Create body (all fields required except endDate):
{
"name": "Sofas autumn",
"startDate": "2026-11-01",
"endDate": "2026-11-30",
"budget": { "amount": 1000, "currency": "EUR" },
"budgetType": "DAILY",
"pacing": "EVEN",
"campaignType": "AUTOMATIC"
}startDatemust be today or later.budgetTypeLIFETIMErequires anendDate.budgetTypeandcampaignTypecannot be changed after creation.- Campaigns cannot be deleted. To stop one, set
{ "status": "PAUSED" }(other values:ACTIVE,WAITING). - Backlane sends PATCH bodies as JSON Merge Patch: omitted fields stay
unchanged,
nullclears a field (e.g."endDate": nullmakes a campaign open-ended).
Targets (advertised products)
Path: /v1/sponsored-product-ads/campaigns/{campaignId}/targets
- Create:
POSTwith{ "targets": [{ "sku": "…", "bid": { "amount": 50, "currency": "EUR" } }] }. Thebidis required forAUTOMATICcampaigns and ignored forMANUALones. Take SKUs from the user's OTTO products (GET /v5/products). - List:
GET …/targets. Get one:GET …/targets/{targetId}. - Change bids:
PATCH …/targetswith{ "targets": [{ "targetId": "…", "bid": {…} }] }. - Remove:
DELETE …/targetswith the IDs as a query array:query: { targetIds: ["…", "…"] }(no body).
Keywords
Path: /v1/sponsored-product-ads/campaigns/{campaignId}/targets/{targetId}/keywords
Each keyword has keyword, matchType (EXACT, PHRASE, BROAD) and
keywordType (POSITIVE, NEGATIVE).
AUTOMATICcampaigns only acceptNEGATIVEkeywords (to exclude searches). Positive keywords need aMANUALcampaign.POSITIVEkeywords need abid;NEGATIVEones take none.- Create:
POSTwith{ "keywords": [ … ] }(up to 2,500 at once). - Change:
PATCHwith{ "keywords": [{ "keywordId": "…", … }] }. - Remove:
DELETEwithquery: { keywordIds: [ … ] }.
Performance
Quick look at one campaign or product:
GET /v1/spa-reporting/campaign-performance?campaignId=…GET /v1/spa-reporting/product-performance?campaignId=…&sku=…
Both take optional fromDate/toDate. A 404 "Data not found" means the
campaign has no traffic data yet, not that it does not exist.
Reports (campaign, product or keyword) for longer periods or many campaigns:
POST /v1/spa-reporting/{campaign|product|keyword}-performance:{ "name": "October campaigns", "fromDate": "2026-10-01", "toDate": "2026-10-31", "configuration": { "columns": ["CAMPAIGN_ID", "TOTAL_VIEWS", "TOTAL_CLICKS", "TOTAL_COSTS", "TOTAL_SALES"], "format": "CSV" } }columnsandformatare required. Optional:groupBy(e.g.EVENT_DATE_CETfor daily rows) andfilters({ "column": "CAMPAIGN_ID", "operator": "=", "value": "…" }). Check the allowed columns per report type withsearch.- Poll
GET /v1/spa-reporting/{kind}-performance/status?reportId=…untilstatusisREADY. GET /v1/spa-reporting/reports/download?reportId=…returns the CSV: readtextto analyse it and give the userfile.downloadUrl(valid 24 hours).
TOTAL_COSTS and TOTAL_SALES are in euro cents. When you report results,
convert to euros and show costs, sales and their ratio (ROAS = sales / costs).
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

