Digital Results

API

Termine aus deinem System. Eine Anfrage, alle Kalender.

Diese Seite kannst du an ein CRM, Newsletter-Tool oder eine Agentur weitergeben. Alles ist JSON über HTTPS, keine SDKs nötig.

Grundlagen

FeldTypBedeutung
Basis-URLTexthttps://calendar.digitalresults.app
FormatJSON im Body, Antwort JSON. Header Content-Type: application/json.
AuthHeaderPOST /api/events und der MCP-Server brauchen Authorization: Bearer <Token>. Jeder Kunde bekommt von Digital Results einen eigenen Token und sieht nur seine Kalender. Alle anderen Aufrufe sind ohne Token.
ZeitenISO 8601Immer mit Offset oder Z, z. B. 2026-11-17T09:00:00+01:00. Angezeigt wird in der Zeitzone des Termins.
ZeitzoneIANAz. B. Europe/Vienna (Standard), Europe/Berlin, UTC. Gilt für Anzeige und VTIMEZONE in der .ics.
FehlerJSON{ "error": "…" } mit Status 400 (Eingabe), 401 (Token), 404 (nicht gefunden).

Termine anlegen: POST /api/events

Legt Termine in einem Kalender an oder aktualisiert sie. Gleicher uid = Update desselben Termins, Abonnenten sehen die Änderung statt eines Duplikats. Existiert der Kalender-Slug nicht, wird er mit calendarName angelegt.

Body

FeldTypBedeutung
calendarText, PflichtSlug des Kalenders, z. B. bni-linz. Nur Kleinbuchstaben, Ziffern, Bindestrich.
calendarNameTextAnzeigename. Nötig, wenn der Kalender noch nicht existiert.
timezoneIANAStandard-Zeitzone des Kalenders. Standard Europe/Vienna.
events[]Liste, PflichtEin oder mehrere Termine, Felder siehe unten.

Ein Termin in events[]

FeldTypBedeutung
uidTextStabile Kennung aus deinem System, z. B. workshop-2026-11-17@mein-crm. Ohne uid wird jedes Mal ein neuer Termin angelegt.
titleText, PflichtTitel, bis 200 Zeichen.
start, endISO 8601, PflichtBeginn und Ende mit Offset. Ende muss nach Beginn liegen.
locationTextOrt, bis 200 Zeichen. Landet im Kalendereintrag und in der Navigation.
descriptionTextBeschreibung, bis 2000 Zeichen. Mitbringen, Parkplatz, Ansprechpartner.
timezoneIANAZeitzone dieses Termins, sonst die des Kalenders.
rruleTextSerie nach RFC 5545, z. B. FREQ=WEEKLY;BYDAY=TU;COUNT=20 oder FREQ=MONTHLY;INTERVAL=1;UNTIL=20261231T000000Z.
exdatesListe ISO 8601Ausnahmen der Serie, jeweils der Beginn des ausfallenden Vorkommens.
statusconfirmed | cancelledcancelled markiert den Termin als abgesagt. Abonnenten sehen ihn durchgestrichen oder er verschwindet.
rsvptrue | falseAnmeldung mit Zusage und Absage vor dem Kalender-Button. Name, E-Mail und Telefon werden immer abgefragt.
rsvpFieldsListeEigene Zusatzfelder der Anmeldung, bis 12. Aufbau siehe nächster Abschnitt.
curl
curl -X POST https://calendar.digitalresults.app/api/events \
  -H "Authorization: Bearer $API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "calendar": "bni-linz",
    "calendarName": "Termine BNI Linz",
    "timezone": "Europe/Vienna",
    "events": [{
      "uid": "workshop-2026-11-17@mein-crm",
      "title": "Workshop Linz",
      "start": "2026-11-17T09:00:00+01:00",
      "end": "2026-11-17T17:00:00+01:00",
      "location": "Dauphinestraße 89, 4030 Linz",
      "description": "Bitte Laptop mitbringen. Parkplätze hinter dem Haus.",
      "rrule": "FREQ=WEEKLY;BYDAY=TU;COUNT=10",
      "exdates": ["2026-12-22T09:00:00+01:00"],
      "status": "confirmed",
      "rsvp": true,
      "rsvpFields": [
        { "id": "essen", "label": "Was möchtest du essen?", "type": "select", "options": ["Fleisch", "Vegetarisch", "Vegan"], "required": true },
        { "id": "personen", "label": "Wie viele Personen?", "type": "slider", "min": 1, "max": 6, "unit": "Personen" }
      ]
    }]
  }'

