curl https://viral.app/api/v1/briefs/orgbrief_AbCdEf123456 \
-H "x-api-key: $VIRAL_APP_API_KEY"
Briefs via the API
Write creator briefs in markdown or TipTap JSON, attach sample videos from your tracked accounts or the viral video library, choose who reads them, and see who has, from your own tools or an agent.
A brief is what creators read before they make content: the instructions, and sample videos that show what you want. It reaches the creators on the campaigns it is linked to, creators you send it to directly, or everyone in your workspace. The briefs API covers the same briefs as the Briefs tab. You write the content as markdown or as the editor's TipTap JSON, and every read returns both, so a script, a CMS or an AI agent can work in whichever format it handles.
The API reference lists every field under Briefs, and API and automation covers keys and access. Campaigns themselves have their own guide: Campaigns via the API.
Before you start
- Base URL and auth: every route below lives under
https://viral.app/api/v1and takes thex-api-keyheader. Bodies and responses are JSON. - Who can do what: creating, editing, deleting and linking briefs needs a key of an owner, admin or member. Reads also work for a viewer's key. Writes land in the workspace's activity log under the key's owner.
- Ids carry their type:
orgbrief_briefs,orgcamp_campaigns,orgcre_creators,orgacc_tracked accounts,orgrsv_viral video library videos. - Names are unique per workspace. They are internal: creators never see a brief's name.
Read briefs
GET /api/v1/briefs lists briefs with a plain-text excerpt, their audience, readCounts, the number of sample videos and notifiableCreatorCount. Filter by search (the name) or campaignIds, and page with page and perPage.
GET /api/v1/briefs/{id} returns the whole brief:
Write the content
Send the content as contentMarkdown or as content, never both. Markdown is the simpler choice:
## The assignment
Hi $creator_first_name, show how **Acme** gets you through exam week.
## Must include
- The planner screen in the first 3 seconds
- A link to [acme.com](https://acme.com)
- Supported:
##and###headings, paragraphs,**bold**,*italic*, links, images,-and1.lists. Code, quotes, rules and tables become plain paragraphs, and deeper headings become###. - Links must be absolute
https,httpormailtoURLs. Images need anhttpsURL. - Variables fill in per creator when they read the brief:
$creator_first_name,$creator_full_name,$creator_email,$creator_accounts,$brand_name,$campaign_video_target,$campaign_payout_cycleand$assignment_start_date. Any other$nameis refused as a typo, so creators never read a raw placeholder. Prices like$50are fine.
With content, send the editor's TipTap JSON: doc, paragraph, heading (levels 2 and 3), bulletList, orderedList, listItem, text, hardBreak, imageResize (images; TipTap's stock image is accepted and renamed) and variable nodes ({ "type": "variable", "attrs": { "id": "creator_first_name" } }), and the bold, italic, strike, underline and link marks. Headings of other levels become level 2 or 3. The easiest way to learn the shape is to read a brief you wrote in the dashboard.
Content the editor could not have produced is refused with 400 CONTENT_INVALID. data.issues lists every problem at once, each with a path and a message:
{
"code": "CONTENT_INVALID",
"status": 400,
"message": "The rich-text content is invalid.",
"data": {
"issues": [{ "path": "contentMarkdown", "message": "Unknown variable $creator_frist_name" }]
}
}
A brief also needs readable text: sample videos alone are not a brief.
Create a brief
POST /api/v1/briefs creates version 1 and returns its id and version:
curl -X POST https://viral.app/api/v1/briefs \
-H "x-api-key: $VIRAL_APP_API_KEY" \
-H "content-type: application/json" \
-d '{
"name": "Acme — exam week demo",
"contentMarkdown": "## The assignment\n\nHi $creator_first_name, show how Acme gets you through exam week.",
"campaignIds": ["orgcamp_RwmraAffhMt9"],
"notify": false
}'
Give it an audience right away with campaignIds, creatorIds or allCreators (see Set the audience), or leave it out: a brief nobody reads is a draft. With notify: true, every creator it reaches is told about the new brief in the app and by email.
Edit a brief
PUT /api/v1/briefs/{id} changes only what you send: name, the content, sampleVideos (the whole list), and the audience (campaignIds, creatorIds, allCreators). Leave a field out to keep it, so renaming a brief never touches its samples or audience.
notify: falsesaves silently. The text changes in place and the version stays.notify: truepublishes the change: the version goes up, every creator in the brief's audience is notified and has to read it again. Sent with nothing else, it re-publishes the brief as it is, for a brief you created silently.
notifiableCreatorCount on GET /briefs/{id} tells you beforehand whether a notify would reach anyone.
Sample videos
Sample videos show creators what you want. Add them to the end of the list with POST /api/v1/briefs/{id}/samples:
curl -X POST https://viral.app/api/v1/briefs/orgbrief_AbCdEf123456/samples \
-H "x-api-key: $VIRAL_APP_API_KEY" \
-H "content-type: application/json" \
-d '{
"videos": [
{ "libraryVideoId": "orgrsv_AbCdEf123456", "label": "Money-glitch hook" },
{ "platform": "tiktok", "platformVideoId": "7412345678901234567" }
],
"notify": false
}'
- Your tracked videos go by
platformandplatformVideoId. For a video tracked through an account rather than on its own, add the account asorgAccountId(orplatformAccountId). - Viral video library videos go by
libraryVideoId, theorgrsv_id from the library endpoints. viral.app tracks each one as a reference-only sample: it counts against your tracked-videos limit, stays out of your analytics and payouts, and is untracked again when no brief uses it anymore. The Basic plan cannot track individual videos and gets402. labelis the short caption creators read above the video, after its number ("2. Money-glitch hook"). Up to 60 characters.- Videos the brief already lists are skipped and counted in
skippedCount. One unknown video refuses the whole call and nothing is added.
To reorder, relabel or remove samples, send the complete list back as sampleVideos on PUT /briefs/{id}: take sampleVideos from GET /briefs/{id}, change it, and send it. Order is the array order, and a video you leave out is removed. Sent on POST /briefs, the same list sets the first samples; library videos are added afterwards through /samples.
Set the audience
A brief reaches creators three ways, and every creator it reaches has to read it on their next visit:
- Campaigns: the creators assigned to the campaigns it is linked to. A campaign holds one brief, and one brief can serve several campaigns.
- Creators: creators you send it to directly (
orgcre_ids fromGET /creators), with or without a campaign. Creators who have not joined viral.app yet read it once they do. - Everyone: every creator in your workspace, including creators added later. Such a brief has no campaigns or creators of its own.
PUT /api/v1/briefs/{id}/audience sets any of the three; parts you leave out stay as they are:
curl -X PUT https://viral.app/api/v1/briefs/orgbrief_AbCdEf123456/audience \
-H "x-api-key: $VIRAL_APP_API_KEY" \
-H "content-type: application/json" \
-d '{ "allCreators": true, "campaignIds": [], "creatorIds": [], "notify": false }'
campaignIdsis the full list of campaigns the brief should be on: campaigns left out are unlinked, and a campaign that had another brief switches to this one.creatorIdsis the full list of creators it is sent to directly (up to 5,000).allCreators: trueshares it with everyone. If the brief still has campaigns or creators, sendcampaignIds: []andcreatorIds: []in the same call to clear them, or you get409 BRIEF_AUDIENCE_CONFLICT. Nothing is cleared without you asking.- With
notify: true, the creators the change newly reaches are told in the app and by email. A creator who already had the brief another way (a campaign, a direct send) is not told again. Removals are always silent, and the version doesn't change.
To send a brief to a few more creators without listing everyone, use PATCH /api/v1/briefs/{id}/creators with add and remove lists of up to 500 each (idempotent; notify again only reaches the newly added). A creator who already gets the brief through a campaign keeps it once that campaign ends, and is not notified twice. A brief is sent directly to at most 5,000 creators (400 TOO_MANY_CREATORS); share it with everyone instead.
From the campaign side, briefId on POST /campaigns and PUT /campaigns/{id} links a brief, with notifyCreatorsOfBriefChange on the edit (see Campaigns via the API). A brief shared with everyone can't be a campaign's brief (409 BRIEF_AUDIENCE_CONFLICT). PUT /api/v1/briefs/{id}/campaigns still sets the campaigns alone.
Campaigns that have ended cannot be linked (409 CAMPAIGN_ENDED). Unlinking never notifies anyone.
Who has read a brief
GET /api/v1/briefs/{id}/read-status lists the creators in the brief's audience: how it reaches each one (routes: a campaign, direct or everyone), their state (read the current version, read_earlier, or unread), readVersion, readAt and notifiedAt. counts covers everyone, including creators who have not joined yet (notJoined, counted but not listed). Filter with filter=unread or search, and page with page and perPage (up to 500); pageCount and totalRows count the listed creators.
Delete a brief
DELETE /api/v1/briefs/{id} removes the brief, its campaign and creator links and read receipts, and untracks the sample videos only this brief used. Nobody is notified.
Webhooks for briefs
Subscribe to brief.published (a brief reached its creators: created or updated with a notification, or newly reaching creators through a campaign, a direct send or everyone) and brief.read (a creator read a version for the first time) in the Webhooks tab. See Webhooks.
Jobs use the same format
Job postings take their rich text the same way: bodyMarkdown or body, and challengeBriefMarkdown or challengeBrief, on POST /jobs and PUT /jobs/{id}. Every job response carries both formats.
Errors at a glance
Campaigns API
Create and edit creator campaigns from your own tools or an agent, preview every payout rule as a sentence, and assign creators.
Payouts API
Read the payout queues, pay a billing window when it is due or early while it runs, pay specific videos, and create manual payouts from your own tools or an agent.