Amba

Content Libraries

Manage and schedule content delivery — daily tips, quotes, articles, and more.

Content libraries let you manage collections of content items (tips, quotes, affirmations, articles) and deliver them to users on a schedule. Great for apps that need daily content rotation.

How it works

  1. Create a content library (e.g., "Daily Motivation", "Workout Tips")
  2. Add content items to the library with text, media, categories, and tags
  3. Create a delivery schedule to control when and how items are delivered
  4. The client SDK fetches today's content or browses the full library

SDK usage

Get today's content

// Channel defaults to "default" — single-channel apps can call this bare.
const today = await Amba.content.getToday();
 
// Multi-channel apps pass the channel slug explicitly:
const lessonsToday = await Amba.content.getToday('lessons');

getToday returns a single ContentItem | null — the one item selected by the channel's active schedule for the current day window.

Browse a library

// Channel defaults to "default" here too. `options.limit` caps the page
// size; `options.cursor` is the opaque token returned by a prior
// response's `next_cursor` for pagination.
const items = await Amba.content.getLibrary('default', { limit: 20 });
 
// Subsequent page:
const nextPage = await Amba.content.getLibrary('default', {
  limit: 20,
  cursor: previousResponseNextCursor,
});

Get a single item

const item = await client.content.getItem('item_xxx');

Create, update, and delete items from the client

The SDK exposes write endpoints for user-owned content. The server stamps owner_app_user_id from the current session — the caller cannot set it. updateItem and deleteItem return 404 when the target item isn't owned by the current user.

// Create — body is required; title, category, tags, metadata, media_url are optional.
const todo = await client.content.createItem('lib_todos', {
  title: 'Buy milk',
  body: '',
  metadata: { completed: false },
});
 
// Update — any field you pass overwrites. `is_active: false` soft-deletes.
await client.content.updateItem(todo.id, {
  metadata: { completed: true },
});
 
// Hard delete.
await client.content.deleteItem(todo.id);

CreateContentItemInput:

interface CreateContentItemInput {
  title?: string | null;
  body: string;
  media_url?: string | null;
  category?: string | null;
  tags?: string[];
  metadata?: Record<string, unknown>;
}

UpdateContentItemInput:

interface UpdateContentItemInput {
  title?: string | null;
  body?: string;
  media_url?: string | null;
  category?: string | null;
  tags?: string[];
  metadata?: Record<string, unknown>;
  is_active?: boolean;
}

Client API reference

MethodPathDescription
GET/client/content/todayToday's scheduled content items.
GET/client/content/libraries/:id/itemsList items in a library (query: category, limit, offset).
GET/client/content/items/:idFetch a single item.
POST/client/content/libraries/:id/itemsCreate an item owned by the current user.
PATCH/client/content/items/:idUpdate an item owned by the current user.
DELETE/client/content/items/:idDelete an item owned by the current user.

Admin API reference

Libraries

MethodPathDescription
POST/admin/content/librariesCreate library
GET/admin/content/librariesList libraries with item counts

Items

MethodPathDescription
POST/admin/content/libraries/:id/itemsAdd items to library
GET/admin/content/libraries/:id/itemsList items (paginated)
PATCH/admin/content/items/:idUpdate an item
DELETE/admin/content/items/:idDelete an item
POST/admin/content/libraries/:id/bulkBulk import items

Schedules

MethodPathDescription
POST/admin/content/schedulesCreate delivery schedule
GET/admin/content/schedulesList schedules
PATCH/admin/content/schedules/:idUpdate schedule

POST /admin/content/libraries/:id/items

Body:

{
  "items": [
    {
      "title": "Stay Consistent",
      "body": "The key to progress isn't perfection — it's consistency.",
      "category": "motivation",
      "tags": ["daily", "mindset"],
      "is_premium": false
    }
  ]
}

Schedule types

TypeDescription
daily_rotationRotates through items one per day
weeklyDelivers one item per week
randomPicks a random item each delivery
sequentialDelivers items in sort order

MCP tools

ToolDescription
amba_content_libraries_createCreate a content library
amba_content_items_addAdd items to a library
amba_content_schedules_createSet up a delivery schedule

Example: Daily affirmations

Agent: "Set up a daily affirmation library with 5 items"

