---
id: 99e2a4b9-4daa-415a-a65d-5d62028a41ba
published: true
blueprint: api
title: "AI Feedback API"
auth_method: http_basic
standard_endpoint: "https://amplitude.com/api/1/ai-feedback"
eu_endpoint: "https://eu.amplitude.com/api/1/ai-feedback"
api_status: beta
lede: "Use the AI Feedback API to send customer feedback into Amplitude for AI-powered analysis."
summary: "Send customer feedback to Amplitude AI Feedback through an HTTP API."
updated_by: 0c3a318b-936a-4cbd-8fdf-771a90c297f0
updated_at: 1711310400
---

## Considerations

- All endpoints use HTTP Basic auth. Use your project's API key as the username and your secret key as the password.
- The request body must be JSON with `Content-Type: application/json`.
- Maximum payload size is 5 MB.
- Each request accepts 1–100 feedback items.
- Amplitude deduplicates both items and conversation parts by their `unique_id` within a source. Sending the same data twice is safe. Amplitude updates (upserts) existing records.
- To append new conversation parts to an item you've already sent, re-send the item with the same `unique_id` and include the new parts. Amplitude updates existing parts (matched by their `unique_id`) in place and adds new parts.

## Find your source ID

To find the `source_id` for a webhook source, navigate to the source details page in AI Feedback and click the **three-dot menu** and then click **Settings**. The page displays the source ID and example request.

## EU data residency

For EU data residency, use `https://eu.amplitude.com` as the base URL instead of `https://amplitude.com`. For example:

- Ingest feedback: `POST https://eu.amplitude.com/api/1/ai-feedback/ingest`.

## Ingest feedback

Sends customer feedback items into Amplitude. Each item can optionally include conversation parts for threaded feedback such as support tickets, call transcripts, or chat conversations.

`POST https://amplitude.com/api/1/ai-feedback/ingest`

{{partial:tabs tabs="cURL, HTTP"}}
{{partial:tab name="cURL"}}

```curl
curl -X POST 'https://amplitude.com/api/1/ai-feedback/ingest' \
  -u '{api_key}:{secret_key}' \
  -H 'Content-Type: application/json' \
  -d '{
    "source_id": "YOUR_SOURCE_ID",
    "items": [
      {
        "unique_id": "review-789",
        "body": "The search feature is broken on mobile",
        "author": "Jane Doe",
        "user_id": "user-123",
        "created_at": "2026-03-15T10:30:00Z"
      }
    ]
  }'
```

{{/partial:tab}}
{{partial:tab name="HTTP"}}

```bash
POST /api/1/ai-feedback/ingest HTTP/1.1
Host: amplitude.com
Authorization: Basic {api-key}:{secret-key} #credentials must be base64 encoded
Content-Type: application/json
```

{{/partial:tab}}
{{/partial:tabs}}

### Body parameters

| Name        | Description                                                                                                                                                                   |
| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `source_id` | Required. String (1–128 characters). The source ID for your feedback source. Find this on the source details page by clicking the **three-dot** menu and then clicking **Settings**. |
| `items`     | Required. Array (1–100 items). Feedback items to ingest.                                                                                                                      |

### Item object

| Name                 | Description                                                                                                                                                                                                                             |
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `unique_id`          | Required. String (max 1024 characters). A stable, unique identifier for this item in your system. Use to deduplicate items. Sending the same `unique_id` again updates the existing item.                                                         |
| `body`               | Optional. String (max 20,000 characters). The feedback text. You can omit `body` when adding conversation parts to an item that Amplitude already ingested. This lets you append new replies without re-sending the original item details.  |
| `author`             | Optional. String (max 512 characters). Display name of the person who wrote this item.                                                                                                                                                  |
| `email`              | Optional. String (max 512 characters). Email address of the author. Must be a valid email.                                                                                                                                              |
| `user_id`            | Optional. String (max 1024 characters). The Amplitude user ID. This maps directly to the `user_id` field in Analytics, allowing you to link feedback to user behavior and see feedback alongside behavioral data in cohorts and charts. |
| `url`                | Optional. String (max 2048 characters). URL associated with this item, for example a source page or ticket link. Must be a valid URL.                                                                                                   |
| `created_at`         | Optional. ISO 8601 datetime string. Denotes the creation of the feedback. Defaults to now.                                                                                                                                                     |
| `conversation_parts` | Optional. Array (max 100). Follow-up messages or replies belonging to this item.                                                                                                                                                        |

