> For the complete documentation index, see [llms.txt](https://developers-apps-in-toss.toss.im/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developers-apps-in-toss.toss.im/documentation/api-and-sdk-zh/common/growth/smart-message.md).

# 智能发送

如支付完成、配送通知等 **服务使用所需的功能性消息**用于在合作伙伴服务器上直接发送。可有效实现提升留存、促进服务使用等目的。

控制台 [智能发送设置指南](https://developers-apps-in-toss.toss.im/guide/marketing/smart-message)。

***

### 预先准备事项

#### 需要进行控制台设置

在调用 API 之前，需要先完成以下步骤。

1. 在控制台中创建功能性活动并获取模板代码(`templateSetCode`）。
2. 如果是需要通知同意文的消息，请在创建活动前先创建通知同意文，并将其关联到功能性活动。
3. 需要通过文案审核批准。批准前甚至不能发送测试消息。

设置方法请参考智能发送介绍文档。

<details>

<summary>确认是否需要通知同意文</summary>

发送消息前 **是否需要通知同意文**取决于消息性质。

**可无需同意文直接发送的情况**

服务使用必需的信息可在无需用户同意的情况下发送。

* 支付完成、发货开始、配送完成、退款完成
* 信息变更通知、条款变更通知

**需要同意文的情况**

当用户选择接收特定通知时，必须 [请求通知同意文](#_4-알림-동의문-요청하기-requestnotificationagreement)先通过其获得同意。

* 申请补货通知
* 申请活动开始通知
* 申请价格变动通知
* 申请预约开启通知

</details>

#### 识别发送对象用户

智能发送 API 会通过以下两种方式之一识别发送对象。请不要同时传递两个值，只选择一个。

| 区分        | 获取方式                                                                                                       |
| --------- | ---------------------------------------------------------------------------------------------------------- |
| `userKey` | [通过 Toss 登录](https://developers-apps-in-toss.toss.im/documentation/common/authentication/toss-login)作为值接收。 |
| `anonKey` | [用户识别键发放](https://developers-apps-in-toss.toss.im/documentation/common/authentication/hash-key)作为值接收。      |

请根据目的选择。

* 如果已经接入 Toss 登录，或者想与姓名·邮箱等会员信息绑定统一管理， `userKey`。
* 如果不需要登录联动，只想轻量识别用户， `anonKey`。

传递位置因 API 而异。

* 测试消息、消息：放在请求头中 `x-toss-user-key` 或 `x-anon-key`传入。
* 批量消息：请求体 `contextList` 在项中 `userKey` 或 `anonKey`传入。

`x-anon-key`如果想提前确认 (hash) 是否为有效值， [验证识别键](https://developers-apps-in-toss.toss.im/documentation/common/authentication/hash-key#식별키-검증하기) API。

***

### 基本信息

| 项目           | 值                                  |
| ------------ | ---------------------------------- |
| Base URL     | `https://apps-in-toss-api.toss.im` |
| 服务器认证        | mTLS（客户端证书）                        |
| Content-Type | `application/json`                 |

{% hint style="info" %}
**服务器间通信需要 mTLS 证书**

智能发送 API 是合作伙伴服务器向 App in Toss 服务器发起调用的服务器间通信。为确保安全，请先在服务器上设置 mTLS 证书后再调用。证书签发方法请参考 [mTLS 证书发放方法](https://developers-apps-in-toss.toss.im/documentation/integration/getting-started)。
{% endhint %}

***

### 1. 发送测试消息

在控制台创建功能性活动并获得文案审核批准后，用于在实际发布前确认 bundle 是否正常工作。

* Content-Type: `application/json`
* Method: `POST`
* URL: `/api-partner/v1/apps-in-toss/messenger/send-test-message`

**请求头**

用于识别发送对象的请求头请从以下两种中选一种。不要同时传递两个请求头。

| 名称                | 类型     | 必填   | 说明                                                                                                                                                                                                                                   |
| ----------------- | ------ | ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `x-toss-user-key` | string | 任选 1 | [通过 Toss 登录](https://developers-apps-in-toss.toss.im/documentation/common/authentication/toss-login)中获得的 `userKey`。 [可通过](https://developers-apps-in-toss.toss.im/documentation/common/authentication/toss-login#_4-사용자-정보-받기)获取用户信息 |
| `x-anon-key`      | string | 任选 1 | [用户识别键发放](https://developers-apps-in-toss.toss.im/documentation/common/authentication/hash-key)中获得的 `hash` 值。                                                                                                                        |

**请求体**

| 名称              | 类型     | 必填 | 说明                                                          |
| --------------- | ------ | -- | ----------------------------------------------------------- |
| templateSetCode | string | Y  | 这是要使用的消息模板代码。请输入在控制台注册的模板代码。                                |
| deploymentId    | string | Y  | 这是测试所用的 bundle 标识值。为 UUID 格式，可在控制台 → App 发布中上传的 bundle 中确认。 |
| context         | 对象     | Y  | 这是模板变量值。请填入用户名、验证码等要放入模板的值。                                 |

```json
{
  "templateSetCode": "ALERT_OTP_TEMPLATE",
  "deploymentId": "019abfe8-fd68-7021-9cdc-30d6053cc009",
  "context": {
    "userName": "洪吉童",
    "otp": "123456"
  }
}
```

**成功响应**

| 名称                  | 类型 | 说明                      |
| ------------------- | -- | ----------------------- |
| msgCount            | 整数 | 这是总发送消息数。               |
| sentPushCount       | 整数 | 这是通过 Push 发送的消息数。       |
| sentInboxCount      | 整数 | 这是通过 Inbox 发送的消息数。      |
| sentSmsCount        | 整数 | 这是通过 SMS 发送的消息数。        |
| sentAlimtalkCount   | 整数 | 这是通过 AlimTalk 发送的消息数。   |
| sentFriendtalkCount | 整数 | 这是通过 FriendTalk 发送的消息数。 |
| 详情                  | 对象 | 这是发送成功消息的各渠道详细列表。       |
| 失败                  | 对象 | 这是发送失败消息的各渠道详细列表。       |

`详情` / `失败` 子级公共字段：

| 名称             | 类型 | 说明                       |
| -------------- | -- | ------------------------ |
| sentPush       | 数组 | 这是 Push 渠道的发送结果列表。       |
| sentInbox      | 数组 | 这是 Inbox 渠道的发送结果列表。      |
| sentSms        | 数组 | 这是 SMS 渠道的发送结果列表。        |
| sentAlimtalk   | 数组 | 这是 AlimTalk 渠道的发送结果列表。   |
| sentFriendtalk | 数组 | 这是 FriendTalk 渠道的发送结果列表。 |

各渠道数组的项字段：

| 名称                | 类型     | 说明                |
| ----------------- | ------ | ----------------- |
| contentId         | string | 这是已发送消息的唯一 ID。    |
| reachedFailReason | string | 当消息送达失败时，会包含失败原因。 |

```json
{
  "resultType": "SUCCESS",
  "success": {
    "msgCount": 1,
    "sentPushCount": 1,
    "sentInboxCount": 0,
    "sentSmsCount": 0,
    "sentAlimtalkCount": 0,
    "sentFriendtalkCount": 0,
    "detail": {
      "sentPush": [{ "contentId": "MSG_ABC123" }],
      "sentInbox": [],
      "sentSms": [],
      "sentAlimtalk": [],
      "sentFriendtalk": []
    },
    "fail": {
      "sentPush": [],
      "sentInbox": [],
      "sentSms": [],
      "sentAlimtalk": [],
      "sentFriendtalk": []
    }
  }
}
```

**失败响应**

```json
{
  "resultType": "FAIL",
  "error": {
    "errorCode": "INVALID_PARAMETER",
    "reason": "请求失败了。"
  }
}
```

| HTTP 状态码 | 说明           |
| -------- | ------------ |
| 400      | 请求有误或缺少必要信息。 |
| 401      | 未认证的用户。      |
| 403      | 没有发送消息的权限。   |

***

### 2. 发送消息

向 1 位特定用户发送功能性消息。文案审核通过后，可在合作伙伴服务器上于所需时点直接调用。

* Content-Type: `application/json`
* Method: `POST`
* URL: `/api-partner/v1/apps-in-toss/messenger/send-message`

{% hint style="info" %}
**调用限制**

按用户识别键每分钟最多可调用 10 次。超过时会返回错误。
{% endhint %}

**请求头**

用于识别发送对象的请求头请从以下两种中选一种。不要同时传递两个请求头。

| 名称                | 类型     | 必填   | 说明                                                                                                                                                                                                                                   |
| ----------------- | ------ | ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `x-toss-user-key` | string | 任选 1 | [通过 Toss 登录](https://developers-apps-in-toss.toss.im/documentation/common/authentication/toss-login)中获得的 `userKey`。 [可通过](https://developers-apps-in-toss.toss.im/documentation/common/authentication/toss-login#_4-사용자-정보-받기)获取用户信息 |
| `x-anon-key`      | string | 任选 1 | [用户识别键发放](https://developers-apps-in-toss.toss.im/documentation/common/authentication/hash-key)中获得的 `hash` 值。                                                                                                                        |

**请求体**

| 名称              | 类型     | 必填 | 说明                           |
| --------------- | ------ | -- | ---------------------------- |
| templateSetCode | string | Y  | 这是要使用的消息模板代码。请输入在控制台注册的模板代码。 |
| context         | 对象     | Y  | 这是模板变量值。请填入用户名、验证码等要放入模板的值。  |

```json
{
  "templateSetCode": "ALERT_OTP_TEMPLATE",
  "context": {
    "userName": "洪吉童",
    "otp": "123456"
  }
}
```

```bash
curl --location 'https://apps-in-toss-api.toss.im/api-partner/v1/apps-in-toss/messenger/send-message' \
--header 'Content-Type: application/json' \\
--header 'x-toss-user-key: {{userKey}}' \
--data '{
  "templateSetCode": "ALERT_OTP_TEMPLATE",
  "context": {
    "userName": "洪吉童",
    "otp": "123456"
  }
}'
```

`anonKey`在发送到 `x-anon-key` 时，

```bash
curl --location 'https://apps-in-toss-api.toss.im/api-partner/v1/apps-in-toss/messenger/send-message' \
--header 'Content-Type: application/json' \\
--header 'x-anon-key: {{anonKey}}' \
--data '{
  "templateSetCode": "ALERT_OTP_TEMPLATE",
  "context": {
    "userName": "洪吉童",
    "otp": "123456"
  }
}'
```

**成功响应**

响应结构与 [发送测试消息](#_1-테스트-메시지-발송하기)相同。

```json
{
  "resultType": "SUCCESS",
  "success": {
    "msgCount": 1,
    "sentPushCount": 1,
    "sentInboxCount": 0,
    "sentSmsCount": 0,
    "sentAlimtalkCount": 0,
    "sentFriendtalkCount": 0,
    "detail": {
      "sentPush": [{ "contentId": "MSG_ABC123" }],
      "sentInbox": [],
      "sentSms": [],
      "sentAlimtalk": [],
      "sentFriendtalk": []
    },
    "fail": {
      "sentPush": [],
      "sentInbox": [],
      "sentSms": [],
      "sentAlimtalk": [],
      "sentFriendtalk": []
    }
  }
}
```

**失败响应**

```json
{
  "resultType": "FAIL",
  "error": {
    "errorCode": "INVALID_PARAMETER",
    "reason": "请求失败了。"
  }
}
```

| HTTP 状态码 | 说明           |
| -------- | ------------ |
| 400      | 请求有误或缺少必要信息。 |
| 401      | 未认证的用户。      |
| 403      | 没有发送消息的权限。   |

***

### 3. 发送批量消息

使用相同的功能性消息模板一次性发送给多位用户。用于 50 条以上的发送，单次请求最多可发送 2,500 条。

* Content-Type: `application/json`
* Method: `POST`
* URL: `/api-partner/v1/apps-in-toss/messenger/send-bulk-message`

**请求体**

| 名称              | 类型     | 必填 | 说明                             |
| --------------- | ------ | -- | ------------------------------ |
| templateSetCode | string | Y  | 这是要使用的消息模板代码。请输入在控制台注册的模板代码。   |
| contextList     | 数组     | Y  | 这是接收消息的用户列表。最少 1 条，最多 2,500 条。 |

`contextList` 项字段：

| 名称      | 类型               | 必填    | 说明                                                                                                                                                                                                                                                         |
| ------- | ---------------- | ----- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| userKey | string \| number | 条件性 Y | [通过 Toss 登录](https://developers-apps-in-toss.toss.im/documentation/common/authentication/toss-login)中获得的 `userKey`。 [可通过](https://developers-apps-in-toss.toss.im/documentation/common/authentication/toss-login#_4-사용자-정보-받기)获取用户信息 `x-anon-key`不能与其一起使用。 |
| anonKey | string           | 条件性 Y | 通过发放用户识别键获得的 `hash` 值。 `userKey`不能与其一起使用。                                                                                                                                                                                                                  |
| context | 对象               | Y     | 这是模板变量值。请填入用户名、验证码等要放入模板的值。                                                                                                                                                                                                                                |

每个 `contextList` 项中 `userKey`和 `anonKey` 必须传递其中之一。

```json
{
  "templateSetCode": "ALERT_OTP_TEMPLATE",
  "contextList": [
    {
      "userKey": {{userKey1}},
      "context": {
        "userName": "洪吉童",
        "otp": "123456"
      }
    },
    {
      "userKey": {{userKey2}},
      "context": {
        "userName": "金哲洙",
        "otp": "654321"
      }
    }
  ]
}
```

`anonKey`在发送到 `anonKey`时，在每个项中

```json
{
  "templateSetCode": "ALERT_OTP_TEMPLATE",
  "contextList": [
    {
      "anonKey": "{{anonKey1}}",
      "context": {
        "userName": "洪吉童",
        "otp": "123456"
      }
    },
    {
      "anonKey": "{{anonKey2}}",
      "context": {
        "userName": "金哲洙",
        "otp": "654321"
      }
    }
  ]
}
```

```bash
curl --location 'https://apps-in-toss-api.toss.im/api-partner/v1/apps-in-toss/messenger/send-bulk-message' \
--header 'Content-Type: application/json' \\
--data '{
  "templateSetCode": "ALERT_OTP_TEMPLATE",
  "contextList": [
    {
      "userKey": 12345678,
      "context": { "userName": "洪吉童", "otp": "123456" }
    },
    {
      "userKey": 87654321,
      "context": { "userName": "金哲洙", "otp": "654321" }
    }
  ]
}'
```

**成功响应**

响应结构与 [发送测试消息](#_1-테스트-메시지-발송하기)相同。

```json
{
  "resultType": "SUCCESS",
  "success": {
    "msgCount": 2,
    "sentPushCount": 2,
    "sentInboxCount": 0,
    "sentSmsCount": 0,
    "sentAlimtalkCount": 0,
    "sentFriendtalkCount": 0,
    "detail": {
      "sentPush": [{ "contentId": "MSG_ABC123" }, { "contentId": "MSG_DEF456" }],
      "sentInbox": [],
      "sentSms": [],
      "sentAlimtalk": [],
      "sentFriendtalk": []
    },
    "fail": {
      "sentPush": [],
      "sentInbox": [],
      "sentSms": [],
      "sentAlimtalk": [],
      "sentFriendtalk": []
    }
  }
}
```

***

### 4. 请求通知同意文（`requestNotificationAgreement`)

`requestNotificationAgreement`是智能发送的 **功能性消息**在发送前，向用户请求接收通知同意 UI 的函数。

在用户选择接收特定通知的情况下，必须先获得同意。同意结果会 `onEvent` 通过回调传递，并可根据结果决定是否发送通知。

{% hint style="info" %}
**需要注册通知同意文**

开发前需先完成以下步骤。

1. 创建通知同意文。
2. 创建功能性活动并关联已注册的通知同意文。
3. 获得文案审核批准后，即可使用已注册功能性活动的模板代码。
4. 这个模板代码 `templateCode`传入。

设置方法请参考智能发送介绍文档。
{% endhint %}

**签名**

```typescript
function requestNotificationAgreement(params: RequestNotificationAgreementOptions): () => void;
```

**参数**

* params必填

  这是用于请求通知同意的参数。详细类型见下方 `RequestNotificationAgreementOptions`。

**返回值**

* () => void

  返回 appbridge cleanup 函数。获得同意结果后必须调用该函数释放资源。

**示例**

{% tabs %}
{% tab title="React" %}

```tsx
import { requestNotificationAgreement } from '@apps-in-toss/web-framework';

function NotificationAgreementButton() {
  const handleRequestAgreement = () => {
    const cleanup = requestNotificationAgreement({
      options: {
        templateCode: 'your-template-code',
      },
      onEvent: ({ type }) => {
        if (type === 'newAgreement') {
          console.log('已同意接收通知。');
        } else if (type === 'alreadyAgreed') {
          console.log('已处于同意状态。');
        } else if (type === 'agreementRejected') {
          console.log('已拒绝接收通知。');
        }
        cleanup();
      },
      onError: (error) => {
        console.error('请求通知同意失败：', error);
        cleanup();
      },
    });
  };

  return <button onClick={handleRequestAgreement}>接收通知</button>;
}
```

{% endtab %}

{% tab title="React Native" %}

```tsx
import { Button } from 'react-native';
import { requestNotificationAgreement } from '@apps-in-toss/framework';

function NotificationAgreementButton() {
  const handleRequestAgreement = () => {
    const cleanup = requestNotificationAgreement({
      options: {
        templateCode: 'your-template-code',
      },
      onEvent: ({ type }) => {
        if (type === 'newAgreement') {
          console.log('已同意接收通知。');
        } else if (type === 'alreadyAgreed') {
          console.log('已处于同意状态。');
        } else if (type === 'agreementRejected') {
          console.log('已拒绝接收通知。');
        }
        cleanup();
      },
      onError: (error) => {
        console.error('请求通知同意失败：', error);
        cleanup();
      },
    });
  };

  return <Button title="接收通知" onPress={handleRequestAgreement} />;
}
```

{% endtab %}
{% endtabs %}

**`RequestNotificationAgreementOptions`**

`requestNotificationAgreement` 这是传递给函数的参数类型。

```typescript
interface RequestNotificationAgreementOptions {
  options: {
    templateCode: string;
  };
  onEvent: (result: { type: NotificationAgreementResult }) => void;
  onError: (error: unknown) => void | Promise<void>;
}
```

* options必填

  这是用于请求通知同意的选项对象。

  * options.templateCode必填

    这是已关联通知同意文的功能性活动模板代码。请输入在控制台注册的模板代码。
* onEvent必填

  同意结果确定时执行的回调。 `NotificationAgreementResult` 类型的 `type` 会传入该值。
* onError必填

  当发生意外错误时执行的回调。错误对象的类型为 `unknown`。

**`NotificationAgreementResult`**

`onEvent` 这是通过回调传递的同意结果类型。

```typescript
type NotificationAgreementResult = 'newAgreement' | 'alreadyAgreed' | 'agreementRejected';
```

| 值                   | 说明                          |
| ------------------- | --------------------------- |
| `newAgreement`      | 用户新完成同意的情况。                 |
| `alreadyAgreed`     | 已处于同意状态，并在未追加同意的情况下返回结果的情况。 |
| `agreementRejected` | 用户拒绝同意的情况。                  |

**参考事项**

* `onEvent` 或 `onError` 请务必调用回调中返回的 cleanup 函数。
* 在同一组件中再次调用该函数前，请先执行之前的 cleanup，否则之前的事件监听器可能会重复残留。
* 有关通知同意文的注册方法和功能性消息发送流程，请参考智能发送介绍文档。
* 若要在用户同意后发送消息，请在合作伙伴服务器上 [调用“发送消息”](#_2-메시지-발송하기)请调用。
* 用户可在 Toss App → 全部标签 → 设置按钮 → 通知 → 各服务通知中直接控制是否接收通知。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://developers-apps-in-toss.toss.im/documentation/api-and-sdk-zh/common/growth/smart-message.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
