Files
anwar-pi-website/web/docs/agent-publish-api.md

4.0 KiB

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

KEY="$AGENT_API_KEY"   # never hardcode — export AGENT_API_KEY in your shell; it lives in the server's .env.production

# 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:

{
  "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

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/<slug>) — 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/<filename>.
  • Attachments are stored as Payload files documents under files-private/ (volume-mount it like media/ on the VPS). Downloads go through GET /api/files/<id>: 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.