Geta.Team Geta.Team
API

Webhooks

Soyez prévenu des événements de vos espaces sur votre propre backend, avec signature.

Donnez à Geta.Team une adresse https, et votre backend est prévenu de chaque événement des espaces de vos clients, dans la minute qui suit.

Le mettre en place

Dans votre dashboard, Compte, section API, Webhook :

  1. Saisissez votre adresse : https:// uniquement, sur une adresse publique.
  2. Enregistrez, puis copiez votre secret de signature : il n’est affiché qu’une fois. Enregistrer à nouveau l’adresse donne un nouveau secret.
  3. Envoyez un événement de test : un ping arrive dans la minute. Les derniers envois et leur statut sont listés en dessous.

Événements

ÉvénementQuand
workspace.readyL’espace du client est prêt : son lien d’invitation est dans inviteLink
payment.failedUn paiement de l’espace a échoué (la carte, le 3D Secure...)
workspace.suspendedL’espace a été suspendu pour impayé
workspace.deletedL’espace a été supprimé (abonnement terminé)
pingUn événement de test, depuis le dashboard

Chaque envoi est en JSON :

{
  "id": "evt_8434fff1b5dc162847b6ac3f",
  "type": "workspace.ready",
  "created": "2026-10-29T10:06:03.000Z",
  "data": {
    "workspace": {
      "id": 77, "clientId": 501, "clientName": "Acme", "clientEmail": "[email protected]",
      "status": "running", "url": "https://acme-x.geta.team",
      "inviteLink": "https://acme-x.geta.team/invite/...", "employees": 1
    }
  }
}

Champs en plus : paymentUrl et graceHours pour payment.failed ; paymentUrl pour workspace.suspended ; reason pour workspace.deleted.

Vérifier la signature

Chaque envoi porte :

Geta-Signature: t=1793872803,v1=5f0c...e91
Geta-Event-Id: evt_8434fff1b5dc162847b6ac3f
Geta-Event-Type: workspace.ready

v1 est le HMAC-SHA256, en hexadécimal, de t, d’un point, puis du corps brut, avec votre secret pour clé (toute la chaîne whsec_...). Vérifiez-le sur le corps brut, avant tout décodage JSON, et refusez un t de plus de 5 minutes (une requête rejouée).

// Node.js (Express) : app.post("/geta/webhooks", express.raw({ type: "application/json" }), handler)
const crypto = require("crypto");

function verify(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const t = Number(parts.t);
  if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false;
  const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
  const given = Buffer.from(parts.v1 || "", "hex");
  return given.length === 32 && crypto.timingSafeEqual(given, Buffer.from(expected, "hex"));
}
# Python
import hmac, hashlib, time

def verify(raw_body: bytes, header: str, secret: str) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    t = int(parts.get("t", "0"))
    if abs(time.time() - t) > 300:
        return False
    expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts.get("v1", ""))
// PHP
function verify(string $rawBody, string $header, string $secret): bool {
  parse_str(str_replace(',', '&', $header), $parts);
  $t = (int)($parts['t'] ?? 0);
  if (abs(time() - $t) > 300) return false;
  $expected = hash_hmac('sha256', $t . '.' . $rawBody, $secret);
  return hash_equals($expected, $parts['v1'] ?? '');
}

Répondre, nouvelles tentatives

  • Répondez n’importe quel statut 2xx rapidement (sous 10 secondes), puis faites le travail.
  • Toute autre réponse, ou aucune réponse, est retentée après 1 minute, 5 minutes, 30 minutes, 2 heures, 6 heures et 24 heures, puis abandonnée.
  • Un même événement peut arriver deux fois : servez-vous de son id pour ne faire le travail qu’une fois.

On this page