← Back to product·Simulmedia Switchboard Docs · v1 draft·Guides: for sellers / Host your own API: the pull connection
Guides: for sellers

Host your own API: the pull connection

Path C made concrete. If you already run order and inventory systems, you do not rebuild them against Switchboard: you host a small API on your side, and we call it. This page is the contract our adapters build to. Declare what you host in your capability profile and the platform never calls anything else.

This contract deliberately speaks your vocabulary: a property, daypart windows, cost_per_second pricing. The connection normalizes what you serve to the canonical objects in the object model and the API object reference. Field parity with the marketplace reference API is not expected, and none is asked for.

What you can host

CapabilityYou serveWe call it for
Catalog, rates, and availsGET /packagesPlanning reads and the pre-order availability check
Order intakePOST /ordersSubmitting confirmed buyer orders into your system
Order statusGET /orders/{id}Following your decision to a terminal state
Health and discoveryGET /healthLiveness and the contract version you implement

Direction stays per capability. Host order intake and push avails by file drop, or host everything, or start with intake alone; your profile declares the mix, and you climb levels when you choose to.

The catalog pull

GET/packagesyour host

One endpoint returns everything a plan needs: your packages (recurring inventory: a property, a daypart window, a weekday pattern, a validity window), each package's dated slots, and each slot's rates with availability. These are the same nouns the rest of the marketplace speaks; see Packages.

FieldTypeNotes
start_date requireddateQuery parameter. First date of the slot window to return.
end_date requireddateQuery parameter. Last date of the slot window. A narrow window on this same call is the pre-order availability check.
packages[]arrayRecurring inventory: property, daypart window, weekday pattern, validity window.
packages[].slots[]arrayDated, orderable instances of the package.
slots[].rates[]arrayPrice and availability entries; one slot can carry several rate classes, including rates scoped to a single buyer.
rates[].cost_per_secondnumberPer-second pricing. We compute unit_cost = cost_per_second x spot_length at order time, so spot length stays an order-time choice rather than part of the slot's identity.
rates[].availablebooleanAvailability of this rate class on this slot.
GET https://api.acmebroadcasting.example/switchboard/packages?start_date=2026-10-05&end_date=2026-10-18

{
  "packages": [{
    "package_id": "ACME-PRIME-MSu",
    "property": "Acme Broadcasting",
    "start_date": "2026-09-28", "end_date": "2026-12-27",
    "daypart_start": "20:00", "daypart_end": "24:00",
    "weekdays": ["Mon","Tue","Wed","Thu","Fri","Sat","Sun"],
    "slots": [{
      "slot_id": "ACME-PRIME-MSu-20261007",
      "date": "2026-10-07",
      "rates": [
        { "class": "standard",    "cost_per_second": 94.0, "hh_impressions": 121000, "available": true },
        { "class": "competitive", "cost_per_second": 86.5, "hh_impressions": 121000, "available": true }
      ]
    }]
  }]
}
  • A slot is a dated, orderable instance of a package. A rate is a price and availability entry on a slot.
  • We plan against the cached read, then re-check exactly the dates in play just before ordering. Validation remains the backstop for the residual race.
Use any stable slot_id scheme you like; the marketplace's canonical form is {package_id}:{date}, and the connection normalizes yours to it. Buyers see your inventory in the same package, slot, and rate shapes documented across these pages; you never restructure your systems.

Order intake

POST/ordersyour host

We submit confirmed buyer orders into your system. The submission is the order envelope:

FieldTypeNotes
order_id requiredstringThe platform order id. With order_version, the idempotency pair.
order_version requiredintegerA revision of intent, never a retry; bumped only when the order's content changes on purpose.
lines[]arrayOne per line item: your slot id (or package and week), rate class, spot length, unit count, and the locked price.
POST https://api.acmebroadcasting.example/switchboard/orders
# body: the order envelope, fields above

202 { "order_id": "ACME-77120", "status": "received", "received_at": "2026-10-05T14:20:00Z" }

GET/orders/{id}your host

The 202 acknowledges receipt under your own order id; the decision comes from the status read, keyed by that id:

FieldTypeNotes
statusstringYour order-level status: received, processing, confirmed, partial, or rejected, mapped onto the canonical state machine.
lines[]arrayOne result per submitted line; every line we send comes back with an outcome.
lines[].statusstringThe per-line outcome, for example accepted.
lines[].num_requested, lines[].num_acceptedintegerPartial acceptance is per line.
lines[].allocation[]arrayAuthoritative air dates: {date, units} entries whose units sum to num_accepted. A week-grain line may allocate across several dates; a slot line has a single entry by construction.
lines[].reasonstringMachine-readable on every rejected line; null otherwise.
GET https://api.acmebroadcasting.example/switchboard/orders/ACME-77120

200 {
  "order_id": "ACME-77120",
  "status": "confirmed",
  "lines": [{
    "slot_id": "ACME-PRIME-MSu-20261007",
    "status": "accepted",
    "num_requested": 4, "num_accepted": 4,
    "allocation": [ { "date": "2026-10-07", "units": 4 } ],
    "reason": null
  }]
}
  • Rejected lines carry a reason we can route on, and an all-rejected response is still a successful response; errors are for transport and logic failures only.
  • Counters, if you support them, arrive as additional lines with status: "countered" and an in_response_to naming the line they answer. Adding counters later is a non-breaking climb; see Negotiation.
  • Your statuses map onto the canonical state machine. Buyers never see your wire format, only marketplace state.

Idempotency and replay

Every submission carries an order_id and an order_version. The version identifies a revision of intent, never a retry: we bump it only when the order's content changes on purpose.

  • On a duplicate (order_id, order_version), replay your stored response verbatim. Do not re-run order logic and do not error: the replay is how we learn the outcome of a request we never got an answer to.
  • The public API holds itself to the same rule: a buyer resubmitting an identical order gets the original response back, and 409 is reserved for a real conflict. See API conventions.

Decision delivery

Three rungs; declare the ones you offer and we adapt.

ModeHow it worksGood for
Poll (baseline)We call GET /orders/{id} until the decision is terminalEveryone; no public endpoint needed on either side
CallbackYou POST the decision to a Switchboard URL registered with youLower latency at volume
Shared queueYou publish decisions to a shared queue we consumeTeams that would rather not host or call public endpoints

GET/healthyour host

FieldTypeNotes
statusstringok when live.
protocol_versionstringThe contract version you implement.
capabilitiesobjectAdvertised capability slots such as rate_card_distribution and live_avails; declared-but-null until you flip them live.
GET https://api.acmebroadcasting.example/switchboard/health

200 {
  "status": "ok",
  "protocol_version": "v1",
  "capabilities": { "rate_card_distribution": null, "live_avails": null }
}

Capabilities you do not support yet ship as declared-but-null. When your rate feed or live avails go live, the slot flips and the platform starts using it; nothing else changes.

What we need from you

  • Auth. An OAuth2 client-credentials grant is the standard ask: a token endpoint plus a client ID and secret issued to us. An API key works if that is what your stack issues. Credentials sit in managed key storage on our side and rotate quarterly.
  • Allowlisting. Mutual IP allowlists on request; mTLS as optional hardening.
  • SLAs. Your decision latency and lead-time rules live in your capability profile (for example: orders need 48 hours before first air), so the platform never submits what you cannot decide in time.
  • A test environment. A sandbox host we can point the connection at before production traffic.
None of this asks you to change how inventory or orders are modeled internally. The contract is the wire shape; the adapter on our side does the translating, and your system stays the system of record for your side of the trade.