# Agent Publish API — /api/agent/posts Publish and manage blog posts programmatically. Auth: send the agent key in the `x-agent-key` header. The key lives on the VPS in `/root/anwarpi/.env.production` (`AGENT_API_KEY`). - Base URL: `https://anwarpy.ai` - Content-Type: `application/json` - Posts are created **draft** by default. Pass `"publish": true` to publish (sets `status: published` + `publishedAt` now). Unpublishing stays admin-only (`/admin`). - `access` controls gating: `"free"` (public) or `"vip"` (members only). - `content` (Lexical) may be omitted on create — a minimal valid doc is inserted; update it later with PUT. Other writable fields: `title`, `slug`, `dek`, `teaser`, `category` (`agents` | `models` | `automation` | `foundations`), `readingMinutes`, `cover`. - `attachments` attaches per-post work files: an array of `{ "filename": "agent-state.md", "dataBase64": "…", "access": "vip" }`. `access` is per file (`"free"` downloads for anyone, `"vip"` members only) and defaults to `"vip"`. Max 5 files, ≤5MB each, extensions pdf/zip/md/csv/ txt/json. On PUT, sending `attachments` **replaces** the post's file set — omit the field to keep existing files. ## Create + publish a post with an image ```bash KEY=033d1eed44ca6abf483de10735c47155d38f7b5bc9d46449 # Inline base64 image (jpeg/png/webp, ≤5MB) python3 - <<'EOF' > /tmp/post.json import base64, json, pathlib print(json.dumps({ "title": "دليلي الجديد", "slug": "my-new-guide", "dek": "وصف قصير يظهر في القوائم.", "teaser": "نص المعاينة للزوار غير المشتركين.", "category": "وكلاء", "access": "vip", # or "free" "readingMinutes": 9, "publish": True, # omit → draft "coverImage": { "filename": "my-cover.jpg", "mimeType": "image/jpeg", "dataBase64": base64.b64encode(pathlib.Path("my-cover.jpg").read_bytes()).decode(), }, })) EOF curl -s -X POST https://anwarpy.ai/api/agent/posts \ -H "x-agent-key: $KEY" -H "Content-Type: application/json" \ --data @/tmp/post.json ``` Response `201`: ```json { "id": 113, "title": "دليلي الجديد", "slug": "my-new-guide", "status": "published", "access": "vip", "category": "agents", "cover": { "id": 113, "url": "/api/media/file/my-cover.jpg" }, "attachments": [ { "id": 42, "filename": "agent-state.md", "access": "vip", "url": "/api/files/42" } ] } ``` ## Update a post ```bash curl -s -X PUT https://anwarpy.ai/api/agent/posts/my-new-guide \ -H "x-agent-key: $KEY" -H "Content-Type: application/json" \ -d '{"access": "free", "publish": true, "coverImage": {"url": "https://example.com/cover.png"}}' ``` - `coverImage` accepts `{ "dataBase64": "…" }` or `{ "url": "https://…" }` (server fetches and stores it; `filename`/`mimeType` optional). - `cover` may also be an existing media ID (integer). ## Where the image appears The cover is rendered on: 1. **Post page** (`/posts/`) — full-width cover under the header. 2. **Articles listing** (`/articles`) — thumbnail on each card. 3. **Homepage** (`/`) — "أحدث الأدلة" section (latest 6 published posts). If a post has no cover, the catalog card falls back to the category's default decorative cover; the post page and listing simply omit the image. ## Rules - The agent **cannot** set `status` directly — only via `"publish": true`. - The agent **cannot** unpublish or delete — do that in `/admin`. - Images are validated: jpeg/png/webp, ≤5MB; stored as Payload `media` documents and served from `/api/media/file/`. - Attachments are stored as Payload `files` documents under `files-private/` (volume-mount it like `media/` on the VPS). Downloads go through `GET /api/files/`: `free` files stream to anyone, `vip` files return 401 anonymous / 403 for non-members. The `vip` badge on the post page links only for entitled viewers.