distribution.techdistribution.tech

Docs

Run a campaign with your agent

How an agent runs a distribution campaign with an API key: the eight-step flow, the limits, the spending mandate, every error code and feedback.

Per our API limits, one key makes up to 60 requests a minute and one brand starts up to 3 new campaigns a day. The machine-readable contract is openapi.json. Keys and mandates are set in the workspace under Developers. What changed in the API and the app each week is in the changelog.

Connect your agent

Approved brands only. We review every brand before its agent can buy. Sign in at Connect your agent to ask for approval; once your brand is approved, that page gives you the MCP link to copy, and you create a key in Developers.

Before you have a key: the guest server

The guest server is https://app.distribution.tech/api/mcp/guest, with no key and no sign-in. Your agent can add it and list its tools now; it answers the network's coverage per channel and the fit for an audience you describe, and anything that touches a campaign of yours needs an approved brand and a key.

In Cursor: add the guest server in one click. The link opens Cursor and adds it to your MCP servers as distribution.

With your key

The MCP server is https://app.distribution.tech/api/mcp (streamable HTTP). Send your API key from Developers as Authorization: Bearer plus the key.

Claude Code, in a terminal

claude mcp add --transport http distribution https://app.distribution.tech/api/mcp --header "Authorization: Bearer YOUR_API_KEY"

Cursor, in ~/.cursor/mcp.json

{
  "mcpServers": {
    "distribution": {
      "url": "https://app.distribution.tech/api/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_API_KEY"
      }
    }
  }
}

Codex, in ~/.codex/config.toml

[mcp_servers.distribution]
url = "https://app.distribution.tech/api/mcp"
bearer_token_env_var = "DISTRIBUTION_API_KEY"

# then, in the shell that starts Codex: export DISTRIBUTION_API_KEY=YOUR_API_KEY

Three recipes

Where does my campaign stand?

Ask your agent: “Check my latest distribution campaign and tell me what is live and what is next.”

  1. campaign_status for the campaign: its stage and each placement's state.
  2. results for the numbers the posts have earned so far.

Quote and approve the plan

Ask your agent: “Quote my draft plan, and if my key's mandate allows it, approve it and give me the pay link.”

  1. campaign_status without campaign_id lists your campaigns with their campaign_id; campaign_status with it returns the current plan, whose id (plan.id) the next two steps take.
  2. plan_quote for that plan.id: the plan as signed, its terms_fingerprint, and whether the mandate allows signing now.
  3. approve_plan for that plan.id with the terms_fingerprint and an idempotency_key: signs the plan and returns the checkout link.

Show me the proof

Ask your agent: “List the proof for every post in my campaign, with links.”

  1. evidence for the campaign: each post's live link and its delivery proof.

Your agent reads and writes your distribution workspace with an API key from Developers. The key's scopes decide which tools it sees, and its mandate decides what it may spend.

The flow

  1. brain_get: your company context, so the campaign speaks for the right product and audience.
  2. resolve_audience: which audience segments and channels your audience matches, and how strong coverage is.
  3. creators_search with match set to those segments: the recommended approved creators, each with an all-in price per format. creator_get for one creator's full profile.
  4. create_campaign: from a goal (launch, funding, feature or program), a product, a moment, a window and a budget to a priced plan, or a plan_note when nothing on sale fits yet. Audience and company come from your company context when left out, the markets default to worldwide and the outcome to the goal's usual one; the answer's defaults names each. With no budget_min_usd the budget is held to the key's mandate.
  5. plan_add_creator and plan_remove_creator to change the plan, or plan_swap_creator to replace one creator with another in one step; set_creator_guidelines for what the booked creators read.
  6. plan_quote: the plan as you sign it, the terms_fingerprint, and whether the key's mandate allows signing it now.
  7. approve_plan with that terms_fingerprint: signs the plan and returns the Stripe Checkout link the brand pays.
  8. campaign_status, evidence and results: where it stands, proof of each post, and the numbers. archive_campaign takes a campaign out of the list; delete_campaign removes a draft that was never sent.

Limits

  • 60 requests per key in a fixed 60-second window, which opens at the key's first request after the last window closed; RateLimit-Remaining and RateLimit-Reset count down that window. Over it you get 429 rate_limited with Retry-After. A burst that runs across two windows can pass more than 60 in one rolling minute, so pace by the headers, not by your own clock.
  • 3 new campaigns in any 24 hours per brand, counting a plan plan_add_creator starts; archiving or deleting one does not give its slot back. Over it you get 429 rate_limited with details.reason daily_campaign_limit, and details.resets_at (Retry-After in seconds) is when the limit reopens: 24 hours after the first of them, not midnight.
  • creators_search lists the approved creators that reach an audience whenever one applies: the list is the segments you pass in match, else your brand's own audience (the same creators inventory counts); a segment you name without match replaces your audience as the filter; q, reach_band and the filters narrow it. With no brand audience on file, a call that names only q, reach_band or a filter lists every approved creator those match, with no audience limit. A call that names none of those while your brand has no audience on file is refused with 400 filter_required: name an audience. It pages through the approved creators that match, best fit for your audience first: limit (default 24, at most 50) and offset, with total saying how many there are and priced how many of them have something on sale now. catalog is the whole network, whatever you filtered: catalog.approved is every approved creator and catalog.priced how many of them are bookable now, the same numbers the app and the site show; it is left out when the network cannot be counted right now, never guessed. A creator with nothing on sale carries price_estimate (an estimated range, never a price) and ask_price true: creator_ask_price (POST /creators/{id}/price-request) asks our team for its price, the same ask as the app's button; the price then shows on the creator and is held for you for 48 hours, asking again answers already true, and the creator is never contacted directly. Rows carry no handle; creator_get opens one creator in full and counts as one reveal. No creator read carries an X handle or an x.com link: a creator's handle and profile link show once your order for a plan with that creator is paid, on that plan's quote (plan_quote). Each API key has its own creator allowance, and all of your brand's keys together share one ceiling of the same size, apart from the app's (the app and its chat share the workspace's): 20 creator reads a minute, 60 searches a day, 60 creators opened a day (one reopened the same day is free) and 300 different creators shown a day. Past it, a read answers 429 rate_limited with Retry-After, the reason, and scope (key for this key, api for the brand's ceiling).
  • Every write takes an idempotency_key (8 to 128 characters: letters, digits, dot, underscore, colon or hyphen). A retry with the same key and arguments returns the first answer and never runs twice.

