← Back to product·Simulmedia VAMOS Docs·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 product 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 with workflow: "proposal". The returned plan carries a buy_type on every line. 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_product_idsstring[]Products 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", "workflow": "proposal",
  "buyer_metadata": { "advertiser_id": "adv_4c1f7a2b9e03" },
  "request": {
    "request_type": "audience_plan",
    "audience": {
      "definition": { "demo": "A25-54", "segments": ["auto_intenders_q4"] },
      "currency_goal": { "unit": "a25_54", "target": 42000000 } },
    "anchor_product_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 products minted for your request. A minted product gets a real product_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

Confirm books the plan in place, on the same order (SPEC-0015 section 3.4): the plan's line_items become the proposal order's own line_items, and exactly one order exists after the confirm, with one event stream and one last_seq. You never write a second create. A buyer-authored POST /v1/orders with line_items is always a catalog order, and a catalog line carrying impressions_goal is refused 422, so re-keying a plan into a create is not how a plan books, by construction.

POST /v1/orders/ord_c19a44e2f0b1/confirm
{ "action": "confirm",
  "actor": "buyer@yourco.com",
  "plan_id": "plan_5b2e" }

202 Response
{ "order_id": "ord_c19a44e2f0b1",
  "status": "seller_review",
  "booked_orders": ["ord_c19a44e2f0b1"] }

booked_orders names this same order, because exactly one order books: a mixed plan books as one order however many pay bases its lines carry, per ADR-0007's collapse of the pay-basis axis. The booked lines are the plan's lines, shape for shape, under the proposal's lineage rather than the catalog path: an audience-guaranteed plan line keeps its impressions_goal and its cpm (this is the one path where a buyer's order legitimately carries them, because the plan, not the buyer, named the shape); a spot-cost plan line keeps its units and unit_cost. Each cites the locked_rate_id the plan returned, quote commits included, and each books one period. Reads echo the derived rate_basis; nothing on this path accepts one. After the confirm the order sits at seller_review, and the seller's gate is not skipped because the seller wrote the plan.