Amba

Content Scheduling

Deliver content library items on a schedule — daily rotation, weekly, random, or sequential.

Content schedules are reliable and recurring — Amba runs them on the cadence you configure, every tick lands a fresh delivery, and pausing or deleting a schedule keeps state consistent.

Schedule types

TypeDefault cronWhat it does
daily_rotation0 9 * * *Rotates through items, one per day, at 09:00 in the chosen timezone
weekly0 9 * * MONDelivers one item per week on Mondays
randomrequiredPicks a random item each tick
sequentialrequiredDelivers items in sort_order order

random and sequential require you to provide an explicit cron — the schedule type alone doesn't imply a cadence.

Create a schedule

POST /admin/content/schedules
 
{
  "library_id": "<uuid>",
  "name": "Morning Affirmations",
  "schedule_type": "daily_rotation",
  "config": {
    "timezone": "America/Los_Angeles"
  }
}

Override the default cron with your own:

{
  "library_id": "<uuid>",
  "name": "Evening Tips",
  "schedule_type": "daily_rotation",
  "config": {
    "cron": "0 19 * * *",
    "timezone": "UTC"
  }
}

PATCH / DELETE

Updates apply atomically — if anything fails, the schedule reverts to its previous state so deliveries don't silently drift.

PATCH /admin/content/schedules/:id
 
{ "is_active": false }

is_active: false pauses the schedule (no new deliveries fire) without deleting the row.

Deleting a schedule is also atomic — the recurring job is torn down first, then the row is removed.

Config keys

Each schedule gets a server-allocated config_key of the form content_<schedule_uuid>. This key is what the SDK sees on GET /client/content/today — it's stable across renames of the schedule's name.

config shape

{
  timezone?: string;     // Default "UTC"
  cron?: string;         // Override the default schedule cron
  // Additional schedule-type-specific fields may be added later.
}

Empty or missing cron for random / sequential returns 400 INVALID_SCHEDULE_CONFIG up front — the row never commits without a working schedule spec.

Overlap policy

If a previous content delivery is still running when the next tick arrives, the next tick is skipped rather than queued. This is the right default for content rotation; queueing would deliver two items simultaneously.

Life of a tick

Each tick:

  1. Loads the schedule + library.
  2. Picks the next item based on schedule_type:
    • daily_rotation / sequential: compute position from (count(deliveries) mod count(items)).
    • random: ORDER BY random() LIMIT 1.
  3. Writes a row to content_deliveries with the chosen item id and timestamp.
  4. The SDK's getToday() reads content_deliveries plus content_items.

SDK consumption

const today = await client.content.getToday();
// Items scheduled for delivery today across all active schedules.

getToday() filters by occurred_at::date = CURRENT_DATE, so timezones matter — schedules default to UTC unless you set config.timezone.

MCP tools

ToolDescription
amba_create_content_scheduleCreate a delivery schedule on a library

Routes reference

MethodPathDescription
POST/admin/content/schedulesCreate schedule
GET/admin/content/schedulesList schedules for the project
PATCH/admin/content/schedules/:idUpdate schedule atomically
DELETE/admin/content/schedules/:idTear down recurring delivery + row

Database tables

TablePurpose
content_schedulesSchedule definition + type, config
content_deliveriesOne row per tick: which item was delivered, at what time
content_itemsThe items themselves, ordered by sort_order

Next

On this page