Update Semantics
This API surface is for integration partners building on top of the Rokt network. Rokt ecommerce partners integrating placements on their own checkout should use the Rokt Ecommerce developer docs instead.
Controls updates are not uniform. blockedVerticals merges onto the merchant's stored vertical policy, while domains and status replace in full. Getting that split wrong is the most common integration bug we see. Read this page before you touch putMarketplaceControls or putPartnershipStatus.
Verticals merge; domains replace. A vertical you omit keeps the policy it already has. A domain you omit is removed. To unblock a vertical, send it explicitly with policy: "Allow" — omitting it does nothing.
What merges and what replacesDirect link to What merges and what replaces
| Field | Behaviour | Omitting an entry means |
|---|---|---|
blockedVerticals | Merge | Leave that vertical's stored policy alone |
domains | Replace | Remove that domain from the list |
status (Status route) | Replace | n/a, the field is a single value |
blockedVerticals is an override list, not a desired-state list. The server starts from the merchant's stored policy for every sub-vertical and applies your entries on top. Anything you don't mention is untouched.
domains is a desired-state list. The server reconciles the stored domain list to exactly what you sent.
Blocking and unblocking verticalsDirect link to Blocking and unblocking verticals
A merchant currently has verticals 1500 and 1502 blocked. The partner wants to add a block on 1504.
{
"name": "Acme Network Controls",
"blockedVerticals": [
{
"partnerVerticalId": 1504,
"partnerSubVerticalId": 1612,
"policy": "Block",
"position1Policy": "Block"
}
]
}
1500 and 1502 keep their blocks because you didn't mention them, so you do not need to fetch and re-send the vertical list.
But you still have to carry domains. Omitting the field clears every stored domain rule, so a vertical-only body like the one above wipes a merchant's competitor blocks. Either include the current domains array from a GET, or only skip the GET when you know the merchant has none.
To unblock 1500, say so explicitly:
{
"name": "Acme Network Controls",
"blockedVerticals": [
{
"partnerVerticalId": 1500,
"partnerSubVerticalId": 1610,
"policy": "Allow",
"position1Policy": "Allow"
}
]
}
Both policy fields are required on every entry. policy covers all offer
positions; position1Policy covers the first, most valuable slot. Send the
pair explicitly on every entry; omitting either returns a 422.
Nothing is inferred on your behalf. Your merchant-facing dashboard already
renders both switches with a concrete value for each, so both values are
already in hand at the point you build the request, and sending them removes
any question about what a partial entry meant. It also lets you express a
partial restriction, such as policy: "Allow" with position1Policy: "Block"
to keep a category out of the top slot only.
There is no "clear every block" operation. Sending blockedVerticals: [] is a no-op for verticals, not a reset. To lift several blocks at once, send one Allow entry per vertical you want opened up.
Building that list takes two calls, because a PUT entry needs both partnerVerticalId and partnerSubVerticalId and the controls GET only returns the sub-vertical. GET the current state to find which sub-verticals are Block, then look each one up in GET /v1/partnership/vertical-mappings to recover its parent partner_vertical_id.
Domains still replaceDirect link to Domains still replace
This is where the read-modify-write discipline still applies.
A merchant has competitor.example.com blocked. The partner wants to add competitor-two.example.com.
- wrong.json
- right.json
{
"name": "Acme Network Controls",
"domains": [
{ "domain": "competitor-two.example.com", "policy": "Block" }
]
}
{
"name": "Acme Network Controls",
"domains": [
{ "domain": "competitor.example.com", "policy": "Block" },
{ "domain": "competitor-two.example.com", "policy": "Block" }
]
}
wrong.json removes the block on competitor.example.com. The server replaces the domain list with what you sent. There is no error and no warning.
Omitting domains entirely from the request body has the same effect as sending []: the stored list is replaced with an empty one. If you are only changing verticals, send the merchant's current domains array back unchanged.
Status replaces tooDirect link to Status replaces too
PUT status=paused pauses every non-archived page variant on the account. There is no "pause only variant X"; per-variant control is not in the Partnerships API surface today.
contentHashDirect link to contenthash
Every GET response includes marketplace_controls_list.content_hash. You can pass it back as the contentHash field of your PUT.
contentHash is advisory today, not enforced. A stale hash is logged server-side and the write still proceeds. Do not rely on it to detect concurrent edits, and do not expect a 409. If two writers can touch the same merchant, serialise them on your side.
We intend to enforce it. This page will be updated when that lands.
Walkthrough: add a domain blockDirect link to Walkthrough: add a domain block
- GET the current MCL state
curl https://accounts.rokt.com/v1/partnership/accounts/<your-account-id>/marketplacecontrolslists \
-H "Authorization: Bearer $ROKT_TOKEN"Response
data(abbreviated):{
"marketplace_controls_list": {
"domains": [
{ "domain": "competitor.example.com", "policy": "Block" }
],
"content_hash": "h_abc123"
},
"translated_verticals": [
{ "vertical_id": 1610, "policy": "Block", "position_1_policy": "Block" }
]
}vertical_idhere is your sub-vertical ID, not a Rokt ID. It corresponds topartnerSubVerticalIdon a PUT. The parentpartnerVerticalIdis not returned by this call; get it fromGET /v1/partnership/vertical-mappings. - Append the new domain locally
Add
{ "domain": "competitor-two.example.com", "policy": "Block" }to thedomainsarray from the GET. Keep the existing entries. - PUT with the full domain list
curl -X PUT https://accounts.rokt.com/v1/partnership/accounts/<your-account-id>/marketplacecontrolslists \
-H "Authorization: Bearer $ROKT_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Network Controls",
"domains": [
{"domain": "competitor.example.com", "policy": "Block"},
{"domain": "competitor-two.example.com", "policy": "Block"}
]
}'Both domains are now blocked. The vertical block on 1610 is preserved automatically, because verticals merge and you didn't mention it.