curl --request POST \
--url https://api.interchange.io/api/v2/storefront/demand-inbox/{runId}/exchange/revisions \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"overrides": {
"pricing": {
"priceAdjustmentBasisPoints": 45000
},
"rules": {
"requiredBundleIds": [
"<string>"
],
"excludedBundleIds": [
"<string>"
],
"maxProducts": 50
}
}
}
'import requests
url = "https://api.interchange.io/api/v2/storefront/demand-inbox/{runId}/exchange/revisions"
payload = { "overrides": {
"pricing": { "priceAdjustmentBasisPoints": 45000 },
"rules": {
"requiredBundleIds": ["<string>"],
"excludedBundleIds": ["<string>"],
"maxProducts": 50
}
} }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
overrides: {
pricing: {priceAdjustmentBasisPoints: 45000},
rules: {
requiredBundleIds: ['<string>'],
excludedBundleIds: ['<string>'],
maxProducts: 50
}
}
})
};
fetch('https://api.interchange.io/api/v2/storefront/demand-inbox/{runId}/exchange/revisions', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));{
"id": "<string>",
"exchangeId": "<string>",
"state": "draft",
"overrides": {
"posture": {
"selectedPosture": "direct_fit"
},
"pricing": {
"priceAdjustmentBasisPoints": 45000,
"floorCpm": {
"amount": 1,
"currency": "<string>"
}
},
"rules": {
"requiredBundleIds": [
"<string>"
],
"excludedBundleIds": [
"<string>"
],
"maxProducts": 50
}
},
"composeResult": "ok",
"composeError": {
"code": "<string>",
"message": "<string>"
},
"proposalPreview": "<unknown>",
"proposalPreviewSnapshots": "<unknown>",
"settlementCurrency": "<string>",
"buyerStatedCurrencies": [
"<string>"
],
"composedByUserId": 0,
"composedAt": "<string>",
"approval": {
"gate": "pending",
"decidedByUserId": 0,
"decidedAt": "<string>",
"notes": "<string>"
},
"send": {
"mode": "manual",
"autoApproveReason": "storefront_auto_approve"
},
"sentProposalArtifactId": "<string>",
"delivery": {
"status": "not_delivered",
"reason": "no_delivery_lane"
},
"canApprove": true,
"canAmend": true,
"canSubmit": true,
"canDiscard": true,
"submissionGeneration": 0,
"amendCount": 0
}{
"data": null,
"error": {
"code": "<string>",
"message": "<string>",
"field": "<string>",
"details": {}
}
}{
"data": null,
"error": {
"code": "<string>",
"message": "<string>",
"field": "<string>",
"details": {}
}
}{
"data": null,
"error": {
"code": "<string>",
"message": "<string>",
"field": "<string>",
"details": {}
}
}{
"data": null,
"error": {
"code": "<string>",
"message": "<string>",
"field": "<string>",
"details": {}
}
}{
"data": null,
"error": {
"code": "<string>",
"message": "<string>",
"field": "<string>",
"details": {}
}
}Compose a demand exchange revision
Declare posture, price, and product-count overrides in the merchandising simulator’s own grammar and compose a silent draft revision on a live demand exchange through the real merchandising engine, against the storefront’s current configuration. The draft holds no delivery state and issues no rate hold. Refused with a named reason (not_live_demand, brief_not_retained, no_operating_instructions, or adjustment_limit_reached) when the exchange is not adjustable — the same rule the exchange read states on every row. Scoped to the caller’s storefront — a run outside it returns 404.
curl --request POST \
--url https://api.interchange.io/api/v2/storefront/demand-inbox/{runId}/exchange/revisions \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"overrides": {
"pricing": {
"priceAdjustmentBasisPoints": 45000
},
"rules": {
"requiredBundleIds": [
"<string>"
],
"excludedBundleIds": [
"<string>"
],
"maxProducts": 50
}
}
}
'import requests
url = "https://api.interchange.io/api/v2/storefront/demand-inbox/{runId}/exchange/revisions"
payload = { "overrides": {
"pricing": { "priceAdjustmentBasisPoints": 45000 },
"rules": {
"requiredBundleIds": ["<string>"],
"excludedBundleIds": ["<string>"],
"maxProducts": 50
}
} }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
overrides: {
pricing: {priceAdjustmentBasisPoints: 45000},
rules: {
requiredBundleIds: ['<string>'],
excludedBundleIds: ['<string>'],
maxProducts: 50
}
}
})
};
fetch('https://api.interchange.io/api/v2/storefront/demand-inbox/{runId}/exchange/revisions', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));{
"id": "<string>",
"exchangeId": "<string>",
"state": "draft",
"overrides": {
"posture": {
"selectedPosture": "direct_fit"
},
"pricing": {
"priceAdjustmentBasisPoints": 45000,
"floorCpm": {
"amount": 1,
"currency": "<string>"
}
},
"rules": {
"requiredBundleIds": [
"<string>"
],
"excludedBundleIds": [
"<string>"
],
"maxProducts": 50
}
},
"composeResult": "ok",
"composeError": {
"code": "<string>",
"message": "<string>"
},
"proposalPreview": "<unknown>",
"proposalPreviewSnapshots": "<unknown>",
"settlementCurrency": "<string>",
"buyerStatedCurrencies": [
"<string>"
],
"composedByUserId": 0,
"composedAt": "<string>",
"approval": {
"gate": "pending",
"decidedByUserId": 0,
"decidedAt": "<string>",
"notes": "<string>"
},
"send": {
"mode": "manual",
"autoApproveReason": "storefront_auto_approve"
},
"sentProposalArtifactId": "<string>",
"delivery": {
"status": "not_delivered",
"reason": "no_delivery_lane"
},
"canApprove": true,
"canAmend": true,
"canSubmit": true,
"canDiscard": true,
"submissionGeneration": 0,
"amendCount": 0
}{
"data": null,
"error": {
"code": "<string>",
"message": "<string>",
"field": "<string>",
"details": {}
}
}{
"data": null,
"error": {
"code": "<string>",
"message": "<string>",
"field": "<string>",
"details": {}
}
}{
"data": null,
"error": {
"code": "<string>",
"message": "<string>",
"field": "<string>",
"details": {}
}
}{
"data": null,
"error": {
"code": "<string>",
"message": "<string>",
"field": "<string>",
"details": {}
}
}{
"data": null,
"error": {
"code": "<string>",
"message": "<string>",
"field": "<string>",
"details": {}
}
}Authorizations
API key or access token
Path Parameters
Surrogate id of the chef compose run that anchors this exchange (the demand-inbox ledger row id).
0 < x <= 900719925474099142
Body
Declare posture, price, and product-count overrides in the merchandising simulator's own grammar and compose a silent draft revision through the real merchandising engine.
Show child attributes
Show child attributes
Response
Compose a demand exchange revision
A seller-composed adjustment to a live demand exchange: silent while draft or awaiting approval, and a real pass on the exchange's compose run once sent.
draft, awaiting_approval, sent, discarded Show child attributes
Show child attributes
ok, error The merchandising engine's own error, kept mounted (with the seller's declared overrides) when composition failed, rather than the sheet being torn down.
Show child attributes
Show child attributes
The draft response envelope, in the same shape the canonical proposal card already renders (redacted). Null when compose failed.
The draft product snapshots keyed by product id, paired with proposalPreview so the canonical proposal card renders a draft with no builder-specific projection. Null when compose failed.
Read once from the persisted brief request at compose time. A draft never converts into these or issues a rate hold — that is the buyer-facing response path's job, not a discardable draft's.
-9007199254740991 <= x <= 9007199254740991Show child attributes
Show child attributes
How a sent revision cleared. Null until sent. An auto send never carries a human decision record.
Show child attributes
Show child attributes
Truthful buyer-visibility (build-5 ruling): null until sent. A sent revision always reports not_delivered/no_delivery_lane today — investigation found no seller-to-buyer lane for a subsequent proposal and no honest staging lane either. The UI states the buyer's agent was not notified; it never claims a notification that did not happen.
Show child attributes
Show child attributes
False when the caller is this revision's own composer (maker-checker) or the revision is not awaiting approval.
True for a draft (fresh or bounced back after rejection) composed by the caller; false once submitted, sent, or discarded, or when a different storefront user composed it.
True when the caller composed this draft, it hasn't been submitted, sent, or discarded, and its last compose succeeded. A draft whose compose failed can only be discarded or re-composed via amend.
True for a draft (fresh or bounced back after rejection) composed by the caller; false once submitted, sent, or discarded, or when a different storefront user composed it.
Bumped on every submit, resubmit, and amend. A decision must be made against this exact value or it is refused as stale.
-9007199254740991 <= x <= 9007199254740991How many times this row has been amended in place. Counts toward the lifetime adjustment cap alongside the row itself.
-9007199254740991 <= x <= 9007199254740991