
# メッセージと会話

Messages APIを使用すると、受信トレイを開くことなく、連絡先へのメッセージ送信、会話の読み取り、送信済みメッセージの修正や削除、リアクションの追加、チャットセッションスレッド全体の取得、トランスクリプトのエクスポート、チャットの既読/未読設定を行うことができます。

このページのすべてのパスは、ベースURL `https://api.youraiconnector.com/v1` からの相対パスです。すべてのリクエストにはAPIキーが必要です。送信方法の全リストについては[認証](authentication.md)を参照してください。以下の例では `X-API-Key` ヘッダーを使用しており、1つのcURL例では `?apiKey=` クエリ形式も示しています。

> **配信の仕組み:** メッセージを送信しても、その到着を待機するわけでは**ありません**。APIはメッセージを受け取ると、即座にメッセージIDを返して終了し、その後バックグラウンドで連絡先のチャネル（WhatsApp、SMS、Instagramなど）を通じて配信します。メッセージが実際に配信または既読されたかどうかを追跡するには、[Webhooks](webhooks.md)を使用してステータス更新をリッスンしてください。ポーリングは行わないでください。送信レスポンスは、メッセージが受け付けられたことのみを確認するものです。

---

## メッセージの送信

送信方法は2通りあります。連絡先の識別方法に合わせて選択してください。

- **連絡先IDで送信** — 連絡先IDがすでにわかっている場合（APIを通じて連絡先を作成した、またはWebhookから取得した場合など）。`POST /contacts/{contactId}/send-message`を使用します。
- **連絡先識別情報で送信** — 電話番号やInstagram IDなどはわかっているが、内部IDが不明な場合。`POST /contacts/send`を使用し、プラットフォームに適切な連絡先を検索させます。

どちらの方法でもメッセージは同じようにキューに入れられ、連絡先が利用しているチャネルで配信されます。トランスポートを選択する必要はありません。プラットフォームがWhatsAppの連絡先にはWhatsApp経由で、SMSの連絡先にはSMS経由でといったようにルーティングを行います。

### 連絡先IDで送信

`POST /contacts/{contactId}/send-message`

