UGCPocket docs

REST API & MCP server

UGC Pocket exposes an ordering layer for agents: estimate then order a UGC campaign over REST or MCP, and, on the creator side, declare published videos. An agent order always creates a campaign in draft status; a human confirms and funds it in the app. No payment is ever triggered by an agent.

Overview

REST basehttps://ugcpocket.com/api
MCP serverhttps://ugcpocket.com/mcp · Streamable HTTP, protocol 2025-06-18
Specification/openapi.json (OpenAPI 3.1)
CurrencyEUR, amounts in cents
Campaign budget300,000 to 10,000,000 cents (€2,000 to €100,000)

The draft created by an agent belongs to the organization that owns the key. The response includes a confirm_url that opens the campaign in the app for confirmation and funding. budget_max_cents is the campaign package, invoiced in full: UGC Pocket briefs, selects and pays the creators, and commits to getting the most views it can.

Getting started

An agent cannot sign up on its own: access is bootstrapped by a human in the app.

  1. Download the UGC Pocket app, then create an organization account (brand side) or a creator account. An agent cannot sign up on its own.
  2. Generate a key: brands in Profile → "API/MCP keys", creators in Settings → "API/MCP keys". The ugcp_live_… key is shown only once.
  3. Configure the agent with the Authorization: Bearer ugcp_live_… header.

First request

curl -X POST https://ugcpocket.com/api/campaigns \
  -H "Authorization: Bearer ugcp_live_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "title": "Kibble launch",
    "brief": "TikTok video featuring the product…",
    "prestation": "video_post",
    "target_categories": ["dog"],
    "platforms": ["TikTok"],
    "budget_max_cents": 350000
  }'
HTTP/1.1 201 Created
{
  "campaign_id": "8b1f…",
  "status": "draft",
  "ordered_by_agent": true,
  "next_step": "The brand must confirm and fund the campaign in the UGC Pocket app.",
  "confirm_url": "https://ugcpocket.com/c/8b1f…"
}

Authentication

API keys use the ugcp_live_… format, sent in the Authorization: Bearer header. A key carries a scope that determines which surfaces it can reach.

ScopeGenerated byGrants access to
campaigns:draftOrganization (brand) accountPOST /campaigns, GET /campaigns/{id}, and the 3 brand MCP tools.
creator:submitCreator accountThe 3 creator MCP tools: accepted campaigns, posted-video submission, deliverables.

A key with the wrong scope gets insufficient_scope (403) over REST, and a business rejection over MCP. Keys are revoked in the app, in the same place they were created; a revoked key then answers key_revoked (401), an expired one key_expired (401).

Without a key. Two REST endpoints are open (GET /service-info, POST /estimate), as is MCP discovery (initialize, tools/list) and the get_service_info tool. Everything else requires a key. Mind the asymmetry: POST /estimate is public over REST, but the MCP tool estimate_campaign requires a campaigns:draft key.

REST API

Base https://ugcpocket.com/api. Responses are JSON; errors use the envelope { "error": "code", "detail": "…" }. Key-authenticated routes emit no CORS headers: they are built for server-to-server calls, not for a browser.

EndpointAuthPurpose
GET/service-infononeProduct descriptor: enums, budget floor, links.
POST/estimatenoneFree-text brief → suggested fields + estimate.
POST/campaignscampaigns:draftCreates a campaign draft.
GET/campaigns/{id}API keyStatus of a campaign in the organization.
GET/api/service-info

Service descriptor, no authentication. Returns the valid enum values (categories, prestations, platforms), currency, budget_unit, min_campaign_budget_cents, the ordering model, and links to openapi.json, mcp and these docs. Call it first: it is the source of truth for enums. GET / returns the same payload.

POST/api/estimate

Analyzes a free-text brief (French or English) and returns structured fields plus an estimate of what the cap can produce. Writes nothing to the database.

// Request
{ "brief": "UGC videos for our new kibble, dog owners, max budget €5,000" }

// 200 OK
{
  "suggested": { "title": "…", "prestation": "video_post", "target_categories": ["dog"],
                 "platforms": ["TikTok"], "budget_max_cents": 500000 },
  "estimate": { "budget_max_cents": 500000, "estimated_creators": 33,
                "estimated_videos": 165, "max_views": 3750000, "note": "…" }
}

estimate is null when no budget can be inferred from the brief. A missing or non-string brief returns validation_error (422).

POST/api/campaigns

Creates a campaign in draft status. Body: a CampaignDraftInput object. Requires a campaigns:draft key. Returns 201 with campaign_id, status, ordered_by_agent, next_step, confirm_url.

Optional Idempotency-Key header: the response is cached for 24 h per key, so replaying the same request returns the same campaign_id instead of creating a duplicate. Use one UUID per ordering intent, not per attempt.

GET/api/campaigns/{id}

Returns campaign_id, title, status, ordered_by_agent and confirm_url. The lookup is scoped to the key owner's organization: an id belonging to another organization returns not_found (404), not 403.

Possible statuses: draft, open, in_progress, completed, cancelled.

CampaignDraftInput

The object sent to POST /campaigns and to the MCP tool create_campaign_order. Full schema in /openapi.json.

The input is coerced, not strictly rejected. Only two things return an error: a missing required field, and budget_max_cents below the floor. Everything else is silently normalised, so read the response rather than assuming your payload went through as written: title and brief are truncated to their maximum length, unknown enum values are dropped from target_categories and platforms, an invalid prestation falls back to video_post, objectives keeps its first 6 entries, a budget above the ceiling is clamped to 10000000, and undeclared fields are ignored.

