Skip to content
Crossfeed

Developer docs

Programs and assistants can do for a network what its members do in the app: import the roster, plan and run launches, answer the agent’s questions, edit drafts, and triage pitches. Approving stays with the members, in the app.

Four ways in

Way inWhat it isReached with
REST APIEvery resource of a network under /api/v1, for scripts in any language, described by an OpenAPI 3.1 document.An API key
TypeScript SDKA typed method for every route, and helpers that wait for long work, in Node, Bun, Deno, and Workers.An API key and the network’s ID
MCP server23 tools for assistants such as Claude Code, over Streamable HTTP at /mcp.An API key
Skills and pluginTwo Agent Skills that plan launch promos and triage guest pitches, and a Claude Code plugin that installs them with the MCP server.An API key in CROSSFEED_API_KEY

What a program can and can’t do

An API key acts for its network. It reads the network and today’s usage, renames the network and sets its CPM, imports the roster and settles its shows, plans launches and starts their runs, answers the agent’s questions, edits draft insertion orders, exports approved ones as CSV, reads the evidence of every Qloo call, loads the inbox, starts pitch runs, and edits or discards drafted replies.

Only a member of the network, signed in to the app, approves an insertion order or a reply. No route or tool approves either (ADR 0002, approval stays in the app): an API key is held by a program that may run unattended, and a plan or an email never ships without a person. An order exports only once a member has approved it; until then its export answers 409 conflict. Members, invites, API keys, the network’s own Qloo key, deleting a launch, and deleting the network are the app’s too.

When a run needs a decision, such as the slot budget, it waits on a question for a person. A program relays the question and answers with that person’s words.

API keys

An owner makes a key in the network’s settings, under API keys, and sees its secret once. The settings show the network’s ID under the keys. Send the key with every request:

HTTP
Authorization: Bearer cf_live_…

A key reaches its own network alone: to it, every other network is the same 404 not_found. Keys don’t expire. Revoking one stops it at once, and its next request answers 401 unauthorized. A member’s session reaches the API too, from Crossfeed’s own pages; programs use a key.

Long work

An import, a comp search, measuring the roster’s overlap, profiling its listeners, an inbox load, and a run answer 202 at once with the resource as it stands; read the resource until the work settles. A run is running, or waiting on its question; then completed, or stopped or failed with the reason in error. With one of the network’s API keys in CROSSFEED_API_KEY and its ID in CROSSFEED_NETWORK_ID, both from its settings, import a made-up roster:

Shell
curl -X POST "https://crossfeed.m1kuorg.workers.dev/api/v1/networks/$CROSSFEED_NETWORK_ID/roster" \
  -H "Authorization: Bearer $CROSSFEED_API_KEY" \
  -H 'Content-Type: text/csv' \
  --data-binary $'name,downloads\nDeep Cuts,12000\nField Notes,8000\n'

It answers 202, with the new shows pending. Read the roster until its status is settled:

Shell
curl "https://crossfeed.m1kuorg.workers.dev/api/v1/networks/$CROSSFEED_NETWORK_ID/roster" -H "Authorization: Bearer $CROSSFEED_API_KEY"

Limits

LimitHow much
Requests120 requests a minute per API key, the MCP server’s included; past it, 429 rate_limited
Model runsa launch run, an inbox load, or a pitch run10 a day per network; past it, 429 limit
Qloo calls2,000 a day per network, counted on the network’s own Qloo key apart from everyone else’s once an owner adds one
A runAt most 12 model turns and 60 Qloo calls
LaunchesAt most 100 a network keeps at once
API keysAt most 10 a network keeps in use at once
RosterAt most 50 shows
InboxAt most 50 pitches of up to 10,000 characters each
Uploads5 MB a body

A day is a UTC day, and counts reset at 00:00 UTC. A network counts as one visitor of its own, so its members’ pages and its keys share its daily counts, beside the limits for everyone in the beta terms. Settings shows the network’s usage, and so does GET /api/v1/networks/{networkId}/usage.

Errors

Every refusal is JSON, with a stable code to branch on and a message for a person that says what to do:

JSON
{
  "error": {
    "code": "conflict",
    "message": "The roster already has shows. Import with replace=true to import over it: shows already on it keep their matches."
  }
}
CodeStatusWhen
invalid400The request, or a value in it, can’t be used as it is. The message names the field.
unauthorized401No API key, or one that is unknown or revoked.
forbidden403The caller can’t do this, such as an API key deleting a launch.
not_found404No such resource, or one the key can’t reach, such as another network.
conflict409The resource’s state refuses it, such as an import over a roster without replace=true.
too_large413An upload over 5 MB.
unreadable422An upload that can’t be read, such as a CSV with no name column.
limit429A daily cap, the network’s or all of Crossfeed’s. Counts reset at 00:00 UTC.
rate_limited429Too many requests a minute with one key. Wait a minute: it answers Retry-After: 60.
internal500Something went wrong in Crossfeed. Try again in a moment.
Next:REST API