GPChat Developers
Documentation · v1.1

Construire sur GPChat

Créez des bots qui discutent avec vos utilisateurs, automatisez des groupes et des listes de diffusion, publiez des stories et connectez vos services au compte GPChat de vos utilisateurs.

URL de base

EnvironnementURLConsole
Productionhttps://api.gpchat.appconsole.html

Il n'existe pas d'environnement de staging ouvert aux développeurs tiers : l'app mobile GPChat distribuée sur les stores ne peut se connecter qu'à la production, il n'y a donc aucun moyen d'échanger de vrais messages ailleurs qu'en production. Pour tester sans risque, voir la FAQ : votre bot n'est trouvable par personne tant que vous n'avez pas partagé son nom d'utilisateur, vous pouvez donc développer et tester directement en production, avec votre propre compte.

Démarrage rapide

  1. Connectez-vous à la console développeur en scannant le QR code avec l'app GPChat (Paramètres → GPChat pour Développeurs → Connexion Web), ou avec Google.
  2. Créez une application avec « Créer le bot » coché. Copiez immédiatement la clé API (gpk_live_…) et le secret de webhook : ils ne seront plus réaffichés.

    Depuis votre téléphone, vous pouvez aussi écrire à @gpchat_forge et taper /newbot.

  3. Exposez un endpoint HTTPS qui reçoit les webhooks, puis renseignez son URL dans l'onglet Webhooks et cliquez sur « Envoyer un ping ».
  4. Écrivez à votre bot depuis l'app GPChat (recherchez son nom d'utilisateur). Votre serveur reçoit un événement message.received.
  5. Répondez avec l'API, en utilisant data.chatId comme destinataire :
import express from 'express';
import crypto from 'node:crypto';

const app = express();
const { GPCHAT_API_KEY, GPCHAT_WEBHOOK_SECRET } = process.env;

// Corps BRUT requis pour vérifier la signature
app.post('/gpchat/webhook', express.raw({ type: 'application/json' }), async (req, res) => {
  if (!verify(req.body, req.get('X-GPChat-Signature'), GPCHAT_WEBHOOK_SECRET)) {
    return res.sendStatus(401);
  }
  res.sendStatus(200); // accuser réception tout de suite (< 10 s)

  const { type, data } = JSON.parse(req.body);
  if (type === 'message.received' && data.textMsg === '/start') {
    await fetch('https://api.gpchat.app/v1/messages', {
      method: 'POST',
      headers: { Authorization: `Bearer ${GPCHAT_API_KEY}`, 'Content-Type': 'application/json' },
      body: JSON.stringify({ to: data.chatId, type: 'text', content: 'Bienvenue ! 👋' }),
    });
  }
});

function verify(rawBody, header, secret) {
  const m = /^t=(\d+),v1=([0-9a-f]{64})$/.exec(header || '');
  if (!m || Math.abs(Date.now() / 1000 - Number(m[1])) > 300) return false;
  const expected = crypto.createHmac('sha256', secret).update(`${m[1]}.${rawBody}`).digest();
  return crypto.timingSafeEqual(expected, Buffer.from(m[2], 'hex'));
}

app.listen(3000);
import hmac, hashlib, json, os, re, time
import requests
from flask import Flask, request, abort

app = Flask(__name__)
API_KEY = os.environ["GPCHAT_API_KEY"]
SECRET = os.environ["GPCHAT_WEBHOOK_SECRET"].encode()

def verify(raw: bytes, header: str) -> bool:
    m = re.fullmatch(r"t=(\d+),v1=([0-9a-f]{64})", header or "")
    if not m or abs(time.time() - int(m[1])) > 300:
        return False
    expected = hmac.new(SECRET, f"{m[1]}.".encode() + raw, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, m[2])

@app.post("/gpchat/webhook")
def webhook():
    raw = request.get_data()  # corps brut
    if not verify(raw, request.headers.get("X-GPChat-Signature")):
        abort(401)
    event = json.loads(raw)
    if event["type"] == "message.received" and event["data"].get("textMsg") == "/start":
        requests.post(
            "https://api.gpchat.app/v1/messages",
            headers={"Authorization": f"Bearer {API_KEY}"},
            json={"to": event["data"]["chatId"], "type": "text", "content": "Bienvenue ! 👋"},
            timeout=10,
        )
    return "", 200
