API overview
The Hotline UGC REST API: what it covers, how requests are shaped, and what it deliberately does not do.
What it is
A REST API over your workspace. Ten endpoints, nine of them GET, served from the app itself rather than a separate host:
https://app.hotlineugc.com/api/v1
There is no api.hotlineugc.com. If you have an older note pointing at one, it was never real.
The API and the MCP server are two doors onto the same services. Anything one can read, the other can. Pick the API for scripts and pipelines, and MCP for an assistant.
What it covers
| Area | What you can read |
|---|---|
| Workspaces | The workspaces your credential reaches, and your role in each |
| Brand context | The workspace's own description of itself, for grounding a model |
| Creators | Your roster and their terms |
| Content | Submissions, their status, and a single creative in detail |
| Analysis | What the creative intelligence pass found on one version |
| Performance | Spend, delivery and revenue per creative |
| Royalties | What has accrued, and against which terms |
| Jobs | The state of a background job |
The one write is requesting an analysis.
What it does not do
Almost nothing writes. The single exception queues an analysis on a video you already own. You cannot approve a video, publish an ad, start a pay run or change terms through the API. Money and publishing stay in the app on purpose.
It is brand side only. Creator accounts are refused. The data model exists to serve creators too, and a creator-facing surface is a real future feature, but lowering the role floor on the endpoints that exist would hand a creator other creators' deals and the whole royalty ledger.
There are no webhooks yet. Hotline receives them from Shopify and Stripe; it does not send them.
The spec
The API describes itself, from the same deploy that serves it:
GET https://app.hotlineugc.com/api/v1/openapi.json
It is public and needs no credential. It names no workspace and carries no data, because it is the shape of the API rather than anything in it, and you need that before you have a key.
The spec is generated from the running code rather than kept as a file beside it, so it cannot drift from what is deployed.
Request shape
Every request carries a bearer token. See Authentication.
curl https://app.hotlineugc.com/api/v1/workspaces \
-H "Authorization: Bearer hl_live_..."
List endpoints take limit and offset, and answer with has_more so you know whether to ask again.
curl "https://app.hotlineugc.com/api/v1/workspaces/$WORKSPACE/content?limit=50&offset=0" \
-H "Authorization: Bearer hl_live_..."
Errors
Every failure returns the same JSON envelope, never an HTML error page.
{
"error": {
"code": "plan_required",
"message": "The Hotline API is on Growth and Agency.",
"request_id": "req_..."
}
}
Branch on code, which is stable. The message is for a human reading a log, and request_id is what to quote if you contact us. It is also returned on success, as the X-Request-Id header.
| Code | Status | Meaning |
|---|---|---|
invalid_request | 400 | The parameters did not validate |
unauthorized | 401 | No credential, or one that is not recognised |
plan_required | 402 | The credential is valid, the plan does not carry the API |
forbidden | 403 | Valid credential, insufficient access to this workspace |
not_found | 404 | No such record, or none your credential can see |
conflict | 409 | The request contradicts the current state |
rate_limited | 429 | More than 120 requests in a minute |
internal | 500 | Our fault. Quote the request_id |
402 rather than 403 for a plan problem is deliberate. It lets a consumer tell an upgrade from a permission problem without reading prose.
Rate limit
120 requests per minute. Exceeding it returns rate_limited rather than dropping the connection.