Antwort

200 OK
{
  "calendar": {
    "slug": "bni-linz", "name": "Termine BNI Linz",
    "url": "https://calendar.digitalresults.app/calendar/bni-linz",
    "feedUrl": "https://calendar.digitalresults.app/feed/<token>.ics",
    "subscribe": { "webcal": "webcal://…", "google": "https://calendar.google.com/…", "outlook": "https://outlook.live.com/…", "office365": "https://outlook.office.com/…" }
  },
  "events": [{
    "id": 42, "uid": "workshop-2026-11-17@mein-crm", "slug": "workshop-linz-5e830a", "sequence": 0,
    "pageUrl": "https://calendar.digitalresults.app/event/workshop-linz-5e830a",
    "qrUrl": "https://calendar.digitalresults.app/qr?slug=workshop-linz-5e830a",
    "icsUrl": "https://calendar.digitalresults.app/event/workshop-linz-5e830a/ics",
    "links": { "google": "…", "outlook": "…", "office365": "…", "msTeams": "…" },
    "emailHtml": "<a href=…>In Kalender speichern</a> …",
    "webHtml": "<a href=…>…</a>"
  }]
}

sequence zählt bei jeder Änderung hoch. pageUrl ist die Auswahlseite mit allen Kalendern (und bei rsvp der Anmeldung), qrUrl liefert ein PNG (&size=1024 für Druck), icsUrl die Datei für Apple und Outlook am PC. Zusagen und Absagen siehst du im Admin als Liste und CSV.

Zusatzfelder der Anmeldung (rsvpFields)

Bis zu 12 Felder. Jedes Feld hat id, label und type. Die Antworten landen als Spalten in der Liste und im CSV.

FeldTypBedeutung
idText, PflichtKurze Kennung ohne Leerzeichen, z. B. essen. Wird zum Spaltennamen.
labelText, PflichtDie Frage, bis 80 Zeichen.
typePflichttext, textarea, checkbox, select, multiselect, number, slider
requiredtrue | falsePflichtfeld bei Zusage.
helpTextHinweis unter dem Feld, bis 200 Zeichen.
optionsListe TextFür select und multiselect, 2 bis 20 Optionen.
min, max, stepZahlFür number und slider. slider braucht min und max.
unitTextEinheit hinter der Zahl, z. B. Personen.

Einmal-Link ohne Kalender: POST /api/build

Für Einladungen ohne Abo: JSON rein, Kurz-Link, QR, .ics, Kalender-Links und HTML-Snippets raus. Kein Token nötig. Das ist genau das, was der Generator macht.

