Aller au contenu principal

API publique

Le helpdesk expose une seule API publique : un point de terminaison GraphQL. Chaque opération du schéma est accessible en dehors du navigateur avec un jeton d'accès personnel (Personal Access Token, PAT).

Cette page est le contrat — ce qui est stable, comment s'authentifier, ce qu'accordent les portées et la forme des erreurs. La CLI hdmx est bâtie dessus, et le versionnage de l'API explique comment cette surface évolue.

Le point de terminaison​

POST https://<your-host>/backend/<subdomain>/api/<version>/graphql
  • <subdomain> identifie votre compte (le même identifiant que celui utilisé pour vous connecter).
  • <version> est une version datée de l'API telle que 2026-07 — voir versionnage de l'API. Vous pouvez aussi omettre le segment de version et appeler …/backend/<subdomain>/graphql ; la requête est alors servie par la plus ancienne version prise en charge, et chaque réponse vous indique quelle version y a répondu.

Authentification​

Envoyez un jeton d'accès personnel comme identifiant Bearer :

Authorization: Bearer hdmx_<random>

Un PAT est de longue durée (avec une expiration obligatoire), doté de portées, restreint par IP et révocable. Il est lié à un seul compte : présenter un jeton au sous-domaine d'un autre compte est rejeté avec un HTTP 401.

Créer un jeton​

Créez et gérez vos jetons vous-même dans l'application agent, sous Settings → API tokens :

  1. Cliquez sur New token.
  2. Donnez-lui un nom que vous reconnaîtrez plus tard.
  3. Choisissez une expiration (jusqu'à 365 jours — il n'y a pas de « jamais »).
  4. Définissez une politique d'accès — activez exactement les ressources dont ce jeton a besoin, en lecture ou en lecture-écriture. Par défaut, aucun accès n'est accordé.
  5. Ajoutez au moins une adresse IP ou un CIDR à la liste d'autorisation (obligatoire — voir Liste d'IP autorisées). Le formulaire peut ajouter votre IP actuelle à votre place.
  6. Vérifiez et créez. Le jeton complet n'est affiché qu'une seule fois — copiez-le immédiatement. Seule une empreinte est stockée, il ne peut donc jamais être réaffiché ; si vous le perdez, faites une rotation du jeton pour en obtenir un nouveau.

Vous pourrez ensuite modifier la politique d'accès et la liste d'autorisation d'un jeton, le faire tourner (émettre un nouveau secret pendant que l'ancien continue de fonctionner durant un court délai de grâce, afin de basculer sans interruption) et le révoquer. Chaque jeton indique quand et depuis où il a été utilisé pour la dernière fois, ainsi qu'un journal d'activité récente.

Portées​

L'accès est à gros grain et structuré par familles de ressources. Un jeton porte un ensemble fixe de portées ; chaque opération de premier niveau en vérifie une.

PortéeAccorde
tickets:readLire les tickets, messages, brouillons, historiques, pièces jointes, rapports
tickets:writeCréer / mettre à jour / supprimer des tickets, publier des messages, téléverser des pièces jointes. Implique tickets:read
chat:readLire les chats, conversations, messages, sessions
chat:writeEnvoyer des messages d'agent, rejoindre / quitter / fermer / rouvrir des conversations. Implique chat:read
customers:readLire les fiches clients
customers:writeCréer / mettre à jour / fusionner des fiches clients. Implique customers:read
settings:readLire les paramètres du compte (services, statuts, priorités, boîtes mail — sans les secrets)

Les champs d'identifiants des boîtes mail (noms d'utilisateur, mots de passe et hôtes IMAP/SMTP) ne sont jamais renvoyés à un jeton, quelle que soit la portée — ces secrets appartiennent à votre fournisseur de messagerie et restent hors de portée de l'API publique.

Taxonomie des erreurs​

Les réponses GraphQL utilisent errors[].extensions.code :

CodeSignificationHTTP
SCOPE_DENIEDLe jeton n'a pas la portée requise. extensions.valid_values liste la ou les portées qui satisferaient l'opération200
RATE_LIMITEDLe budget de requêtes par jeton est épuisé. extensions.retryAfter correspond au nombre de secondes avant réinitialisation, et l'en-tête Retry-After porte le même nombre429
INVALID_API_TOKENJeton introuvable, mal formé ou inutilisable pour une autre raison200