| フィールド | 必須 | 説明 |
|---|---|---|
| `body` | はい | 送信するメッセージテキスト。 |
| `mediaUrl` | いいえ | 添付するメディアファイル（画像、ドキュメントなど）のURL。 |
| `mediaContentType` | いいえ | 添付メディアのMIMEタイプ（例: `image/jpeg`）。 |
| `pauseBot` | いいえ | `true`は、メッセージ送信時にこの連絡先に対するAIを一時停止します（人間が対応を引き継ぐ場合など）。[AIの一時停止または再開](#pause-or-resume-the-ai-for-one-contact)を参照してください。 |
| `clearIncompleteReply` | いいえ | `true`は、作成途中のボットの返信を破棄し、メッセージ送信後に再開されないようにします。 |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/contacts/contact123/send-message" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "body": "Hi! Your appointment is confirmed for tomorrow at 10:00."
  }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/contacts/contact123/send-message",
  {
    method: "POST",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      body: "Hi! Your appointment is confirmed for tomorrow at 10:00.",
    }),
  }
);
const data = await res.json();
console.log(data.messageId);
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/contacts/contact123/send-message",
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={"body": "Hi! Your appointment is confirmed for tomorrow at 10:00."},
)
print(res.json()["messageId"])
```

**レスポンス** (`200 OK`):

```json
{
  "success": true,
  "messageId": "aB3dE5fG7hI9jK1lM2nO",
  "contactId": "contact123",
  "channel": "whatsapp",
  "message": "Message created successfully. Delivery is being processed."
}
```

### 連絡先識別情報で送信

`POST /contacts/send`

連絡先の内部IDがない場合に使用します。メッセージの `body` に加えて、`contact_id` **または** `channel` のいずれかと、そのチャネルに一致する識別フィールドを指定してください。

| フィールド | 必須 | 説明 |
|---|---|---|
| `body` | はい | 送信するメッセージテキスト。 |
| `contact_id` | いいえ | 既存の連絡先のID。設定されている場合、以下の識別フィールドは不要です。 |
| `channel` | いいえ | 送信に使用するチャネル。`contact_id`が指定されていない場合に必須です。送信可能な14のチャネルのうちの1つ：`whatsapp`、`whatsapp_web`、`sms`、`instagram`、`instagram_private`、`messenger`、`telegram`、`chat-widget`、`custom`、`email`、`line`、`imessage`、`linkedin`、`viber`。 |
| `phone_number` | いいえ | 国際形式の連絡先の電話番号。`whatsapp`、`whatsapp_web`、`sms`と共に使用します。 |
| `instagram_id` | いいえ | 連絡先のInstagramユーザーID。`instagram`と共に使用します。 |
| `messenger_id` | いいえ | 連絡先のMessengerユーザーID。`messenger`と共に使用します。 |
| `telegram_user_id` | いいえ | 連絡先のTelegramユーザーID。`telegram`と共に使用します。 |
| `media_url` | いいえ | 添付するメディアファイルのURL。 |
| `media_content_type` | いいえ | 添付メディアのMIMEタイプ（例：`image/jpeg`）。 |

**識別情報によって解決可能なチャネル。** 14チャネルのうち、`contact_id`の代わりに識別フィールドを受け入れるのは6つのみです。`whatsapp`、`whatsapp_web`、`sms`は`phone_number`で検索され、`instagram`は`instagram_id`で、`messenger`は`messenger_id`で、`telegram`は`telegram_user_id`で検索されます。その他の8つ（`instagram_private`、`chat-widget`、`custom`、`email`、`line`、`imessage`、`linkedin`、`viber`）には検索可能な公開識別情報がないため、これらのチャネルで送信するには`contact_id`が必要です。`channel`のみを渡すと、`contact_id`が必要であることを示す`400`が返されます。

**cURL** (`?apiKey=` クエリ形式を使用)

```bash
curl -X POST "https://api.youraiconnector.com/v1/contacts/send?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "channel": "whatsapp",
    "phone_number": "+31612345678",
    "body": "Hi! Your appointment is confirmed."
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/contacts/send", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    channel: "whatsapp",
    phone_number: "+31612345678",
    body: "Hi! Your appointment is confirmed.",
  }),
});
const data = await res.json();
console.log(data.message_id, data.channel);
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/contacts/send",
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={
        "channel": "whatsapp",
        "phone_number": "+31612345678",
        "body": "Hi! Your appointment is confirmed.",
    },
)
data = res.json()
print(data["message_id"], data["channel"])
```

**レスポンス** (`201 Created`):

```json
{
  "success": true,
  "message_id": "aB3dE5fG7hI9jK1lM2nO",
  "contact_id": "contact123",
  "channel": "whatsapp"
}
```

> **メッセージが拒否される理由:** おやすみモードやプライベートモードがオンになっている連絡先は、アウトバウンドメッセージを受信できません。その場合、リクエストは`422`で失敗します。指定したIDや識別情報に一致する連絡先がない場合は、`404`が返されます。

---

## 連絡先のメッセージを一覧表示する

`GET /contacts/{contactId}/messages`

連絡先のメッセージを新しい順に返し、カーソルベースのページネーションを使用します。

| クエリパラメータ | 必須 | 説明 |
|---|---|---|
| `limit` | いいえ | ページサイズ。デフォルトは`50`、最大は`100`。 |
| `cursor` | いいえ | 前回のレスポンスからの`next_cursor`値。カーソルより古いメッセージを返します。 |
| `filter` | いいえ | コンテンツタイプによるフィルタリング: `all` (デフォルト)、`text`、`media`、または`tool_use`。 |
| `direction` | いいえ | 方向によるフィルタリング: `all` (デフォルト)、`inbound` (連絡先から受信)、または`outbound` (あなたが送信)。 |

> **フィルタリングとページネーションに関する注意:** `filter`および`direction`フィルタは、各ページが読み込まれた後に適用されるため、フィルタリングされたページには`limit`より少ないアイテムが含まれる場合があります。`next_cursor`は会話全体を通じて進むため、`next_cursor`が`null`になるまでページングを続けてください。

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/contacts/contact123/messages?limit=50&direction=inbound" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const params = new URLSearchParams({ limit: "50", direction: "inbound" });
const res = await fetch(
  `https://api.youraiconnector.com/v1/contacts/contact123/messages?${params}`,
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
console.log(data.messages, data.next_cursor);
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/contacts/contact123/messages",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={"limit": 50, "direction": "inbound"},
)
data = res.json()
print(data["messages"], data["next_cursor"])
```

**レスポンス** (`200 OK`):

```json
{
  "success": true,
  "contact_id": "contact123",
  "messages": [
    {
      "id": "aB3dE5fG7hI9jK1lM2nO",
      "body": "Hi! Thanks for reaching out.",
      "direction": "inbound",
      "channel": "whatsapp",
      "status": "delivered",
      "type": null,
      "timestamp": "2026-06-01T10:00:00.000Z",
      "media_url": null,
      "media_content_type": null,
      "bot_reply": false
    }
  ],
  "next_cursor": "cD4eF6gH8iJ0kL2mN3oP"
}
```

### メッセージフィールド

| フィールド | 説明 |
|---|---|
| `id` | メッセージの一意のID。 |
| `body` | メッセージのテキストコンテンツ。 |
| `direction` | `inbound`（連絡先から受信）または `outbound`（アカウントから送信）。 |
| `channel` | メッセージの送受信に使用されたチャネル（例: `whatsapp`、`sms`、`instagram`）。 |
| `status` | 現在の配信ステータス（例: `Created`、`sent`、`delivered`、`read`、`failed`）。 |
| `type` | メッセージタイプ。プレーンテキストメッセージは `null` タイプです。自動アシスタントツールの活動は `tool_use` とマークされます。 |
| `timestamp` | メッセージが作成されたISO 8601形式の時刻。 |
| `media_url` | 添付メディアファイルがある場合のURL。 |
| `media_content_type` | 添付メディアがある場合のMIMEタイプ。 |
| `bot_reply` | AIアシスタントによって生成されたメッセージの場合の `true`。 |
| `score` | メッセージに対する評価：`1` 高評価、`-1` 低評価、`0` 未評価。詳細は [メッセージの評価またはスター付け](#rate-or-star-a-message) を参照してください。 |
| `is_important` | メッセージにスターが付けられている場合の `true`。 |
| `is_deleted` | メッセージが削除されている場合の `true`。削除されたメッセージはリストに残りますが、その `body` と `media_url` は空になります。 |
| `reactions` | メッセージに対する双方からの絵文字リアクション。常に配列形式で、リアクションがない場合は空になります。各エントリには `emoji`、`from_phone_number`、`from_me`（自分のリアクションの場合は `true`）、および `reacted_at` が含まれます。 |

---

## チャットセッションの一覧表示

チャットセッションとは、連絡先との1つの会話ウィンドウのことです。連絡先が話し始めると開始され、会話が終了すると閉じられます。セッションを使用することで、終わりのないリストではなく、読みやすい会話単位で履歴をページングできます。

### すべての連絡先の最近のセッション

`GET /chat-sessions/recent`

アカウント内のすべての連絡先を対象に、過去X時間以内に開始されたセッションを新しい順に返します。

| クエリパラメータ | 必須 | 説明 |
|---|---|---|
| `hours` | はい | 何時間前まで遡るか。正の整数である必要があります。 |
| `status` | いいえ | 指定したステータス（`ChatSessionOpened` または `ChatSessionClosed`）のセッションのみを返します。 |
| `limit` | いいえ | 返すセッションの最大数。デフォルトは `100`、最大は `100` です。 |
| `includeMessages` | いいえ | `true` を指定すると、すべてのセッションに `messages` 配列が追加されます。レスポンスサイズが大幅に増加するため、デフォルトではオフになっています。 |

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/chat-sessions/recent?hours=24&status=ChatSessionClosed" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const params = new URLSearchParams({ hours: "24", status: "ChatSessionClosed" });
const res = await fetch(`https://api.youraiconnector.com/v1/chat-sessions/recent?${params}`, {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
console.log(data.data.total_sessions, data.data.sessions);
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/chat-sessions/recent",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={"hours": 24, "status": "ChatSessionClosed"},
)
data = res.json()["data"]
print(data["total_sessions"], data["sessions"])
```

**レスポンス** (`200 OK`):

```json
{
  "success": true,
  "data": {
    "hours_ago": 24,
    "total_sessions": 2,
    "sessions": [
      {
        "session_id": "session456",
        "contact_id": "contact123",
        "contact_name": "Jane Doe",
        "contact_phone": "+31612345678",
        "contact_email": "jane@example.com",
        "start_date_time": "2026-06-01T09:55:00.000Z",
        "end_date_time": "2026-06-01T10:20:00.000Z",
        "status": "ChatSessionClosed",
        "tag": "Booking enquiry"
      }
    ]
  }
}
```

### 特定の連絡先のすべてのセッション

`GET /chat-sessions/{contactId}`

特定の連絡先のすべてのチャットセッションを返します。上記の `status`、`limit`、`includeMessages` パラメータと同じですが、`hours` はここでは適用されません。

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/chat-sessions/contact123?limit=20" \
  -H "X-API-Key: YOUR_API_KEY"
```

