L'API REST

Lire et écrire dans OIO depuis vos propres outils.

L'API REST expose les pages, les blocs, les bases de données et les commentaires en JSON. Elle s'authentifie avec une clé d'API.

S'authentifier

Deux en-têtes sont acceptés, au choix :

Authorization: Bearer oio_votre_cle
X-API-Key: oio_votre_cle

Une requête sans clé valide reçoit un code 401. Une requête portant sur une ressource hors du périmètre de la clé reçoit un 403 — et jamais le contenu.

Principaux points d'entrée

Méthode et chemin Effet
GET /api/search?q=… Rechercher dans les titres et les contenus
GET /api/pages/{id}/blocks Lire les blocs d'une page
POST /api/pages/{id}/blocks Ajouter un bloc
PATCH /api/blocks/{id} Modifier un bloc
DELETE /api/blocks/{id} Supprimer un bloc
POST /api/pages/{id}/blocks/reorder Réordonner les blocs
GET /api/databases Lister les bases de données
GET /api/databases/{id}/rows Lire les lignes d'une base
GET /api/databases/{id}/rows/count Compter les lignes d'une base
POST /api/databases/{id}/rows Ajouter une ligne
POST /api/databases/{id}/rows/upsert Ajouter ou mettre à jour une ligne
DELETE /api/databases/{id}/rows/{pageId} Mettre une ligne à la corbeille
GET /api/notifications Lire vos notifications
POST /api/pages/{id}/comments Déposer un commentaire

Exemple

curl -H "Authorization: Bearer oio_votre_cle" \
     https://votre-instance/api/pages/42/blocks

Lire les lignes d'une grande base par pages

GET /api/databases/{id}/rows renvoie, par défaut, toutes les lignes de la base dans un tableau JSON — le format d'origine, inchangé pour ne casser aucun script existant. Pour lire une grande base par pages, ajoutez page (à partir de 1) et, si besoin, pageSize (50 par défaut, 200 au maximum) :

GET /api/databases/12/rows?page=1&pageSize=50

Dès que page est présent, la réponse change de forme : un objet plutôt qu'un tableau nu, recherche/filtres/tri (mêmes paramètres search, filters, sorts qu'en mode non paginé) appliqués avant la découpe en pages, jamais sur un sous-ensemble déjà chargé :

{
  "rows": [ /* au plus pageSize lignes */ ],
  "page": 1,
  "pageSize": 50,
  "total": 340,
  "matchedTotal": 340
}

total est le nombre de lignes de la base (l'archive comprise seulement si includeArchived=1) ; matchedTotal est le nombre de lignes qui correspondent à la recherche/aux filtres en cours — les deux nombres du compteur « 12 sur 340 » affiché dans l'interface. GET /api/databases/{id}/rows/count (mêmes paramètres) renvoie ces deux nombres seuls, sans charger aucune ligne — pratique pour n'afficher qu'un total.

Les colonnes Relation (elle référence des pages par identifiant, pas par titre) et Formule (calculée, jamais stockée) ne sont pas triables ni filtrables en pagination : un tri ou un filtre qui les cible est silencieusement ignoré plutôt que de produire un résultat trompeur.

Synchroniser un tableau depuis un autre outil

Quand une autre application tient la donnée de référence — vos inscrits, vos clients, vos commandes — vous voulez que le tableau OIO en reflète l'état, sans le dupliquer à chaque envoi. C'est le rôle de l'upsert : vous désignez une colonne qui identifie la ligne (souvent l'adresse e-mail), et OIO se charge de créer la ligne la première fois, puis de la mettre à jour les fois suivantes.

Les colonnes se désignent par leur nom, tel qu'il apparaît en en-tête de votre tableau.

curl -X POST \
     -H "X-API-Key: oio_votre_cle" \
     -H "Content-Type: application/json" \
     -d '{
           "matchProperty": "Email",
           "matchValue": "jean@exemple.fr",
           "title": "Jean Dupont",
           "values": {
             "Email": "jean@exemple.fr",
             "Inscription": "En cours",
             "Rôle": "Développeur"
           }
         }' \
     https://votre-instance/api/databases/12/rows/upsert

La réponse indique si la ligne vient d'être créée, et signale les colonnes que votre envoi mentionnait sans qu'elles existent dans le tableau :

{
  "pageId": 87,
  "title": "Jean Dupont",
  "created": true,
  "matchedCount": 0,
  "values": { "Email": "jean@exemple.fr", "Inscription": "En cours", "Rôle": "Développeur" },
  "ignoredProperties": []
}

Quelques comportements à connaître avant de brancher votre synchronisation :

  • Seules les colonnes que vous envoyez sont écrites. Celles que vous omettez gardent leur valeur : vous pouvez donc n'envoyer que ce qui a changé, et laisser intactes les colonnes que vos collègues remplissent à la main dans OIO.
  • Une valeur vide efface la colonne. C'est ce qui permet de propager une donnée supprimée chez vous. Pour ne pas toucher une colonne, ne la mentionnez pas du tout.
  • Une colonne inconnue n'interrompt rien. Son nom est renvoyé dans ignoredProperties : le reste de la ligne est bien écrit, et vous voyez immédiatement quel intitulé ne correspond à rien — le plus souvent une faute de frappe ou un accent.
  • Une colonne « Auteur » ne s'écrit jamais. Elle reflète automatiquement le créateur de la ligne et refuse toute valeur envoyée — elle apparaît elle aussi dans ignoredProperties, comme une colonne inconnue.
  • La valeur par défaut d'une liste déroulante ne s'applique pas ici. Elle est réservée aux lignes créées depuis le tableau ; l'upsert n'écrit que ce que vous transmettez.
  • Les listes de choix s'enrichissent toutes seules. Une valeur absente des options existantes y est ajoutée, avec une couleur prise dans la palette. Vous n'avez pas à déclarer vos statuts à l'avance.
  • La casse compte, par défaut : Jean@Exemple.fr et jean@exemple.fr sont deux lignes distinctes. Ajoutez "caseSensitive": false pour que les deux graphies désignent la même.
  • En cas de doublons, c'est la ligne la plus ancienne qui est mise à jour, et matchedCount vous indique combien de lignes correspondaient — un chiffre supérieur à 1 signale un ménage à faire dans le tableau.

Une requête sans colonne clé, sans valeur de correspondance, ou désignant une colonne clé qui n'existe pas dans le tableau, est refusée avec un code 400 et un message explicite.

Ce qui est refusé, et pourquoi

Une clé en lecture seule reçoit un 403 sur toute écriture, y compris un commentaire. Une clé restreinte à certaines pages ne voit pas les autres — elles n'apparaissent ni dans les recherches, ni dans les listes, ni dans les autocomplétions. Une page absente d'un résultat peut donc simplement être hors du périmètre de la clé.

Confirmation