FeldTypBedeutung
titleTextÜberschrift des Links, z. B. der Name der Veranstaltung.
timezoneIANAStandard Europe/Vienna.
rsvptrue | falseAnmeldung vor dem Kalender-Button.
rsvpFieldsListeWie oben.
events[]Liste, Pflicht1 bis 20 Termine mit title, start, end, location, description.
curl
curl -X POST https://calendar.digitalresults.app/api/build -H "Content-Type: application/json" -d '{
  "title": "BNI Workshop 2026", "timezone": "Europe/Vienna", "rsvp": true,
  "events": [
    { "title": "Workshop Tag 1", "start": "2026-09-18T09:00:00+02:00", "end": "2026-09-18T17:00:00+02:00", "location": "Linz", "description": "Bitte Laptop mitbringen." },
    { "title": "Workshop Tag 2", "start": "2026-09-19T09:00:00+02:00", "end": "2026-09-19T17:00:00+02:00", "location": "Linz" }
  ]
}'
200 OK
{
  "id": "PXWfS-6Y",
  "pageUrl": "https://calendar.digitalresults.app/s/PXWfS-6Y",
  "qrUrl": "https://calendar.digitalresults.app/qr?s=PXWfS-6Y",
  "icsUrl": "https://calendar.digitalresults.app/ics?d=…",
  "longUrl": "https://calendar.digitalresults.app/e?d=…",
  "rsvp": true,
  "answersUrl": "https://calendar.digitalresults.app/s/PXWfS-6Y/antworten?t=<geheim>",
  "links": [{ "google": "…", "outlook": "…", "office365": "…", "msTeams": "…" }],
  "emailHtml": "…", "webHtml": "…"
}

answersUrl ist geheim und wird nur einmal ausgegeben: Zusagen und Absagen als Liste, CSV unter derselben Adresse mit .csv.

Dynamische Links ohne API

Für Newsletter- und CRM-Tools, die nur Platzhalter in URLs einsetzen können. Die Auswahlseite baut sich aus den Parametern.

URL
https://calendar.digitalresults.app/e?title=Workshop%20Linz&start=2026-11-17T09:00&end=2026-11-17T17:00&location=Linz&description=Laptop%20mitbringen&tz=Europe/Vienna&heading=BNI%20Workshop
FeldTypBedeutung
titlePflichtTitel des Termins.
start, endPflichtWandzeit 2026-11-17T09:00 in der Zeitzone tz, oder ISO mit Offset.
location, descriptionOrt und Beschreibung, URL-kodiert.
tzIANAStandard Europe/Vienna.
headingÜberschrift der Seite.

Widget einbetten

Ein Script, ein Element. Das Widget rendert in einem Shadow DOM mit eigenem, neutralem Design und stört dein CSS nicht.

HTML
<script async src="https://calendar.digitalresults.app/widget.js"></script>

<!-- Einmal-Link aus /api/build oder dem Generator -->
<div data-atc-id="PXWfS-6Y"></div>

<!-- Kalender-Termin aus /api/events (slug aus der Antwort) -->
<div data-atc-event="workshop-linz-5e830a"></div>

<!-- Dynamisch, ohne Vorab-Registrierung -->
<div data-atc-title="Workshop Linz" data-atc-start="2026-11-17T09:00" data-atc-end="2026-11-17T17:00"
     data-atc-location="Linz" data-atc-description="Laptop mitbringen" data-atc-tz="Europe/Vienna"></div>

Die Daten dahinter liefert GET /api/widget?id=… bzw. ?slug=… als JSON mit CORS, falls du das Menü selbst bauen willst. Für Terminreihen gibt es zusätzlich den Monatskalender als iframe: https://calendar.digitalresults.app/embed/calendar/<slug>.

Feeds, Seiten, QR

FeldTypBedeutung
/calendar/<slug>SeiteKalender-Landingpage mit kommenden Terminen und Abo-Buttons (Apple, Outlook, Google, Andere).
/feed/<token>.icsFeedAbo-Feed mit allen Terminen, Serien, Ausnahmen, Absagen. Token kommt aus der Antwort von /api/events. Cache 5 Minuten.
/event/<slug>SeiteAuswahlseite eines Kalender-Termins, mit Anmeldung, wenn rsvp aktiv.
/event/<slug>/icsDatei.ics für Apple, Outlook am PC, Thunderbird.
/s/<id>SeiteAuswahlseite eines Einmal-Links.
/qr?slug=… · /qr?s=…PNGQR-Code auf die jeweilige Seite. &size=1024 für Druck.
/embed/calendar/<slug>iframeMonatskalender aller Termine mit Klick zur Termin-Seite.

