Skip to content

Winglark

Référence développeur

API et webhooks Winglark

Lisez les événements, les invités, les réponses et les entrées ; ajoutez des invités ; soyez prévenu dès que quelque chose se passe. Une API REST en JSON, des clés aux autorisations limitées et des webhooks signés.

L’API et les webhooks font partie des offres Professional et Agency. Les clés et les webhooks sont créés par le propriétaire de l’espace de travail ou un responsable, dans Société → Intégrations.

Vue d’ensemble

L’adresse de base est https://winglark.com/api/v1. Chaque requête et chaque réponse est en JSON sur HTTPS. Les dates sont en ISO-8601 et en UTC ; les identifiants sont des UUID.

L’API est faite pour les serveurs. Elle n’envoie aucun en-tête CORS et ignore les cookies : appelez-la depuis votre back-end, jamais depuis un navigateur, et gardez la clé hors du code client.

Authentification

Envoyez la clé dans l’en-tête Authorization comme jeton bearer. Une clé n’est affichée qu’une fois, à sa création ; Winglark n’en garde qu’une empreinte chiffrée avec une clé.

Une clé agit au nom du membre qui l’a créée, dans la limite de ses autorisations. Elle est refusée si elle est révoquée ou expirée, si ce membre a quitté l’espace de travail ou ne peut plus gérer les intégrations, ou si l’offre de l’espace n’inclut plus l’API.

curl https://winglark.com/api/v1/events \
  -H "Authorization: Bearer wlk_live_ab12cd34_…"

Autorisations

Chaque point d’accès demande une autorisation. Une clé avec guests:write peut aussi lire les invités. Les coordonnées (e-mail, téléphone) sont masquées, sauf si la clé a été créée avec coordonnées visibles par un membre autorisé à les voir.

AutorisationSignification
events:readLire les événements. Titres, dates, lieux et statut.
guests:readLire les invités. La liste d’invités de chaque événement.
guests:writeAjouter et modifier des invités. Avec les mêmes règles que le panneau : limite de l’offre et contrôle des doublons.
rsvps:readLire les réponses. Qui a répondu, combien viennent. Jamais les réponses sur l’alimentation, les allergies ou l’accessibilité.
checkins:readLire les entrées. Qui est entré, quand, et par quelle porte.
webhooks:manageGérer les webhooks. Permet à un outil d’automatisation de s’abonner et de se désabonner seul.

Erreurs

Les erreurs utilisent le statut HTTP et un corps JSON avec un code stable et une phrase lisible. Chaque réponse porte X-Request-Id ; citez-le quand vous nous écrivez.

400
Un paramètre n’est pas valide.
401
Pas de clé, une clé inconnue, révoquée ou expirée.
403
La clé n’a pas l’autorisation, son créateur a perdu l’accès, ou l’offre n’inclut pas l’API.
404
Aucun enregistrement de ce type dans cet espace de travail.
409
La requête contredit l’enregistrement : adresse en double, limite d’invités de l’offre, événement clos.
422
Des champs manquent ou ne sont pas valides.
429
Trop de requêtes ; attendez Retry-After.
500
Notre erreur. Réessayez avec la même Idempotency-Key.
HTTP/1.1 409 Conflict
X-Request-Id: req_4f1e…

{ "error": { "code": "guest_limit_reached", "message": "This event has reached the guest limit of the workspace plan." } }

Pagination

Les listes commencent par le plus récent et renvoient au plus limit éléments (50 par défaut, 100 au maximum). Si has_more vaut true, passez next_cursor comme cursor pour la page suivante. Les listes d’invités et de réponses acceptent aussi updated_since, une date ISO.

Limites de débit

600 requêtes par minute et par clé, 1 200 par réseau. Au-delà, la réponse est 429 avec Retry-After en secondes. Les requêtes répétées avec une clé invalide sont refusées pendant dix minutes.

Idempotence

Envoyez un en-tête Idempotency-Key avec une création. Pendant 24 heures, un nouvel essai avec la même clé et le même corps renvoie la première réponse (marquée Idempotent-Replayed) au lieu de créer un second invité. La même clé avec un corps différent est refusée avec 422.

curl -X POST https://winglark.com/api/v1/events/{event_id}/guests \
  -H "Authorization: Bearer wlk_live_ab12cd34_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: crm-10442-create" \
  -d '{ "first_name": "Ada", "email": "ada@example.com" }'

Points d’accès

Chaque liste accepte limit et cursor.

GET/v1/me

La clé elle-même : ses autorisations et son espace de travail. Pour tester une clé.

Autorisation: —

