Pour les développeurs

L’API Areza

Cherche des clubs, lis leurs créneaux libres, réserve et paie depuis ton application. C’est l’API dont se sert notre propre site : même contrat, mêmes règles, aucun passe-droit.

Démarrer en quatre appels

  1. Connecte-toi et crée une clé dans l’espace développeur. Commence en bac à sable.
  2. Passe-la dans l’en-tête de chaque appel. Elle n’est montrée qu’une fois : garde-la côté serveur.
  3. Cherche, lis les créneaux, réserve, paie :
export AREZA_KEY=areza_test_…   # ta clé, créée dans l'espace développeur
API=https://areza.jrec.fr/api/v1

# 1. Les clubs de padel à moins de 10 km
curl -s "$API/establishments?lat=48.8566&lng=2.3522&radius_km=10&sport=padel" \
  -H "Authorization: Bearer $AREZA_KEY"

# 2. Les créneaux d'un club, un jour donné
curl -s "$API/establishments/court-central-padel-bercy/availability?date=2026-10-21" \
  -H "Authorization: Bearer $AREZA_KEY"

# 3. Réserver un créneau libre (resourceId et startsAt viennent de l'appel 2)
curl -s -X POST "$API/bookings" \
  -H "Authorization: Bearer $AREZA_KEY" -H "Content-Type: application/json" \
  -d '{"resourceId":"…","startsAt":"2026-10-21T19:00:00+02:00"}'

# 4. Payer, si le club encaisse en ligne (statut PENDING_PAYMENT)
curl -s -X POST "$API/bookings/$BOOKING_ID/payment" \
  -H "Authorization: Bearer $AREZA_KEY" -H "Content-Type: application/json" \
  -d '{"paymentMethod":"pm_card_visa"}'

S’authentifier

Authorization: Bearer areza_test_… ou, si ton outil préfère, X-Api-Key: areza_test_…. Une clé agit au nom du compte qui l’a créée : ce qu’elle réserve est à ce compte. Remplacer une clé en crée une nouvelle ; l’ancienne fonctionne encore 24 h, le temps de déployer. Révoquer l’arrête tout de suite.

Les périmètres

Une clé n’ouvre que ce qu’on lui a donné. Un appel hors de ses périmètres répond 403 insufficient_scope.

PérimètreCe qu’il ouvre
availability:readÉtablissements, créneaux libres, parties ouvertes
bookings:readRéservations, reçus, fils avec les clubs
bookings:writeRéserver, payer, annuler, ouvrir ou rejoindre une partie
club:manageL'espace club des clubs dont je suis membre
recordings:writePour la caméra d'un court équipé : les replays des matchs de mes clubs

La gestion des clés et des webhooks reste réservée à l’utilisateur connecté : une clé ne crée pas de clé.

Le bac à sable

Une clé de test (areza_test_…) lit tout, mais ne réserve que dans les trois clubs de démonstration (Court Central Padel à Bercy, Tennis Club de la Cité à Boulogne, Five Arena à Saint-Ouen). Les paiements y passent par des cartes de test : pm_card_visa et pm_card_mastercard sont acceptées, pm_card_chargeDeclined est refusée. Elle n’est jamais facturée.

Débit et quotas

120 appels par minute et par clé ; 1 000 appels par jour pour une clé de test, 10 000 pour une clé de production. Chaque réponse le dit : X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-Quota-Limit et X-Quota-Remaining. Au-delà, 429 avec Retry-After, en secondes. Un appel refusé n’est pas compté.

Ce que ça coûte

La lecture est gratuite dans le quota. Une réservation confirmée par une clé de production coûte 0,10 € ; le relevé du mois, jour par jour et clé par clé, est dans l’espace développeur. Le bac à sable ne coûte rien.

Les webhooks

Areza prévient ton serveur de ce qui arrive aux réservations prises par tes clés, que le changement vienne de ton application, du joueur ou du club :

  • booking.created — Réservation créée
  • booking.confirmed — Réservation confirmée (payée ou accordée)
  • booking.cancelled — Réservation annulée ou libérée

Chaque envoi est un POST JSON signé. Réponds 2xx pour l’accuser ; sinon il est rejoué après 1 min, 5 min, 30 min, 2 h et 12 h, puis abandonné. L’adresse doit être en HTTPS et publique.

POST /ton/adresse
Areza-Event: booking.confirmed
Areza-Delivery: 5d1c…
Areza-Signature: t=1760428800,v1=f0e5684b…

{
  "id": "8f2a…",
  "type": "booking.confirmed",
  "createdAt": "2026-10-14T08:00:05Z",
  "data": {
    "booking": { "id": "…", "status": "CONFIRMED", "startsAt": "2026-10-21T19:00:00+02:00", … },
    "apiKey": { "id": "…", "prefix": "areza_live_3f9a1c", "mode": "live" }
  }
}

