Tickets-API
Whitelabel-Support-Ticket-System. Embedde unser Widget auf deiner Website oder bau dein eigenes UI auf der REST-API.
Base-URL: https://www.theredstonee.de/api/tickets/v1
Format: JSON. Alle Requests/Responses sind UTF-8.
Quick Start
1. Tenant erstellen unter tickets-api/
2. Du bekommst zwei Keys: pk_live_* (public, nur Create) und sk_live_* (secret, alles).
3. Widget einbauen oder API direkt nutzen:
<script src="https://www.theredstonee.de/widget/tickets.js"
data-key="pk_live_DEIN_KEY" async></script>await fetch('https://www.theredstonee.de/api/tickets/v1/create.php', {
method: 'POST',
headers: {
'Authorization': 'Bearer pk_live_DEIN_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'Max Mustermann',
email: 'max@example.com',
subject: 'Hilfe benötigt',
message: 'Es funktioniert nicht...',
}),
});curl -X POST https://www.theredstonee.de/api/tickets/v1/create.php \
-H 'Authorization: Bearer pk_live_DEIN_KEY' \
-H 'Content-Type: application/json' \
-d '{"name":"Max","email":"max@example.com","subject":"Hi","message":"Test"}'<?php
$ch = curl_init('https://www.theredstonee.de/api/tickets/v1/create.php');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer pk_live_DEIN_KEY',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'name' => 'Max',
'email' => 'max@example.com',
'subject' => 'Hi',
'message' => 'Test',
]),
CURLOPT_RETURNTRANSFER => true,
]);
$res = json_decode(curl_exec($ch), true);
curl_close($ch);import requests
res = requests.post(
'https://www.theredstonee.de/api/tickets/v1/create.php',
headers={'Authorization': 'Bearer pk_live_DEIN_KEY'},
json={
'name': 'Max',
'email': 'max@example.com',
'subject': 'Hi',
'message': 'Test',
},
).json()Authentifizierung
Bearer-Token im Authorization-Header. Zwei Key-Typen:
| Key-Prefix | Zweck | Sichtbarkeit | Endpoints |
|---|---|---|---|
pk_live_* | Public Key — fürs Widget | Darf im Frontend stehen | create, ticket (eigene), reply (eigene), close-request, upload (eigene), file (eigene), widget-config |
sk_live_* | Secret Key — Backend/API | Niemals im Frontend | Alle Endpoints, inkl. list, status, me |
Für Aktionen mit pk_live_ auf einem konkreten Ticket (lesen/antworten) musst du zusätzlich die E-Mail-Adresse des Tickets als email-Parameter mitschicken — sonst 403.
Endpoints
POST/create.php
Erstellt ein neues Ticket. Auth: pk oder sk.
Body (JSON):
| Feld | Typ | Pflicht | Beschreibung |
|---|---|---|---|
| name | string | ✓ | Name des Absenders, max 100 |
| string | ✓ | Gültige E-Mail-Adresse | |
| subject | string | ✓ | Betreff, max 200 |
| message | string | ✓ | Nachricht, min 5 / max 10000 |
| page_url | string | — | URL der Seite (für Tracking) |
Response: {"ok": true, "ticket": {...}}
GET/list.php
Listet Tickets. Auth: sk only.
Query-Parameter:
| Param | Typ | Default | Beschreibung |
|---|---|---|---|
| status | string | — | Filter: open / in_progress / closed |
| limit | int | 50 | Max 200 |
| offset | int | 0 | Pagination |
Response: {"ok": true, "total": N, "offset": 0, "limit": 50, "tickets": [...]}
GET/ticket.php?id=tkt_xxx
Liest ein einzelnes Ticket. Mit pk: zusätzlich email-Parameter nötig.
POST/reply.php
Fügt eine Antwort an. pk = als Kunde, sk = als Owner/Support.
| Feld | Typ | Pflicht | Beschreibung |
|---|---|---|---|
| ticket_id | string | ✓ | ID des Tickets |
| message | string | ✓ | Antwort-Text |
| string | pk only | E-Mail des Kunden (Match-Pflicht) | |
| author_name | string | sk only | Name des Antwortenden (z.B. "Anna Support") |
POST/status.php
Ändert den Status. Auth: sk only.
Body: {"ticket_id":"tkt_x", "status":"closed"} — erlaubt: open, in_progress, closed.
POST/close-request.php
Kunde fragt das Schließen des Tickets an. Owner muss bestätigen via Status-Change.
POST/upload.php
Datei-Upload (multipart/form-data). Max 5 MB. Allowed: jpg, png, gif, webp, pdf, txt, log, json, csv, zip.
curl -X POST https://www.theredstonee.de/api/tickets/v1/upload.php \
-H 'Authorization: Bearer pk_live_KEY' \
-F 'ticket_id=tkt_xxx' \
-F 'email=max@example.com' \
-F 'file=@screenshot.png'
GET/file.php?ticket_id=X&file_id=Y
Lädt eine angehängte Datei. Mit pk: zusätzlich email-Parameter.
GET/me.php
Tenant-Info + Stats. Auth: sk only.
JS-Widget
Drop-in Snippet mit Konfigurations-Attributen:
<script src="https://www.theredstonee.de/widget/tickets.js"
data-key="pk_live_KEY"
data-color="#3b82f6"
data-position="right"
data-brand="Mein Shop"
data-lang="de"
data-text-title="Wie können wir helfen?"
data-text-submit="Senden"
async></script>
Alle data-*-Attribute sind optional — Defaults kommen aus deinem Dashboard.
| Attribut | Werte | Beschreibung |
|---|---|---|
| data-key | pk_live_* | Pflicht |
| data-color | #hex | Primärfarbe (überschreibt Dashboard) |
| data-position | right / left | Position |
| data-brand | String | Brand-Name im Header |
| data-lang | de / en | Sprache |
| data-text-* | String | title, subtitle, bubble, name, email, subject, message, submit, success, error |
Webhooks
Wenn du im Dashboard eine Webhook-URL setzt, POSTen wir JSON bei jedem Event.
Payload-Format:
{
"event": "ticket.created",
"tenant_id": "tnt_xxx",
"data": { /* event-spezifisch */ },
"ts": "2026-05-24 15:30:00"
}
Events:
| Event | data enthält |
|---|---|
| ticket.created | Vollständiges Ticket-Objekt |
| ticket.reply | ticket_id, reply, is_customer |
| ticket.status | ticket_id, status |
| ticket.close_requested | ticket_id |
Slack-kompatibel: setze eine Slack-Incoming-Webhook-URL. Wir senden minimal-formatiert.
Fehler-Codes
| HTTP | Bedeutung |
|---|---|
| 400 | Validierungsfehler (fehlende/ungültige Felder) |
| 401 | Ungültiger oder fehlender Key |
| 403 | Tenant noch nicht freigegeben oder fehlende Berechtigung |
| 404 | Ticket nicht gefunden |
| 413 | Datei zu groß (> 5 MB) |
| 415 | Datei-Typ nicht erlaubt |
| 429 | Rate-Limit überschritten |
| 500 | Server-Fehler |
Alle Fehler-Responses haben das Format: {"ok": false, "error": "Beschreibung"}
Rate-Limits
Pro Key, gleitendes 60-Sekunden-Fenster:
pk_live_*(Widget): 20 Requests/Minutesk_live_*(Backend): 120 Requests/Minute
Bei Überschreitung: 429. Warte 60 Sekunden und versuche es erneut.
Noch Fragen? Schreib uns ein Ticket oder check den Discord.