
# Contacts API

コンタクトとは、メッセージをやり取りする個人のことで、名前、電話番号、メールアドレス、チャネル、タグ、カスタムフィールド、およびそのコンタクトが属するリストやキャンペーンなどが含まれます。Contacts APIを使用すると、ダッシュボードを使わずに、コンタクトの作成、検索、更新、タグ付け、一括インポート、削除を行うことができます。

このページのすべてのパスは、ベースURLからの相対パスです：

```
https://api.youraiconnector.com/v1
```

したがって、`/contacts` は `https://api.youraiconnector.com/v1/contacts` を意味します。

> **APIを初めてお使いですか？** まず[APIアクセス](../integrations/api-access.md)をお読みください。APIキーの生成方法、3つの認証方法、レート制限、エラー形式について説明しています。このページのすべての内容は、すでに有効なAPIキーをお持ちであることを前提としています。

---

## コンタクトIDについて

すべてのコンタクトには一意のIDがあります。コンタクトを**作成**した際（`data.contactId`）に返されるIDは、他のすべての場所（コンタクトの取得、更新、タグ付け、メッセージ送信、削除など）で使用するIDと同じです。一度保存すれば、再利用可能です。

IDを取得するためにコンタクトを作成する必要はありません。電話番号やメールアドレスで検索する（[コンタクトの取得](#get-a-contact-by-phone-or-email)を参照）、またはすべてのコンタクトをページ送りで確認する（[コンタクトの一覧表示](#list-contacts)を参照）こともできます。いずれの場合も、同じIDが返されます。

---

## コンタクトの作成

`POST /contacts`

アカウントに新しいコンタクトを追加します。**国番号を含む電話番号が必須**であり、メールアドレスのみでは不十分です。それ以外の項目はすべて任意です。

オプションとして、`listId`（単一のリスト）または `listIds`（配列）を使用して、新しいコンタクトを1つ以上のリストに直接追加できます。両方が送信された場合は、`listIds` が優先されます。

以下の**連絡先の作成**フィールドテーブルに記載されている標準的な作成フィールド（`phoneNumber`、`firstName`、`lastName`、`email`、`channel`、`is_bot_active`、`is_private`、`lead_profile`、`listId`、`listIds`、`custom_fields`）以外のフィールドを送信した場合、そのフィールドは自動的に**カスタムフィールド**として保存されます。そのため、MakeやZapierのようなツールからのフラットなペイロードをネストせずにそのまま使用できます。明示的な `custom_fields` オブジェクトを渡すことも可能です。

| フィールド | 必須 | 説明 |
|---|---|---|
| `phoneNumber` | はい | 国番号を含むコンタクトの電話番号（例: `+15551234567`）。 |
| `firstName` | いいえ | 名。 |
| `lastName` | いいえ | 姓。 |
| `email` | いいえ | メールアドレス。 |
| `channel` | いいえ | メッセージングチャネル。`whatsapp`、`sms`、`whatsapp_web`のいずれか。デフォルトは `whatsapp`。 |
| `is_bot_active` | いいえ | AIアシスタントがこのコンタクトに返信するかどうか。デフォルトは `true`。 |
| `is_private` | いいえ | コンタクトをプライベートとしてマークします。`true`の場合、そのコンタクトに対してAIアシスタントはオフになります。デフォルトは `false`。 |
| `lead_profile` | いいえ | リードに関する自由記述のメモ。 |
| `listId` | いいえ | コンタクトを追加する単一のリストID。 |
| `listIds` | いいえ | コンタクトを追加するリストIDの配列（`listId`よりも優先されます）。 |
| `custom_fields` | いいえ | 独自のキー/値フィールドのオブジェクト。これらはトップレベルのキーとして渡すこともできます。 |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/contacts?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phoneNumber": "+15551234567",
    "firstName": "Jane",
    "lastName": "Smith",
    "email": "jane@example.com",
    "is_bot_active": true,
    "listIds": ["list123", "list456"]
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/contacts", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    phoneNumber: "+15551234567",
    firstName: "Jane",
    lastName: "Smith",
    email: "jane@example.com",
    is_bot_active: true,
    listIds: ["list123", "list456"],
  }),
});
const data = await res.json();
console.log(data.data.contactId);
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/contacts",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "phoneNumber": "+15551234567",
        "firstName": "Jane",
        "lastName": "Smith",
        "email": "jane@example.com",
        "is_bot_active": True,
        "listIds": ["list123", "list456"],
    },
)
print(res.json()["data"]["contactId"])
```

**レスポンス**

```json
{
  "success": true,
  "data": {
    "message": "Successfully created new contact",
    "contactId": "contact_abc123",
    "listsAdded": ["list123", "list456"]
  }
}
```

新しい連絡先のIDは `data.contactId` にあります。追加されたリストは `data.listsAdded` にエコーバックされます。

> **重複は作成されません。** 同じ電話番号を持つ連絡先が既に存在する場合、作成呼び出しによってその連絡先が作成されたり、返されたりすることはありません。レスポンスはHTTPステータス `200` とボディ内の `error_code` が `409` として返されるため、HTTPステータスではなく `error_code` で分岐処理を行ってください：
>
> ```json
> { "success": false, "error_code": 409, "error": "A contact with this phone number already exists for the current user." }
> ```
>
> `error_code` が `409` の場合に既存の連絡先を操作するには、[電話番号またはメールアドレスによる連絡先の取得](#get-a-contact-by-phone-or-email) — `GET /contacts?phoneNumber=...` — を使用して検索し、返されたIDを再利用してください。

> **WhatsAppの同等な表記は同一の番号としてカウントされます。** 一部の国では、同じモバイル回線に対して2つの有効な表記が存在し、WhatsAppはいずれか一方を報告する場合があります。メキシコ（`+52…`およびレガシーな`+521…`）、ブラジル（9桁目の有無）、アルゼンチン（`+54`の後の`9`の有無）などがこれに該当します。作成時の重複チェックおよび`GET /contacts?phoneNumber=`は両方の表記で照合されるため、どちらの形式で送信しても既存の連絡先が返されます。連絡先に保存されている`phone_number`が書き換えられることはありません。

---

## 電話番号またはメールアドレスによる連絡先の取得

`GET /contacts?phoneNumber=...` または `GET /contacts?email=...`

単一の連絡先を検索し、完全で詳細な連絡先オブジェクトを返します。これには、`{ id, name }` ペアに解決されたリスト、タグ、キャンペーン、および最後に行われたメッセージのやり取りが含まれます。

`phoneNumber`（国際形式）または `email` の**いずれか**を渡してください。どちらも渡さない場合、このエンドポイントは [連絡先リストの取得](#list-contacts) モードに切り替わります。

**cURL**

```bash
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts?phoneNumber=%2B15551234567&apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const phone = encodeURIComponent("+15551234567");
const res = await fetch(`https://api.youraiconnector.com/v1/contacts?phoneNumber=${phone}`, {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
console.log(data.contact);
```

**Python**

```python
import requests

res = requests.get(
    "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={"phoneNumber": "+15551234567"},
)
print(res.json()["contact"])
```

**レスポンス**

```json
{
  "success": true,
  "contactId": "contact_abc123",
  "contact": {
    "id": "contact_abc123",
    "firstName": "Jane",
    "lastName": "Smith",
    "email": "jane@example.com",
    "phoneNumber": "+15551234567",
    "channel": "whatsapp",
    "isBotActive": true,
    "isPrivate": false,
    "doNotDisturb": false,
    "lead_profile": null,
    "avatarUrl": "https://example.com/photo.jpg",
    "customFields": {},
    "lists": [{ "id": "list123", "name": "VIP customers" }],
    "tags": [{ "id": "tagHotLead", "name": "Hot lead" }],
    "campaigns": [{ "id": "campaign789", "name": "Spring promo" }],
    "currentCampaign": { "id": "campaign789", "name": "Spring promo" },
    "lastMessage": {
      "direction": "inbound",
      "body": "Sounds good, thanks!",
      "status": "received",
      "timestamp": "2026-06-09T10:21:00.000Z"
    }
  }
}
```

連絡先IDは、トップレベル (`contactId`) とオブジェクト内 (`contact.id`) の両方で返されます。一致するものがない場合は、`{ "success": false, "message": "Contact not found" }` を含む `404` が返されます。

> **`avatarUrl`** は、連絡先がWhatsAppやMetaからメッセージを送信した際に取得されるプロフィール写真です。これは読み取り専用であり、設定することはできません。また、写真が設定されていない連絡先や、写真を共有しないチャネル経由で連絡してきた連絡先の場合は `null` となります。これらの写真リンクの一部は期限切れとなり自動的に更新されるため、リンクを保存するのではなく、一時的なものとして扱ってください。（以下のリストエンドポイントでは、同じ値が `avatar_url` と呼ばれます。）

> **URL内の電話番号について。** クエリ文字列内の `+` 記号は `%2B` としてURLエンコードする必要があります。そうしないとスペースとして読み取られます。上記の例ではこれを行っています。

---

## IDで連絡先を取得する

`GET /contacts/{contactId}`

連絡先のIDがすでにわかっている場合は、直接取得できます。レスポンスの形式は上記の検索と同じです。

**cURL**

```bash
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123?apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123", {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
console.log(data.contact);
```

**Python**

```python
import requests

res = requests.get(
    "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
print(res.json()["contact"])
```

アカウントに存在しない連絡先IDを指定すると、`404`が返されます。

---

## コンタクトの統計を取得する

`GET /contacts/{contactId}/stats`

特定のコンタクトのメッセージ統計（合計、AI対人間の返信数、消費クレジット、最初/最後のメッセージのタイムスタンプ）を集計して返します。

**cURL**

```bash
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/stats?apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/stats", {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
console.log(data.totalMessages, data.creditsUsed);
```

**Python**

```python
import requests

res = requests.get(
    "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/stats",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
print(data["totalMessages"], data["creditsUsed"])
```

**レスポンス**

```json
{
  "success": true,
  "totalMessages": 48,
  "sent": 21,
  "received": 27,
  "aiReplies": 18,
  "humanReplies": 3,
  "creditsUsed": 34,
  "botMessageCount": 18,
  "firstMessageAt": "2026-05-01T09:00:00.000Z",
  "lastMessageAt": "2026-06-09T10:21:00.000Z"
}
```

`botMessageCount` は、アプリ内の「リセット」ボタンでゼロにリセットされるAIメッセージカウンターと同じものです。`creditsUsed` は、この応答の数値だけでなく、そのコンタクトの累積クレジット合計です。アカウントに存在しないコンタクトIDを指定すると、`404` が返されます。

---

## 連絡先を一覧表示する

`GET /contacts`

`phoneNumber`も`email`も指定せずに`GET /contacts`を呼び出すと、すべての連絡先を新しい順にページングして取得できます。各ページには連絡先のコンパクトな概要（リスト、タグ、キャンペーンは完全なオブジェクトではなくID配列として返されます）と`next_cursor`が含まれます。

| クエリパラメータ | 説明 |
|---|---|
| `limit` | ページサイズ。デフォルトは50、最大100です。 |
| `cursor` | 前のページから取得した`next_cursor`の値。最初のページでは省略します。 |
| `listId` | オプション。このリストに属する連絡先のみを返します。 |

すべてのページを順に取得するには、最初の呼び出しをカーソルなしで行い、その後は返された`next_cursor`を`cursor`として渡し続けます。`next_cursor`が`null`になったら停止してください。それが結果の終わりを意味します。

**cURL**

```bash
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts?limit=50&apiKey=YOUR_API_KEY"

# next page:
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts?limit=50&cursor=contact_abc123&apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
async function listAllContacts() {
  const all = [];
  let cursor = null;
  do {
    const url = new URL("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts");
    url.searchParams.set("limit", "100");
    if (cursor) url.searchParams.set("cursor", cursor);
    const res = await fetch(url, { headers: { "X-API-Key": "YOUR_API_KEY" } });
    const data = await res.json();
    all.push(...data.contacts);
    cursor = data.next_cursor;
  } while (cursor);
  return all;
}
```

**Python**

```python
import requests

def list_all_contacts():
    all_contacts = []
    cursor = None
    while True:
        params = {"limit": 100}
        if cursor:
            params["cursor"] = cursor
        res = requests.get(
            "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts",
            headers={"X-API-Key": "YOUR_API_KEY"},
            params=params,
        )
        data = res.json()
        all_contacts.extend(data["contacts"])
        cursor = data["next_cursor"]
        if not cursor:
            break
    return all_contacts
```

**レスポンス**

```json
{
  "success": true,
  "contacts": [
    {
      "id": "contact_abc123",
      "first_name": "Jane",
      "last_name": "Smith",
      "email": "jane@example.com",
      "phone_number": "+15551234567",
      "channel": "whatsapp",
      "is_bot_active": true,
      "is_private": false,
      "do_not_disturb": false,
      "avatar_url": "https://example.com/photo.jpg",
      "custom_fields": {},
      "created_at": "2026-06-01T09:00:00.000Z",
      "list_ids": ["list123"],
      "tag_ids": ["tagHotLead"],
      "campaign_ids": ["campaign789"],
      "current_campaign_id": "campaign789"
    }
  ],
  "next_cursor": "contact_abc123"
}
```

::: note
**注:** アカウントに存在しない `listId` でフィルタリングすると、`404` が返されます。無効な `cursor` は `400` を返します。
:::


---

## コンタクトのカウント

`GET /contacts/count`

フィルタに一致するコンタクトの数と、チャネルごとの内訳を、ページングなしで取得します。ダッシュボードのタイル、自動化、Champへの問い合わせなど、「いくつあるか」を知りたいあらゆる場面でこの呼び出しを使用してください。すべてのフィルタはオプションであり、複数を組み合わせるとカウントが絞り込まれます（コンタクトは指定したすべての条件に一致する必要があります）。

| クエリパラメータ | 説明 |
|---|---|
| `agentId` | このAIエージェントに割り当てられたコンタクトのみ。割り当てられたエージェントがいないコンタクト（チャネルのデフォルトエージェントが対応するもの）には `none` を渡します。 |
| `channel` | このチャネル上のコンタクトのみ（例: `whatsapp`, `messenger`, `instagram`, `sms`, `email`, `chat_widget`）。 |
| `tag` | このタグを持つコンタクトのみ（タグの**名前**で指定、大文字・小文字は区別されません）。存在しないタグ名を指定すると `404` が返されます。 |
| `listId` | このリスト上のコンタクトのみ。 |
| `botActive` | `true` または `false` — AIアシスタントがオン、またはオフになっているコンタクトのみ。 |
| `status` | このステータスのコンタクトのみ（例: `Lead`）。 |
| `rules` | スマートリストと同じ形式のURLエンコードされたJSONルールオブジェクト（後述の「[`smart_rules`の形式](#the-smart_rules-shape)」を参照）。他のフィルタと組み合わせることはできません。 |

フィルタを何も送信しない場合、アカウント上のコンタクトの総数が返されます。

**cURL**

```bash
# everything
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/count?apiKey=YOUR_API_KEY"

# only the contacts one agent handles on Messenger
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/count?agentId=agent_xyz789&channel=messenger&apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const url = new URL("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/count");
url.searchParams.set("agentId", "agent_xyz789");
url.searchParams.set("channel", "messenger");

const res = await fetch(url, { headers: { "X-API-Key": "YOUR_API_KEY" } });
const data = await res.json();
console.log(data.total);
```

**Python**

```python
import requests

res = requests.get(
    "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/count",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={"agentId": "agent_xyz789", "channel": "messenger"},
)
data = res.json()
print(data["total"])
```

**レスポンス**

```json
{
  "success": true,
  "total": 3423,
  "by_channel": { "messenger": 2744, "instagram": 667, "none": 12 },
  "filters": { "agentId": "agent_xyz789" }
}
```

`by_channel` は同じ合計数をチャネルごとに分割します。どのチャネルにも属さないコンタクトは `none` にカウントされます。`filters` は適用されたフィルタをそのまま返すため、呼び出しが意図通りに行われたかを確認できます。

::: note
**注意:** `rules` を他のフィルタと一緒に送信したり、有効なJSONではない `rules` 値を送信したりすると、`400` が返されます。アカウントに存在しないタグ名やリストIDを指定すると `404` が返されます。
:::


---

## 連絡先の更新

`PUT /contacts/{contactId}`

既存の連絡先を更新します。含めたフィールドのみが変更されます。変更したくないフィールドは除外してください。少なくとも1つのフィールドを送信する必要があります。そうしないと、`400`（「更新するフィールドがありません」）が返されます。

| フィールド | 説明 |
|---|---|
| `firstName` | 名（ファーストネーム）。 |
| `lastName` | 姓（ラストネーム）。 |
| `email` | メールアドレス。 |
| `is_bot_active` | AIアシスタントがこの連絡先に返信するかどうか。 |
| `is_private` | 非公開に設定します。`true`に設定すると、AIアシスタントもオフになります。 |
| `do_not_disturb` | この連絡先への自動アウトリーチを一時停止します。AIの返信も停止します。 |
| `follow_ups_disabled` | この連絡先に対するすべての自動フォローアップ（クイック、サイクル、コールドリード）を停止します。ただし、相手から送信されたメッセージへのAIによる返信は継続されます。購入済みの場合などに便利です。`false`に戻すまでオフのままになります。 |
| `lead_profile` | リードに関する自由記述のメモ。 |
| `custom_fields` | カスタムフィールドのオブジェクト。**キーごとにマージされます**。送信したキーのみが書き込まれ、既存のカスタムフィールドの残りは保持されます。カスタムフィールドのキーをトップレベルで渡すことも可能です。 |

**cURL**

```bash
curl -X PUT "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "firstName": "Jane", "do_not_disturb": true }'
```

**JavaScript**

```javascript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123", {
  method: "PUT",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ firstName: "Jane", do_not_disturb: true }),
});
const data = await res.json();
console.log(data.message);
```

**Python**

```python
import requests

