Zurück zur Übersicht

Dokumentation

API für Agenten

Als Text für KI-Agenten

Anmeldung, Grenzen, Idempotenz, Fehler und fertige Beispiele für die öffentliche API.

Die öffentliche API liegt unter /api/v1 und richtet sich an Agenten, Skripte und eigene Anwendungen. Diese Seite fasst den Einstieg zusammen und zeigt drei vollständige Beispiele. Die verbindliche Beschreibung steht als OpenAPI 3.1 unter https://api.eventwerk.eu/openapi.json und unter /api/v1/openapi.json.

Anmeldung

Jede Anfrage trägt den API-Schlüssel im Kopf Authorization. Das Format ist Bearer ew_....

bash
Authorization: Bearer ew_...

Ein Schlüssel gehört zu genau einer Organisation und handelt mit den Rechten des Eigentümers. Als Eigentümer legen Sie ihn in den Einstellungen unter /dashboard/einstellungen an. Der volle Schlüssel wird nur einmal angezeigt.

  • API-Zugang gibt es nur mit Eventwerk Plus.
  • Je Organisation sind höchstens 25 aktive Schlüssel erlaubt.
  • Höchstens 10 neue Schlüssel pro Tag, gezählt in einem rollierenden Zeitfenster von 24 Stunden.
  • Ein zurückgezogener Schlüssel wird sofort abgelehnt und ergibt invalid_api_key.

Der Schlüssel ist ein Geheimnis. Speichern Sie ihn nicht in Repositorys, Protokollen oder öffentlichen Konfigurationen.

Grenzen

Je Schlüssel sind 200 Anfragen pro Minute erlaubt. Der Zähler liegt im Arbeitsspeicher jeder Instanz und wird nicht über Instanzen geteilt. Bei Überschreitung antwortet die API mit Status 429, dem Code rate_limited und dem Kopf Retry-After in Sekunden. Warten Sie die genannte Zeit ab, bevor Sie es erneut versuchen.

Schreibende Aufrufe wiederholen

POST /events akzeptiert den optionalen Kopf Idempotency-Key. Damit lässt sich eine Anfrage nach einer Zeitüberschreitung wiederholen, ohne ein zweites Event anzulegen. Der Kopf darf höchstens 255 Zeichen lang sein.

  • Gleicher Schlüssel und gleicher Rumpf: Die API liefert dieselbe Antwort erneut.
  • Gleicher Schlüssel und anderer Rumpf: Status 409 und idempotency_conflict.
  • Ohne Kopf legt jeder Aufruf ein neues Event an.
  • Verwenden Sie je beabsichtigter Anlage einen eigenen, zufälligen Schlüssel.

Fehlerformat

Fehler kommen immer als JSON mit code und message. code ist stabil und für Programme gedacht. message ist eine deutsche Meldung für Menschen. Das optionale Feld details enthält zusätzliche Angaben.

json
{
  "code": "idempotency_conflict",
  "message": "Der Idempotency-Key wurde bereits mit einem anderen Rumpf verwendet."
}
CodeStatusBedeutung
invalid_json400Der Rumpf ist kein gültiges JSON.
unknown_field400Ein Feld im Rumpf ist unbekannt. details.field nennt den Namen.
invalid_filter400Ein Filterwert ist unbekannt.
invalid_limit400Ein limit ist ungültig, etwa keine ganze Zahl oder kleiner als 1.
invalid_api_key401Der Schlüssel fehlt, ist unbekannt oder wurde zurückgezogen.
plan_required403Die Aktion erfordert Eventwerk Plus.
event_not_found404Das Event wurde nicht gefunden.
registration_not_found404Die Anmeldung wurde nicht gefunden.
checkin_not_found404Der QR-Token ist unbekannt.
idempotency_conflict409Derselbe Idempotency-Key wurde mit einem anderen Rumpf verwendet.
event_has_registrations409Ein veröffentlichtes Event mit Anmeldungen lässt sich nicht löschen.
registration_not_confirmed, registration_not_checkinable409Die Anmeldung lässt sich nicht einchecken.
rate_limited429Mehr als 200 Anfragen pro Minute je Schlüssel und Instanz.
internal_error500Interner Fehler.