curl -X POST https://api.gpchat.app/v1/messages \
  -H "Authorization: Bearer $GPCHAT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"to": "USER_ID", "type": "text", "content": "Bienvenue ! 👋"}'

Concepts clés

ConceptDescription
ApplicationConteneur de vos identifiants : App ID (= client_id OAuth2), client_secret, clé(s) API, webhook et secret de signature. 20 applications maximum par compte.
BotUn vrai compte GPChat (isBot: true) rattaché à une application (un seul par application). Il reçoit et envoie des messages comme un utilisateur, en privé ou dans les groupes dont il est membre.
Clé APIgpk_live_… — identifie le bot. Scopes par défaut : messages:send, groups:manage. Seule son empreinte SHA-256 est stockée : elle n'est affichée qu'une fois.
Token OAuth2gpat_… (accès, 1 h) et gprt_… (rafraîchissement, 30 jours) — permettent d'agir au nom d'un utilisateur qui a donné son consentement.
WebhookURL HTTPS de votre serveur, appelée à chaque message reçu par votre bot. Signée en HMAC-SHA256.
userIdIdentifiant stable d'un utilisateur GPChat. Vous l'obtenez via les webhooks (from, chatId) ou via /oauth/userinfo (sub).

Authentification

Chaque requête porte un jeton dans l'en-tête Authorization :