res = requests.put(
    "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"firstName": "Jane", "do_not_disturb": True},
)
print(res.json()["message"])
```

**レスポンス**

```json
{
  "success": true,
  "message": "Contact updated successfully"
}
```

> **カスタムフィールドは置換ではなくマージされます。** `{ "custom_fields": { "tier": "gold" } }` を送信すると `tier` のみが設定され、連絡先の他のカスタムフィールドはそのまま維持されます。すべての連絡先からカスタムフィールドを完全に削除するには、[カスタムフィールドの削除](#delete-a-custom-field)を使用してください。

---

## タグの追加または削除

`POST /contacts/{contactId}/tags`

1回の呼び出しで、単一の連絡先に対してタグの追加や削除を行います。`addTagIds` と `removeTagIds` にタグの **ID** を渡します。両方のうち少なくとも1つは空であってはなりません。

タグはあらかじめアカウントに存在している必要があります。[タグエンドポイント](reference.md)から先に作成してください。連絡先または参照されたタグが存在しない場合、`404` が返されます。

| フィールド | 説明 |
|---|---|
| `addTagIds` | コンタクトに追加するタグIDの配列。 |
| `removeTagIds` | コンタクトから削除するタグIDの配列。 |

**cURL**

```bash
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/tags?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "addTagIds": ["tagHotLead"], "removeTagIds": ["tagColdLead"] }'
```

**JavaScript**

```javascript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/tags", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    addTagIds: ["tagHotLead"],
    removeTagIds: ["tagColdLead"],
  }),
});
const data = await res.json();
console.log(data.added, data.removed);
```

**Python**

```python
import requests