Prüfen Sie code und den HTTP-Status, nicht den deutschen Text von message.

Webhooks

Webhooks gehören zu Eventwerk Plus. Eventwerk sendet bei einer neuen Benachrichtigung einen POST mit JSON-Rumpf an die hinterlegte URL. Die Verwaltung läuft über die Sitzungs-API unter /api/webhooks, nicht über den API-Schlüssel. Die vollständige Beschreibung steht unter API: Webhooks.

  • Signatur: Der Kopf X-Eventwerk-Signature enthält sha256=<HMAC-SHA256> über den unveränderten Rumpf, berechnet mit dem Webhook-Geheimnis. Vergleichen Sie die Signatur in konstanter Zeit.
  • Wiederholungen: Jeder Webhook erhält höchstens zwei Versuche im Abstand von 500 Millisekunden. Ein Versuch hat ein Zeitlimit von 5 Sekunden. Eine Antwort mit Status 2xx gilt als erfolgreich.
  • Reihenfolge: Die Zustellungen sind nicht in einer festen Reihenfolge garantiert. Es gibt keine dauerhafte Warteschlange; nach einem Neustart gehen nicht zugestellte Aufrufe verloren.

Fehler bei der Zustellung werden nur protokolliert. Der auslösende Vorgang, etwa eine Anmeldung, bleibt gültig.

Verweise

Beispiele

Die Beispiele lesen den Schlüssel aus EVENTWERK_API_KEY. EVENT_ID ist die Kennung des Events, QR_TOKEN der Token aus dem Ticket. Die Basisadresse ist https://api.eventwerk.eu/api/v1. Alle Zeitpunkte in Antworten sind UTC und enden auf Z; die Zeitzone des Events steht getrennt in timezone.

Der QR-Token steht nicht in der Anmeldeliste. Er steckt im Ticket des Gasts unter /ticket/<token>.

Events auflisten

GET /events liefert eine Seite mit items, has_more und next_cursor. Jeder Eintrag enthält unter anderem id, title, start_at, timezone, status und registration_count. limit hat den Standard 50. Werte über 200 werden auf 200 begrenzt.

bash
curl -s "https://api.eventwerk.eu/api/v1/events?limit=50" \
  -H "Authorization: Bearer $EVENTWERK_API_KEY"
javascript
const apiBase = "https://api.eventwerk.eu/api/v1";
const apiKey = process.env.EVENTWERK_API_KEY;

async function main() {
  const response = await fetch(apiBase + "/events?limit=50", {
    headers: { Authorization: "Bearer " + apiKey },
  });

  if (!response.ok) {
    const error = await response.json();
    throw new Error(error.code + ": " + error.message);
  }

  const page = await response.json();

  for (const event of page.items) {
    console.log(event.id, event.title, event.start_at);
  }
}

main();
python
import os

import requests

api_base = "https://api.eventwerk.eu/api/v1"
api_key = os.environ["EVENTWERK_API_KEY"]

response = requests.get(
    f"{api_base}/events",
    params={"limit": 50},
    headers={"Authorization": f"Bearer {api_key}"},
    timeout=30,
)
response.raise_for_status()
page = response.json()

for event in page["items"]:
    print(event["id"], event["title"], event["start_at"])

Die Antwort hat die Form { "items": [...], "has_more": false, "next_cursor": null }. Ist has_more false, ist next_cursor null.

Anmeldungen eines Events lesen

GET /events/{eventID}/registrations liefert eine Seite mit items, has_more und next_cursor. Jeder Eintrag enthält unter anderem id, name, email, status, ticket_type_id, price_paid_cents, checked_in_at und created_at.

