Proposals
A proposal is an open request: you send a budget and rough criteria, and the seller assembles the first pass to hit your goal. You do not need package IDs; you need a goal. It is also the path a seller takes when it withholds avails or prices per campaign: the plan is the disclosure, revealing exactly what the buy needs. The ask travels on the normal create call as a proposal request riding deal_type proposal; the orders that eventually book carry a pay-basis deal type instead (see the deal model). Three request types cover the ground:
request_type | You send | Seller returns |
|---|---|---|
audience_plan | An audience definition or uploaded plan, a currency goal, budget, flight | A plan hitting your audience with their inventory, priced |
specs | Budget, flight, daypart mix, spot lengths, copy split | A schedule meeting the specs |
open | A brief in plain language, plus any constraints | Their best interpretation, as a plan you can interrogate |
The request block:
| Field | Type | Notes |
|---|---|---|
request_type required | string | One of the request types above. |
audience.definition | object | Provide one of audience.definition, audience.audience_id, or audience.upload_id. This one is an inline audience definition: demo plus segments. |
audience.audience_id | string | A shared audience reference. |
audience.upload_id | string | An uploaded audience plan file. |
audience.currency_goal | object | The unit and target the plan must hit, alongside the audience on audience_plan requests. |
anchor_package_ids | string[] | Packages you already like; the plan builds around them. |
budget_gross | number | Gross budget for the flight. |
flight | object | Start and end dates. |
constraints | object | Spot lengths, unit-cost ceilings, copy splits. |
acceptance_band | object | Optional. {metric, tolerance_pct, action}: auto-accept (or flag) a plan that lands within tolerance of your ask. The "within 2% of specs" rule, made machine-checkable. |
brief | string | Plain language; on open requests, the whole ask. |
POST /v1/orders
{ "seller": "acme", "deal_type": "proposal",
"request": {
"request_type": "audience_plan",
"audience": {
"definition": { "demo": "A25-54", "segments": ["auto_intenders_q4"] },
"currency_goal": { "unit": "a25_54", "target": 42000000 } },
"anchor_package_ids": ["acme-prime"],
"budget_gross": 500000,
"flight": { "start": "2026-10-05", "end": "2026-12-27" },
"constraints": { "spot_lengths": [15,30], "max_unit_cost": 450.00,
"copy_split": { "brand-a": 0.7, "brand-b": 0.3 } },
"acceptance_band": { "metric": "delivery", "tolerance_pct": 2, "action": "auto_accept" },
"brief": "Reach auto intenders in the Northeast; avoid overnight rotations."
} }
The seller's response arrives as a plan_proposal artifact: line items priced off their card, and, where nothing published fits, custom packages minted for your request. A minted package gets a real package_id scoped to your agreement, so accepting the plan books it through the same rails, and reordering it next quarter is one call. If the seller prices per campaign, its rates come back as quote commits that expire; a lapsed quote is a stale card, so you re-request rather than book at an old price. Where it cannot meet the whole ask, the plan lists the shortfall as unmet[] (the availability counter: "you asked for 1, 2, 3; we have 1 and 2"), never a silent gap. Confirm as-is, counter through the negotiation flow, or decline. Nothing books until you confirm, unless the plan lands inside an acceptance_band you set, in which case it books on delivery of the plan without another round.
Acceptance bands, precisely
An acceptance_band lets a plan book without another human round. Because it commits money, its semantics are deterministic: the platform evaluates it (never an agent) the moment the seller returns the plan, so both sides compute the same result. The band is {metric, tolerance_pct, action}; tolerance_pct is a percentage (2 means 2%), and the comparison is one-sided per metric, against the target you stated in the request.
| metric | Plan value | Target | Satisfied when (one-sided) |
|---|---|---|---|
delivery | plan_totals.projected_delivery[unit] | audience.currency_goal.target (in currency_goal.unit) | plan_value >= target × (1 - tolerance_pct/100). Over-delivery never fails. |
budget | plan_totals.gross | budget_gross | plan_value <= target × (1 + tolerance_pct/100). Under-budget never fails. |
- Denominator is the request target; comparison is exact decimal (no rounding); the tolerance edge is inclusive.
- Missing metric. If the plan omits the metric (no
projected_delivery[unit]), or the request omits the target the metric needs, the band is not evaluable: the order waits atneeds_confirmationfor a person, and the event notesacceptance_band: not_evaluable. Adeliveryband on a request with nocurrency_goalis rejected at create withINVALID_INPUT. - Action. Satisfied and
auto_accept: the platform issues the confirm for you (actor recorded as the band).flag, not-satisfied, or not-evaluable all park atneeds_confirmation. - Currency. A
budgetcomparison is in the order's one trade currency; the model never converts.
Worked examples, both with tolerance_pct: 2:
// metric: delivery, target 42,000,000 a25_54 plan projected_delivery.a25_54 = 41,800,000 threshold = 42,000,000 × (1 - 0.02) = 41,160,000 41,800,000 >= 41,160,000 // SATISFIED -> auto_accept confirms // metric: budget, target 500,000.00 USD plan_totals.gross = 511,000.00 threshold = 500,000.00 × (1 + 0.02) = 510,000.00 511,000.00 <= 510,000.00 // NOT satisfied -> waits for a human
What books on confirm
The plan is not itself an order. On confirm, its line items book through the normal order path as one or more catalog orders. deal_type is order-level, so a plan that mixes pay bases (say a guaranteed audience block plus some preemptible spots) books as one order per type, each referencing the now-published package_ids from the plan; see the deal model.
POST /v1/orders // one call per pay-basis type in the accepted plan
{ "seller": "acme", "deal_type": "audience_guaranteed", "workflow": "catalog",
"external_order_id": "q4-auto-ag", "rate_version": "acme-2026q4-v1",
"line_items": [
{ "package_id": "acme-auto-q4-9f", "week": "2026-W40",
"impressions_goal": 21000000, "spot_length": 30,
"rate": { "class": "scatter", "unit_cost": 11.90, "locked_rate_id": "lr_2f7a" } },
{ "package_id": "acme-auto-q4-9f", "week": "2026-W41",
"impressions_goal": 21000000, "spot_length": 30,
"rate": { "class": "scatter", "unit_cost": 11.90, "locked_rate_id": "lr_2f7a" } }
] }
On impression currencies, unit_cost is the CPM. Each line books one week; a line can book a slot instead.