res = requests.post(
    "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/tags",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"addTagIds": ["tagHotLead"], "removeTagIds": ["tagColdLead"]},
)
data = res.json()
print(data["added"], data["removed"])
```

**レスポンス**

```json
{
  "success": true,
  "contact_id": "contact_abc123",
  "added": 1,
  "removed": 1
}
```

---

## タグライブラリを管理する

これらのエンドポイントは、コンタクトへのタグの適用や削除（上記の[タグの追加または削除](#add-or-remove-tags)を参照）とは異なり、アカウント上のタグそのものの名前変更や削除を行います。アカウント上のすべてのタグにはID（`tagId`）があります。これはダッシュボードのタグマネージャーに表示されるIDであり、`POST /tags` と `{ "name": "..." }` のJSONボディ（`phoneNumber`、`email`、または `contactId` は不要）を使用してタグを作成した際に `data.tag_id` として返されるIDです。

### タグを更新する

`PUT /tags/{tagId}`

変更するフィールドのみを送信してください。

| フィールド | 説明 |
|---|---|
| `name` | タグの名前。 |

```bash
curl -X PUT "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/tags/tagHotLead?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Hot lead (Q3)" }'
```

**レスポンス**

```json
{ "success": true, "tag_id": "tagHotLead" }
```

アカウントに存在しない `tagId` を指定すると、`404` が返されます。

### タグを削除する

`DELETE /tags/{tagId}`

IDを指定してタグを1つ削除します。**この操作は取り消せません**。そのタグが付与されていたコンタクトからはタグが削除されます。すでに存在しない（または最初から存在しなかった）タグを削除しようとした場合、列挙するものがないため、`404` ではなく `deleted: 0` を含む `200` が返されます。

```bash
curl -X DELETE "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/tags/tagColdLead?apiKey=YOUR_API_KEY"
```

**レスポンス**

```json
{ "success": true, "deleted": 1 }
```

### 複数のタグを一度に削除する

`DELETE /tags`

| フィールド | 説明 |
|---|---|
| `tagIds` | 削除するタグIDの配列（最大1000個）。 |

```bash
curl -X DELETE "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/tags?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "tagIds": ["tagColdLead", "tagUnsubscribed"] }'
```

**レスポンス**

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

存在しないIDや別のアカウントに属するIDは、エラーにならずにスキップされ、`deleted`にはカウントされません。

---

## フラグの一括設定

`POST /contacts/bulk-flag`

一度に多数のコンタクトに対して1つのブール値フラグを設定します。リクエストごとに最大500件のコンタクトIDを指定できます。アカウントに存在しないIDはスキップされ、`skipped` にカウントされます。

| フィールド | 説明 |
|---|---|
| `contactIds` | 更新するコンタクトIDの配列（最大500件）。 |
| `field` | 設定するフラグ。`bot_active`（AIアシスタントのオン/オフ）、`dnd`（自動アウトリーチの一時停止）、`spam`、`private` のいずれか。 |
| `value` | フラグに設定するブール値。 |

**cURL**

```bash
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/bulk-flag?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "contactIds": ["contactId1", "contactId2"],
    "field": "bot_active",
    "value": false
  }'
