版本: v2.2\ 最後更新: 2026-06-26\ 適用對象: 第三方系統整合開發者\ 服務模組: aile-service-integration\ 程式碼分支: release
v2.2 保留 AIFF 寫入規格、
pictureUrl主資料與 setting 級圖片、訊息傳送、服務廣播與 Simulator 聯調說明。v2.2+ 變更摘要(2026-06-25 release):
- • Breaking: OpenAPI 路徑重構 — 從
/integration/openapi/v1/<資源>改為/<資源>/v1/<動作>。- • Breaking: 所有 OpenAPI 端點統一為 POST,不再支援 GET。
- • Breaking: HMAC 簽名公式修正 —
integrationId + nonce + body(不含 method/path)。- • Breaking: Authorization 頭字首從
HMAC-SHA256改為AILE。- • 新增: AIFF 寫入介面(create/update/delete)。
- • 新增: 訊息傳送介面(
POST /messages/v1/send)。- • 新增: 群發廣播介面(
POST /messages/v1/broadcast/create)。- • 修正: 事件資源域和事件型別列表(基於原始碼)。
Aile 平臺整合(Platform Integration)是一套標準化的第三方系統接入框架,允許外部 SaaS 平臺以租戶級別安裝 Aile 服務,並交換業務事件與資料。
┌─────────────────────────────────────────────────┐
│ Aile 平臺 │
│ │
│ aile-service-integration │
│ ├── /integration/app/* 應用定義管理 │
│ ├── /integration/tenant/* 安裝生命週期管理 │
│ ├── /messages/* 訊息傳送 / 廣播 │
│ ├── /openapi/* 第三方 API(HMAC簽名)│
│ └── InternalEvent → Webhook → 第三方回撥 │
│ │
│ aile-service-tenant (事件釋出) │
│ └── InternalEventTrigger → 業務事件發布 │
└─────────────────────────────────────────────────┘
│ 控制面 (HTTP) │ OpenAPI (HMAC) │ Webhook (HMAC)
▼ ▼ ▼
┌─────────────────────────────────────────────────┐
│ 第三方整合平臺(你) │
│ ├── 安裝介面 (接收 Aile 安裝請求) │
│ ├── OpenAPI 呼叫 (查詢/傳送訊息/群發廣播) │
│ └── Webhook 接收 (接收 Aile 業務事件) │
└─────────────────────────────────────────────────┘注意: aile-service-job 模組中有一個舊的 TenantAppModel / TenantAppController 體系,以 /tenantapp、/webhook 為路徑字首。本手冊描述的是新的 Integration Platform 體系(基於 IntegrationAppModel / TenantIntegrationModel),路徑字首為 /integration/。新體系相較舊體繫有以下增強:
| 特性 | 舊 TenantAppModel | 新 Integration Platform |
|---|---|---|
| 應用定義 | 無全域性應用定義 | IntegrationApp(獨立於租戶) |
| 安裝生命週期 | 簡單 CRUD | 完整狀態機 Pending→Active→Suspended→Deleted |
| 金鑰管理 | 固定 secretKey | HMAC appSecret + rotate-secret |
| 事件訂閱 | webHookTypes | subscribedEvents(事件資源域粒度) |
| 第三方回撥 | 無 | install/update/rotateSecret/uninstall |
| OpenAPI 目錄 | 無 | catalog 端點自動發現 |
| 安全隔離 | 簽名+租戶繫結 | integrationId + body 一致性校驗 + nonce |
這是由 Aile 管理員建立的全域性應用註冊,描述一個第三方平臺的身份和能力。
{
"appId": "your-app-id", // 全域性唯一應用 ID
"appName": "Your Platform Name", // 應用名稱
"provider": "your-company", // 提供方
"supportedEvents": [ // 支援的事件資源域(*. 為通配)
"contact.*",
"service_number.*"
],
"authType": "HMAC_SHA256", // 鑑權型別
"secret": "platform-secret", // 平臺級金鑰(Aile 呼叫第三方時使用)
"installUrl": "https://your-platform.com/api/aile/install", // 第三方安裝 URL
"updateUrl": "https://your-platform.com/api/aile/update", // 第三方更新 URL
"rotateSecretUrl": "https://your-platform.com/api/aile/rotate", // 第三方輪換金鑰 URL
"uninstallUrl": "https://your-platform.com/api/aile/uninstall", // 第三方解除安裝 URL
"installAckMode": "Sync", // Sync(同步完成)或 Async(非同步受理)
"status": "Active" // Draft → Active → Suspended → Deleted
}──→ 狀態機:
Draft → Active → Suspended → Active → Deleted
當一個 Aile 租戶安裝你的平臺應用時,會在 Aile 端建立一個安裝例項:
{
"integrationId": "ti_xxxxx", // 安裝例項唯一 ID(Aile 生成)
"appId": "your-app-id", // 關聯的應用 ID
"tenantId": "T001", // Aile 租戶 ID
"tenantType": "enterprise", // 租戶型別
"externalTenantId": "EXT-12345", // 第三方系統對應租戶 ID(由你指定)
"appSecret": "generated-secret", // 安裝級金鑰(用於 HMAC 簽名,由 Aile 生成)
"webhookUrl": "https://your-platform.com/webhook/events", // 事件接收地址
"subscribedEvents": [ // 實際訂閱的事件資源域
"contact.*",
"service_number.*"
],
"installAckMode": "Sync", // 受理模式
"status": "Active" // 安裝狀態
}──→ 安裝狀態機:
Pending → Active → Suspended/Disabled → Active → DeletedInstallFailed狀態表示安裝失敗。
IntegrationApp 可以被多個租戶安裝(多個 TenantIntegration)TenantIntegration 擁有獨立的 appSecret 和 webhookUrlexternalTenantId 欄位管理(不存在獨立的 TenantMapping 模型)Step 0: Aile 管理員註冊 IntegrationApp(後臺操作)
│
Step 1: Aile 租戶發起安裝
POST /integration/tenant/system/v1/install
{ appId, tenantId, tenantType }
│
├── Aile 建立 TenantIntegration(狀態=Pending)
├── Aile 呼叫第三方 installUrl(傳遞 integrationId、appSecret 等)
│
Step 2: 第三方處理安裝
├── 驗證請求合法性
├── 建立外部租戶關聯
├── 確認 webhookUrl 和 subscribedEvents
│
├── Sync 模式:直接返回 { status: "Active", externalTenantId, webhookUrl, subscribedEvents }
│ → Aile 更新狀態為 Active
│
└── Async 模式:先返回 { accepted: true, status: "Pending" }
→ 後續回撥 Aile
POST /integration/tenant/open/v1/install/callback
{ integrationId, status: "Active", externalTenantId, webhookUrl, subscribedEvents }
→ Aile 更新狀態為 Active
Step 3: 日常執行
├── 第三方呼叫 Aile OpenAPI(HMAC 簽名,查詢/同步資料)
└── Aile 推送業務事件到第三方 webhookUrl(HMAC 簽名)
Step 4: 解除安裝
POST /integration/tenant/system/v1/uninstall?integrationId=xxx
├── Aile 呼叫第三方 uninstallUrl
└── TenantIntegration 狀態 = Deleted當租戶發起安裝後,Aile 會呼叫 IntegrationApp.installUrl,傳送的請求體:
{
"integrationId": "ti_xxxxx",
"appId": "your-app-id",
"tenantId": "T001",
"tenantType": "enterprise",
"operatorId": "emp_001",
"appSecret": "generated-secret-for-this-instance",
"installationCallbackUrl": "https://aile-api.com/integration/tenant/open/v1/install/callback",
"installAckMode": "Sync",
"subscribedEvents": ["contact.*", "service_number.*"]
}Sync 模式(installAckMode=Sync):
{
"status": "Active",
"externalTenantId": "EXT-12345",
"webhookUrl": "https://your-platform.com/webhook/events",
"subscribedEvents": ["contact.*", "service_number.*"]
}Async 模式(installAckMode=Async):
{
"accepted": true,
"status": "Pending"
}之後非同步回撥 Aile:
curl -X POST https://aile-api.com/integration/tenant/open/v1/install/callback \
-H "Content-Type: application/json" \
-d '{
"integrationId": "ti_xxxxx",
"status": "Active",
"externalTenantId": "EXT-12345",
"webhookUrl": "https://your-platform.com/webhook/events",
"subscribedEvents": ["contact.*", "service_number.*"]
}'Aile 在以下場景會呼叫第三方對應 URL(定義在 IntegrationApp 中):
| 操作 | Aile 呼叫 | 傳遞引數 |
|---|---|---|
| 更新配置 | updateUrl | { integrationId, webhookUrl, subscribedEvents } |
| 輪換金鑰 | rotateSecretUrl | { integrationId, operatorId } |
| 解除安裝 | uninstallUrl | { integrationId } |
所有第三方呼叫 Aile OpenAPI 的請求都必須攜帶 HMAC 簽名。Aile 推送的 Webhook 事件也會使用相同簽名機制。
演演算法: HMAC-SHA256
金鑰: appSecret(來自 TenantIntegration 安裝例項)
簽名內容: integrationId + nonce + requestBody
簽名結果: Base64( HMAC-SHA256(appSecret, 簽名內容) )| Header | 值 | 說明 |
|---|---|---|
Authorization | AILE {integrationId}:{signature} | 簽名身份與結果 |
X-Aile-Nonce | nonce_1718256000123 | 每次請求唯一的隨機字串 |
Content-Type | application/json | 請求體格式 |
import hmac
import hashlib
import base64
import time
import requests
def build_signature(integration_id: str, app_secret: str, nonce: str, body: str) -> str:
"""
計算 HMAC-SHA256 簽名。
簽名內容 = integrationId + nonce + body
"""
raw = integration_id + nonce + body
mac = hmac.new(
app_secret.encode('utf-8'),
raw.encode('utf-8'),
hashlib.sha256
)
return base64.b64encode(mac.digest()).decode('utf-8')
def build_auth_header(integration_id: str, signature: str) -> str:
return f"AILE {integration_id}:{signature}"
def call_aile_openapi(integration_id: str, app_secret: str,
method: str, path: str, body: dict = None) -> dict:
"""
呼叫 Aile OpenAPI。
path 格式: /tenants/v1/me
"""
base_url = "https://aile-api.com" # 替換為實際環境地址
body_str = json.dumps(body) if body else ""
nonce = f"nonce_{int(time.time() * 1000)}"
signature = build_signature(integration_id, app_secret, nonce, body_str)
authorization = build_auth_header(integration_id, signature)
response = requests.request(
method=method,
url=f"{base_url}{path}",
headers={
"Authorization": authorization,
"X-Aile-Nonce": nonce,
"Content-Type": "application/json"
},
data=body_str if body_str else None
)
return response.json()import crypto from "crypto";
function buildSignature(integrationId, appSecret, nonce, bodyString) {
const raw = integrationId + nonce + (bodyString ?? "");
return crypto.createHmac("sha256", appSecret).update(raw).digest("base64");
}
function buildAuthHeader(integrationId, signature) {
return `AILE ${integrationId}:${signature}`;
}
async function callAileOpenApi(integrationId, appSecret, method, path, body) {
const bodyStr = body ? JSON.stringify(body) : "";
const nonce = `nonce_${Date.now()}`;
const signature = buildSignature(integrationId, appSecret, nonce, bodyStr);
const response = await fetch(`https://aile-api.com${path}`, {
method,
headers: {
"Authorization": buildAuthHeader(integrationId, signature),
"X-Aile-Nonce": nonce,
"Content-Type": "application/json"
},
body: bodyStr || undefined
});
return response.json();
}import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public class AileHmacSigner {
public static String buildSignature(String integrationId, String appSecret,
String nonce, String body) {
String raw = integrationId + nonce + (body != null ? body : "");
try {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(appSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
return Base64.getEncoder().encodeToString(mac.doFinal(raw.getBytes(StandardCharsets.UTF_8)));
} catch (Exception e) {
throw new RuntimeException("HMAC signing failed", e);
}
}
public static String buildAuthHeader(String integrationId, String signature) {
return "AILE " + integrationId + ":" + signature;
}
}Aile 服務端驗證流程:
/tenants/v1/me、/contacts/v1/list、/aiffs/v1/create)Authorization 頭解析 integrationId 和 requestSignatureintegrationId 欄位,與簽名中的身份一致性校驗(防止冒用)integrationId 查詢 TenantIntegrationModel,取得 appSecretappSecret 對 integrationId + nonce + body 計算 HMAC-SHA256MessageDigest.isEqual() 時間恆定比對簽名Active、所屬應用狀態為 Active以下 API 供 Aile 內部管理面使用,第三方不需要呼叫這些介面。列於此處供理解完整流程。
| 方法 | 路徑 | 說明 |
|---|---|---|
POST | /integration/app/system/v1/create | 建立應用定義 |
POST | /integration/app/system/v1/update | 更新應用定義 |
GET | /integration/app/system/v1/detail?appId= | 查詢應用詳情 |
POST | /integration/app/system/v1/items | 分頁查詢應用列表 |
POST | /integration/app/system/v1/enable | 上架應用 |
POST | /integration/app/system/v1/disable | 下架應用 |
POST | /integration/app/system/v1/delete | 刪除應用 |
GET | /integration/app/system/v1/statistics/usage?appId= | 使用統計 |
| 方法 | 路徑 | 說明 |
|---|---|---|
POST | /integration/tenant/system/v1/install | 發起安裝 |
POST | /integration/tenant/system/v1/update | 更新安裝配置 |
POST | /integration/tenant/system/v1/suspend?integrationId= | 暫停安裝 |
POST | /integration/tenant/system/v1/resume?integrationId= | 恢復安裝 |
POST | /integration/tenant/system/v1/uninstall?integrationId= | 解除安裝 |
POST | /integration/tenant/system/v1/rotate-secret?integrationId= | 輪換金鑰 |
GET | /integration/tenant/system/v1/detail?integrationId= | 查詢安裝詳情 |
GET | /integration/tenant/system/v1/items?tenantId= | 依租戶查詢列表 |
GET | /integration/tenant/system/v1/items/by-app?appId= | 依應用查詢列表 |
POST | /integration/tenant/open/v1/install/callback | 第三方安裝回撥(公開介面) |
發起安裝:
{
"appId": "your-app-id", // 必填
"tenantId": "T001", // 必填
"tenantType": "enterprise" // 必填
}安裝回撥(第三方 → Aile):
{
"integrationId": "ti_xxxxx", // 必填
"status": "Active", // 必填(Active 或 InstallFailed)
"externalTenantId": "EXT-12345", // 可選但不建議為空
"webhookUrl": "https://...", // 可選
"subscribedEvents": ["contact.*"], // 可選
"message": "安裝完成" // 可選
}更新配置:
{
"integrationId": "ti_xxxxx",
"webhookUrl": "https://new-url.com/webhook",
"subscribedEvents": ["tenant.*", "contact.*"]
}所有 OpenAPI 端點採用 /<資源>/v1/<動作> 路徑結構(例如 /contacts/v1/list),所有請求均為 POST 方法(含查詢請求),且必須攜帶 HMAC 簽名。舊版 /integration/openapi/v1/<資源> 路徑不再作為對外穩定契約。
所有 OpenAPI 請求體都必須包含 integrationId:
{
"integrationId": "ti_xxxxx"
}其餘欄位因 API 而異。詳見各端點說明。
{
"code": 200,
"message": "success",
"data": { ... }
}| 方法 | 路徑 | 說明 |
|---|---|---|
POST | /tenants/v1/me | 查詢當前安裝例項的租戶資訊 |
請求:
{
"integrationId": "ti_xxxxx"
}響應 data:
{
"tenantId": "T001",
"tenantName": "某某公司",
"tenantType": "enterprise",
"status": "Active"
}| 方法 | 路徑 | 說明 |
|---|---|---|
POST | /service-numbers/v1/list | 查詢服務號列表 |
POST | /service-numbers/v1/detail | 查詢服務號詳情 |
POST | /service-numbers/v1/sync | 查詢服務號差異同步列表 |
列表請求:
{
"integrationId": "ti_xxxxx",
"current": 1,
"size": 20
}詳情請求:
{
"integrationId": "ti_xxxxx",
"serviceNumberId": "SN001"
}同步請求:
{
"integrationId": "ti_xxxxx"
}| 方法 | 路徑 | 說明 |
|---|---|---|
POST | /employees/v1/list | 查詢租戶員工列表 |
{
"integrationId": "ti_xxxxx",
"current": 1,
"size": 20
}| 方法 | 路徑 | 說明 |
|---|---|---|
POST | /contacts/v1/list | 查詢聯絡人列表 |
POST | /contacts/v1/detail | 查詢聯絡人詳情 |
POST | /contacts/v1/interactions | 查詢近期互動記錄 |
列表請求:
{
"integrationId": "ti_xxxxx",
"serviceNumberId": "SN001",
"current": 1,
"size": 20
}詳情請求:
{
"integrationId": "ti_xxxxx",
"contactId": "C001",
"serviceNumberId": "SN001"
}互動記錄請求:
{
"integrationId": "ti_xxxxx",
"serviceNumberId": "SN001",
"days": 30,
"current": 1,
"size": 20
}| 方法 | 路徑 | 說明 |
|---|---|---|
POST | /groups/v1/list | 查詢群組列表 |
{
"integrationId": "ti_xxxxx",
"current": 1,
"size": 20
}| 方法 | 路徑 | 說明 |
|---|---|---|
POST | /sessions/v1/statistics | 查詢會話月統計 |
POST | /sessions/v1/statistics/detail | 查詢會話月統計詳情 |
{
"integrationId": "ti_xxxxx",
"serviceNumberId": "SN001",
"month": "2026-06"
}v2.2 更新。 新增 create / update / delete 寫介面,支援主資料圖片 (
pictureUrl) 和 setting 級別配置。
| 方法 | 路徑 | 說明 |
|---|---|---|
POST | /aiffs/v1/list | 查詢 AIFF 列表(分頁) |
POST | /aiffs/v1/detail | 查詢 AIFF 詳情(含 settings) |
POST | /aiffs/v1/create | 建立 AIFF 主資料與設定 |
POST | /aiffs/v1/update | 更新 AIFF 主資料與設定 |
POST | /aiffs/v1/delete | 刪除 AIFF 主資料與全部設定 |
列表請求:
{
"integrationId": "ti_xxxxx"
}詳情請求:
{
"integrationId": "ti_xxxxx",
"extraId": "your-app-id"
}建立請求 OpenApiAiffCreateRequestDto:
| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
integrationId | String | ✅ | 租戶安裝例項 ID |
extraId | String | ✅ | 外部應用唯一識別(外部系統中的 AIFF ID) |
name | String | ✅ | AIFF 應用名稱 |
description | String | AIFF 應用描述 | |
pictureUrl | String | AIFF 主資料圖片地址,直接儲存外部傳入的 URL(不下載、不上傳) | |
settings | Object[] | 建立時同步寫入的 AIFF 設定列表(可選) |
更新請求 OpenApiAiffUpdateRequestDto:
| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
integrationId | String | ✅ | 租戶安裝例項 ID |
extraId | String | ✅ | 外部應用唯一識別 |
name | String | ✅ | AIFF 應用名稱 |
description | String | AIFF 應用描述 | |
pictureUrl | String | AIFF 主資料圖片地址 | |
settings | Object[] | 更新時同步維護的 AIFF 設定列表(可選) |
刪除請求:
{
"integrationId": "ti_xxxxx",
"extraId": "your-app-id"
}刪除主資料時會連帶刪除該 AIFF 下的所有設定。
Setting 子結構 OpenApiAiffSettingRequestDto:
每個 setting 對應一組前端展示配置,可在建立/更新 AIFF 時一併提交。
| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
id | String | 既有設定 ID(更新既有設定時使用) | |
extraKey | String | 外部設定唯一識別 | |
displayName | String | 前端展示名稱 | |
pictureUrl | String | 設定級別圖片地址。未傳時查詢返回回退外層主資料 pictureUrl | |
serviceNumberIds | String[] | 設定生效的服務號 ID 列表 | |
displayType | String | 顯示型別,見下表 | |
roomType | String[] | 聊天室型別列表(僅 displayType=Room 時生效),見下表 | |
displayLocation | String | 嵌入位置(僅 displayType=Room 時生效),見下表 | |
endPointUrl | String | AIFF 跳轉目標地址 | |
supportDevice | String | 支援裝置型別,見下表 | |
popupSize | String | 彈窗尺寸,見下表 |
displayType 列舉值:
| 值 | 說明 |
|---|---|
Frame | 主框架 |
AppList | 應用列表 |
Room | 聊天室內嵌(此時需設定 roomType + displayLocation) |
ContactIndex | 客戶主頁 |
roomType 列舉值(可多選):
| 值 | 說明 |
|---|---|
One | 單人聊天室 |
Person | 個人聊天室 |
Friend | 好友聊天室 |
System | 系統聊天室 |
Discuss | 多人聊天室 |
Group | 社團聊天室 |
GroupOwner | 社團聊天室(擁有者) |
BusinessCustomer | 商務號聊天室(客戶角度) |
BusinessEmployee | 商務號聊天室(員工角度) |
BusinessSecretary | 商務號秘書群聊天室 |
ServiceNumberCustomer | 服務號聊天室(客戶角度) |
ServiceNumberEmployee | 服務號聊天室(員工角度) |
ServiceNumberAgent | 服務號聊天室(服務人員角度) |
ServiceNumberGroup | 服務號群內聊天室 |
Object | 物件聊天室 |
displayLocation 列舉值:
| 值 | 說明 |
|---|---|
Toolbar | 工具列 |
Extend | 擴充套件欄位 |
Embed | 下沉式(內嵌) |
MessageMenu | 訊息選單 |
supportDevice 列舉值:
| 值 | 說明 |
|---|---|
Desktop | 桌面端 |
Mobile | 手機端 |
popupSize 列舉值:
| 值 | 說明 |
|---|---|
Full | 全螢幕 |
Half | 半螢幕 |
詳情響應 data 結構 OpenApiAiffVO:
{
"id": "aiff-xxx",
"extraId": "your-app-id",
"tenantId": "T001",
"name": "外部應用名稱",
"description": "應用描述",
"pictureUrl": "https://example.com/image.png",
"settings": [
{
"id": "setting-001",
"extraId": "your-app-id",
"extraKey": "setting-key-1",
"displayName": "設定顯示名稱",
"pictureUrl": "https://example.com/setting-icon.png",
"endPointUrl": "https://example.com/aiff",
"serviceNumberIds": ["SN001", "SN002"],
"aiffId": "aiff-xxx"
}
]
}重要說明:
pictureUrl 僅做字串透傳與儲存,不下載圖片、不上傳附件。setting 未傳 pictureUrl 時,寫入階段不自動補外層值,僅查詢返回時回退外層主資料圖片icon 檔案上傳(內部 job 服務的 multipart/form-data icon 欄位在 OpenAPI 鏈路中固定為空)extraKey 定位既有 setting;若未傳可識別的標識,更新行為依賴既有業務約束extraId 下所有 setting| 方法 | 路徑 | 說明 |
|---|---|---|
POST | /catalog/v1/business-objects | 查詢業務物件清單 |
POST | /catalog/v1/sync-resources | 查詢支援同步的資源清單 |
POST | /catalog/v1/event-types | 查詢支援的事件型別清單 |
POST | /catalog/v1/event-scopes | 查詢支援的事件資源域清單 |
所有目錄端點請求格式相同:
{
"integrationId": "ti_xxxxx"
}v2.1 新增功能。 供外部整合應用向指定聊天室傳送訊息。
| 方法 | 路徑 | 說明 |
|---|---|---|
POST | /messages/v1/send | 傳送訊息到指定聊天室 |
功能範圍:
Text(文字訊息)、Template(模板訊息 / 卡片訊息)Image、File、Video、Audio、Voice、Sticker 等需附件契約的型別(會返回請求非法)Room、System、User、ServiceNumber、ServiceMemberSystem(系統訊息)、User(以使用者身份傳送)、ServiceNumber(以服務號身份傳送)sourceType 控制單條訊息的顯示方式(System / User / Assistant)請求 DTO 結構:
頂層請求 OpenApiMessageSendRequestDto:
| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
integrationId | String | ✅ | 租戶安裝例項 ID |
sender | Object | ✅ | 傳送者資料,見下表 |
to | Object | ✅ | 接收者資料,見下表 |
messages | Object[] | ✅ | 訊息列表(至少 1 條),見下表 |
傳送者 OpenApiMessageSenderDto:
| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
type | String | ✅ | 傳送者型別:System / User / ServiceNumber |
userType | String | 使用者型別:Employee / Contact(type=User 時使用,預設為 Employee) | |
code | String | openId 或服務號代號(type 非 System 時必填) | |
incoming | String | 進線標記(可選) |
接收者 OpenApiMessageReceiverDto:
| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
type | String | ✅ | 接收目標型別:Room / System / User / ServiceNumber / ServiceMember |
userType | String | 使用者型別:Employee / Contact / Visitor(type=User 時使用) | |
code | String | 接收者代號(openId 或 roomId) | |
serviceNumberId | String | 服務號 ID(type=ServiceNumber/ServiceMember 時使用) | |
channelType | String | 指定渠道:Line / Facebook / Webchat / Aiwow 等 |
單條訊息 OpenApiMessageItemDto:
| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
type | String | ✅ | 訊息型別:Text / Template |
altText | String | 替代文字(渠道不支援時的降級顯示) | |
sourceType | String | 訊息來源型別:User / System / Assistant / Broadcast。未傳時按 sender.type 推導:System→System,其餘→User | |
content | Object | ✅ | 訊息內容,見下表 |
訊息內容 OpenApiMessageContentDto:
| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
type | String | 模板型別(訊息為 Template 時使用):Buttons / Confirm / Process / Carousel | |
title | String | 標題 | |
text | String | ✅ | 文字內容(Text 訊息的主體;Template 訊息的內文) |
imageUrl | String | 圖片網址(Template 訊息使用) | |
orientation | String | 內容方向:Vertical(垂直)/ Horizontal(水平) | |
defaultAction | Object | 預設動作(點選整張卡片的行為),結構:{ type, label, url } | |
actions | Object[] | 互動動作列表(按鈕等),每項結構同上 |
動作型別(defaultAction / actions[].type):
| 值 | 說明 |
|---|---|
Action | 前端本地行為 |
Postback | 提交到後端的回傳行為 |
Url | 前端開啟外部連結 |
Aiff | 前端開啟 Aiff 容器 |
響應格式:
{
"code": 200,
"message": "success",
"data": [
{
"id": "message-xxx",
"roomId": "room-001",
"tenantId": "T001",
"type": "Text",
"content": { "text": "Hello" },
"senderId": "U001",
"createdAt": "2026-06-23T10:30:00Z"
}
]
}訊息路由規則說明:
傳送訊息時,系統根據 to.type 和 sender.type 的組合決定最終的目標聊天室:
| to.type | sender.type | 路由邏輯 |
|---|---|---|
Room | 任意 | 直接使用 to.code 作為 roomId |
System | 任意 | 透過 to.code(員工 openId)定位員工的系統聊天室 |
User | System | 以接收者 openId 定位其系統聊天室(系統通知場景) |
User | User(同一個 openId) | 查詢該使用者的個人聊天室 |
User | User(不同 openId) | 查詢傳送者的好友聊天室(非好友則報錯) |
User | ServiceNumber | 查詢服務號擁有者與目標使用者的好友聊天室 |
ServiceNumber | 任意 | 查詢指定服務號與指定客戶的服務號聊天室 |
ServiceMember | 任意 | 查詢指定服務號的服務成員聊天室 |
v2.1 新增功能。 供外部整合應用建立並傳送服務號廣播(群發),內部走 tenant 服務既有廣播建立流程。
| 方法 | 路徑 | 說明 |
|---|---|---|
POST | /messages/v1/broadcast/create | 建立並傳送服務廣播 |
請求 DTO 結構 OpenApiBroadcastCreateRequestDto:
| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
integrationId | String | ✅ | 租戶安裝例項 ID |
openId | String | ✅ | 本次建立廣播的員工 OpenID(用以解析建立者身份) |
serviceNumberId | String | ✅ | 目標服務號 ID |
name | String | ✅ | 廣播任務標題 |
remark | String | 廣播備註 | |
channel | String | ✅ | 廣播頻道:Line / Facebook / Webchat / Aiwow 等 |
content | Object[] | ✅ | 廣播內容列表,每個元素為 BroadcastBody |
broadcastTime | Long | 預約廣播時間(Unix 毫秒時間戳),不填則立即傳送 | |
labelIds | String[] | 標籤匹配範圍(以標籤篩選目標客戶) | |
customerIds | String[] | 指定廣播目標客戶 ID 列表 | |
matchLogic | String | 標籤匹配邏輯:OR(滿足任一)/ AND(全部滿足) |
廣播內容 BroadcastBody:
| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
index | Integer | 順序編號 | |
type | String | ✅ | 訊息型別(廣播支援:Text / Image / File / Template) |
content | String | ✅ | 訊息內容(與既有服務號廣播格式一致) |
響應格式:
{
"code": 200,
"message": "success",
"data": {
"id": "broadcast-xxx",
"serviceNumberId": "SN001",
"name": "節日推廣",
"status": "Doing",
"createdAt": "2026-06-23T10:30:00Z"
}
}重要說明:
openId 必須對應一個存在於當前安裝例項租戶中的員工,否則請求失敗labelIds 和 customerIds 都可選;若均不填,廣播目標將為空broadcastTime 則立即嘗試傳送;填寫未來時間則排程傳送Doing,由 tenant 既有流程觸發下游廣播傳送;傳送完成後狀態變更為 Doneintegration 服務會臨時建立 AileContext(含 tenantId + accountId + employeeId),完成後恢復原上下文,保證執行緒池安全TenantIntegration.webhookUrlapplication/jsonappSecret,簽名規則同 OpenAPI)eventId 保證,重複推送同一 eventId 應返回成功{
"eventId": "evt_abc123",
"eventType": "contact.created",
"eventVersion": "v1",
"occurredAt": "2026-06-16T10:30:00Z",
"source": "aile-tenant",
"integration": {
"appId": "your-app-id",
"integrationId": "ti_xxxxx"
},
"tenant": {
"tenantId": "T001",
"externalTenantId": "EXT-12345",
"tenantType": "enterprise"
},
"scope": {
"serviceNumberId": "SN001",
"entrySourceId": "line_channel_123"
},
"data": {
"contactId": "C001",
"name": "張三",
"channel": "Line",
"scopeId": "Uxxx_line_id"
},
"metadata": {
"traceId": "trace_001",
"retryCount": 0
}
}status=Active 的安裝例項推送subscribedEvents 中訂閱的事件資源域metadata.retryCount 記錄用於 supportedEvents、subscribedEvents 的訂閱值:
| 資源域程式碼 | 說明 |
|---|---|
tenant.* | 租戶事件 |
user.* | 使用者事件 |
service_number.* | 服務號事件 |
contact.* | 聯絡人(客戶)事件 |
visitor.* | 訪客事件 |
group.* | 群組事件 |
addressbook.* | 通訊錄事件 |
notice.* | 通知事件 |
session.* | 會話事件 |
以下事件型別在 aile-service-tenant 中已實作並自動釋出:
| 事件型別 | 資源域 | 說明 |
|---|---|---|
service_number.created | service_number.* | 服務號建立 |
service_number.updated | service_number.* | 服務號更新 |
service_number.deleted | service_number.* | 服務號刪除 |
contact.created | contact.* | 聯絡人建立 |
contact.updated | contact.* | 聯絡人更新 |
contact.deleted | contact.* | 聯絡人刪除 |
contact.service_number_unfollowed | contact.* | 聯絡人取消追蹤服務號 |
visitor.merged | visitor.* | 匿名訪客合併為實名 |
employee.disabled | employee.disabled | 員工停用 |
tenant.disabled | tenant.* | 租戶停用 |
注意: 當前事件推送機制(InternalEvent → Pub/Sub → Webhook HTTP)中,Pub/Sub 釋出已實現,但 Webhook HTTP 投遞層目前正在規劃中。具體可用性請與 Aile 團隊確認。
appId(你的應用識別碼)https://api.aile.com)POST /your-platform/api/aile/installPOST /your-platform/api/aile/updatePOST /your-platform/api/aile/rotatePOST /your-platform/api/aile/uninstallPOST /your-platform/webhook/eventsAile 呼叫 installUrl 時,你的介面需要:
appSecret 和 integrationId)externalTenantId(你的系統中對應的租戶 ID)示例(Node.js / Express):
app.post('/api/aile/install', (req, res) => {
const { integrationId, appId, tenantId, tenantType, appSecret,
installationCallbackUrl, subscribedEvents } = req.body;
// 儲存安裝資訊到你的資料庫
db.installations.save({
integrationId,
appId,
tenantId,
tenantType,
appSecret, // 重要:後續所有 OpenAPI 呼叫都需要
installationCallbackUrl,
subscribedEvents,
externalTenantId: `ext_${tenantId}`,
webhookUrl: 'https://your-platform.com/webhook/events',
status: 'Active'
});
// 同步模式直接返回成功
res.json({
status: 'Active',
externalTenantId: `ext_${tenantId}`,
webhookUrl: 'https://your-platform.com/webhook/events',
subscribedEvents: ['contact.*', 'service_number.*']
});
});使用儲存的 appSecret 和 integrationId,透過 HMAC 簽名呼叫 Aile API。
// 查詢租戶資訊
const result = await callAileOpenApi(
integrationId, appSecret,
'POST',
'/tenants/v1/me',
{ integrationId }
);
// 查詢服務號列表
const services = await callAileOpenApi(
integrationId, appSecret,
'POST',
'/service-numbers/v1/list',
{ integrationId, current: 1, size: 20 }
);
// 查詢聯絡人列表
const contacts = await callAileOpenApi(
integrationId, appSecret,
'POST',
'/contacts/v1/list',
{ integrationId, serviceNumberId: 'SN001', current: 1, size: 20 }
);接收 Aile 推送的業務事件:
app.post('/webhook/events', (req, res) => {
const event = req.body;
// 冪等檢查:避免重複處理同一事件
if (alreadyProcessed(event.eventId)) {
return res.json({ success: true, duplicated: true });
}
// 根據事件型別進行業務處理
switch (event.eventType) {
case 'contact.created':
handleContactCreated(event.data);
break;
case 'contact.updated':
handleContactUpdated(event.data);
break;
case 'service_number.created':
handleServiceNumberCreated(event.data);
break;
// ... 更多事件型別
}
markAsProcessed(event.eventId);
res.json({ success: true, duplicated: false });
});app.post('/api/aile/update', (req, res) => {
const { integrationId, webhookUrl, subscribedEvents } = req.body;
db.installations.update(integrationId, { webhookUrl, subscribedEvents });
res.json({ status: 'Active' });
});
app.post('/api/aile/rotate', (req, res) => {
const { integrationId, operatorId } = req.body;
// 更新 appSecret(Aile 端會生成新金鑰,這裡接收通知)
res.json({ status: 'Active' });
});
app.post('/api/aile/uninstall', (req, res) => {
const { integrationId } = req.body;
db.installations.update(integrationId, { status: 'Deleted' });
res.json({ status: 'Deleted' });
});nonce_{timestamp} 格式bodyString 為空字串 "",簽名使用 integrationId + nonce + ""{ code: 200, message: "success", data: {...} }integration-third-party-simulator 是一個完整的第三方模擬服務,可用於本地開發聯調。
cd integration-third-party-simulator
cp .env.example .env
npm install
npm start預設埠:3301
PORT=3301
INSTALL_MODE=sync # sync 或 async
ASYNC_CALLBACK_DELAY_MS=200 # 非同步回撥延遲
ASYNC_FINAL_STATUS=Active # 非同步最終狀態
DEFAULT_WEBHOOK_BASE_URL=http://localhost:3301
AILE_OPENAPI_BASE_URL=http://localhost:8080 # Aile 服務地址控制面(第三方平臺端,接收 Aile 請求):
# 安裝
curl -X POST http://localhost:3301/control-plane/install \
-H "Content-Type: application/json" \
-d '{
"integrationId": "ti_001",
"appId": "aipower",
"tenantId": "tenant_001",
"tenantType": "PERSONAL",
"appSecret": "secret_001",
"installationCallbackUrl": "http://localhost:8080/integration/tenant/open/v1/install/callback",
"subscribedEvents": ["contact.*"]
}'
# 更新
curl -X POST http://localhost:3301/control-plane/update \
-H "Content-Type: application/json" \
-d '{"integrationId": "ti_001", "webhookUrl": "https://new.example.com/webhook"}'
# 解除安裝
curl -X POST http://localhost:3301/control-plane/uninstall \
-H "Content-Type: application/json" \
-d '{"integrationId": "ti_001"}'除錯介面:
# 檢視所有安裝
curl http://localhost:3301/debug/installations
# 檢視所有 Webhook 事件
curl http://localhost:3301/debug/webhooks
# 以安裝例項身份觸發 OpenAPI 呼叫
curl -X POST http://localhost:3301/debug/installations/ti_001/openapi/invoke \
-H "Content-Type: application/json" \
-d '{"method": "POST", "path": "/tenants/v1/me"}'IntegrationApp.installUrl 指向 http://localhost:3301/control-plane/installINSTALL_MODE=sync 打通主鏈路async 驗證回撥分支AILE_OPENAPI_BASE_URL 指向可訪問的 Aile 環境| 錯誤碼 | 說明 | 解決建議 |
|---|---|---|
FAIL_OPENAPI_AUTH_HEADER_REQUIRED | 缺少 Authorization 或 X-Aile-Nonce 頭 | 檢查請求頭是否正確傳遞 |
FAIL_OPENAPI_SIGNATURE_INVALID | 簽名驗證失敗 | 檢查簽名演算法(integrationId+nonce+body),確認 appSecret 正確 |
FAIL_OPENAPI_INTEGRATION_NOT_FOUND | 安裝例項不存在或未啟用 | 檢查 integrationId 是否正確,安裝狀態是否為 Active |
FAIL_OPENAPI_INTEGRATION_DISABLED | 安裝例項已停用 | 聯絡 Aile 管理員確認安裝狀態 |
FAIL_TENANT_INTEGRATION_NOT_FOUND | 資料許可權校驗失敗 | 確認查詢的服務號或聯絡人是否屬於當前租戶 |
FAIL_INTEGRATION_APP_NOT_FOUND | 應用定義不存在或已下架 | 聯絡 Aile 管理員確認應用狀態 |
Q: 簽名驗證一直失敗?
A: 請確認簽名內容為 integrationId + nonce + bodyString(注意不是 method + path + nonce + body)。bodyString 為原始 JSON 字串,無 body 時為空字串。
Q: Authorization 頭格式?
A: 格式為 AILE {integrationId}:{signature},例如 AILE ti_001:abc123...。
Q: 查詢請求使用 GET 還是 POST?
A: 所有 OpenAPI 端點均使用 POST 方法(含查詢請求),請求體為 JSON。
Q: 如何判斷安裝是否為 Async 模式?
A: 檢視請求中的 installAckMode 欄位。Sync 表示同步完成(直接返回狀態),Async 表示先受理再回撥。
Q: 第三方需要實現哪些介面?
A: 至少需要實現 installUrl(安裝),建議也實現 updateUrl、uninstallUrl;Webhook 接收介面用於接收事件。
| 層級 | 檔案路徑 | 說明 |
|---|---|---|
| 應用定義模型 | aile-api/aile-integration-api/.../model/IntegrationAppModel.java | IntegrationApp 持久化模型 |
| 安裝例項模型 | aile-api/aile-integration-api/.../model/TenantIntegrationModel.java | TenantIntegration 持久化模型 |
| 事件信封 DTO | aile-api/aile-integration-api/.../dto/event/EventEnvelopeDto.java | 對外事件格式 |
| 安裝請求 DTO | aile-api/aile-integration-api/.../dto/control/InstallationRequestDto.java | 安裝請求引數 |
| 安裝響應 DTO | aile-api/aile-integration-api/.../dto/control/InstallationResponse.java | 第三方安裝響應 |
| 回撥請求 DTO | aile-api/aile-integration-api/.../dto/control/InstallationCallbackRequestDto.java | 非同步回撥引數 |
| 事件資源域列舉 | aile-api/aile-integration-api/.../enums/IntegrationEventScopeType.java | 9 個事件資源域定義 |
| 安裝狀態列舉 | aile-api/aile-integration-api/.../enums/TenantIntegrationStatus.java | 6 種安裝狀態 |
| HMAC 簽名校驗器 | aile-service/aile-service-integration/.../service/impl/IntegrationOpenApiSignatureVerifier.java | 入站簽名校驗實現 |
| OpenAPI 過濾器 | aile-service/aile-service-integration/.../config/IntegrationOpenApiSignatureFilter.java | 請求攔截過濾器 |
| 訪問驗證器 | aile-service/aile-service-integration/.../service/impl/IntegrationOpenApiAccessValidator.java | 安裝狀態與資料許可權校驗 |
| OpenAPI 服務 | aile-service/aile-service-integration/.../service/impl/IntegrationOpenApiServiceImpl.java | OpenAPI 業務邏輯 |
| 安裝控制器 | aile-service/aile-service-integration/.../controller/TenantIntegrationController.java | 安裝 API 端點 |
| 應用控制器 | aile-service/aile-service-integration/.../controller/IntegrationAppController.java | 應用管理 API 端點 |
| 事件型別常量 | aile-service/aile-service-tenant/.../constants/TenantIntegrationEventTypes.java | 已實現的業務事件型別 |
| Simulator 簽名 | integration-third-party-simulator/src/utils/signature.js | 簽名實現參考 |
| Simulator 安裝 | integration-third-party-simulator/src/services/installationService.js | 安裝流程參考 |
| 訊息傳送 DTO | aile-api/aile-integration-api/.../dto/openapi/OpenApiMessageSendRequestDto.java | 訊息傳送請求 |
| 訊息接收者 DTO | aile-api/aile-integration-api/.../dto/openapi/OpenApiMessageReceiverDto.java | 接收目標定義 |
| 訊息傳送者 DTO | aile-api/aile-integration-api/.../dto/openapi/OpenApiMessageSenderDto.java | 傳送者身份定義 |
| 訊息內容 DTO | aile-api/aile-integration-api/.../dto/openapi/OpenApiMessageContentDto.java | 訊息內容結構 |
| 訊息條目 DTO | aile-api/aile-integration-api/.../dto/openapi/OpenApiMessageItemDto.java | 單條訊息結構 |
| 廣播請求 DTO | aile-api/aile-integration-api/.../dto/openapi/OpenApiBroadcastCreateRequestDto.java | 廣播建立請求 |
| 廣播內容 DTO | aile-api/aile-tenant-api/.../model/servicenumber/BroadcastBody.java | 廣播訊息體 |
| 訊息控制器 | aile-service/aile-service-integration/.../controller/IntegrationOpenApiMessageController.java | 訊息與廣播 API 端點 |
| 訊息服務實現 | aile-service/aile-service-integration/.../service/impl/IntegrationOpenApiMessageServiceImpl.java | 訊息傳送與廣播業務邏輯 |
| AIFF 建立 DTO | aile-api/aile-integration-api/.../dto/openapi/OpenApiAiffCreateRequestDto.java | AIFF 建立請求 |
| AIFF 更新 DTO | aile-api/aile-integration-api/.../dto/openapi/OpenApiAiffUpdateRequestDto.java | AIFF 更新請求 |
| AIFF 刪除 DTO | aile-api/aile-integration-api/.../dto/openapi/OpenApiAiffDeleteRequestDto.java | AIFF 刪除請求 |
| AIFF Setting DTO | aile-api/aile-integration-api/.../dto/openapi/OpenApiAiffSettingRequestDto.java | AIFF 設定請求 |
| AIFF 控制器 | aile-service/aile-service-integration/.../controller/IntegrationOpenApiAiffController.java | AIFF CRUD API 端點 |
| RoomTypeEnum | aile-api/aile-job-api/.../aiff/enums/RoomTypeEnum.java | 15 種聊天室型別 |
| DisplayTypeEnum | aile-api/aile-job-api/.../aiff/enums/DisplayTypeEnum.java | 4 種顯示型別 |
| AiffModel | aile-api/aile-job-api/.../aiff/model/AiffModel.java | AIFF 主資料模型(含 pictureUrl) |
| 版本 | 日期 | 變更 |
|---|---|---|
| v2.2+ | 2026-06-25 | 在 v2.2 AIFF 規格基礎上補充 release 變更:OpenAPI 路徑由 /integration/openapi/v1/<資源> 重構為 /<資源>/v1/<動作>;所有端點統一 POST;HMAC 簽名公式確認為 integrationId + nonce + body;Authorization 字首改為 AILE;同步更新 AIFF 寫入、訊息傳送、群發廣播與事件資源域/事件型別說明。 |
| v2.2 | 2026-06-24 | AIFF 章節全面重寫:新增 create / update / delete 寫介面完整規格;補充 pictureUrl 圖片支援說明(主資料級 + setting 級);補齊 5 組列舉表(displayType / roomType 15 種 / displayLocation / supportDevice / popupSize);新增 setting 子結構 DTO 與詳情響應;更新附錄 A |
| v2.1 | 2026-06-23 | 新增 6.3.9 傳送訊息 API 完整規格 + 6.3.10 廣播 API 完整規格;更新架構全景圖;更新附錄 A 檔案索引 |
| v2.0 | 2026-06-16 | 基於 release 分支最新程式碼重寫;修正 HMAC 簽名演算法文件;補充 10 個 OpenAPI 控制器細節;增加目錄章節 |
| v1.0 | 2026-06-13 | 初版 |