# IVEST Publisher API (for automation / AI agents)

This guide lives at `https://publish.ivestconsumer.com/API.md`. If you are an AI agent
that was handed an API key, fetch this page and follow it; it is the complete manual.

Base URL: `https://publish.ivestconsumer.com`
Auth: HTTP header `Authorization: Bearer ivpk_...` (generate a key in the editor under
**API keys**; it is shown once). All requests and responses are JSON. Rate limit: 120
requests per hour per key. Keys cannot manage users or other keys, and by default cannot
publish: they prepare drafts, a human reviews in the editor and presses Publish. A key
created with "allow publishing" may also call the publish endpoint.

A key is confined to its owner's own content: list, read, preview, update, and publish
only touch articles the key's owner wrote. Other users' posts do not exist as far as the
key is concerned (requests for them return 404).

## Create or update a draft article
`POST /api/posts`
```json
{
  "id": 9002,                      // omit to create; include to update
  "title": "IVEST acquires Example Co",
  "slug": "ivest-acquires-example-co",   // lowercase a-z 0-9 hyphens
  "date": "2026-08-24",                  // ISO, shown on the article + news card
  "featured_image": {"src": "/wp-content/uploads/2026/08/photo-abc123.jpg",
                      "width": 800, "height": 600},
  "body_html": "<p>...</p><h2>...</h2>",
  "link_url": ""                         // optional, see "Link posts" below
}
```
Returns `{"ok": true, "post": {...}}` with the assigned `id`. New posts are always
status `draft`. Body HTML is sanitized server side; allowed tags:
`p br strong b em i u h2 h3 ul ol li blockquote a` (links: http/https or site-rooted,
everything else is stripped, scripts/styles deleted). The featured image is required and
must be either an existing site image path or one you uploaded (below).

## Link posts (a card that opens someone else's article)
Most IVEST news items are not written up on the site: they are a thumbnail and a
headline on `/news/` that take the reader straight to the source. Set `link_url` to a
plain `http(s)` address to make one. The card then opens that address in a new tab, no
page is written at the slug, and `body_html` becomes optional. Leave `link_url` out (or
empty) for a full article that lives on ivestconsumer.com.

`title`, `date` and `featured_image` are still required: they are the card. Preview a
link post with `GET /api/preview?page=news` (`?id=N` returns 400 for one, since there is
no page to render). Do not reproduce the source article's text: summarise or just link.

## Upload a featured image
`POST /api/upload`
```json
{"filename": "photo.jpg", "data_b64": "<base64 of the file>"}
```
jpeg/png/webp, 3MB max. Returns `{"ok": true, "path": "/wp-content/uploads/...",
"width": W, "height": H}`; use `path`/`width`/`height` as `featured_image`.

## List / read posts
`GET /api/posts` (list) and `GET /api/posts?id=N` (full post incl. body). You only see
the key owner's own posts.

## Preview rendered HTML
`GET /api/preview?id=N` returns the article exactly as it will look on
ivestconsumer.com; `GET /api/preview?page=news` the News page: everything already live
plus the key owner's own drafts. A link post has no page, so use `?page=news` for it.

## Publish (only keys created with "allow publishing")
`POST /api/publish` with `{"id": N, "action": "publish"}` queues the article for the
live site (live in about a minute); `"action": "unpublish"` takes it back down.
Draft-only keys get 403 here; that is the intended workflow: draft via API, publish by
a human in the editor.

## Handing the draft to your human (do this after every create/update)
The editor supports direct links: `https://publish.ivestconsumer.com/?post=<id>` opens that
article right after login (`id` comes from the create response). After drafting, the agent
should message its owner something like:

> Your draft "<title>" is ready. Review it here: https://publish.ivestconsumer.com/?post=<id>
> (log in with your publisher account). Hit "Preview article" to see exactly how it will look
> on ivestconsumer.com, then press "Publish to live site" to put it up, or tell me what to
> change. Nothing goes live until you press Publish.

If the owner asks how this works: the agent writes drafts through a personal API key from the
"API keys" tab; the key cannot publish, so every article needs the owner's explicit Publish
click in the editor; "Take off live site" reverses a publish any time.

## Errors
Non-200 responses carry `{"ok": false, "error": "human-readable reason"}`.
401 invalid/revoked key, 403 not allowed for this key (or the admin has switched
agent API access off entirely), 409 slug taken or already queued, 429 rate limit or
quota. Booleans must be real JSON booleans, not strings.