```

**JavaScript**

```javascript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/bulk-flag", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    contactIds: ["contactId1", "contactId2"],
    field: "bot_active",
    value: false,
  }),
});
const data = await res.json();
console.log(data.updated, data.skipped);
```

**Python**

```python
import requests

res = requests.post(
    "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/bulk-flag",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "contactIds": ["contactId1", "contactId2"],
        "field": "bot_active",
        "value": False,
    },
)
data = res.json()
print(data["updated"], data["skipped"])
```

**レスポンス**

```json
{
  "success": true,
  "updated": 2,
  "skipped": 0
}
```

---

## コンタクトの一括インポート

`POST /contacts/import`

JSON配列から1回の呼び出しで最大500件の連絡先を作成します。各レコードには国際形式の `phone_number` が必要です。それ以外の項目は任意です。電話番号が無効なレコードやサポートされていないチャネルのレコードは**スキップ**（作成されません）されます。スキップされたすべてのレコードは、インデックスと理由とともに報告されるため、失敗したレコードのみを修正して再試行できます。

アカウントに既に存在する電話番号は、デフォルトで `duplicate` としてスキップされます。`updateExisting: true` を送信すると、代わりにそれらの連絡先を**更新**できます。レコードに存在するフィールドが連絡先の情報を上書きし（`first_name`、`last_name`、`email`、`lead_profile`、および `custom_fields` はキーごとにマージされます）、`tags` が追加され、連絡先が `listId` に追加されます。既存の連絡先のチャネル、電話番号、ボットフラグは変更されません。

オプションで、インポート（または更新）されたすべての連絡先を `listId` を使用してリストに追加したり、指定のないレコードに対して `defaultChannel` を設定したり、`tags` を使用してレコードにタグ付けしたりできます（タグ名。存在しないタグは作成され、既存のタグは大文字と小文字を区別せずに照合されます）。

**トップレベルフィールド**

| フィールド | 必須 | 説明 |
|---|---|---|
| `contacts` | はい | 連絡先レコードの配列（最大500件）。 |
| `listId` | いいえ | インポート（および更新）されたすべての連絡先を追加するリスト。アカウント上のリストである必要があります。 |
| `defaultChannel` | いいえ | `channel` を省略したレコードに適用されるチャネル。`whatsapp`、`sms`、`whatsapp_web` のいずれか。デフォルトは `whatsapp` です。 |
| `updateExisting` | いいえ | 電話番号が既に存在する場合に、`duplicate` としてスキップする代わりに連絡先を更新するには `true` を指定します。デフォルトは `false` です。 |

**レコードごとのフィールド**

| フィールド | 必須 | 説明 |
|---|---|---|
| `phone_number` | はい | 国際形式の電話番号（不足している場合は先頭に `+` が追加されます）。 |
| `first_name` | いいえ | 名。 |
| `last_name` | いいえ | 姓。 |
| `email` | いいえ | メールアドレス。 |
| `channel` | いいえ | `whatsapp`、`sms`、`whatsapp_web` のいずれか。`defaultChannel` にフォールバックします。 |
| `is_bot_active` | いいえ | AIアシスタントが返信するかどうか。デフォルトは `true` です。 |
| `is_private` | いいえ | 非公開にするかどうか。デフォルトは `false` です。 |
| `lead_profile` | いいえ | フリーテキストのリードメモ。 |
| `custom_fields` | いいえ | カスタムフィールドのキーと値のオブジェクト。 |
| `tags` | いいえ | タグ名の配列（単一の `"a; b"` 文字列も使用可能）。存在しないタグは作成され、既存のタグは大文字と小文字を区別せずに照合されます。1レコードにつき最大25個まで。 |

**cURL**

```bash
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/import?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "contacts": [
      { "phone_number": "+12025551234", "first_name": "Ann", "last_name": "Lee", "tags": ["vip", "newsletter"] },
      { "phone_number": "+12025551235", "first_name": "Bob" }
    ],
    "listId": "list123",
    "defaultChannel": "whatsapp_web",
    "updateExisting": true
  }'
```

**JavaScript**

```javascript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/import", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    contacts: [
      { phone_number: "+12025551234", first_name: "Ann", last_name: "Lee", tags: ["vip", "newsletter"] },
      { phone_number: "+12025551235", first_name: "Bob" },
    ],
    listId: "list123",
    defaultChannel: "whatsapp_web",
    updateExisting: true,
  }),
});
const data = await res.json();
console.log(`Imported ${data.imported}, updated ${data.updated}, skipped ${data.skipped.length}`);
```

**Python**

```python
import requests

res = requests.post(
    "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/import",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "contacts": [
            {"phone_number": "+12025551234", "first_name": "Ann", "last_name": "Lee", "tags": ["vip", "newsletter"]},
            {"phone_number": "+12025551235", "first_name": "Bob"},
        ],
        "listId": "list123",
        "defaultChannel": "whatsapp_web",
        "updateExisting": True,
    },
)
data = res.json()
print(f"Imported {data['imported']}, updated {data['updated']}, skipped {len(data['skipped'])}")
```

**レスポンス**

```json
{
  "success": true,
  "imported": 2,
  "contact_ids": ["contact_abc123", "contact_def456"],
  "updated": 0,
  "updated_contact_ids": [],
  "skipped": []
}
```

一部のレコードが作成できない場合、その理由とともに `skipped` に表示されます（ここでは `updateExisting` がないため、既存の番号はスキップされます）。

```json
{
  "success": true,
  "imported": 1,
  "contact_ids": ["contact_abc123"],
  "updated": 0,
  "updated_contact_ids": [],
  "skipped": [
    { "index": 1, "phone_number": "+12025551235", "reason": "duplicate" }
  ]
}
```

`updateExisting: true` を使用すると、同じリクエストで既存の連絡先が `updated` / `updated_contact_ids` として報告されます。

スキップされる理由：`invalid_record`、`missing_phone_number`、`invalid_phone_number`、`invalid_channel`、`duplicate_in_request`、`duplicate`、`contact_limit_reached`、`create_failed`。

> **プランの制限。** プランの連絡先上限によりこれほど多くの新規連絡先を作成できない場合、リクエスト全体が事前に`403`で拒否されます。途中で上限に達した場合、残りのレコードは理由`contact_limit_reached`としてスキップされた状態で返されます。

---

## CSVファイルから連絡先をインポートする

[一括インポート](#bulk-import-contacts)でサポートされている件数（最大約50,000行）を超えるインポートを行う場合は、アカウントのストレージに保存済みのCSVファイルに対して非同期インポートジョブをキューに入れ、完了するまでポーリングしてください。

### インポートを開始する

`POST /contacts/import-csv`

| フィールド | 必須 | 説明 |
|---|---|---|
| `csvStoragePath` | はい | `users/{your account id}/imports/`配下にある、`.csv`で終わるCSVファイルのストレージパス。 |
| `listName` | はい | この名前のリストを作成（または再利用）し、インポートされたすべての連絡先を追加します。 |
| `existingListRefs` | いいえ | インポートされたすべての連絡先を追加する既存のリストIDの配列。 |
| `defaultChannel` | いいえ | 指定がない行に適用されるチャネル。 |

```bash
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/import-csv?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "csvStoragePath": "users/abc123/imports/leads.csv",
    "listName": "Webinar signups"
  }'
```

**JavaScript**

```javascript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/import-csv", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    csvStoragePath: "users/abc123/imports/leads.csv",
    listName: "Webinar signups",
  }),
});
const data = await res.json();
console.log(data.job_id);
```

**Python**

```python
import requests

