Welcome, First Last
Back To Dashboard
How to give an AI agent — Claude, a cron worker, a custom script — scoped access to a CMS Caddie project.
blog-publisher-bot) so you can find it later in audit logs.cmsk_…) and the agent ID once. Copy the key immediately — only a SHA-256 hash is stored, so we genuinely cannot show it to you again.cmsk_… value like a password.Three ways to wire an agent up, from most automatic to most manual.
For Claude Desktop, Claude Code, Cursor, or any MCP-aware client. Two ways to connect, pick whichever fits.
Uses OAuth. No API keys to paste; users log in through the browser and approve the app.
https://mcp.cmscaddie.com and click Add. This is the same URL for every user — per-project access is chosen at login, not baked into the URL.agents.cmscaddie.com/oauth/authorize. Sign in with your CMS Caddie email and verification code. The consent screen lists every project on your account; tick the ones you want this connector to access, and under each, the specific scopes (e.g. content:read, content:write). Click Approve.The issued token is short-lived (1 hour) and rotates automatically via its refresh token (90 days). You can add more projects, edit scopes, or remove projects from the connector anytime from the CMS Caddie dashboard — changes apply to the live token immediately.
Uses a long-lived cmsk_… agent key you minted in step 1. Good for scripts, CI runners, or if your MCP client doesn't support OAuth-based connectors yet. Edit claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"cms-caddy": {
"url": "https://mcp.cmscaddie.com",
"headers": {
"Authorization": "Bearer cmsk_YOUR_RAW_KEY_HERE"
}
}
}
}
Fully quit and reopen Claude Desktop.
The client automatically pulls:
content_create, content_update, content_delete, files_upload, etc.) with full input schemas. Adding a route to the API later makes it appear here without reconfiguring.You only need to tell the agent one extra thing: its project ID (the id in the URL of this dashboard). Paste it into the agent's system prompt or first user message.
For ChatGPT custom GPTs or agent frameworks that ingest OpenAPI:
| OpenAPI 3.1 spec | https://agents.cmscaddie.com/openapi.json |
|---|---|
| Plugin manifest | https://agents.cmscaddie.com/.well-known/ai-plugin.json |
| Plain-text summary | https://agents.cmscaddie.com/llms.txt |
Auth is Bearer — paste the raw key when the client asks for it.
For scripts, workers, or non-AI integrations, call the REST endpoints directly:
curl -X POST https://agents.cmscaddie.com/projects/PROJECT_ID/groups/GROUP/content \
-H "Authorization: Bearer cmsk_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"name":"Hello","slug":"hello","published":"999901010000"}'
Discover the full route list at https://agents.cmscaddie.com/agent-manifest.
| Scope | What it grants |
|---|---|
content:read | List, retrieve, and search content items. |
content:write | Create and update content items (not publish-to-live). |
content:delete | Delete content items and fire the delete webhook. |
content:publish | Set published to a time at or before now (makes content go live). |
files:write | Request presigned URLs to upload files. |
files:delete | Delete files under this project's agents/ prefix. |
content:read. An agent that drafts but should never auto-publish gets content:write without content:publish. This forces the agent to schedule future dates rather than go live.Every content item has a published timestamp at the top level and a fields object whose keys are the group's field slugs. Each value must be a dict with name, type, and value — the dashboard renderer and webhook consumers depend on that shape.
{
"published": "999901010000",
"fields": {
"title": { "name": "Title", "type": "plain-text", "value": "Hello" },
"body": { "name": "Body", "type": "html", "value": "<p>…</p>" },
"thumbnail-image": {
"name": "Thumbnail Image",
"type": "image",
"value": "https://cms-caddy.s3.us-east-2.amazonaws.com/agents/<project>/<agent>/<file_id>/thumb.jpg",
"alt": "Description of the image"
}
}
}
Discover the schema first. Call groups_list — each group in the response includes a fields array with every field's slug, name, type, and required flag. Use those slugs as the keys in fields when you create or patch content; copy the name and type into each entry.
Updates use the same shape. When patching a single field, send the full {name, type, value} object — the API merges at the field-slug level, not deeper. Bare strings like "title": "New title" will silently produce broken content the dashboard can't render.
Field types: plain-text, html, number, image, video, audio, file, code, multiple-files, content::<group_id>. Image/video/file types may include alt.
The published field on a content item is a UTC timestamp in the form YYYYMMDDHHMM. The platform uses a single rule to decide whether an item is live:
published value | Effect | Scope required |
|---|---|---|
| ≤ now (UTC) | Live — publishes on the next minute tick. | content:write + content:publish |
| > now (UTC) | Scheduled to go live at that time. | content:write |
Far future (e.g. 999901010000) | Effectively unpublished. | content:write |
So to publish immediately, set published to now; to schedule, set it to a future UTC minute; to "unpublish", push the date far into the future. There is no separate publish/unpublish verb.
On the project settings page, each agent key row exposes three actions:
revoked. Subsequent calls return 401 unauthorized. The key metadata stays, and you can reactivate it with the same button (which now reads "Reactivate"). Prefer this over delete in almost every case.