Documentation

API and AI-driven management.

A JEIKO site can be run by hand from its admin panel, or by a program. This page covers the second route: how an agent works, what you entrust to it, and every route it has at its disposal.

General documentationInstallation

What an agent is — and what it is not

An agent has no privileged access to the site. It receives a mandate — a token, roles, an expiry date, a call limit — and goes through the same checks as an administrator working by hand in the interface. It never touches the database directly: there is no back door to open, and nothing to close if the mandate slips out of your hands.

In practice, that means three things.

  1. The mandate is limited in time. A key carries an expiry date. Past that date it is worthless, without anyone having to remember to revoke it.
  2. The mandate is limited in scope. Eight roles, granted separately. An agent that writes pages has no reason to read booked appointments — and cannot.
  3. The mandate is limited in rate. An hourly limit, set per agent. A loop that runs away stops on its own.

The manual is generated

The agent starts by reading GET /. That document is not written by hand: routes are read from the router, permissions from the views, block types from the model. So it cannot describe an API that no longer exists.

Everything is logged

Every call leaves a line in the log: the agent, the route, the object touched, the outcome. You know afterwards who wrote what, and the admin panel shows it without anyone reading files on the server.

Refusals are explicit

An unknown key in a request body is refused, never silently swallowed. That is the difference between a call that fails and gets fixed, and a call that succeeds while producing nothing — the costliest defect there is.

An agent cannot delete a page, nor publish one without an explicit step. Neither limit is an oversight: a deleted page cannot be recovered, and going live is an editorial decision.

Create an agent from the admin panel

The first call

The manual lives at the root of the API. It is generated from the code — routes are read from the router, permissions from the view classes, block options from the constants the renderer applies. So it cannot describe a route that does not exist, or leave a permission unsaid.

GET https://yoursite.com/api/agent/v1/
Authorization: Bearer jk_ag_<prefix>_<secret>

{
"version": { "numero": "1" },
"points_d_entree": [ … every route, each with its permission … ],
"catalogue_des_blocs": { … every content type … },
"champs_de_page": { … },
"interdits": [ … ]
}

Authentication

A bearer token, and nothing else. Seven guards stack up before a call reaches a view.

  1. HTTPS is mandatory — a token never travels in clear text
  2. Authorization header only: never in the URL, where it would end up in server logs and browser history
  3. The session does not authenticate. A logged-in administrator without a token is refused like a stranger — which is what makes the CSRF exemption of these views legitimate rather than a hole
  4. Constant-time comparison, active agent, expiry date, list of allowed addresses, call limit
  5. The secret is never stored — only its SHA-256 fingerprint is. A leaked database grants no access

Refusals do not explain themselves: same message, same code, whatever the cause. The precise reason goes to the log, which is for the site owner — spelling it out would tell a hostile caller how close they are.

The routes

Each one states the permission it requires: an agent knows before calling whether it is allowed to, rather than finding out from a refusal.

