> **Sendgo API 的机器可读契约**

如果你使用的语言没有官方 Sendgo SDK —— 或者你想自己生成客户端 —— 请从 OpenAPI 3.0.3 规范开始。

- 始终最新：[`https://sendgo.io/openapi.yaml`](https://sendgo.io/openapi.yaml)
- 源码：[send-go/openapi](https://github.com/send-go/openapi)
- 服务器：`https://api.sendgo.io/api`

---

## 端点

| 渠道 | 方法 | 路径 |
|------|------|------|
| 签发令牌 | `POST` | `/{version}/token` |
| Kakao 通知消息 | `POST` | `/{version}/notices/send` |
| Kakao 好友消息 —— **已停用** | `POST` | `/{version}/friends/send` |
| 品牌消息 —— 发送 | `POST` | `/{version}/brand-messages/send` |
| 品牌消息 —— 活动列表 | `GET` | `/{version}/brand-messages` |
| 品牌消息 —— 活动详情 | `GET` | `/{version}/brand-messages/{campaign_id}` |
| SMS / LMS / MMS | `POST` | `/{version}/messages/send` |
| 短链接 —— 创建 | `POST` | `/{version}/short-urls` |
| 短链接 —— 列表 | `GET` | `/{version}/short-urls` |
| 短链接 —— 详情 | `GET` | `/{version}/short-urls/{code}` |
| 短链接 —— 反应统计 | `GET` | `/{version}/short-urls/{code}/stats` |
| 短链接 —— 停止重定向 | `DELETE` | `/{version}/short-urls/{code}` |

`{version}` 为 `v1` 或 `v2`。**品牌消息仅 v2 支持。**

**按 Kakao 政策，好友消息已于 2025-12-31 停止服务。** 自 2026-01-01 起，发往 `/{version}/friends/send` 的请求会自动以**品牌消息（自由形式）**发出 —— 调用仍会成功，但实际发出的是品牌消息。该端点不会被移除：自由文本类型（`FT`/`FI`/`FW`）发往单个接收者的路径目前仍只有这一条，而 `/{version}/brand-messages/send` 对该组合会返回 `NOT_A_BRAND_MESSAGE`。基于模板的富文本类型（`FL`/`FC`/`FM`/`FP`/`FA`）、非好友定向（`N`/`I`）与群发（`F`）请使用品牌消息。

---

## 认证

分两步。

**1. 签发令牌** —— 对 `accessKey:secretKey` 使用 Basic 认证：

```bash
curl -X POST https://api.sendgo.io/api/v2/token \
  -H "Authorization: Basic $(printf '%s:%s' "$ACCESS_KEY" "$SECRET_KEY" | base64)"
```

**2. 调用 API** —— 用该令牌作为 Bearer 凭据：

| 版本 | 请求头 |
|------|--------|
| v1 | `Authorization: Bearer base64(token)` |
| v2 | `Authorization: Bearer token` |

v1 要求令牌**再做一次 Base64 编码**；v2 原样发送。把这两者搞反，是刚签发的令牌却收到 `401` 的常见原因。

令牌有效期为 **24 小时**。请从签发响应中读取 `expiresAt`（v2）/ `expires_at`（v1），缓存令牌，只在即将过期时才重新签发 —— 每次请求都签发一次纯属浪费延迟。

请求还会与该应用配置的 IP 白名单做校验，因此仅有令牌、来自未登记地址仍然不够（`IP_NOT_ALLOWED`）。

---

## 发送通知消息

```bash
curl -X POST https://api.sendgo.io/api/v2/notices/send \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "templateCode": "ORDER_CONFIRM_001",
    "contacts": [
      { "contact": "01012345678", "var1": "ORD-001" }
    ]
  }'
```

---

## 发送品牌消息

```bash
# 定向发送 —— targeting 为 M/N/I 时必须提供 contacts
curl -X POST https://api.sendgo.io/api/v2/brand-messages/send \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "targeting": "M",
    "messageType": "FL",
    "friendTemplateUuid": "9cd5460b-6458-4edc-9b11-c26d3013c340",
    "contacts": [{ "contact": "01012345678", "var1": "29,000元" }]
  }'

# 群发 —— 已同意接收的全部频道好友，不传接收者列表
curl -X POST https://api.sendgo.io/api/v2/brand-messages/send \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "targeting": "F",
    "messageType": "FW",
    "friendTemplateUuid": "9cd5460b-6458-4edc-9b11-c26d3013c340"
  }'
```

群发在上游为异步处理，因此发送响应只表示已被接受。请轮询活动详情查看进度：

```bash
curl "https://api.sendgo.io/api/v2/brand-messages/$CAMPAIGN_ID" \
  -H "Authorization: Bearer $TOKEN"
```

`GET /{version}/brand-messages` 只返回品牌消息活动（`BRAND_GROUP`、`BRAND_BASIC`）—— 好友消息活动请使用 `/{version}/friends`。

`targeting` 的取值含义：

| 值 | 含义 |
|----|------|
| `M` | 频道好友中的指定接收者 |
| `N` | 非频道好友的接收者 |
| `I` | 按标识符指定的接收者 |
| `F` | 已同意接收的全部频道好友（群发） |

---

## 响应结构

成功：

```json
{
  "traceId": "01J2X8N4YV5T3Q9M0K7B6C5D4E",
  "message": "Success",
  "data": { }
}
```

失败：

```json
{
  "traceId": "01J2X8N4YV5T3Q9M0K7B6C5D4E",
  "code": "INVALID_TEMPLATE_CODE",
  "message": "The template code does not exist.",
  "errors": { },
  "timestamp": "2026-08-10 04:31:22"
}
```

两种结构都是**扁平的** —— 错误字段并不嵌套在 `data` 或 `error` 之下。请根据 `code` 分支，而不要根据 `message`：消息是给人读的散文，可能被改写；错误代码才是契约。联系支持时请带上 `traceId`，它能在我们的日志中定位到那一次具体请求。

`timestamp` 是服务器本地时间（`Y-m-d H:i:s`，Asia/Seoul）且**不带时区偏移** —— 请按 KST 解析，不要当成 UTC 或 ISO 8601。

---

## 生成客户端

```bash
# TypeScript
npx @openapitools/openapi-generator-cli generate \
  -i https://sendgo.io/openapi.yaml -g typescript-fetch -o ./sendgo-client

# Python
openapi-generator-cli generate \
  -i https://sendgo.io/openapi.yaml -g python -o ./sendgo-client

# Go
openapi-generator-cli generate \
  -i https://sendgo.io/openapi.yaml -g go -o ./sendgo-client
```

**若已有官方 SDK，请优先使用 SDK。** 生成的客户端只能给你带类型的请求体，除此之外别无其他：官方 SDK 会缓存令牌、在 `TOKEN_EXPIRED` / `TOKEN_MISMATCH` 时透明地重新签发并重试那一次请求 —— 否则这些逻辑你得在每个服务里自己写一遍。支持的 20 种语言与框架见 [SDK 索引](/zh/sdk)。

---

## 浏览与校验

把 `openapi.yaml` 粘贴到 [Swagger Editor](https://editor.swagger.io) 即可可视化浏览端点与数据结构。

```bash
npx @redocly/cli lint openapi.yaml
```

---

## 面向 AI 编码工具

本规范是若干机器可读入口之一：

| URL | 内容 |
|-----|------|
| `/openapi.yaml` | 本规范 |
| `/llms.txt` | 全部指南的索引（[llmstxt.org](https://llmstxt.org) 规范） |
| `/llms-full.txt` | 全部指南合并为纯文本 |
| `/{lang}/sdk/{slug}.md` | 任意 SDK 指南的原始 markdown |

---

## 包信息

- **包名**：`send-go/openapi`（GitHub）
- **仓库**：[send-go/openapi](https://github.com/send-go/openapi)
- **许可**：MIT

### 如何获取 API 密钥

登录 Sendgo 后，在 **API/SDK → API 对接** 菜单中签发访问密钥与私密密钥。
使用通知消息／品牌消息需在 **Kakao 渠道** 登记发送者资料；使用短信需在 **发送号码** 菜单登记主叫号码。