FieldTypeDetails
titlerequiredstring ≤ 120Short campaign title.
briefrequiredstring ≤ 2000Creative guidelines, tone, constraints.
budget_max_centsrequiredintegerEuro cents. Minimum 200000 (€2,000), below that a 422. Above 10000000 (€100,000) the value is clamped, not refused. The campaign package, invoiced in full.
prestationenumvideo_post (default and only value).
target_categoriesenum[]See the 22 categories below.
platformsenum[]TikTok, Instagram, YouTube, Snapchat, Facebook.
objectivesstring[]Free-form campaign objectives.
brief_rawstringThe original untouched brief, stored as-is.
payment_triggerenumpublication or views.
views_thresholdinteger ≥ 0Views threshold, when payment_trigger=views.
deadlineISO dateYYYY-MM-DD.

Categories

dogcatpetscarcookingcouplefamilykidsbusinessfinancepodcastsportwellnessbeautyfashiontechgaminghomegardendiytravellifestyle

These are the values the API accepts. Call GET /service-info rather than hard-coding them.

Errors

Every REST error uses the { "error", "detail" } envelope. The error field is stable; detail is a human-readable message that may change.

HTTPerrorCause
401invalid_api_keyMissing, unknown or malformed key.
401key_revokedKey revoked in the app.
401key_expiredKey past its expiry.
403insufficient_scopeThe key does not carry campaigns:draft.
404not_foundCampaign missing, outside the organization, or unknown route.
422validation_errorMissing title, non-string brief, or budget_max_cents absent, non-numeric or below 200000. Unknown enums and undeclared fields do not land here: they are dropped silently.
429rate_limitedRate exceeded. Retry-After: 60 header.
400create_failedWrite rejected by the database: 200-draft cap over 24 h reached, or the key is not authorized for the organization. The reason is in detail.
500internalServer error. Safe to replay with the same Idempotency-Key.

Limits

LimitValueBehaviour when exceeded
Rate per key30 requests / minute429 rate_limited + Retry-After: 60
Drafts per key200 per rolling 24 h window400 create_failed, detail = "daily draft cap reached"
Campaign budget300,000 – 10,000,000 centsBelow the floor: 422 validation_error. Above the ceiling: clamped to 10,000,000.
Idempotency cache24 h per keyAfter that, the same idempotency key creates a new campaign

MCP server

Streamable HTTP MCP server at https://ugcpocket.com/mcp, protocol 2025-06-18, ugc-pocket-mcp v1.2.0. JSON transport only: the server emits no SSE stream, and a client that does not accept application/json gets 406. Only POST is accepted.

Discovery (initialize, tools/list) and the get_service_info tool work without a key. The other six tools require the key header.

Client configuration

{
  "mcpServers": {
    "ugc-pocket": {
      "type": "http",
      "url": "https://ugcpocket.com/mcp",
      "headers": { "Authorization": "Bearer ugcp_live_YOUR_KEY" }
    }
  }
}
Client-side limitation. Consumer claude.ai MCP connectors currently only accept OAuth authentication, not a bearer key. The MCP server is therefore used from clients that support custom headers (Claude Code, Cursor, etc.). The REST API works everywhere. An OAuth connector is a future iteration.

Brand tools campaigns:draft

ToolInputPurpose
get_service_info
no key
noneProduct descriptor and valid enums. Read-only, idempotent.
estimate_campaignbrief requiredFree-text brief → structured fields + estimate (creators, videos, maximum views). Writes nothing.
create_campaign_orderCampaignDraftInputCreates a draft. Accepts the HTTP Idempotency-Key header, as REST does.
check_campaign_statuscampaign_id requiredStatus of a campaign in the organization.

Creator tools creator:submit

Built for creators declaring many published videos, for example several hundred during a challenge. The key is generated on the creator side in Settings → "API/MCP keys".

ToolInputPurpose
list_my_campaignsnoneCampaigns you are selected on: campaign_id, title, status, package, payment trigger, remuneration, deadline, brief.
submit_posted_videocampaign_id, video_url requiredDeclares a published video. TikTok, Instagram or YouTube URLs get daily view tracking for 1 month; other https URLs are stored without tracking. Idempotent per campaign + URL pair, with already_submitted flagging a replay. 30 requests/min.
list_my_deliverablescampaign_id, before (optional)Your videos and their state (submitted, published, validated, revision, paid), tracked views, validated amounts in cents. 200 per call; paginate by passing the oldest created_at as before.

JSON-RPC errors

Two distinct registers, which an agent should not conflate: a protocol failure is an error object, a business rejection is a result with isError: true and an actionable message.

CodeCause
-32001Missing or invalid key. HTTP 401 + WWW-Authenticate: Bearer.
-32601Unknown tool or method.
-32602Missing required argument.
-32700 / -32600Unparseable JSON / invalid JSON-RPC request.
-32603Internal error.
isError: trueBusiness rejection: wrong scope, budget out of bounds, campaign not found, daily cap.

Machine resources

ResourcePurpose
/openapi.jsonOpenAPI 3.1 specification of the REST API.
/llms.txtLLM-readable product discovery and context.
/llms-full.txtExtended version.

Integration questions: hello@logics-studio.com