Vérifier la signature, en Node :

import crypto from "node:crypto";

// secret : le secret du webhook, affiché dans l'espace développeur (whsec_…)
export function verify(secret, signatureHeader, rawBody) {
  const parts = Object.fromEntries(signatureHeader.split(",").map((part) => part.split("=")));
  const expected = crypto.createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex");
  const given = Buffer.from(parts.v1 ?? "");
  const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300; // refuser un envoi rejoué
  return fresh && given.length === expected.length && crypto.timingSafeEqual(Buffer.from(expected), given);
}

Connecter un agent (MCP)

Areza parle aussi MCP : un assistant comme Claude cherche un créneau et réserve pour le joueur, avec les mêmes règles que le site. L’adresse du serveur : https://areza.jrec.fr/api/mcp.

  • Avec le compte du joueur : dans Claude, ajouter un connecteur personnalisé avec cette adresse. Claude envoie le joueur sur Areza, qui se connecte et donne son accord ; l’agent agit ensuite en son nom, sans clé, et rien n’est facturé.
  • Avec une clé d’API, pour un agent à toi : la passer dans l’en-tête, comme pour l’API. Mêmes périmètres, mêmes quotas, même facturation.
# Claude Code, avec le compte du joueur (OAuth)
claude mcp add --transport http areza https://areza.jrec.fr/api/mcp

# Claude Code, avec une clé d'API
claude mcp add --transport http areza https://areza.jrec.fr/api/mcp   --header "Authorization: Bearer areza_live_…"
OutilCe qu’il fait
search_clubsChercher un club autour d'un point, par sport ou par nom
get_clubLa fiche d'un club : adresse, terrains, prix, annulation, paiement
get_availabilityLes créneaux libres d'un club un jour donné, terrain par terrain
book_slotRéserver un créneau, après l'accord du joueur
list_my_bookingsLes réservations du joueur
cancel_bookingAnnuler une réservation, après l'accord du joueur
list_sportsLes sports qu'on peut réserver

L’agent ne réserve et n’annule qu’après l’accord du joueur. Si le club encaisse en ligne, il lui donne un lien pour payer lui-même : il ne voit jamais sa carte.

Exemples

JavaScript (Node 18 et plus)

const api = "https://areza.jrec.fr/api/v1";
const headers = { Authorization: `Bearer ${process.env.AREZA_KEY}`, "Content-Type": "application/json" };

const day = await fetch(`${api}/establishments/court-central-padel-bercy/availability?date=2026-10-21`, { headers })
  .then((response) => response.json());
const court = day.resources[0];
const slot = court.slots.find((candidate) => candidate.status === "AVAILABLE");

const response = await fetch(`${api}/bookings`, {
  method: "POST",
  headers,
  body: JSON.stringify({ resourceId: court.resourceId, startsAt: slot.startsAt }),
});
if (!response.ok) {
  const error = await response.json(); // { code: "slot_taken", message: "…" }
  throw new Error(error.code);
}
const booking = await response.json(); // status : CONFIRMED, ou PENDING_PAYMENT à payer

Python

import os, requests

api = "https://areza.jrec.fr/api/v1"
session = requests.Session()
session.headers["Authorization"] = f"Bearer {os.environ['AREZA_KEY']}"

day = session.get(f"{api}/establishments/court-central-padel-bercy/availability",
                  params={"date": "2026-10-21"}).json()
court = day["resources"][0]
slot = next(s for s in court["slots"] if s["status"] == "AVAILABLE")

booking = session.post(f"{api}/bookings",
                       json={"resourceId": court["resourceId"], "startsAt": slot["startsAt"]})
booking.raise_for_status()
print(booking.json()["status"], booking.headers["X-Quota-Remaining"])

Les erreurs

Toujours la même forme : {"code": "slot_taken", "message": "…"}. Le code est stable, fais-lui confiance ; le message est pour les humains.

HTTPCodeQuand
401invalid_api_keyClé inconnue, révoquée ou mal formée
403insufficient_scopeLa clé n'a pas le périmètre de cet appel
403api_key_not_allowedAppel réservé à l'utilisateur connecté (gérer ses clés, par exemple)
403sandbox_onlyUne clé de test ne réserve que dans les clubs de démonstration
409slot_takenQuelqu'un vient de prendre le créneau
402card_declinedCarte refusée : la réservation attend toujours, on peut réessayer
429rate_limitedTrop d'appels dans la minute : attendre Retry-After secondes
429quota_exceededQuota du jour atteint : il se renouvelle à minuit, heure de Paris

La référence complète, endpoint par endpoint : Swagger UI et le document OpenAPI.