### Conversation part object

Each conversation part uses the same fields as an item, except `body` is **required** (min 1 character) and `conversation_parts` can't be nested.

| Name         | Description                                                                                                                            |
| ------------ | -------------------------------------------------------------------------------------------------------------------------------------- |
| `unique_id`  | Required. String (max 1024 characters). A stable, unique identifier for this conversation part in your system. Used for deduplication. |
| `body`       | Required. String (max 20,000 characters). The text content.                                                                           |
| `author`     | Optional. String (max 512 characters). Display name of the person who wrote this part.                                                 |
| `email`      | Optional. String (max 512 characters). Email address of the author.                                                                    |
| `user_id`    | Optional. String (max 1024 characters). The Amplitude user ID. This maps directly to the `user_id` field in Analytics.                 |
| `url`        | Optional. String (max 2048 characters). URL associated with this conversation part.                                                    |
| `created_at` | Optional. ISO 8601 datetime string. Denotes the creation of the feedback. Defaults to now.                                                                                   |

### Response

```json
{
  "ingested": 2,
  "failed": 0,
  "errors": []
}
```

| Property   | Description                                                                                        |
| ---------- | -------------------------------------------------------------------------------------------------- |
| `ingested` | Number of items successfully ingested.                                                             |
| `failed`   | Number of items that failed validation or processing.                                              |
| `errors`   | Array of error objects. Each has `unique_id` (string) and `error` (string) describing the failure. |

If some items fail:

```json
{
  "ingested": 1,
  "failed": 1,
  "errors": [
    {
      "unique_id": "ticket-9999",
      "error": "Body text exceeds maximum length"
    }
  ]
}
```

---

## Examples

### Simple feedback

Send standalone pieces of feedback (NPS responses, survey answers, reviews). Each piece of feedback is its own item with no conversation parts.

{{partial:tabs tabs="cURL, HTTP"}}
{{partial:tab name="cURL"}}

```curl
curl -X POST 'https://amplitude.com/api/1/ai-feedback/ingest' \
  -u '{api_key}:{secret_key}' \
  -H 'Content-Type: application/json' \
  -d '{
    "source_id": "YOUR_SOURCE_ID",
    "items": [
      {
        "unique_id": "review-789",
        "body": "The search feature is broken on mobile",
        "author": "Jane Doe",
        "user_id": "user-123",
        "created_at": "2026-03-15T10:30:00Z",
        "url": "https://feedback.example.com/reviews/789"
      },
      {
        "unique_id": "nps-response-4822",
        "body": "The onboarding flow was confusing and I almost gave up.",
        "author": "Bob Johnson",
        "created_at": "2026-03-20T15:00:00Z"
      }
    ]
  }'
```

{{/partial:tab}}
{{partial:tab name="HTTP"}}

```bash
POST /api/1/ai-feedback/ingest HTTP/1.1
Host: amplitude.com
Authorization: Basic {api-key}:{secret-key} #credentials must be base64 encoded
Content-Type: application/json

{
  "source_id": "YOUR_SOURCE_ID",
  "items": [
    {
      "unique_id": "review-789",
      "body": "The search feature is broken on mobile",
      "author": "Jane Doe",
      "user_id": "user-123",
      "created_at": "2026-03-15T10:30:00Z",
      "url": "https://feedback.example.com/reviews/789"
    },
    {
      "unique_id": "nps-response-4822",
      "body": "The onboarding flow was confusing and I almost gave up.",
      "author": "Bob Johnson",
      "created_at": "2026-03-20T15:00:00Z"
    }
  ]
}
```