MethodPathPurposePermission
GET/This manual. Read it first.none — a valid token is enough
GET/guide/The same manual, at an address that is easy to remember.none — a valid token is enough
GET/contrats/The exact shape of every call: description, JSON schema of the body and filters, every field described.none — a valid token is enough
GET/qui-suis-je/Who I am, and what I am allowed to do.none — a valid token is enough
GET/pages/The list of the site's pages.administration_pages.view_page
POST/pages/creer/Create a page. It starts as a draft.administration_pages.add_page
GET, PATCH/pages/<int:page_id>/The full tree of a page; PATCH edits its fields.administration_pages.view_page
POST/pages/<int:page_id>/sections/Add a section to a page.administration_pages.change_page
POST/pages/<int:page_id>/versions/Create a page's version in another language, by duplication — a draft, to translate by hand.administration_pages.add_page
GET, PATCH/langues/The multilingual site: the option and the added languages. Removing a language UNPUBLISHES its pages and records.administration.view_website
GET/pages/<int:page_id>/mesures/PageSpeed scores, Core Web Vitals and failed audits. Read-only; mesure_obsolete flags a URL that differs from the current one.administration_pages.view_page
GET, PATCH, DELETE/sections/<int:section_id>/A section and its settings.administration_pages.change_page
POST/sections/<int:section_id>/lignes/Add a row to a section.administration_pages.change_page
GET, PATCH, DELETE/lignes/<int:ligne_id>/A row and its settings.administration_pages.change_page
POST/lignes/<int:ligne_id>/blocs/Add a block to one of the row's columns.administration_pages.change_page
GET, PATCH, DELETE/blocs/<int:bloc_id>/A block and its settings.administration_pages.change_page
PUT/blocs/<int:bloc_id>/contenu/Set or replace a block's content.administration_pages.change_page
POST/deplacer/<str:niveau>/<int:element_id>/Move a section, row or block up or down; or send a row to another section (« vers_section »), a block to another row (« vers_ligne », « colonne »).administration_pages.change_page
GET/images/The media library, read-only. Gives you the IDs to reuse.administration_pages.view_page
POST/images/creer/Upload an image to the media library: as JSON with « fichier_base64 » (what the MCP server uses), or multipart with « fichier ». Plus title, alt or decorative.administration_pages.add_imagecontent
GET/rdv/types/Appointment types. Their duration slices the availability.calendars.view_appointmenttype
POST/rdv/types/creer/Create an appointment type.calendars.add_appointmenttype
GET, PATCH/rdv/types/<int:type_id>/A type: duration, capacity, price, waiting list.calendars.view_appointmenttype
GET/rdv/disponibilites/The time ranges declared available.calendars.view_availability
POST/rdv/disponibilites/creer/Declare a time range — it GENERATES its slots.calendars.add_availability
GET, PATCH, DELETE/rdv/disponibilites/<int:dispo_id>/A time range. Deletion is refused if appointments are booked in it.calendars.view_availability
GET/rdv/creneaux/The generated slots — READ-ONLY. Filters: type, depuis, jusqu_a, ouverts.calendars.view_availability
GET/rdv/Booked appointments. Read-only, with a separate permission: personal data.calendars.view_appointment
GET/menus/Menus, their tree and the categories they serve.administration_menu.view_menu
POST/menus/creer/Create a menu — « copie_de » duplicates an existing one, tree included.administration_menu.add_menu
GET, PATCH/menus/<int:menu_id>/A menu; sending « elements » REPLACES its tree.administration_menu.view_menu
PATCH/categories/<int:categorie_id>/menus/Assign menus to a category — this is what makes sub-sites possible.administration_menu.change_menu
GET, PATCH/identite/Theme colours and site fonts. Partial updates.administration.view_website
GET/emails/evenements/The 67 email events and THEIR KEYS. Read before writing an email.emailing.view_emailtemplate
GET/emails/Configured emails.emailing.view_emailtemplate
POST/emails/creer/Dress an event: subject, body page (unpublished, or already an email template — a live page is refused: it would vanish from the site). Starts INACTIVE.emailing.add_emailtemplate
GET, PATCH/emails/<int:email_id>/An email: subject, body, sender, activation.emailing.view_emailtemplate
GET/questionnaires/Expert questionnaires.questionnaires_expert.view_experttest
POST/questionnaires/creer/Create a questionnaire. It starts INACTIVE.questionnaires_expert.add_experttest
GET, PATCH/questionnaires/<int:test_id>/The full tree: profiles, questions, answers, weights, combinations.questionnaires_expert.view_experttest
POST/questionnaires/<int:test_id>/profils/Add a profile — a possible outcome, with its page.questionnaires_expert.change_experttest
POST/questionnaires/<int:test_id>/questions/Add a question, its answers and their weights.questionnaires_expert.change_experttest
POST/questionnaires/<int:test_id>/combinaisons/Add a combination — settling two close profiles.questionnaires_expert.change_experttest
POST/questionnaires/<int:test_id>/simuler/« Dimensions » mode: build the result page for given scores, without saving anything.questionnaires_expert.view_experttest
PATCH/profils/<int:profil_id>/A profile. Its slug cannot change: it is in the result URL.questionnaires_expert.change_experttest
PATCH, DELETE/questions/<int:question_id>/A question; sending « reponses » REPLACES the list, weights included. Deletion refused once the test has been taken.questionnaires_expert.change_experttest
DELETE/reponses/<int:reponse_id>/Delete a single answer, without rewriting the others.questionnaires_expert.change_experttest
PATCH/combinaisons/<int:combinaison_id>/A combination and its criteria.questionnaires_expert.change_experttest
GET/categories/Categories and subcategories, with their slugs.administration_pages.view_page
POST/categories/creer/Create a category. « afficher_dans_l_url » puts it in page addresses.administration_pages.add_page
GET, PATCH, DELETE/categories/<int:categorie_id>/Correct a category. The slug never changes: it is in the addresses. Deletion only accepts an EMPTY category.administration_pages.change_page
GET/modeles/Record types and their fields. Read before creating one.custom_objects.view_customobject
POST/modeles/creer/Create a record type with its fields, and mark its template page.custom_objects.add_customobjectmodel
GET, PATCH/modeles/<int:modele_id>/A record type; PATCH ADDS fields, never renames one.custom_objects.view_customobject
GET/types-de-produit/Product types — the commercial layer.shop.view_producttype
POST/types-de-produit/creer/Create a product type. « expedition » decides whether it consumes stock.shop.add_producttype
GET, PATCH/types-de-produit/<int:type_id>/Correct a product type. The code never changes: it is in the URLs.shop.view_producttype
GET/objets/Existing records. ?modele=<id-or-slug> to filter, ?langue=en for one language's versions only.custom_objects.view_customobject
GET/fiches/Business alias of /objets/: existing records, with the same ?modele= filter.custom_objects.view_customobject
POST/objets/creer/Create a record. It starts unpublished.custom_objects.add_customobject
GET, PATCH/objets/<int:objet_id>/A record and its values; PATCH edits part of them.custom_objects.view_customobject
POST/objets/<int:objet_id>/versions/Create a record's version in another language, by duplication — unpublished, to translate by hand.custom_objects.add_customobject
GET/produits/Products.shop.view_product
POST/produits/creer/Create a product on an existing record.shop.add_product
GET, PATCH/produits/<int:produit_id>/A product: SKU, price, VAT, stock.shop.view_product

