Skip to content
Crossfeed

REST API

Every resource of a network, under https://crossfeed.m1kuorg.workers.dev/api/v1/networks/{networkId}. This reference is generated from the API’s OpenAPI 3.1 document, the contract its requests are validated against, served at /api/v1/openapi.json. Signed in, you can send any route to your own network from the playground.

Every route

Each route takes the network’s ID, from its settings, and one of its API keys as Authorization: Bearer cf_live_…. The paths below are under /api/v1/networks/{networkId}. Bodies are JSON unless a route takes a file. The errors and limits are the same for every route. The inbox and pitch run routes answer 404 while guest pitches are switched off.

Each route’s cost says what a call does before you send it:

  • Reads The call changes nothing.
  • Changes It edits or adds to what the network has.
  • Deletes It removes something for good.
  • Spends a model run It counts as one of the network’s daily model runs, whose turns call Qloo too.
  • Calls Qloo It counts in the network’s daily Qloo calls: an import’s lookups, a comp search, measuring the roster’s overlap, profiling its listeners, or a run resumed by an answer.
  • Session only Only a member’s session, from one of Crossfeed’s pages, can send it. An API key answers 403.

Two routes do more with replace=true. Importing a roster over one that has shows also changes the figures the file brings. Loading the inbox over one that has pitches also deletes them, with the last pitch run’s lists and replies, and ends a pitch run waiting on a question. The OpenAPI document gives each route’s cost as x-crossfeed-cost, and a session-only route x-crossfeed-session-only: true with the session alone as its security.

MethodPathWhat it doesCost
GET/Read the networkReads
PATCH/Rename the network or set its CPMChanges
GET/usageRead today's usageReads
GET/rosterRead the rosterReads
POST/rosterImport a rosterCalls Qloo
PATCH/roster/shows/{name}Settle or edit a showChanges
POST/roster/shows/{name}/comp-searchSearch for a compCalls Qloo
GET/roster/overlapRead the roster's overlapReads
POST/roster/overlapMeasure the roster's overlapCalls Qloo
GET/roster/listenersRead the roster's listener profileReads
POST/roster/listenersProfile the roster's listenersCalls Qloo
GET/inboxRead the inboxReads
POST/inboxLoad the inboxSpends a model run
PATCH/inbox/pitches/{pitchId}Confirm a pitch's workChanges
GET/pitch-runsList pitch runsReads
POST/pitch-runsStart a pitch runSpends a model run
GET/pitch-runs/{runId}Read a pitch runReads
POST/pitch-runs/{runId}/answerAnswer a pitch run's questionCalls Qloo
PATCH/pitch-runs/{runId}/replies/{pitchId}Edit a replyChanges
POST/pitch-runs/{runId}/replies/{pitchId}/discardDiscard a replyChanges
GET/launchesList launchesReads
POST/launchesCreate a launchChanges
GET/launches/{launchId}Read a launchReads
DELETE/launches/{launchId}Delete a launchDeletesSession only
GET/launches/{launchId}/evidenceRead a launch's evidenceReads
POST/launches/{launchId}/runsStart a runSpends a model run
GET/launches/{launchId}/runs/{runId}Read a runReads
POST/launches/{launchId}/runs/{runId}/answerAnswer a run's questionCalls Qloo
PATCH/launches/{launchId}/insertion-orders/{orderId}Edit a draft insertion orderChanges
GET/launches/{launchId}/insertion-orders/{orderId}/csvExport an approved insertion orderReads

Network

Read the network

GET /api/v1/networks/{networkId} Reads

Answers 200The network. (Network)
Refuses401 unauthorized, 404 not_found, 429 limit or rate_limited

Rename the network or set its CPM

PATCH /api/v1/networks/{networkId} Changes

A field left out stays as it is. A member's session must be an owner's; an API key, which an owner made, may.

