> ## Documentation Index
> Fetch the complete documentation index at: https://docs.armin.cx/llms.txt
> Use this file to discover all available pages before exploring further.

# Ticket erstellen

> Support-Tickets über POST /public/v1/tickets anlegen.

Lege Tickets programmatisch an — aus CRM, Bestellsystem, eigenem Backend oder Automatisierung (Zapier, Make, Webhooks). Braucht den Scope **`tickets:write`**. Rate Limit: **30 Requests / Minute** (siehe [Rate Limits](/de/api/rate-limits)).

<Warning>
  Für neue Integrationen nicht Legacy `POST https://api.armin.cx/public/tickets` nutzen. v1 bevorzugen; Abschaltdatum siehe [Migration von Legacy](/de/api/v1/migration) (**2026-10-31**).
</Warning>

## Voraussetzungen

1. **API-Schlüssel** mit **`tickets:read`** und **`tickets:write`** — unter [Einstellungen → API](https://armin.cx/app/_/settings/api) anlegen oder erweitern.
2. **Verbundener Kanal** — der Wert `channel` muss zu einem in eurem Workspace verbundenen Kanaltyp passen (z. B. `email` oder `widget`). Bei mehreren Kanälen desselben Typs: `channel_identifier` für eine konkrete Posteingangs-Adresse setzen.
3. **HTTP-Client** — `curl`, Postman oder ein Server mit JSON-`POST`.

## Request

|             |                                                                    |
| ----------- | ------------------------------------------------------------------ |
| **Methode** | `POST`                                                             |
| **URL**     | `https://api.armin.cx/public/v1/tickets`                           |
| **Header**  | `cx-api-key: DEIN_API_SCHLÜSSEL`, `Content-Type: application/json` |

Nur die unten aufgeführten Felder senden. Zusätzliche Felder im JSON-Body führen zu einem Fehler.

## Request-Body

### Pflichtfeld

| Feld      | Typ    | Beschreibung                                                                                                                                                                                |
| --------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `channel` | string | Primärer Posteingangskanal. Einer von: `email`, `widget`, `whatsapp`, `voice`, `instagram`, `facebook`, `tiktok`, `youtube`, `contact_form`. Der Kanaltyp muss im Workspace verbunden sein. |

### Optional — Ticket

| Feld                 | Typ       | Default  | Beschreibung                                                                                                                                              |
| -------------------- | --------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `subject`            | string    | —        | Betreff (max. 500 Zeichen).                                                                                                                               |
| `note`               | string    | —        | Interne Notiz (max. 10.000 Zeichen). Wird genutzt, wenn `messages` fehlt oder leer ist.                                                                   |
| `tags`               | string\[] | —        | Tag-Namen (max. 20 Tags, je 100 Zeichen). Tags werden angelegt, falls nötig, und am Ticket gesetzt.                                                       |
| `metadata`           | object    | —        | Schlüssel/Wert für externe Referenzen (max. 50 Keys; String-Werte max. 2.000 Zeichen). z. B. Bestell-ID, Fallnummer — standardmäßig nicht kundensichtbar. |
| `channel_identifier` | string    | —        | Eindeutige Posteingangs-ID bei mehreren Kanälen desselben `channel`-Typs.                                                                                 |
| `priority`           | string    | `medium` | `none`, `urgent`, `high`, `medium`, `low`.                                                                                                                |
| `status`             | string    | `open`   | `open`, `need_more_info`, `resolved`, `closed`.                                                                                                           |
| `language`           | string    | —        | Ticket-Sprache (unterstützte Werte wie in der Posteingang-Sprachliste).                                                                                   |
| `is_spam`            | boolean   | `false`  | Ticket als Spam markieren.                                                                                                                                |
| `ticket_type`        | string    | —        | `regular` oder `side`.                                                                                                                                    |
| `parent_ticket_id`   | UUID      | —        | Side-Ticket an Eltern-Ticket koppeln.                                                                                                                     |
| `assignee_email`     | string    | —        | Ticket per Agenten-E-Mail zuweisen.                                                                                                                       |

### Optional — Kontakt

| Feld                 | Typ    | Beschreibung                                                 |
| -------------------- | ------ | ------------------------------------------------------------ |
| `contact.email`      | string | Kunden-E-Mail. Bestehenden Kontakt matchen oder neu anlegen. |
| `contact.first_name` | string | Vorname.                                                     |
| `contact.last_name`  | string | Nachname.                                                    |
| `contact.phone`      | string | Telefonnummer.                                               |

### Optional — Nachrichten

Statt (oder zusätzlich zu) `note` bis zu **10** Nachrichten in `messages[]`:

| Feld                         | Typ     | Beschreibung                                                                                                                                                                                                                                            |
| ---------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `messages[].channel`         | string  | Kanal für diese Nachricht (gleiche Enum wie Ticket-`channel`).                                                                                                                                                                                          |
| `messages[].from_agent`      | boolean | `false`, wenn nicht angegeben. Auf `true` setzen, damit die Nachricht als von deinem Unternehmen gesendet gilt statt vom Kunden. Bei Agent-Nachrichten ist `from` optional.                                                                             |
| `messages[].body`            | string  | Klartext (max. 100.000 Zeichen).                                                                                                                                                                                                                        |
| `messages[].html_body`       | string  | HTML (max. 100.000 Zeichen).                                                                                                                                                                                                                            |
| `messages[].prefilled_input` | boolean | `false`, wenn nicht angegeben. Auf `true` setzen, damit `body` als KI-Antwortentwurf für den Agenten hinterlegt wird, statt als Nachricht im Verlauf. Nur eine Nachricht pro Anfrage darf dies setzen.                                                  |
| `messages[].from.email`      | string  | Absender-E-Mail. Optional, wenn `contact.email` gesetzt ist.                                                                                                                                                                                            |
| `messages[].from.first_name` | string  | Absender-Vorname.                                                                                                                                                                                                                                       |
| `messages[].from.last_name`  | string  | Absender-Nachname.                                                                                                                                                                                                                                      |
| `messages[].from.phone`      | string  | Absender-Telefon.                                                                                                                                                                                                                                       |
| `messages[].skip_email`      | boolean | `false`, wenn nicht angegeben. Auf `true` setzen, damit die Nachricht gespeichert wird, ohne eine ausgehende Benachrichtigung (z. B. E-Mail) zu senden — etwa beim Nachtragen oder Importieren von Nachrichten, die bereits anderswo zugestellt wurden. |
| `messages[].metadata`        | object  | Metadaten pro Nachricht (gleiche Limits wie Ticket-`metadata`).                                                                                                                                                                                         |
| `messages[].attachment_ids`  | UUID\[] | IDs von Anhängen, bestätigt über den [Anhänge](#anhänge)-Ablauf unten (max. **10** pro Nachricht). Bei `prefilled_input`-Nachrichten nicht unterstützt.                                                                                                 |

<Tip>
  Für typische Integrationen reichen **`note` + `contact` + `metadata`**. `messages` nutzen, wenn beim Anlegen eine sichtbare Kundennachricht im Thread stehen soll.
</Tip>

## Anhänge

Eine Datei in drei Aufrufen an eine Nachricht anhängen: Upload-URL anfordern, Datei hochladen, dann bestätigen — erst danach beim Ticket referenzierbar.

1. `POST /public/v1/attachments/upload-url` mit Dateiname, MIME-Type und Größe. Liefert `id` und eine signierte `upload_url` (30 Minuten gültig).
2. Die rohen Dateibytes per `PUT` an `upload_url` senden — direkt an den Speicher, nicht über diese API.
3. `POST /public/v1/attachments/{id}/confirm`. Prüft die Datei gegen das Größenlimit und den **tatsächlichen Inhalt** — nicht nur den in Schritt 1 angegebenen MIME-Type.
4. Die bestätigte `id` beim Ticket-Anlegen in `messages[].attachment_ids` übergeben.

| Limit                      | Wert                                                                  |
| -------------------------- | --------------------------------------------------------------------- |
| Max. Dateigröße            | 20 MB                                                                 |
| Max. Anhänge pro Nachricht | 10                                                                    |
| Erlaubte Typen             | Bilder, PDF, Klartext, CSV, Word (`.docx`), Excel (`.xlsx`), MP4, MP3 |

```bash theme={null}
# 1. Upload-URL anfordern
curl -sS -X POST \
  -H "cx-api-key: DEIN_API_SCHLÜSSEL" \
  -H "Content-Type: application/json" \
  "https://api.armin.cx/public/v1/attachments/upload-url" \
  -d '{
    "file_name": "rechnung.pdf",
    "mime_type": "application/pdf",
    "size": 20480
  }'
# -> { "data": { "id": "...", "upload_url": "...", "public_url": "...", "expires_at": "..." } }

# 2. Datei direkt an die zurückgegebene upload_url hochladen
curl -sS -X PUT "UPLOAD_URL_AUS_SCHRITT_1" \
  -H "Content-Type: application/pdf" \
  --data-binary @rechnung.pdf

# 3. Upload bestätigen
curl -sS -X POST \
  -H "cx-api-key: DEIN_API_SCHLÜSSEL" \
  "https://api.armin.cx/public/v1/attachments/ANHANG_ID/confirm"

# 4. Ticket anlegen und den bestätigten Anhang referenzieren
curl -sS -X POST \
  -H "cx-api-key: DEIN_API_SCHLÜSSEL" \
  -H "Content-Type: application/json" \
  "https://api.armin.cx/public/v1/tickets" \
  -d '{
    "channel": "email",
    "messages": [
      {
        "channel": "email",
        "from_agent": true,
        "body": "Hier ist deine Rechnung.",
        "attachment_ids": ["ANHANG_ID"]
      }
    ]
  }'
```

## Minimales Beispiel

Nur interne Notiz — kein Kontakt nötig:

```bash theme={null}
curl -sS -X POST \
  -H "cx-api-key: DEIN_API_SCHLÜSSEL" \
  -H "Content-Type: application/json" \
  "https://api.armin.cx/public/v1/tickets" \
  -d '{
    "channel": "email",
    "subject": "Nachverfolgung erforderlich",
    "note": "Aus Bestellüberwachung — Kunde ohne Telefonnummer."
  }'
```

## Vollständiges Beispiel

Kontakt, Tags und externe Referenz in `metadata`:

```bash theme={null}
curl -sS -X POST \
  -H "cx-api-key: DEIN_API_SCHLÜSSEL" \
  -H "Content-Type: application/json" \
  "https://api.armin.cx/public/v1/tickets" \
  -d '{
    "channel": "email",
    "subject": "Telefonnummer fehlt — Bestellung #1042",
    "contact": {
      "email": "kunde@beispiel.de",
      "first_name": "Alex",
      "last_name": "Beispiel"
    },
    "note": "Bestellung #1042: Versandart erfordert Telefonnummer, keine angegeben.\nKunde: Alex Beispiel\nBitte Rückrufnummer vor Versand einholen.",
    "tags": ["telefon-fehlt", "express-versand"],
    "metadata": {
      "external_order_id": "5678901234",
      "external_order_name": "#1042",
      "source": "order-automation"
    }
  }'
```

`metadata`-Keys an euer System anpassen — die API speichert sie als Integrationsfelder.

## Automatisierung und Webhooks

Viele Teams rufen den Endpoint aus **HTTP-Request**-Aktionen auf (eigene Skripte, Zapier, Make, n8n, E-Commerce-Workflows):

1. Trigger, wenn eure Geschäftsregel greift (Tag gesetzt, Feld fehlt, Statuswechsel).
2. `POST` auf `https://api.armin.cx/public/v1/tickets` mit Variablen eurer Plattform in `subject`, `note`, `contact`, `metadata`.
3. Externe Bestell- oder Fall-ID in `metadata` mappen, damit Agenten die Quelle sehen.

Trigger so bauen, dass sie **nur bei der Bedingung** feuern, nicht bei jedem Update.

<Note>
  Jeder erfolgreiche Request erstellt ein **neues** Ticket. Automatisierung nur auslösen, wenn wirklich ein Ticket entstehen soll (z. B. wenn ein Tag gesetzt wird), nicht bei jedem Update desselben Datensatzes.
</Note>

## Antwort

**`201 Created`** — v1-Erfolgs-Envelope:

```json theme={null}
{
  "success": true,
  "data": {
    "id": "019b0000-0000-7000-8000-000000000001",
    "ticket_number": 1042,
    "subject": "Telefonnummer fehlt — Bestellung #1042",
    "status": "open",
    "priority": "medium",
    "channel": "email",
    "created_at": "2026-08-25T10:00:00.000Z",
    "updated_at": "2026-08-25T10:00:00.000Z",
    "tags": ["telefon-fehlt", "express-versand"]
  }
}
```

`data.id` (UUID) oder `data.ticket_number` (Posteingangsnummer) für spätere `GET`-Requests speichern.

## Fehler

| Situation                                                                            | HTTP         | Code                                       |
| ------------------------------------------------------------------------------------ | ------------ | ------------------------------------------ |
| Fehlender/ungültiger API-Schlüssel                                                   | 401          | `API_KEY_REQUIRED`, `INVALID_API_KEY`      |
| Scope `tickets:write` fehlt                                                          | 403          | `FORBIDDEN_SCOPE`                          |
| Ungültiger Body, unbekanntes Feld, `channel` fehlt                                   | 400          | `VALIDATION_ERROR`                         |
| Kein verbundener Kanal für `channel` / `channel_identifier`                          | 400          | `VALIDATION_ERROR`                         |
| Anhang nicht gefunden, nicht bestätigt oder schon einer anderen Nachricht zugeordnet | 404 oder 400 | `ATTACHMENT_NOT_FOUND`, `VALIDATION_ERROR` |
| Rate Limit überschritten                                                             | 429          | `RATE_LIMIT_EXCEEDED`                      |

Vollständige Liste: [Fehler](/de/api/errors).

## Zuerst testen

1. Unter [Einstellungen → API](https://armin.cx/app/_/settings/api) einen Schlüssel mit **`tickets:write`** anlegen.
2. Einen Test-Request mit den Beispielen oben senden. Test-`subject` oder Tag setzen (z. B. `api-test`), damit euer Team das Ticket im Posteingang erkennt.
3. Prüfen, ob das Ticket mit dem erwarteten Kanal, Kontakt, Tags und `metadata` ankommt.

Test-Schlüssel danach widerrufen.

## Weiterführend

<CardGroup cols={2}>
  <Card title="OpenAPI-Referenz" icon="book" href="https://api.armin.cx/docs/v1">
    Interaktives Schema und Try it für `POST /tickets`.
  </Card>

  <Card title="Authentifizierung" icon="key" href="/de/api/authentication">
    Scopes, Rotation, Least Privilege.
  </Card>

  <Card title="Rate Limits" icon="gauge" href="/de/api/rate-limits">
    Write-Stufe (30/min) und Backoff bei 429.
  </Card>

  <Card title="Migration von Legacy" icon="arrow-right-arrow-left" href="/de/api/v1/migration">
    Legacy `POST /public/tickets` auf v1 mappen.
  </Card>
</CardGroup>