{{/partial:tab}}
{{/partial:tabs}}

### Support ticket with replies

Send a support ticket where there is an initial message followed by conversation parts. To add new replies to an existing ticket after the initial import, go to [Add replies to an existing item](#add-replies-to-an-existing-item).

{{partial:tabs tabs="cURL, HTTP"}}
{{partial:tab name="cURL"}}

```curl
curl -X POST 'https://amplitude.com/api/1/ai-feedback/ingest' \
  -u '{api_key}:{secret_key}' \
  -H 'Content-Type: application/json' \
  -d '{
    "source_id": "YOUR_SOURCE_ID",
    "items": [
      {
        "unique_id": "ticket-456",
        "body": "I need to export my dashboard data to Excel but I cannot find the option",
        "author": "Jane Doe",
        "user_id": "user-123",
        "created_at": "2026-03-10T09:00:00Z",
        "url": "https://support.example.com/tickets/456",
        "conversation_parts": [
          {
            "unique_id": "ticket-456-1",
            "body": "Thanks for reaching out! The export feature is under Settings > Data Export.",
            "author": "Support Agent",
            "email": "agent@company.com",
            "url": "https://support.example.com/tickets/456/1",
            "created_at": "2026-03-10T09:15:00Z"
          },
          {
            "unique_id": "ticket-456-2",
            "body": "I see it now, but it only exports CSV. We really need native Excel support.",
            "author": "Jane Doe",
            "user_id": "user-123",
            "created_at": "2026-03-10T09:20:00Z"
          }
        ]
      }
    ]
  }'
```

{{/partial:tab}}
{{partial:tab name="HTTP"}}

```bash
POST /api/1/ai-feedback/ingest HTTP/1.1
Host: amplitude.com
Authorization: Basic {api-key}:{secret-key} #credentials must be base64 encoded
Content-Type: application/json

{
  "source_id": "YOUR_SOURCE_ID",
  "items": [
    {
      "unique_id": "ticket-456",
      "body": "I need to export my dashboard data to Excel but I cannot find the option",
      "author": "Jane Doe",
      "user_id": "user-123",
      "created_at": "2026-03-10T09:00:00Z",
      "url": "https://support.example.com/tickets/456",
      "conversation_parts": [
        {
          "unique_id": "ticket-456-1",
          "body": "Thanks for reaching out! The export feature is under Settings > Data Export.",
          "author": "Support Agent",
          "email": "agent@company.com",
          "url": "https://support.example.com/tickets/456/1",
          "created_at": "2026-03-10T09:15:00Z"
        },
        {
          "unique_id": "ticket-456-2",
          "body": "I see it now, but it only exports CSV. We really need native Excel support.",
          "author": "Jane Doe",
          "user_id": "user-123",
          "created_at": "2026-03-10T09:20:00Z"
        }
      ]
    }
  ]
}
```

{{/partial:tab}}
{{/partial:tabs}}

### Call transcript

Send a call transcript where each speaker turn is a conversation part. The item represents the entire call. Each conversation part should represent a new speaker turn and alternate between speakers so the transcript reads as a natural conversation. For call transcripts, you don't need to include a `body` on the item itself, just send the individual conversation parts.

{{partial:tabs tabs="cURL, HTTP"}}
{{partial:tab name="cURL"}}

```curl
curl -X POST 'https://amplitude.com/api/1/ai-feedback/ingest' \
  -u '{api_key}:{secret_key}' \
  -H 'Content-Type: application/json' \
  -d '{
    "source_id": "YOUR_SOURCE_ID",
    "items": [
      {
        "unique_id": "call-123",
        "author": "Sales Rep",
        "created_at": "2026-03-12T14:00:00Z",
        "url": "https://gong.io/calls/123",
        "conversation_parts": [
          {
            "unique_id": "call-123-1",
            "body": "How does that compare to the competition on pricing?",
            "author": "Buyer",
            "user_id": "user-456",
            "created_at": "2026-03-12T14:00:18Z"
          },
          {
            "unique_id": "call-123-2",
            "body": "Great question. Our base tier starts at...",
            "author": "Sales Rep",
            "created_at": "2026-03-12T14:00:22Z"
          }
        ]
      }
    ]
  }'
```

{{/partial:tab}}
{{partial:tab name="HTTP"}}

```bash
POST /api/1/ai-feedback/ingest HTTP/1.1
Host: amplitude.com
Authorization: Basic {api-key}:{secret-key} #credentials must be base64 encoded
Content-Type: application/json

{
  "source_id": "YOUR_SOURCE_ID",
  "items": [
    {
      "unique_id": "call-123",
      "author": "Sales Rep",
      "created_at": "2026-03-12T14:00:00Z",
      "url": "https://gong.io/calls/123",
      "conversation_parts": [
        {
          "unique_id": "call-123-1",
          "body": "How does that compare to the competition on pricing?",
          "author": "Buyer",
          "user_id": "user-456",
          "created_at": "2026-03-12T14:00:18Z"
        },
        {
          "unique_id": "call-123-2",
          "body": "Great question. Our base tier starts at...",
          "author": "Sales Rep",
          "created_at": "2026-03-12T14:00:22Z"
        }
      ]
    }
  ]
}
```

{{/partial:tab}}
{{/partial:tabs}}

### Add replies to an existing item

To append new conversation parts to an item you have already sent, re-send the item with the same `unique_id` and include only the new parts. You don't need to include a `body` or re-send the original item details. Only send the `unique_id` and the new conversation parts.

{{partial:tabs tabs="cURL, HTTP"}}
{{partial:tab name="cURL"}}

```curl
curl -X POST 'https://amplitude.com/api/1/ai-feedback/ingest' \
  -u '{api_key}:{secret_key}' \
  -H 'Content-Type: application/json' \
  -d '{
    "source_id": "YOUR_SOURCE_ID",
    "items": [
      {
        "unique_id": "ticket-456",
        "conversation_parts": [
          {
            "unique_id": "ticket-456-3",
            "body": "We have added Excel export in the latest release. Can you try again?",
            "author": "Support Agent",
            "email": "agent@company.com",
            "created_at": "2026-03-11T10:00:00Z"
          }
        ]
      }
    ]
  }'
```

{{/partial:tab}}
{{partial:tab name="HTTP"}}

```bash
POST /api/1/ai-feedback/ingest HTTP/1.1
Host: amplitude.com
Authorization: Basic {api-key}:{secret-key} #credentials must be base64 encoded
Content-Type: application/json

{
  "source_id": "YOUR_SOURCE_ID",
  "items": [
    {
      "unique_id": "ticket-456",
      "conversation_parts": [
        {
          "unique_id": "ticket-456-3",
          "body": "We have added Excel export in the latest release. Can you try again?",
          "author": "Support Agent",
          "email": "agent@company.com",
          "created_at": "2026-03-11T10:00:00Z"
        }
      ]
    }
  ]
}
```

{{/partial:tab}}
{{/partial:tabs}}

{{partial:admonition type="note" heading=""}}
You only need to include the new conversation parts. You don't need to re-send parts that Amplitude already ingested. However, re-sending them is safe and Amplitude updates (upserts) them based on their `unique_id`.
{{/partial:admonition}}

## Status and error codes

| Code               | Description                                                                                                        |
| ------------------ | ------------------------------------------------------------------------------------------------------------------ |
| `200 OK`           | Successful request.                                                                                                |
| `400 Bad Request`  | Invalid request body — missing required fields or validation errors. Response includes detailed validation issues. |
| `401 Unauthorized` | Missing authentication.                                                                                            |
| `403 Forbidden`    | Invalid credentials, or the source identified by `source_id` isn't a webhook type.                                |
| `404 Not Found`    | No source found matching the provided `source_id`.                                                                 |
