REST API · Version 1

Tickets aus Ihren Systemen anlegen und bearbeiten.

Die API bildet den vollständigen öffentlichen Ticket-Workflow ab: Teams lesen, Tickets anlegen und filtern, antworten, zusammenführen und Anhänge laden. Interne Notizen verlassen 1stlevel.tech nicht.

In drei Schritten

Schnellstart

  1. 01 · TokenAls Admin unter Konto → API erstellen und sofort sicher speichern.
  2. 02 · TeamMit GET /teams die passende Team-ID ermitteln.
  3. 03 · TicketTicket mit einem eindeutigen Idempotency-Key anlegen.
curlhttps://1stlevel.tech/api/v1
curl "https://1stlevel.tech/api/v1/tickets" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: crm-case-4711" \
  -d '{
    "team": 1,
    "requester_email": "kunde@example.com",
    "requester_name": "Ada Beispiel",
    "subject": "VPN-Zugang funktioniert nicht",
    "body": "Seit heute schlägt die Anmeldung fehl.",
    "external_ref": "CRM-4711",
    "locale": "de"
  }'

Alle Antworten sind JSON, außer dem Binärdownload eines Anhangs. Ressourcen liegen unter data; Listen enthalten zusätzlich links und meta.

Zugriff, Scopes und Limits

Bearer-Token

Senden Sie den Token als Authorization: Bearer <token>. In der Oberfläche erzeugte Tokens laufen nach einem Jahr ab und werden beim Widerruf oder nach einem Passwort-Reset ungültig.

Berechtigungen

tickets:read erlaubt GET. Schreibende Anfragen brauchen zusätzlich tickets:write, ein nutzbares Konto und einen Tarif mit API-Schreibzugriff. Die E-Mail-Adresse des Token-Inhabers muss bestätigt sein.

Team-Bereich

Ein Token kann auf ein Team beschränkt sein. Fremde Tickets und Anhänge antworten dann mit 404; beim Anlegen mit abweichender Team-ID folgt 403. Ohne Team-Beschränkung gelten alle Teams des Mandanten.

Rate Limits pro Token

Solo
60/min · nur lesen
Test & Team
120/min
Team+
300/min

Bei 429 gibt Retry-After die Wartezeit an.

POST sicher wiederholen

Jede POST-Anfrage benötigt Idempotency-Key mit 1–128 Zeichen aus A–Z a–z 0–9 . _ : -. Für einen Retry senden Sie denselben Schlüssel, dieselbe Route und exakt dieselben rohen JSON-Bytes. Eine erfolgreiche Wiederholung liefert die ursprüngliche Antwort mit Idempotency-Replayed: true. Wird derselbe Schlüssel für eine andere Anfrage verwendet, antwortet die API mit 409. Gespeicherte Antworten bleiben bis zum Widerruf des Tokens erhalten.

PATCH und GET benötigen keinen Idempotency-Key.

Alle Endpunkte

GET
/teamsTeams abrufen

Team-IDs und Namen im Token-Bereich.

GET
/ticketsTickets auflisten

Paginiert, filterbar und auf den Token-Bereich begrenzt.

POST
/ticketsTicket anlegen

Legt Ticket und erste eingehende Nachricht gemeinsam an.

GET
/tickets/{ticketId}Ticket abrufen

Enthält ein- und ausgehende Nachrichten, aber keine internen Notizen.

PATCH
/tickets/{ticketId}Ticket bearbeiten

Ändert Status, Priorität, Zuweisung, Kategorie, Sprache oder Wiedervorlage.

POST
/tickets/{ticketId}/messagesNachricht hinzufügen

Erfasst eine Kundennachricht oder stellt eine Antwort in die Versandwarteschlange.

POST
/tickets/{ticketId}/mergeTickets zusammenführen

Verschiebt Nachrichten und Anhänge in das Zielticket.

GET
/attachments/{attachmentId}Anhang laden

Privater Binärdownload innerhalb des Mandanten- und Team-Bereichs.

Tickets

Listen, lesen, anlegen, ändern

GET/tickets

Liefert Tickets nach ID sortiert und paginiert. Standard: 25 Einträge, maximal 100.

QueryTypWirkung
statusopen | in_progress | doneExakter Status
teamIntegerTeam innerhalb des Token-Bereichs
due_todayBooleanOffene Tickets mit Wiedervorlage heute oder früher
requester_emailE-MailExakte Adresse, ohne Beachtung der Großschreibung
external_refString ≤ 255Exakte externe Referenz
updated_sinceDatum/ZeitSeit diesem Zeitpunkt aktualisiert
per_page1–100Seitengröße, Standard 25
pageIntegerSeitennummer

POST/tickets

Pflichtfelder: team (entfällt bei Team-Token), requester_email (max. 190), subject (max. 200) und body (1–100.000 Zeichen). Optional: requester_name (max. 190), locale (de oder en), external_ref (max. 255) und attachments. Antwort: 201 mit Ticket und erster Nachricht.

GET/tickets/{ticketId}

Liefert das Ticket mit sichtbaren Nachrichten und deren Anhängen. Interne Notizen werden weder ausgegeben noch angedeutet. Ein zusammengeführtes Quellticket bleibt als schreibgeschützter Verweis mit merged_into_id lesbar.