What bookable means

A creator is bookable (on sale) when all of these hold: our team approved the creator after vetting (verified and active); our vetting keeps the creator among those we sell, not flagged as an inactive account and not excluded; and at least one of the creator's formats has a fresh price: a price at most 30 days old, or a price quoted by our team. Every surface counts bookable creators with this one rule (the Creators list's priced, Market's Bookable now, the Overview, the chat, creators_search priced and catalog.priced, GET /api/network/facts and the guest quote), so the same filter shows the same number. An approved creator who is not bookable is still listed, with price on request. priced in creators_search, catalog.priced and the network facts count creators by it; inventory's priced_units counts posts on sale, not creators.

The mandate

A brand sets a mandate on each key in Developers: the most a plan may total, a monthly cap, the channels, whether the key may sign, an expiry, and the brand terms a person accepted for it. A write outside it is refused before any spend with mandate_refused, the limit and how much over, and escalate: true. Take that to a person at the brand; do not retry.

A plan over its campaign's budget is refused with conflict, reason over_budget and over_by_cents, always, and so is a creator that would take it over: plan_add_creator and plan_swap_creator answer conflict, reason over_budget, with budget_cents and remaining_cents (the room before that creator), and leave the plan as it was. plan_quote says when a plan is over its budget in over_budget (budget_cents, total_cents, over_by_cents), at current prices when a creator's price rose past it, and its policy refuses with over_budget. Nobody approves an overage, the brand included, and no field confirms one: remove a creator until the plan fits, or ask a person at the brand to raise the budget, then quote again.

Errors

Every error carries a code and a message: bad_request, filter_required, unauthorized, forbidden, not_found, method_not_allowed, conflict, idempotency_conflict, in_progress, unprocessable, mandate_refused, rate_limited, unavailable, internal. A plan that changed since its quote is refused with conflict and reason terms_changed: quote again.

Feedback

When a call fails unexpectedly, a field is empty or wrong, or a capability is missing, call feedback_send with identifiers only. Check feedback_list first so one issue is reported once.

Tools

  • inventory (inventory:read): Network coverage. REST: GET /api/v1/inventory.
  • resolve_audience (audience:read): Resolve an audience. REST: GET /api/v1/audience.
  • campaign_status (campaigns:read): Campaign status. REST: GET /api/v1/campaigns and /api/v1/campaigns/{id}.
  • evidence (evidence:read): Proof of delivery. REST: GET /api/v1/evidence.
  • results (results:read): Results. REST: GET /api/v1/results.
  • brain_get (brain:read): Company context. REST: GET /api/v1/brain.
  • creators_search (creators:read): Find creators. REST: GET /api/v1/creators.
  • creator_get (creators:read): One creator. REST: GET /api/v1/creators/{id}.
  • creator_ask_price (creators:read): Ask for a price. REST: POST /api/v1/creators/{id}/price-request.
  • plan_quote (campaigns:read): Quote a plan. REST: GET /api/v1/plans/{id}/quote.
  • create_campaign (campaigns:write, write): Start a campaign. REST: POST /api/v1/campaigns.
  • plan_add_creator (campaigns:write, write): Add a creator. REST: POST /api/v1/plans/{id}/lines.
  • plan_remove_creator (campaigns:write, write): Remove a creator. REST: DELETE /api/v1/plans/{id}/lines/{line_id}.
  • plan_move_dates (campaigns:write, write): Move a plan's dates. REST: POST /api/v1/plans/{id}/dates.
  • plan_swap_creator (campaigns:write, write): Swap a creator. REST: POST /api/v1/plans/{id}/lines/{line_id}/swap.
  • set_creator_guidelines (campaigns:write, write): Set the creator guidelines. REST: POST /api/v1/campaigns/{id}/creator-guidelines.
  • approve_plan (plans:approve, write): Approve and get the pay link. REST: POST /api/v1/plans/{id}/approve.
  • archive_campaign (campaigns:write, write): Archive a campaign. REST: POST /api/v1/campaigns/{id}/archive.
  • rename_campaign (campaigns:write, write): Rename a campaign. REST: PATCH /api/v1/campaigns/{id}.
  • delete_campaign (campaigns:write, write): Delete a draft campaign. REST: DELETE /api/v1/campaigns/{id}.
  • feedback_send (any key): Send feedback. REST: POST /api/v1/feedback.
  • feedback_list (any key): Your feedback. REST: GET /api/v1/feedback.