Authorization: Bearer gpk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
EndpointClé API (bot)Token OAuth2 (utilisateur)Scope requis
/v1/messages, /v1/messages/group✅—messages:send
/v1/groups/*✅✅groups:manage
/v1/stories, /v1/stories/delete—✅stories:write
/v1/stories/mine—✅stories:read
/v1/polls/*✅✅polls:write
/oauth/userinfo—✅profile

Ne jamais exposer une clé API ou un client_secret côté client (navigateur, app mobile, dépôt de code). Appelez l'API depuis votre serveur uniquement.

Créer et gérer un bot

Dans la console, onglet Bot :

Avec @gpchat_forge sur mobile : /newbot, /mybots, /token, /setdescription, /setcommands, /setwebhook, /cancel. Le nom d'utilisateur doit se terminer par bot (5 à 32 caractères, minuscules/chiffres/_) et ne peut pas contenir « gpchat ».

/token révoque immédiatement la clé en cours et en émet une nouvelle.

Envoyer des messages

POST/v1/messagesClé API · messages:send

Message privé. Champ to = userId du destinataire, plus les champs du type choisi.

POST/v1/messages/groupClé API · messages:send

Message de groupe. Champ groupId à la place de to. Le bot doit être membre (et administrateur si le groupe est en « seuls les admins écrivent »).

Réponse — 201 Created

{
  "status": "success",
  "message": "Opération réussie",
  "data": { "msgId": "8f14e45f-ceea-4e7b-9a4c-1b2c3d4e5f60" },
  "timestamp": 1735000000000
}

Conservez msgId : il sert à citer ce message plus tard (replyToMsgId) et à le retrouver dans les webhooks.

Tous les types de messages

Les messages de votre bot s'affichent dans l'app exactement comme ceux d'un utilisateur. Toutes les URL doivent être en https:// et publiquement accessibles : l'app les télécharge directement (hébergez-les sur votre CDN, S3, Cloudinary…).

typeChamps requisOptionnelsRendu dans l'app
textcontent (≤ 4 096)—Bulle de texte, liens cliquables
imagefileUrlcaption (≤ 1 024)Photo, ouverture plein écran
videofileUrl, thumbnailUrlcaptionMiniature + lecteur vidéo
audiofileUrl—Lecteur audio
docfileUrlcaptionCarte document téléchargeable
giffileUrl—GIF animé
stickerfileUrl (PNG/WebP transparent)—Sticker sans bulle
locationlocation.latitude, location.longitudelocation.name, location.address (≤ 256)Carte avec itinéraire

Tous les types acceptent replyToMsgId. Pour un sondage, utilisez /v1/polls.

Texte

{ "to": "USER_ID", "type": "text", "content": "Votre colis arrive demain entre 9h et 12h 🚚" }

Image avec légende

{ "to": "USER_ID", "type": "image", "fileUrl": "https://cdn.example.com/recu-042.jpg", "caption": "Votre reçu" }

Vidéo

{
  "to": "USER_ID",
  "type": "video",
  "fileUrl": "https://cdn.example.com/tuto.mp4",
  "thumbnailUrl": "https://cdn.example.com/tuto.jpg",
  "caption": "Tutoriel en 60 secondes"
}

Audio

{ "to": "USER_ID", "type": "audio", "fileUrl": "https://cdn.example.com/annonce.m4a" }

Document

{ "to": "USER_ID", "type": "doc", "fileUrl": "https://cdn.example.com/facture-042.pdf", "caption": "Facture n°042" }

GIF et sticker

{ "to": "USER_ID", "type": "gif", "fileUrl": "https://media.giphy.com/media/l0MYt5jPR6QX5pnqM/giphy.gif" }
{ "to": "USER_ID", "type": "sticker", "fileUrl": "https://cdn.example.com/stickers/merci.webp" }

Position

{
  "to": "USER_ID",
  "type": "location",
  "location": { "latitude": 4.0511, "longitude": 9.7679, "name": "Agence Akwa", "address": "Bd de la Liberté, Douala" }
}

Formats recommandés : images JPEG/PNG/WebP (≤ 10 Mo), vidéos MP4 H.264 (≤ 50 Mo), audio M4A/MP3, documents PDF. L'API ne télécharge pas vos fichiers : c'est l'app de l'utilisateur qui le fait.

Réponses et mentions

Citer un message (réponse)

Ajoutez replyToMsgId : le message cité apparaît au-dessus de votre réponse. Il doit appartenir à la même conversation (sinon 404). Utilisez le msgId reçu dans le webhook.

{ "to": "USER_ID", "type": "text", "content": "C'est noté, je m'en occupe !", "replyToMsgId": "MSG_ID_RECU" }

Mentionner des membres (groupes)

mentionedUserIds (50 max) — uniquement dans un groupe, et uniquement des membres du groupe.

{ "groupId": "GROUP_ID", "type": "text", "content": "@Alice ton ticket est résolu ✅", "mentionedUserIds": ["USER_ID_ALICE"] }

Règles et anti-spam

Pour protéger les utilisateurs, les bots suivent des règles strictes, vérifiées côté serveur :

RègleErreur
Un bot ne peut écrire en privé qu'à un utilisateur qui lui a déjà écrit au moins une fois. Un bot répond, il ne démarche pas.400 failed-precondition
Aucun message à un utilisateur qui a bloqué le bot.403 permission-denied
Un bot ne peut pas écrire à un autre bot (évite les boucles).403 permission-denied
Un bot ne peut ajouter à un groupe (création ou ajout de membres) que des utilisateurs qui lui ont déjà écrit.403 permission-denied
Un bot par application, 20 applications par compte.409 / 429

Pour permettre à vos utilisateurs de démarrer une conversation, partagez le nom d'utilisateur de votre bot ou publiez-le dans le Store des bots. La première action de l'utilisateur (même /start) ouvre la conversation.

Webhooks

Configurez l'URL dans la console (onglet Webhooks). GPChat y envoie un POST JSON pour chaque message reçu par votre bot.

En-têtes

En-têteValeur
Content-Typeapplication/json
X-GPChat-Eventmessage.received, group_message.received ou ping
X-GPChat-Signaturet=<timestamp_unix>,v1=<hmac_sha256_hex>

Événements et charge utile

Le corps a toujours la forme { "type": "<événement>", "data": { … } }.

message.received

{
  "type": "message.received",
  "data": {
    "msgId": "8f14e45f-ceea-4e7b-9a4c-1b2c3d4e5f60",
    "type": "text",
    "textMsg": "/start",
    "from": "USER_ID",
    "chatId": "USER_ID",
    "sentAt": 1735000000000
  }
}

group_message.received

{
  "type": "group_message.received",
  "data": {
    "msgId": "MSG_ID",
    "type": "image",
    "textMsg": "",
    "fileUrl": "https://…/photo.jpg",
    "caption": "Regardez ça",
    "from": "USER_ID",
    "groupId": "GROUP_ID",
    "mentionedUserIds": ["BOT_USER_ID"],
    "isMentioned": true,
    "replyToMsgId": "MSG_ID_CITE",
    "sentAt": 1735000000000
  }
}
ChampDescription
msgIdIdentifiant unique du message. Utilisez-le pour dédoublonner : une même livraison peut être rejouée.
typetext, image, video, audio, doc, gif, sticker, location, poll
textMsgTexte en clair (déchiffré par GPChat avant l'envoi).
fileUrl, caption, gifUrl, videoThumbnailMédias, selon le type.
location{ latitude, longitude, name?, address? }
pollSondage (question, options).
replyToMsgIdMessage cité par l'utilisateur, le cas échéant.
fromuserId de l'expéditeur.
chatIdMessages privés : à utiliser comme to pour répondre.
groupId, mentionedUserIds, isMentionedMessages de groupe ; isMentioned vaut true si votre bot est mentionné.
sentAtDate d'envoi (epoch ms).
sandboxtrue uniquement pour un événement simulé depuis la console.

Vérifier la signature

La signature est un HMAC-SHA256 de {timestamp}.{corps brut}, calculé avec le secret de webhook de votre application. Vérifiez-la avant tout traitement, sur le corps brut (jamais sur un JSON re-sérialisé), en temps constant, et rejetez un timestamp de plus de 5 minutes (protection contre le rejeu).

import crypto from 'node:crypto';

export function verifyGpchatSignature(rawBody, header, secret, toleranceSec = 300) {
  const m = /^t=(\d+),v1=([0-9a-f]{64})$/.exec(header || '');
  if (!m) return false;
  if (Math.abs(Date.now() / 1000 - Number(m[1])) > toleranceSec) return false;
  const expected = crypto.createHmac('sha256', secret).update(`${m[1]}.${rawBody}`).digest();
  return crypto.timingSafeEqual(expected, Buffer.from(m[2], 'hex'));
}
import hashlib, hmac, re, time

def verify_gpchat_signature(raw_body: bytes, header: str, secret: str, tolerance: int = 300) -> bool:
    m = re.fullmatch(r"t=(\d+),v1=([0-9a-f]{64})", header or "")
    if not m or abs(time.time() - int(m[1])) > tolerance:
        return False
    expected = hmac.new(secret.encode(), f"{m[1]}.".encode() + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, m[2])
<?php
function verifyGpchatSignature(string $rawBody, ?string $header, string $secret, int $tolerance = 300): bool {
    if (!preg_match('/^t=(\d+),v1=([0-9a-f]{64})$/', $header ?? '', $m)) return false;
    if (abs(time() - (int) $m[1]) > $tolerance) return false;
    $expected = hash_hmac('sha256', $m[1] . '.' . $rawBody, $secret);
    return hash_equals($expected, $m[2]);
}
// $rawBody = file_get_contents('php://input');
// $header  = $_SERVER['HTTP_X_GPCHAT_SIGNATURE'] ?? null;
func VerifyGpchatSignature(rawBody []byte, header, secret string) bool {
	re := regexp.MustCompile(`^t=(\d+),v1=([0-9a-f]{64})$`)
	m := re.FindStringSubmatch(header)
	if m == nil {
		return false
	}
	ts, _ := strconv.ParseInt(m[1], 10, 64)
	if math.Abs(float64(time.Now().Unix()-ts)) > 300 {
		return false
	}
	mac := hmac.New(sha256.New, []byte(secret))
	mac.Write([]byte(m[1] + "."))
	mac.Write(rawBody)
	expected := hex.EncodeToString(mac.Sum(nil))
	return hmac.Equal([]byte(expected), []byte(m[2]))
}

Réessais et monitoring

En cas d'échec (code non-2xx, délai dépassé, erreur réseau), GPChat réessaie avec un repli exponentiel : 30 s, 1 min, 2 min, 4 min… jusqu'à 1 h entre deux essais, soit 8 tentatives. Au-delà, l'événement est marqué abandoned et vos contacts d'alerte (e-mail, webhook Slack/Discord) sont prévenus.

L'onglet Monitoring de la console affiche le taux de succès, la latence moyenne de votre serveur, l'activité horaire, la consommation du quota et le journal détaillé des 100 derniers événements (charge utile, réponse de votre serveur, signature), exportable en JSON ou CSV.

Groupes & diffusion

Acteur : votre bot (clé API) ou un utilisateur (token OAuth2, scope groups:manage). Le créateur devient l'unique administrateur. Un groupe isBroadcast: true est une liste de diffusion, où seul son créateur écrit.

MéthodeEndpointCorpsDroits
POST/v1/groupsname (≤ 100), description? (≤ 500), members? (≤ 100), isBroadcast?, photoUrl?Tous
DELETE/v1/groups/deletegroupIdAdmin
POST/v1/groups/membersgroupId, memberIds (1 à 100)Admin
POST/v1/groups/members/removegroupId, memberIdAdmin, ou soi-même (quitter)
PATCH/v1/groups/members/rolegroupId, memberId, isAdminAdmin (hors diffusion)
PATCH/v1/groups/detailsgroupId + name, description, photoUrl, sendMessages, editInfoOnlyAdminAdmin
POST/v1/groups/broadcastgroupId + champs d'un messageMembre (créateur pour une diffusion)
curl -X POST https://api.gpchat.app/v1/groups \
  -H "Authorization: Bearer $GPCHAT_API_KEY" -H "Content-Type: application/json" \
  -d '{"name": "Clients Premium", "isBroadcast": true, "members": ["USER_ID_1", "USER_ID_2"]}'

Stories

Token OAuth2 uniquement : une story est toujours publiée au nom de l'utilisateur qui a donné son consentement.

MéthodeEndpointDétailsScope
POST/v1/storiestype = text (text ≤ 1 000), image (imageUrl) ou video (videoUrl, thumbnailUrl?)stories:write
GET/v1/stories/mineStories de l'utilisateurstories:read
DELETE/v1/stories/deletestoryId (appartenant à l'utilisateur)stories:write

Sondages

Clé API (bot) ou token OAuth2 — scope polls:write dans les deux cas. Cible : groupId (l'acteur doit être membre) ou receiverId (conversation privée). Seul le créateur du sondage peut le clore.

MéthodeEndpointCorps
POST/v1/pollsquestion (≤ 300), options (2 à 12, ≤ 100 chacune), allowMultipleAnswers?, isAnonymous?
POST/v1/polls/votemessageId, optionId — bascule le vote
POST/v1/polls/closemessageId — créateur uniquement
{
  "groupId": "GROUP_ID",
  "question": "Quel créneau pour la réunion ?",
  "options": ["Lundi 10h", "Mardi 14h", "Jeudi 16h"],
  "allowMultipleAnswers": true
}

Login with GPChat (OAuth2)

Permettez à vos utilisateurs de se connecter à votre service avec leur compte GPChat, puis d'agir en leur nom (stories, sondages, groupes). Flux Authorization Code (RFC 6749).

Scopes

ScopeAccès
profileNom et photo de profil (/oauth/userinfo)
stories:read / stories:writeLire / publier et supprimer les stories de l'utilisateur
polls:writeCréer des sondages, voter, clôturer
groups:manageCréer et administrer des groupes et diffusions
  1. Enregistrez vos URI de redirection (console → Identifiants). Correspondance exacte ; https:// obligatoire (http://localhost accepté en développement).
  2. Redirigez l'utilisateur vers la page de consentement, avec un state aléatoire mémorisé en session :
    https://api.gpchat.app/oauth/authorize
      ?response_type=code
      &client_id=APP_ID
      &redirect_uri=https%3A%2F%2Fmonapp.com%2Fcallback
      &scope=profile%20stories%3Awrite
      &state=RANDOM_CSRF_TOKEN
  3. Récupérez le code : l'utilisateur revient sur redirect_uri?code=…&state=… (ou ?error=access_denied). Vérifiez que state correspond.
  4. Échangez le code depuis votre serveur (usage unique, valide 5 minutes) :
    curl -X POST https://api.gpchat.app/oauth/token -H "Content-Type: application/json" -d '{
      "grant_type": "authorization_code",
      "code": "CODE",
      "redirect_uri": "https://monapp.com/callback",
      "client_id": "APP_ID",
      "client_secret": "CLIENT_SECRET"
    }'
    # → { "access_token": "gpat_…", "refresh_token": "gprt_…", "token_type": "Bearer", "expires_in": 3600, "scope": "profile stories:write" }
  5. Appelez l'API avec Authorization: Bearer gpat_…, par exemple GET /oauth/userinfo → { "sub", "name", "picture" }.
  6. Rafraîchissez avant expiration (1 h) avec grant_type=refresh_token. Chaque rafraîchissement révoque l'ancien refresh token et en émet un nouveau (rotation) : stockez toujours le dernier reçu.
  7. Déconnexion : POST /oauth/revoke avec { token, client_id, client_secret } révoque le token et toute sa famille.

Si un refresh token déjà utilisé est présenté de nouveau, la requête échoue (401) : c'est le signe d'un vol possible. Redemandez alors le consentement de l'utilisateur.

Limites et quotas

RessourceLimite
Requêtes API60 / minute par défaut, par clé API et par endpoint (par application pour les tokens OAuth2). Quota relevable sur demande.
Applications20 par compte (hors supprimées)
Bot1 par application
Texte4 096 caractères ; légende 1 024
URL2 048 caractères, https://
Mentions50 par message
Membres ajoutés100 par requête (taille maximale du groupe vérifiée côté serveur)
WebhookRéponse attendue en moins de 10 s ; 8 tentatives

Au-delà du quota : 429. Réessayez après quelques secondes avec un repli exponentiel (1 s, 2 s, 4 s…). La consommation en temps réel est visible dans la console (Identifiants → Consommation du quota).

Erreurs

Toute erreur renvoie un code HTTP et un corps uniforme :

{ "status": "error", "code": "invalid-argument", "message": "Champ 'fileUrl' : seules les URL https:// sont acceptées" }
HTTPcodeCauses fréquentesQue faire
400invalid-argumentChamp manquant ou invalide, URL non https, type inconnuCorriger la requête (le message indique le champ)
400failed-preconditionL'utilisateur n'a jamais écrit au bot ; sondage closAttendre que l'utilisateur écrive au bot
401unauthenticatedJeton absent, invalide, révoqué ou expiréVérifier l'en-tête ; rafraîchir le token OAuth2
403permission-deniedScope manquant ; application désactivée/suspendue ; bot bloqué ; pas administrateurVérifier les droits et le statut de l'app
404not-foundDestinataire, groupe ou message cité introuvableVérifier les identifiants
409already-existsL'application a déjà un botRégénérer la clé plutôt que recréer
422failed-preconditionClé API sans botCréer le bot dans la console
429RATE_LIMIT_EXCEEDED / resource-exhaustedQuota dépassé ; limite d'apps ou de membresRepli exponentiel
500INTERNAL_ERRORErreur inattendueRéessayer ; si ça persiste, contactez-nous avec l'heure et l'endpoint

Désactiver ou supprimer une application

Console → onglet Paramètres → Zone de danger.

DésactiverSupprimer
Réversible✅ Oui (bouton « Réactiver »)❌ Non
Clés APIRefusées (403), conservéesRévoquées
Tokens OAuth2Refusés (403), conservésRévoqués
WebhooksPlus aucun envoiURL et secret retirés
BotInchangéDésactivé : ne peut plus se connecter, profil marqué supprimé
Messages déjà envoyésConservésConservés (restent visibles pour les utilisateurs)

Une application suspendue par l'équipe GPChat ne peut pas être réactivée depuis la console : contactez infos@gpchat.app.

Bonnes pratiques de sécurité

FAQ

Mon bot ne reçoit pas de webhooks.

Vérifiez (1) que l'URL est enregistrée et répond au ping, (2) que l'application est active, (3) que vous attendez au plus 5 minutes après la création du bot, (4) le journal de l'onglet Monitoring pour voir les réponses de votre serveur.

Je reçois failed-precondition en envoyant un message.

L'utilisateur n'a jamais écrit à votre bot. Demandez-lui de lui envoyer un premier message (par exemple /start).

Où trouver le userId d'un utilisateur ?

Dans chaque webhook (from, chatId), ou via /oauth/userinfo (sub) pour un utilisateur connecté avec Login with GPChat.

Puis-je tester sans risque, sachant qu'il n'y a pas de staging ?

Oui, directement en production : utilisez le simulateur d'événements de la console (aucun utilisateur réel impliqué), puis écrivez à votre bot depuis votre propre compte GPChat habituel. Tant que vous n'avez partagé son nom d'utilisateur avec personne, vous êtes la seule personne au monde capable de le trouver — le tester ainsi n'affecte jamais d'autres utilisateurs.

Mon texte apparaît-il chiffré dans les webhooks ?

Non. Les messages privés sont chiffrés au repos, mais GPChat vous transmet toujours textMsg en clair.

Nouveautés

v1.1 — septembre 2026