bash
curl -s "https://api.eventwerk.eu/api/v1/events/$EVENT_ID/registrations?status=confirmed&limit=100" \
  -H "Authorization: Bearer $EVENTWERK_API_KEY"
javascript
const apiBase = "https://api.eventwerk.eu/api/v1";
const apiKey = process.env.EVENTWERK_API_KEY;
const eventId = process.env.EVENT_ID;

async function main() {
  const url =
    apiBase +
    "/events/" +
    encodeURIComponent(eventId) +
    "/registrations?status=confirmed&limit=100";

  const response = await fetch(url, {
    headers: { Authorization: "Bearer " + apiKey },
  });

  if (!response.ok) {
    const error = await response.json();
    throw new Error(error.code + ": " + error.message);
  }

  const page = await response.json();

  for (const registration of page.items) {
    console.log(
      registration.id,
      registration.name,
      registration.status,
      registration.checked_in_at,
    );
  }
}

main();
python
import os

import requests

api_base = "https://api.eventwerk.eu/api/v1"
api_key = os.environ["EVENTWERK_API_KEY"]
event_id = os.environ["EVENT_ID"]

response = requests.get(
    f"{api_base}/events/{event_id}/registrations",
    params={"status": "confirmed", "limit": 100},
    headers={"Authorization": f"Bearer {api_key}"},
    timeout=30,
)
response.raise_for_status()
page = response.json()

for registration in page["items"]:
    print(
        registration["id"],
        registration["name"],
        registration["status"],
        registration["checked_in_at"],
    )

status filtert nach pending, confirmed, waitlisted, cancelled oder declined. q sucht in Name und E-Mail-Adresse. limit hat den Standard 50. Werte über 200 werden auf 200 begrenzt.

Check-in setzen

POST /events/{eventID}/checkin setzt den Check-in einmalig. Der Rumpf enthält qr_token.

bash
curl -s -X POST "https://api.eventwerk.eu/api/v1/events/$EVENT_ID/checkin" \
  -H "Authorization: Bearer $EVENTWERK_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"qr_token\":\"$QR_TOKEN\"}"
javascript
const apiBase = "https://api.eventwerk.eu/api/v1";
const apiKey = process.env.EVENTWERK_API_KEY;
const eventId = process.env.EVENT_ID;
const qrToken = process.env.QR_TOKEN;

async function main() {
  const response = await fetch(
    apiBase + "/events/" + encodeURIComponent(eventId) + "/checkin",
    {
      method: "POST",
      headers: {
        Authorization: "Bearer " + apiKey,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ qr_token: qrToken }),
    },
  );

  const result = await response.json();

  if (!response.ok) {
    throw new Error(result.code + ": " + result.message);
  }

  console.log(result.status, result.already_checked_in, result.registration.id);
}

main();
python
import os

import requests

api_base = "https://api.eventwerk.eu/api/v1"
api_key = os.environ["EVENTWERK_API_KEY"]
event_id = os.environ["EVENT_ID"]
qr_token = os.environ["QR_TOKEN"]

response = requests.post(
    f"{api_base}/events/{event_id}/checkin",
    headers={"Authorization": f"Bearer {api_key}"},
    json={"qr_token": qr_token},
    timeout=30,
)
response.raise_for_status()
result = response.json()

print(result["status"], result["already_checked_in"], result["registration"]["id"])

Die Antwort enthält status, already_checked_in und registration. Der erste Aufruf liefert checked_in und already_checked_in: false. Ein erneuter Aufruf mit demselben Token liefert already_checked_in: true und verändert den Zeitpunkt nicht. Ein unbekannter Token ergibt 404 checkin_not_found. Eine Anmeldung, die nicht eingecheckt werden kann, ergibt 409 mit registration_not_confirmed oder registration_not_checkinable.

Der Check-in ist von sich aus wiederholbar. Ein zweiter Aufruf mit demselben Token legt keinen zweiten Check-in an, sondern meldet already_checked_in.