oqimDocs
API referenceConsole

Start here

  • Introduction
  • Getting started

Guides

  • Telegram accounts
  • Recipients
  • Campaigns
  • Scheduling
  • Webhooks
  • AI writing assistant

AI Seller

  • AI Seller
  • Channels
  • Live inbox
  • Past conversations and privacy
  • Business insights

API

  • Authentication
  • API reference
  • SDK
  • Errors
  • Rate limits

Trust and operations

  • Security
  • Compliance
  • Administration
Docs / Scheduling

Start here

  • Introduction
  • Getting started

Guides

  • Telegram accounts
  • Recipients
  • Campaigns
  • Scheduling
  • Webhooks
  • AI writing assistant

AI Seller

  • AI Seller
  • Channels
  • Live inbox
  • Past conversations and privacy
  • Business insights

API

  • Authentication
  • API reference
  • SDK
  • Errors
  • Rate limits

Trust and operations

  • Security
  • Compliance
  • Administration

Scheduling

A campaign sends only inside its schedule: after its start, before its end, during its daily window and on its chosen weekdays, all in the campaign's own time zone. It can also repeat on a rule.

Schedule fields#

FieldDescription
timezoneAn IANA time zone name such as Asia/Tashkent. The console defaults to the organization's time zone.
start_atOptional instant (RFC 3339). null means as soon as it's launched.
end_atOptional instant. At this time the campaign completes and anyone not yet sent to is skipped. Must be later than start_at and in the future at launch.
window_start, window_endMinutes after local midnight, from 0 to 1440: 600 is 10:00. A window whose end is earlier than its start crosses midnight. 0 to 1440 means all day.
window_daysISO weekdays the window opens on: 1 is Monday, 7 is Sunday.
Weekdays 10:00 to 20:00 in Tashkent, finishing by Friday evening
curl -X PATCH https://app.example.com/api/v1/campaigns/cmp_01j9r4c6e8g0j2m4p6r8t0w2y4 \
  -H "Authorization: Bearer $OQIM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "timezone": "Asia/Tashkent",
    "start_at": "2026-09-28T05:00:00Z",
    "end_at": "2026-10-02T15:00:00Z",
    "window_start": 600,
    "window_end": 1200,
    "window_days": [1, 2, 3, 4, 5]
  }'

Times in the window are wall-clock times in the campaign's time zone, so a 10:00 window stays 10:00 across daylight-saving changes. start_at and end_at are absolute instants.

Daily windows#

00:0006:0012:0018:0024:00

540–1080: office hours, 09:00–18:00

00:0006:0012:0018:0024:00

1320–120: evenings, 22:00–02:00 the next day

A window that crosses midnight belongs to the day it opens. With window_days of [5] and 22:00–02:00, sending runs from Friday 22:00 to Saturday 02:00, and not on Saturday night.

Goalwindow_startwindow_endwindow_days
Office hours on weekdays5401080[1, 2, 3, 4, 5]
Any time, every day01440[1, 2, 3, 4, 5, 6, 7]
Weekend mornings480720[6, 7]
Friday and Saturday nights, past midnight1320120[5, 6]

Validation fails when the window is empty (start equals end), when no days are set, or when the window never opens between the start and the end time.

Repeating campaigns#

Set recurrence on a draft to send it again on a rule: every few days, on chosen weekdays, or on a day of the month. Each send is a run: a campaign of its own, with its own audience, counters and delivery log, linked to the first run by series_id and numbered by occurrence.

FieldDescription
frequencyDAILY, WEEKLY or MONTHLY.
intervalEvery N days (1 to 60), weeks or months (1 to 12). Defaults to 1.
weekdaysWEEKLY only: the days to run on, MON to SUN. Each must be one of the campaign's sending days.
month_dayMONTHLY only: 1 to 31. Months without that day run on their last day, so 31 means the last day of every month.
ends{"type": "NEVER"}; AFTER with count, 2 to 365 runs including the first; or ON with until, the last date a run may start in the campaign's time zone, at most two years ahead.
Every Monday and Thursday at 10:00 in Tashkent, eight runs
curl -X POST https://app.example.com/api/v1/campaigns \
  -H "Authorization: Bearer $OQIM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Weekly digest",
    "message_text": "Hi {{first_name|there}}! This week: {{highlights}}. Stop: {{opt_out_url}}",
    "audience": {"list_ids": ["lst_01j9r5e2g4j6m8p0r2t4w6y8a0"]},
    "account_ids": ["acc_01j9r3a8f2k5m7p9s1t3v5x7z9"],
    "timezone": "Asia/Tashkent",
    "start_at": "2026-10-05T05:00:00Z",
    "window_start": 600,
    "window_end": 1080,
    "window_days": [1, 2, 3, 4, 5],
    "recurrence": {
      "frequency": "WEEKLY",
      "weekdays": ["MON", "THU"],
      "ends": {"type": "AFTER", "count": 8}
    }
  }'