The eight roles

One mandate per area, granted and revoked separately. You do not give an agent that writes pages the right to read booked appointments.

  1. Page editor agent — create and edit pages, upload images. Never delete a page
  2. Records and store agent — record types, records, products, product types
  3. Menus agent — navigation and assignment to categories, which makes sub-sites possible
  4. Design agent — colours and fonts. A mistake here damages every page at once: it is a mandate of its own
  5. Emails agent — subject and body of transactional messages. No access to send logs
  6. Questionnaires agent — build a test. No access to attempts or leads
  7. Calendar agent — appointment types and availability
  8. Diary agent (read-only) — read booked appointments, names and addresses included. A separate role, because this is personal data

What the API refuses, and why

The refusals are not decorative precautions: each one closes a case where the database would have accepted something unusable.

  1. No page deletion. No route, no permission — two barriers, because a single one eventually gives way
  2. An unknown key is refused, never ignored. A caller cannot correct itself if nobody tells it it got something wrong
  3. Nothing is published on creation — pages, records, products and questionnaires all start as drafts
  4. An email token the event does not provide is refused: it would reach the recipient as raw text
  5. A weight aimed at another questionnaire's profile is refused: the calculation would go wrong without any warning

See the feature

Plugging an assistant into your site

The text below can be pasted as is into the instructions of a Claude project, an agent, or any assistant able to call an API. Replace the two values in square brackets with your site's address and the key you created under Settings ▸ AI agents.

The key gives access to your site. Never put it in a public repository, and create one per use: each carries its own roles, expiry date and call limit, and can be revoked on its own.

You can manage a JEIKO site through its API.

Site : [SITE ADDRESS]
Key : [API KEY]

FIRST CALL, BEFORE ANY OTHER
Read the manual. It is generated from the site's code: routes come from
the router, permissions from the view classes, block options from the
constants the renderer applies. So it can neither describe a missing route
nor leave a permission unsaid.

GET [SITE ADDRESS]/api/agent/v1/
Header: Authorization: Bearer [API KEY]

It gives you: the API version, the full list of routes with the permission
each one requires, the catalogue of block types with their options and
fields, the editable fields of a page, and the list of what is forbidden.

GET [SITE ADDRESS]/api/agent/v1/qui-suis-je/

It tells you your roles and permissions. Check it before concluding that an
action is impossible: it may simply not have been granted to you.

STRUCTURE OF A PAGE
page → sections → rows (lignes) → blocks (blocs) → content

Four traps, in order of importance:

1. « colonne » is a COLUMN NUMBER, not a vertical position. The position is
« rang ».
2. The column layout — columns_type, columns_type_tablet, columns_type_phone —
must have AS MANY SEGMENTS AS THE ROW HAS COLUMNS. « 4-4-4 » for three
columns side by side, « 12-12-12 » for the same stacked. A row created with
« colonnes: 3 » is already described that way: only touch it to depart from
it, an « 8-4 » for instance.
3. A link (a tag) cannot wrap blocks. HTML sanitising splits a link that
contains paragraphs, and the card falls apart. Put the link on the title.
4. The style attribute is CUT DOWN to six properties: color, background-color,
text-align, font-weight, font-style, text-decoration. Anything that lays
things out — backgrounds of boxes, borders, margins, sizes, alignment —
goes through the native settings of the section, row or block. CSS is a
last resort, almost never needed.

WHAT YOU CAN DO
Pages create, edit, publish, file into categories, translate
Content text, image, button, form, video, FAQ, tabs, carousels,
pricing tables, galleries…
Media upload images, with a description (alt)
Store record types, records, products, product types
Menus tree, duplication, assignment to categories
Identity theme colours, fonts on three devices
Emails subject and body of the sixty-seven events
Questionnaires profiles, questions, weights, combinations
Appointments types and availability

Each area is a separate role. If a route answers 403, you do not have that
role — it is not a mistake on your part.

WHAT YOU CANNOT DO, AND MUST NOT WORK AROUND
· delete a page — no route, no permission
· publish on creation — pages, records, products and questionnaires start
as drafts; going live is a separate call
· read questionnaire attempts, leads or email send logs: this is personal
data

WORKING RULES
· An unknown key is REFUSED, never ignored — in content as in settings. If
you get a 400, read the message: it names the accepted keys. Background
colours and images all live under « fond ».
· Sending a list — « elements », « champs », « reponses » — REPLACES the whole
list. Not sending it leaves the existing one untouched.
· Read back what you write. A write can succeed without producing anything;
reading back is the only way to notice.
· Check the real rendering on phone and tablet before saying it is done.
Many defects only show at 390 pixels wide.
· Write the search metadata as soon as you create a page. Nobody comes back
to add it.

WHEN IN DOUBT
The manual is authoritative, not this text. It is generated from the site
you are managing and describes its exact version.

Using Claude — claude.ai, the mobile app or Claude Code? The connector is simpler than this text to paste: no key to copy, and access is withdrawn in one click. See Connect Claude to your site.