**レスポンス** (`200 OK`):

```json
{
  "success": true,
  "data": {
    "contact_id": "contact123",
    "contact_name": "Jane Doe",
    "total_sessions": 2,
    "sessions": [
      {
        "id": "session456",
        "start_date_time": "2026-06-01T09:55:00.000Z",
        "end_date_time": "2026-06-01T10:20:00.000Z",
        "status": "ChatSessionClosed",
        "tag": "Booking enquiry"
      }
    ]
  }
}
```

> **セッションIDのフィールド名は、2つのエンドポイント間で異なります。** 最近のセッションリストでは `session_id` と呼ばれます（セッションは多くの連絡先から取得されるため、連絡先の詳細も含まれます）。連絡先ごとのリストでは `id` と呼ばれます。どちらの値も、以下のスレッド全体を取得する際に `{sessionId}` として渡す値です。

`includeMessages=true` の場合、各セッションに `messages` 配列が追加され、そのエントリには `id`、`body`、`direction`、`timestamp`、`type`、`channel`、`status` が含まれます。

---

## チャットセッションのスレッドを取得する

`GET /contacts/{contactId}/chat-sessions/{sessionId}/messages`

チャットセッションは、連絡先とのメッセージを1つの会話ウィンドウにグループ化します。このエンドポイントは、単一セッションの全スレッドを**古い順**に、セッションのメタデータとともに返します。連絡先のセッションIDは、チャットセッションのエンドポイントから取得できます。

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/contacts/contact123/chat-sessions/session456/messages" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/contacts/contact123/chat-sessions/session456/messages",
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
console.log(data.session, data.messages);
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/contacts/contact123/chat-sessions/session456/messages",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
print(data["session"], data["messages"])
```

**レスポンス** (`200 OK`):

```json
{
  "success": true,
  "contact_id": "contact123",
  "session": {
    "id": "session456",
    "status": "ChatSessionClosed",
    "start_date_time": "2026-06-01T09:55:00.000Z",
    "end_date_time": "2026-06-01T10:20:00.000Z",
    "tag": "Booking enquiry"
  },
  "messages": [
    {
      "id": "aB3dE5fG7hI9jK1lM2nO",
      "body": "Hi! Thanks for reaching out.",
      "direction": "inbound",
      "channel": "whatsapp",
      "status": "delivered",
      "type": null,
      "timestamp": "2026-06-01T09:55:00.000Z",
      "media_url": null,
      "media_content_type": null,
      "bot_reply": false
    }
  ]
}
```

`session` オブジェクトは、`status`（アクティブな間は `ChatSessionOpened`、終了後は `ChatSessionClosed`）、`start_date_time`、`end_date_time`、および人間が読み取れる `tag` を報告します。`messages` 配列は、リストエンドポイントと同じ [メッセージフィールド](#message-fields) を使用します。

---

## メッセージの編集、削除、リアクション

これらのエンドポイントは、送信後のメッセージを変更します。そのうち2つは、自分のコピーだけでなく連絡先のチャネルにも影響を与えるため、実装前にセクションの導入部をお読みください。何が可能かは、会話が行われているチャネルに完全に依存します。

**各チャネルで可能な操作**

| アクション | 連絡先のコピーを変更できるチャネル | 制限時間 |
|---|---|---|
| 送信済みメッセージの編集 | チャットウィジェット、WhatsApp Web、Telegram、LinkedIn | チャットウィジェットは制限なし、WhatsApp Webは15分、Telegramは48時間、LinkedInは60分 |
| 全員に対して削除 | チャットウィジェット、WhatsApp Web、Telegram、LinkedIn | LinkedInは60分。その他は公開されている制限時間なし |
| 絵文字でリアクション | WhatsApp Web、Telegram | なし |

その他のすべてのチャネル（WhatsApp Business API、SMS、Instagram、Messenger、メール、LINE、カスタムチャネル）では、削除を行うと受信トレイからはメッセージが削除されますが、連絡先側のコピーは保持されます。また、編集やリアクションは一切できません。

### メッセージの編集

`POST /contacts/{contactId}/messages/{messageId}/edit`

すでに送信したメッセージを、連絡先のデバイス上および自分のコピーの両方で書き換えます。

| フィールド | 必須 | 説明 |
|---|---|---|
| `body` | はい | 新しいメッセージテキスト。空にすることはできず、最大4096文字までです。 |

削除とは異なり、この操作はチャネル側で拒否された場合に**明確な失敗**となります。その際、`409` が返され、自分のコピーは連絡先側の状態と完全に一致したままになります。これは、相手に届かなかった編集内容を表示すると、双方の同期が取れなくなるためです。`edit_reason` フィールドには、チャネルの編集可能時間が終了した、チャネルが切断されている、あるいはその他の問題が発生したといった理由が示されます。

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/contacts/contact123/messages/msg_1/edit" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "body": "Sorry - I meant Thursday at 3pm." }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/contacts/contact123/messages/msg_1/edit",
  {
    method: "POST",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ body: "Sorry - I meant Thursday at 3pm." }),
  }
);
const data = await res.json();
console.log(data.edited, data.edit_reason);
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/contacts/contact123/messages/msg_1/edit",
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={"body": "Sorry - I meant Thursday at 3pm."},
)
data = res.json()
print(data.get("edited"), data.get("edit_reason"))
```

