← Back to product·Simulmedia Switchboard Docs · v1 draft·Guides: for buyers / Proposals
Guides: for buyers

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_typeYou sendSeller returns
audience_planAn audience definition or uploaded plan, a currency goal, budget, flightA plan hitting your audience with their inventory, priced
specsBudget, flight, daypart mix, spot lengths, copy splitA schedule meeting the specs
openA brief in plain language, plus any constraintsTheir best interpretation, as a plan you can interrogate

The request block:

FieldTypeNotes
request_type requiredstringOne of the request types above.
audience.definitionobjectProvide one of audience.definition, audience.audience_id, or audience.upload_id. This one is an inline audience definition: demo plus segments.
audience.audience_idstringA shared audience reference.
audience.upload_idstringAn uploaded audience plan file.
audience.currency_goalobjectThe unit and target the plan must hit, alongside the audience on audience_plan requests.
anchor_package_idsstring[]Packages you already like; the plan builds around them.
budget_grossnumberGross budget for the flight.
flightobjectStart and end dates.
constraintsobjectSpot lengths, unit-cost ceilings, copy splits.
acceptance_bandobjectOptional. {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.
briefstringPlain 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.

metricPlan valueTargetSatisfied when (one-sided)
deliveryplan_totals.projected_delivery[unit]audience.currency_goal.target (in currency_goal.unit)plan_value >= target × (1 - tolerance_pct/100). Over-delivery never fails.
budgetplan_totals.grossbudget_grossplan_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 at needs_confirmation for a person, and the event notes acceptance_band: not_evaluable. A delivery band on a request with no currency_goal is rejected at create with INVALID_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 at needs_confirmation.
  • Currency. A budget comparison 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.