Browse API docs
Advertising
Advertising is currently in limited beta and is not available to every account. When access has been enabled for an account, a read token can inspect its Meta connection and campaign state; campaign changes require a Read + Write token and the same owner-confirmation rules as the dashboard. Requests from accounts without access return 403 Forbidden with an errors.base message. Personal co-organizer tokens cannot use these endpoints.
Meta bills the organizer’s own ad account; Usetix never funds ads. Starting, resuming and raising a budget spend money, so they take the preview_id of the exact terms you showed the person deciding: if the campaign changed since, Usetix answers 422. An account write token counts as the owner for these steps.
Starting, pausing, resuming and budget changes are queued. A 200 or 201 means Usetix accepted the work, not that Meta has finished it. Read the campaign again for status, budget, last_error_code and last_error_message.
| Method | URL | Purpose |
|---|---|---|
| GET | /admin/advertising |
The Meta connection and every campaign of the account |
| GET | /admin/events/:slug/promotions |
One event’s campaigns |
| GET | /admin/events/:slug/promotions/:id |
One campaign with its results |
| POST | /admin/events/:slug/promotions |
Propose a draft; 201 |
| PATCH | /admin/events/:slug/promotions/:id |
Change a draft |
| DELETE | /admin/events/:slug/promotions/:id |
Delete a campaign Meta never received, or one Meta holds paused; 204 |
| POST | /admin/events/:slug/promotions/:id/approval |
Start a draft (or retry a failed start) |
| PATCH | /admin/events/:slug/promotions/:id/budget |
Change a running campaign’s budget |
| POST | /admin/events/:slug/promotions/:id/pause |
Pause |
| DELETE | /admin/events/:slug/promotions/:id/pause |
Resume |
| POST | /admin/events/:slug/promotions/:id/synchronization |
Fetch status and results from Meta again |
| DELETE | /admin/events/:slug/promotions/:id/recommendation |
Hide the daily review’s suggestion |
:id is the campaign’s public ID, not a database number.
GET /admin/advertising
Returns the account’s Meta connection summary and a cursor-paginated list of campaigns, newest first.
curl -H "Authorization: Bearer your-token-here" \
-H "Accept: application/json" \
"https://app.usetix.io/admin/advertising?limit=50"
Query parameters:
| Parameter | Description |
|---|---|
limit |
Campaigns per page, from 1 to 100. Defaults to 50. |
page |
Opaque cursor from the previous response’s pagination.next_page value or Link header. |
Response:
{
"connection": {
"provider": "meta",
"status": "ready",
"configured": true,
"ad_account_name": "Usetix Ads",
"page_name": "Usetix Club",
"instagram_username": "usetixclub",
"conversion_tracking_enabled": true,
"organic_publishing_enabled": true
},
"advertising_campaigns": [
{
"id": "V3x7Kp9Q",
"event_slug": "summer-showcase",
"event_title": "Summer Showcase",
"name": "Summer Showcase · Meta",
"status": "active",
"objective": "sales",
"autonomy_mode": "manual",
"budget": { "amount": "20.00", "currency": "EUR" },
"budget_period": "daily",
"total_budget": { "amount": "240.00", "currency": "EUR" },
"spent": { "amount": "118.42", "currency": "EUR" },
"starts_at": "2026-08-20T10:00:00Z",
"ends_at": "2026-09-01T18:00:00Z",
"desired_active": true,
"recommendation": null,
"last_error_code": null,
"last_error_message": null,
"creatives": [],
"created_at": "2026-08-19T09:00:00Z",
"updated_at": "2026-08-29T12:00:00Z"
}
],
"pagination": {
"total_count": 12,
"limit": 50,
"next_page": null
}
}
connection is null until Meta has been connected. Connection status is one of pending, ready, degraded, expired, or revoked. Each campaign carries the campaign fields; the example leaves some out.
The response sends X-Total-Count with the total campaign count. When another page exists, the Link header contains the full next URL with rel="next"; clients should follow that URL without parsing or modifying the cursor. Invalid cursors and limits return 400 Bad Request with an error string.
GET /admin/events/:slug/promotions
Lists one event’s campaigns, newest first: { "event": { "id", "slug", "title" }, "advertising_campaigns": [ ... ] }, each with the campaign fields.
GET /admin/events/:slug/promotions/:id
Returns one campaign with the campaign fields and its results.
curl -H "Authorization: Bearer your-token-here" \
-H "Accept: application/json" \
https://app.usetix.io/admin/events/summer-showcase/promotions/V3x7Kp9Q
POST /admin/events/:slug/promotions
Proposes a draft: name, run, audience around the venue, placements and ad copy. Nothing is created at Meta and nothing is spent. The account needs a configured Meta connection; to start, the ad needs the event’s picture, a video or a Reel.
curl -X POST \
-H "Authorization: Bearer your-token-here" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"advertising_campaign": {"budget": "20.00", "budget_period": "daily", "days": 14, "audience_hint": "People looking for a Friday night out"}}' \
https://app.usetix.io/admin/events/summer-showcase/promotions
Body parameters:
| Field | Required | Notes |
|---|---|---|
budget |
yes | In the ad account’s currency, as a decimal string. An amount a day with budget_period daily, else the total for the whole run. At least 5.00. |
budget_period |
no | daily: Meta spends the amount a day on average and may spend up to 75% more on a single day, keeping each week within seven days’ worth. lifetime (the default): a hard total for the whole run. |
days |
no | Runs the campaign over the last this many days before the event, ending when ticket sales end or the event starts, whichever is first. Without it the run starts now. |
audience_hint |
no | A short description of the intended audience. Usetix starts near the venue and never infers sensitive traits. |
autonomy_mode |
no | manual (the default) or guarded. Guarded lets Usetix pause, resume and lower the budget on its own; it never raises the budget or extends the run. |
use_ai |
no | true queues an improvement of copy and audience. Read the campaign again until its creatives change. |
media_source |
no | event_image (the default), video with the file in video (MP4 or MOV, sent as multipart/form-data), or instagram_post with a Reel of the connected Instagram account in source_instagram_media_id. A Reel runs on Instagram’s placements only. |
The dashboard suggests an amount a day and a run from the ticket price, the tickets left and what a sold ticket cost in the account’s earlier campaigns.
Response: 201 Created with the campaign and its results, and a Location header. 422 when Meta is not connected, the event is too close to its start or sales end, or a field is invalid.
PATCH /admin/events/:slug/promotions/:id
Changes a draft. Once started, a campaign’s terms stay as they were approved; change a running campaign’s amount through its budget.
curl -X PATCH \
-H "Authorization: Bearer your-token-here" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"advertising_campaign": {"budget": "250.00", "budget_period": "lifetime"}}' \
https://app.usetix.io/admin/events/summer-showcase/promotions/V3x7Kp9Q
Body parameters (all optional): name, budget, budget_period, starts_at, ends_at, autonomy_mode, audience (age_min, age_max, summary, location.city, location.radius_km), placements, creatives_attributes (id, primary_text, headline, description, media_source, video, source_instagram_media_id).
Datetimes without an offset are read in the event’s time zone. creatives_attributes.id is the creative’s id from GET. A city is looked up at Meta; where several places share a name, the one near the venue’s postal code is taken, and the campaign shows Meta’s match before it starts.
Response: 200 OK with the campaign. 422 when the campaign is not a draft or a field is invalid.
DELETE /admin/events/:slug/promotions/:id
Deletes a campaign Meta never received (a draft, or one that failed or was paused before Meta created it) or one whose pause Meta confirmed. A paused one is deleted at Meta first, where Ads Manager keeps its figures; Usetix drops its results with it. Owners, managers and write tokens may delete.
curl -X DELETE \
-H "Authorization: Bearer your-token-here" \
-H "Accept: application/json" \
https://app.usetix.io/admin/events/summer-showcase/promotions/V3x7Kp9Q
Response: 204 No Content. 422 while the campaign runs at Meta, is on its way there, or Meta did not confirm the deletion; pause it first.
POST /admin/events/:slug/promotions/:id/approval
Starts the exact current draft and queues its publication to Meta. This lets Meta spend the budget.
curl -X POST \
-H "Authorization: Bearer your-token-here" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"preview_id": "0f1e2d3c4b5a69788796a5b4c3d2e1f0..."}' \
https://app.usetix.io/admin/events/summer-showcase/promotions/V3x7Kp9Q/approval
preview_id is the one from GET. When sent and the draft changed since, Usetix returns 422.
Response: 200 OK with the campaign. status becomes approved and then moves through publishing, in_review, scheduled and active as Meta reports back. The same endpoint retries a failed start. A manager’s session gets 403.
PATCH /admin/events/:slug/promotions/:id/budget
Changes the budget of a campaign Meta runs or holds paused. Meta keeps an ad set’s kind of budget, so only the amount changes: an amount a day stays an amount a day.
curl -X PATCH \
-H "Authorization: Bearer your-token-here" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"budget": "30.00"}' \
https://app.usetix.io/admin/events/summer-showcase/promotions/V3x7Kp9Q/budget
Body parameters:
| Field | Required | Notes |
|---|---|---|
budget |
yes | The new amount, a day or in total like the campaign’s budget_period. At least 5.00; a total must stay above what is already spent. |
preview_id |
for a higher amount | The preview_id Usetix answered for this amount. |
A lower amount goes ahead at once. A higher one is confirmed like a start: without preview_id, Usetix answers 422 with the ID for exactly this amount and the campaign as it is now:
{
"errors": { "base": ["Confirm the higher budget with its preview_id."] },
"preview_id": "9a8b7c6d5e4f30211203f4e5d6c7b8a9..."
}
Show the new amount, then send the same budget again with that preview_id. Raising needs the owner or an account write token; a manager’s session gets 422.
Response: 200 OK with the campaign. budget changes once Meta has taken the new amount; if Meta refuses, the old one stays and last_error_message says why. 422 when the campaign is not at Meta, has ended or failed, or the amount is not allowed.
POST /admin/events/:slug/promotions/:id/pause
Queues a pause. Owners, managers and write tokens may pause. desired_active turns false at once; delivery stops when Meta confirms.
Response: 200 OK with the campaign.
DELETE /admin/events/:slug/promotions/:id/pause
Resumes a paused campaign with its unchanged terms. Same owner and preview_id rules as a start.
Response: 200 OK with the campaign. 422 when the campaign is not paused, has ended, or the event no longer sells tickets.
POST /admin/events/:slug/promotions/:id/synchronization
Queues a fresh read of the campaign’s status and results from Meta.
Response: 200 OK with the last known campaign and results. Read it again shortly for the new figures.
Daily review
Every morning Usetix reviews each campaign running at Meta and stores a suggestion in recommendation: keep it, raise or lower the budget, or pause. Fixed rules weigh what the campaign spent against the tickets it sold and the ticket price; when the account has AI, the AI weighs the same figures within those rules and explains them in plain words. Nothing changes while Meta still learns (the first two days, or less than two days’ budget spent), and for three days after a budget change only a pause is suggested.
Raising always stays a suggestion for the owner: send it as a budget change. With autonomy_mode guarded, Usetix lowers the budget or pauses on its own where the rules call for it, and the suggestion’s status becomes autopilot.
{
"action": "lower_budget",
"status": "open",
"reason": "A sold ticket currently costs EUR 30,00 in ads, more than the ticket itself (EUR 20,00). With less budget the cost usually drops.",
"budget": { "amount": "12.00", "currency": "EUR" },
"reviewed_at": "2026-10-14T07:30:12Z"
}
| Field | Notes |
|---|---|
action |
keep, raise_budget, lower_budget or pause. |
status |
info (keep), open (a step to take), applied (someone took it), autopilot (guarded autopilot took it) or dismissed. |
reason |
Plain words in the account’s language. |
budget |
The suggested amount for raise_budget and lower_budget, a day or in total like the campaign; otherwise null. |
reviewed_at |
When the review ran, ISO 8601 UTC. |
recommendation is null until the first review. DELETE /admin/events/:slug/promotions/:id/recommendation hides the suggestion (status dismissed) until the review suggests something else, and returns the campaign.
Campaign fields
| Field | Type | Notes |
|---|---|---|
id |
string | Public ID for URLs. |
event_slug / event_title |
string | |
name |
string | |
status |
string | draft, approved, publishing, in_review, scheduled, active, paused, completed, rejected or failed. |
objective |
string | sales or traffic. |
autonomy_mode |
string | manual or guarded. |
budget |
money | The amount a day or the total, as budget_period says. |
budget_period |
string | daily or lifetime. |
total_budget |
money | What the run comes to: the total itself, or the amount a day on every day of the run. |
spent |
money | What Meta reports as spent. |
cost_per_click |
money or null | Spend divided by link clicks, as Ads Manager shows it. |
starts_at / ends_at |
string | ISO 8601 UTC. The run ends no later than ticket sales or the event start. |
audience |
object | Ages 18 to 65, a summary, and a location with country and city or coordinates and a radius of 1 to 80 km. |
placements |
string[] | From facebook_feed, facebook_stories, facebook_reels, instagram_feed, instagram_stories and instagram_reels. Without a connected Instagram account, Facebook’s only. |
beneficiary_name / payer_name |
string | Named for EU ad transparency. |
preview_id |
string | Identifies the current spend terms. Send it back to start or resume. |
desired_active |
boolean | Whether Usetix wants the campaign to deliver. |
recommendation |
object or null | The daily review’s suggestion. |
last_error_code / last_error_message |
string or null | The last refusal from Meta, in Meta’s own words where it gives them. |
creatives |
array | Each ad: id, name, primary_text, headline, description, destination_url, position, generated_by_ai, media_source, video_attached, instagram_media_id, instagram_permalink. |
created_at / updated_at |
string | ISO 8601 UTC. |
Results
A single campaign (GET, and the answers of the endpoints that change it) also includes:
| Field | Notes |
|---|---|
provider.spend |
What Meta reports as spent (money). |
provider.impressions / reach / clicks / landing_page_views / purchases |
Integers. |
provider.purchase_value |
Purchase value Meta attributes to the ads (money). |
provider.roas |
Return on ad spend, or null while nothing is spent. |
usetix.orders |
Orders whose first visit came through this campaign. |
usetix.revenue |
Net revenue of those orders (money). |
Meta’s attribution and Usetix’s first-visit orders can differ; treat them as two measurements.