Les jetons révoqués, expirés, les incohérences entre comptes et les refus par liste d'IP autorisées sont rejetés avant l'exécution de la requête et se manifestent par un HTTP 401 sans corps GraphQL. Traitez un 401 sur une requête avec jeton comme « ce jeton n'est plus valide pour cette requête — vérifiez son expiration, ses portées et sa liste d'IP autorisées ».

Limites de débit​

Chaque requête avec jeton porte des en-têtes :

X-RateLimit-Limit: 120
X-RateLimit-Remaining: 96
X-RateLimit-Reset: 1715692800

Le budget par défaut est de 120 requêtes par minute et par jeton. Chaque requête coûte 1, qu'elle lise ou qu'elle écrive. Un dépassement renvoie un HTTP 429 avec un en-tête Retry-After et extensions.code = "RATE_LIMITED" ; un client bien élevé patiente jusqu'à l'instant de réinitialisation.

Liste d'IP autorisées​

Chaque jeton créé en libre-service doit porter au moins une IP ou un CIDR — il n'existe pas de jetons « toutes IP ». L'IP source de chaque requête est comparée à la liste du jeton ; une non-correspondance est rejetée avec un HTTP 401 et extensions.sub_code = "ip_not_allowed".

  • IPv4, IPv6 et plages CIDR sont tous acceptés. Utilisez /32 (IPv4) ou /128 (IPv6) pour un hôte unique.
  • Les plages sont validées et normalisées lors de l'enregistrement.
  • La liste d'autorisation est modifiable après création — la modifier ne change pas le secret.

Utilisez un jeton depuis un point de sortie stable : un runner CI, une tâche planifiée, un NAT de bureau fixe ou vos propres serveurs sur une plage d'IP connue. Un jeton divulgué est inutilisable depuis n'importe où en dehors de sa liste d'autorisation.

Activité du jeton​

Chaque requête authentifiée effectuée par un jeton est enregistrée : l'heure, le nom de l'opération, l'IP source, le résultat (succès, erreur, limite de débit, portée refusée, révoqué ou expiré) et la latence aller-retour. C'est la piste à consulter si un jeton se comporte de manière inattendue.

Versionnage et dépréciation​

L'API est versionnée avec des versions datées YYYY-MM. Épinglez-en une dans l'URL et elle reste stable pendant au moins 12 mois ; les dépréciations sont annoncées dans le flux avant que quoi que ce soit que vous utilisiez ne disparaisse. Voir Versionnage de l'API pour la politique complète.

Découverte du schéma​

Le schéma est auto-descriptif : utilisez l'introspection GraphQL standard sur le point de terminaison versionné pour générer des types et explorer la surface. Le GraphQL Playground interactif est disponible à l'adresse …/backend/<subdomain>/playground.

Démarrage rapide avec curl​

Ces exemples supposent l'hôte helpdesk.example.com, le sous-domaine acme, la version 2026-07 et un jeton dans $TOKEN.

Lire l'utilisateur courant (n'importe quelle portée)​

curl -sS https://helpdesk.example.com/backend/acme/api/2026-07/graphql \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"query":"{ currentUser { id email firstname lastname } }"}'

Lister les tickets récents (tickets:read)​

curl -sS https://helpdesk.example.com/backend/acme/api/2026-07/graphql \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"query": "query ($first: Int!) {
ticketList(first: $first) {
edges { node { code subject status { name } updatedAt } }
}
}",
"variables": { "first": 10 }
}'

Publier une réponse publique (tickets:write)​

curl -sS https://helpdesk.example.com/backend/acme/api/2026-07/graphql \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"query": "mutation ($input: SaveTicketInput!) {
saveTicket(input: $input) {
ticket { code }
error { code message }
}
}",
"variables": {
"input": {
"id": "TICKET-UUID-HERE",
"message": "Thanks — fixing this now.",
"messageType": "PUBLIC"
}
}
}'

Pour l'usage quotidien, la CLI hdmx enveloppe toute cette surface derrière des commandes ergonomiques.