# Create message

> POST /v1/messages · Core resources API

Source: https://developer.kustomer.com/api/core-resources/create-message

```http
POST https://{orgname}.api.kustomerapp.com/v1/messages
Authorization: Bearer <API key>
```

## Authorization

Requires any one of these roles: `org.permission.message.create`; or legacy `org.user.message.write`. The API key or user must hold at least one of them. Legacy roles predate permission sets and still work.

Adds an instance of a message to an existing conversation timeline with a customer. Interactions with a customer that occurred via email, SMS, web or mobile chat, Facebook direct messages or comments, Twitter direct messages or Tweets, voice, Instagram direct messages or WhatsApp messages can be added to the conversation timeline.

Does not send a message to a customer, only to be used to record the instance of a message. To send a message, first see the [Create a draft by customer endpoint](https://developer.kustomer.com/api/core-resources/create-draft-for-customer).

### Prerequisites

An existing conversation must be created to add the message. See the [Create conversation](https://developer.kustomer.com/api/core-resources/create-conversation) or [Bulk create conversations](https://developer.kustomer.com/api/core-resources/bulk-create-conversations) or [Create conversation for customer](https://developer.kustomer.com/api/core-resources/create-conversation-for-customer) endpoints.

### Rate limit
Messages are subject to a rate limit. A single client can create up to 120 messages per minute per customer.

Messages with `importedAt` in their body will not be subject to the rate limit.

A `429 Too Many Requests` response will be returned if the rate limit has been reached for a customer.

## About Messages

A message is one communication between your team and a customer, such as an email, a chat line, a text, a call or a social post, recorded inside a conversation. Creating one through the API records it on the timeline; it does not deliver anything.

Messages are the customer-visible part of a [conversation](https://developer.kustomer.com/api/core-resources#conversations). Each one belongs to a conversation and to that conversation's customer, and agents see them in order on the customer's timeline. Kustomer's native channels create messages automatically: an inbound email or chat line becomes an `in` message, and when an agent replies, the sent reply becomes an `out` message. You create messages through the API to bring in conversations from somewhere Kustomer doesn't connect to natively, to import history, or to record something your own system already sent.

#### Channel, app and direction

| Field | Meaning |
| --- | --- |
| `channel` | The kind of communication: `email`, `sms`, `chat`, `facebook`, `twitter-dm`, `twitter-tweet`, `voice`, `instagram` or `whatsapp`. Required. Responses can also contain channels you can't create, such as `instagram-comment` or `form`. |
| `app` | Which integration produced the message, such as your own `acme-inbox`. Required. Use one stable value per integration so you can recognize your own records. |
| `direction` | `in` from the customer, `out` to the customer. |
| `status` | `received`, `sent` or `error`, with details in `error` and `errorAt` when delivery failed. |

`preview` holds the text (up to 10,240 characters), `subject` the subject line for channels that have one, and `meta` any channel-specific details, such as `from` and `to` addresses. `sentAt` is when the message was sent in the source system, and it decides where the message sits on the timeline. `auto: true` marks automated messages, which are left out of reporting metrics. For email, `reference` links a reply, reply-all or forward to the message it answers. Files go in `attachments`; upload them first, as described in [Upload attachments](https://developer.kustomer.com/docs/guides/attachments).

#### Recorded, not sent

An `out` message you create through the API is a record of something already delivered. Kustomer does not send it to the customer by email, SMS or chat. To have Kustomer deliver a reply through a connected channel, create a [draft](https://developer.kustomer.com/api/core-resources#drafts) and send it. Kustomer then records the resulting `out` message for you.

Messages can't be deleted through the API. To remove sensitive content, such as a card number a customer pasted into chat, redact the message. You supply replacement `preview` text, a replacement `meta` object, or the IDs of up to 10 of the message's attachments to redact, and the replacements overwrite the original content. Redacted attachments are marked `redacted: true` and their files are no longer available.

#### Which endpoint to use

| To | Call |
| --- | --- |
| Add a message to a conversation you already track | [`POST /v1/conversations/{id}/messages`](https://developer.kustomer.com/api/core-resources/create-message-from-conversation), or [`POST /v1/messages`](https://developer.kustomer.com/api/core-resources/create-message) with `conversation` in the body |
| Add a message for a customer, creating a conversation unless you pass `conversation` | [`POST /v1/customers/{id}/messages`](https://developer.kustomer.com/api/core-resources/create-message-by-customer-id) |
| Read or update a message | [`GET`](https://developer.kustomer.com/api/core-resources/get-message-by-id), [`PUT`](https://developer.kustomer.com/api/core-resources/update-message-by-id) or [`PATCH /v1/messages/{id}`](https://developer.kustomer.com/api/core-resources/update-message-attributes-by-id) |
| Check whether you've already imported a message | [`GET /v1/messages/externalId={externalId}`](https://developer.kustomer.com/api/core-resources/get-a-message-by-external-id) |
| List a conversation's messages | [`GET /v1/conversations/{id}/messages`](https://developer.kustomer.com/api/core-resources/get-messages-by-conversation), optionally filtered by `channel` |
| List a customer's messages across conversations | [`GET /v1/customers/{id}/messages`](https://developer.kustomer.com/api/core-resources/get-messages-by-customer-id) |
| Page through all messages | [`GET /v1/messages`](https://developer.kustomer.com/api/core-resources/get-messages) |
| Remove sensitive content | [`POST /v1/messages/{id}/redactions`](https://developer.kustomer.com/api/core-resources/redact-a-message) |
| Import or update up to 100 messages per request | [`POST`](https://developer.kustomer.com/api/core-resources/bulk-create-messages) or [`PUT /v1/messages/bulk`](https://developer.kustomer.com/api/core-resources/bulk-batch-update-messages) |

A client can create 120 messages per customer per minute; set `importedAt` on historical imports to skip that limit, and keep `sentAt` as the original send time. One message can be updated 100 times in 10 minutes. Only tokens with the `org.admin` role can set `createdAt`, `createdBy`, `modifiedAt` and `modifiedBy`. Set `externalId` to the source system's message ID so retries don't create duplicates. To import history, create customers and conversations before their messages, as in [Bulk operations](https://developer.kustomer.com/docs/guides/bulk-operations).

Related: [Conversations and messages](https://developer.kustomer.com/docs/concepts/conversations-and-messages), [Bring messages from a custom channel](https://developer.kustomer.com/docs/guides/custom-channel-messages), [Drafts](https://developer.kustomer.com/api/core-resources#drafts), [Upload attachments](https://developer.kustomer.com/docs/guides/attachments)

### Recipes

#### 1. Import a message from your own channel exactly once

Put a customer's message from an in-app inbox, marketplace or partner platform on their Kustomer timeline, safely, even when your handler retries.

**Step 1.** [`GET /v1/messages/externalId={externalId}`](https://developer.kustomer.com/api/core-resources/get-a-message-by-external-id.md). Use your message ID as `externalId` and check for it first. A `200` means you've already imported this message, so stop. A `404` means it's new.

```bash title="cURL"
curl "https://api.kustomerapp.com/v1/messages/externalId=$MESSAGE_ID" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY"
```

```bash title="kusty"
# exits with code 4 when the message hasn't been imported yet
kusty api GET "/v1/messages/externalId=$MESSAGE_ID" --fields id
```

**Step 2.** [`POST /v1/conversations/{id}/messages`](https://developer.kustomer.com/api/core-resources/create-message-from-conversation.md). Create the message in the thread's conversation (find or create it by `externalId`; see [Conversations](https://developer.kustomer.com/api/core-resources#conversations)). `channel` and `app` are required: pick the closest native channel, such as `chat`, and one stable `app` value for your integration. `direction` is `in` for the customer's message, `preview` holds the text, and `sentAt` is when it was sent in your system, which decides its place on the timeline. Free-form details go in `meta`.

```bash title="cURL"
curl --request POST "https://api.kustomerapp.com/v1/conversations/$CONVERSATION_ID/messages" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY" \
  --header "Content-Type: application/json" \
  --data "{\"channel\": \"chat\", \"app\": \"acme-inbox\", \"direction\": \"in\", \"externalId\": \"$MESSAGE_ID\", \"preview\": \"$TEXT\", \"sentAt\": \"$SENT_AT\", \"meta\": {\"threadUrl\": \"$THREAD_URL\"}}"
```

```bash title="kusty"
kusty api POST /v1/conversations/$CONVERSATION_ID/messages \
  --field channel=chat --field app=acme-inbox --field direction=in \
  --field externalId=$MESSAGE_ID --field preview="$TEXT" --field sentAt=$SENT_AT \
  --field meta.threadUrl=$THREAD_URL
```

> A new inbound message on a conversation that's `done` reopens it under your org's normal rules. For backfills, set `importedAt` so the 120-per-customer-per-minute create limit doesn't apply, and use [`POST /v1/messages/bulk`](https://developer.kustomer.com/api/core-resources/bulk-create-messages). The full flow, including delivering agent replies back to your channel, is in [Bring messages from a custom channel](https://developer.kustomer.com/docs/guides/custom-channel-messages).

#### 2. Record an outbound message your system already sent

Log an order confirmation, a shipping SMS or a billing email that your own platform delivered, so agents see it in context. Kustomer doesn't send anything.

**Step 1.** [`POST /v1/customers/{id}/messages`](https://developer.kustomer.com/api/core-resources/create-message-by-customer-id.md). Create the message under the customer with `direction: "out"` and `status: "sent"`. Pass `conversation` to put it in an existing conversation; leave it out and Kustomer creates a new conversation for it. Set `auto: true` for automated notifications so they're left out of reporting metrics, and use the real send time as `sentAt`.

```bash title="cURL"
curl --request POST "https://api.kustomerapp.com/v1/customers/$CUSTOMER_ID/messages" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY" \
  --header "Content-Type: application/json" \
  --data "{\"channel\": \"email\", \"app\": \"acme-billing\", \"direction\": \"out\", \"status\": \"sent\", \"auto\": true, \"subject\": \"Your order has shipped\", \"preview\": \"$TEXT\", \"sentAt\": \"$SENT_AT\", \"externalId\": \"$EMAIL_ID\"}"
```

```bash title="kusty"
kusty api POST /v1/customers/$CUSTOMER_ID/messages \
  --field channel=email --field app=acme-billing --field direction=out \
  --field status=sent --field auto:=true --field subject="Your order has shipped" \
  --field preview="$TEXT" --field sentAt=$SENT_AT --field externalId=$EMAIL_ID
```

**Step 2.** [`GET /v1/conversations/{id}/messages`](https://developer.kustomer.com/api/core-resources/get-messages-by-conversation.md). The response's `relationships.conversation` holds the conversation it landed in. List that conversation's messages to check the timeline order, which follows `sentAt`.

```bash title="cURL"
curl "https://api.kustomerapp.com/v1/conversations/$CONVERSATION_ID/messages?sort=asc" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY"
```

```bash title="kusty"
kusty api GET /v1/conversations/$CONVERSATION_ID/messages --query sort=asc --all \
  | jq -r '[.data.attributes.sentAt, .data.attributes.direction, .data.attributes.preview] | @tsv'
```

> To have Kustomer actually deliver a message through a connected email, SMS or chat channel, create and send a [draft](https://developer.kustomer.com/api/core-resources#drafts) instead; Kustomer then records the `out` message for you. Creating an `out` message is only a record.

#### 3. Redact a card number a customer pasted into chat

Remove sensitive content from a message for compliance, since messages can't be deleted through the API.

**Step 1.** [`GET /v1/conversations/{id}/messages`](https://developer.kustomer.com/api/core-resources/get-messages-by-conversation.md). Find the message. Page through the conversation's messages and match `preview` against the pattern you're looking for. Keep the message `id`.

```bash title="cURL"
curl "https://api.kustomerapp.com/v1/conversations/$CONVERSATION_ID/messages?pageSize=100" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY"
```

```bash title="kusty"
kusty api GET /v1/conversations/$CONVERSATION_ID/messages --all \
  | jq -r 'select(.data.attributes.preview // "" | test("\\b(?:\\d[ -]?){13,16}\\b")) | .data.id'
```

**Step 2.** [`POST /v1/messages/{id}/redactions`](https://developer.kustomer.com/api/core-resources/redact-a-message.md). Send the replacement text as `preview`. It overwrites the original, which can't be recovered. You can instead send a replacement `meta` object, or `attachments` with the IDs of up to 10 of the message's files; redacted files are marked `redacted: true` and can no longer be downloaded.

```bash title="cURL"
curl --request POST "https://api.kustomerapp.com/v1/messages/$MESSAGE_ID/redactions" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{"preview": "[card number removed]"}'
```

```bash title="kusty"
kusty api POST /v1/messages/$MESSAGE_ID/redactions --field preview="[card number removed]"
```

> Redaction needs its own permission, `org.permission.message_redaction.create`, separate from message update access.

#### 4. Export a conversation's transcript

Produce a plain-text transcript of a conversation, for example to attach to a case in another system or to archive.

**Step 1.** [`GET /v1/conversations/{id}/messages`](https://developer.kustomer.com/api/core-resources/get-messages-by-conversation.md). Page through the messages oldest first. Add `channel` to keep only one channel, such as `email`.

```bash title="cURL"
curl "https://api.kustomerapp.com/v1/conversations/$CONVERSATION_ID/messages?sort=asc&pageSize=100" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY"
```

```bash title="kusty"
kusty api GET /v1/conversations/$CONVERSATION_ID/messages --query sort=asc --all > messages.jsonl
```

**Step 2.** Print each message with its time and who it came from. `preview` is the text; email subjects are in `meta.subject`.

```bash title="kusty"
jq -r '.data.attributes
  | "[\(.sentAt)] \(if .direction == "in" then "Customer" else "Support" end) (\(.channel)): \(.preview // "")"' \
  messages.jsonl > transcript.txt
```

> Internal notes aren't messages. Add them from [`GET /v1/conversations/{id}/notes`](https://developer.kustomer.com/api/core-resources/get-notes-by-conversation) and merge the two lists by `createdAt` if the transcript is for your own team.

### Example object

A real record from [`GET /v1/messages/{id}`](https://developer.kustomer.com/api/core-resources/get-message-by-id.md):

```json
{
  "type": "message",
  "id": "57c8d2b01a57ed1400392073",
  "attributes": {
    "externalId": "<CADBFQxRPMLtQrBS9x+mt=7Wcy0GEw0FxcneO5sn=k4XgGDyVZg@mail.gmail.com>",
    "channel": "email",
    "app": "postmark",
    "size": 0,
    "direction": "in",
    "preview": "Can you please help me?",
    "sentiment": {
      "confidence": 1,
      "polarity": 1
    },
    "meta": {
      "subject": "Re: hey",
      "from": "jeremy@kustomer.com",
      "to": [
        {
          "email": "support@kustomer.com"
        }
      ],
      "cc": [],
      "recipient": {
        "email": "support@kustomer.com",
        "mailboxHash": ""
      },
      "inReplyTo": "<ef265bbbd801e5e38722f66fa3819c79cd07dc7971b4ad84fafce7e57737e911@kustomer.mail.kustomerapp.com>"
    },
    "status": "received",
    "lang": "zh_cn",
    "sentAt": "2016-09-02T01:15:14.000Z",
    "createdAt": "2016-09-02T01:15:31.140Z",
    "updatedAt": "2016-11-13T16:34:12.214Z",
    "modifiedAt": "2016-11-13T16:34:12.214Z"
  },
  "relationships": {
    "org": {
      "links": {
        "self": "/v1/orgs/57bb5d2e32e83a130052e94e"
      },
      "data": {
        "type": "org",
        "id": "57bb5d2e32e83a130052e94e"
      }
    },
    "modifiedBy": {
      "links": {
        "self": "/v1/users/57bb5d2e32e83a130052e94f"
      },
      "data": {
        "type": "user",
        "id": "57bb5d2e32e83a130052e94f"
      }
    },
    "customer": {
      "links": {
        "self": "/v1/customers/57c08ccde595371400462df2"
      },
      "data": {
        "type": "customers",
        "id": "57c08ccde595371400462df2"
      }
    },
    "conversation": {
      "links": {
        "self": "/v1/conversations/57c8d2b365f7cc14004e674f"
      },
      "data": {
        "type": "conversation",
        "id": "57c8d2b365f7cc14004e674f"
      }
    }
  },
  "links": {
    "self": "/v1/messages/57c8d2b01a57ed1400392073",
    "related": "/v1/postmark/messages/57c8d2b01a57ed1400392073"
  }
}
```

Fields:

- `attributes.externalId` (string): The message's ID in the system it came from, up to 256 characters. For email it's the email's `Message-ID` header, as here. Set it on messages you create so `GET /v1/messages/externalId={externalId}` can tell you whether you already imported one.
- `attributes.channel` (string): The kind of communication. You can create `email`, `sms`, `chat`, `facebook`, `twitter-dm`, `twitter-tweet`, `voice`, `instagram` or `whatsapp` messages; responses can also show other channels, such as `instagram-comment` or `form`.
- `attributes.app` (string): Which integration produced the message. Kustomer's own channels set it, as `postmark` does here for email; on messages you create it's required, and you should use one stable value per integration, such as `acme-inbox`.
- `attributes.direction` (string): `in` from the customer, `out` from your team to the customer.
- `attributes.preview` (string): The message text, up to 10,240 characters. Redaction replaces it.
- `attributes.sentiment` (object): Sentiment of this message. `polarity` is `-1`, `0` or `1`; `confidence` runs from `-1` to `1`.
- `attributes.meta` (object): Channel-specific details. For email that's the `subject`, `from`, `to`, `cc`, the `recipient` mailbox and `inReplyTo`; on messages you create it's free-form.
- `attributes.meta.recipient` (object): The support address the email was delivered to.
- `attributes.meta.inReplyTo` (string): The `Message-ID` of the email this one replies to.
- `attributes.status` (string): `received` for inbound messages, `sent` for delivered outbound ones, or `error` when delivery failed, with details in `error` and `errorAt`.
- `attributes.lang` (string): The language of the message, as a language code. Writable, up to 100 characters.
- `attributes.sentAt` (string): When the message was sent in its source system. It decides where the message sits on the timeline, so keep the original time when you import history.
- `attributes.createdAt` (string): When Kustomer recorded the message, which can be later than `sentAt`. Only tokens with the `org.admin` role can set it.
- `relationships.customer` (object): The customer the message is with, always the same as the conversation's customer.
- `relationships.conversation` (object): The conversation the message belongs to.

### Relationships

- Points to **Customers** via `relationships.customer`: The customer a message was exchanged with. It's always the customer of the message's conversation, and a customer's messages can be listed across all their conversations.
- Points to **Conversations** via `relationships.conversation`: The conversation a message belongs to. Every message is in exactly one conversation, which can hold many messages.
- Referenced by **Attachments** via `relationships.message`: The message a file is attached to. A message can carry several attachments, and its files are read through `GET /v1/messages/{message_id}/attachments/{id}`.
- Referenced by **Conversations** via `relationships.messages` (list): The messages in a conversation, in both directions. Each message belongs to exactly one conversation.
- Referenced by **Customers** via `relationships.messages` (list): Every message exchanged with this customer, across all of their conversations. Each message also points back to its customer.

### Permissions

Fewest roles for a read-only integration: `org.permission.message.read`. For every endpoint: `org.permission.message.read`, `org.permission.message.create`, `org.permission.message.update`, `org.permission.message_redaction.create`.

### Events

Webhook events:

- `kustomer.message.create`: A message is sent or received on any channel
- `kustomer.message.update`: A message changes, for example its delivery `status`

Workflow triggers:

- `kustomer.message.create` (Message Created): An inbound or outbound message is created
- `kustomer.message.update` (Message Updated): A message changes, for example its delivery status

Workflow actions:

- `kustomer.message.find-last` (Customer: Lookup Last Message): required inputs `customer`
- `kustomer.message.create` (Message: Create): required inputs `customer`, `channel`, `app`, `size`
- `kustomer.message.update` (Message: Update): required inputs `id`
- `kustomer.message.find-by-id` (Message: Lookup (By ID)): required inputs `id`
- `kustomer.message.find-by-external` (Message: Lookup (By External ID)): required inputs `identifier`

## Request body

Content type: `application/json` (required)

- `id` (string<object-id>)
- `externalId` (string): The unique identifier from the channel the message. For example, a message ID from Instagram for a specific DM. (max length 256)
- `customer` (string<object-id>): The customer's name
- `conversation` (string<object-id>): The conversation ID of the conversation message is to be added to.
- `reference` (object)
  - `message` (string<object-id>, required): For email channel only. The unique message ID for an email that is a reply, reply-all, or forward.
  - `type` (string, required): Indicate if the message is a reply, reply-all, or forward. One of: `reply`, `replyAll`, `forward`.
- `channel` (string, required): The communication channel of the message: email, SMS, chat, facebook, twitter-dm, twitter-tweet, voice, instagram, whatsapp One of: `email`, `sms`, `chat`, `facebook`, `twitter-dm`, `twitter-tweet`, `voice`, `instagram`, `whatsapp`.
- `preview` (string): (max length 10240)
- `subject` (string): (max length 256)
- `direction` (string, required): Indicates if the message is coming in from a customer or sent out by an agent. One of: `in`, `out`.
- `app` (string, required)
- `size` (number)
- `related` (string)
- `status` (string): One of: `sent`, `received`, `error`.
- `error` (object)
  - `status` (number)
  - `code` (string)
  - `title` (string)
  - `detail` (string)
  - `source` (object)
  - `meta` (object)
  - `links` (object)
- `errorAt` (string<date-time>): The timestamp for when the message errored out.
- `auto` (boolean): If true, message was automatically generated from a Kustomer workflow or auto-response feature. If set to true, this message will be excluded from reporting metrics. If false, message was created by a manual method.
- `sentAt` (string<date-time>): The timestamp for when the message was sent.
- `source` (string): Indicate if the message is part of a bulk message (bulk) or CSAT (Customer Satisfaction Survey) (satisfaction) message. One of: `bulk`, `satisfaction`.
- `shortcuts` (array<string<object-id>>)
- `kbArticles` (array<string<object-id>>)
- `attachments` (anyOf)
  - Any of:
    - array<attachment>
      - `_id` (string<object-id>, required)
      - `name` (string, required)
      - `contentType` (string, required)
      - `contentLength` (number, required)
      - `sourceId` (string<object-id>)
    - array<string<object-id>>
- `location` (object)
  - `name` (string)
  - `address` (string): (max length 256)
  - `address2` (string): (max length 256)
  - `address3` (string): (max length 256)
  - `latitude` (number)
  - `longitude` (number)
  - `countryCode` (string): (min length 2; max length 2)
  - `countryName` (string): (max length 64)
  - `regionCode` (string): (max length 2)
  - `regionName` (string): (max length 128)
  - `cityName` (string): (max length 128)
  - `zipCode` (string): (max length 30)
  - `areaCode` (string): (max length 30)
- `meta` (object)
- `custom` (object)
- `sentiment` (object)
  - `polarity` (number, required): One of: `-1`, `0`, `1`.
  - `confidence` (number, required): (≥ -1; ≤ 1)
- `createdAt` (string<date-time>)
- `modifiedAt` (string<date-time>)
- `createdBy` (string<object-id>)
- `modifiedBy` (string<object-id>)
- `importedAt` (string<date-time>)
- `lang` (string, nullable): (max length 100)
- `queue` (object)
  - Any of:
    - any
    - any
  - `id` (string<object-id>)
  - `external` (string): One of: `amazon-connect`.

Example:

```json
{
  "externalId": "crm-10293",
  "channel": "email",
  "preview": "Example text",
  "subject": "Example title",
  "direction": "in",
  "app": "string"
}
```

## Responses

### 201

Created

- `data` (object, required)
  - `type` (string, required)
  - `id` (string, required)
  - `attributes` (object, required)
    - `channel` (string, required)
    - `app` (string, required)
    - `size` (string, required)
    - `direction` (string, required)
    - `preview` (string, required)
    - `subject` (string, required)
    - `meta` (string, required)
    - `status` (string, required)
    - `sentAt` (string, required)
    - `createdAt` (string, required)
    - `updatedAt` (string, required)
    - `modifiedAt` (string, required)
    - `lang` (string, required)
  - `relationships` (object, required)
    - `org` (object, required)
    - `createdBy` (object, required)
    - `modifiedBy` (object, required)
    - `customer` (object, required)
    - `conversation` (object, required)
  - `links` (object, required)
    - `self` (string, required)

```json
{
  "data": {
    "type": "example",
    "id": "example",
    "attributes": {
      "channel": "example",
      "app": "example",
      "size": 1,
      "direction": "example",
      "preview": "example",
      "subject": "example",
      "meta": {},
      "status": "example",
      "sentAt": "example",
      "createdAt": "example",
      "updatedAt": "example",
      "modifiedAt": "example",
      "lang": "example"
    },
    "relationships": {
      "org": {
        "links": {
          "self": "example"
        },
        "data": {
          "type": "example",
          "id": "example"
        }
      },
      "createdBy": {
        "links": {
          "self": "example"
        },
        "data": {
          "type": "example",
          "id": "example"
        }
      },
      "modifiedBy": {
        "links": {
          "self": "example"
        },
        "data": {
          "type": "example",
          "id": "example"
        }
      },
      "customer": {
        "links": {
          "self": "example"
        },
        "data": {
          "type": "example",
          "id": "example"
        }
      },
      "conversation": {
        "links": {
          "self": "example"
        },
        "data": {
          "type": "example",
          "id": "example"
        }
      }
    },
    "links": {
      "self": "example"
    }
  }
}
```

### 400

Bad Request

### 401

Unauthorized

### 403

Forbidden

## Example request

```bash
curl --request POST \
  --url 'https://api.kustomerapp.com/v1/messages' \
  --header "Authorization: Bearer $KUSTOMER_API_KEY" \
  --header 'Content-Type: application/json' \
  --data '{
  "externalId": "crm-10293",
  "channel": "email",
  "preview": "Example text",
  "subject": "Example title",
  "direction": "in",
  "app": "string"
}'
```
