EnglishUser APICampaigns, screens & media

Campaigns, screens & media

Full campaign management (CRUD, deep link, pause/resume), tracker integration (flow and its statistics), triggered broadcasts, multi-screen welcome flows with buttons, CAS protection, group cleanup and media upload.

Every endpoint on this page requires an active subscription (see Authentication & conventions). On an expired subscription the response is unpaid; on invalid authentication — key. The response always comes back with an HTTP 200 code and an application/json body; success or failure is reported in the status field. The common rules live in Authentication & conventions.


List campaigns

GET /api/camp/list.json

Returns all non-deleted campaigns (the campaign status ≠ 2) of the current account, newest first. Each campaign is a full card with lifetime counters and (if a flow has been created) the resolved tracker flow URL.

{
  "status": "ok",
  "data": [
    {
      "id": 12,
      "code": "a1b2c3d4",
      "bot": 7,
      "bot_username": "mytrackbot",
      "chat": -1001234567890,
      "chat_type": 0,
      "chat_title": "My Channel",
      "chat_about": "Exclusive content for subscribers",
      "chat_link": "https://t.me/mychannel",
      "name": "Летняя кампания",
      "text": "Подпишитесь на *{title}* — эксклюзив внутри!",
      "media": "",
      "media_type": 0,
      "button": "Получить доступ",
      "link": "https://example.com/lander?click={click}",
      "mode": 0,
      "cas_ban": 0,
      "auto_accept": 0,
      "pb_wait": "",
      "pb_hold": "",
      "pb_approve": "",
      "pb_cancel": "",
      "pb_trash": "",
      "unsub_window": 2592000,
      "track_organic": 1,
      "notify_level": 1,
      "flow_id": 0,
      "flow_url": "",
      "status": 0,
      "created": 1746860400,
      "counters": {
        "wait": 120,
        "hold": 30,
        "approve": 98,
        "cancel": 8,
        "trash": 4,
        "org_join": 15,
        "org_leave": 3
      }
    }
  ]
}
FieldMeaning
code8-character campaign code, used in the deep link
chat_type0 channel, 1 group
chat_aboutChannel/group description pulled from Telegram (read-only)
chat_linkPublic chat link (t.me/<username> or invite), "" if unknown (read-only)
modeWelcome type: 0 classic single message, 1 multi-screen flow
cas_ban1 — kick joiners listed in CAS (see CAS protection)
auto_accept1 — automatically approve incoming join requests (see Create campaign)
track_organic1 — log organic joins/leaves, 0 — don’t
notify_levelLead notifications: 0 off, 1 approve, 2 approve + wait, 3 all statuses
flow_id / flow_urlTracker flow bound to the campaign (0/"" until a flow is created)
createdUnix creation time (integer seconds)
countersLifetime counters from the daily rollups; zeros until there is stats data
statusMeaning
0Active
1Paused
2Deleted (soft — leads and stats are kept; not shown in the list)
3Sleeping (suspended by the worker on tariff expiry; woken on renewal)

Get one campaign

GET /api/camp/get.json

Returns the card of a single campaign by id (same structure as in the list) with lifetime counters and the resolved flow URL.

ParameterRequiredDescription
idCampaign ID
{
  "status": "ok",
  "data": {
    "id": 12,
    "code": "a1b2c3d4",
    "bot": 7,
    "bot_username": "mytrackbot",
    "chat": -1001234567890,
    "chat_type": 0,
    "chat_title": "My Channel",
    "chat_about": "Exclusive content for subscribers",
    "chat_link": "https://t.me/mychannel",
    "name": "Летняя кампания",
    "text": "Подпишитесь на *{title}* — эксклюзив внутри!",
    "media": "",
    "media_type": 0,
    "button": "Получить доступ",
    "link": "https://example.com/lander?click={click}",
    "mode": 0,
    "cas_ban": 0,
    "auto_accept": 0,
    "pb_wait": "",
    "pb_hold": "",
    "pb_approve": "",
    "pb_cancel": "",
    "pb_trash": "",
    "unsub_window": 2592000,
    "track_organic": 1,
    "notify_level": 1,
    "flow_id": 0,
    "flow_url": "",
    "status": 0,
    "created": 1746860400,
    "counters": {
      "wait": 120,
      "hold": 30,
      "approve": 98,
      "cancel": 8,
      "trash": 4,
      "org_join": 15,
      "org_leave": 3
    }
  }
}

