API Tickets (externe)

Consultez et répondez aux tickets ouverts sur votre serveur Discord depuis un autre site — la conversation (texte, images, fichiers) est relue en direct depuis Discord.

L'historique des messages n'est plus dupliqué nulle part : l'API va chercher la vraie conversation directement sur Discord à chaque appel (texte, images et fichiers en pièce jointe, avec leurs liens de téléchargement). Le bot n'a plus qu'à enregistrer la création et la fermeture d'un ticket — pas chaque message.

Authentification

Comme le reste de l'API BotHost, chaque requête doit inclure un code d'accès et l'identifiant du bot (son client_id), en paramètres GET — y compris sur les routes qui acceptent aussi du POST.

  • code-check — le code d'accès partagé de la plateforme
  • Id-bot — le client_id du bot concerné

Lister les tickets

GET /api/tickets/list.php?code-check=...&Id-bot=...

Paramètre optionnel status (open ou closed) pour filtrer. Renvoie les métadonnées de chaque ticket (léger, pas les messages).

{ "tickets": [ { "channel_id": "1234567890", "guild_id": "...", "subject": "Support", "status": "open", "user_id": "9876543210", "user_tag": "pseudo_discord", "created_at": "2026-01-10T14:32:00+00:00", "closed_at": null, "closed_by": null } ] }

Obtenir un ticket avec sa conversation

GET /api/tickets/get.php?code-check=...&Id-bot=...&channel-id=...

Renvoie le ticket avec l'historique complet des messages (utilisateur, staff et bot), dans l'ordre chronologique, lu en direct depuis Discord — y compris les pièces jointes.

{ "ticket": { "channel_id": "1234567890", "subject": "Support", "status": "open", "user_tag": "pseudo_discord", "messages": [ { "id": "...", "author_name": "pseudo_discord", "is_bot": false, "content": "Bonjour, j'ai un problème avec...", "attachments": [ { "filename": "capture.png", "url": "https://cdn.discordapp.com/attachments/.../capture.png?ex=...", "content_type": "image/png", "is_image": true, "size": 245812 } ], "created_at": "2026-01-10T14:33:12.000000+00:00" } ], "messages_error": null } }

messages_error est renseigné (et messages vide) si le salon Discord n'existe plus — typique d'un ticket fermé dont le salon a ensuite été supprimé.

Répondre à un ticket

POST /api/tickets/reply.php?code-check=...&Id-bot=...

Corps de la requête (JSON ou form-data) :

  • channel_id — le salon Discord du ticket
  • staff_name — le pseudo du membre du staff qui répond depuis le site
  • message — le contenu de la réponse (3800 caractères maximum)
  • reply_to — optionnel, l'id d'un message précis auquel répondre (comme la fonction "Répondre" de Discord) — récupérable dans messages[].id renvoyé par get.php

Le bot poste alors immédiatement un embed dans le salon Discord du ticket, avec le message et le nom du staff qui a répondu. Rien à enregistrer côté API : ce message fera naturellement partie de la conversation au prochain appel à get.php.

Fermer un ticket

POST /api/tickets/close.php?code-check=...&Id-bot=...

Corps : channel_id (obligatoire), staff_name (optionnel). Marque le ticket comme fermé et poste un message de fermeture dans le salon.

Côté bot : enregistrer un ticket

Il suffit d'écrire dans data/open_tickets.json (chemin attendu : {dossier-du-bot}/bot/data/open_tickets.json) à deux moments : quand un ticket est créé, et quand il est fermé. Pas besoin de logguer chaque message — get.php s'en charge en lisant Discord directement.

const fs = require('fs'); const path = require('path'); const registryFile = path.join(__dirname, 'data', 'open_tickets.json'); function loadRegistry() { try { return JSON.parse(fs.readFileSync(registryFile, 'utf8')); } catch (e) { return {}; } } function saveRegistry(data) { fs.mkdirSync(path.dirname(registryFile), { recursive: true }); fs.writeFileSync(registryFile, JSON.stringify(data, null, 2)); } // À la création d'un ticket : function registerTicket(channelId, { guildId, userId, userTag, subject }) { const tickets = loadRegistry(); tickets[channelId] = { guild_id: guildId, user_id: userId, user_tag: userTag, subject: subject, created_at: new Date().toISOString(), status: 'open', closed_at: null, }; saveRegistry(tickets); } // À la fermeture (avant de supprimer le salon, sinon l'historique // ne sera plus consultable par le site) : function markTicketClosed(channelId, closedByTag = null) { const tickets = loadRegistry(); if (!tickets[channelId]) return; tickets[channelId].status = 'closed'; tickets[channelId].closed_at = new Date().toISOString(); tickets[channelId].closed_by = closedByTag; saveRegistry(tickets); }

C'est exactement l'intégration déjà en place dans la commande /tickets du bot BotHost — deux appels ajoutés, rien d'autre à changer dans la logique existante.

Où voir ça en action

  • Dans le panelTickets ouverts, dans la sidebar du dashboard d'un bot
  • Identifiants de ce bot — page API du dashboard (Client ID, code-check, testeur intégré)
  • Exemple de site externe/external-example-tickets/, une page autonome (HTML/JS pur) qui consomme cette API depuis l'extérieur, à copier comme point de départ