Bodyapplication/json (NetworkChange)
Answers 200The network as changed. (Network)
Refuses400 invalid, 401 unauthorized, 403 forbidden, 404 not_found, 429 limit or rate_limited

Read today's usage

GET /api/v1/networks/{networkId}/usage Reads

The network's Qloo calls and model runs today against its own daily caps, whichever member or key made them. Crossfeed's caps across all networks apply too.

Answers 200Today's usage. (Usage)
Refuses401 unauthorized, 404 not_found, 429 limit or rate_limited

Roster

Read the roster

GET /api/v1/networks/{networkId}/roster Reads

Answers 200The roster. (Roster)
Refuses401 unauthorized, 404 not_found, 429 limit or rate_limited

Import a roster

POST /api/v1/networks/{networkId}/roster Calls Qloo

Send the roster file as the body: a CSV with a name column and optional downloads, status, and description columns, or plain text with one show name per line, at most 50 shows and 5 MB. It answers 202 at once with the new shows pending; poll the roster until it settles. Over a roster that has shows, it needs replace=true: shows already on it keep their matches, comps, and exclusions, the figures the file brings change, and only new shows are looked up.

replaceIn the query, optional. Import over a roster that has shows.
Bodytext/csv, or text/plain
Answers 202The roster, with the import's shows pending. (Roster)
Refuses400 invalid, 401 unauthorized, 403 forbidden, 404 not_found, 409 conflict, 413 too_large, 422 unreadable, 429 limit or rate_limited

Settle or edit a show

PATCH /api/v1/networks/{networkId}/roster/shows/{name} Changes

Pick one of an ambiguous show's candidates (entityId), map a show to one of its comp search's results (compId), or change its figures or exclusion. A field left out stays as it is.

nameThe show's name as on the roster, URL-encoded.
Bodyapplication/json (ShowChange)
Answers 200The show as changed. (Show)
Refuses400 invalid, 401 unauthorized, 403 forbidden, 404 not_found, 429 limit or rate_limited

Search for a comp

POST /api/v1/networks/{networkId}/roster/shows/{name}/comp-search Calls Qloo

Search Qloo for a comparable show to stand in for one it has no match for. It answers 202 with the show's comp search pending; poll the roster until it's done, then map the show to a result with compId.

nameThe show's name as on the roster, URL-encoded.
Bodyapplication/json (CompQuery)
Answers 202The show, with its comp search pending. (Show)
Refuses400 invalid, 401 unauthorized, 403 forbidden, 404 not_found, 409 conflict, 429 limit or rate_limited

Read the roster's overlap

GET /api/v1/networks/{networkId}/roster/overlap Reads

Which of the roster's shows share listeners with each other, as Qloo measured it: each show's closest shows, as ranks and bands within its own ranking, and the close pairs, two shows each among the other's closest.

Answers 200The roster's overlap. (RosterOverlap)
Refuses401 unauthorized, 404 not_found, 429 limit or rate_limited

Measure the roster's overlap

POST /api/v1/networks/{networkId}/roster/overlap Calls Qloo

Ranks the rest of the roster against each show matched in Qloo, one Qloo call each, counting shows on the same Qloo show once and including shows left out of launches. It takes at least 4 such shows and a roster with no lookup pending, and is refused while a measure runs. It answers 202 at once with the overlap measuring; poll it until it isn't. With the roster's matched shows unchanged, it asks only the shows whose ranking is missing or failed, so it's up to one call per show, and none once every ranking has landed. It counts against the network's daily Qloo calls, and is refused when the Qloo requests left today, Crossfeed's shared ones or the network's own key's, couldn't take it and still leave a run's 60.

Answers 202The overlap, measuring, or as it was when there was nothing left to measure. (RosterOverlap)
Refuses401 unauthorized, 403 forbidden, 404 not_found, 409 conflict, 429 limit or rate_limited

Read the roster's listener profile

GET /api/v1/networks/{networkId}/roster/listeners Reads