1. amba_content_libraries_create({ name: "Daily Affirmations", description: "Positive affirmations delivered daily" })
2. amba_content_items_add({ library_id: "<id from step 1>", items: [
     { body: "I am capable of achieving my goals." },
     { body: "Today I choose joy and gratitude." },
     { body: "I am getting stronger every day." },
     { body: "I deserve success and happiness." },
     { body: "Every day is a fresh start." }
   ]})
3. amba_content_schedules_create({ library_id: "<id from step 1>", name: "Morning Affirmation", schedule_type: "daily_rotation" })

Versioning + rollouts

Each content_item row carries three columns that together let you ship copy changes without an app deploy:

  • content_key — a stable, user-facing key (e.g. mission_intro, paywall_headline). Multiple rows with the same (library_id, content_key) are treated as versions of each other.
  • version — integer, immutable per row. The first POST under a key seeds version 1; subsequent POSTs auto-increment.
  • rollout_percent — integer in [0, 100]. The server hashes (user_id, item_id) into a [0, 99] bucket and serves the highest-version row whose bucket < rollout_percent. If no version matches, the highest-version row pinned at 100% serves as the stable baseline so every user always gets a row.

The hash is deterministic, so the same user always sees the same version on subsequent requests until you change rollout_percent. Distribution is uniform — at 50% rollout, expect within ±10% of half your users to land on the new version.

Items with content_key = NULL predate this feature and continue to behave exactly as before.

Ramp a rollout: 10% → 50% → 100%

The partner-shipped recipe for ramping a copy change with no app deploy:

# 1. Seed v1 — first POST under a fresh key. Auto-seeds at rollout_percent=100.
curl -X POST -H "Authorization: Bearer $DEV_TOKEN" \
  https://api.amba.dev/v1/admin/projects/$PROJECT/content/libraries/$LIB/items/mission_intro/versions \
  -d '{ "body": "Welcome to your mission. Tap to begin." }'
 
# 2. Author v2 — second POST under the same key. Lands at rollout_percent=0
#    (invisible to everyone) so you can review before exposing it.
curl -X POST -H "Authorization: Bearer $DEV_TOKEN" \
  https://api.amba.dev/v1/admin/projects/$PROJECT/content/libraries/$LIB/items/mission_intro/versions \
  -d '{ "body": "Welcome — your first mission is ready. Lets go." }'
 
# 3. Ramp v2: 0 → 10 → 50 → 100. Each PATCH is atomic; users re-bucket
#    deterministically so monitoring is straightforward.
curl -X PATCH -H "Authorization: Bearer $DEV_TOKEN" \
  https://api.amba.dev/v1/admin/projects/$PROJECT/content/libraries/$LIB/items/mission_intro/versions/2 \
  -d '{ "rollout_percent": 10 }'
# … review metrics …
curl -X PATCH .../mission_intro/versions/2 -d '{ "rollout_percent": 50 }'
# … review metrics …
curl -X PATCH .../mission_intro/versions/2 -d '{ "rollout_percent": 100 }'
 
# 4. (Optional) Demote v1 once v2 has been at 100% for a stable period.
curl -X PATCH .../mission_intro/versions/1 -d '{ "rollout_percent": 0 }'
 
# 5. Inspect rollout state at any time.
curl -H "Authorization: Bearer $DEV_TOKEN" \
  https://api.amba.dev/v1/admin/projects/$PROJECT/content/libraries/$LIB/items/mission_intro/versions
# → { "data": [{"version":2,"rollout_percent":100,...},{"version":1,"rollout_percent":0,...}], "total": 2 }

Versioning admin endpoints

MethodPathDescription
POST/admin/content/libraries/:id/items/:key/versionsCreate a new version. First version of a key seeds at 100%; subsequent at 0%.
PATCH/admin/content/libraries/:id/items/:key/versions/:vUpdate body / metadata / rollout_percent (0–100). Common: ramp the rollout.
GET/admin/content/libraries/:id/items/:key/versionsList all versions of a key (DESC by version) with their rollout_percent each.

Versioned client fetch

GET /client/content/libraries/:libraryId/keys/:contentKey

Returns the version the current authenticated user should see. The bucket is computed from the user's id, so two different users may receive different versions of the same key. The response shape matches the regular GET /client/content/items/:id (a single ContentItem), so the SDK's rendering pipeline does not need special-casing.

If no version's bucket matches and no 100% baseline exists (the operator-error case where every version is at 0%), the highest-version row is returned anyway — better to serve something than nothing while the operator notices.