{"openapi":"3.1.0","info":{"title":"Ticket Transcript API","version":"1.0.0","description":"API zum Speichern und Anzeigen von Discord-Ticket-Transcripts. Bots schreiben über die Bot API (`/api/v1`), Nutzer und Teammitglieder lesen nach Discord-Login über den Web-Viewer bzw. die Web API."},"servers":[{"url":"https://transcripts.dislook.buzz"}],"tags":[{"name":"Bot API","description":"Für Discord-Bots. Authentifizierung per API-Key."},{"name":"Web API","description":"Für eingeloggte Nutzer (Session-Cookie nach Discord-Login)."},{"name":"Auth","description":"Discord OAuth2"},{"name":"Admin","description":"Verwaltung (Master-Key)"}],"paths":{"/api/v1/me":{"get":{"tags":["Bot API"],"summary":"API-Key prüfen","description":"Gibt Informationen über den verwendeten Key zurück (Scopes, Guilds). Gut zum Testen der Verbindung.","security":[{"BotKey":[]}],"parameters":[],"responses":{"200":{"description":"Key-Informationen","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyResponse"}}}},"401":{"description":"Nicht authentifiziert","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/guilds/{guildId}":{"put":{"tags":["Bot API"],"summary":"Guild registrieren / konfigurieren","description":"Legt die Guild an oder aktualisiert sie. Hier werden u.a. die Staff-Rollen gesetzt, deren Mitglieder alle Transcripts sehen dürfen.\n\n**Benötigter Scope:** `guilds:write`","security":[{"BotKey":[]}],"parameters":[{"name":"guildId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Aktualisiert","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GuildResponse"}}}},"201":{"description":"Angelegt","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GuildResponse"}}}},"400":{"description":"Validierungsfehler","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Keine Berechtigung","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"iconHash":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}]},"staffRoleIds":{"maxItems":50,"type":"array","items":{"type":"string","pattern":"^\\d{15,21}$","description":"Discord-ID (Snowflake) als String"}},"managerRoleIds":{"maxItems":50,"type":"array","items":{"type":"string","pattern":"^\\d{15,21}$","description":"Discord-ID (Snowflake) als String"}},"allowParticipantAccess":{"type":"boolean"},"retentionDays":{"anyOf":[{"type":"integer","minimum":1,"maximum":3650},{"type":"null"}],"description":"null = nie automatisch löschen"}},"description":"GuildUpsert"}}}}},"get":{"tags":["Bot API"],"summary":"Guild-Konfiguration abrufen","description":"**Benötigter Scope:** `guilds:read`","security":[{"BotKey":[]}],"parameters":[{"name":"guildId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Guild","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GuildResponse"}}}},"404":{"description":"Nicht gefunden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/guilds/{guildId}/stats":{"get":{"tags":["Bot API"],"summary":"Statistiken einer Guild","description":"**Benötigter Scope:** `guilds:read`","security":[{"BotKey":[]}],"parameters":[{"name":"guildId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Statistiken","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GuildStatsResponse"}}}},"404":{"description":"Nicht gefunden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/transcripts":{"post":{"tags":["Bot API"],"summary":"Transcript erstellen (Upsert über externalId)","description":"Erstellt ein Transcript inkl. bis zu 1000 Nachrichten. Ist `externalId` gesetzt und existiert bereits, wird das Transcript aktualisiert und neue Nachrichten werden angehängt (idempotent – Duplikate per Message-ID werden übersprungen). Für größere Tickets weitere Nachrichten über `POST /api/v1/transcripts/{id}/messages` nachschieben. Unterstützt `Content-Encoding: gzip`.\n\n**Benötigter Scope:** `transcripts:write`","security":[{"BotKey":[]}],"parameters":[],"responses":{"200":{"description":"Bestehendes Transcript aktualisiert","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TranscriptWriteResponse"}}}},"201":{"description":"Erstellt","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TranscriptWriteResponse"}}}},"400":{"description":"Validierungsfehler","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Keine Berechtigung","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Body zu groß","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"guildId":{"type":"string","pattern":"^\\d{15,21}$","description":"Discord-ID (Snowflake) als String"},"guild":{"description":"Legt die Guild an bzw. aktualisiert Name/Icon","type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"iconHash":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}]}},"required":["name"]},"externalId":{"description":"Ticket-ID in deinem Bot. Gleiche externalId = Upsert (idempotent)","type":"string","minLength":1,"maxLength":128},"ticketNumber":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"channel":{"type":"object","properties":{"id":{"type":"string","pattern":"^\\d{15,21}$","description":"Discord-ID (Snowflake) als String"},"name":{"type":"string","minLength":1,"maxLength":100}},"required":["id","name"]},"category":{"type":"string","maxLength":100},"subject":{"type":"string","maxLength":500},"tags":{"default":[],"maxItems":20,"type":"array","items":{"type":"string","minLength":1,"maxLength":50}},"status":{"default":"CLOSED","type":"string","enum":["OPEN","CLOSED"]},"opener":{"type":"object","properties":{"id":{"type":"string","pattern":"^\\d{15,21}$","description":"Discord-ID (Snowflake) als String"},"username":{"type":"string","minLength":1,"maxLength":100},"displayName":{"description":"Server-Nickname oder Global Name","anyOf":[{"type":"string","maxLength":100},{"type":"null"}]},"avatarUrl":{"anyOf":[{"type":"string","maxLength":4096,"format":"uri"},{"type":"null"}]},"bot":{"default":false,"type":"boolean"},"color":{"description":"Farbe der höchsten Rolle als Integer","anyOf":[{"type":"integer","minimum":0,"maximum":16777215},{"type":"null"}]}},"required":["id","username"],"description":"DiscordUser"},"claimedBy":{"type":"object","properties":{"id":{"type":"string","pattern":"^\\d{15,21}$","description":"Discord-ID (Snowflake) als String"},"username":{"type":"string","minLength":1,"maxLength":100},"displayName":{"description":"Server-Nickname oder Global Name","anyOf":[{"type":"string","maxLength":100},{"type":"null"}]},"avatarUrl":{"anyOf":[{"type":"string","maxLength":4096,"format":"uri"},{"type":"null"}]},"bot":{"default":false,"type":"boolean"},"color":{"description":"Farbe der höchsten Rolle als Integer","anyOf":[{"type":"integer","minimum":0,"maximum":16777215},{"type":"null"}]}},"required":["id","username"],"description":"DiscordUser"},"closedBy":{"type":"object","properties":{"id":{"type":"string","pattern":"^\\d{15,21}$","description":"Discord-ID (Snowflake) als String"},"username":{"type":"string","minLength":1,"maxLength":100},"displayName":{"description":"Server-Nickname oder Global Name","anyOf":[{"type":"string","maxLength":100},{"type":"null"}]},"avatarUrl":{"anyOf":[{"type":"string","maxLength":4096,"format":"uri"},{"type":"null"}]},"bot":{"default":false,"type":"boolean"},"color":{"description":"Farbe der höchsten Rolle als Integer","anyOf":[{"type":"integer","minimum":0,"maximum":16777215},{"type":"null"}]}},"required":["id","username"],"description":"DiscordUser"},"closeReason":{"type":"string","maxLength":2000},"openedAt":{"type":"string","format":"date-time"},"closedAt":{"type":"string","format":"date-time"},"participants":{"default":[],"maxItems":500,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^\\d{15,21}$","description":"Discord-ID (Snowflake) als String"},"username":{"type":"string","minLength":1,"maxLength":100},"displayName":{"description":"Server-Nickname oder Global Name","anyOf":[{"type":"string","maxLength":100},{"type":"null"}]},"avatarUrl":{"anyOf":[{"type":"string","maxLength":4096,"format":"uri"},{"type":"null"}]},"bot":{"default":false,"type":"boolean"},"color":{"description":"Farbe der höchsten Rolle als Integer","anyOf":[{"type":"integer","minimum":0,"maximum":16777215},{"type":"null"}]},"role":{"description":"Rolle im Ticket (Standard: aus Kontext abgeleitet)","type":"string","enum":["OPENER","STAFF","MEMBER","BOT"]},"canView":{"description":"Darf der Nutzer das Transcript im Web-Viewer sehen? (Standard: true)","type":"boolean"}},"required":["id","username"]}},"resolved":{"type":"object","properties":{"users":{"type":"object","propertyNames":{"type":"string","pattern":"^\\d{15,21}$","description":"Discord-ID (Snowflake) als String"},"additionalProperties":{"type":"object","properties":{"name":{"type":"string","maxLength":100}},"required":["name"]}},"roles":{"type":"object","propertyNames":{"type":"string","pattern":"^\\d{15,21}$","description":"Discord-ID (Snowflake) als String"},"additionalProperties":{"type":"object","properties":{"name":{"type":"string","maxLength":100},"color":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]}},"required":["name"]}},"channels":{"type":"object","propertyNames":{"type":"string","pattern":"^\\d{15,21}$","description":"Discord-ID (Snowflake) als String"},"additionalProperties":{"type":"object","properties":{"name":{"type":"string","maxLength":100}},"required":["name"]}}},"description":"Namen für Mentions (<@id>, <@&id>, <#id>) im Nachrichtentext"},"metadata":{"type":"object","propertyNames":{"type":"string","maxLength":64},"additionalProperties":{},"description":"Beliebige zusätzliche Daten deines Bots (max. 16 KB)"},"messages":{"default":[],"maxItems":1000,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^\\d{15,21}$","description":"Discord-Message-ID (dient der Deduplizierung)"},"author":{"type":"object","properties":{"id":{"type":"string","pattern":"^\\d{15,21}$","description":"Discord-ID (Snowflake) als String"},"username":{"type":"string","minLength":1,"maxLength":100},"displayName":{"description":"Server-Nickname oder Global Name","anyOf":[{"type":"string","maxLength":100},{"type":"null"}]},"avatarUrl":{"anyOf":[{"type":"string","maxLength":4096,"format":"uri"},{"type":"null"}]},"bot":{"default":false,"type":"boolean"},"color":{"description":"Farbe der höchsten Rolle als Integer","anyOf":[{"type":"integer","minimum":0,"maximum":16777215},{"type":"null"}]}},"required":["id","username"],"description":"DiscordUser"},"content":{"default":"","type":"string","maxLength":10000},"timestamp":{"description":"Erstellungszeitpunkt (ISO-8601 oder Unix-ms)","type":"string","format":"date-time"},"editedTimestamp":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"type":{"default":0,"description":"Discord-Message-Type","type":"integer","minimum":0,"maximum":9007199254740991},"pinned":{"default":false,"type":"boolean"},"replyTo":{"description":"ID der Nachricht, auf die geantwortet wird","anyOf":[{"type":"string","pattern":"^\\d{15,21}$","description":"Discord-ID (Snowflake) als String"},{"type":"null"}]},"embeds":{"default":[],"maxItems":10,"type":"array","items":{"type":"object","properties":{"title":{"type":"string","maxLength":256},"description":{"type":"string","maxLength":4096},"url":{"type":"string","maxLength":2048},"color":{"type":"integer","minimum":0,"maximum":16777215},"timestamp":{"type":"string","maxLength":64},"author":{"type":"object","properties":{"name":{"type":"string","maxLength":256},"url":{"type":"string"},"icon_url":{"type":"string"}},"required":["name"],"additionalProperties":{}},"footer":{"type":"object","properties":{"text":{"type":"string","maxLength":2048},"icon_url":{"type":"string"}},"required":["text"],"additionalProperties":{}},"image":{"type":"object","properties":{"url":{"type":"string","maxLength":4096}},"required":["url"],"additionalProperties":{}},"thumbnail":{"type":"object","properties":{"url":{"type":"string","maxLength":4096}},"required":["url"],"additionalProperties":{}},"fields":{"maxItems":25,"type":"array","items":{"type":"object","properties":{"name":{"type":"string","maxLength":256},"value":{"type":"string","maxLength":1024},"inline":{"type":"boolean"}},"required":["name","value"]}}},"additionalProperties":{},"description":"Discord-Embed im Format der Discord-API (snake_case)"}},"attachments":{"default":[],"maxItems":25,"type":"array","items":{"type":"object","properties":{"id":{"description":"Discord-Attachment-ID","type":"string","pattern":"^\\d{15,21}$"},"filename":{"type":"string","minLength":1,"maxLength":255},"url":{"type":"string","maxLength":4096,"format":"uri","description":"Discord-CDN-URL – wird (falls aktiviert) automatisch archiviert"},"contentType":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}]},"size":{"default":0,"type":"integer","minimum":0,"maximum":9007199254740991},"width":{"anyOf":[{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},{"type":"null"}]},"height":{"anyOf":[{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},{"type":"null"}]},"spoiler":{"default":false,"type":"boolean"}},"required":["filename","url"]}},"stickers":{"default":[],"maxItems":3,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^\\d{15,21}$","description":"Discord-ID (Snowflake) als String"},"name":{"type":"string","maxLength":100},"formatType":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["id","name"]}},"reactions":{"default":[],"maxItems":50,"type":"array","items":{"type":"object","properties":{"emoji":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^\\d{15,21}$","description":"Discord-ID (Snowflake) als String"},{"type":"null"}]},"name":{"type":"string","maxLength":100},"animated":{"default":false,"type":"boolean"}},"required":["name"]},"count":{"type":"integer","minimum":1,"maximum":9007199254740991}},"required":["emoji","count"]}},"components":{"default":[],"description":"Rohe Discord-Components (Buttons etc.)","maxItems":10,"type":"array","items":{}}},"required":["id","author","timestamp"]}}},"required":["guildId","channel","opener","openedAt"],"description":"TranscriptCreate"}}}}},"get":{"tags":["Bot API"],"summary":"Transcripts auflisten / suchen","description":"Cursor-basierte Pagination: `nextCursor` der Antwort als `cursor` übergeben.\n\n**Benötigter Scope:** `transcripts:read`","security":[{"BotKey":[]}],"parameters":[{"name":"guildId","in":"query","required":false,"description":"Discord-ID (Snowflake) als String","schema":{"type":"string","pattern":"^\\d{15,21}$","description":"Discord-ID (Snowflake) als String"}},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["OPEN","CLOSED"]}},{"name":"userId","in":"query","required":false,"description":"Nur Transcripts, an denen dieser Nutzer beteiligt ist","schema":{"description":"Nur Transcripts, an denen dieser Nutzer beteiligt ist","type":"string","pattern":"^\\d{15,21}$"}},{"name":"openerId","in":"query","required":false,"description":"Discord-ID (Snowflake) als String","schema":{"type":"string","pattern":"^\\d{15,21}$","description":"Discord-ID (Snowflake) als String"}},{"name":"externalId","in":"query","required":false,"schema":{"type":"string","maxLength":128}},{"name":"category","in":"query","required":false,"schema":{"type":"string","maxLength":100}},{"name":"tag","in":"query","required":false,"schema":{"type":"string","maxLength":50}},{"name":"q","in":"query","required":false,"description":"Suche in Kanalname, Betreff, externalId","schema":{"description":"Suche in Kanalname, Betreff, externalId","type":"string","minLength":1,"maxLength":200}},{"name":"searchMessages","in":"query","required":false,"description":"Suche zusätzlich im Nachrichtentext","schema":{"description":"Suche zusätzlich im Nachrichtentext","type":"string","enum":["0","1","true","false"]}},{"name":"from","in":"query","required":false,"description":"openedAt >= from","schema":{"description":"openedAt >= from","type":"string","format":"date-time"}},{"name":"to","in":"query","required":false,"description":"openedAt <= to","schema":{"description":"openedAt <= to","type":"string","format":"date-time"}},{"name":"limit","in":"query","required":false,"schema":{"default":25,"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","required":false,"description":"nextCursor aus der vorherigen Antwort","schema":{"description":"nextCursor aus der vorherigen Antwort","type":"string","maxLength":64}}],"responses":{"200":{"description":"Liste","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TranscriptList"}}}},"400":{"description":"Validierungsfehler","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/transcripts/{transcriptId}":{"get":{"tags":["Bot API"],"summary":"Transcript abrufen (inkl. Teilnehmer, Metadaten)","description":"**Benötigter Scope:** `transcripts:read`","security":[{"BotKey":[]}],"parameters":[{"name":"transcriptId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Transcript","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TranscriptResponse"}}}},"404":{"description":"Nicht gefunden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"tags":["Bot API"],"summary":"Transcript aktualisieren","description":"**Benötigter Scope:** `transcripts:write`","security":[{"BotKey":[]}],"parameters":[{"name":"transcriptId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Aktualisiert","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TranscriptResponse"}}}},"404":{"description":"Nicht gefunden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"ticketNumber":{"anyOf":[{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},{"type":"null"}]},"channelName":{"type":"string","minLength":1,"maxLength":100},"category":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}]},"subject":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}]},"tags":{"maxItems":20,"type":"array","items":{"type":"string","minLength":1,"maxLength":50}},"status":{"type":"string","enum":["OPEN","CLOSED"]},"claimedBy":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^\\d{15,21}$","description":"Discord-ID (Snowflake) als String"},"username":{"type":"string","minLength":1,"maxLength":100},"displayName":{"description":"Server-Nickname oder Global Name","anyOf":[{"type":"string","maxLength":100},{"type":"null"}]},"avatarUrl":{"anyOf":[{"type":"string","maxLength":4096,"format":"uri"},{"type":"null"}]},"bot":{"default":false,"type":"boolean"},"color":{"description":"Farbe der höchsten Rolle als Integer","anyOf":[{"type":"integer","minimum":0,"maximum":16777215},{"type":"null"}]}},"required":["id","username"],"description":"DiscordUser"},{"type":"null"}]},"closedBy":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^\\d{15,21}$","description":"Discord-ID (Snowflake) als String"},"username":{"type":"string","minLength":1,"maxLength":100},"displayName":{"description":"Server-Nickname oder Global Name","anyOf":[{"type":"string","maxLength":100},{"type":"null"}]},"avatarUrl":{"anyOf":[{"type":"string","maxLength":4096,"format":"uri"},{"type":"null"}]},"bot":{"default":false,"type":"boolean"},"color":{"description":"Farbe der höchsten Rolle als Integer","anyOf":[{"type":"integer","minimum":0,"maximum":16777215},{"type":"null"}]}},"required":["id","username"],"description":"DiscordUser"},{"type":"null"}]},"closeReason":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}]},"closedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"resolved":{"anyOf":[{"type":"object","properties":{"users":{"type":"object","propertyNames":{"type":"string","pattern":"^\\d{15,21}$","description":"Discord-ID (Snowflake) als String"},"additionalProperties":{"type":"object","properties":{"name":{"type":"string","maxLength":100}},"required":["name"]}},"roles":{"type":"object","propertyNames":{"type":"string","pattern":"^\\d{15,21}$","description":"Discord-ID (Snowflake) als String"},"additionalProperties":{"type":"object","properties":{"name":{"type":"string","maxLength":100},"color":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]}},"required":["name"]}},"channels":{"type":"object","propertyNames":{"type":"string","pattern":"^\\d{15,21}$","description":"Discord-ID (Snowflake) als String"},"additionalProperties":{"type":"object","properties":{"name":{"type":"string","maxLength":100}},"required":["name"]}}},"description":"Namen für Mentions (<@id>, <@&id>, <#id>) im Nachrichtentext"},{"type":"null"}]},"metadata":{"anyOf":[{"type":"object","propertyNames":{"type":"string","maxLength":64},"additionalProperties":{},"description":"Beliebige zusätzliche Daten deines Bots (max. 16 KB)"},{"type":"null"}]}},"description":"TranscriptUpdate"}}}}},"delete":{"tags":["Bot API"],"summary":"Transcript löschen (inkl. Dateien)","description":"**Benötigter Scope:** `transcripts:delete`","security":[{"BotKey":[]}],"parameters":[{"name":"transcriptId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Gelöscht"},"404":{"description":"Nicht gefunden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/transcripts/{transcriptId}/messages":{"post":{"tags":["Bot API"],"summary":"Nachrichten anhängen","description":"Bis zu 1000 Nachrichten pro Request. Bereits vorhandene Message-IDs werden übersprungen – Requests können also gefahrlos wiederholt werden.\n\n**Benötigter Scope:** `transcripts:write`","security":[{"BotKey":[]}],"parameters":[{"name":"transcriptId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Alles Duplikate","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppendResult"}}}},"201":{"description":"Eingefügt","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppendResult"}}}},"404":{"description":"Nicht gefunden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"messages":{"minItems":1,"maxItems":1000,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^\\d{15,21}$","description":"Discord-Message-ID (dient der Deduplizierung)"},"author":{"type":"object","properties":{"id":{"type":"string","pattern":"^\\d{15,21}$","description":"Discord-ID (Snowflake) als String"},"username":{"type":"string","minLength":1,"maxLength":100},"displayName":{"description":"Server-Nickname oder Global Name","anyOf":[{"type":"string","maxLength":100},{"type":"null"}]},"avatarUrl":{"anyOf":[{"type":"string","maxLength":4096,"format":"uri"},{"type":"null"}]},"bot":{"default":false,"type":"boolean"},"color":{"description":"Farbe der höchsten Rolle als Integer","anyOf":[{"type":"integer","minimum":0,"maximum":16777215},{"type":"null"}]}},"required":["id","username"],"description":"DiscordUser"},"content":{"default":"","type":"string","maxLength":10000},"timestamp":{"description":"Erstellungszeitpunkt (ISO-8601 oder Unix-ms)","type":"string","format":"date-time"},"editedTimestamp":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"type":{"default":0,"description":"Discord-Message-Type","type":"integer","minimum":0,"maximum":9007199254740991},"pinned":{"default":false,"type":"boolean"},"replyTo":{"description":"ID der Nachricht, auf die geantwortet wird","anyOf":[{"type":"string","pattern":"^\\d{15,21}$","description":"Discord-ID (Snowflake) als String"},{"type":"null"}]},"embeds":{"default":[],"maxItems":10,"type":"array","items":{"type":"object","properties":{"title":{"type":"string","maxLength":256},"description":{"type":"string","maxLength":4096},"url":{"type":"string","maxLength":2048},"color":{"type":"integer","minimum":0,"maximum":16777215},"timestamp":{"type":"string","maxLength":64},"author":{"type":"object","properties":{"name":{"type":"string","maxLength":256},"url":{"type":"string"},"icon_url":{"type":"string"}},"required":["name"],"additionalProperties":{}},"footer":{"type":"object","properties":{"text":{"type":"string","maxLength":2048},"icon_url":{"type":"string"}},"required":["text"],"additionalProperties":{}},"image":{"type":"object","properties":{"url":{"type":"string","maxLength":4096}},"required":["url"],"additionalProperties":{}},"thumbnail":{"type":"object","properties":{"url":{"type":"string","maxLength":4096}},"required":["url"],"additionalProperties":{}},"fields":{"maxItems":25,"type":"array","items":{"type":"object","properties":{"name":{"type":"string","maxLength":256},"value":{"type":"string","maxLength":1024},"inline":{"type":"boolean"}},"required":["name","value"]}}},"additionalProperties":{},"description":"Discord-Embed im Format der Discord-API (snake_case)"}},"attachments":{"default":[],"maxItems":25,"type":"array","items":{"type":"object","properties":{"id":{"description":"Discord-Attachment-ID","type":"string","pattern":"^\\d{15,21}$"},"filename":{"type":"string","minLength":1,"maxLength":255},"url":{"type":"string","maxLength":4096,"format":"uri","description":"Discord-CDN-URL – wird (falls aktiviert) automatisch archiviert"},"contentType":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}]},"size":{"default":0,"type":"integer","minimum":0,"maximum":9007199254740991},"width":{"anyOf":[{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},{"type":"null"}]},"height":{"anyOf":[{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},{"type":"null"}]},"spoiler":{"default":false,"type":"boolean"}},"required":["filename","url"]}},"stickers":{"default":[],"maxItems":3,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^\\d{15,21}$","description":"Discord-ID (Snowflake) als String"},"name":{"type":"string","maxLength":100},"formatType":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["id","name"]}},"reactions":{"default":[],"maxItems":50,"type":"array","items":{"type":"object","properties":{"emoji":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^\\d{15,21}$","description":"Discord-ID (Snowflake) als String"},{"type":"null"}]},"name":{"type":"string","maxLength":100},"animated":{"default":false,"type":"boolean"}},"required":["name"]},"count":{"type":"integer","minimum":1,"maximum":9007199254740991}},"required":["emoji","count"]}},"components":{"default":[],"description":"Rohe Discord-Components (Buttons etc.)","maxItems":10,"type":"array","items":{}}},"required":["id","author","timestamp"]}},"participants":{"default":[],"maxItems":500,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^\\d{15,21}$","description":"Discord-ID (Snowflake) als String"},"username":{"type":"string","minLength":1,"maxLength":100},"displayName":{"description":"Server-Nickname oder Global Name","anyOf":[{"type":"string","maxLength":100},{"type":"null"}]},"avatarUrl":{"anyOf":[{"type":"string","maxLength":4096,"format":"uri"},{"type":"null"}]},"bot":{"default":false,"type":"boolean"},"color":{"description":"Farbe der höchsten Rolle als Integer","anyOf":[{"type":"integer","minimum":0,"maximum":16777215},{"type":"null"}]},"role":{"description":"Rolle im Ticket (Standard: aus Kontext abgeleitet)","type":"string","enum":["OPENER","STAFF","MEMBER","BOT"]},"canView":{"description":"Darf der Nutzer das Transcript im Web-Viewer sehen? (Standard: true)","type":"boolean"}},"required":["id","username"]}}},"required":["messages"],"description":"AppendMessages"}}}}},"get":{"tags":["Bot API"],"summary":"Nachrichten abrufen (chronologisch, paginiert)","description":"**Benötigter Scope:** `transcripts:read`","security":[{"BotKey":[]}],"parameters":[{"name":"transcriptId","in":"path","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"default":100,"type":"integer","minimum":1,"maximum":500}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string","maxLength":64}},{"name":"authorId","in":"query","required":false,"description":"Discord-ID (Snowflake) als String","schema":{"type":"string","pattern":"^\\d{15,21}$","description":"Discord-ID (Snowflake) als String"}}],"responses":{"200":{"description":"Nachrichten","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageList"}}}}}}},"/api/v1/transcripts/{transcriptId}/close":{"post":{"tags":["Bot API"],"summary":"Ticket schließen","description":"Setzt Status CLOSED, closedAt, closedBy und closeReason. Optional mit letzter Nachrichten-Batch.\n\n**Benötigter Scope:** `transcripts:write`","security":[{"BotKey":[]}],"parameters":[{"name":"transcriptId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Geschlossen","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TranscriptWriteResponse"}}}},"404":{"description":"Nicht gefunden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"closedBy":{"type":"object","properties":{"id":{"type":"string","pattern":"^\\d{15,21}$","description":"Discord-ID (Snowflake) als String"},"username":{"type":"string","minLength":1,"maxLength":100},"displayName":{"description":"Server-Nickname oder Global Name","anyOf":[{"type":"string","maxLength":100},{"type":"null"}]},"avatarUrl":{"anyOf":[{"type":"string","maxLength":4096,"format":"uri"},{"type":"null"}]},"bot":{"default":false,"type":"boolean"},"color":{"description":"Farbe der höchsten Rolle als Integer","anyOf":[{"type":"integer","minimum":0,"maximum":16777215},{"type":"null"}]}},"required":["id","username"],"description":"DiscordUser"},"closeReason":{"type":"string","maxLength":2000},"closedAt":{"description":"Standard: jetzt","type":"string","format":"date-time"},"messages":{"default":[],"description":"Letzte Nachrichten-Batch","maxItems":1000,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^\\d{15,21}$","description":"Discord-Message-ID (dient der Deduplizierung)"},"author":{"type":"object","properties":{"id":{"type":"string","pattern":"^\\d{15,21}$","description":"Discord-ID (Snowflake) als String"},"username":{"type":"string","minLength":1,"maxLength":100},"displayName":{"description":"Server-Nickname oder Global Name","anyOf":[{"type":"string","maxLength":100},{"type":"null"}]},"avatarUrl":{"anyOf":[{"type":"string","maxLength":4096,"format":"uri"},{"type":"null"}]},"bot":{"default":false,"type":"boolean"},"color":{"description":"Farbe der höchsten Rolle als Integer","anyOf":[{"type":"integer","minimum":0,"maximum":16777215},{"type":"null"}]}},"required":["id","username"],"description":"DiscordUser"},"content":{"default":"","type":"string","maxLength":10000},"timestamp":{"description":"Erstellungszeitpunkt (ISO-8601 oder Unix-ms)","type":"string","format":"date-time"},"editedTimestamp":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"type":{"default":0,"description":"Discord-Message-Type","type":"integer","minimum":0,"maximum":9007199254740991},"pinned":{"default":false,"type":"boolean"},"replyTo":{"description":"ID der Nachricht, auf die geantwortet wird","anyOf":[{"type":"string","pattern":"^\\d{15,21}$","description":"Discord-ID (Snowflake) als String"},{"type":"null"}]},"embeds":{"default":[],"maxItems":10,"type":"array","items":{"type":"object","properties":{"title":{"type":"string","maxLength":256},"description":{"type":"string","maxLength":4096},"url":{"type":"string","maxLength":2048},"color":{"type":"integer","minimum":0,"maximum":16777215},"timestamp":{"type":"string","maxLength":64},"author":{"type":"object","properties":{"name":{"type":"string","maxLength":256},"url":{"type":"string"},"icon_url":{"type":"string"}},"required":["name"],"additionalProperties":{}},"footer":{"type":"object","properties":{"text":{"type":"string","maxLength":2048},"icon_url":{"type":"string"}},"required":["text"],"additionalProperties":{}},"image":{"type":"object","properties":{"url":{"type":"string","maxLength":4096}},"required":["url"],"additionalProperties":{}},"thumbnail":{"type":"object","properties":{"url":{"type":"string","maxLength":4096}},"required":["url"],"additionalProperties":{}},"fields":{"maxItems":25,"type":"array","items":{"type":"object","properties":{"name":{"type":"string","maxLength":256},"value":{"type":"string","maxLength":1024},"inline":{"type":"boolean"}},"required":["name","value"]}}},"additionalProperties":{},"description":"Discord-Embed im Format der Discord-API (snake_case)"}},"attachments":{"default":[],"maxItems":25,"type":"array","items":{"type":"object","properties":{"id":{"description":"Discord-Attachment-ID","type":"string","pattern":"^\\d{15,21}$"},"filename":{"type":"string","minLength":1,"maxLength":255},"url":{"type":"string","maxLength":4096,"format":"uri","description":"Discord-CDN-URL – wird (falls aktiviert) automatisch archiviert"},"contentType":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}]},"size":{"default":0,"type":"integer","minimum":0,"maximum":9007199254740991},"width":{"anyOf":[{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},{"type":"null"}]},"height":{"anyOf":[{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},{"type":"null"}]},"spoiler":{"default":false,"type":"boolean"}},"required":["filename","url"]}},"stickers":{"default":[],"maxItems":3,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^\\d{15,21}$","description":"Discord-ID (Snowflake) als String"},"name":{"type":"string","maxLength":100},"formatType":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["id","name"]}},"reactions":{"default":[],"maxItems":50,"type":"array","items":{"type":"object","properties":{"emoji":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^\\d{15,21}$","description":"Discord-ID (Snowflake) als String"},{"type":"null"}]},"name":{"type":"string","maxLength":100},"animated":{"default":false,"type":"boolean"}},"required":["name"]},"count":{"type":"integer","minimum":1,"maximum":9007199254740991}},"required":["emoji","count"]}},"components":{"default":[],"description":"Rohe Discord-Components (Buttons etc.)","maxItems":10,"type":"array","items":{}}},"required":["id","author","timestamp"]}}},"description":"TranscriptClose"}}}}}},"/api/v1/transcripts/{transcriptId}/participants":{"get":{"tags":["Bot API"],"summary":"Teilnehmer auflisten","description":"**Benötigter Scope:** `transcripts:read`","security":[{"BotKey":[]}],"parameters":[{"name":"transcriptId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Teilnehmer","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ParticipantList"}}}}}},"post":{"tags":["Bot API"],"summary":"Teilnehmer hinzufügen / Lesezugriff vergeben","description":"Nutzer mit `canView: true` können das Transcript nach Discord-Login sehen, auch ohne Staff-Rolle.\n\n**Benötigter Scope:** `transcripts:write`","security":[{"BotKey":[]}],"parameters":[{"name":"transcriptId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Teilnehmer","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ParticipantList"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"participants":{"minItems":1,"maxItems":500,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^\\d{15,21}$","description":"Discord-ID (Snowflake) als String"},"username":{"type":"string","minLength":1,"maxLength":100},"displayName":{"description":"Server-Nickname oder Global Name","anyOf":[{"type":"string","maxLength":100},{"type":"null"}]},"avatarUrl":{"anyOf":[{"type":"string","maxLength":4096,"format":"uri"},{"type":"null"}]},"bot":{"default":false,"type":"boolean"},"color":{"description":"Farbe der höchsten Rolle als Integer","anyOf":[{"type":"integer","minimum":0,"maximum":16777215},{"type":"null"}]},"role":{"description":"Rolle im Ticket (Standard: aus Kontext abgeleitet)","type":"string","enum":["OPENER","STAFF","MEMBER","BOT"]},"canView":{"description":"Darf der Nutzer das Transcript im Web-Viewer sehen? (Standard: true)","type":"boolean"}},"required":["id","username"]}}},"required":["participants"],"description":"ParticipantsUpsert"}}}}}},"/api/v1/transcripts/{transcriptId}/participants/{userId}":{"delete":{"tags":["Bot API"],"summary":"Lesezugriff eines Teilnehmers entziehen","description":"**Benötigter Scope:** `transcripts:write`","security":[{"BotKey":[]}],"parameters":[{"name":"transcriptId","in":"path","required":true,"schema":{"type":"string"}},{"name":"userId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Entzogen"},"404":{"description":"Nicht gefunden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/transcripts/{transcriptId}/attachments":{"post":{"tags":["Bot API"],"summary":"Datei hochladen","description":"multipart/form-data mit Feld `file`, optional `messageId` (Discord-Message-ID) und `attachmentId` (ersetzt ein per Nachricht gemeldetes Attachment).\n\n**Benötigter Scope:** `transcripts:write`","security":[{"BotKey":[]}],"parameters":[{"name":"transcriptId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"Hochgeladen","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttachmentResponse"}}}},"413":{"description":"Datei zu groß","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"},"messageId":{"type":"string"},"attachmentId":{"type":"string"}},"required":["file"]}}}}}},"/api/users/me":{"get":{"tags":["Web API"],"summary":"Eingeloggter Nutzer + zugängliche Guilds","security":[{"Session":[]}],"parameters":[],"responses":{"200":{"description":"Nutzer","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Me"}}}},"401":{"description":"Nicht authentifiziert","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/guilds":{"get":{"tags":["Web API"],"summary":"Zugängliche Guilds","description":"`?refresh=true` gleicht Guild-Mitgliedschaften sofort mit Discord ab.","security":[{"Session":[]}],"parameters":[],"responses":{"200":{"description":"Guilds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccessibleGuildList"}}}}}}},"/api/guilds/{guildId}":{"get":{"tags":["Web API"],"summary":"Guild-Details (Stats für Staff, Einstellungen für Manager)","security":[{"Session":[]}],"parameters":[{"name":"guildId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Guild"},"404":{"description":"Nicht gefunden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"tags":["Web API"],"summary":"Guild-Einstellungen ändern (MANAGER)","security":[{"Session":[]}],"parameters":[{"name":"guildId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Aktualisiert","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GuildResponse"}}}},"403":{"description":"Keine Berechtigung","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"staffRoleIds":{"maxItems":50,"type":"array","items":{"type":"string","pattern":"^\\d{15,21}$","description":"Discord-ID (Snowflake) als String"}},"managerRoleIds":{"maxItems":50,"type":"array","items":{"type":"string","pattern":"^\\d{15,21}$","description":"Discord-ID (Snowflake) als String"}},"allowParticipantAccess":{"type":"boolean"},"retentionDays":{"anyOf":[{"type":"integer","minimum":1,"maximum":3650},{"type":"null"}],"description":"null = nie automatisch löschen"}},"description":"GuildSettings"}}}}}},"/api/guilds/{guildId}/transcripts":{"get":{"tags":["Web API"],"summary":"Transcripts einer Guild","description":"STAFF/MANAGER sehen alle Transcripts, Mitglieder nur solche, an denen sie beteiligt sind.","security":[{"Session":[]}],"parameters":[{"name":"guildId","in":"path","required":true,"schema":{"type":"string"}},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["OPEN","CLOSED"]}},{"name":"userId","in":"query","required":false,"description":"Nur Transcripts, an denen dieser Nutzer beteiligt ist","schema":{"description":"Nur Transcripts, an denen dieser Nutzer beteiligt ist","type":"string","pattern":"^\\d{15,21}$"}},{"name":"openerId","in":"query","required":false,"description":"Discord-ID (Snowflake) als String","schema":{"type":"string","pattern":"^\\d{15,21}$","description":"Discord-ID (Snowflake) als String"}},{"name":"externalId","in":"query","required":false,"schema":{"type":"string","maxLength":128}},{"name":"category","in":"query","required":false,"schema":{"type":"string","maxLength":100}},{"name":"tag","in":"query","required":false,"schema":{"type":"string","maxLength":50}},{"name":"q","in":"query","required":false,"description":"Suche in Kanalname, Betreff, externalId","schema":{"description":"Suche in Kanalname, Betreff, externalId","type":"string","minLength":1,"maxLength":200}},{"name":"searchMessages","in":"query","required":false,"description":"Suche zusätzlich im Nachrichtentext","schema":{"description":"Suche zusätzlich im Nachrichtentext","type":"string","enum":["0","1","true","false"]}},{"name":"from","in":"query","required":false,"description":"openedAt >= from","schema":{"description":"openedAt >= from","type":"string","format":"date-time"}},{"name":"to","in":"query","required":false,"description":"openedAt <= to","schema":{"description":"openedAt <= to","type":"string","format":"date-time"}},{"name":"limit","in":"query","required":false,"schema":{"default":25,"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","required":false,"description":"nextCursor aus der vorherigen Antwort","schema":{"description":"nextCursor aus der vorherigen Antwort","type":"string","maxLength":64}}],"responses":{"200":{"description":"Liste","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TranscriptList"}}}}}}},"/api/guilds/{guildId}/audit-log":{"get":{"tags":["Web API"],"summary":"Audit-Log (MANAGER)","security":[{"Session":[]}],"parameters":[{"name":"guildId","in":"path","required":true,"schema":{"type":"string"}},{"name":"transcriptId","in":"query","required":false,"schema":{"type":"string","maxLength":32}},{"name":"action","in":"query","required":false,"schema":{"type":"string","maxLength":64}},{"name":"limit","in":"query","required":false,"schema":{"default":50,"type":"integer","minimum":1,"maximum":200}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string","maxLength":64}}],"responses":{"200":{"description":"Einträge"},"403":{"description":"Keine Berechtigung","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/transcripts/{transcriptId}":{"get":{"tags":["Web API"],"summary":"Transcript abrufen","security":[{"Session":[]}],"parameters":[{"name":"transcriptId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Transcript"},"404":{"description":"Nicht gefunden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Web API"],"summary":"Transcript löschen (MANAGER)","security":[{"Session":[]}],"parameters":[{"name":"transcriptId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Gelöscht"},"403":{"description":"Keine Berechtigung","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/transcripts/{transcriptId}/messages":{"get":{"tags":["Web API"],"summary":"Nachrichten abrufen","security":[{"Session":[]}],"parameters":[{"name":"transcriptId","in":"path","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"default":100,"type":"integer","minimum":1,"maximum":500}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string","maxLength":64}},{"name":"authorId","in":"query","required":false,"description":"Discord-ID (Snowflake) als String","schema":{"type":"string","pattern":"^\\d{15,21}$","description":"Discord-ID (Snowflake) als String"}}],"responses":{"200":{"description":"Nachrichten","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageList"}}}}}}},"/api/transcripts/{transcriptId}/export":{"get":{"tags":["Web API"],"summary":"Export als HTML oder JSON","security":[{"Session":[]}],"parameters":[{"name":"transcriptId","in":"path","required":true,"schema":{"type":"string"}},{"name":"format","in":"query","required":false,"schema":{"default":"html","type":"string","enum":["html","json"]}},{"name":"download","in":"query","required":false,"schema":{"type":"string","enum":["0","1","true","false"]}}],"responses":{"200":{"description":"Datei"}}}},"/api/attachments/{attachmentId}":{"get":{"tags":["Web API"],"summary":"Attachment herunterladen","description":"Funktioniert mit Session-Cookie oder Bot-Key (`transcripts:read`). `?download` erzwingt einen Download.","security":[{"Session":[]}],"parameters":[{"name":"attachmentId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Datei"},"302":{"description":"Weiterleitung zum Discord-CDN (nicht archiviert)"}}}},"/api/auth/login":{"get":{"tags":["Auth"],"summary":"Discord-Login starten","description":"Leitet zu Discord weiter. `?next=/pfad` bestimmt das Ziel nach dem Login.","security":[],"parameters":[],"responses":{"307":{"description":"Weiterleitung zu Discord"}}}},"/api/auth/callback":{"get":{"tags":["Auth"],"summary":"OAuth2-Callback (von Discord aufgerufen)","security":[],"parameters":[],"responses":{"307":{"description":"Weiterleitung ins Dashboard"}}}},"/api/auth/logout":{"post":{"tags":["Auth"],"summary":"Abmelden","security":[{"Session":[]}],"parameters":[],"responses":{"204":{"description":"Abgemeldet"}}}},"/api/admin/keys":{"get":{"tags":["Admin"],"summary":"API-Keys auflisten","security":[{"AdminKey":[]}],"parameters":[],"responses":{"200":{"description":"Keys"}}},"post":{"tags":["Admin"],"summary":"API-Key erstellen","description":"Der Klartext-Key (`secret`) wird **nur einmal** zurückgegeben.","security":[{"AdminKey":[]}],"parameters":[],"responses":{"201":{"description":"Erstellt","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyCreated"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"scopes":{"default":["transcripts:read","transcripts:write","transcripts:delete","guilds:read","guilds:write"],"minItems":1,"type":"array","items":{"type":"string","enum":["transcripts:read","transcripts:write","transcripts:delete","guilds:read","guilds:write"]}},"allGuilds":{"default":false,"type":"boolean"},"guildIds":{"default":[],"maxItems":500,"type":"array","items":{"type":"string","pattern":"^\\d{15,21}$","description":"Discord-ID (Snowflake) als String"}},"expiresAt":{"type":"string","format":"date-time"}},"required":["name"],"description":"ApiKeyCreate"}}}}}},"/api/admin/keys/{keyId}":{"patch":{"tags":["Admin"],"summary":"API-Key ändern","security":[{"AdminKey":[]}],"parameters":[{"name":"keyId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Aktualisiert","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyResponse"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"scopes":{"minItems":1,"type":"array","items":{"type":"string","enum":["transcripts:read","transcripts:write","transcripts:delete","guilds:read","guilds:write"]}},"allGuilds":{"type":"boolean"},"guildIds":{"maxItems":500,"type":"array","items":{"type":"string","pattern":"^\\d{15,21}$","description":"Discord-ID (Snowflake) als String"}},"expiresAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]}},"description":"ApiKeyUpdate"}}}}},"delete":{"tags":["Admin"],"summary":"API-Key widerrufen","security":[{"AdminKey":[]}],"parameters":[{"name":"keyId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Widerrufen","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyResponse"}}}}}}},"/api/cron/retention":{"post":{"tags":["Admin"],"summary":"Aufräum-Job ausführen","description":"Löscht Transcripts gemäß `retentionDays`, abgelaufene Sessions, alte Caches und Audit-Einträge (> 365 Tage).","security":[{"CronSecret":[]}],"parameters":[],"responses":{"200":{"description":"Ergebnis"}}}},"/api/health":{"get":{"tags":["Admin"],"summary":"Health-Check","security":[],"parameters":[],"responses":{"200":{"description":"OK"},"503":{"description":"Datenbank nicht erreichbar"}}}}},"components":{"securitySchemes":{"BotKey":{"type":"http","scheme":"bearer","description":"Bot-API-Key `tt_...` (alternativ Header `X-API-Key`)"},"Session":{"type":"apiKey","in":"cookie","name":"tt_session","description":"Wird beim Discord-Login gesetzt (`__Host-tt_session` bei HTTPS)"},"AdminKey":{"type":"http","scheme":"bearer","description":"ADMIN_API_KEY"},"CronSecret":{"type":"http","scheme":"bearer","description":"CRON_SECRET"}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","examples":["VALIDATION_ERROR"]},"message":{"type":"string"},"details":{},"requestId":{"type":"string"}},"required":["code","message","requestId"]}}},"Participant":{"type":"object","properties":{"userId":{"type":"string"},"username":{"type":"string"},"displayName":{"type":["string","null"]},"avatarUrl":{"type":["string","null"]},"bot":{"type":"boolean"},"role":{"enum":["OPENER","STAFF","MEMBER","BOT"]},"messageCount":{"type":"integer"},"canView":{"type":"boolean"}}},"Transcript":{"type":"object","properties":{"id":{"type":"string","examples":["aZ3kP9qL2xW7vB1n"]},"url":{"type":"string","description":"Link zum Web-Viewer – kann direkt in Discord gepostet werden"},"guildId":{"type":"string"},"externalId":{"type":["string","null"]},"ticketNumber":{"type":["integer","null"]},"channel":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}}},"category":{"type":["string","null"]},"subject":{"type":["string","null"]},"tags":{"type":"array","items":{"type":"string"}},"status":{"enum":["OPEN","CLOSED"]},"openerId":{"type":"string"},"claimedById":{"type":["string","null"]},"closedById":{"type":["string","null"]},"closeReason":{"type":["string","null"]},"openedAt":{"type":"string","format":"date-time"},"closedAt":{"type":["string","null"],"format":"date-time"},"messageCount":{"type":"integer"},"attachmentCount":{"type":"integer"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"opener":{"oneOf":[{"$ref":"#/components/schemas/Participant"},{"type":"null"}],"description":"Nur in Listen"},"participants":{"type":"array","items":{"$ref":"#/components/schemas/Participant"},"description":"Nur in Detailansicht"},"resolved":{"type":["object","null"],"description":"Nur in Bot-Detailansicht"},"metadata":{"type":["object","null"],"description":"Nur in Bot-Detailansicht"}}},"Attachment":{"type":"object","properties":{"id":{"type":"string"},"discordId":{"type":["string","null"]},"filename":{"type":"string"},"contentType":{"type":["string","null"]},"size":{"type":"integer"},"width":{"type":["integer","null"]},"height":{"type":["integer","null"]},"spoiler":{"type":"boolean"},"archived":{"type":"boolean","description":"true = Datei liegt im eigenen Storage (läuft nicht ab)"},"url":{"type":"string"}}},"Message":{"type":"object","properties":{"id":{"type":"string","description":"Discord-Message-ID"},"author":{"type":"object","properties":{"id":{"type":"string"},"username":{"type":"string"},"displayName":{"type":["string","null"]},"avatarUrl":{"type":["string","null"]},"bot":{"type":"boolean"},"color":{"type":["integer","null"]}}},"type":{"type":"integer"},"content":{"type":"string"},"embeds":{"type":"array","items":{"type":"object"}},"attachments":{"type":"array","items":{"$ref":"#/components/schemas/Attachment"}},"stickers":{"type":"array","items":{"type":"object"}},"reactions":{"type":"array","items":{"type":"object"}},"components":{"type":"array","items":{}},"pinned":{"type":"boolean"},"replyTo":{"type":["string","null"]},"timestamp":{"type":"string","format":"date-time"},"editedTimestamp":{"type":["string","null"],"format":"date-time"}}},"Guild":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"iconUrl":{"type":["string","null"]},"staffRoleIds":{"type":"array","items":{"type":"string"}},"managerRoleIds":{"type":"array","items":{"type":"string"}},"allowParticipantAccess":{"type":"boolean"},"retentionDays":{"type":["integer","null"]}}},"ApiKey":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"prefix":{"type":"string","examples":["tt_3fa9c1d2"]},"scopes":{"type":"array","items":{"enum":["transcripts:read","transcripts:write","transcripts:delete","guilds:read","guilds:write"]}},"allGuilds":{"type":"boolean"},"guildIds":{"type":"array","items":{"type":"string"}},"lastUsedAt":{"type":["string","null"],"format":"date-time"},"expiresAt":{"type":["string","null"],"format":"date-time"},"revokedAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"}}},"TranscriptWriteResponse":{"type":"object","properties":{"transcript":{"$ref":"#/components/schemas/Transcript"},"created":{"type":"boolean"},"messages":{"type":"object","properties":{"inserted":{"type":"integer"},"skipped":{"type":"integer"}}}}},"TranscriptResponse":{"type":"object","properties":{"transcript":{"$ref":"#/components/schemas/Transcript"}}},"TranscriptList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Transcript"}},"nextCursor":{"type":["string","null"]}}},"MessageList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Message"}},"nextCursor":{"type":["string","null"]}}},"ParticipantList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Participant"}}}},"AppendResult":{"type":"object","properties":{"inserted":{"type":"integer"},"skipped":{"type":"integer"}}},"AttachmentResponse":{"type":"object","properties":{"attachment":{"$ref":"#/components/schemas/Attachment"}}},"GuildResponse":{"type":"object","properties":{"guild":{"$ref":"#/components/schemas/Guild"},"created":{"type":"boolean"}}},"GuildStatsResponse":{"type":"object","properties":{"stats":{"type":"object","properties":{"transcripts":{"type":"object","properties":{"total":{"type":"integer"},"open":{"type":"integer"},"closed":{"type":"integer"},"last30Days":{"type":"integer"}}},"messages":{"type":"integer"},"attachments":{"type":"integer"},"categories":{"type":"array","items":{"type":"object","properties":{"category":{"type":["string","null"]},"count":{"type":"integer"}}}},"topClosers":{"type":"array","items":{"type":"object","properties":{"userId":{"type":"string"},"count":{"type":"integer"}}}}}}}},"ApiKeyResponse":{"type":"object","properties":{"key":{"$ref":"#/components/schemas/ApiKey"}}},"ApiKeyCreated":{"type":"object","properties":{"key":{"$ref":"#/components/schemas/ApiKey"},"secret":{"type":"string","description":"Vollständiger Key – nur einmal sichtbar!"}}},"AccessLevel":{"enum":["NONE","MEMBER","STAFF","MANAGER"]},"AccessibleGuildList":{"type":"object","properties":{"items":{"type":"array","items":{"allOf":[{"$ref":"#/components/schemas/Guild"},{"type":"object","properties":{"access":{"$ref":"#/components/schemas/AccessLevel"},"transcriptCount":{"type":"integer"}}}]}}}},"Me":{"type":"object","properties":{"user":{"type":"object","properties":{"id":{"type":"string"},"username":{"type":"string"},"globalName":{"type":["string","null"]},"avatarUrl":{"type":"string"}}},"guilds":{"type":"array","items":{"type":"object"}}}}}}}