Who the roster's shows reach, as Qloo profiled them: each show's leans by age band and gender, relative to the average in Qloo, which describe listeners, not people.

Answers 200The roster's listener profile. (RosterListeners)
Refuses401 unauthorized, 404 not_found, 429 limit or rate_limited

Profile the roster's listeners

POST /api/v1/networks/{networkId}/roster/listeners Calls Qloo

Asks Qloo for the listeners of every show matched in Qloo in one call, counting shows on the same Qloo show once and including shows left out of launches. It takes at least 4 such shows and a roster with no lookup pending, and is refused while a profile is being made. It answers 202 at once with the profile profiling; poll it until it isn't. With the roster's matched shows unchanged since a profile that landed, it asks nothing. It counts against the network's daily Qloo calls, and is refused when the Qloo requests left today, Crossfeed's shared ones or the network's own key's, couldn't take it and still leave a run's 60.

Answers 202The listener profile, profiling, or as it was when there was nothing to ask. (RosterListeners)
Refuses401 unauthorized, 403 forbidden, 404 not_found, 409 conflict, 429 limit or rate_limited

Inbox

Read the inbox

GET /api/v1/networks/{networkId}/inbox Reads

Answers 200The inbox. (Inbox)
Refuses401 unauthorized, 404 not_found, 429 limit or rate_limited

Load the inbox

POST /api/v1/networks/{networkId}/inbox Spends a model run