**レスポンス** (`200 OK`):

```json
{
  "success": true,
  "contact_id": "contact123",
  "message_id": "msg_1",
  "edited": true,
  "edit_reason": "edit_dispatched"
}
```

チャネルが編集を受け付けない場合、代わりに `409` が返され、何も変更されません：

```json
{
  "success": false,
  "error": "The message could not be edited",
  "edit_reason": "channel_disconnected"
}
```

すでに削除済みのメッセージ、編集機能を持たないチャネル、チャネルの制限時間を過ぎたメッセージに対するリクエストはすべて `400` を返します。リクエストはチャネルに到達しません。

### メッセージを1件削除する

`DELETE /contacts/{contactId}/messages/{messageId}`

会話からメッセージを削除します。チャネルが許可している場合は、連絡先のコピーも削除します。リクエストボディは不要です。

メッセージが存在していた場合、連絡先のコピーを削除できなかったとしても、この操作は常に `200` を返します。自分のコピーは**確実に**削除されているため、エラーを返すと誤解を招く可能性があるからです。レスポンスに含まれる3つのフィールドを確認して、実際に何が起こったのかをユーザーに伝えてください：

| フィールド | 説明 |
|---|---|
| `revoke_supported` | このチャネルがメッセージを削除できるかどうか。 |
| `revoked` | 連絡先のデバイス上のコピーが削除されたかどうか。 |
| `revoke_reason` | `revoked` が `false` の場合に削除されなかった理由（例：`revoke_window_closed` や `already_deleted`）。 |