Anmeldungen: POST /api/rsvp

Nur nötig, wenn du ein eigenes Formular baust. Die Termin-Seite erledigt das sonst selbst.

FeldTypBedeutung
slug | linkPflichtslug des Kalender-Termins oder id des Einmal-Links.
statusyes | noZusage oder Absage.
name, emailPflichtGleiche E-Mail ersetzt die vorherige Antwort.
phoneOptional.
answersObjektAntworten auf rsvpFields, Schlüssel = id des Felds. Wird nur bei yes geprüft.
curl
curl -X POST https://calendar.digitalresults.app/api/rsvp -H "Content-Type: application/json" -d '{ "slug": "workshop-linz-5e830a", "status": "yes", "name": "Anna Muster", "email": "anna@muster.at", "phone": "+43 664 0000000", "answers": { "essen": "Vegetarisch", "personen": 2 } }'

KI-Agenten: MCP-Server

Der Kalender ist ein MCP-Server (Model Context Protocol, Streamable HTTP). Claude Code, Claude Desktop, ChatGPT, Cursor und andere Agenten bekommen damit dieselben Möglichkeiten wie die REST-API, als Tools mit Beschreibung. Auth wie bei /api/events: Bearer-Token.

Claude Code
claude mcp add --transport http calendar https://calendar.digitalresults.app/api/mcp --header "Authorization: Bearer <API_TOKEN>"
Claude Desktop, ChatGPT, Cursor und andere (JSON-Konfiguration)
{
  "mcpServers": {
    "calendar": {
      "url": "https://calendar.digitalresults.app/api/mcp",
      "headers": { "Authorization": "Bearer <API_TOKEN>" }
    }
  }
}
Clients ohne HTTP-Transport (stdio über mcp-remote)
{ "mcpServers": { "calendar": { "command": "npx", "args": ["-y", "mcp-remote", "https://calendar.digitalresults.app/api/mcp", "--header", "Authorization: Bearer <API_TOKEN>"] } } }

Tools

FeldTypBedeutung
upsert_eventscalendar, calendarName?, timezone?, events[]Wie POST /api/events. Termine anlegen oder per uid aktualisieren, Kalender bei Bedarf anlegen. Liefert Links, QR, .ics, Feed.
build_linktitle?, timezone?, rsvp?, rsvpFields?, events[]Wie POST /api/build. Einmal-Link mit Auswahlseite, QR und Kalender-Links.
list_calendarsAlle Kalender mit Slug, Name, Zeitzone, Seite, Feed-URL.
list_eventscalendar, includePast?Termine eines Kalenders mit uid, slug, Zeiten, Status, Serie, Termin-Link.
cancel_eventcalendar, uid | slugTermin absagen. Abonnenten sehen die Absage beim nächsten Sync.
list_rsvpsslug | linkZusagen und Absagen mit Zähler, Name, E-Mail, Telefon und Antworten auf Zusatzfelder.

Beispiel-Anweisung an den Agenten: „Leg für den 17. November 9 bis 17 Uhr den Workshop Linz in den Kalender bni-linz, Ort Dauphinestraße 89, mit Anmeldung, und gib mir Termin-Link und QR.“

Grenzen

  • 1 bis 20 Termine pro Einmal-Link, Titel und Ort bis 200 Zeichen, Beschreibung bis 2000 Zeichen.
  • Google, Outlook.com, Microsoft 365 und Teams legen pro Klick einen Termin an. Mehrere Termine auf einmal nur per .ics oder Abo.
  • Abo-Sync liegt bei den Apps: Apple bis stündlich, Outlook etwa alle 3 Stunden, Google bis 24 Stunden.
  • Feeds sind ohne Login abrufbar, geschützt nur durch den langen Token in der URL.
  • Es gibt keine Bestätigungs-Mails an Teilnehmer. Antworten holst du im Admin oder über answersUrl.