jekcms ships a single, stable REST API at /api/v1: token authentication, a consistent response envelope, role-aware write access, and publishing that goes through the same policy gate as every other channel. A practical guide for integrators.
jekcms ships a single, stable REST API mounted at /api/v1. This guide covers the parts an integration actually touches: authentication, the endpoint map, publishing behaviour, and rate limits — as they really work in the current release.
Authentication
The API uses static tokens that you create and revoke from the admin panel. Send the token either as an X-API-Key header or as a standard bearer token — both are accepted:
curl https://yoursite.com/api/v1/posts \
-H "Authorization: Bearer YOUR_TOKEN"
# equivalent:
curl https://yoursite.com/api/v1/posts \
-H "X-API-Key: YOUR_TOKEN"
Tokens are stored as SHA-256 hashes on the server, can carry an optional expiry date, and can be deactivated individually. There is no refresh-token dance: rotate a token by creating a new one, switching your integration over, and deactivating the old one.
Response Envelope
Every response uses the same wrapper. Success:
{"success": true, "data": { ... }}
Errors carry the HTTP code and a message:
{"success": false, "error": {"code": 401, "message": "Unauthorized"}}
The Endpoint Map
/api/v1/posts— list, read, create, update, delete; pluspublishandscheduleactions and arevisionsreader/api/v1/media— file upload and management/api/v1/categories,/api/v1/tags— taxonomy/api/v1/comments— moderation/api/v1/users— author management/api/v1/settings— site settings/api/v1/webhooks— outbound webhook management/api/v1/stats,/api/v1/trends— analytics data/api/v1/search,/api/v1/sitemap,/api/v1/health— utilities
Write Access Is Role-Aware
A token inherits the role of the user it belongs to. Authentication alone is not enough to mutate content: an author-level token can only modify its own posts, and publish rights are checked separately from edit rights. Give integrations the least privileged user that can do the job.
Publishing Goes Through the Same Gate as Everything Else
Creating a post with status: "published", or calling the publish action, does not bypass anything: the request runs through the same publish policy as every other channel. The source is recorded as api, the publication mode is stored, and if the content quality gate is enabled and the draft fails it, the post stays a draft and the response tells you why.
Post responses also include a review_valid field: true when a human approval is still valid for the current content, false when the content changed after approval, and null when the post was never human-approved. Editorial dates are part of the payload, so integrations cannot accidentally fake freshness.
Publish and Schedule Actions
# Publish an existing draft
curl -X POST https://yoursite.com/api/v1/posts/42/publish \
-H "Authorization: Bearer YOUR_TOKEN"
# Schedule it instead
curl -X POST https://yoursite.com/api/v1/posts/42/schedule \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{"scheduled_at": "2026-08-01 09:00:00"}'
Scheduling only tags the post; the quality gate runs at the moment the post actually flips to published. That flip works even without a system cron — the built-in pseudo-cron triggers it on normal site traffic.
Rate Limiting
The API applies a shared per-IP rate limit (100 requests per hour by default, configurable via API_RATE_LIMIT). Exceeding it returns HTTP 429 with "Rate limit exceeded". Batch your writes and back off on 429 rather than hammering the endpoint.
Practical Advice
- Treat
success: falseplus the HTTP code as the single source of truth — do not parse messages. - Use the
revisionsendpoint before destructive updates; every content change is snapshotted server-side. - Prefer draft-then-publish over direct
status: "published"creation when a human should look at the content first.