**cURL**

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/contacts/contact123/messages/msg_1" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/contacts/contact123/messages/msg_1",
  { method: "DELETE", headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
console.log(data.revoked, data.revoke_reason);
```

**Python**

```python
import requests

res = requests.delete(
    "https://api.youraiconnector.com/v1/contacts/contact123/messages/msg_1",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
print(data["revoked"], data["revoke_reason"])
```

**レスポンス** (`200 OK`):

```json
{
  "success": true,
  "contact_id": "contact123",
  "message_id": "msg_1",
  "revoke_supported": true,
  "revoked": true,
  "revoke_reason": "revoke_dispatched"
}
```

> 削除されたメッセージは会話履歴から削除されません。それらは `GET /contacts/{contactId}/messages` に残り、`is_deleted: true` と空の `body` および `media_url` が保持されます。

### 複数のメッセージを一度に削除する

`POST /contacts/{contactId}/messages/bulk-delete`

自分側のメッセージを一括で消去します。本文と添付ファイルは空になりますが、**相手のデバイス上のメッセージは取り消されません**。メッセージを相手側からも取り消すには、上記の単一メッセージ用エンドポイントを使用して1つずつ削除してください。

| フィールド | 必須 | 説明 |
|---|---|---|
| `message_ids` | はい | メッセージIDの空ではない配列（1リクエストにつき最大500件）。`messageIds` もエイリアスとして受け入れられます。 |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/contacts/contact123/messages/bulk-delete" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "message_ids": ["msg_1", "msg_2"] }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/contacts/contact123/messages/bulk-delete",
  {
    method: "POST",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ message_ids: ["msg_1", "msg_2"] }),
  }
);
console.log((await res.json()).deleted);
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/contacts/contact123/messages/bulk-delete",
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={"message_ids": ["msg_1", "msg_2"]},
)
print(res.json()["deleted"])
```

**レスポンス** (`200 OK`):

```json
{
  "success": true,
  "contact_id": "contact123",
  "deleted": 2
}
```

### メッセージにリアクションする

`POST /contacts/{contactId}/messages/{messageId}/react`

メッセージに自分の絵文字リアクションを追加します。空の文字列を送信すると、リアクションを取り消すことができます。相手自身のリアクションには一切影響しません。

| フィールド | 必須 | 説明 |
|---|---|---|
| `emoji` | はい | リアクションに使用する絵文字、またはリアクションを削除するための `""`。スペースを含まない単一の文字列で、最大16文字である必要があります。 |

編集と同様に、相手に届かなかったリアクションを表示するのではなく失敗します。その際、再試行する価値があるかどうかがエラー内容からわかります：

- `422` — この会話では配信できません：チャンネルがリアクションをサポートしていない、メッセージにチャンネル側のIDがない、または絵文字がそのチャンネルで許可されているセットに含まれていません。
- `409` — チャンネルに一時的に接続できませんでした。再試行すれば成功する可能性があります。

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/contacts/contact123/messages/msg_1/react" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "emoji": "👍" }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/contacts/contact123/messages/msg_1/react",
  {
    method: "POST",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ emoji: "👍" }),
  }
);
const data = await res.json();
console.log(data.reactions);
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/contacts/contact123/messages/msg_1/react",
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={"emoji": "👍"},
)
print(res.json()["reactions"])
```

**レスポンス** (`200 OK`):

```json
{
  "success": true,
  "contact_id": "contact123",
  "message_id": "msg_1",
  "reaction_supported": true,
  "reaction_reason": "reaction_dispatched",
  "reactions": [
    {
      "emoji": "👍",
      "from_phone_number": "+31612345678",
      "from_me": true,
      "reacted_at": "2026-06-01T10:05:00.000Z"
    }
  ]
}
```

`reactions` 配列は、自分と相手のものを含め、現在メッセージに付いているリアクションの完全なセットです。`409` または `422` の場合、変更されずに返されるため、そこから直接レンダリングするクライアントは、配信されなかったリアクションを表示することはありません。

### メッセージを評価またはスター付けする

`PATCH /contacts/{contactId}/messages/{messageId}`

メッセージに高評価（サムズアップ）または低評価（サムズダウン）を付けたり、重要としてスターを付けたりします。これは自分側のみの記録であり、相手には何も送信されません。

| フィールド | 必須 | 説明 |
|---|---|---|
| `score` | いいえ | `1` 高評価、`-1` 低評価、`0` 評価をクリアします。 |
| `is_important` | いいえ | `true` メッセージにスターを付けます。`false` スターを外します。文字列の `"true"` ではなく、実際のブール値である必要があります。 |

少なくともどちらか一方を送信してください。そうしないと `400` が返されます。送信した内容のみが書き込まれるため、メッセージにスターを付けても評価がクリアされることはなく、その逆も同様です。また、レスポンスには送信したフィールドのみがエコーバックされます。

**cURL**

```bash
curl -X PATCH "https://api.youraiconnector.com/v1/contacts/contact123/messages/msg_1" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "score": 1, "is_important": true }'
```

**JavaScript**

```javascript
await fetch("https://api.youraiconnector.com/v1/contacts/contact123/messages/msg_1", {
  method: "PATCH",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ score: 1, is_important: true }),
});
```

**Python**

```python
import requests

