Sources and campaigns

Two ways to keep producing over time: scan a feed when news breaks, or lay out a schedule by topic.

Which one to use

News sourceCampaign
Triggered byA new item on the feedA slot time in the schedule
Content fromThe scanned article itselfThe topic list you supply
Runs untilYou turn it offThe configured days run out, unless auto-renew is on

News sources

GET    /sources
GET    /sources/{id}
POST   /sources
PATCH  /sources/{id}
DELETE /sources/{id}

POST /sources needs an Idempotency-Key. A PATCH only changes fields that are present in the body; anything omitted is left alone.

FieldMeaning
feedUrlRSS address or an article listing page
enabledPause without deleting the configuration
autoConnectionIdsChannels that receive videos from this source. In responses the field is named channelIds.
maxItemsPerPollCap on VIDEOS per scan — not on articles attempted
maxItemsPerDayCap on videos per day
scanMode, scanTimes, scanDays, scanTimezoneScan schedule: fixed interval, or only at the listed times
includeKeywords, excludeKeywordsFilter articles before rendering
maxAgeHoursSkip articles older than this
stopWhenCreditBelowStop scanning when the balance drops under this — a safety catch worth setting
scheduleMode, scheduleDelayMinutes, immediateSpacingMinutesPublish now or later, and the spacing between posts
commentAuto-posted first comment. Send null to disable; omit to keep the current setting.

Responses carry nextScanAt, lastPolledAt and lastSuccessAt so you can tell a live source from a quiet one.

lastSuccessAt says nothing about whether videos were produced

A successful scan can yield zero videos: no new items, everything filtered out by keywords, or the source page changed shape so no image could be extracted. Read GET /sources/{id}/items to see what actually happened.

Scanned items

GET /sources/{id}/items?page=1&pageSize=25

Each entry is a discovered article with status, attempts and videoId — the video built from it, matching GET /videos/{id}. Items with no video yet have videoId: null.

Campaigns

GET  /campaigns?page=1&pageSize=25&status=ACTIVE
GET  /campaigns/{id}
POST /campaigns → 201

status accepts DRAFT, ACTIVE, PAUSED, COMPLETED or CANCELLED.

POST /campaigns creates a draft: no schedule laid out, nothing charged. Needs an idempotency header.

{
  "name": "Personal finance — October",
  "kind": "video-talk",
  "connectionIds": ["4e1c…"],
  "durationDays": 30,
  "startAt": "2026-10-01T00:00:00.000Z",
  "videosPerDay": 2,
  "timezone": "Asia/Ho_Chi_Minh",
  "timeWindows": ["08:00", "20:00"],
  "topics": ["Compound interest", "Emergency fund"],
  "autoRenew": false
}
  • timeWindows are times of day in timezone, not server time. The count should match videosPerDay.
  • topics are cycled through; the list wraps around when exhausted.
  • autoRenew restarts the campaign at the end of a cycle and keeps spending credits.

Campaign lifecycle

POST /campaigns/{id}/launch   → 202
POST /campaigns/{id}/pause    → 200
POST /campaigns/{id}/resume   → 200
POST /campaigns/{id}/cancel   → 200
CommandEffectReversible?
launchLays out slots for the whole run. From here each slot spends credits at its time.No — but you can pause right after
pauseStops rendering and publishingYes
resumeStarts again, and spends againYes
cancelStops for good. Slots that have not come due will never run.NO — cannot be reopened

All four re-read the campaign after the change, so you get the new state, not the old one. None require an idempotency header: calling twice equals calling once.

To stop temporarily, pause — do not cancel

cancel cannot be undone.

Slots

GET /campaigns/{id}/slots?page=1&pageSize=25

A slot is one scheduled render-and-publish: scheduledAt, topic, status, attempts, and videoId once rendered. Failed slots carry errorCode and errorDetail.

A missed slot is not posted late. The schedule simply moves to the next mark — dumping several posts at once on a real channel is the fastest way to get throttled by the platform.

For cost, read creditSpent (cumulative) and unitCost (per video) on the detail endpoint. See also Campaigns in the web app.

Quick health check

renderedVideoCount and failedSlotCount on the detail endpoint tell you whether a campaign is running smoothly — watch those two rather than counting slots by hand.