{
  "object": "api_key",
  "id": "3b2a1c0d-9e8f-4a7b-b6c5-d4e3f2a1b0c9",
  "name": "CRM sync",
  "prefix": "wlk_live_ab12cd34",
  "scopes": ["events:read", "guests:read", "rsvps:read"],
  "contacts_visible": false,
  "created_at": "2026-11-04T10:00:00.000Z",
  "expires_at": null,
  "organization": { "id": "9d8c7b6a-5f4e-4d3c-a2b1-0f9e8d7c6b5a", "name": "Northwind Events" }
}

GET/v1/events

Les événements de l’espace de travail, du plus récent au plus ancien. Filtrez avec status.

Autorisation: events:read · ?status=active&limit=20

{
  "object": "list",
  "data": [
    {
      "id": "6f1c2a7e-4b1d-4c55-9a3e-2f8d7b0c1e42",
      "object": "event",
      "title": "Spring partner summit",
      "summary": "",
      "status": "active",
      "type": "corporate",
      "starts_at": "2026-12-03T09:00:00.000Z",
      "ends_at": "2026-12-03T17:00:00.000Z",
      "timezone": "Europe/Berlin",
      "locale": "en",
      "location_mode": "in_person",
      "venue_name": "Hall 4",
      "venue_address": "Messedamm 22, Berlin",
      "rsvp_deadline": "2026-11-26T23:00:00.000Z",
      "created_at": "2026-10-20T08:12:44.120Z",
      "updated_at": "2026-11-02T15:03:10.004Z"
    }
  ],
  "has_more": true,
  "next_cursor": "MjAyNi0xMS0wNFQxMDoyMTowMy41NTFafGI4ZTFkM2M0LTlmMjAtNGE3Yi04YzYxLTVkMmUwZjlhM2IxNw"
}

GET/v1/events/{event_id}

Un événement.

Autorisation: events:read

{
  "id": "6f1c2a7e-4b1d-4c55-9a3e-2f8d7b0c1e42",
  "object": "event",
  "title": "Spring partner summit",
  "summary": "",
  "status": "active",
  "type": "corporate",
  "starts_at": "2026-12-03T09:00:00.000Z",
  "ends_at": "2026-12-03T17:00:00.000Z",
  "timezone": "Europe/Berlin",
  "locale": "en",
  "location_mode": "in_person",
  "venue_name": "Hall 4",
  "venue_address": "Messedamm 22, Berlin",
  "rsvp_deadline": "2026-11-26T23:00:00.000Z",
  "created_at": "2026-10-20T08:12:44.120Z",
  "updated_at": "2026-11-02T15:03:10.004Z"
}

GET/v1/events/{event_id}/guests

Les invités d’un événement. include_archived=true ajoute les invités retirés de la liste.

Autorisation: guests:read · ?updated_since=2026-11-01T00:00:00Z

{
  "object": "list",
  "data": [
    {
      "id": "b8e1d3c4-9f20-4a7b-8c61-5d2e0f9a3b17",
      "object": "guest",
      "event_id": "6f1c2a7e-4b1d-4c55-9a3e-2f8d7b0c1e42",
      "first_name": "Ada",
      "last_name": "Kaya",
      "display_name": "Ada Kaya",
      "email": "a•••@example.com",
      "phone": "••• ••• 321",
      "company": "Northwind",
      "job_title": "Head of Partnerships",
      "locale": "en",
      "guest_type": "standard",
      "status": "pending",
      "source": "api",
      "tags": ["partner"],
      "external_reference": "crm-10442",
      "expected_party_size": 1,
      "archived_at": null,
      "created_at": "2026-11-04T10:21:03.551Z",
      "updated_at": "2026-11-04T10:21:03.551Z"
    }
  ],
  "has_more": true,
  "next_cursor": "MjAyNi0xMS0wNFQxMDoyMTowMy41NTFafGI4ZTFkM2M0LTlmMjAtNGE3Yi04YzYxLTVkMmUwZjlhM2IxNw"
}

GET/v1/events/{event_id}/guests/{guest_id}

Un invité.

Autorisation: guests:read

{
  "id": "b8e1d3c4-9f20-4a7b-8c61-5d2e0f9a3b17",
  "object": "guest",
  "event_id": "6f1c2a7e-4b1d-4c55-9a3e-2f8d7b0c1e42",
  "first_name": "Ada",
  "last_name": "Kaya",
  "display_name": "Ada Kaya",
  "email": "a•••@example.com",
  "phone": "••• ••• 321",
  "company": "Northwind",
  "job_title": "Head of Partnerships",
  "locale": "en",
  "guest_type": "standard",
  "status": "pending",
  "source": "api",
  "tags": ["partner"],
  "external_reference": "crm-10442",
  "expected_party_size": 1,
  "archived_at": null,
  "created_at": "2026-11-04T10:21:03.551Z",
  "updated_at": "2026-11-04T10:21:03.551Z"
}