requests.patch(
    "https://api.youraiconnector.com/v1/contacts/contact123/messages/msg_1",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"score": 1, "is_important": True},
)
```

**レスポンス** (`200 OK`):

```json
{
  "success": true,
  "contact_id": "contact123",
  "message_id": "msg_1",
  "score": 1,
  "is_important": true
}
```

---

## メッセージを既読にする

未読状態は、特定のメッセージに対して、または会話全体に対してクリアできます。

### 特定のメッセージを既読にする

`POST /contacts/{contactId}/messages/mark-read`

既読にするメッセージのIDを渡します。

| フィールド | 必須 | 説明 |
|---|---|---|
| `message_ids` | はい | メッセージIDの空ではない配列（1リクエストにつき最大500個まで）。 |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/contacts/contact123/messages/mark-read" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "message_ids": ["aB3dE5fG7hI9jK1lM2nO", "cD4eF6gH8iJ0kL2mN3oP"]
  }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/contacts/contact123/messages/mark-read",
  {
    method: "POST",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      message_ids: ["aB3dE5fG7hI9jK1lM2nO", "cD4eF6gH8iJ0kL2mN3oP"],
    }),
  }
);
const data = await res.json();
console.log(data.marked_read);
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/contacts/contact123/messages/mark-read",
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={"message_ids": ["aB3dE5fG7hI9jK1lM2nO", "cD4eF6gH8iJ0kL2mN3oP"]},
)
print(res.json()["marked_read"])
```

**レスポンス** (`200 OK`):

```json
{
  "success": true,
  "contact_id": "contact123",
  "marked_read": 2
}
```

### チャット全体を既読にする

`POST /contacts/{contactId}/mark-read`