res = requests.post(
    "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/import-csv",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "csvStoragePath": "users/abc123/imports/leads.csv",
        "listName": "Webinar signups",
    },
)
job_id = res.json()["job_id"]
```

**レスポンス** (`202` — インポートはキューに入れられましたが、まだ完了していません)

```json
{
  "success": true,
  "job_id": "csvimp_abc123",
  "status": "queued"
}
```

> **ストレージへのファイル配置について。** このエンドポイントはインポートジョブを開始および追跡するものであり、アップロード自体を受け付けるものではありません。このエンドポイントを呼び出す前に、CSVファイルがすでに`csvStoragePath`に配置されている必要があります。ダッシュボードのCSVインポーターは、最初のステップとしてこれを行います。

### インポートジョブをポーリングする

`GET /contacts/import-csv/{jobId}`

```bash
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/import-csv/csvimp_abc123?apiKey=YOUR_API_KEY"
```

**レスポンス**

```json
{
  "success": true,
  "job_id": "csvimp_abc123",
  "status": "completed",
  "imported": 812,
  "updated": 0,
  "skipped": 14,
  "errors": [],
  "error_message": null
}
```

`status`は、`queued` → `processing` → `completed`の順に遷移するか、`error_message`に理由が示されて`failed`となります。アカウントに存在しない`jobId`を指定すると、`404`が返されます。

---

## 連絡先をエクスポートする

連絡先の非同期CSVエクスポートを開始し、完了までポーリングするためのジョブを返します。

### エクスポートを開始する

`POST /contacts/export`

| フィールド | 必須 | 説明 |
|---|---|---|
| `listId` | いいえ | このリストに属する連絡先のみをエクスポートします。 |
| `contactIds` | いいえ | 指定した特定の連絡先IDのみをエクスポートします。 |

両方を指定しない場合は、アカウント上のすべての連絡先がエクスポートされます。

```bash
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/export?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "listId": "list123" }'
```

**JavaScript**

```javascript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/export", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ listId: "list123" }),
});
const data = await res.json();
console.log(data.job_id);
```

**Python**

```python
import requests

res = requests.post(
    "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/export",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"listId": "list123"},
)
job_id = res.json()["job_id"]
```

**レスポンス** (`202` — エクスポートがキューに入れられました)

```json
{
  "success": true,
  "job_id": "export_abc123",
  "status": "queued"
}
```

### エクスポートジョブをポーリングする

`GET /contacts/export/{jobId}`

```bash
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/export/export_abc123?apiKey=YOUR_API_KEY"
```

**レスポンス**

```json
{
  "success": true,
  "job_id": "export_abc123",
  "status": "completed",
  "export_id": "exp_xyz789",
  "contact_count": 812,
  "error_message": null
}
```

> `status` が `"completed"` になると、`export_id` と `contact_count` が送信されます。生成されたCSVファイルのダウンロードは、ダッシュボードの「エクスポート」ページから行います。

---

## 連絡先にメッセージを送信する

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

既存の連絡先が現在利用しているチャネルに対してメッセージを送信します。メッセージはキューに入れられ、バックグラウンドで配信されます。レスポンスはメッセージが受け付けられたことを示すものであり、配信が完了したことを示すものではありません。

| フィールド | 必須 | 説明 |
|---|---|---|
| `body` | はい | 送信するメッセージのテキスト。 |
| `mediaUrl` | いいえ | 添付するメディアファイルのURL。 |
| `mediaContentType` | いいえ | 添付メディアのMIMEタイプ（例: `image/jpeg`）。 |

**cURL**

```bash
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/send-message?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "body": "Hi! Your appointment is confirmed." }'
```

**JavaScript**

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

**Python**

```python
import requests

res = requests.post(
    "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/send-message",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"body": "Hi! Your appointment is confirmed."},
)
print(res.json()["messageId"])
```

**レスポンス**

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

> **今すぐ送信できませんか？** 連絡先が「おやすみモード」や「プライベートモード」を有効にしている場合、またはアウトバウンドメッセージを受信できないチャネルを使用している場合、リクエストは `422` と説明用の `error` を伴って拒否されます。

連絡先IDではなく、電話番号、Instagram ID、またはその他のチャネル識別子を使用して送信する場合、およびメッセージング全般の詳細については、[Messages API](messages.md) を参照してください。

---

## AIエージェントを連絡先に割り当てる

`POST /contacts/{contactId}/assign-agent`

既存の会話を、次のメッセージから別のAIエージェントに引き継ぎます。これはチャットメニューの **AIエージェントを割り当てる** と同じ機能であり、オートメーションの **AIエージェントまたはキャンペーンを割り当てる** アクションで使用されるステップと同じです。

| フィールド | 必須 | 説明 |
|---|---|---|
| `agentId` | はい | 引き継ぎを行うAIエージェントのID、または割り当てを解除して会話をチームの受信トレイに戻すための `null` を指定します。 |
| `triggerAIResponse` | いいえ | `true` を指定すると、新しく割り当てられたエージェントが連絡先の未回答の最新メッセージに即座に返信します。デフォルトは `false` です。 |

> **`triggerAIResponse: true` には注意してください** — コンタクトに対してその場でメッセージを送信するため、今すぐメッセージを送りたい場合にのみ使用してください。MessengerやInstagramでは、コンタクトからの最後のメッセージから24時間以上経過している場合、メッセージの送信は失敗します。

**cURL**

```bash
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/assign-agent?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "agentId": "agent_xyz789" }'
```

**JavaScript**

```javascript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/assign-agent", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ agentId: "agent_xyz789" }),
});
const data = await res.json();
console.log(data.data.agentId);
```

**Python**

```python
import requests

res = requests.post(
    "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/assign-agent",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"agentId": "agent_xyz789"},
)
print(res.json()["data"]["agentId"])
```

**レスポンス**

```json
{
  "success": true,
  "data": {
    "contactId": "contact_abc123",
    "agentId": "agent_xyz789",
    "aiResponseTriggered": false
  }
}
```

> エージェントは連絡先と同じアカウントに属している必要があります。そうでない場合、リクエストは `404` または `403` で拒否されます。エージェントIDは「AIエージェント」ページで確認できます（各エージェントのURLの末尾がIDになっています）。

---

## 多数のコンタクトへのAIエージェントの割り当て

`POST /contacts/bulk-assign-agent`

1回の呼び出しで多数の会話を別のAIエージェントに移動するか、`null` を使用してすべての割り当てを解除します。これは純粋なルーティングの変更であり、**メッセージは送信されず、エージェントが誰かに返信することもありません**。各コンタクトは、次にメッセージを送信した際に新しいエージェントが割り当てられます（そのため、ここには `triggerAIResponse` がありません）。

| フィールド | 必須 | 説明 |
|---|---|---|
| `agentId` | はい | 引き継ぐAIエージェント、または割り当てを解除するための `null`。 |
| `contactIds` | 3つのうち1つ | 移動する最大500件のコンタクトID。 |
| `filter` | 3つのうち1つ | コンタクトをリストアップする代わりに、サーバー上で新しい順に選択します。カウントエンドポイントのフィルタと同じキーを使用します: `agentId` (または `none`), `channel`, `tag`, `listId`, `botActive`, `status`。 |
| `rules` | 3つのうち1つ | スマートリストのルールオブジェクト — 「[`smart_rules`の形式](#the-smart_rules-shape)」を参照してください。 |
| `limit` | いいえ | `filter` または `rules` で選択した際に、この呼び出しで移動するコンタクトの数。1〜500で、デフォルトは500です。 |

`contactIds`、`filter`、`rules` のいずれか1つを正確に送信してください。

**cURL**

```bash
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/bulk-assign-agent?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "agentId": "agent_xyz789",
    "filter": { "agentId": "agent_abc123", "channel": "messenger" }
  }'
