# Create note

> POST /v1/notes · Core resources API

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

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

## Authorization

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

Creates a note.

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

## About Notes

A note is an internal comment that your team leaves on a conversation. Customers never see notes; agents use them to hand off context, record what they checked, and @mention teammates.

In the agent desktop, notes appear in a conversation's thread next to its [messages](https://developer.kustomer.com/api/core-resources#messages) but are styled as internal and are never sent to the customer. Every note belongs to one [conversation](https://developer.kustomer.com/api/core-resources#conversations), and through it to that conversation's customer, so you can read notes per conversation or across everything you know about a customer. Agents write notes by hand; integrations and workflows use them to drop in context such as an order lookup result or a fraud check, without the customer seeing it.

Notes attach to conversations. Internal comments on custom objects, such as a task or an order, are a separate resource called [comments](https://developer.kustomer.com/api/core-resources#comments).

#### Key fields

| Field | Meaning |
| --- | --- |
| `body` | The note text, 1 to 10,240 characters. Required. |
| `conversation` | The conversation the note belongs to. Required when you create a note with `POST /v1/notes` or in bulk; the customer comes from the conversation. |
| `userMentions` | Up to 20 `{ user }` or `{ team }` entries. Mentioned users are notified, the same as an @mention typed in the agent desktop. |
| `attachments` | Files uploaded beforehand with [`POST /v1/attachments`](https://developer.kustomer.com/api/core-resources/create-attachment), passed as `{ _id, name, contentType, contentLength }`. |
| `externalId` | Your own ID for the note, up to 256 characters. |

The conversation's `noteCount` tracks how many notes it has. An unsent note is a different object: a [draft](https://developer.kustomer.com/api/core-resources#drafts) with `channel: "note"`, limited to one per conversation.

#### Which endpoint to use

| To | Call |
| --- | --- |
| Add a note to a conversation | [`POST /v1/conversations/{id}/notes`](https://developer.kustomer.com/api/core-resources/create-note-within-conversation), or [`POST /v1/notes`](https://developer.kustomer.com/api/core-resources/create-note) with `conversation` in the body |
| Read, edit or delete a note | [`GET`](https://developer.kustomer.com/api/core-resources/get-note-by-id), [`PUT`](https://developer.kustomer.com/api/core-resources/update-note-by-id), [`PATCH`](https://developer.kustomer.com/api/core-resources/update-note-attributes-by-id) or [`DELETE /v1/notes/{id}`](https://developer.kustomer.com/api/core-resources/delete-note-by-id) |
| List a conversation's notes | [`GET /v1/conversations/{id}/notes`](https://developer.kustomer.com/api/core-resources/get-notes-by-conversation) |
| List every note about a customer | [`GET /v1/customers/{id}/notes`](https://developer.kustomer.com/api/core-resources/get-notes-by-customer-id) |
| Page through all notes | [`GET /v1/notes`](https://developer.kustomer.com/api/core-resources/get-notes) |
| Import or update up to 100 notes per request | [`POST`](https://developer.kustomer.com/api/core-resources/bulk-create-notes) or [`PUT /v1/notes/bulk`](https://developer.kustomer.com/api/core-resources/bulk-batch-update-notes) |

A client can create 120 notes per customer per minute. Only tokens with the `org.admin` role can set `createdAt`, `createdBy`, `modifiedAt` and `modifiedBy`, which you need when importing history so notes keep their original author and time. `PUT` merges your changes unless you add `replace=true`, in which case `conversation` and `body` are both required. Bulk jobs run asynchronously; see [Bulk operations](https://developer.kustomer.com/docs/guides/bulk-operations).

Related: [Conversations](https://developer.kustomer.com/api/core-resources#conversations), [Messages](https://developer.kustomer.com/api/core-resources#messages), [Comments on custom objects](https://developer.kustomer.com/api/core-resources#comments), [Conversations and messages](https://developer.kustomer.com/docs/concepts/conversations-and-messages)

### Recipes

#### 1. Add an internal note and @mention a teammate

Hand off context on a conversation, such as a fraud check result or an escalation reason, and notify the person who needs to act on it.

**Step 1.** [`GET /v1/users`](https://developer.kustomer.com/api/access-management/get-users.md). Mentions take user or team IDs, not names. Find the user by email and keep their `id`.

```bash title="cURL"
curl "https://api.kustomerapp.com/v1/users?email=$TEAMMATE_EMAIL" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY"
```

```bash title="kusty"
kusty api GET /v1/users --query email=$TEAMMATE_EMAIL | jq -r '.data[0].id'
```

**Step 2.** [`POST /v1/conversations/{id}/notes`](https://developer.kustomer.com/api/core-resources/create-note-within-conversation.md). Post the note on the conversation. `body` is required and up to 10,240 characters. `userMentions` takes up to 20 entries, each `{ "user": … }` or `{ "team": … }`, and mentioned users are notified just as if an agent had typed the @mention. The customer never sees the note.

```bash title="cURL"
curl --request POST "https://api.kustomerapp.com/v1/conversations/$CONVERSATION_ID/notes" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY" \
  --header "Content-Type: application/json" \
  --data "{\"body\": \"Fraud check flagged this order (score 0.91). Please verify the shipping address before refunding.\", \"userMentions\": [{\"user\": \"$USER_ID\"}]}"
```

```bash title="kusty"
kusty api POST /v1/conversations/$CONVERSATION_ID/notes \
  --field body="Fraud check flagged this order (score 0.91). Please verify the shipping address before refunding." \
  --field userMentions.0.user=$USER_ID
```

> To mention a whole team, look it up with [`GET /v1/teams`](https://developer.kustomer.com/api/access-management/get-teams) and send `{ "team": "<team id>" }`. To attach a file, upload it first and pass it in `attachments`; see [Attachments](https://developer.kustomer.com/api/core-resources#attachments).

#### 2. Post an integration note once per event

Drop the result of a lookup, such as an order status or a CRM summary, into a conversation without repeating it when your webhook handler is retried.

**Step 1.** [`GET /v1/conversations/{id}/notes`](https://developer.kustomer.com/api/core-resources/get-notes-by-conversation.md). There's no lookup by a note's `externalId`, so list the conversation's notes and check whether one already carries your event ID.

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

```bash title="kusty"
kusty api GET /v1/conversations/$CONVERSATION_ID/notes --all \
  | jq -r --arg ref "$EVENT_ID" 'select(.data.attributes.externalId == $ref) | .data.id'
```

**Step 2.** [`POST /v1/conversations/{id}/notes`](https://developer.kustomer.com/api/core-resources/create-note-within-conversation.md). If nothing matched, create the note with your event ID as `externalId` (up to 256 characters), so the next retry finds it.

```bash title="cURL"
curl --request POST "https://api.kustomerapp.com/v1/conversations/$CONVERSATION_ID/notes" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY" \
  --header "Content-Type: application/json" \
  --data "{\"externalId\": \"$EVENT_ID\", \"body\": \"Order 4417: shipped 2 days ago via UPS, tracking 1Z999AA10123456784.\"}"
```

```bash title="kusty"
kusty api POST /v1/conversations/$CONVERSATION_ID/notes \
  --field externalId=$EVENT_ID \
  --field body="Order 4417: shipped 2 days ago via UPS, tracking 1Z999AA10123456784."
```

> A client can create 120 notes per customer per minute. If your integration can fire many times for one customer, batch the results into a single note.

#### 3. Export every note about a customer

Pull all internal notes across a customer's conversations, for an account review, a handover to another team or a data request.

**Step 1.** [`GET /v1/customers/{id}/notes`](https://developer.kustomer.com/api/core-resources/get-notes-by-customer-id.md). Page through the customer's notes. Each one has `relationships.conversation` and `relationships.createdBy`, so you can group by conversation and show the author.

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

```bash title="kusty"
kusty api GET /v1/customers/$CUSTOMER_ID/notes --all > notes.jsonl
```

**Step 2.** Write one CSV row per note. Authors are user IDs; resolve them with [`GET /v1/users/{id}`](https://developer.kustomer.com/api/access-management/get-user-by-id) and cache the names.

```bash title="kusty"
jq -r '[.data.attributes.createdAt, .data.relationships.conversation.data.id,
        .data.relationships.createdBy.data.id, .data.attributes.body] | @csv' notes.jsonl > notes.csv
```

> Notes can be deleted with [`DELETE /v1/notes/{id}`](https://developer.kustomer.com/api/core-resources/delete-note-by-id). Messages can't, so for a full data request also list the customer's [messages](https://developer.kustomer.com/api/core-resources/get-messages-by-customer-id).

### Example object

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

```json
{
  "type": "note",
  "id": "57cf1d65079e780006e34f79",
  "attributes": {
    "body": "Hey",
    "createdAt": "2016-09-06T19:47:49.235Z",
    "updatedAt": "2016-09-06T19:47:49.236Z"
  },
  "relationships": {
    "org": {
      "links": {
        "self": "/v1/orgs/57bb5d2e32e83a130052e94e"
      },
      "data": {
        "type": "org",
        "id": "57bb5d2e32e83a130052e94e"
      }
    },
    "customer": {
      "links": {
        "self": "/v1/customers/57c08ccde595371400462df2"
      },
      "data": {
        "type": "customer",
        "id": "57c08ccde595371400462df2"
      }
    },
    "createdBy": {
      "links": {
        "self": "/v1/users/57bb5d2e32e83a130052e94f"
      },
      "data": {
        "type": "user",
        "id": "57bb5d2e32e83a130052e94f"
      }
    },
    "conversation": {
      "links": {
        "self": "/v1/conversations/57ce17d2369f251400fc8e89"
      },
      "data": {
        "type": "conversation",
        "id": "57ce17d2369f251400fc8e89"
      }
    }
  },
  "links": {
    "self": "/v1/notes/57cf1d65079e780006e34f79"
  }
}
```

Fields:

- `attributes.body` (string): The note text, 1 to 10,240 characters. Required. Agents see it in the conversation thread, styled as internal; the customer never does.
- `attributes.createdAt` (string): When the note was written. Only tokens with the `org.admin` role can set it, which you need when importing old notes so they keep their original time.
- `relationships.customer` (object): The customer the note is about. Kustomer copies it from the conversation, so you never set it yourself.
- `relationships.createdBy` (object): The note's author. It's the user or API key that created it, unless an `org.admin` token set `createdBy` during an import.
- `relationships.conversation` (object): The conversation the note belongs to. Required when you create a note with `POST /v1/notes` or in bulk.

### Relationships

- Points to **Customers** via `relationships.customer`: The customer a note is about, taken from its conversation. List every note about a customer with `GET /v1/customers/{id}/notes`.
- Points to **Conversations** via `relationships.conversation`: The conversation a note is left on. Every note belongs to one conversation, which counts its notes in `noteCount`.

### Permissions

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

### Events

Workflow triggers:

- `kustomer.note.create` (Note Created): An agent or workflow adds a note
- `kustomer.note.update` (Note Updated): A note is edited

Workflow actions:

- `kustomer.note.create` (Note: Create): required inputs `conversation`, `body`
- `kustomer.note.update` (Note: Update): required inputs `id`
- `kustomer.note.find-by-id` (Note: Lookup (By ID)): required inputs `id`
- `kustomer.note.find-by-external` (Note: Lookup (By External ID)): required inputs `identifier`

## Request body

Content type: `application/json` (required)

- `id` (string)
- `externalId` (string): (max length 256)
- `body` (string, required): (min length 1; max length 10240)
- `userMentions` (array<object>): (max items 20)
  - `user` (string)
  - `team` (string)
- `createdAt` (string<date-time>)
- `modifiedAt` (string<date-time>)
- `createdBy` (string<object-id>)
- `modifiedBy` (string<object-id>)
- `lang` (string, nullable): (max length 100)

Example:

```json
{
  "id": "65f2c0a1b4e3d2001234abcd",
  "externalId": "crm-10293",
  "body": "Example text",
  "userMentions": [
    {
      "user": "65f2c0a1b4e3d2001234abcd",
      "team": "string"
    }
  ],
  "createdAt": "2026-03-14T15:09:26.535Z",
  "modifiedAt": "2026-03-14T15:09:26.535Z"
}
```

## Responses

### 201

Created

- `data` (object, required)
  - `type` (string, required)
  - `id` (string, required)
  - `attributes` (object, required)
    - `body` (string, required)
    - `externalId` (string, required)
    - `createdAt` (string, required)
  - `relationships` (object, required)
    - `org` (object, required)
    - `customer` (object, required)
    - `createdBy` (object, required)
    - `conversation` (object, required)
    - `userMentions` (object, required)
  - `links` (object, required)
    - `self` (string, required)

```json
{
  "data": {
    "type": "note",
    "id": "5828a3be7c7f551a00b5b720",
    "attributes": {
      "body": "Hey __Agent Smith__, do you know the answer to this problem?",
      "externalId": "example",
      "createdAt": "2021-11-13T17:32:46.480Z"
    },
    "relationships": {
      "org": {
        "links": {
          "self": "/v1/orgs/57bb5d2e32e83a130052e94e"
        },
        "data": {
          "type": "org",
          "id": "57bb5d2e32e83a130052e94e"
        }
      },
      "customer": {
        "links": {
          "self": "/v1/customers/57c08ccde595371400462df2"
        },
        "data": {
          "type": "customer",
          "id": "57c08ccde595371400462df2"
        }
      },
      "createdBy": {
        "links": {
          "self": "/v1/users/57bb5d2e32e83a130052e94f"
        },
        "data": {
          "type": "user",
          "id": "57bb5d2e32e83a130052e94f"
        }
      },
      "conversation": {
        "links": {
          "self": "/v1/conversations/57c8d2b365f7cc14004e674f"
        },
        "data": {
          "type": "conversation",
          "id": "57c8d2b365f7cc14004e674f"
        }
      },
      "userMentions": {
        "data": [
          {
            "type": "user",
            "id": "57c8ccd7b417854d0003246b"
          }
        ]
      }
    },
    "links": {
      "self": "/v1/notes/5828a3be7c7f551a00b5b720"
    }
  }
}
```

### 400

Bad Request

### 401

Unauthorized

### 403

Forbidden

## Example request

```bash
curl --request POST \
  --url 'https://api.kustomerapp.com/v1/notes' \
  --header "Authorization: Bearer $KUSTOMER_API_KEY" \
  --header 'Content-Type: application/json' \
  --data '{
  "id": "65f2c0a1b4e3d2001234abcd",
  "externalId": "crm-10293",
  "body": "Example text",
  "userMentions": [
    {
      "user": "65f2c0a1b4e3d2001234abcd",
      "team": "string"
    }
  ],
  "createdAt": "2026-03-14T15:09:26.535Z",
  "modifiedAt": "2026-03-14T15:09:26.535Z"
}'
```