Rule errors come back per field, like recurrence.weekdays. The rule can change only while the campaign is a draft.

When runs happen

  • You launch the first run, and it needs a start_at. Every later run starts at the first run's time of day, on the dates the rule allows, in the campaign's time zone. A 10:00 run stays at 10:00 across daylight-saving changes; a time the clocks skip, such as 02:30 on the night they jump forward, moves to 03:30, and a time that happens twice is the first one.
  • The interval counts from the first run: every 2 weeks means the first run's week, then every other week. A Monday and Thursday rule whose first run is on a Tuesday runs next on that Thursday.
  • DAILY runs only on the campaign's sending days, so daily with Monday to Friday sends on weekdays. A monthly run whose date isn't a sending day starts on it and waits for the window to open.
  • Runs never overlap. The next run is placed when the current one finishes, on the first date after that; dates missed while a run was still sending are skipped, not made up.
  • A run lasts as long, on the wall clock, as the one before it: 10:00 to 18:00 is followed by 10:00 to 18:00.

How each run is added and launched

  • When a run completes, Oqim adds the next one as a draft, named like Weekly digest · run 3, with the same message, attachment, audience definition (lists, tags and chosen recipients), accounts, window, time zone and rule, and notifies the organization.
  • At auto_launch_at, 15 minutes before the run starts, Oqim launches it with the same checks as POST /campaigns/{id}/start: organization active, acceptable-use policy accepted, room in the plan, healthy accounts, consent and a valid schedule. The audience is resolved then, so people added to a list since the last run get it and people who opted out don't.
  • If a check fails, the run stays a draft, its automatic launch is called off, and the organization is notified with the reasons. Fix the cause and launch it yourself; the series goes on from there.
  • Until it launches, the next run is an ordinary draft. Edits apply to it and carry over to the runs after it. A new start or end time applies to this run only; its automatic launch moves with its start, and later runs keep the rule's times.

Stopping a series

  • POST /campaigns/{id}/stop-repeating on any run stops the series; a run in progress finishes normally. The next run waiting to launch is deleted if nobody edited it, and kept as an ordinary draft if someone did. The run you call it on is always kept.
  • A series also ends after its last run (AFTER), when no date is left before until, when a run is cancelled or fails, or when the next run is deleted while it's a draft.
  • Each of these endings sends a webhook event, campaign.repeat_stopped, with the reason. Setting recurrence to null on the next run instead makes that run the last one.

A run keeps recurrence while it still leads to another run; once its successor is added, its own copy is cleared. next_run_at projects when the run after a campaign would start. GET /campaigns/{id} adds a series summary: runs launched so far, the planned total, whether it still repeats, the rule, the next run and the runs before and after this one.

Note

Repeating never widens what a campaign may do. Every run passes the full pre-launch checks, opted-out and suppressed people are never included, and a restricted or signed-out account stops the next launch until someone fixes it.

Pausing and resuming#

  • Pausing stops dispatch immediately; a message a worker has already picked up may still go out. Running and scheduled campaigns can be paused.
  • While paused or scheduled you can change the end time, the window, the days and the accounts. The message and the audience are fixed at launch.
  • Resuming outside the window is fine: the campaign waits for the window to open. A campaign that never started goes back to SCHEDULED.
  • A pause by Oqim, after a restriction, a long flood wait or a high failure rate, has a status_reason. Deal with the cause before resuming.

How dispatch paces accounts#

The scheduler runs every second. It starts scheduled campaigns whose start time has come, completes those past their end time, and then, for each running campaign whose window is open, looks for the campaign's accounts that are ready to send: ACTIVE, not cooling down, with no message in flight, under their daily limit and past their next send time. It hands each ready account one message. When the send finishes, the account's next send time is set min_interval_seconds ahead.

  • Recipients go to whichever account is ready next. Three accounts at 8-second intervals deliver roughly one message every 3 seconds between them, while each sends at most one every 8 seconds.
  • An account in several campaigns has one pace, one daily limit and one message in flight across all of them, and serves the campaigns in turn.
  • Pacing is exact. There's no random jitter, nothing speeds up to catch up, and no message moves to another account to get around a limit. If the window closes before everyone is reached, sending continues at the same pace when it next opens.
  • At end_at the campaign completes; recipients not yet sent to are marked cancelled.

Note

To finish sooner, add accounts or widen the window. Raising an account's limits works too, within the platform's bounds, but carries more risk; see Pacing. The Delivery capacity check estimates whether a campaign will finish before its end time.
PreviousCampaignsNext Webhooks