受信トレイ内の連絡先との会話全体の未読バッジをクリアします。リクエストボディは不要です。

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/contacts/contact123/mark-read" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/contacts/contact123/mark-read",
  { method: "POST", headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
console.log(data.success);
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/contacts/contact123/mark-read",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
print(res.json()["success"])
```

**レスポンス** (`200 OK`):

```json
{
  "success": true,
  "contact_id": "contact123"
}
```

### チャット全体を未読にする

`POST /contacts/{contactId}/mark-unread`

会話に未読バッジを再度表示します。チームの誰かがチャットを開いたものの、対応を戻す場合に便利です。リクエストボディは不要です。

これは受信トレイ専用のフラグであり、会話が最後にいつ読まれたかは変更されません。そのため、既読通知をサポートしているチャネルであっても、連絡先には既読通知は送信されません。

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/contacts/contact123/mark-unread" \
  -H "X-API-Key: YOUR_API_KEY"
```

**レスポンス** (`200 OK`):

```json
{
  "success": true,
  "contact_id": "contact123"
}
```

---

## 会話をエクスポートする

エクスポート機能を使用すると、メッセージをページ送りするのではなく、会話全体を読み取り可能なトランスクリプトとして取得できます。すべてのエクスポートエンドポイントは、メッセージリストのフィルターと一致する `filter`（デフォルトは `all`、その他 `text`、`media`、`tool_use`）を受け付けます。

### 1人の連絡先とのチャットをエクスポートする

`GET /chat-exports/{contactId}`

| クエリパラメータ | 必須 | 説明 |
|---|---|---|
| `format` | いいえ | `txt`（デフォルト）はプレーンテキストのトランスクリプトへのダウンロードリンクを返します。`json` はメッセージを構造化データとしてレスポンスで返します。 |
| `filter` | いいえ | `all`（デフォルト）、`text`、`media`、または `tool_use`。 |

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/chat-exports/contact123?format=json" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/chat-exports/contact123",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={"format": "json"},
)
print(res.json()["data"]["messages"])
```

**`format=json` を含むレスポンス** (`200 OK`):

```json
{
  "success": true,
  "data": {
    "contact": {
      "id": "contact123",
      "name": "Jane Doe",
      "phone": "+31612345678",
      "email": "jane@example.com"
    },
    "messages": [
      {
        "body": "Hi! I have a question about my order.",
        "direction": "inbound",
        "timestamp": "2026-06-01T09:55:00.000Z",
        "type": "text",
        "media_url": null,
        "media_content_type": null,
        "name": null,
        "args": null
      }
    ]
  }
}
```

`format=txt`（デフォルト）の場合、`data` は生成されたトランスクリプトファイルへのダウンロードリンクになります：

```json
{
  "success": true,
  "data": "https://storage.googleapis.com/.../chat-export-contact123-....txt"
}
```

> **ダウンロードリンクの有効期限は短いです。** リンクを取得したら保存せずにすぐにファイルをダウンロードしてください。トランスクリプトが再度必要な場合は、新しくエクスポートをリクエストしてください。

### 最近のすべての会話をエクスポートする

`GET /chat-exports/recent`

過去X時間以内にアクティブだったすべての連絡先の会話を、1回の呼び出しでエクスポートします。

| クエリパラメータ | 必須 | 説明 |
|---|---|---|
| `hours` | はい | アクティビティを遡る時間数。正の整数である必要があります。 |
| `format` | いいえ | `json`（デフォルト）は連絡先ごとに1つのエントリを返します。`txt`はすべての会話を含む単一のダウンロード可能なテキストファイルを返します。 |
| `limit` | いいえ | エクスポートする連絡先の最大数。デフォルトは`50`、最大は`100`です。 |
| `filter` | いいえ | `all`（デフォルト）、`text`、`media`、または`tool_use`。 |

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/chat-exports/recent?hours=24&limit=25" \
  -H "X-API-Key: YOUR_API_KEY"
```

**レスポンス** (`200 OK`):

```json
{
  "success": true,
  "data": {
    "hours_ago": 24,
    "total_contacts": 2,
    "exports": [
      {
        "contactId": "contact123",
        "contactName": "Jane Doe",
        "phoneNumber": "+31612345678",
        "email": "jane@example.com",
        "messageCount": 12,
        "chatExport": "Acme Export - Jane Doe\nPhone: +31612345678\n..."
      }
    ]
  }
}
```

`format=txt`を使用すると、レスポンスはJSONではなく、ダウンロードとして送信されるテキストファイルそのものになります。

> この1回の呼び出しで、一致するすべての連絡先の全履歴が取得されるため、アクティブなアカウントでは`hours`と`limit`を控えめに設定してください。

### 連絡先にトランスクリプトをメールで送信する

`POST /chat-exports/{contactId}/email`

連絡先自身の会話トランスクリプトをメールで送信します。これは、独自のシステムから実行される「このチャットをメールで送信」フローです。

| フィールド | 必須 | 説明 |
|---|---|---|
| `recipient_email` | いいえ | 送信先。デフォルトは連絡先に保存されているメールアドレスです。 |
| `via` | いいえ | `auto`（デフォルト）は最適なルートを選択し、`transactional`はシステムメールとして送信し、`email_channel`は接続済みのメールチャネルから送信します。 |
| `note` | いいえ | トランスクリプトの上に表示される短いメッセージ。最大1000文字まで。 |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/chat-exports/contact123/email" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "note": "Here is a copy of our chat, as promised." }'
```

**レスポンス** (`200 OK`):

```json
{
  "success": true,
  "data": {
    "via": "transactional",
    "recipientEmail": "jane@example.com",
    "messageCount": 42,
    "omittedCount": 0
  }
}
```

`omittedCount`は、メールを適切な長さに保つために、古いメッセージがいくつ除外されたかを示します。`200`は、トランスクリプトが作成され送信待ち状態であることを意味し、まだ受信トレイに届いていないことを示します。

---

## 連絡先ごとのAIの一時停止または再開

`PUT /contacts/{contactId}`

`is_bot_active`を`false`に設定すると、特定の連絡先に対するAIの返信が停止します。`true`に戻すと、会話がボットに引き継がれます。人間が会話に介入する際に使用する引き継ぎスイッチです。ボットが一時停止している間も、API経由で送信したメッセージは相手に届きます。

**cURL**

```bash
curl -X PUT "https://api.youraiconnector.com/v1/contacts/contact123" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "is_bot_active": false }'
```

**JavaScript**

```javascript
await fetch("https://api.youraiconnector.com/v1/contacts/contact123", {
  method: "PUT",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ is_bot_active: false }),
});
```

**Python**

```python
import requests

