Skip to content

Webhooks ​

L'onglet Webhooks de /nay (scope nay.admin) envoie les événements des scripts vers vos URL. Choisissez les événements (ou tous), appuyez sur Tester pour vérifier la configuration, et consultez la dernière réponse de chaque webhook.

Il y a deux types :

  • Discord : collez l'URL de webhook d'un salon Discord (https://discord.com/api/webhooks/...). Chaque événement devient un embed, titré et libellé dans la langue du serveur, avec les champs de l'événement dans l'ordre. Le markdown Discord des valeurs est échappé, personne n'est mentionné, et les messages trop longs sont coupés à la limite de 6000 caractères.
  • JSON signé : pour votre propre site ou bot. Chaque webhook a un secret, affiché à la création ou quand il est régénéré.

Envois ​

Un envoi est un POST de JSON :

json
{ "id": "3f9c2a1b7d4e8f60", "event": "nay_car_wash.wash", "resource": "nay_car_wash", "time": 1791670794, "data": { "player": "John", "price": 150 } }

En-têtes : X-Nay-Event, X-Nay-Delivery (identique à id), X-Nay-Timestamp (secondes Unix) et X-Nay-Signature: sha256=<hex>.

  • Répondez avec n'importe quel statut 2xx. Sans réponse en 15 secondes, l'envoi compte comme une erreur réseau.
  • Les erreurs réseau, les réponses 5xx et 429 sont renvoyées après 1, 10 et 60 secondes avec le même X-Nay-Delivery. Les autres réponses 4xx ne sont pas renvoyées.
  • Après 20 envois ratés d'affilée, le webhook est désactivé. Réactivez-le dans l'onglet, ou enregistrez-le avec une nouvelle URL.
  • Les envois Discord ne sont pas signés et respectent le retry_after de Discord en cas de limite.

Événements ​

Événements de nay_lib :

ÉvénementQuandDonnées
nay.settingUn réglage est modifié ou réinitialisé.resource, key, action, actor
nay.permissionUne règle de permission est accordée ou retirée.action, type, identifier, scope, actor
nay.tokenUn jeton d'API est créé ou révoqué.action, id, label, actor
nay.testLe bouton Tester.message

Chaque script liste ses propres événements sur sa page, par exemple Car Wash.

Vérifier la signature ​

Avant de faire confiance à un envoi signé, vérifiez sa signature : HMAC-SHA256 de <X-Nay-Timestamp>.<raw body>, avec pour clé le secret tel qu'affiché (le texte hexadécimal lui-même). Refusez les horodatages trop anciens.

Node.js (Express) :

js
import { createHmac, timingSafeEqual } from "node:crypto";
import express from "express";

const app = express();
app.post("/nayretis", express.raw({ type: "application/json" }), (req, res) => {
	const timestamp = req.get("X-Nay-Timestamp") ?? "";
	const expected = "sha256=" + createHmac("sha256", process.env.NAY_WEBHOOK_SECRET).update(`${timestamp}.${req.body}`).digest("hex");
	const given = req.get("X-Nay-Signature") ?? "";
	const valid = given.length === expected.length && timingSafeEqual(Buffer.from(given), Buffer.from(expected));
	if (!valid || Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) {
		return res.sendStatus(401);
	}
	const delivery = JSON.parse(req.body);
	res.sendStatus(204);
});

PHP :

php
<?php
$body = file_get_contents('php://input');
$timestamp = $_SERVER['HTTP_X_NAY_TIMESTAMP'] ?? '';
$expected = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $body, getenv('NAY_WEBHOOK_SECRET'));
if (!hash_equals($expected, $_SERVER['HTTP_X_NAY_SIGNATURE'] ?? '') || abs(time() - (int) $timestamp) > 300) {
	http_response_code(401);
	exit;
}
$delivery = json_decode($body, true);
http_response_code(204);