POST/v1/events/{event_id}/guests

Ajouter un invité. Seul first_name est obligatoire. Les limites de l’offre et la règle des adresses en double s’appliquent.

Autorisation: guests:write

{
  "first_name": "Ada",
  "last_name": "Kaya",
  "email": "ada@example.com",
  "company": "Northwind",
  "tags": ["partner"],
  "external_reference": "crm-10442"
}
{
  "id": "b8e1d3c4-9f20-4a7b-8c61-5d2e0f9a3b17",
  "object": "guest",
  "event_id": "6f1c2a7e-4b1d-4c55-9a3e-2f8d7b0c1e42",
  "first_name": "Ada",
  "last_name": "Kaya",
  "display_name": "Ada Kaya",
  "email": "a•••@example.com",
  "phone": "••• ••• 321",
  "company": "Northwind",
  "job_title": "Head of Partnerships",
  "locale": "en",
  "guest_type": "standard",
  "status": "pending",
  "source": "api",
  "tags": ["partner"],
  "external_reference": "crm-10442",
  "expected_party_size": 1,
  "archived_at": null,
  "created_at": "2026-11-04T10:21:03.551Z",
  "updated_at": "2026-11-04T10:21:03.551Z"
}

PATCH/v1/events/{event_id}/guests/{guest_id}

Modifier le nom, les coordonnées, la société, la fonction, la langue, le type ou les étiquettes d’un invité.

Autorisation: guests:write

{ "job_title": "Chief Partnerships Officer", "tags": ["partner", "vip-desk"] }
{
  "id": "b8e1d3c4-9f20-4a7b-8c61-5d2e0f9a3b17",
  "object": "guest",
  "event_id": "6f1c2a7e-4b1d-4c55-9a3e-2f8d7b0c1e42",
  "first_name": "Ada",
  "last_name": "Kaya",
  "display_name": "Ada Kaya",
  "email": "a•••@example.com",
  "phone": "••• ••• 321",
  "company": "Northwind",
  "job_title": "Head of Partnerships",
  "locale": "en",
  "guest_type": "standard",
  "status": "pending",
  "source": "api",
  "tags": ["partner"],
  "external_reference": "crm-10442",
  "expected_party_size": 1,
  "archived_at": null,
  "created_at": "2026-11-04T10:21:03.551Z",
  "updated_at": "2026-11-04T10:21:03.551Z"
}

GET/v1/events/{event_id}/rsvps

Les réponses d’un événement, les dernières modifiées d’abord. Filtrez avec response.

Autorisation: rsvps:read · ?response=yes

{
  "object": "list",
  "data": [
    {
      "id": "0c9b7a5e-3d1f-4e2a-b6c8-9f0e1d2c3b4a",
      "object": "rsvp",
      "event_id": "6f1c2a7e-4b1d-4c55-9a3e-2f8d7b0c1e42",
      "guest_id": "b8e1d3c4-9f20-4a7b-8c61-5d2e0f9a3b17",
      "response": "yes",
      "party_size": 2,
      "attendance_mode": "in_person",
      "revision": 1,
      "responded_via": "link",
      "responded_at": "2026-11-05T18:40:12.000Z",
      "updated_at": "2026-11-05T18:40:12.000Z"
    }
  ],
  "has_more": true,
  "next_cursor": "MjAyNi0xMS0wNFQxMDoyMTowMy41NTFafGI4ZTFkM2M0LTlmMjAtNGE3Yi04YzYxLTVkMmUwZjlhM2IxNw"
}

GET/v1/events/{event_id}/checkins

Les passages à l’entrée d’un événement, du plus récent au plus ancien.

Autorisation: checkins:read

{
  "object": "list",
  "data": [
    {
      "id": "5a4b3c2d-1e0f-4a9b-8c7d-6e5f4a3b2c1d",
      "object": "checkin",
      "event_id": "6f1c2a7e-4b1d-4c55-9a3e-2f8d7b0c1e42",
      "guest_id": "b8e1d3c4-9f20-4a7b-8c61-5d2e0f9a3b17",
      "action": "admitted",
      "result": "ok",
      "admitted": 2,
      "occurred_at": "2026-12-03T08:57:31.000Z",
      "recorded_offline": false
    }
  ],
  "has_more": true,
  "next_cursor": "MjAyNi0xMS0wNFQxMDoyMTowMy41NTFafGI4ZTFkM2M0LTlmMjAtNGE3Yi04YzYxLTVkMmUwZjlhM2IxNw"
}