Empty pb_* fields mean the campaign inherits the global postback templates. unsub_window is in seconds; 0 means the window never closes (cancel/trash postbacks are always sent, regardless of the unsubscribe time).

ErrorMeaning
funcid not supplied or equal to 0
accessCampaign not found or belongs to another account

Create campaign

POST /api/camp/add.json

Creates a campaign on a bot and chat you own and that are not deleted. Your plan’s campaign limit applies (-1 = unlimited). The name and chat type are pulled from the bot’s admin-chats cache; an 8-character code is generated automatically.

bot and chat must be taken from bots/chats — this is an admin bot and a chat it belongs to.

ParameterRequiredDescription
botBot ID
chatChat ID (signed number, from bots/chats)
nameCampaign name
textWelcome message text (Telegram Markdown)
mediaMedia ref from media/upload; omit for a text message
media_typeMedia type: 1 photo, 2 video
buttonLabel of the CPA button in the welcome message
linkCPA button URL — may contain {click} and other macros (validated: https://, http://, tg://, t.me/, telegram.me/, @handle or empty)
pb_waitPostback URL template for the wait event
pb_holdPostback URL template for the hold event
pb_approvePostback URL template for the approve event
pb_cancelPostback URL template for the cancel event
pb_trashPostback URL template for the trash event
unsub_windowSeconds after the join during which cancel/trash postbacks are sent; 0 = always
track_organicLog organic joins/leaves. Enabled by default; turned off only by an explicit falsy value (0/false/no/off)
auto_acceptAutomatically approve incoming chat join requests. Disabled by default; turned on by an explicit truthy value (1/true/yes/on). Not tariff-gated
notify_levelLead notification level: 0 off, 1 approve, 2 approve + wait, 3 all statuses
{ "status": "ok", "id": 12, "code": "a1b2c3d4" }

On creation the chat’s title, description and public link are pulled from Telegram (non-critical — backfilled later if unavailable) and stored on the card (chat_title, chat_about, chat_link).

ErrorMeaning
funcbot or chat equal 0, or link failed validation
accessThe bot isn’t yours or is deleted
limitThe per-tariff campaign limit was reached — upgrade your tariff

Edit campaign

POST /api/camp/edit.json

Partial update: only the supplied fields change, the rest stay as they are. Each field, including each of the pb_*, is edited independently. The bot and chat cannot be changed after creation. Changing media resets the cached Telegram file_id (the file is re-uploaded on the next /start).

ParameterRequiredDescription
idCampaign ID
nameNew campaign name
textNew welcome message text
mediaNew media ref (must belong to you; pass "" to remove the current media)
media_typeNew media type: 1 photo, 2 video
buttonNew CPA button label
linkNew CPA button URL
pb_waitNew wait postback URL template
pb_holdNew hold postback URL template
pb_approveNew approve postback URL template
pb_cancelNew cancel postback URL template
pb_trashNew trash postback URL template
unsub_windowNew unsubscribe window in seconds
track_organic1/true/yes/on — log organic, otherwise don’t
auto_accept1/true/yes/on — auto-approve join requests, otherwise don’t
notify_levelNew lead notification level (0–3)
{ "status": "ok" }
ErrorMeaning
funcid equals 0, or link or your own media failed validation
accessThe campaign isn’t yours

GET /api/camp/link.json

Returns the subscriber deep link for this campaign and a tracker-connection flag. Distribute this link through your traffic sources — when a subscriber opens it, the bot greets them and the conversion is recorded.

ParameterRequiredDescription
idCampaign ID
{
  "status": "ok",
  "code": "a1b2c3d4",
  "url": "https://t.me/mytrackbot?start=a1b2c3d4-{click}",
  "tracker_connected": true
}

The link always contains the literal {click} macro — the advertiser’s tracker substitutes the real click ID at the moment of the redirect. tracker_connected indicates whether you have a tracker connected (whether a flow can be created via camp/flow).

ErrorMeaning
funcid equals 0
accessThe campaign isn’t yours

Pause or resume

POST /api/camp/pause.json

ParameterRequiredDescription
idCampaign ID
pause1 — pause, 0/absent — resume
{ "status": "ok", "camp_status": 1 }

camp_status is the campaign’s new status in the response: 1 paused, 0 active.

ErrorMeaning
funcid equals 0
accessThe campaign isn’t yours

Delete campaign

POST /api/camp/del.json

Soft-deletes the campaign (the campaign status = 2). Existing leads, journal records and stats are kept and continue to reference the campaign.

ParameterRequiredDescription
idCampaign ID
{ "status": "ok" }
ErrorMeaning
funcid equals 0
accessThe campaign isn’t yours

Switch welcome mode

POST /api/camp/mode.json

Switches the welcome flow between a classic single message (mode = 0) and a multi-screen flow (mode = 1). The first time screen mode is turned on, an entry screen is seeded from the classic creative (text/media + CTA button), so the switch loses no data and is immediately usable.

Turning on screen mode requires the Screens feature on your tariff (checked on the server); turning it off is always allowed — a user without the feature can return to the classic message.

ParameterRequiredDescription
idCampaign ID
mode1 — multi-screen flow, 0 — classic message (field must be present)
{ "status": "ok", "mode": 1 }
ErrorMeaning
funcid equals 0, or mode not supplied
accessThe campaign isn’t yours
screensThe screens feature isn’t enabled on the tariff (only when turning on) — upgrade your tariff

Create tracker flow

POST /api/camp/flow.json

Creates a flow in the connected AlterCPA Lite tracker for the campaign’s deep link, saves the flow ID and URL onto the campaign, and returns the URL. If a flow already exists, it returns the stored URL without contacting the tracker. Requires a connected tracker (see connecting a tracker).

ParameterRequiredDescription
idCampaign ID
{ "status": "ok", "url": "https://track.example.com/abcd1234" }

If the flow-create call to the tracker fails, the response stays status: ok but carries a soft error:

{ "status": "ok", "error": "create" }
ErrorMeaning
funcid equals 0
accessThe campaign isn’t yours, or no tracker is connected
createThe tracker couldn’t create the flow (soft error, status = ok)

Re-sync tracker flow

POST /api/camp/flowsync.json

Restores a campaign’s link to its tracker flow if the flow may have been deleted on the tracker side. It probes the saved flow: if it’s still there, the URL is refreshed and recreated: false is returned; if the tracker reports the flow is gone, it is recreated from the campaign’s deep link, the new ID and URL are saved, and recreated: true is returned. If the tracker is unreachable, the flow is not recreated (to avoid duplicates). Use this call to recover a flow deleted on the tracker side.

ParameterRequiredDescription
idCampaign ID
{ "status": "ok", "url": "https://track.example.com/abcd1234", "recreated": false }

Soft errors come back with status: ok and an error field:

{ "status": "ok", "error": "sync" }
ErrorMeaning
funcid equals 0
accessThe campaign isn’t yours, or no tracker is connected
noflowThe campaign has no flow yet — create one via camp/flow first (soft error, status = ok)
syncThe tracker is unreachable, recreation skipped (soft error, status = ok)
createThe flow was deleted on the tracker but recreation failed (soft error, status = ok)
busyToo many requests (limit 30 per minute)

Flow statistics

GET /api/camp/flowstats.json

Returns daily statistics from the connected tracker for the campaign’s flow over the last N days.

ParameterRequiredDescription
idCampaign ID
daysNumber of days; default 7, max 90
{
  "status": "ok",
  "rows": [
    {
      "date": "2026-06-20",
      "clicks": 540,
      "unique": 512,
      "wait": 120,
      "hold": 30,
      "approve": 98,
      "cancel": 8,
      "trash": 4,
      "cr": 19.14,
      "ar": 81.67,
      "exit_pct": 10.91
    }
  ]
}

If the campaign has no flow yet, rows is empty ([]). If the request to the tracker fails, the response stays status: ok with a soft error {"status":"ok","error":"stats"}.

FieldMeaning
clicks / uniqueTotal clicks / unique clicks
crCR%: valid leads / unique clicks (computed by the tracker)
arAR%: approved / valid leads (computed by the tracker)
exit_pct(cancel + trash) / (approve + cancel + trash), %
ErrorMeaning
funcid equals 0
accessThe campaign isn’t yours, or no tracker is connected
statsThe tracker couldn’t return statistics (soft error, status = ok)

Triggered broadcasts: get

GET /api/camp/broadcast.json

Returns the campaign’s five triggered auto-message blocks — one per lead status (0 wait, 1 hold, 2 approve, 3 cancel, 4 trash). A status with no saved block is returned as the zero block (on = 0). These are the triggered per-status broadcasts; bulk manual broadcasts are described on the Broadcasts page.

ParameterRequiredDescription
idCampaign ID
{
  "status": "ok",
  "data": [
    {
      "status": 0,
      "on": 1,
      "delay": 3600,
      "text": "Ещё думаете? Доступ закрывается!",
      "media": "",
      "media_type": 0,
      "button": "Вступить",
      "link": "https://t.me/mytrackbot?start=a1b2c3d4-{click}"
    }
  ]
}
ErrorMeaning
funcid equals 0
accessThe campaign isn’t yours

Triggered broadcasts: save

POST /api/camp/broadcast.json

Saves (upserts) one broadcast block by status. Enabling (on = 1) requires the Broadcasts feature on your tariff; disabling and editing a disabled block are always allowed. Changing media resets the cached file_id.

ParameterRequiredDescription
idCampaign ID
statusLead status 04 (>4func)
on1 — enable the broadcast (requires the broadcasts feature), otherwise disable
delaySeconds after entering the status before sending
textMessage text
mediaMedia ref from media/upload (must belong to you)
media_typeMedia type: 1 photo, 2 video
buttonCTA button label
linkCTA button URL (validated as for the campaign)
{ "status": "ok" }
ErrorMeaning
funcid equals 0, status > 4, or link/media failed validation
accessThe campaign isn’t yours
broadcastsThe broadcasts feature isn’t enabled on the tariff (only when enabling) — upgrade your tariff

Test broadcast

POST /api/camp/btest.json

Enqueues a “send to myself” — a preview of the broadcast block (text + CTA only) to the owner via the service bot. The block must be saved beforehand.

ParameterRequiredDescription
idCampaign ID
statusBlock status 04
{ "status": "ok" }
ErrorMeaning
funcid equals 0 or status > 4
accessThe campaign isn’t yours

Welcome screens: list

GET /api/screen/list.json

Returns the campaign’s screens (entry screen first; sorted by scr_order, scr_id), each with its ordered buttons. Owner access only; not restricted by the screens feature (but requires an active subscription).

The multi-screen flow is used by the campaign when the campaign mode mode = 1 (see welcome mode). A screen is media + text + an ordered list of inline buttons that the bot lays out in rows of columns width.

ParameterRequiredDescription
campCampaign ID
{
  "status": "ok",
  "data": [
    {
      "id": 30,
      "campaign": 12,
      "order": 0,
      "name": "Старт",
      "text": "Привет! Готовы вступить?",
      "media": "",
      "media_type": 0,
      "columns": 1,
      "created": 1746860400,
      "buttons": [
        { "id": 51, "order": 0, "label": "Подробнее", "kind": 1, "link": "", "target": 31 },
        { "id": 52, "order": 1, "label": "Открыть сайт", "kind": 0, "link": "https://example.com/lander", "target": 0 }
      ]
    }
  ]
}
Button fieldMeaning
kind0 URL button (link set), 1 navigation to another screen (target set)
targetscr_id of the destination screen in the same campaign (for kind = 1)
ErrorMeaning
funccamp equals 0
accessThe campaign isn’t yours

Add screen

POST /api/screen/add.json

Adds a screen to the campaign (limit screens_max, default 10). The first screen gets order 0 (entry). Returns the new screen card.

ParameterRequiredDescription
campCampaign ID
nameScreen name
textScreen text
mediaMedia ref from media/upload (must belong to you; empty is allowed)
media_typeMedia type: 1 photo, 2 video
columnsButtons per row, clamped to 1–8 (default 1)
{
  "status": "ok",
  "data": {
    "id": 32,
    "campaign": 12,
    "order": 2,
    "name": "Бонус",
    "text": "Дарим бонус новым подписчикам",
    "media": "",
    "media_type": 0,
    "columns": 1,
    "buttons": [],
    "created": 0
  }
}
ErrorMeaning
funccamp equals 0 or media failed validation
accessThe campaign isn’t yours
screensThe screens feature isn’t enabled on the tariff — upgrade your tariff
limitThe screen limit (screens_max) was reached

Edit screen

POST /api/screen/edit.json

Partial update of the screen’s content: only the supplied fields change. Changing media resets the cached file_id. Requires the screens feature on the tariff.

ParameterRequiredDescription
idScreen ID
nameNew screen name
textNew screen text
mediaNew media ref (must belong to you)
media_typeNew media type: 1 photo, 2 video
columnsButtons per row, clamped to 1–8
{ "status": "ok" }
ErrorMeaning
funcid equals 0 or media failed validation
accessThe screen isn’t yours
screensThe screens feature isn’t enabled on the tariff

Copy screen

POST /api/screen/copy.json

Duplicates a screen (text + media + buttons) and appends the copy to the same campaign (limit screens_max). Navigation buttons keep their target. Requires the screens feature.

ParameterRequiredDescription
idSource screen ID
{
  "status": "ok",
  "data": {
    "id": 33,
    "campaign": 12,
    "order": 3,
    "name": "Старт",
    "text": "Привет! Готовы вступить?",
    "media": "",
    "media_type": 0,
    "columns": 1,
    "buttons": [
      { "id": 60, "order": 0, "label": "Открыть сайт", "kind": 0, "link": "https://example.com/lander", "target": 0 }
    ],
    "created": 1750000000
  }
}

If the copy can’t be re-read from the database, the response is a fallback {"status":"ok","id":33} (only id, no data).

ErrorMeaning
funcid equals 0
accessThe screen isn’t yours
screensThe screens feature isn’t enabled on the tariff
limitThe screen limit (screens_max) was reached

Delete screen

POST /api/screen/del.json

Deletes a screen and its buttons. Refuses if it’s the campaign’s only screen or if a navigation button still points to it. Requires the screens feature.

ParameterRequiredDescription
idScreen ID
{ "status": "ok" }
ErrorMeaning
funcid equals 0
accessThe screen isn’t yours
screensThe screens feature isn’t enabled on the tariff
lastThis is the campaign’s only screen — it can’t be deleted
linkedA navigation button still points to this screen — redirect it first

Reorder screens

POST /api/screen/reorder.json

Reassigns the screen order from a list of their IDs. The screen at position 0 becomes the entry screen. IDs that don’t belong to the campaign are silently skipped. Requires the screens feature.

ParameterRequiredDescription
campCampaign ID
orderComma-separated screen IDs in the desired order, e.g. 5,3,7
{ "status": "ok" }
ErrorMeaning
funccamp equals 0 or the order list is empty
accessThe campaign isn’t yours
screensThe screens feature isn’t enabled on the tariff

Test screen

POST /api/screen/test.json

Sends a “send to myself” preview of the screen (text + buttons only; media is omitted) to the owner via the service bot. Owner access only; not restricted by the screens feature.

ParameterRequiredDescription
idScreen ID
{ "status": "ok" }
ErrorMeaning
funcid equals 0
accessThe screen isn’t yours

Add button

POST /api/button/add.json

Adds a button to a screen (limit screen_buttons_max, default 8). kind, link and target are validated together. Requires the screens feature. Returns the new button card.

ParameterRequiredDescription
screenScreen ID
labelButton label
kind0 URL button (default), 1 navigation to another screen
linkRequired when kind = 0: a valid URL (https/http/tg/t.me/telegram.me/@handle); forcibly cleared when kind = 1
targetRequired when kind = 1: scr_id of another screen in the same campaign (not this screen); forcibly 0 when kind = 0
{
  "status": "ok",
  "data": { "id": 61, "order": 1, "label": "Подробнее", "kind": 1, "link": "", "target": 31 }
}
ErrorMeaning
funcscreen equals 0 or an invalid kind/link/target combination
accessThe screen isn’t yours
screensThe screens feature isn’t enabled on the tariff
limitThe button limit (screen_buttons_max) was reached

Edit button

POST /api/button/edit.json

Fully replaces the button’s label/kind/link/target (not a partial update — all fields are rewritten as a unit). kind/link/target are validated together, as on add. Requires the screens feature.

ParameterRequiredDescription
idButton ID
labelNew button label
kind0 URL button, 1 navigation
linkRequired when kind = 0 (a valid URL); cleared when kind = 1
targetRequired when kind = 1 (another screen in the same campaign); when kind = 0 → 0
{ "status": "ok" }
ErrorMeaning
funcid equals 0 or an invalid kind/link/target combination
accessThe button isn’t yours
screensThe screens feature isn’t enabled on the tariff

Delete button

POST /api/button/del.json

Deletes a button. Requires the screens feature.

ParameterRequiredDescription
idButton ID
{ "status": "ok" }
ErrorMeaning
funcid equals 0
accessThe button isn’t yours
screensThe screens feature isn’t enabled on the tariff

Reorder buttons

POST /api/button/reorder.json

Reassigns the order of one screen’s buttons from a list of their IDs. IDs that don’t belong to the screen are silently skipped. Requires the screens feature.

ParameterRequiredDescription
screenScreen ID
orderComma-separated button IDs in the desired order
{ "status": "ok" }
ErrorMeaning
funcscreen equals 0 or the order list is empty
accessThe screen isn’t yours
screensThe screens feature isn’t enabled on the tariff

CAS protection

POST /api/camp/cas.json

Turns the campaign’s CAS protection on or off — auto-kicking joiners listed in the CAS ban list. Turning it on requires the Screens feature on your tariff (the same feature as for screens); turning it off is always allowed.

ParameterRequiredDescription
idCampaign ID
cas_ban1 — enable protection, 0 — disable (field must be present)
{ "status": "ok", "cas_ban": 1 }
ErrorMeaning
funcid equals 0 or cas_ban not supplied
accessThe campaign isn’t yours
screensThe screens feature isn’t enabled on the tariff (only when enabling) — upgrade your tariff

Start group cleanup

POST /api/camp/clean.json

Starts a group cleanup: it gathers the observed member base (joined leads still in the chat ∪ members from the journal marked as present) into a queue and arms a job that our backend drains. Only one cleanup per campaign can run at a time. Requires the Screens feature on your tariff.

ParameterRequiredDescription
idCampaign ID
{ "status": "ok", "clean_id": 5, "total": 842, "state": 0 }

If there is nobody to check, the job finishes immediately: {"status":"ok","clean_id":5,"total":0,"state":1}.

stateMeaning
0Running
1Finished
ErrorMeaning
screensThe screens feature isn’t enabled on the tariff — upgrade your tariff
funcid equals 0
accessThe campaign isn’t yours
runningA cleanup of this campaign is already running

Cleanup status

GET /api/camp/clean.json

Returns the campaign’s latest cleanup job for progress polling. Reading the status is not restricted by the screens feature (unlike starting a cleanup).

ParameterRequiredDescription
idCampaign ID

If a cleanup has never been run:

{ "status": "ok", "exists": false }

If a job exists:

{
  "status": "ok",
  "exists": true,
  "state": 0,
  "total": 842,
  "checked": 310,
  "kicked_cas": 4,
  "kicked_dead": 12,
  "failed": 1,
  "started": 1750000000,
  "finished": 0
}
FieldMeaning
state0 running, 1 finished
total / checkedTotal members in the base / already checked
kicked_casKicked per the CAS list
kicked_deadKicked as deleted/inactive accounts
failedCouldn’t be checked
started / finishedUnix start and finish time (0 for an unfinished job)
ErrorMeaning
funcid equals 0
accessThe campaign isn’t yours

Upload media

POST /api/media/upload.json

Uploads an image or video for a campaign welcome message, a screen, a broadcast or a mailing. Returns the media ref and type code to save via camp/add, camp/edit, screen/* and so on.

Send it as multipart/form-data, with the file in the file field. Accepted formats: jpg, jpeg, png, webp, gif (photo) and mp4, mov, webm (video); documents are not accepted. The size is limited (default ~20 MiB). The limit is 20 uploads per hour per account.

{ "status": "ok", "media": "17/abc123.jpg", "media_type": 1 }

media is the path relative to the media directory (this is what you save onto the campaign/screen). media_type is the type code: 1 photo, 2 video; pass it together with media.

ErrorMeaning
funcNo file supplied (no file part) or a save error
typeInvalid file format (not in the photo/video list)
sizeThe file exceeds the size limit
busyThe limit of 20 uploads per hour was exceeded