Overview
Add a creator to a plan awaiting approval by its unit_id (a format's unit_id from creators_search or creator_get), at that format's current all-in price, the same as the brand adding it in the workspace. Leave out plan_id (REST: POST /api/v1/plans/current/lines) to add to the brand's newest plan awaiting approval, or to start a plan for this creator when none waits; that also needs the mandate to allow starting a campaign, and counts toward the brand's 3 new campaigns in 24 hours. Only approved creators on sale can be added (not_approved otherwise). A creator who would take the plan's all-in total over the mandate's maximum plan total, or this month's signed spend plus that total over its monthly cap, is refused (mandate_refused, over_max_plan_total or over_monthly_cap) and the plan is left as it was. A creator who would take the plan's all-in total over its campaign's budget is refused for everyone (conflict, reason over_budget, with budget_cents and remaining_cents, the room left before this creator) and the plan is left as it was; pick a creator priced within remaining_cents or remove one first. Returns the new line_id and the plan as plan_quote returns it. Needs an idempotency_key. Scope: campaigns:write. MCP tool: plan_add_creator.
- Endpoint
POST https://app.distribution.tech/api/v1/plans/{id}/lines- Scope
campaigns:write- Changes data
- Yes, send an Idempotency-Key header
- MCP tool
- plan_add_creator
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string (uuid) or one of "current" | Yes | A plan id, or current: the brand's newest plan awaiting approval, or a new plan started for this creator. |
Idempotency-Key | header | string | Yes | 8 to 128 letters, digits or . _ : -. Retry with the same key and the same request to get the same answer; it never runs twice. |
Request body
| Name | Type | Required | Description |
|---|---|---|---|
unit_id | string (uuid) | Yes | A format's unit_id from /creators. |
{
"unit_id": "<uuid>"
}Request with curl
Set DISTRIBUTION_API_KEY to a key from Developers with the campaigns:write scope, and each path variable to an id from an earlier answer. The body carries the required fields.
curl -X POST "https://app.distribution.tech/api/v1/plans/$PLAN_ID/lines" \
-H "Authorization: Bearer $DISTRIBUTION_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"unit_id": "<uuid>"
}'Response
200 OK. The body has these fields:
| Name | Type | Required | Description |
|---|---|---|---|
line_id | string (uuid) | Yes | |
plan | object | Yes | A plan as the person sees it and as an agent signs it (v1-product-spec 8.5 Quote). |
{
"line_id": "<uuid>",
"plan": {
"plan_id": "<uuid>",
"campaign_id": "<uuid>",
"version": 0,
"status": "awaiting_approval",
"currency": "<string>",
"launch_window_start": "<date>",
"launch_window_end": "<date>",
"terms_fingerprint": "<string>",
"total_cents": 0,
"totals": {},
"lines": [
{
"id": "<uuid>",
"creator": {
"id": "<uuid>",
"name": "<string>",
"handle": "<string>",
"profile_url": "<string>",
"avatar_url": "<uri>",
"followers": 0
},
"creator_id": "<uuid>",
"unit_id": "<uuid>",
"stage_id": "planned",
"stage": "<string>",
"channel": "<string>",
"channel_label": "<string>",
"format": "<string>",
"deliverable": "<string>",
"scheduled_date": "<date>",
"price_cents": 0,
"vetting": "<string>",
"reason": "<string>",
"expected_views_low": 0,
"expected_views_high": 0,
"on_sale": false
}
],
"payment_options": [
{
"rail": "checkout",
"label": "<string>",
"clears": "<string>"
}
],
"agent_payment": "checkout",
"brand_terms": {
"current_version": "<string>",
"current_number": 0,
"url": "<string>",
"accepted_for_this_key": "<string>",
"accepted_number_for_this_key": 0
},
"mandate": {
"key_id": "<uuid>",
"version": 0,
"max_plan_cents": 0,
"monthly_cap_cents": 0,
"channels": [
"<string>"
],
"may_approve": false,
"may_exceed_budget": false,
"expires_at": "<date-time>",
"updated_at": "<date-time>",
"spent_this_month_cents": 0,
"terms_version": "<string>",
"terms_accepted_at": "<date-time>",
"terms_accepted_email": "<string>"
},
"policy": {
"allowed": false,
"refusal": {}
},
"blocked_lines": [
"<uuid>"
],
"over_budget": {
"budget_cents": 0,
"total_cents": 0,
"over_by_cents": 0
},
"order": {},
"valid_while": "terms_fingerprint_unchanged"
}
}Errors
| Status | When |
|---|---|
400 | A parameter is not valid: details.problems names each field and what it accepts. |
401 | No API key, an unknown one, or a revoked one. |
403 | The key lacks the scope, or the mandate refuses (mandate_refused, with the limit and how much over). |
404 | No plan, line or creator of yours has that id. |
409 | conflict (with a reason), idempotency_conflict, or in_progress (Retry-After). |
422 | unprocessable: the body is readable but a value cannot be used (a blank or too long name, a campaign that cannot be planned yet); details.problems names each field when there are several. |
429 | Too many requests for this key in the current minute (60); Retry-After and RateLimit-Reset say when to try again. On POST /campaigns and on POST /plans/current/lines when it starts a plan: the brand's 3 new campaigns in 24 hours are used (details.reason daily_campaign_limit); details.resets_at and Retry-After say when the limit reopens, a rolling 24 hours from the first of them, not a calendar day; archiving or deleting a campaign does not give its slot back. On GET /creators and GET /creators/{id}: a creator read limit is used (details.reason per_minute, daily_lists, daily_reveals, daily_seen or locked). daily_seen is your workspace's one pool of 300 creator views a day (300 different creators shown or opened since midnight UTC), shared by the app and every API key of the brand: details.scope workspace and details.limit 300; a creator already seen today, in the app or through any key, still opens and costs nothing. daily_lists is the day's list calls (each GET /creators page or creators_search is one; details.limit, 60): it is counted apart from creator views, resets at midnight UTC, and Retry-After says when. daily_reveals is the day's creator profiles opened (details.limit, 60). The other reasons are this key's own limit (details.scope key) or the brand's ceiling over all its keys (details.scope api). |
500 | internal: the request failed on our side. Retry it with the same Idempotency-Key. |
503 | unavailable: writes are not available right now (for an approval: payments are not open yet). |
Related endpoints
POST /plans/{id}/approve: Approve and get the pay linkPOST /plans/{id}/dates: Move a plan's datesGET /plans/{id}/quote: A plan as you sign itDELETE /plans/{id}/lines/{line_id}: Remove a creatorPOST /plans/{id}/lines/{line_id}/swap: Swap a creator
Get access
Keys are created in Developers once your brand is approved. Connect an agent to ask for access, then read the agent guide for the full flow.