GET/v1/webhooks

Les points de réception de l’espace de travail.

Autorisation: webhooks:manage

{ "object": "list", "data": [{
    "id": "e2d4c6b8-a0f1-4e3d-9c5b-7a8f6e4d2c1b",
    "object": "webhook_endpoint",
    "url": "https://hooks.example.com/winglark",
    "description": "CRM sync",
    "events": ["rsvp.submitted", "checkin.admitted"],
    "event_ids": [],
    "include_contacts": false,
    "active": true,
    "disabled_reason": null,
    "source": "api",
    "created_at": "2026-11-04T10:30:00.000Z"
  }], "has_more": false, "next_cursor": null }

POST/v1/webhooks

Abonner une adresse. La réponse contient le secret de signature, une seule fois.

Autorisation: webhooks:manage

{
  "url": "https://hooks.example.com/winglark",
  "events": ["rsvp.submitted", "checkin.admitted"],
  "event_ids": [],
  "description": "CRM sync"
}
{
  "id": "e2d4c6b8-a0f1-4e3d-9c5b-7a8f6e4d2c1b",
  "object": "webhook_endpoint",
  "url": "https://hooks.example.com/winglark",
  "description": "CRM sync",
  "events": ["rsvp.submitted", "checkin.admitted"],
  "event_ids": [],
  "include_contacts": false,
  "active": true,
  "disabled_reason": null,
  "source": "api",
  "created_at": "2026-11-04T10:30:00.000Z",
  "secret": "whsec_6Jx…shown once…"
}

DELETE/v1/webhooks/{webhook_id}

Se désabonner.

Autorisation: webhooks:manage

204 No Content

Webhooks

Ajoutez un point de réception dans le panneau ou avec POST /v1/webhooks, choisissez ses types d’événements et, si vous le souhaitez, limitez-le à certains événements. Winglark envoie alors un message JSON par fait.

Répondez par un 2xx sous 10 secondes. Les redirections ne sont pas suivies. L’id du message est le même à chaque nouvel essai et pour chaque point de réception : servez-vous-en pour ignorer un message déjà traité.

Type d’événementQuand
guest.createdUn invité a été ajouté : à la main, par import, par l’API ou à l’entrée.
guest.updatedLes informations d’un invité ont changé.
guest.archivedUn invité a été retiré de la liste.
rsvp.submittedUn invité a répondu ou modifié sa réponse.
invitation.sentUn e-mail d’invitation a été remis au fournisseur d’e-mail.
invitation.deliveredLe serveur de messagerie du destinataire a accepté l’invitation.
invitation.bouncedL’invitation n’a pas pu être remise.
checkin.admittedQuelqu’un est entré, à la porte ou à une borne.
ticket.issuedDes billets ont été émis pour une commande payée.
event.publishedUn événement est en ligne : les invités ont leurs liens, l’entrée compte.
event.cancelledUn événement a été annulé.
seatmap.publishedUn plan de table a été publié pour les invités.
badge.printedDes badges ont été imprimés.

Le message

Chaque message a la même enveloppe. data contient l’enregistrement concerné, sous la forme que renvoie l’API. Les messages ne contiennent jamais de liens d’invitation, de codes de billets, la note privée sur l’invité, ni les réponses sur l’alimentation, les allergies et l’accessibilité.

Winglark-Signature
t=1762338012,v1=5f2b…c9e1
Winglark-Event
rsvp.submitted
Winglark-Delivery
7c1e9a42-…
User-Agent
Winglark-Webhooks/1
Content-Type
application/json
{
  "id": "evt_3f9a1c7e5b2d4a6c8e0f1a2b",
  "object": "event",
  "type": "rsvp.submitted",
  "api_version": "v1",
  "created_at": "2026-11-05T18:40:12.481Z",
  "organization_id": "9d8c7b6a-5f4e-4d3c-a2b1-0f9e8d7c6b5a",
  "event_id": "6f1c2a7e-4b1d-4c55-9a3e-2f8d7b0c1e42",
  "data": {
    "rsvp": {
      "id": "0c9b7a5e-3d1f-4e2a-b6c8-9f0e1d2c3b4a",
      "object": "rsvp",
      "event_id": "6f1c2a7e-4b1d-4c55-9a3e-2f8d7b0c1e42",
      "guest_id": "b8e1d3c4-9f20-4a7b-8c61-5d2e0f9a3b17",
      "response": "yes",
      "party_size": 2,
      "attendance_mode": "in_person",
      "revision": 1,
      "responded_via": "link",
      "responded_at": "2026-11-05T18:40:12.000Z",
      "updated_at": "2026-11-05T18:40:12.000Z"
    },
    "guest": { "id": "b8e1d3c4-9f20-4a7b-8c61-5d2e0f9a3b17", "first_name": "Ada", "last_name": "Kaya", "email": "a•••@example.com", "…": "…" },
    "response": "yes",
    "party_size": 2,
    "revision": 1,
    "revised": false,
    "via": "link"
  }
}