Send the pitches as the body: one email as message/rfc822 (an .eml file), an mbox export as application/mbox, plain text with a line of three dashes between pitches as text/plain (a pitch's first lines may give its From: and Subject:), or JSON pitches, at most 50 pitches and 5 MB. Of each email, only its sender, subject, and text are kept, never its attachments or other headers, and only its work's title and type reach Qloo. An email with no text, or over 10,000 characters, is skipped and named. It answers 202 at once with the pitches reading; poll the inbox until it's done. Reading the pitches counts as one of the network's model runs. Over an inbox that has pitches, it needs replace=true: they go, with the last pitch run's lists and replies, and a pitch run waiting on a question ends.

replaceIn the query, optional. Load over an inbox that has pitches.
Bodymessage/rfc822, or application/mbox, or text/plain, or application/json (PitchBatch)
Answers 202The inbox, with its new pitches reading. (Inbox)
Refuses400 invalid, 401 unauthorized, 403 forbidden, 404 not_found, 409 conflict, 413 too_large, 422 unreadable, 429 limit or rate_limited

Confirm a pitch's work

PATCH /api/v1/networks/{networkId}/inbox/pitches/{pitchId} Changes

Confirm which of an ambiguous work's same-title candidates the pitch is about (entityId). Only a work matched in Qloo is ranked.

pitchId
Bodyapplication/json (WorkConfirmation)
Answers 200The pitch, its work confirmed. (Pitch)
Refuses400 invalid, 401 unauthorized, 403 forbidden, 404 not_found, 429 limit or rate_limited

Pitch runs

List pitch runs

GET /api/v1/networks/{networkId}/pitch-runs Reads

The network's pitch runs, newest first: the first is the latest, whose replies the inbox holds until another run or a new inbox replaces them. Read one in full by its ID.

Answers 200The pitch runs. (application/json)
Refuses401 unauthorized, 404 not_found, 429 limit or rate_limited

Start a pitch run

POST /api/v1/networks/{networkId}/pitch-runs Spends a model run

Rank the inbox's pitched works against a roster show matched in Qloo, and against its best past guests' works too, then draft a reply to every pitch. Only each work's title and type reach Qloo, never a guest's name or a sender. It answers 202 at once with the run running; poll it until it's done or waiting on a question. It counts as one of the network's model runs, and a new run ends one left waiting.

Bodyapplication/json (PitchRunStart)
Answers 202The run, running. (PitchRun)
Refuses400 invalid, 401 unauthorized, 403 forbidden, 404 not_found, 409 conflict, 429 limit or rate_limited

Read a pitch run

GET /api/v1/networks/{networkId}/pitch-runs/{runId} Reads

Its status, any question it waits on, and as they arrive, its ranked lists and drafted replies. An earlier run reads as it ended.

runIdThe ID the run answered with when it started.
Answers 200The run. (PitchRun)
Refuses401 unauthorized, 404 not_found, 429 limit or rate_limited

Answer a pitch run's question

POST /api/v1/networks/{networkId}/pitch-runs/{runId}/answer Calls Qloo

Answer the question the run is waiting on, by its ID: with one of its options as choice, or your own words as text. The run resumes, answering 202; poll it again. It goes on with what's left of the run's Claude turns and Qloo calls, not as a new model run. A run started later, or loading the inbox, ends a run left waiting.

runIdThe ID the run answered with when it started.
Bodyapplication/json (Answer)
Answers 202The run, resumed. (PitchRun)
Refuses400 invalid, 401 unauthorized, 403 forbidden, 404 not_found, 409 conflict, 429 limit or rate_limited

Replies

Edit a reply

PATCH /api/v1/networks/{networkId}/pitch-runs/{runId}/replies/{pitchId} Changes

Reword one of the latest pitch run's drafted replies, once the run has ended. Editing an approved reply returns it to draft, and editing a discarded one restores it. Only a member approves a reply, in the app.

runIdThe ID the run answered with when it started.
pitchId
Bodyapplication/json (ReplyChange)
Answers 200The reply as edited. (Reply)
Refuses400 invalid, 401 unauthorized, 403 forbidden, 404 not_found, 409 conflict, 429 limit or rate_limited

Discard a reply

POST /api/v1/networks/{networkId}/pitch-runs/{runId}/replies/{pitchId}/discard Changes

Leave one of the latest pitch run's drafted replies out, once the run has ended. Editing it restores it.

runIdThe ID the run answered with when it started.
pitchId
Answers 200The reply, discarded. (Reply)
Refuses401 unauthorized, 403 forbidden, 404 not_found, 409 conflict, 429 limit or rate_limited

Launches

List launches

GET /api/v1/networks/{networkId}/launches Reads

The network's launches, newest first, as its Launches page lists them.

Answers 200The launches. (application/json)
Refuses401 unauthorized, 404 not_found, 429 limit or rate_limited

Create a launch

POST /api/v1/networks/{networkId}/launches Changes

Plan a new launch by its name, as Qloo lists a released show, or by up to three released shows as comps for an unreleased one. It joins the network's list, and every member's Launches page, with status new; start a run to rank the roster for it. A network keeps a limited number of launches at once, as the terms say: past it, a new one is refused with limit until a member deletes one on the Launches page.

Bodyapplication/json (LaunchPlan)
Answers 201The launch. (Launch)
Refuses400 invalid, 401 unauthorized, 403 forbidden, 404 not_found, 429 limit or rate_limited

Read a launch

GET /api/v1/networks/{networkId}/launches/{launchId} Reads

Its status and figures, its runs, and its latest run in full: any question it waits on, the chart, the topic baseline, the demographics check, and the insertion order, as they arrive.

launchIdThe ID the launch answered with when it was created.
Answers 200The launch. (Launch)
Refuses401 unauthorized, 404 not_found, 429 limit or rate_limited

Delete a launch

DELETE /api/v1/networks/{networkId}/launches/{launchId} DeletesSession only

Delete the launch for every member, with its runs, their insertion orders, and their evidence, which can't be brought back. Only a member's session from one of Crossfeed's pages may, as the Launches page does; an API key answers 403.

launchIdThe ID the launch answered with when it was created.
Answers 204Deleted.
Refuses401 unauthorized, 403 forbidden, 404 not_found, 429 limit or rate_limited

Evidence

Read a launch's evidence

GET /api/v1/networks/{networkId}/launches/{launchId}/evidence Reads

The launch's evidence log, oldest first: one record per Qloo call its runs made, the roster's lookups each run copied as it started, and the rankings of the roster's overlap each run copied for its finalists as it drafted its order. Every call number a run cites opens one of these.

launchIdThe ID the launch answered with when it was created.
Answers 200The evidence log. (application/json)
Refuses401 unauthorized, 404 not_found, 429 limit or rate_limited

Runs

Start a run

POST /api/v1/networks/{networkId}/launches/{launchId}/runs Spends a model run

Rank the network's roster, as it stands now, by fit with the launch's listeners in Qloo, then plan its promos to a draft insertion order priced at the network's CPM. With no body, the run plans the launch as it stands; a name or comps plan it anew. It answers 202 at once with the run running; poll it until it's done or waiting on a question. It counts as one of the network's model runs: past the network's daily model runs, it stops at once, saying why. A new run ends one left waiting.

launchIdThe ID the launch answered with when it was created.
Bodyapplication/json (LaunchPlan), optional
Answers 202The run, running. (LaunchRun)
Refuses400 invalid, 401 unauthorized, 403 forbidden, 404 not_found, 409 conflict, 429 limit or rate_limited

Read a run

GET /api/v1/networks/{networkId}/launches/{launchId}/runs/{runId} Reads

Its status, any question it waits on, and as they arrive, its chart, topic baseline, demographics check, and insertion order. An earlier run reads as it ended, with its order as it was left, to compare with a re-run.

launchIdThe ID the launch answered with when it was created.
runIdThe ID the run answered with when it started.
Answers 200The run. (LaunchRun)
Refuses401 unauthorized, 404 not_found, 429 limit or rate_limited

Answer a run's question

POST /api/v1/networks/{networkId}/launches/{launchId}/runs/{runId}/answer Calls Qloo

Answer the question the run is waiting on, by its ID, with the fields its kind takes: choice or text for a choice question; choice (one of its options) or text (the name Qloo lists the launch under) for launch; slots or dollars for budget; exclude, from its options, for exclusions; comps or text for comps; and picks for confirm_comps. Relay the question to the person planning the launch and answer with their words. The run resumes, answering 202; poll it again. It goes on with what's left of the run's Claude turns and Qloo calls, not as a new model run.

launchIdThe ID the launch answered with when it was created.
runIdThe ID the run answered with when it started.
Bodyapplication/json (Answer)
Answers 202The run, resumed. (LaunchRun)
Refuses400 invalid, 401 unauthorized, 403 forbidden, 404 not_found, 409 conflict, 429 limit or rate_limited

Insertion orders

Edit a draft insertion order

PATCH /api/v1/networks/{networkId}/launches/{launchId}/insertion-orders/{orderId} Changes

Change the lines of the launch's current order, once its run has ended: each finalist's promo slots and host-read angle, or drop a show by leaving its line out. Code prices it again at the network's CPM. Editing an approved order returns it to draft. Only a member approves an order, in the app.

launchIdThe ID the launch answered with when it was created.
orderId
Bodyapplication/json (OrderChange)
Answers 200The order as edited. (InsertionOrder)
Refuses400 invalid, 401 unauthorized, 403 forbidden, 404 not_found, 409 conflict, 429 limit or rate_limited

Export an approved insertion order

GET /api/v1/networks/{networkId}/launches/{launchId}/insertion-orders/{orderId}/csv Reads

The order as a CSV file for ad operations: a row per line, then the total, in whole dollars. Only an order a member approved in the app exports; a draft answers 409. An earlier run's order exports as it was left.

launchIdThe ID the launch answered with when it was created.
orderId
Answers 200The order as CSV. (text/csv)
Refuses401 unauthorized, 404 not_found, 409 conflict, 429 limit or rate_limited
Next:TypeScript SDK