Quickstart: place your first order
Ten minutes, sandbox credentials, one order on air (in the sandbox sense). The sandbox seller is Acme Broadcasting; it behaves like a real seller, including the approval pause. This walks the catalog path because it is the fastest way to see an order move; it is not the only front door. When a seller withholds avails or prices per campaign, or when you would rather send a brief than pick line items, start with a proposal instead. Both are first-class.
1. Get a token
In production you exchange a client id and secret for a short-lived scoped JWT: see Authentication. The sandbox skips that exchange: you are issued a bearer secret directly, so there is no client id and no token endpoint to call. The secret is the whole credential, and it travels in the same Authorization: Bearer header production uses, so everything after this step is identical either way.
export TOKEN=<your buyer secret> export VAMOS_SESSION=$(uuidgen)
VAMOS_SESSION is not optional. Every call below sends it as X-Sandbox-Session, and they have to send the same value: the sandbox keeps each run in its own namespace, so an order created under one session cannot be read under another. Omit the header and the sandbox mints a fresh run per request, which means step 5 would go looking for step 4's order somewhere it was never written.
2. Browse the catalog
curl -s https://api.sandbox.vamos.simulmedia.com/v1/sellers/acme/catalog \ -H "Authorization: Bearer $TOKEN" -H "X-Sandbox-Session: $VAMOS_SESSION" { "packages": [ { "package_id": "acme-prime", "type": "daypart", "definition": { "selling_title": "Prime", "days": ["Mon","Tue","Wed","Thu","Fri","Sat","Sun"], "start": "20:00", "end": "23:00" }, "taxonomy_version": "acme-2026q4-v1", "network": "ACME", "currencies": ["spots","hh","p2plus","a25_54"], "buy_types": ["preemptible","non_preemptible","fixed_position","audience_guaranteed"] } ] }
3. Check avails and rates
curl -s "https://api.sandbox.vamos.simulmedia.com/v1/sellers/acme/avails?package_id=acme-prime&weeks=2026-W41&spot_length=30" \ -H "Authorization: Bearer $TOKEN" -H "X-Sandbox-Session: $VAMOS_SESSION" { "avails": [ { "week": "2026-W41", "grain": "week", "state": "available", "basis": "actual", "spots": { "available": 42, "total": 80 }, "sellout_level": 0.62, "rates": [ { "class": "scatter", "advertiser_class": "general", "spot_length": 30, "buy_type": "preemptible", "clearance_tier": "P2", "rate_basis": "per_unit", "gross_rate_unit": 405.00, "locked_rate_id": "lr_88c1" }, { "class": "scatter", "advertiser_class": "general", "spot_length": 30, "buy_type": "non_preemptible", "rate_basis": "per_unit", "gross_rate_unit": 520.00, "locked_rate_id": "lr_88c2" }, { "class": "scatter", "advertiser_class": "general", "spot_length": 30, "buy_type": "fixed_position", "rate_basis": "per_unit", "gross_rate_unit": 640.00, "locked_rate_id": "lr_88c4" }, { "class": "scatter", "advertiser_class": "general", "spot_length": 30, "buy_type": "audience_guaranteed", "rate_basis": "per_unit", "gross_rate_unit": 435.20, "gross_rate_cpm": 3.40, "guaranteed_impressions": 128000, "audience_code": "hh", "locked_rate_id": "lr_88c3" } ] } ] }
4. Place the order
Catalog orders reference package IDs and locked rates; because you ordered against published availability and a current rate version, there is no confirmation round-trip.
curl -s https://api.sandbox.vamos.simulmedia.com/v1/orders \ -H "Authorization: Bearer $TOKEN" -H "X-Sandbox-Session: $VAMOS_SESSION" \ -H "Content-Type: application/json" \ -d '{ "seller": "acme", "workflow": "catalog", "external_order_id": "q4-campaign-042", "rate_version": "acme-2026q4-v1", "line_items": [ { "package_id": "acme-prime", "provider": "National ACME", "network": "ACME", "week": "2026-W41", "units": 10, "spot_length": 30, "rate": { "class": "scatter", "advertiser_class": "general", "buy_type": "preemptible", "clearance_tier": "P2", "rate_basis": "per_unit", "unit_cost": 405.00, "locked_rate_id": "lr_88c1", "currency": "USD" } }], "buyer_metadata": { "advertiser": "YourClient", "campaign": "Q4-2026" }, "notify_url": "https://yourapp.example.com/webhooks/vamos" }' { "order_id": "ord_9f3a12c4b7e1", "status": "submitted" }
5. Watch it move
Your notify_url receives events as the order validates and enters seller_review (a person at the seller approves; in the sandbox the order waits there until you make that decision yourself, with the seller secret, in Things to try below). At any time the snapshot is authoritative:
curl -s https://api.sandbox.vamos.simulmedia.com/v1/orders/ord_9f3a12c4b7e1 \ -H "Authorization: Bearer $TOKEN" -H "X-Sandbox-Session: $VAMOS_SESSION"
On approval you receive a placement_receipt (the booking receipt: the seller's order ID, daylocks applied, timestamp) as the order lands in placed: the buy is booked. In production, a final completed event closes the order once the flight has run; the sandbox stops at placed, because nothing there runs a flight. Discovered, ordered, approved, placed. No email involved.
Things to try
The sandbox seller publishes four packages across the whole quarter, 2026-W40 through 2026-W52, so there is a second question worth asking. Below is one runnable request per outcome, in the order of the table. The sandbox tutorial walks them with the responses and the reasoning.
| Outcome | What you get |
|---|---|
Full acceptance to placed | units_accepted: 10, an allocation, and a placement_receipt |
| Partial acceptance | The order is placed; the unit is partial with num_accepted: 6 |
INVENTORY_UNAVAILABLE | rejected from a well-formed request, because 2026-W43 is dark |
STALE_RATE_CARD | failed, with rate_drift per unit: what you locked against what the card says now |
A countered unit | negotiating, and the unit reads countered with num_accepted: 0 |
| Two buy types on one order | placed at 11049.60. No buy type on the order; each line carries its own |
| Two posting policies on one order | placed at 5964.00, each line reconciling under its own package's policy |
Two more variables, on top of the TOKEN and VAMOS_SESSION you set in step 1. Keep that same session value: these examples act on the order you already placed, and a fresh session would not have it. The seller secret is a second credential, because the approval decision is a different actor and the sandbox will not let the buyer token make it.
export VAMOS=https://api.sandbox.vamos.simulmedia.com/v1 export SELLER_TOKEN=<your seller secret>
1. Full acceptance. Approve the order you placed in step 4. The order id is derived from your external_order_id, so the same key always resolves to the same order.
curl -s -X POST $VAMOS/me/orders/ord_9f3a12c4b7e1/decide \
-H "Authorization: Bearer $SELLER_TOKEN" -H "X-Sandbox-Session: $VAMOS_SESSION" \
-H "Content-Type: application/json" \
-d '{ "decision": "approve", "actor": "sales@acmebroadcasting.example",
"units": [ { "unit_id": "u-0001", "action": "accept", "num_accepted": 10 } ] }'
{ "order_id": "ord_9f3a12c4b7e1", "status": "placed",
"units_accepted": 10, "units_countered": 0, "units_rejected": 0 }
2. Partial acceptance. Ask for 10 and have the seller take 6. Plan against num_accepted, never against what you asked for.
curl -s -X POST $VAMOS/orders -H "Authorization: Bearer $TOKEN" \
-H "X-Sandbox-Session: $VAMOS_SESSION" -H "Content-Type: application/json" \
-d '{
"seller": "acme", "workflow": "catalog",
"external_order_id": "try-partial",
"rate_version": "acme-2026q4-v1",
"buyer_metadata": { "advertiser": "Northwind Coffee" },
"line_items": [
{ "package_id": "acme-prime", "provider": "National ACME", "network": "ACME",
"week": "2026-W41", "units": 10, "spot_length": 30,
"rate": { "class": "scatter", "advertiser_class": "general",
"buy_type": "preemptible", "clearance_tier": "P2",
"rate_basis": "per_unit", "unit_cost": 405.00,
"locked_rate_id": "lr_88c1", "currency": "USD" } }]
}'
{ "order_id": "ord_dd4117e6dfa2", "status": "submitted" }
curl -s -X POST $VAMOS/me/orders/ord_dd4117e6dfa2/decide \
-H "Authorization: Bearer $SELLER_TOKEN" -H "X-Sandbox-Session: $VAMOS_SESSION" \
-H "Content-Type: application/json" \
-d '{ "decision": "approve", "actor": "sales@acmebroadcasting.example",
"units": [ { "unit_id": "u-0001", "action": "accept", "num_accepted": 6 } ] }'
{ "order_id": "ord_dd4117e6dfa2", "status": "placed",
"units_accepted": 6, "units_countered": 0, "units_rejected": 4 }
3. Inventory unavailable. 2026-W43 is dark. Availability is the only business reason an order is rejected outright, and the check runs at create.
curl -s -X POST $VAMOS/orders -H "Authorization: Bearer $TOKEN" \
-H "X-Sandbox-Session: $VAMOS_SESSION" -H "Content-Type: application/json" \
-d '{
"seller": "acme", "workflow": "catalog",
"external_order_id": "try-unavailable",
"rate_version": "acme-2026q4-v1",
"buyer_metadata": { "advertiser": "Northwind Coffee" },
"line_items": [
{ "package_id": "acme-prime", "provider": "National ACME", "network": "ACME",
"week": "2026-W43", "units": 4, "spot_length": 30,
"rate": { "class": "scatter", "advertiser_class": "general",
"buy_type": "preemptible", "clearance_tier": "P2",
"rate_basis": "per_unit", "unit_cost": 405.00,
"locked_rate_id": "lr_pr43p", "currency": "USD" } }]
}'
curl -s "$VAMOS/events?order=ord_b7afeb158ee0&after_seq=2" \
-H "Authorization: Bearer $TOKEN" -H "X-Sandbox-Session: $VAMOS_SESSION"
{ "order_id": "ord_b7afeb158ee0", "events": [
{ "event_type": "order.status_changed", "seq": 3, "status": "rejected",
"substatus": null, "artifact": null,
"error": { "error_code": "INVENTORY_UNAVAILABLE",
"error_message": "Requested week or slot lacks availability",
"stage": "availability", "recoverable": true, "details": {} },
"occurred_at": "2026-10-01T14:02:13Z" } ], "last_seq": 3 }
4. Stale rate card. Send a rate_version behind the current one and the platform refuses to guess which price you meant. rate_drift is the useful half: per unit, what you locked against what the card says now.
curl -s -X POST $VAMOS/orders -H "Authorization: Bearer $TOKEN" \
-H "X-Sandbox-Session: $VAMOS_SESSION" -H "Content-Type: application/json" \
-d '{
"seller": "acme", "workflow": "catalog",
"external_order_id": "try-stale",
"rate_version": "acme-2026q4-v0",
"buyer_metadata": { "advertiser": "Northwind Coffee" },
"line_items": [
{ "package_id": "acme-prime", "provider": "National ACME", "network": "ACME",
"week": "2026-W41", "units": 4, "spot_length": 30,
"rate": { "class": "scatter", "advertiser_class": "general",
"buy_type": "preemptible", "clearance_tier": "P2",
"rate_basis": "per_unit", "unit_cost": 405.00,
"locked_rate_id": "lr_88c1", "currency": "USD" } }]
}'
curl -s "$VAMOS/events?order=ord_3ec0e342f256&after_seq=2" \
-H "Authorization: Bearer $TOKEN" -H "X-Sandbox-Session: $VAMOS_SESSION"
{ "order_id": "ord_3ec0e342f256", "events": [
{ "event_type": "order.status_changed", "seq": 3, "status": "failed",
"substatus": null, "artifact": null,
"error": { "error_code": "STALE_RATE_CARD",
"error_message": "rate_version acme-2026q4-v0 is behind acme-2026q4-v1",
"stage": "validation", "recoverable": true,
"details": { "rate_drift": [ { "unit_id": "u-0001",
"locked_rate_id": "lr_88c1",
"your_cost": 405, "current_cost": 405 } ] } },
"occurred_at": "2026-10-01T14:02:13Z" } ], "last_seq": 3 }
5. A countered unit. On the cheaper daypart, have the seller answer with a price instead of a yes.
curl -s -X POST $VAMOS/orders -H "Authorization: Bearer $TOKEN" \
-H "X-Sandbox-Session: $VAMOS_SESSION" -H "Content-Type: application/json" \
-d '{
"seller": "acme", "workflow": "catalog",
"external_order_id": "try-counter",
"rate_version": "acme-2026q4-v1",
"buyer_metadata": { "advertiser": "Northwind Coffee" },
"line_items": [
{ "package_id": "acme-daytime", "provider": "National ACME", "network": "ACME",
"week": "2026-W41", "units": 4, "spot_length": 30,
"rate": { "class": "scatter", "advertiser_class": "general",
"buy_type": "preemptible", "clearance_tier": "P2",
"rate_basis": "per_unit", "unit_cost": 120.00,
"locked_rate_id": "lr_dt41p", "currency": "USD" } }]
}'
{ "order_id": "ord_3699c15718f3", "status": "submitted" }
curl -s -X POST $VAMOS/me/orders/ord_3699c15718f3/decide \
-H "Authorization: Bearer $SELLER_TOKEN" -H "X-Sandbox-Session: $VAMOS_SESSION" \
-H "Content-Type: application/json" \
-d '{ "decision": "counter", "actor": "sales@acmebroadcasting.example",
"units": [ { "unit_id": "u-0001", "action": "counter",
"reason": "Rate is below card for this week",
"counter": { "week": "2026-W41", "unit_cost": 135.00,
"num_offered": 4 } } ] }'
{ "order_id": "ord_3699c15718f3", "status": "negotiating",
"units_accepted": 0, "units_countered": 4, "units_rejected": 0 }
6. Two buy types on one order. A fixed position in the tentpole plus a CPM guarantee on Prime. One order, two ways of buying, and no buy type on the order itself.
curl -s -X POST $VAMOS/orders -H "Authorization: Bearer $TOKEN" \
-H "X-Sandbox-Session: $VAMOS_SESSION" -H "Content-Type: application/json" \
-d '{
"seller": "acme", "workflow": "catalog",
"external_order_id": "try-mixed-buy-types",
"rate_version": "acme-2026q4-v1",
"buyer_metadata": { "advertiser": "Northwind Coffee" },
"line_items": [
{ "package_id": "acme-gameday", "provider": "National ACME", "network": "ACME",
"week": "2026-W47", "units": 2, "spot_length": 30,
"rate": { "class": "scatter", "advertiser_class": "general",
"buy_type": "fixed_position", "rate_basis": "per_unit",
"unit_cost": 2600.00, "locked_rate_id": "lr_gd47f",
"currency": "USD" } },
{ "package_id": "acme-prime", "provider": "National ACME", "network": "ACME",
"week": "2026-W47", "units": 10, "spot_length": 30,
"rate": { "class": "scatter", "advertiser_class": "general",
"buy_type": "audience_guaranteed", "rate_basis": "per_unit",
"unit_cost": 584.96, "guaranteed_impressions": 128000,
"audience_code": "hh", "locked_rate_id": "lr_pr47g",
"currency": "USD" } }]
}'
{ "order_id": "ord_f944243d5f02", "status": "submitted" }
curl -s -X POST $VAMOS/me/orders/ord_f944243d5f02/decide \
-H "Authorization: Bearer $SELLER_TOKEN" -H "X-Sandbox-Session: $VAMOS_SESSION" \
-H "Content-Type: application/json" \
-d '{ "decision": "approve", "actor": "sales@acmebroadcasting.example",
"units": [ { "unit_id": "u-0001", "action": "accept", "num_accepted": 2 },
{ "unit_id": "u-0002", "action": "accept", "num_accepted": 8 } ] }'
{ "order_id": "ord_f944243d5f02", "status": "placed",
"units_accepted": 10, "units_countered": 0, "units_rejected": 0 }
7. Two posting policies on one order. Prime posts over four weeks at 90 percent; the syndicated sponsorship over two at 95. Mixed-policy orders are legal, and each line reconciles under its own package's terms.
curl -s -X POST $VAMOS/orders -H "Authorization: Bearer $TOKEN" \
-H "X-Sandbox-Session: $VAMOS_SESSION" -H "Content-Type: application/json" \
-d '{
"seller": "acme", "workflow": "catalog",
"external_order_id": "try-mixed-policies",
"rate_version": "acme-2026q4-v1",
"buyer_metadata": { "advertiser": "Northwind Coffee" },
"line_items": [
{ "package_id": "acme-prime", "provider": "National ACME", "network": "ACME",
"week": "2026-W41", "units": 10, "spot_length": 30,
"rate": { "class": "scatter", "advertiser_class": "general",
"buy_type": "audience_guaranteed", "rate_basis": "per_unit",
"unit_cost": 435.20, "guaranteed_impressions": 128000,
"audience_code": "hh", "locked_rate_id": "lr_88c3",
"currency": "USD" } },
{ "package_id": "acme-morning-syndicated", "provider": "Acme Syndication",
"network": "ACME", "week": "2026-W41",
"units": 10, "spot_length": 30,
"rate": { "class": "scatter", "advertiser_class": "general",
"buy_type": "audience_guaranteed", "rate_basis": "per_unit",
"unit_cost": 161.20, "guaranteed_impressions": 62000,
"audience_code": "hh", "locked_rate_id": "lr_88d3",
"currency": "USD" } }]
}'
{ "order_id": "ord_4415819986b9", "status": "submitted" }
curl -s -X POST $VAMOS/me/orders/ord_4415819986b9/decide \
-H "Authorization: Bearer $SELLER_TOKEN" -H "X-Sandbox-Session: $VAMOS_SESSION" \
-H "Content-Type: application/json" \
-d '{ "decision": "approve", "actor": "sales@acmebroadcasting.example",
"units": [ { "unit_id": "u-0001", "action": "accept", "num_accepted": 10 },
{ "unit_id": "u-0002", "action": "accept", "num_accepted": 6 } ] }'
{ "order_id": "ord_4415819986b9", "status": "placed",
"units_accepted": 16, "units_countered": 0, "units_rejected": 0 }
Reading the quarter is cheaper still, and needs no order at all:
curl -s "$VAMOS/sellers/acme/avails?package_id=acme-prime&weeks=2026-W40&weeks=2026-W47" \ -H "Authorization: Bearer $TOKEN" -H "X-Sandbox-Session: $VAMOS_SESSION" # 320 a spot against 545 curl -s "$VAMOS/sellers/acme/catalog?buy_type=fixed_position" \ -H "Authorization: Bearer $TOKEN" -H "X-Sandbox-Session: $VAMOS_SESSION" # acme-daytime drops out curl -s "$VAMOS/sellers/acme/catalog?distribution=syndication" \ -H "Authorization: Bearer $TOKEN" -H "X-Sandbox-Session: $VAMOS_SESSION" # the syndication layer alone curl -s "$VAMOS/sellers/acme/avails?package_id=acme-gameday&weeks=2026-W47&weeks=2026-W51" \ -H "Authorization: Bearer $TOKEN" -H "X-Sandbox-Session: $VAMOS_SESSION" # two dates, and nowhere else
POST /v1/webhooks/endpoints; unregistered hosts fail order creation fast with a 422 so you never wonder why events did not arrive.