← Back to product·Simulmedia VAMOS Docs · v1 draft·Get started / Quickstart: place your first order
Get started

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.

OutcomeWhat you get
Full acceptance to placedunits_accepted: 10, an allocation, and a placement_receipt
Partial acceptanceThe order is placed; the unit is partial with num_accepted: 6
INVENTORY_UNAVAILABLErejected from a well-formed request, because 2026-W43 is dark
STALE_RATE_CARDfailed, with rate_drift per unit: what you locked against what the card says now
A countered unitnegotiating, and the unit reads countered with num_accepted: 0
Two buy types on one orderplaced at 11049.60. No buy type on the order; each line carries its own
Two posting policies on one orderplaced 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
One-time setup: your webhook host must be allowlisted before the platform will call it. Register it under Settings → Webhooks or POST /v1/webhooks/endpoints; unregistered hosts fail order creation fast with a 422 so you never wonder why events did not arrive.