Vérifier la signature

Chaque message porte un en-tête Winglark-Signature : t est l’heure Unix de la signature, v1 le HMAC-SHA256 hexadécimal de t, d’un point et du corps brut, avec le secret de votre point de réception pour clé.

Calculez-le sur les octets bruts avant d’analyser le JSON, comparez en temps constant et refusez un t éloigné de plus de cinq minutes de votre horloge. Après un renouvellement, il y a deux valeurs v1 pendant 24 heures ; acceptez le message si l’une correspond.

Node.js

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

const secret = process.env.WINGLARK_WEBHOOK_SECRET; // whsec_…
const app = express();

// The raw body: the signature covers the bytes, not the parsed JSON.
app.post('/winglark', express.raw({ type: 'application/json' }), (req, res) => {
  const header = req.get('Winglark-Signature') ?? '';
  const parts = header.split(',').map((part) => part.split('='));
  const t = Number(parts.find(([key]) => key === 't')?.[1]);
  const signatures = parts.filter(([key]) => key === 'v1').map(([, value]) => value);

  if (!t || Math.abs(Date.now() / 1000 - t) > 300) return res.sendStatus(400);

  const expected = createHmac('sha256', secret).update(`${t}.`).update(req.body).digest();
  const valid = signatures.some((signature) => {
    const given = Buffer.from(signature, 'hex');
    return given.length === expected.length && timingSafeEqual(given, expected);
  });
  if (!valid) return res.sendStatus(400);

  const event = JSON.parse(req.body.toString('utf8'));
  // Deduplicate on event.id: a message can arrive more than once.
  res.sendStatus(204);
});

app.listen(3000);

Python

import hashlib
import hmac
import json
import os
import time

from flask import Flask, abort, request

SECRET = os.environ["WINGLARK_WEBHOOK_SECRET"].encode()  # whsec_…
app = Flask(__name__)


@app.post("/winglark")
def winglark():
    header = request.headers.get("Winglark-Signature", "")
    parts = [part.split("=", 1) for part in header.split(",") if "=" in part]
    t = next((value for key, value in parts if key == "t"), None)
    signatures = [value for key, value in parts if key == "v1"]

    if not t or not t.isdigit() or abs(time.time() - int(t)) > 300:
        abort(400)

    raw = request.get_data()  # the raw bytes, before any JSON parsing
    expected = hmac.new(SECRET, t.encode() + b"." + raw, hashlib.sha256).hexdigest()
    if not any(hmac.compare_digest(expected, signature) for signature in signatures):
        abort(400)

    event = json.loads(raw)
    # Deduplicate on event["id"]: a message can arrive more than once.
    return "", 204

Nouveaux essais

Un envoi sans réponse 2xx est retenté après 1 minute, 5 minutes, 30 minutes, 2 heures, 6 heures, 12 heures et 24 heures, puis marqué en échec. Les nouveaux essais partent à la première occasion après leur échéance, au plus tard dans l’heure.

Si tous les envois vers un point de réception échouent pendant trois jours, Winglark cesse de lui envoyer et le signale dans le panneau. Reprenez-le une fois le récepteur corrigé ; un envoi échoué peut être renvoyé depuis le journal.

  1. 1. 1 min
  2. 2. 5 min
  3. 3. 30 min
  4. 4. 2 h
  5. 5. 6 h
  6. 6. 12 h
  7. 7. 24 h

Versions

Ceci est la version 1. Dans cette version, nous ne faisons qu’ajouter : nouveaux champs, nouveaux types d’événements, nouveaux paramètres facultatifs. Ignorez les champs que vous ne connaissez pas. Tout ce qui casserait une intégration ira dans une nouvelle version, annoncée à l’avance.

Plateformes d’automatisation

Les outils compatibles REST hooks s’abonnent avec POST /v1/webhooks et se désabonnent avec DELETE /v1/webhooks/{id}, avec une clé disposant de webhooks:manage. GET /v1/me teste une clé. Les listes, du plus récent au plus ancien, servent aussi de données d’exemple.