```

**JavaScript**

```javascript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/bulk-assign-agent", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    agentId: "agent_xyz789",
    filter: { agentId: "agent_abc123", channel: "messenger" },
  }),
});
const data = await res.json();
console.log(data.updated, data.remaining);
```

**Python**

```python
import requests

res = requests.post(
    "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/bulk-assign-agent",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "agentId": "agent_xyz789",
        "filter": {"agentId": "agent_abc123", "channel": "messenger"},
    },
)
data = res.json()
print(data["updated"], data["remaining"])
```

**レスポンス**

```json
{
  "success": true,
  "agentId": "agent_xyz789",
  "matched": 3415,
  "updated": 500,
  "skipped": 0,
  "remaining": 2915,
  "filters": { "agentId": "agent_abc123" }
}
```

`matched` は選択によって見つかったコンタクトの総数、`updated` はこの呼び出しで移動された数、`skipped` は送信したIDのうちアカウントで見つからなかった数、`remaining` はこの呼び出し完了時点で条件に一致している残りの数です。

**全員の移動について。** 1回の呼び出しで移動できるのは最大500件のコンタクトであるため、大規模なグループの場合は数回の呼び出しが必要です。移動したコンタクトが条件に一致しなくなるようなフィルタ（例: `agent_xyz789` に割り当てながら `filter: { "agentId": "agent_abc123" }` を使用するなど）を使用し、`remaining` が `0` として返されるまで全く同じ呼び出しを繰り返してください。`contactIds` を渡した場合、`remaining` は常に `0` になります。

---

## 連絡先を部門に割り当てる

`POST /contacts/{contactId}/department`

「このリードを営業に割り当てる」は、連絡先を指定した部門に登録し、デフォルトではその部門内で現在最も連絡先が少ない担当者に割り当てます。これは[AIエージェントの割り当て](#assign-an-ai-agent-to-a-contact)とは別物です。部門は「どのチームが担当するか」を決定し、エージェントは「どのAIが対応するか」を決定します。一方を設定しても、もう一方がクリアされることはありません。

| フィールド | 必須 | 説明 |
|---|---|---|
| `department_id` | はい | 連絡先を登録する部門。クリアするには `null` を渡します。 |
| `hand_to_member` | いいえ | その部門で最も負荷の低い担当者にも連絡先を割り当てます。デフォルトは `true` です。すでに担当者がいる連絡先を再割り当てすることはありません。 |

**cURL**

```bash
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/department?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "department_id": "dept_sales" }'
```

**JavaScript**

```javascript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/department", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ department_id: "dept_sales" }),
});
const data = await res.json();
console.log(data.assigned_to);
```

**Python**

```python
import requests

res = requests.post(
    "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/department",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"department_id": "dept_sales"},
)
print(res.json()["assigned_to"])
```

**レスポンス**

```json
{
  "success": true,
  "department_id": "dept_sales",
  "assigned_to": "member_uid_123"
}
```

連絡先がすでに誰かに割り当てられている場合、または `hand_to_member: false` を渡した場合、`assigned_to` は `null` になります。

---

## チャネル間で連絡先をリンクする

「WhatsAppで継続」（またはSMS）は、別の電話ベースのチャネルでその人物の連絡先を検索または作成し、両者をリンクします。これにより、アプリの他の部分で同一人物として認識されるようになります。

### 別のチャネルへのリンク

`POST /contacts/{contactId}/link-channel`

| フィールド | 必須 | 説明 |
|---|---|---|
| `channel` | はい | リンク先のチャネル。`whatsapp`、`whatsapp_web`、`sms` のいずれか。 |
| `phoneNumber` | いいえ | 新しいチャネルで使用する電話番号。デフォルトはソース連絡先自身の番号です。 |

```bash
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/link-channel?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "channel": "sms" }'
```

**レスポンス**

```json
{
  "success": true,
  "data": {
    "contact_id": "contact_def456",
    "person_id": "person_xyz789",
    "created": true
  }
}
```

`created` は、ターゲットチャネル用に新しい連絡先が作成されたか、既存の連絡先が見つかってリンクされたかを示します。これを2回呼び出しても安全です。重複を作成するのではなく、同じ `contact_id` を `created: false` とともに返します。

`422` は、現在アカウントがこのリンクを実行できないことを意味します。連絡先がすでにそのチャネルファミリーに存在している、使用する電話番号がない、またはターゲットチャネルに接続された送信者が存在しない場合です。 `409` は、2つの連絡先がすでに別々の人物にリンクされていることを意味します。先に一方のリンクを解除してください。

### 連絡先のリンク済み会話を一覧表示する

`GET /contacts/{contactId}/linked`

この連絡先と同一人物である他の会話を返します。リンクされていない連絡先は `404` ではなく空の配列を返します。「この人物には他のチャネルがない」というのは正常な状態です。

```bash
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/linked?apiKey=YOUR_API_KEY"
```

**レスポンス**

```json
{
  "success": true,
  "data": [
    {
      "contact_id": "contact_def456",
      "channel": "sms",
      "custom_channel": null,
      "first_name": "Jane",
      "last_name": "Smith",
      "phone_number": "+15551234567",
      "last_message": "Sounds good, thanks!",
      "last_message_timestamp": "2026-06-09T10:21:00.000Z",
      "linked_from": {
        "contact_id": "contact_abc123",
        "channel": "whatsapp",
        "linked_at": "2026-06-01T09:00:00.000Z",
        "reason": "continue_on_channel"
      }
    }
  ]
}
```

### 連絡先のリンクを解除する

`DELETE /contacts/{contactId}/link`

この連絡先をその人物から一方的に削除します。その人物にリンクされている他の連絡先はリンクを維持するため、3つのうち1つのリンクを解除してもグループが解散されることはありません。

```bash
curl -X DELETE "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/link?apiKey=YOUR_API_KEY"
```

**レスポンス**

```json
{ "success": true }
```

---

## 連絡先のプロフィール画像を取得する

`POST /contacts/{contactId}/profile-pic`

連絡先のWhatsAppまたはMetaのプロフィール写真をオンデマンドで取得（およびキャッシュ）します。これは[連絡先の取得](#get-a-contact-by-phone-or-email)で`avatarUrl`として返される写真と同じもので、更新された状態のものです。

```bash
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/profile-pic?apiKey=YOUR_API_KEY"
```

**レスポンス**

```json
{
  "success": true,
  "avatar_url": "https://example.com/photo.jpg",
  "cached": false
}
```

`cached: true` は、URLがプロバイダーへの新規ルックアップではなく、最近の取得から得られたものであることを意味します。写真は7日間キャッシュされ、プロバイダーが到達可能な写真がないと報告した連絡先は、24時間「利用不可」としてキャッシュされます。取得する写真がない場合、`avatar_url` は省略され、`message` にその理由が説明されます。

---

## AIによる連絡先の自動タグ付け

アカウントのタグ付けルールを1つ以上の連絡先の全会話履歴に対して実行し、ライブチャット中に実行されるリアルタイムのタグ付けとまったく同じようにタグを適用（または削除）します。ルールやタグごとのクレジットコストも同様です。

### 実行を開始する

`POST /contacts/auto-tag`

| フィールド | 必須 | 説明 |
|---|---|---|
| `scope` | はい | 特定の連絡先にタグを付ける場合は `"contacts"` を、1つのAIエージェントが現在処理しているすべての会話にタグを付ける場合は `"agent"` を指定します。 |
| `contact_ids` | `scope` が `"contacts"` の場合に必須 | 連絡先IDの配列（1〜500個）。 |
| `agent_id` | `scope` が `"agent"` の場合に必須 | タグ付け対象の会話を持つAIエージェント。`scope` が `"contacts"` の場合、これはオプションとなり、実行するエージェントのタグ付けルールを絞り込むために使用されます。 |

```bash
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/auto-tag?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "scope": "contacts", "contact_ids": ["contact_abc123", "contact_def456"] }'
```

**単一**の連絡先はインラインで実行され、結果がすぐに返されます：

```json
{ "success": true, "result": { "tags_applied": 2, "tags_removed": 0 } }
```

**2つ以上**の連絡先（または `scope: "agent"`）はバックグラウンドジョブとして実行され、すぐに `202` が返されます：

```json
{ "success": true, "run_id": "m1x2y3-a1b2c3d4", "total": 214 }
```

### 実行状況をポーリングする

`GET /contacts/auto-tag/run`

アカウントの現在（または最新）の実行状況を返します。これにより、`run_id` を自分で追跡することなく進捗状況をポーリングできます。

```bash
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/auto-tag/run?apiKey=YOUR_API_KEY"
```

**レスポンス**

```json
{
  "success": true,
  "run": {
    "run_id": "m1x2y3-a1b2c3d4",
    "status": "running",
    "total": 214,
    "processed": 58,
    "tagged_contacts": 12,
    "tags_applied": 15,
    "tags_removed": 2,
    "credits_charged": 15
  }
}
```

アカウントで一度も実行が開始されていない場合、`run` は `null` になります。`status` は `"running"` から `"completed"` または `"failed"` へと移行します。

アカウントごとに一度に実行できるバルク実行は1つだけです。別の実行中に2つ目を開始すると、`409` とともに `error_code: "auto_tag_run_in_progress"` が返されます。単一連絡先の実行でクレジットが不足すると、`402` とともに `error_code: "insufficient_credits"` が返されます。一方、バルク実行の場合は途中で停止し、`run` にどこまで完了したかが報告されます。

---

## 連絡先を削除する

`DELETE /contacts/{contactId}`

IDを指定して連絡先を1件、メッセージ履歴とともに完全に削除します。**この操作は取り消せません。** 複数の連絡先を1回の呼び出しで削除するには、以下の [連絡先を削除](#delete-contacts) を使用してください。

**cURL**

```bash
curl -X DELETE "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123?apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123", {
  method: "DELETE",
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
console.log(data.success);
```

**Python**

```python
import requests

res = requests.delete(
    "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
print(res.json()["success"])
```

**レスポンス**

```json
{
  "success": true
}
```

アカウントに存在しない、または別のアカウントに属するコンタクトIDは、`404`を返します。

---

## 連絡先の削除

`DELETE /contacts`

1回の呼び出しで、IDを指定して1つ以上の連絡先を完全に削除します（最大500件まで）。アカウントに存在しないIDはスキップされ、`skipped` にカウントされます。**この操作は元に戻せません。**

| フィールド | 説明 |
|---|---|
| `contactIds` | 削除する連絡先IDの配列（最大500件）。 |

**cURL**

```bash
curl -X DELETE "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "contactIds": ["contactId1", "contactId2"] }'
```

**JavaScript**

```javascript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts", {
  method: "DELETE",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ contactIds: ["contactId1", "contactId2"] }),
});
const data = await res.json();
console.log(`Deleted ${data.deleted}, skipped ${data.skipped}`);
```

**Python**

```python
import requests

