> ## 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.

# Nachricht senden

> Antwort oder interne Notiz senden via POST /public/v1/tickets/:ticketNumber/messages.

Fügt einem bestehenden Ticket eine Nachricht hinzu — eine kundensichtbare Antwort oder eine interne Notiz. Braucht den Scope **`tickets:write`**. Rate Limit: **30 Requests / Minute** (siehe [Rate Limits](/de/api/rate-limits)).

## Voraussetzungen

1. **API-Schlüssel** mit **`tickets:write`** — unter [Einstellungen → API](https://armin.cx/app/_/settings/api) anlegen oder erweitern.
2. **Ein bestehendes Ticket** — `ticket_number` aus [Ticket erstellen](/de/api/create-ticket) oder `GET /public/v1/tickets`.
3. **HTTP-Client** — `curl`, Postman oder ein Server mit JSON-`POST`.

## Request

|             |                                                                                                                        |
| ----------- | ---------------------------------------------------------------------------------------------------------------------- |
| **Methode** | `POST`                                                                                                                 |
| **URL**     | `https://api.armin.cx/public/v1/tickets/{ticket_number}/messages`                                                      |
| **Header**  | `cx-api-key: DEIN_API_SCHLÜSSEL`, `Content-Type: application/json`, `Idempotency-Key: DEIN_EINDEUTIGER_KEY` (optional) |

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

## Request-Body

| Feld                 | Typ     | Default | Beschreibung                                                                                                                                                                                                                                                            |
| -------------------- | ------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `type`               | string  | `reply` | `reply` sendet die Nachricht an den Kunden. `note` speichert eine interne Notiz, die nur Mitarbeitende sehen — wird nie versendet.                                                                                                                                      |
| `body`               | string  | —       | Klartext (max. 100.000 Zeichen). `body` oder `html_body` ist erforderlich.                                                                                                                                                                                              |
| `html_body`          | string  | —       | HTML (max. 100.000 Zeichen).                                                                                                                                                                                                                                            |
| `channel`            | string  | —       | Auf einem bestimmten Kanaltyp senden statt auf dem Ticket-eigenen Kanal. Einer von: `email`, `widget`, `whatsapp`, `voice`, `instagram`, `facebook`, `tiktok`, `youtube`, `contact_form`.                                                                               |
| `channel_identifier` | string  | —       | Eindeutige Posteingangs-ID bei mehreren Kanälen desselben `channel`-Typs. Nur zusammen mit `channel` genutzt.                                                                                                                                                           |
| `from_agent`         | string  | —       | E-Mail des Teammitglieds, von dem die Nachricht gesendet wird. Muss zu einem bestehenden Agenten in eurer Organisation passen, sonst schlägt der Request fehl. Ohne Angabe gilt die Nachricht weiterhin als von eurem Unternehmen gesendet, ohne namentlichen Absender. |
| `attachment_ids`     | UUID\[] | —       | IDs von Anhängen, bestätigt über den [Anhänge](#anhänge)-Ablauf unten (max. **10**).                                                                                                                                                                                    |
| `skip_email`         | boolean | `false` | Auf `true` setzen, damit die Nachricht gespeichert wird, ohne eine ausgehende Benachrichtigung zu senden — etwa beim Nachtragen bereits anderswo zugestellter Nachrichten.                                                                                              |
| `metadata`           | object  | —       | Schlüssel/Wert für externe Referenzen (max. 50 Keys; String-Werte max. 2.000 Zeichen).                                                                                                                                                                                  |

<Tip>
  `channel` im Regelfall weglassen — die Nachricht wird auf dem Kanal versendet, den das Ticket bereits nutzt.
</Tip>

## Idempotenz (optional)

Mit dem Header `Idempotency-Key` — ein beliebiger, pro echtem Sendeversuch eindeutiger Wert (z. B. eine UUID) — lässt sich ein Request sicher wiederholen (etwa nach einem Timeout), ohne die Nachricht doppelt zu senden.

* **Erster Request mit einem Key:** wird normal verarbeitet. Die Antwort wird **1 Stunde** lang gespeichert.
* **Gleicher Key, gleicher Body, innerhalb von 1 Stunde:** keine neue Nachricht wird gesendet. Ihr bekommt genau die Antwort des ersten Requests zurück.
* **Gleicher Key, anderer Body:** wird mit `400 IDEMPOTENCY_KEY_CONFLICT` abgelehnt — einen Key nur für Wiederholungen des identischen Requests wiederverwenden.
* **Kein Key:** funktioniert wie oben beschrieben, ohne Schutz vor Duplikaten.

```bash theme={null}
curl -sS -X POST \
  -H "cx-api-key: DEIN_API_SCHLÜSSEL" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 3f29f...eindeutig-pro-versuch" \
  "https://api.armin.cx/public/v1/tickets/1042/messages" \
  -d '{
    "type": "reply",
    "body": "Danke für deine Nachricht! Deine Bestellung wird morgen versendet."
  }'
```

## Anhänge

Eine Datei in drei Aufrufen anhängen: Upload-URL anfordern, Datei hochladen, dann bestätigen — erst danach hier referenzierbar. Vollständiger Ablauf und Limits: [Ticket erstellen → Anhänge](/de/api/create-ticket#anhänge).

```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/1042/messages" \
  -d '{
    "type": "reply",
    "body": "Hier ist deine Rechnung.",
    "attachment_ids": ["ANHANG_ID"]
  }'
```

## Minimales Beispiel

Antwort auf dem Ticket-eigenen Kanal:

```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/1042/messages" \
  -d '{
    "type": "reply",
    "body": "Danke für deine Nachricht! Deine Bestellung wird morgen versendet."
  }'
```

## Vollständiges Beispiel

Antwort von einem namentlichen Agenten, mit Metadaten:

```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/1042/messages" \
  -d '{
    "type": "reply",
    "body": "Danke für deine Nachricht! Deine Bestellung wird morgen versendet.",
    "html_body": "<p>Danke für deine Nachricht! Deine Bestellung wird morgen versendet.</p>",
    "from_agent": "support@acme.com",
    "metadata": {
      "source": "order-automation"
    }
  }'
```

Interne Notiz, wird nie an den Kunden gesendet:

```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/1042/messages" \
  -d '{
    "type": "note",
    "body": "An Logistik eskaliert — warte auf Update vom Versanddienstleister."
  }'
```

## Antwort

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

```json theme={null}
{
  "success": true,
  "data": {
    "id": "019b0000-0000-7000-8000-000000000020",
    "ticket_id": "019b0000-0000-7000-8000-000000000001",
    "ticket_number": 1042,
    "role": "agent",
    "direction": "outbound",
    "text": "Danke für deine Nachricht! Deine Bestellung wird morgen versendet.",
    "channel": "email",
    "author_name": "Acme Support",
    "status": "queued",
    "created_at": "2026-08-25T10:00:00.000Z",
    "attachments": []
  }
}
```

`status` zeigt den Zustellstatus (`queued`, `sent`, `delivered`, `failed`, …). Eine `reply` startet als `queued` und wird von der bestehenden Sende-Pipeline abgeholt; eine `note` ist sofort `delivered`, da sie nie versendet wird.

## Fehler

| Situation                                                                                              | HTTP         | Code                                       |
| ------------------------------------------------------------------------------------------------------ | ------------ | ------------------------------------------ |
| Fehlender/ungültiger API-Schlüssel                                                                     | 401          | `API_KEY_REQUIRED`, `INVALID_API_KEY`      |
| Scope `tickets:write` fehlt                                                                            | 403          | `FORBIDDEN_SCOPE`                          |
| Unbekannte `ticket_number` in dieser Organisation                                                      | 404          | `TICKET_NOT_FOUND`                         |
| Ungültiger Body, unbekanntes Feld, `body`/`html_body` fehlt, oder `from_agent` passt zu keinem Agenten | 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`                      |
| Gleicher `Idempotency-Key` mit anderem Request-Body wiederverwendet                                    | 400          | `IDEMPOTENCY_KEY_CONFLICT`                 |
| Ein vorheriger Request mit diesem `Idempotency-Key` läuft noch                                         | 409          | `IDEMPOTENCY_KEY_IN_PROGRESS`              |

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

<Note>
  Jeder erfolgreiche Request erstellt eine **neue** Nachricht. Mit dem optionalen [`Idempotency-Key`-Header](#idempotenz-optional) lässt sich sicher wiederholen, ohne sie zu duplizieren.
</Note>

## Zuerst testen

1. Unter [Einstellungen → API](https://armin.cx/app/_/settings/api) einen Schlüssel mit **`tickets:write`** anlegen.
2. Ein Test-Ticket anlegen (siehe [Ticket erstellen](/de/api/create-ticket)) und mit den Beispielen oben eine Nachricht senden.
3. Prüfen, ob die Nachricht mit dem erwarteten Kanal und Absender im Ticket-Verlauf 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/:ticketNumber/messages`.
  </Card>

  <Card title="Ticket erstellen" icon="ticket" href="/de/api/create-ticket">
    Ticket samt erster Nachricht anlegen.
  </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>
</CardGroup>
