Content
Content libraries, items, bulk import, and scheduled delivery via Temporal Schedules.
Content is organized as libraries (groupings) containing items (the actual content rows). Schedules run on Temporal Schedules — one schedule row owns exactly one Temporal Schedule under the deterministic id content-schedule-<row-id>. The CONTENT_DELIVERY workflow picks up the next item on each tick.
Source: apps/api/src/routes/admin/content.ts.
Endpoints
| Method | Path | Description |
|---|---|---|
| POST | /admin/projects/:projectId/content/libraries | Create a library. |
| GET | /admin/projects/:projectId/content/libraries | List libraries with item count. |
| POST | /admin/projects/:projectId/content/libraries/:libraryId/items | Insert items (serialized via advisory lock for stable sort_order). |
| GET | /admin/projects/:projectId/content/libraries/:libraryId/items | Paginated item list. |
| PATCH | /admin/projects/:projectId/content/items/:itemId | Partial update of a single item. |
| DELETE | /admin/projects/:projectId/content/items/:itemId | Hard-delete an item. |
| POST | /admin/projects/:projectId/content/libraries/:libraryId/bulk | Bulk import (id-only response). |
| POST | /admin/projects/:projectId/content/schedules | Create a schedule (validates cron first, then creates Temporal schedule). |
| GET | /admin/projects/:projectId/content/schedules | List schedules with library name. |
| PATCH | /admin/projects/:projectId/content/schedules/:scheduleId | Update a schedule (serialized under row lock; Temporal-rollback-aware). |
| DELETE | /admin/projects/:projectId/content/schedules/:scheduleId | Delete a schedule + its Temporal handle. |
POST /admin/projects/:projectId/content/libraries
Request
| Field | Type | Required |
|---|---|---|
name | string | yes |
description | string | no |
Response 201
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/content/librariescurl -X POST 'https://api.amba.dev/admin/projects/%7B%7BprojectId%7D%7D/content/libraries'Curl:
GET /admin/projects/:projectId/content/libraries
Response 200
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/content/librariescurl -X GET 'https://api.amba.dev/admin/projects/%7B%7BprojectId%7D%7D/content/libraries'Curl:
POST /admin/projects/:projectId/content/libraries/:libraryId/items
Insert one or more items. sort_order is computed from MAX+1 inside a per-library advisory lock so concurrent POSTs never collide.
Request
Per-item fields (from CreateContentItemInput):
| Field | Type | Required |
|---|---|---|
title | string | no |
body | string | yes |
media_url | string | no |
category | string | no |
tags | string[] | no |
metadata | object | no |
is_premium | boolean | no |
Response 201
Errors
500 CREATE_FAILED.
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/content/libraries/%7B%7BlibraryId%7D%7D/itemscurl -X POST 'https://api.amba.dev/admin/projects/%7B%7BprojectId%7D%7D/content/libraries/%7B%7BlibraryId%7D%7D/items'Curl:
GET /admin/projects/:projectId/content/libraries/:libraryId/items
Paginated, is_active = true only.
Query
| Param | Default |
|---|---|
limit | 50 |
offset | 0 |
Response 200
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/content/libraries/%7B%7BlibraryId%7D%7D/itemscurl -X GET 'https://api.amba.dev/admin/projects/%7B%7BprojectId%7D%7D/content/libraries/%7B%7BlibraryId%7D%7D/items'Curl:
PATCH /admin/projects/:projectId/content/items/:itemId
Partial update. Allowed fields: title, body, media_url, category, tags, metadata, is_premium, is_active, sort_order.
Response 200
Updated row.
Errors
404 NOT_FOUND.500 UPDATE_FAILED.
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/content/items/%7B%7BitemId%7D%7Dcurl -X PATCH 'https://api.amba.dev/admin/projects/%7B%7BprojectId%7D%7D/content/items/%7B%7BitemId%7D%7D'Curl:
DELETE /admin/projects/:projectId/content/items/:itemId
Response 200
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/content/items/%7B%7BitemId%7D%7Dcurl -X DELETE 'https://api.amba.dev/admin/projects/%7B%7BprojectId%7D%7D/content/items/%7B%7BitemId%7D%7D'Curl:
POST /admin/projects/:projectId/content/libraries/:libraryId/bulk
Same sort-order behavior as the items POST, but returns only the count.
Response 201
Errors
500 BULK_FAILED.
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/content/libraries/%7B%7BlibraryId%7D%7D/bulkcurl -X POST 'https://api.amba.dev/admin/projects/%7B%7BprojectId%7D%7D/content/libraries/%7B%7BlibraryId%7D%7D/bulk'Curl:
POST /admin/projects/:projectId/content/schedules
Create a schedule and its backing Temporal Schedule in a single tx. A cron field in config always wins; otherwise daily_rotation defaults to 0 9 * * *, weekly defaults to 0 9 * * MON, and random / sequential require an explicit cron.
Request
| Field | Type | Required | Description |
|---|---|---|---|
library_id | uuid | yes | Source library. |
name | string | yes | Display name. |
schedule_type | "daily_rotation" | "weekly" | "random" | "sequential" | yes | |
config.cron | string | conditional | Overrides defaults; required for random/sequential. |
config.timezone | string | no | IANA zone; defaults to "UTC". |
Response 201
Errors
400 INVALID_SCHEDULE_CONFIG— bad cron or missing required cron.500 CREATE_FAILED.
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/content/schedulescurl -X POST 'https://api.amba.dev/admin/projects/%7B%7BprojectId%7D%7D/content/schedules'Curl:
GET /admin/projects/:projectId/content/schedules
Response 200
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/content/schedulescurl -X GET 'https://api.amba.dev/admin/projects/%7B%7BprojectId%7D%7D/content/schedules'Curl:
PATCH /admin/projects/:projectId/content/schedules/:scheduleId
Update name, schedule_type, config, and/or is_active. Row is locked FOR UPDATE so concurrent PATCHes serialize; Temporal mutations are compensated if the DB commit later fails.
Request
| Field | Type |
|---|---|
name | string |
schedule_type | string |
config | object |
is_active | boolean |
Response 200
Updated row.
Errors
400 INVALID_SCHEDULE_CONFIG— proposed cron invalid.404 NOT_FOUND.502 SCHEDULE_UPDATE_FAILED— Temporal refused the mutation; DB rolled back.500 UPDATE_FAILED.
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/content/schedules/%7B%7BscheduleId%7D%7Dcurl -X PATCH 'https://api.amba.dev/admin/projects/%7B%7BprojectId%7D%7D/content/schedules/%7B%7BscheduleId%7D%7D'Curl:
DELETE /admin/projects/:projectId/content/schedules/:scheduleId
Delete the Temporal handle first, then the row. Wrapped in a tx with FOR UPDATE so concurrent PATCHes can't recreate a Temporal schedule mid-delete.
Response 200
Errors
404 NOT_FOUND.502 SCHEDULE_DELETE_FAILED— Temporal teardown failed; row still exists.500 DELETE_FAILED.
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/content/schedules/%7B%7BscheduleId%7D%7Dcurl -X DELETE 'https://api.amba.dev/admin/projects/%7B%7BprojectId%7D%7D/content/schedules/%7B%7BscheduleId%7D%7D'Curl: