API Authentication

The jekcms REST API authenticates with a token — one long-lived secret sent in a header. No OAuth dance, no session cookies. That is what n8n, Make, Zapier and most automation platforms expect, and it is all the API asks for.

This page describes what the API really does. Where a feature does not exist, it says so.

Sending the token

Two headers are accepted, and they are equivalent:

X-API-Key: YOUR_TOKEN
Authorization: Bearer YOUR_TOKEN

A minimal request:

curl -H "X-API-Key: YOUR_TOKEN" https://yoursite.com/api/v1/posts

A missing or unrecognised token returns 401:

{ "success": false, "error": "Unauthorized" }

Do not put the token in a query string — the API does not read it from there, and query strings end up in access logs anyway.

Where tokens come from

Admin → API Keys. Create a token, give it a name you will recognise later (n8n, zapier, mobile-staging), and optionally an expiry date.

The full token is shown once, at creation. jekcms stores only its SHA-256 hash, so it cannot show it to you again — if you lose it, create a new one and deactivate the old.

Each token row carries:

  • user — the account the token acts as. This is the important one; see the next section.
  • name — your label.
  • active flag — deactivate to revoke. Revocation takes effect on the next request; there is no cache to wait out.
  • expiry — optional. Once passed, the token stops authenticating (401). Empty means no expiry.
  • last used — updated on every successful request, so you can spot tokens nobody uses any more.

How authorization works — roles, not scopes

This is the part most API docs get wrong, so read it once.

A jekcms token has no scopes. There is no posts:write and no per-token permission list. A token is an account: it belongs to a user, and it can do exactly what that user's role can do.

Write operations are checked against the role's capabilities (ROLE_CAPABILITIES in config/constants.php):

| Role | Can do, via the API | |---|---| | admin | everything: posts, pages, media, users, settings, automation | | editor | posts, pages, media, comments, categories — not users or settings | | author | own posts and uploads only — the API refuses writes to another author's content (403) | | subscriber | read only |

A caller without the required capability gets HTTP 403:

{ "success": false, "error": "Insufficient role for this operation" }

The practical consequence: least privilege is done by choosing the right user, not by ticking permission boxes. If your n8n workflow only needs to publish posts and upload images, create a dedicated editor (or author) account and issue the token from it. Do not hand an automation an admin token because it was the quickest thing on the screen.

The api_tokens table carries an abilities column left over from an earlier design. It is not enforced. Do not rely on it; the role is what governs access.

Rate limit

The limit is applied per client IP, not per token: API_RATE_LIMIT requests per hour, default 100. It lives in config/constants.php and can be overridden with the API_RATE_LIMIT environment variable.

Over the limit:

HTTP/1.1 429 Too Many Requests

{ "success": false, "error": "Rate limit exceeded" }

There is no Retry-After header and no per-token bucket. If you run a busy integration, raise API_RATE_LIMIT in your environment rather than hammering the default and retrying blindly. Every endpoint — including /api/v1/health — passes through the limiter and requires a token.

Endpoints the token opens

posts, media, categories, tags, comments, users, settings, trends, stats, sitemap, search, health, and webhook (the action-based automation surface used by n8n).

The API creates and updates posts. It does not write to the content queue — see Content Queue for the real intake paths.

Sensible habits

  • One token per integration, named after it. Revoking is then obvious and the blast radius is small.
  • Issue the token from an account with the smallest role that does the job.
  • Set an expiry on tokens handed to contractors or short-lived pipelines.
  • Store the secret in a real secret manager (n8n credentials, environment variable, a password manager) — never in Git.
  • Deactivate immediately when someone leaves or a secret leaks.

Be the first to know

New features, release notes & CMS guides — a couple of emails a month, no spam.