Dry-Run Mode
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.
Every write endpoint accepts ?dry_run=true. Rokt validates the full request — authentication, payload shape, vertical mappings, relationship checks — and returns a response identical to a real commit, but no state is persisted. The response body is identical to a real call; the side effects are not.
When to use itDirect link to When to use it
- Validate a payload shape before committing. Surface
400s, missing vertical mappings, or relationship 403s without touching state. - Integration tests against stage. Confirm your build works end-to-end without polluting your sandbox account data.
- Pre-flight checks in your onboarding UI. Show the merchant exactly what would happen before they click Save.
What dry-run doesn't doDirect link to What dry-run doesn't do
Dry-run is not a permission bypass or a fast path — it is a full, real request that returns the would-be response without persisting state.
- Auth still runs. A wrong API token or revoked relationship returns
401/403exactly as it would on a real call. - Vertical translation still runs. A missing vertical mapping row returns
400exactly as it would on a real call. - Idempotency keys still apply. A dry-run is recorded in the idempotency cache, keyed by your
Idempotency-Key.
What dry-run suppressesDirect link to What dry-run suppresses
Dry-run suppresses both the persisted state change and the side effects that would otherwise fan out from it. Specifically, a dry-run does not:
- Send any outbound email to the merchant or to Rokt operators.
- Make external API calls that would mutate state on third-party systems (e.g. Stripe Connect account creation on
payout-setup— which is why that endpoint doesn't support dry-run at all). - Emit webhooks to the partner platform or downstream Rokt services.
- Trigger downstream processing pipelines or notifications.
- Create a polled operation — dry-run responses do not carry an
X-Operation-Id, because no work was persisted to poll on.
The only durable trace a dry-run leaves is the cached response under your Idempotency-Key (so retrying the same key replays the dry-run) and a server-side audit log entry tagged dry_run=true for diagnostics. Neither is partner-observable through the API.
Dry-runs burn idempotency keys. If you dry-run with key K, the server caches the dry-run response under K. If you then send the real write with the same K, you get the cached dry-run response back — no commit happens. Use a fresh Idempotency-Key for the real call.
Supported endpointsDirect link to Supported endpoints
| Endpoint | Method | Dry-run? |
|---|---|---|
/v1/partnership/accounts/{account_id}/marketplacecontrolslists | PUT | Yes |
/v1/partnership/accounts/{account_id}/status | PUT | Yes |
Any GET | — | No effect (ignored) |
POST /v1/accounts/register/partnership | POST | No |
POST /v1/partnership/accounts/{account_id}/payout-setup | POST | No |
Walkthrough: validate an MCL PUT, confirm state is unchangedDirect link to Walkthrough: validate an MCL PUT, confirm state is unchanged
- GET current MCL state — capture the contentHash
curl https://accounts.rokt.com/v1/partnership/accounts/<your-account-id>/marketplacecontrolslists \
-H "Authorization: Bearer $ROKT_TOKEN"Response includes
"contentHash": "h_abc123"and your currentblockedVerticals. - Dry-run a PUT with a new blocked vertical
- curl
- python
- node
curl -X PUT "https://accounts.rokt.com/v1/partnership/accounts/<your-account-id>/marketplacecontrolslists?dry_run=true" \
-H "Authorization: Bearer $ROKT_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Network Controls",
"blockedVerticals": [
{"partnerVerticalId": 1500, "partnerSubVerticalId": 1610, "policy": "Block"},
{"partnerVerticalId": 1500, "partnerSubVerticalId": 1611, "policy": "Block"}
],
"domains": [],
"contentHash": "h_abc123"
}'import requests, uuid
resp = requests.put(
f"https://accounts.rokt.com/v1/partnership/accounts/{account_id}/marketplacecontrolslists",
params={"dry_run": "true"},
headers={
"Authorization": f"Bearer {token}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"name": "Acme Network Controls",
"blockedVerticals": [
{"partnerVerticalId": 1500, "partnerSubVerticalId": 1610, "policy": "Block"},
{"partnerVerticalId": 1500, "partnerSubVerticalId": 1611, "policy": "Block"},
],
"domains": [],
"contentHash": "h_abc123",
},
)
assert resp.status_code == 200, resp.json()import { randomUUID } from "node:crypto";
const resp = await fetch(
`https://accounts.rokt.com/v1/partnership/accounts/${accountId}/marketplacecontrolslists?dry_run=true`,
{
method: "PUT",
headers: {
Authorization: `Bearer ${token}`,
"Idempotency-Key": randomUUID(),
"Content-Type": "application/json",
},
body: JSON.stringify({
name: "Acme Network Controls",
blockedVerticals: [
{ partnerVerticalId: 1500, partnerSubVerticalId: 1610, policy: "Block" },
{ partnerVerticalId: 1500, partnerSubVerticalId: 1611, policy: "Block" },
],
domains: [],
contentHash: "h_abc123",
}),
}
);Response:
200with the fullMarketplaceControlsResponseshape, as if it had committed. - GET again — confirm state is unchanged
curl https://accounts.rokt.com/v1/partnership/accounts/<your-account-id>/marketplacecontrolslists \
-H "Authorization: Bearer $ROKT_TOKEN"contentHashis stillh_abc123.blockedVerticalsis still the pre-dry-run list. No state was persisted. - Real commit — use a FRESH Idempotency-Key
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 @mcl.jsonNow you'll get a fresh write, not the cached dry-run.