requests.put(
    "https://api.youraiconnector.com/v1/contacts/contact123",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"is_bot_active": False},
)
```

**レスポンス**

```json
{
  "success": true,
  "contact_id": "contact123"
}
```

**返信の一部として一時停止する**

人間が返信を送ることで対応を引き継ぐ場合、2回目の呼び出しを行う代わりに、同じリクエスト内でボットを一時停止できます。`POST /contacts/{contactId}/send-message`は2つのオプションフラグを受け付けます。

| フィールド | 説明 |
|---|---|
| `pauseBot` | `true`は、メッセージ送信時にこの連絡先に対するAIを一時停止します。 |
| `clearIncompleteReply` | `true`は、作成途中のボットの返信を破棄し、その後再開されないようにします。 |

```bash
curl -X POST "https://api.youraiconnector.com/v1/contacts/contact123/send-message" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "body": "Hi, Sarah here - taking over from the assistant.",
    "pauseBot": true,
    "clearIncompleteReply": true
  }'
```

一時停止が適用された場合、レスポンスには`"botPaused": true`が含まれます。

> 連絡先を[`POST /contacts/bulk-flag`](contacts.md)でプライベートに設定することでも、その連絡先に対するボットが一時停止されます。フィールドの全リストについては[連絡先](contacts.md)を参照してください。

---

## 独自のインボックスを構築する

インボックスに必要なすべての情報は、このページおよび[連絡先](contacts.md)に記載されています。

| 必要な操作 | エンドポイント |
|---|---|
| 会話のリスト | `GET /contacts` |
| 会話の読み取り | `GET /contacts/{contactId}/messages` |
| 連絡先のチャットセッションのリスト | `GET /chat-sessions/{contactId}` |
| 最近の受信内容の確認 | `GET /chat-sessions/recent` |
| 1つのチャットセッションの読み取り | `GET /contacts/{contactId}/chat-sessions/{sessionId}/messages` |
| 手動返信の送信 | `POST /contacts/{contactId}/send-message` |
| 送信した返信の修正 | `POST /contacts/{contactId}/messages/{messageId}/edit` |
| メッセージの削除 | `DELETE /contacts/{contactId}/messages/{messageId}` |
| 複数のメッセージの消去 | `POST /contacts/{contactId}/messages/bulk-delete` |
| 絵文字でのリアクション | `POST /contacts/{contactId}/messages/{messageId}/react` |
| メッセージの評価またはスター付け | `PATCH /contacts/{contactId}/messages/{messageId}` |
| 既読にする | `POST /contacts/{contactId}/mark-read` |
| チャットをチームに戻す | `POST /contacts/{contactId}/mark-unread` |
| トランスクリプトのエクスポート | `GET /chat-exports/{contactId}` |
| AIの一時停止または再開 | `PUT /contacts/{contactId}` （`is_bot_active`を使用） |

ライブアップデートを取得するには、タイマーでこのAPIをポーリングするのではなく、[Webhook](webhooks.md)を使用して`New Message`、`Replies`、`Human Alerted`、`Chat Concluded`のイベントを購読してください。

---

## Messages APIのエラー

メッセージエンドポイントは、標準的なエラーエンベロープを返します：

```json
{
  "success": false,
  "error": "Contact not found"
}
```

| ステータス | メッセージエンドポイントで発生する場合 |
|---|---|
| `400` | 必須フィールドが欠落しているか、パラメータが無効です（不正な`limit`、`hours`、`filter`、`direction`、`status`、空または500を超える`message_ids`配列、無効な`cursor`、空または長すぎる編集`body`、`-1`/`0`/`1`の範囲外の`score`、またはスペースを含むか16文字を超える絵文字）。また、メッセージが全く編集できない場合（削除済み、チャネルが編集をサポートしていない、またはチャネルの編集可能期間を過ぎている）にも返されます。 |
| `404` | 連絡先、チャットセッション、または提供されたメッセージIDのいずれかが見つかりませんでした。 |
| `409` | チャネルが現在変更を受け付けられません。何も書き込まれませんでした：編集の場合、`edit_reason`が理由を示します。リアクションの場合、チャネルが一時的に到達不能であり、再試行で成功する可能性があります。 |
| `422` | 連絡先がアウトバウンドメッセージを受信できません（おやすみモード、プライベート、またはサポートされていないチャネル）、またはこの会話ではリアクションを配信できません（`reaction_reason`が理由を示します）。 |

すべてのエンドポイントが返す共通コード（`401`、`403`（プランにAPIアクセスが含まれていない）、`429`（レート制限）、`500`）については、再試行のガイダンスと共に[エラーとページネーション](errors-and-pagination.md)に記載されています。

---

## 次のステップ

- [Webhooks](webhooks.md) — ポーリングの代わりに、配信ステータスの更新をプッシュで受け取ります。
- [連絡先](contacts.md) — メッセージを送信する連絡先を作成および検索します。
- [予約](appointments.md) — 連絡先の予約を登録および管理します。