PATCH/tickets/{ticketId}

FeldZulässige Werte
statusopen, in_progress, done
prioritynormal, high
assignee_idExistierende Benutzer-ID oder null
categoryhardware, software, access, network, other oder null
localede oder en
due_atYYYY-MM-DD oder null

POST/tickets/{ticketId}/merge

Body: {"target_id": 456}. Quelle und Ziel müssen demselben Team und derselben anfragenden E-Mail-Adresse gehören. Nachrichten und Anhänge wandern atomar zum Ziel; das Ziel öffnet wieder, wenn die Quelle noch offen war. Die Quell-ID bleibt lesbar und verweist über merged_into_id auf das Ziel. Weitere Änderungen an der Quelle antworten mit 409.

Ticket-Antwort
{
  "data": {
    "id": 123,
    "number": 1042,
    "merged_into_id": null,
    "team": 1,
    "requester_email": "kunde@example.com",
    "requester_name": "Ada Beispiel",
    "subject": "VPN-Zugang funktioniert nicht",
    "status": "open",
    "priority": "normal",
    "category": "access",
    "due_at": null,
    "assignee_id": null,
    "locale": "de",
    "external_ref": "CRM-4711",
    "created_at": "2026-09-09T10:15:00+00:00",
    "updated_at": "2026-09-09T10:15:00+00:00",
    "messages": []
  }
}

Nachrichten

Kundennachricht erfassen oder antworten

POST/tickets/{ticketId}/messages

Pflichtfelder: direction (in oder out) und body (1–100.000 Zeichen). in erfasst eine eingegangene Nachricht. out legt eine Antwort an und stellt die E-Mail asynchron zu; delivery_status beginnt dann mit pending. Anhänge sind optional. Interne Notizen können über die öffentliche API nicht angelegt werden.

{"direction":"out","body":"Bitte versuchen Sie die Anmeldung erneut."}

Anhänge

Base64 hinein, privater Download heraus

Ticket- und Nachrichten-POSTs akzeptieren maximal 10 Anhänge. Pro Datei gelten 10 MiB, für alle dekodierten Dateien einer Nachricht zusammen 15 MiB. Dateinamen sind höchstens 200 Zeichen lang. Nur name und content_base64 sind erlaubt; ungültiges Base64 liefert 422.

"attachments": [{"name":"diagnose.txt","content_base64":"SGVsbG8="}]

Nachrichtenantworten enthalten id, name, size, mime_type, sha256 und download_url. Rufen Sie diese URL mit demselben Bearer-Token ab. Die Antwort ist application/octet-stream, nicht JSON; Team- und Leserechte gelten weiterhin.

Antworten

Statuscodes und Fehlerformat

CodeBedeutung
200 / 201Erfolgreich / Ressource angelegt
401Token fehlt, ist ungültig, abgelaufen oder widerrufen
403Fähigkeit, Tarif, Kontostatus, E-Mail-Bestätigung oder Team-Zugriff fehlt
404Ressource existiert im Token-Bereich nicht
409Idempotency-Key kollidiert oder zusammengeführte Quelle ist schreibgeschützt
422Validierung fehlgeschlagen oder Idempotency-Key fehlt/ist ungültig
429Rate Limit erreicht; Retry-After beachten
{
  "message": "The requester email field is required.",
  "errors": {"requester_email": ["The requester email field is required."]}
}

Webhooks

Änderungen empfangen

Admins hinterlegen unter Konto → API bis zu 10 Empfänger, optional auf ein Team beschränkt. Zulässig sind ausschließlich öffentlich erreichbare HTTPS-Adressen auf Port 443. 1stlevel.tech folgt keinen Weiterleitungen und prüft die Zieladresse vor jeder Zustellung.

ticket.created

Ticket wurde angelegt.

ticket.status_changed

Status hat sich geändert.

message.created

Sichtbare Nachricht wurde angelegt.

Webhook-Nutzlast
{
  "id": "a9635c13-949f-4f68-a006-e53aa6f9f912",
  "type": "message.created",
  "created_at": "2026-09-09T10:18:00+00:00",
  "data": {
    "ticket_id": 123,
    "merged_into_id": null,
    "team_id": 1,
    "status": "open",
    "external_ref": "CRM-4711",
    "message": {"id": 987, "direction": "out", "body": "Bitte versuchen Sie die Anmeldung erneut."}
  }
}

Signatur prüfen

Der einmal angezeigte Webhook-Schlüssel signiert timestamp + "." + rawBody mit HMAC-SHA256. Lesen Sie t und v1 aus X-1stlevel-Signature, lehnen Sie Zeitstempel außerhalb von fünf Minuten ab und vergleichen Sie die Hex-Signatur konstantzeitlich. Verwenden Sie unveränderte Request-Bytes, nicht erneut serialisiertes JSON.

X-1stlevel-Event-Id: <UUID>
X-1stlevel-Signature: t=<Unix>,v1=<64 hex>

Zustellung erfolgt mindestens einmal und ohne Reihenfolgegarantie. Deduplizieren Sie anhand der Ereignis-UUID. Bei Fehlern folgen bis zu acht Versuche mit wachsendem Abstand; jeder HTTP-Status außerhalb 200–299 gilt als Fehler. Interne Notizen erzeugen nie Webhooks.