Welcome, First Last

Back To Dashboard

Onboarding an agent

How to give an AI agent — Claude, a cron worker, a custom script — scoped access to a CMS Caddie project.

1. Mint an agent API key

  1. Open the project settings page and scroll to Agent API Keys.
  2. Enter a descriptive name (e.g. blog-publisher-bot) so you can find it later in audit logs.
  3. Tick only the scopes the agent actually needs. Start narrow — you can always mint another key. See scopes reference.
  4. Click Add. A modal shows the raw key (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.
Security: each key is bound to one project. A leaked key cannot touch other projects, and agent keys cannot reach dashboard admin endpoints. Still — treat the raw cmsk_… value like a password.

2. Connect the agent

Three ways to wire an agent up, from most automatic to most manual.

A. MCP (recommended)

For Claude Desktop, Claude Code, Cursor, or any MCP-aware client. Two ways to connect, pick whichever fits.

A1. Browser login (non-technical users) — recommended

Uses OAuth. No API keys to paste; users log in through the browser and approve the app.

  1. Open Claude Desktop → Settings → Connectors → Add custom connector.
  2. Paste 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.
  3. A browser window opens at 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.
  4. Claude Desktop finishes the exchange automatically. You should see cms-caddy in the connectors list with 8 tools ready.

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.

A2. Config file (power users, CI, headless)

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.

After connecting (both paths)

The client automatically pulls:

  • An instructions block covering publish semantics, idempotency, project scoping, error handling, and file-upload flow — injected into the model's context.
  • A tool list (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.

B. OpenAPI / plugin-style clients

For ChatGPT custom GPTs or agent frameworks that ingest OpenAPI:

OpenAPI 3.1 spechttps://agents.cmscaddie.com/openapi.json
Plugin manifesthttps://agents.cmscaddie.com/.well-known/ai-plugin.json
Plain-text summaryhttps://agents.cmscaddie.com/llms.txt

Auth is Bearer — paste the raw key when the client asks for it.

C. Raw REST

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.

3. Scopes reference

ScopeWhat it grants
content:readList, retrieve, and search content items.
content:writeCreate and update content items (not publish-to-live).
content:deleteDelete content items and fire the delete webhook.
content:publishSet published to a time at or before now (makes content go live).
files:writeRequest presigned URLs to upload files.
files:deleteDelete files under this project's agents/ prefix.
Principle of least privilege: a key that only needs to read should only have 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.

3.5. Content field schema

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.

4. Publish semantics

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 valueEffectScope 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.

5. Revoke, audit, and delete

On the project settings page, each agent key row exposes three actions:

  • Audit — opens a modal of the last ~50 events this key performed: action, timestamp, target, status code, and request ID. Events are retained even if the key is revoked or deleted.
  • Revoke — flips the key's status to 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.
  • Trash icon — permanent delete. Metadata (name, created_by, timestamps) is lost; audit rows stay because they are keyed by agent ID. Use only when you are sure the key will never be re-examined.