> 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 通过以下 2 种方式之一识别发送对象。请不要同时传递两个值，只选择一个。

| 区分        | 发放方式                                                                                                    |
| --------- | ------------------------------------------------------------------------------------------------------- |
| `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) 是否为有效值， [请使用识别键验证 API。](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 是从合作伙伴服务器调用 Apps 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`

**请求头**

用于识别发送对象的头请使用以下 2 种之一。不要同时传递两个头。

| 名称                | 类型     | 必填  | 说明                                                                                                                                                                                                                                       |
| ----------------- | ------ | --- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `x-toss-user-key` | string | 二选一 | [通过 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 | 二选一 | [用户识别键发放](https://developers-apps-in-toss.toss.im/documentation/common/authentication/hash-key)通过 `hash` 是其值。                                                                                                                            |

**请求体**

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

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

**成功响应**

| 名称                  | 类型      | 说明                   |
| ------------------- | ------- | -------------------- |
| msgCount            | integer | 是总发送消息数。             |
| sentPushCount       | integer | 是通过推送（Push）发送的消息数。   |
| sentInboxCount      | integer | 是通过收件箱（Inbox）发送的消息数。 |
| sentSmsCount        | integer | 是通过 SMS 发送的消息数。      |
| sentAlimtalkCount   | integer | 是通过通知消息发送的消息数。       |
| sentFriendtalkCount | integer | 是通过好友消息发送的消息数。       |
| detail              | object  | 是按渠道列出的发送成功消息详细列表。   |
| fail                | object  | 是按渠道列出的发送失败消息详细列表。   |

`detail` / `fail` 下级公共字段：

| 名称             | 类型    | 说明                   |
| -------------- | ----- | -------------------- |
| sentPush       | array | 推送（Push）渠道的发送结果列表。   |
| sentInbox      | array | 收件箱（Inbox）渠道的发送结果列表。 |
| sentSms        | array | SMS 渠道的发送结果列表。       |
| sentAlimtalk   | array | 通知消息渠道的发送结果列表。       |
| sentFriendtalk | array | 好友消息渠道的发送结果列表。       |

各渠道数组项字段：

| 名称                | 类型     | 说明               |
| ----------------- | ------ | ---------------- |
| 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 %}

**请求头**

用于识别发送对象的头请使用以下 2 种之一。不要同时传递两个头。

| 名称                | 类型     | 必填  | 说明                                                                                                                                                                                                                                       |
| ----------------- | ------ | --- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `x-toss-user-key` | string | 二选一 | [通过 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 | 二选一 | [用户识别键发放](https://developers-apps-in-toss.toss.im/documentation/common/authentication/hash-key)通过 `hash` 是其值。                                                                                                                            |

**请求体**

| 名称              | 类型     | 必填 | 说明                           |
| --------------- | ------ | -- | ---------------------------- |
| templateSetCode | string | Y  | 是要使用的消息模板代码。请输入在控制台注册的模板代码。  |
| context         | object | 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     | array  | 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 | object           | 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

  返回 App Bridge 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` | 用户拒绝同意的情况。                    |

**错误代码**

错误代码可通过 `onError`接收，或者在包裹调用的 `try/catch`中捕获的错误的 `error.code` 是其值。

| 值                               | 说明                                                                                                                       |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `UNSUPPORTED_APP_VERSION`       | 如果正在运行的 Toss 应用版本不支持此功能，会在调用时立即发生。 `Notification.requestAgreement.isSupported()`事先确认，若发生则 `error.message`（更新提示文案）请展示给用户。 |
| `NOTIFICATION_AGREEMENT_FAILED` | 功能性推送模板代码错误时发生。\n模板代码必须 `{appName}-` 以...开头。                                                                             |
| `TERMS_DISAGREED_MEMBER`        | <p>这是用户拒绝同意的情况。<br>需要在 设置 > 条款及个人信息处理同意 > 用户优化产品同意 设置中进行配置。</p>                                                          |

**注意事项**

* `onEvent` 或 `onError` 请务必调用回调中返回的清理函数。
* 在同一组件中重新调用函数之前，请先执行上一次的清理。否则，之前的事件监听器可能会重复残留。
* 请参考智能发送介绍文档，了解通知同意文案的注册方法和功能性消息发送流程。
* 若要在用户同意后发送消息，请在合作伙伴服务器上 [发送消息](#_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.