res = requests.delete(
    "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"contactIds": ["contactId1", "contactId2"]},
)
data = res.json()
print(f"Deleted {data['deleted']}, skipped {data['skipped']}")
```

**レスポンス**

```json
{
  "success": true,
  "deleted": 2,
  "skipped": 0
}
```

---

## カスタムフィールドの削除

`DELETE /contacts/custom-fields/{fieldKey}`

アカウント内の**すべて**の連絡先から、指定したカスタムフィールドキーを削除します。カスタムフィールドの名前変更や廃止後の整理にご利用ください。キーには英数字、アンダースコア、ハイフンのみを使用できます。更新された連絡先の数が返されます。**この操作は取り消せません。**

**cURL**

```bash
curl -X DELETE "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/custom-fields/webinar_date_nh?apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/custom-fields/webinar_date_nh", {
  method: "DELETE",
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
console.log(`Removed from ${data.updated} contacts`);
```

**Python**

```python
import requests

res = requests.delete(
    "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/custom-fields/webinar_date_nh",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
print(f"Removed from {res.json()['updated']} contacts")
```

**レスポンス**

```json
{
  "success": true,
  "updated": 42
}
```

::: note
**注:** サポートされていない文字を含むフィールドキーは、`400` を返します。
:::


---

## リスト

リストは連絡先をグループ化するものです。リストには**静的**（ユーザーがメンバーを決定する）なものと、**スマート**（ルールに基づいてメンバーが計算され、自動的に最新の状態に保たれるもの。詳細は[リストと連絡先の整理](../get-started/list-and-contact-management.md#smart-lists-auto-updating)を参照）なものがあります。

| フィールド | 説明 |
|---|---|
| `name` | 作成時に必須。最大100文字。 |
| `status` | `live`（デフォルト）または `draft`。小文字。 |
| `contact_ids` | リストに追加する連絡先IDの配列。**静的リストのみ。** |
| `type` | `static`（デフォルト）または `smart`。 |
| `smart_rules` | ルールセット。`type` が `smart` の場合に必須。以下を参照。 |

### リストを作成する

`POST /lists`

```bash
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/lists?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
        "name": "Hot leads (active)",
        "type": "smart",
        "smart_rules": {
          "match": "all",
          "conditions": [
            { "field": "tags", "op": "has_any", "value": ["tagHotLead"] },
            { "field": "last_activity_at", "op": "within_last", "value": { "amount": 90, "unit": "days" } }
          ]
        }
      }'
```

**レスポンス**

```json
{
  "success": true,
  "list_id": "list_abc123",
  "evaluation": { "added": 3, "removed": 0, "total": 3 }
}
```

スマートリストは同じリクエスト内で**インライン**で評価されるため、`evaluation` によって最終的に誰がリストに含まれたかを正確に把握できます。静的リストの場合、`evaluation` は `null` となります。

### リストを更新する

`PUT /lists/{listId}`

変更するフィールドのみを送信してください。`smart_rules` を変更すると、リストが即座に再評価され、同じ `evaluation` オブジェクトが返されます。

```bash
curl -X PUT "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/lists/list_abc123?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "smart_rules": { "match": "any", "conditions": [ { "field": "tags", "op": "has_any", "value": ["tagHotLead", "tagWebinar"] } ] } }'
```

リストの種類は以下のように切り替えることができます：

- **静的 → スマート**: `{ "type": "smart", "smart_rules": { … } }` を送信します。その場でルールが適用されます。
- **スマート → 静的**: `{ "type": "static" }` を送信します。ルールは破棄され、その時点でリストに存在するメンバーはそのまま維持されます。

### `smart_rules` の形式

```json
{
  "match": "all",
  "conditions": [
    { "field": "tags", "op": "has_any", "value": ["tagHotLead"] },
    { "field": "channel", "op": "is_any", "value": ["whatsapp", "sms"] },
    { "field": "last_incoming_message_at", "op": "not_within_last", "value": { "amount": 7, "unit": "days" } },
    { "field": "created_at", "op": "after", "value": "2026-01-01" },
    { "field": "is_bot_active", "op": "is", "value": true },
    { "field": "email", "op": "is_set" },
    { "field": "custom_field", "key": "Plan", "op": "eq", "value": "pro" }
  ]
}
```

- `match` — `all`（すべての条件が真である必要がある）または `any`（少なくとも1つが真である必要がある）。
- `conditions` — 1〜20個の条件。各条件は最大100個の値、文字列は最大200文字まで。

| `field` | `op` | `value` |
|---|---|---|
| `tags` | `has_any`, `has_all`, `has_none` | タグIDの配列 |
| `lists` | `in_any`, `not_in_any` | リストIDの配列 (**静的リストのみ** — スマートリストからスマートリストを作成することはできません) |
| `channel` | `is_any`, `is_none` | チャネルの配列 |
| `status` | `is_any`, `is_none` | コンタクトステータスの配列 |
| `created_at`, `last_activity_at`, `last_incoming_message_at`, `last_outgoing_message_at`, `first_ai_interaction_at`, `last_ai_interaction_at` | `within_last`, `not_within_last` | `{ "amount": 1–3650, "unit": "hours" \| "days" }` |
| 同一の日付フィールド | `before`, `after` | ISO日付 (`"2026-01-01"`、日単位で比較) または完全なISO日時 (`"2026-01-01T14:30:00Z"`、正確な時刻で比較) |
| 同一の日付フィールド | `is_set`, `not_set` | — |
| `has_interacted_with_ai` | `is` | `true` / `false` — `true` AIが少なくとも1回（これまでに）メッセージを送信したコンタクトに一致します |
| `is_bot_active`, `do_not_disturb`, `is_private`, `has_ever_responded` | `is` | `true` / `false` |
| `email`, `phone_number`, `first_name`, `last_name` | `is_set`, `not_set`, `contains`, `not_contains` | `contains` フォーム用の文字列 |
| `current_campaign_id`, `assigned_agent` | `is_any`, `is_none`, `is_set`, `not_set` | `is_any` / `is_none` フォーム用のID配列 |
| `custom_field` (および `key`) | `eq`, `neq`, `contains`, `not_contains`, `is_set`, `not_set` | 値フォーム用の文字列 |

`not_within_last` は、日付が設定されていない連絡先（「N日以上前、**または未設定**」）にも一致し、テキスト比較では大文字と小文字が区別されません。

**AIエンゲージメント。** `has_interacted_with_ai` はライフタイムフラグです。AIが少なくとも1通のメッセージを送信したすべてのコンタクトには `true` が、それ以外のすべてのコンタクト（チームのみが対応したコンタクトを含む）には `false` が付与されます。これはAIがコンタクトに最初のメッセージを送信した時点でスタンプされ、消去されることはありません。そのため、コンタクトのAI返信をオフにしたり、別のキャンペーンに移動したりしてもリセットされません。課金に関する一般的な質問である「今月AIが対応したコンタクト」のような*期間*については、代わりに `last_ai_interaction_at` の範囲を使用してください：

```json
{ "field": "last_ai_interaction_at", "op": "within_last", "value": { "amount": 30, "unit": "days" } }
```

`is_bot_active`（AIに返信の*許可*があるかどうか、実際に返信したかどうかではない）や `has_ever_responded`（*コンタクト*が誰かに対して返信したかどうか）と混同しないでください。これら2つのスタンプは、すべてのコンタクトに対して `first_ai_interaction_at` / `last_ai_interaction_at` として返され、ルールセット全体は `GET /contacts?rules=` でも機能するため、リストを作成せずに一致数をカウントできます。

### ルールセットをプレビューする

`POST /lists/preview`

ルールセットが一致する連絡先をカウントおよびサンプリングします。何も作成したり変更したりすることはありません。ルールを保存する前に、ルールが正しいか確認するために使用してください。

```bash
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/lists/preview?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "smart_rules": { "match": "all", "conditions": [ { "field": "tags", "op": "has_any", "value": ["tagHotLead"] } ] } }'
```

**レスポンス**

```json
{
  "success": true,
  "count": 3,
  "sample": [
    {
      "id": "contact_abc123",
      "first_name": "Sofia",
      "last_name": "Martinez",
      "phone_number": "+31600000000",
      "email": "sofia@example.com",
      "channel": "whatsapp"
    }
  ]
}
```

`sample` には最大10件の連絡先が保持され、最近アクティブになった順に表示されます。

### スマートリストを今すぐ再実行する

`POST /lists/{listId}/evaluate`

強制的に即時再評価を行います（ダッシュボードの **[今すぐ更新]** と同じ動作です）。スマートリストは、連絡先が変更された際や、時間ベースのルールの場合は15分ごとに自動的に更新されるため、この操作が必要になるのは、*今すぐ*結果を確認したい場合のみです。

**レスポンス**

```json
{
  "success": true,
  "list_id": "list_abc123",
  "evaluation": { "added": 2, "removed": 1, "total": 4 }
}
```

`evaluation.skipped: true` は、同じリストの別の評価がすでに実行中であり、この呼び出しでは何も行われなかったことを意味します。

### スマートリストは手動で選択されたメンバーを受け付けません

ターゲットリストがスマートリストである場合、メンバーシップエンドポイントは `"This is a smart list — its members are computed from its rules. Edit the rules instead."` を伴う **`409`** を返します。これには `POST /contacts/lists`、`DELETE /contacts/lists`、`POST /contacts/lists/batch`、`contact_ids` の `POST /lists` および `PUT /lists/{listId}`、そしてCSVインポートのターゲットとしてスマートリストを選択することが含まれます。代わりにルールを変更してください。

**静的**リストに対して `POST /lists/{listId}/evaluate` を呼び出すことも `409` となります。実行すべきルールが存在しないためです。

---

## 連絡先APIエラー

連絡先エンドポイントは、標準のエラーエンベロープを返します。

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

一部のエンドポイントには `error_code` も含まれており、通常はHTTPステータスと一致します。唯一の例外は以下の連絡先の重複ケースで、HTTPステータスは `200` ですが、`error_code` のみが `409` を保持します。連絡先エンドポイント固有のコードは以下の通りです：

| コード | コンタクトエンドポイントで発生する場合 |
|---|---|
| `400` | 不正なリクエスト — フィールドの欠落/無効、空のボディ、不正なカーソル、またはバッチ内で500個を超えるID。 |
| `402` | 1件のコンタクトに対するAIタグ付け実行を完了するためのクレジットが不足しています (`error_code: "insufficient_credits"`)。 |
| `404` | コンタクト、リスト、またはタグがアカウントで見つかりませんでした。 |
| `409` | その電話番号を持つコンタクトが既に存在します（作成時）。HTTPステータス`200`のボディ内で`error_code`として返されるため、ここで`error_code`に基づいて分岐してください。また、一括自動タグ付けが既に進行中の場合（`error_code: "auto_tag_run_in_progress"`）、またはコンタクトを別のチャネルにリンクすることで、既に異なる2人にリンクされている2つのコンタクトが結合されてしまう場合にも返されます。 |
| `422` | コンタクトが現在メッセージを受信できません（おやすみモード、非公開、またはサポートされていないチャネル）。チャネルリンクエンドポイントでは、電話番号がない、サポートされていないチャネルの組み合わせ、またはターゲットチャネルに接続された送信者がいない場合も含まれます。 |

連絡先エンドポイントでの `403` は、プランのアクセス権限ではなく、連絡先制限やリストの権限に関する問題を示している可能性もあります。すべてのエンドポイントが返す共通コード（`401`、`403`（プランにAPIアクセスが含まれていない）、`429`（レート制限）、`500`）については、再試行のガイダンスとともに [エラーとページネーション](errors-and-pagination.md) に記載されています。

---

## 次のステップ

- [メッセージAPI](messages.md) — チャネルIDによるメッセージの送信と会話の管理。
- [APIリファレンス](reference.md) — タグやリストを含むエンドポイントの完全なリスト。
- [APIアクセス](../integrations/api-access.md) — 認証、レート制限、エラー処理。
