Documentation

Tout ce qu'il faut pour brancher ton app sur ton backend Clicbase. L'API suit les conventions Supabase/PostgREST : si tu connais l'une, tu connais l'autre.

Démarrage rapide

1. Crée un projet dans le dashboard : tu obtiens une base PostgreSQL isolée, une URL d'API et tes clés (anon côté navigateur, service côté serveur uniquement).

2. Crée une table (éditeur SQL du dashboard) :

create table articles (
  id uuid primary key default gen_random_uuid(),
  title text not null,
  created_at timestamptz default now()
);
-- l'API la sert immédiatement, sans redéploiement

3. Lis-la depuis ton app (remplace <slug> par l'identifiant de ton projet) :

const res = await fetch(
  "https://clicbase.com/db/<slug>/articles?select=*&order=created_at.desc",
  { headers: { apikey: "<clé anon>" } }
);
const articles = await res.json();

Connecter une IA (MCP)

MCP est le protocole par lequel un assistant reçoit des outils plutôt que des explications. Une fois le serveur branché, tu demandes en langage courant : Claude crée tes tables, écrit tes requêtes, pose tes règles de sécurité et lit tes données, sans que tu recopies une clé nulle part.

Sur ton propre projet, c'est la variante clicbase-db-mcp. Les deux valeurs se trouvent dans le tableau de bord, en ouvrant ton projet puis sa section API: « API REST » donne l'URL, « Clé service » donne la clé.

claude mcp add ma-base \
  -e CLICBASE_DB_URL=https://clicbase.com/db/<slug> \
  -e CLICBASE_SERVICE_KEY=<clé service> \
  -e CLICBASE_READ_ONLY=1 \
  -- npx -y -p @clicbase/mcp clicbase-db-mcp

Cette section API affiche la commande déjà remplie, avec un bouton pour la copier · rien à assembler à la main.

Pour créer des projets, et non en administrer un seul, le serveur tout-en-un prend une clé de compte cbk_…, qui se génère dans le menu VPS ou Docker › Clé API. Elle n'est affichée qu'une fois : le serveur n'en garde qu'une empreinte. C'est une permissionet non une identité, à la différence d'un mot de passe · elle a une portée, une expiration, et un clic la révoque sans rien casser ailleurs.

claude mcp add clicbase \
  -e CLICBASE_API_KEY=cbk_... \
  -e CLICBASE_READ_ONLY=1 \
  -- npx -y -p @clicbase/mcp clicbase-mcp

-pdésigne le paquet à installer, l'argument suivant la commande à lancer. Sans lui, npx cherche une commande portant le dernier segment du nom du paquet, et npx -y @clicbase/mcp échouait sur could not determine executable to run : le paquet en porte plusieurs. Réparé en 0.1.1, la forme -p reste la plus sûre.

À la main, dans ~/.claude.json ou claude_desktop_config.json :

{
  "mcpServers": {
    "clicbase": {
      "command": "npx",
      "args": ["-y", "@clicbase/mcp"],
      "env": {
        "CLICBASE_API_KEY": "cbk_...",
        "CLICBASE_READ_ONLY": "1"
      }
    }
  }
}

CLICBASE_READ_ONLY=1est dans la commande exprès : c'est le mode à garder par défaut. En lecture seule, les outils qui écrivent ne sont pas refusés, ils sont absents. Un outil présent qui répond « interdit » laisse l'assistant croire qu'un chemin existe, donc essayer, reformuler, insister. Un outil qui n'est pas dans la liste n'est jamais demandé.

Restent list_tables, describe_table, get_oauth_provider, clicbase_conventions, list_site_files, list_sftp_accounts, list_sites et list_projects. Partent run_sql, create_database, enable_realtime, enable_rls, create_policy, set_oauth_provider, set_email_smtp, get_credentials, read_site_file, create_sftp_account, delete_sftp_account , write_site_files et append_site_file.

create_sftp_account part aussi : il ne touche à aucune donnée, et il rend un mot de passe qui ouvre tout le dossier du site. Il fabrique un compte dédié, jamais le compte principal · celui-là est l'identité du site, il ne se révoque pas.

read_site_file part pour la même raison : lister les fichiers d'un site révèle une structure, lire leur contenu peut livrer des identifiants écrits en dur dans un config.php. Les fichiers cachés, eux, sont refusés à tout niveau · .env ne peut ni être déposé ni être lu.

get_credentials ne modifie rien, et il part quand même : il PEUT rendre la clé service du projet, laquelle ouvre le SQL complet et contourne la RLS. Depuis le 19 septembre 2026 il rend la clé anonpar défaut, et la clé service seulement si on la demande explicitement · elle n'expire jamais et fonctionne hors du connecteur, là où un jeton dure une heure. Le garder reviendrait à retirer run_sqld'une main et à livrer de quoi le refaire de l'autre.

Sans cette ligne, le mode complet rend run_sql, donc du SQL arbitraire avec les droits du propriétaire de la base. Garde-le pour ta propre plateforme, sous tes yeux. Pour la base d'un client, ou pour un agent qui travaille sans personne devant l'écran, reste en lecture seule : aucune liste de mots interdits ne distingue un DROP TABLE prévu d'un DROP TABLE accidentel.

L'assistant n'arrive pas les mains vides. Le serveur pose ses conventions dans le contexte du modèle dès la connexion, avant le premier appel : la forme exacte d'une policy d'écriture, le fait qu'un grant n'enlève rien sans revoke, le notify pgrst, 'reload schema' qui suit toute DDL. Ces six pièges ont en commun de répondre 200 OK: un assistant qui les ignore croit avoir réussi. L'outil clicbase_conventions en rend le détail quand il en a besoin.

Le paquet est public et sous licence MIT : npmjs.com/package/@clicbase/mcp · code source sur github.com/clicbase/mcp. Une variante limitée à un seul projet, clicbase-db-mcp, prend CLICBASE_DB_URL et CLICBASE_SERVICE_KEY au lieu de la clé de compte.

Le connecteur hébergé(l'adresse à coller dans claude.ai, sans installation) sert aussi : search_docs (cherche dans cette documentation et les guides du blog, avec le lien de chaque passage), check_domain(le DNS et ce que sert vraiment chaque adresse d'un site, apex et www, avec les enregistrements à poser), get_advisors(l'audit de sécurité et de performance, avec le SQL de correction), get_logs (le journal PostgreSQL : refus de RLS, droits manquants, sans jamais le contenu des lignes), list_migrations et apply_migration (du SQL appliqué avec son historique, dans une transaction), list_storage (compartiments et fichiers), generate_typescript_types (les types de ta base, au format de supabase gen types), list_project_users et manage_project_user (les utilisateurs de ton projet : confirmer, renvoyer un mot de passe, déconnecter, supprimer), create_storage_bucket, upload_storage_file et delete_storage_files (déposer et retirer des fichiers), set_site_domain (le domaine d'un site hébergé), get_billing (ton forfait, tes factures, ta consommation), list_servers et list_dockers (tes serveurs, tes Dockers et la consommation réelle de chaque base), deploy_site, rollback_site et list_deployments (lancer, suivre et annuler un déploiement Git, journal de build compris), list_env_vars, set_env_var et delete_env_var(les variables d'environnement, noms seulement en lecture), list_secrets, set_secret et delete_secret (le coffre lu par tes fonctions), check_email_dns (SPF, DKIM, DMARC) et send_test_email (un essai réel, vers toi seulement), list_email_templates et set_email_template (les modèles de courriel ; un modèle qui perdrait son lien est refusé), get_site_health (le bilan d'un site en un appel), list_mailboxes et create_mailbox(les boîtes mail du domaine ; le mot de passe part par courriel au propriétaire, jamais à l'IA), set_mailbox_enabled, reset_mailbox_password et delete_mailbox(mettre une boîte hors ligne, lui donner un nouveau mot de passe envoyé au propriétaire, la supprimer ; une boîte qui sert d'expéditeur aux courriels d'un projet n'est pas coupée), describe_table (colonnes, clés, index, RLS, policies et droits d'une table), list_sftp_accounts et get_oauth_provider (les comptes SFTP d'un site, la connexion Google d'un projet, sans aucun secret), set_site_maintenance(une page de maintenance à la place de l'accueil), request_ai_credit (demander une recharge du crédit IA, le paiement se fait hors du connecteur), contact_support et list_support_tickets (écrire au support quand un problème ne se règle pas, et suivre la réponse ; le message est signé comme écrit par une IA), set_site_build et set_auto_deploy (la commande de build, le dossier publié, le déploiement à chaque push), set_site_online (un site entièrement hors ligne, ou de retour), set_bucket_public (un compartiment public ou privé ; passer en public demande de retaper son nom), set_signup_confirmation(exiger la confirmation de l'adresse à l'inscription ; refusé si des comptes non confirmés seraient bloqués), set_auth_domain (le domaine de connexion du projet), rename_project et set_mailbox_label (les noms affichés), list_backups et create_backup(les sauvegardes de la base, dont une à la demande avant une migration, et l'état de la copie hors serveur), get_account et update_account(la fiche de ton compte, et son nom ; le mot de passe et l'adresse ne se changent que sur le site), get_ai_usage(ta consommation d'IA par fonction et par utilisateur de ton projet), list_email_log et retry_email (les courriels de ton projet, ceux en échec, et leur relance un par un), list_functions et get_function_logs (tes fonctions et leur journal récent : appels, erreurs, console.log), request_backup_download (télécharger ta base : le lien part par courriel au propriétaire du projet, une heure, une seule fois), pause_project et resume_project(arrêter toute l'API d'un projet, données conservées, puis la relancer), et pour l'IA get_ai_credit, list_ai_instructions et set_ai_instruction (voir la section IA). Sur le connecteur hébergé, chaque outil est annoncé comme lecture ou écriture : claude.ai te demande ta permission avant un outil qui écrit, et tu règles chaque outil dans les paramètres du connecteur. Le mode où les outils qui écrivent sont absents est celui du serveur local, avec CLICBASE_READ_ONLY=1. Un outil ajouté n'apparaît qu'après avoir reconnecté le connecteur ou ouvert une nouvelle conversation.

API REST (tables)

Chaque table de ton schéma public est exposée sur /db/<slug>/<table> avec la syntaxe PostgREST. La sécurité se règle en base (RLS), pas dans ton code.

# Lire (filtres, tri, pagination)
GET /db/<slug>/articles?select=id,title&status=eq.published&order=created_at.desc&limit=10

# Créer
POST /db/<slug>/articles
Content-Type: application/json
{ "title": "Bonjour" }

# Modifier
PATCH /db/<slug>/articles?id=eq.<uuid>
{ "title": "Nouveau titre" }

# Supprimer
DELETE /db/<slug>/articles?id=eq.<uuid>

# En-têtes (toutes les requêtes)
apikey: <clé anon>
Authorization: Bearer <token utilisateur>   # après connexion (sinon rôle anon)

Authentification

Comptes email + mot de passe intégrés, confirmation d'email en option, OAuth Google si configuré. Le token retourné s'utilise en Authorization: Bearer sur l'API REST : la RLS voit alors l'utilisateur connecté.

# Inscription
POST /db/<slug>/auth/signup        { "email": "...", "password": "..." }

# Connexion
POST /db/<slug>/auth/login         { "email": "...", "password": "..." }
# → { "token": "...", "user": { ... } }

# Compatible clients Supabase (GoTrue)
POST /db/<slug>/auth/token?grant_type=password

# Mot de passe oublié / réinitialisation
POST /db/<slug>/auth/recover       { "email": "..." }
POST /db/<slug>/auth/reset         { "token": "...", "password": "..." }

# Utilisateur courant
GET  /db/<slug>/auth/user          (Authorization: Bearer <token>)

# OAuth Google (si activé sur le projet)
GET  /db/<slug>/auth/authorize/google?redirect_to=<url>

Storage (fichiers)

# Envoyer un fichier
POST /db/<slug>/storage/v1/object/<bucket>/<chemin/fichier.jpg>
(corps = fichier binaire, Authorization: Bearer <token>)

# Télécharger / servir
GET  /db/<slug>/storage/v1/object/<bucket>/<chemin>

# Image redimensionnée à la volée
GET  /db/<slug>/storage/v1/render/image/<bucket>/<chemin>?width=400

# Gérer les buckets
GET/POST /db/<slug>/storage/v1/bucket

Les buckets peuvent être publics, privés, ou « owner-scoped » (chaque utilisateur n'écrit que dans son dossier).

Realtime

Abonne-toi aux changements d'une table en websocket :

const ws = new WebSocket(
  "wss://clicbase.com/db/<slug>/realtime?apikey=<clé anon>"
);
ws.onopen = () =>
  ws.send(JSON.stringify({ type: "subscribe", table: "articles" }));
ws.onmessage = (e) => {
  const { event, record } = JSON.parse(e.data); // INSERT | UPDATE | DELETE
  console.log(event, record);
};

Functions

Du code serveur, appelé en HTTP. Les fonctions sont installées par l'équipe Clicbase, sur demande au support : elles s'exécutent sur la plateforme, et aucun compte ne peut en déposer une lui-même.

POST /db/<slug>/functions/v1/<nom-de-la-fonction>
apikey: <clé anon>
Authorization: Bearer <jeton de l'utilisateur connecté, si la fonction l'exige>
{ "n'importe": "quel JSON" }

Une fonction reçoit un contexte ctx et rend { status, body }, jamais un objet Response :

export const requireRole = "authenticated"; // facultatif : exige une session

export default async function handler(ctx) {
  ctx.utilisateur;          // la session vérifiée ({ sub, role… }), si requireRole
  await ctx.db.query(sql);  // la base du projet
  await ctx.secret("NOM");  // un secret du coffre du projet
  ctx.jetonService();       // un jeton service de 60 secondes, jamais copié ailleurs
  await ctx.consigne("ma-fonction", "texte par défaut"); // voir la section IA
  return { status: 200, body: { ok: true } };
}

IA (crédit et consignes)

Tes projets appellent l'IA par Clicbase, sans clé d'Anthropic à gérer. Chaque appel est payé sur ton crédit IA, en euros(au prix d'Anthropic converti au cours du jour, commission Clicbase comprise), partagé par tous tes projets et visible dans Mon compte › IA, avec les modèles disponibles et leur prix. Un projet ne peut pas dépenser plus d'un plafond par jour : une boucle mal écrite ne vide pas ton crédit en une nuit. Crédit épuisé : l'appel rend 402.

# Côté serveur uniquement (une fonction), jamais depuis une page
POST /db/<slug>/ai/v1/messages
Authorization: Bearer <clé service, ou ctx.jetonService() dans une fonction>
{ "model": "claude-sonnet-5-5", "max_tokens": 1024,
  "system": "…", "messages": [{ "role": "user", "content": "Bonjour" }] }

Des consignes que tu modifies toi-même

La consigne est le texte qui dit à l'IA comment travailler. Une fonction la demande avec ctx.consigne("identifiant", "texte par défaut") : dès son premier appel, elle apparaît dans Mon compte › IA › Consignes de tes fonctions. Tu la réécris, tu reviens au texte par défaut, ou tu annules ta dernière modification. Le changement s'applique aux appels suivants dans la minute, sans redéploiement. Le propriétaire du projet et ses membres en Écriture ou Administration peuvent la modifier.

Ton assistant peut faire de même par le connecteur : list_ai_instructions pour les lire, set_ai_instruction pour en remplacer une (avec revenir_au_defaut: truepour revenir au texte d'origine), get_ai_credit pour ton crédit.

Voir qui consomme

Mon compte › IA montre ta consommation par jour, par semaine et par mois, par projet et par fonction. Pour la voir aussi par utilisateur de ton projet, ta fonction ajoute deux en-têtes à son appel :

x-clicbase-utilisateur: <ctx.utilisateur.sub>
x-clicbase-fonction: <nom de la fonction>

Ce n'est qu'une étiquette : elle ne change ni le prix ni le débit. L'adresse de chaque utilisateur est lue dans la base de ton projet.

Faire payer l'IA à tes utilisateurs

Tes utilisateurs n'ont pas de compte Clicbase : ton crédit paie l'IA, et tu fixes ton prix de vente comme tu veux. Pour leur attribuer un solde (« 10 corrections »), garde-le dans une table de ta base, qu'ils peuvent lire mais pas modifier, et fais-le débiter par la fonction, jamais par la page : un contrôle fait seulement dans le navigateur se contourne en appelant l'adresse de la fonction directement. Pense à garder assez de crédit IA pour couvrir ce que tu as vendu.

Pages protégées

Un site statique est servi tel quel : sans protection, n'importe qui peut ouvrir ses pages et télécharger son code. Pour réserver certaines adresses aux comptes connectés de ton projet, ajoute clicbase.json à la racine du dépôt (à côté du dossier publié, pas dedans) :

{ "acces": {
    "proteger": ["/app/*", "/moteur.js"],
    "exiger": ["approuve"],
    "table": "profils"
} }

exiger liste des colonnes booléennes de la table (par défaut profils, dont la clé id est auth.uid()) : toutes doivent valoir true. Elles sont relues dans ta base avec les droits de l'utilisateur, au plus toutes les cinq minutes : retirer un droit ferme l'accès dans ce délai.

Après la connexion, et au chargement si un jeton est déjà en mémoire, ta page envoie la session au serveur, sur son propre domaine. Il pose un cookie invisible au JavaScript, valable 30 jours :

// Connexion : rend { connecte: true, acces: true | false }
await fetch("/_clicbase/session", {
  method: "POST", credentials: "same-origin",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ access_token: jeton }),
});
// État :        GET    /_clicbase/session
// Déconnexion : DELETE /_clicbase/session

Sans session, une page protégée renvoie vers /?acces=connexion-requise (ou /?acces=refuse si les droits manquent), un fichier répond 401 (ou 403).

Pousse clicbase.jsonet l'envoi de la session dans le même commit : le fichier seul fermerait le site à tout le monde. Le déploiement refuse un clicbase.json mal formé, et vérifie que les adresses protégées sont bien refusées sans session avant de mettre la version en ligne.

Ne protège pas le service worker (sw.js), ne mets jamais /_clicbase/sessionen cache, et ne remplace pas une copie en cache par une réponse 302, 401 ou 403. Hors ligne, les fichiers déjà en cache restent servis. La protection empêche l'accès sans compte, pas la copie par un utilisateur légitime.

Emails

Envoi d'emails transactionnels depuis ton projet (adresse à ton domaine), avec modèles gérés dans le dashboard :

# Envoi direct (clé service, côté serveur uniquement)
POST /db/<slug>/email/send
{ "to": "client@example.com", "subject": "...", "html": "..." }

# Envoi d'un modèle (variables substituées)
POST /db/<slug>/email/send-template
{ "template": "welcome", "to": "...", "vars": { "name": "Hina" } }

SDK JavaScript

Un client minimal et typé, sans dépendance :

npm install clicbase-js
import { createClient } from "clicbase-js";

const db = createClient("https://clicbase.com/db/<slug>", "<clé anon>");

// Auth
await db.auth.signUp({ email, password });
const { token, user } = await db.auth.signIn({ email, password });

// Tables (la RLS s'applique automatiquement au token)
const articles = await db.from("articles")
  .select("id,title").eq("status", "published")
  .order("created_at", { ascending: false }).limit(10).get();

await db.from("articles").insert({ title: "Bonjour" });

// Storage
await db.storage.upload("photos", "avatars/moi.jpg", file);
const url = db.storage.publicUrl("photos", "avatars/moi.jpg");

Tu utilises déjà @supabase/supabase-js ? Il fonctionne en pointant son URL sur https://clicbase.com/db/<slug> : l'API REST et le login (grant_type=password) suivent les mêmes conventions. C'est aussi la voie de la migration depuis Supabase, dans les deux sens.

Une question, un endpoint manquant dans cette doc ? contact@clicbase.com· réponse rapide, c'est le fondateur qui lit.