Jant

Automation and API

If you want scripts, scheduled jobs, or agents to operate your own Jant site, this is the entry point. By the end you should know how to issue a token, which endpoints to call, and how to debug failures.

Jant provides two channels:

  • HTTP JSON API: general-purpose, works with curl.
  • MCP interface (/api/mcp): for agents that already speak the tool-calling protocol.

For full field definitions and request bodies, see the API Reference.

A project created with create-jant also has AGENTS.md, which points an agent at the running site's /skill.md, at npx jant --help, and at these docs, and examples/agent-content-automation/README.md with ready-to-run requests.

Which path first

  • Scripts, scheduled jobs, external integrations: use the HTTP API.
  • The caller is itself an MCP client: use MCP.
  • An AI assistant working with one site: hand it that site's /skill.md URL (for example, https://example.com/skill.md), a site-bound guide to reading, publishing, organizing, and migrating content through HTTP or MCP. To move an old blog in, see Export and import.

Default to HTTP. MCP only beats it when the caller already speaks the tool-calling protocol.

Local CLI

Setting up, deploying, moving, and backing up a site stay on the CLI: setup, migrate, deploy, reset-password, site export, site import, site snapshot, db export, and so on. site export and site import call the site's API with an API token; the rest read and write the database directly. See Command line.

Content automation (publishing posts, uploading media, updating settings) goes through HTTP or MCP.

HTTP API

Issuing a token

  1. Sign in to the site and open Settings → API Tokens.
  2. Click New Token and give it a recognizable name — that's how you identify it later when revoking.
  3. Copy the string that starts with jnt_. It's shown only once — if you lose it, issue a new one.

To revoke, delete the row on the same page; revocation takes effect immediately. Today every token is site-scoped with full read/write — no scopes, no expiry. Both may show up later; this page will be updated when they do.

For local development, DEV_API_TOKEN from packages/core/.dev.vars works the same way.

Authentication

Send the header:

Authorization: Bearer jnt_...

A few public read endpoints don't need a token by default: GET /api/public/threads, GET /api/public/threads/:slug, GET /api/public/threads/:slug/posts, and GET /api/public/posts/:slug. When PUBLIC_API_ENABLED=false, /api/public/* returns 404 to every caller. Public HTML pages, including /search, remain available.

Common endpoints

Endpoint Methods Purpose
/api/posts GET / POST / PUT / DELETE List, read, create, update, delete posts
/api/threads GET List Threads, read one, and page through its posts, drafts and private Threads included
/api/public/threads GET Public read of Threads — the homepage's list by default, with the archive's filters (no token)
/api/public/posts GET Public read of one published post by slug (no token). Lists of posts are Thread lists: /api/public/threads
/api/upload POST One-shot multipart upload, single file per call — preferred for scripts
/api/media GET / PUT / DELETE List, read, update the alt text of, and delete uploaded files; give a video its poster
/api/uploads POST → PUT → POST Multipart upload session — for large files or unstable networks
/api/attachments GET Fetch raw attachment content by id
/api/collections GET / POST / PUT / DELETE Collections
/api/smart-collections GET / POST / PUT / DELETE Smart collections — collections whose members come from conditions
/api/settings GET / PUT Site settings
/api/search GET Full-text search of published posts, private ones included
/api/mcp POST MCP JSON-RPC (initialize / tools/list, etc.)

/api/upload and /api/uploads differ by a single s but mean different things — the first is a one-shot single-file multipart upload, the second is a three-step init/part/complete session. Default to /api/upload; reach for /api/uploads only when files are large or the connection is unreliable.

Minimal example: post a note

curl -X POST "$JANT_URL/api/posts" \
  -H "Authorization: Bearer $JANT_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "format": "note",
    "bodyMarkdown": "Hello from curl."
  }'

examples/agent-content-automation/ has a quote post and a settings update to start from. Every field, including collectionIds, publishedAt, slug, pinned, and featured, is in the API reference.

Upload an image

curl -X POST "$JANT_URL/api/upload" \
  -H "Authorization: Bearer $JANT_API_TOKEN" \
  -F "file=@./path/to/photo.webp;type=image/webp" \
  -F "alt=Cover image"

Update settings

curl -X PUT "$JANT_URL/api/settings" \
  -H "Authorization: Bearer $JANT_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d @./examples/agent-content-automation/site-settings.json

Error responses

Every failure under /api returns JSON with a fixed shape:

{
  "error": "human readable message",
  "code": "ERROR_CODE",
  "details": {
    "formErrors": [],
    "fieldErrors": { "title": ["..."] }
  }
}

details is present only on 400 validation failures and names the fields that failed. Common status codes:

Status code Meaning and handling
400 VALIDATION_ERROR Request body failed validation; fix per details
401 UNAUTHORIZED Missing or invalid token; reissue and update env vars
403 FORBIDDEN Token is valid but lacks access to the resource
404 NOT_FOUND Resource doesn't exist
409 CONFLICT and friends State conflict (slug collision, hosted media quota, etc.)
500 EXTERNAL_SERVICE_ERROR, etc. Server error; retry, and if it keeps failing, send the request details for debugging

Rate limiting

The API has no hard rate limit. The search limit, RATE_LIMIT_SEARCH_PER_MIN in Configuration, applies to signed-out readers on the /search page, not to /api/search. A single Cloudflare Workers instance has limited concurrency, though — for bulk writes, keep calls sequential and add a small gap between writes when needed. If rate limits are added to the API later, they'll return 429 with Retry-After, and this section will be updated.

MCP interface

For agents that already support MCP. The transport is HTTP JSON-RPC:

  • Path: /api/mcp
  • Authentication: scripts and agents send Authorization: Bearer jnt_...; same-origin browser extensions can reuse the session cookie.
  • Protocol header: MCP-Protocol-Version: 2025-06-18. A request without it is read as that version; any other version answers 400. This page is updated when the version changes.
  • Methods: initialize, ping, tools/list, tools/call.

The tools cover posts, Threads, media, attachments, collections, settings, and search, and each does what its HTTP endpoint does. Navigation, custom URLs, smart collections, the collections directory, pinning a Thread in a collection, translations, avatar and icon uploads, and export are HTTP-only. Get the exact tool names and parameters from tools/list.

Minimal initialization request:

curl -X POST "$JANT_URL/api/mcp" \
  -H "Authorization: Bearer $JANT_API_TOKEN" \
  -H "Content-Type: application/json" \
  -H "MCP-Protocol-Version: 2025-06-18" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18"}}'

When to use MCP: the caller already has an MCP client; you want Jant exposed as a set of tools rather than handwritten fetch calls.

When not to use MCP: simple shell scripts; one-off bulk content imports; anything that doesn't need tool discovery or the tool-calling protocol.

Recommended starting point: get these three calls working first

If the goal is just "let an agent reliably publish posts, upload images, and change settings," getting these three HTTP calls working is enough:

  1. POST /api/posts
  2. POST /api/upload
  3. PUT /api/settings

Once those are stable, layer on MCP, multi-tool orchestration, or more complex content workflows.

What's next

  • API Reference — full fields, request bodies, error formats
  • FAQ
  • examples/agent-content-automation/README.md in the generated project — runnable end-to-end examples