Raise Open API
版本 1.2.0。下载 OpenAPI 文件(YAML)
概述
给应用后端用的开放接口(主版本 v1)。调用根地址是 https://api.{域名}/open/v1(https://app.{域名}/open/v1 同样可用)。
本文件里的路径已经去掉了这个前缀:GET /whoami 就是 GET https://api.{域名}/open/v1/whoami。
第一次调用
- 在开发者平台创建应用,拿到
client_id和密钥(测试环境以kwo_test_开头,正式以kwo_live_开头;旧密钥kwo_也是正式)。 POST /oauth/token,grant_type=client_credentials。商店应用(应用级凭证)必须带tenant;租户自建应用(绑定在一个组织上)不要带,或只能带它自己的组织。- 用返回的
access_token调GET /whoami或GET /ping。令牌 300 秒过期,SDK 会在到期前 60 秒自动换。
代表一位终端客户(小程序用户)调用 /me… 时,用 grant_type=urn:raise:grant-type:customer,带上后台已经验证过的 phone。
约定
- 认证
Authorization: Bearer <access_token>。换令牌本身用 HTTP Basic(client_id:client_secret)或把这两个字段放在正文里。 - 错误 状态 ≥ 400 的正文都是
{"error":{"code","message","requestId","docUrl","details"}}。code稳定,用它分支;message给人看。details没内容时是{}。requestId与响应头X-Request-Id相同,报问题时带上。 - 列表
?limit=(默认 20,最大 100)&cursor=→{"data":[…],"nextCursor":"…" 或 null}。没有下一页时nextCursor为null。会员/me下的短列表(自己的会员、课包等)不翻页,字段名见各操作。 - 幂等 除
/oauth/token外每个POST收Idempotency-Key(1–255 字符)。范围是应用 + 组织(客户令牌再加上客户)。保存 24 小时。同键同方法同路径同正文 → 重放第一次的回答,并带Idempotent-Replayed: true。同键不同正文 → 409idempotency_conflict。第一次还在处理 → 409idempotency_in_progress。状态 ≥ 500 的回答不保存。SDK 会在 POST 上自动生成 uuid v4。 - 限流 认证过的调用都带
X-RateLimit-Limit、X-RateLimit-Remaining、X-RateLimit-Reset。429 另带Retry-After(秒)。Raise-Environment: test|live说明这把令牌属于哪边。 - 时间 RFC 3339 UTC。金额 整数最小货币单位 +
currency。JSON 字段 camelCase;令牌接口的成功正文用 RFC 6749 的名字(access_token、expires_in…)。
用 Go 模块 github.com/21stware/raise-go 或 npm 包 @21stware/raise-sdk,不要自己拼签名和重试。
地址
https://api.{domain}/open/v1domain:平台域名(默认example.com)
认证方式
| 名称 | 类型 | 说明 |
|---|---|---|
bearer | http bearer | POST /oauth/token 换到的短令牌。Authorization: Bearer <access_token> |
basic | http basic | 换令牌时的应用密钥。用户名是 client_id,密码是 client_secret。也可以把这两个字段放进正文。 |
oauth
用应用密钥换短令牌。SDK 内部调用,应用代码不要自己打这条
POST /oauth/token
用应用密钥换一把短令牌(租户令牌或客户令牌)
这条接口不要用 Bearer,也不要带 Idempotency-Key。SDK 的传输层会调它。
认证:HTTP Basic(client_id:client_secret)或把 client_id、client_secret 放进正文。未知的 id 和错误的密钥都回答 401 invalid_client,并带 WWW-Authenticate: Basic。
正文 application/x-www-form-urlencoded 或 application/json 都可以。
grant_type=client_credentials:换租户令牌,300 秒。tenant 对应用级凭证(商店应用、凭证不绑在一个组织上)是必填;对租户绑定凭证必须省略或等于它自己的组织,否则 400 tenant_required。scope 可选,空格分隔,只能是已经授予的子集。
grant_type=urn:raise:grant-type:customer:换客户令牌,900 秒。phone 必填(后台自己验证过的号码,平台不再验)。customer_id 可选,在 409 ambiguous_customer 之后用来指定是哪一位。需要范围 customer:assert。没有对应会员时 200 {"matched":false},不发令牌、不建客户。
grant_type=authorization_code:员工授权(代某位员工,在工作台之外:手机、桌面、命令行、外部 agent)。先把人带到身份服务的授权地址(client_id = 应用的 OIDC 客户端,code_challenge + S256,redirect_uri 必须在开发者平台登记过);人登录、在同意页看过范围并同意后,回调带回 code。再到这里换:code、code_verifier、redirect_uri、client_id 必填;机密客户端另用应用密钥认证(HTTP Basic 或 client_secret),公开客户端不带密钥。得到员工令牌(600 秒,sub 是这个人)与 refresh_token。人没同意过是 403 consent_required。
grant_type=refresh_token:用 refresh_token 续员工令牌。每用一次换一把新的(30 天滚动);用过的再用一次,整条链作废(400 invalid_grant)。人或管理员吊销授权、应用停用、凭证吊销后都是 400 invalid_grant,已发出的员工令牌 30 秒内失效(401 consent_revoked)。员工令牌能做的 = 人同意的范围 ∩ 组织此刻授予应用的范围 ∩ 这个人此刻的角色与数据范围。
测试密钥只能打测试企业;正式密钥不能打测试企业。否则 403 wrong_environment。应用没装、关掉、组织停用是 403 not_installed。一把范围都没授是 403 no_scope。密钥吊销或过期是 401 credential_ended。来源 IP 不在允许名单是 403 address_not_allowed。
同一地址失败太多次是 429 too_many_attempts(Retry-After: 60)。换令牌本身也有次数限制(429 rate_limited)。
- 认证:
basic或 不带凭证 - operationId:
createToken
请求正文(必填)(application/x-www-form-urlencoded、application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
grant_type | string | 是 | client_credentials 换租户令牌;urn:raise:grant-type:customer 换客户令牌;authorization_code / refresh_token 换员工令牌 可选值: client_credentials、urn:raise:grant-type:customer、authorization_code、refresh_token |
client_id | string | 未用 HTTP Basic 时必填 | |
client_secret | string | 未用 HTTP Basic 时必填 | |
tenant | string | 应用级凭证必填。租户绑定凭证必须省略或等于它自己的组织 | |
scope | string | 空格分隔的范围子集。省略则给已授予的全部 | |
phone | string | 客户授权必填。数字,可带前导 +。空格和短横忽略。大陆手机也可写成 86… / +86… | |
customer_id | string | 客户授权可选。在 ambiguous_customer 之后指定是哪一位 | |
code | string | authorization_code 必填。身份服务回调带回的授权码 | |
code_verifier | string | authorization_code 必填。PKCE 原文(S256) | |
redirect_uri | string | authorization_code 必填。与授权请求里的一致,且在开发者平台登记过 | |
refresh_token | string | refresh_token 必填。kwr_ 开头,只能用一次 |
响应
| 状态 | 说明 |
|---|---|
| 200 | 换到令牌:access_token、token_type=Bearer、expires_in、scope、tenant、environment。客户授权另带 customer。 客户授权找不到人:{"matched":false},没有令牌。 |
| 400 | invalid_request:查询或正文读不出来。见 BadRequest |
| 401 | invalid_client:client_id 不存在或 client_secret 不对。见 InvalidClient |
| 403 | 见 Forbidden |
| 409 | 见 Conflict |
| 422 | 见 Unprocessable |
| 429 | 见 RateLimited |
| 500 | internal:平台出错。见 Internal |
| 501 | not_implemented。见 NotImplemented |
| 503 | unavailable:暂时不可用(读凭证、读授权、读库失败)。见 Unavailable |
200 响应正文(application/json)
以下其一:
Token:RFC 6749 成功正文。customer 只在客户授权时出现
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
access_token | string | 是 | |
token_type | string | 是 | 固定为 "Bearer" |
expires_in | integer | 是 | 秒。租户令牌 300,客户令牌 900,员工令牌 600 |
scope | string | 是 | 空格分隔。客户令牌不含 customer:assert |
tenant | string | 是 | |
environment | string | 是 | 可选值:test、live |
customer | string | 客户中心 id。仅客户授权 | |
refresh_token | string | 仅员工授权。下一次续期用它,30 天不用就过期 | |
user | string | 仅员工授权。这位员工的用户 id |
CustomerMiss:客户授权找不到这个号码下的会员。不发令牌、不建客户
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
matched | boolean | 是 | 固定为 false |
platform
令牌身份与连通性
GET /whoami
这把令牌现在是谁:组织、应用、凭证和仍然有效的范围
不需要特定范围。任何还活着的集成令牌都可以调。返回的 scopes 是令牌里的范围与此刻授予的交集:管理员收回的范围最多 30 秒内从这里消失。
用它确认换到的令牌打的是哪个组织和哪把凭证。
- 认证:
bearer - operationId:
getWhoami
响应
| 状态 | 说明 |
|---|---|
| 200 | {tenant, app, credential, scopes} |
| 401 | 见 Unauthorized |
| 429 | 见 RateLimited |
| 503 | unavailable:暂时不可用(读凭证、读授权、读库失败)。见 Unavailable |
200 响应正文(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
tenant | string | 是 | |
app | string | 是 | |
credential | string | 是 | 换出这把令牌的 client_id |
scopes | string[] | 是 | 令牌范围与此刻授予的交集 |
user | string | 员工令牌的员工;租户令牌为空 | |
roles | string[] | 员工令牌:这个人此刻在组织里的角色(每次调用现读);租户令牌为空 | |
environment | string | 可选值:test、live |
GET /ping
连通性探测,需要范围 platform:ping,正文只有 {ok:true}
需要范围 platform:ping。不返回组织里的任何数据,只说明这把令牌还能用。没有该范围是 403 scope_required(这次调用已经计入限流)。
- 认证:
bearer - operationId:
getPing
响应
| 状态 | 说明 |
|---|---|
| 200 | {ok: true} |
| 401 | 见 Unauthorized |
| 403 | 见 Forbidden |
| 429 | 见 RateLimited |
| 503 | unavailable:暂时不可用(读凭证、读授权、读库失败)。见 Unavailable |
200 响应正文(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
ok | boolean | 是 | 固定为 true |
tables
多维表只读(必须经视图;个人字段永远不返回)
GET /tables/{table}/views/{view}/rows
经视图列出一张多维表的行(不含个人字段)
需要这张表的范围:租户自建表是 tables.base:read;系统表 sys_<id> 是 tables.<id>:read。表还必须「开放给项目」。缺范围是 403 scope_required(消息里会写出缺的那条,方便你去要)。
后台模式令牌(租户令牌)按视图自己的筛选读行,员工专属视图也可以。客户模式令牌按视图的行规则来(own-read / own-write 只看见断言客户的行)。个人(PII)列永远不返回。
表、视图不存在,或表未开放,或这条令牌进不了这个视图:一律 404 not_found,避免探测。
查询与造物的 data.list 相同:limit(默认 20,1–100)、cursor、sort(字段 id,前面加 - 表示降序,逗号分隔)、filter[字段][运算符]=值。运算符:eq、ne、in、lt、lte、gt、gte、contains、between。只能筛、只能排视图里出现的字段,否则 400 invalid_request。
列表形状是 data + nextCursor。每一行是 {id, version, fields, createdAt},fields 只有视图里的列。
- 认证:
bearer - operationId:
listTableRows
参数
| 名称 | 位置 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
table | path | string | 是 | 表名。系统表以 sys_ 开头;否则是租户自建表 |
view | path | string | 是 | 视图 id |
limit | query | integer | 每页条数,默认 20,最大 100 默认 20最小 1,最大 100 | |
cursor | query | string | 上一页返回的 nextCursor。第一页省略 | |
sort | query | string | 字段 id,逗号分隔;前面加 - 表示降序。只能是视图里的字段。 | |
filter | query | map[string, map[string, string]] | 只按视图里的字段筛选。写成 filter[字段][运算符]=值。 例:filter[status][eq]=active。in 的值用逗号分隔;between 的值是 lo,hi。 |
响应
| 状态 | 说明 |
|---|---|
| 200 | 一页行 |
| 400 | invalid_request:查询或正文读不出来。见 BadRequest |
| 401 | 见 Unauthorized |
| 403 | 见 Forbidden |
| 404 | not_found:不存在,或你不该知道它存在(别人的会员、未开放的表、进不了的视图)。见 NotFound |
| 429 | 见 RateLimited |
| 503 | unavailable:暂时不可用(读凭证、读授权、读库失败)。见 Unavailable |
200 响应正文(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
data | Row[] | 是 | |
data[].id | string | 是 | |
data[].version | integer | 是 | 乐观并发版本 |
data[].fields | object | 是 | |
data[].createdAt | string(date-time) | ||
nextCursor | string,可为 null | 是 |
GET /tables/{table}/views/{view}/rows/{id}
经视图读取多维表的一行
范围和可见性与列表相同。行不在视图里、不是这位客户的、或 id 不存在:404 not_found。个人字段不返回。
- 认证:
bearer - operationId:
getTableRow
参数
| 名称 | 位置 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
table | path | string | 是 | 表名。系统表以 sys_ 开头;否则是租户自建表 |
view | path | string | 是 | 视图 id |
id | path | string | 是 | 行 id |
响应
| 状态 | 说明 |
|---|---|
| 200 | 这一行 |
| 401 | 见 Unauthorized |
| 403 | 见 Forbidden |
| 404 | not_found:不存在,或你不该知道它存在(别人的会员、未开放的表、进不了的视图)。见 NotFound |
| 429 | 见 RateLimited |
| 503 | unavailable:暂时不可用(读凭证、读授权、读库失败)。见 Unavailable |
200 响应正文(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | string | 是 | |
version | integer | 是 | 乐观并发版本 |
fields | object | 是 | |
createdAt | string(date-time) |
customers
客户档案。电话按员工界面打码;customer:write 可按手机号创建或找到,并改姓名与标签
GET /customers
列出组织里的客户(电话已打码)
需要范围 customer:read。租户令牌。列表形状是 data + nextCursor。默认 20 条,最大 100。
phone 按客户中心的查找:整段号码(大陆 11 位或带国家码)走身份查找;也可以是屏幕上打码后仍能看见的数字。updatedSince 是 RFC 3339,只留下 lastTouchAt(没有则 createdAt)不早于该时刻的客户。
phone 是员工列表上的打码形式(与 customer:write 无关;揭开号码仍是员工在客户应用里的 reveal)。tags 是标签 id。不返回明文号码、邮箱、备注(客户中心没有备注字段)。
- 认证:
bearer - operationId:
listCustomers
参数
| 名称 | 位置 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
limit | query | integer | 每页条数,默认 20,最大 100 默认 20最小 1,最大 100 | |
cursor | query | string | 上一页返回的 nextCursor。第一页省略 | |
phone | query | string | 按号码查找(整段或打码后仍可见的数字) | |
updatedSince | query | string(date-time) | RFC 3339 UTC。只列出该时刻及之后有过接触(或新建)的客户 |
响应
| 状态 | 说明 |
|---|---|
| 200 | 一页客户。没有下一页时 nextCursor 为 null |
| 400 | invalid_request:查询或正文读不出来。见 BadRequest |
| 401 | 见 Unauthorized |
| 403 | 见 Forbidden |
| 429 | 见 RateLimited |
| 500 | internal:平台出错。见 Internal |
| 503 | unavailable:暂时不可用(读凭证、读授权、读库失败)。见 Unavailable |
200 响应正文(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
data | Customer[] | 是 | |
data[].id | string | 是 | |
data[].name | string | 是 | |
data[].phone | string | 员工列表上的打码形式。没有电话则省略 | |
data[].stage | string | 是 | lead、customer 或 dormant(按最近接触算) |
data[].tags | string[] | 是 | 标签 id |
data[].createdAt | string(date-time) | 是 | |
data[].lastTouchAt | string(date-time) | 是 | |
data[].identities | CustomerIdentity[] | 详情才有。masked 已打码,没有明文 value | |
data[].identities[].kind | string | 是 | phone、email、openid 等 |
data[].identities[].masked | string | 是 | |
data[].identities[].verified | boolean | 是 | |
nextCursor | string,可为 null | 是 |
POST /customers
按手机号创建客户,或返回已经存在的那位
需要范围 customer:write。租户令牌。phone 必填,否则 422。走客户中心的 Resolve:号码已有客户则 200 并返回那位(不改姓名、不加标签、不发事件);没有则 201 并写入 customer.created。
新建时可选 name、tags(标签 id)。请带 Idempotency-Key。返回的 phone 已打码。
- 认证:
bearer - operationId:
createCustomer
参数
| 名称 | 位置 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
Idempotency-Key | header | string | 除 /oauth/token 外每个 POST 都收。SDK 未传入时会生成 uuid v4。24 小时内同键同正文重放第一次的回答 最短 1,最长 255 |
请求正文(必填)(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
phone | string | 是 | 必填。大陆 11 位或带国家码 |
name | string | 新建时使用。号码已有客户时忽略 | |
tags | string[] | 标签 id。只在新建时加上 |
响应
| 状态 | 说明 |
|---|---|
| 200 | 这个号码已经有客户,返回那位(未改) |
| 201 | 新建的客户 |
| 400 | invalid_request:查询或正文读不出来。见 BadRequest |
| 401 | 见 Unauthorized |
| 403 | 见 Forbidden |
| 409 | 见 Conflict |
| 413 | payload_too_large:正文太大。见 PayloadTooLarge |
| 422 | 见 Unprocessable |
| 429 | 见 RateLimited |
| 500 | internal:平台出错。见 Internal |
| 503 | unavailable:暂时不可用(读凭证、读授权、读库失败)。见 Unavailable |
200 响应正文(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | string | 是 | |
name | string | 是 | |
phone | string | 员工列表上的打码形式。没有电话则省略 | |
stage | string | 是 | lead、customer 或 dormant(按最近接触算) |
tags | string[] | 是 | 标签 id |
createdAt | string(date-time) | 是 | |
lastTouchAt | string(date-time) | 是 | |
identities | CustomerIdentity[] | 详情才有。masked 已打码,没有明文 value | |
identities[].kind | string | 是 | phone、email、openid 等 |
identities[].masked | string | 是 | |
identities[].verified | boolean | 是 |
201 响应正文(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | string | 是 | |
name | string | 是 | |
phone | string | 员工列表上的打码形式。没有电话则省略 | |
stage | string | 是 | lead、customer 或 dormant(按最近接触算) |
tags | string[] | 是 | 标签 id |
createdAt | string(date-time) | 是 | |
lastTouchAt | string(date-time) | 是 | |
identities | CustomerIdentity[] | 详情才有。masked 已打码,没有明文 value | |
identities[].kind | string | 是 | phone、email、openid 等 |
identities[].masked | string | 是 | |
identities[].verified | boolean | 是 |
GET /customers/{id}
读取一位客户(电话已打码)
需要范围 customer:read。不存在是 404 not_found。phone 与 identities[].masked 是打码形式;identities 不含明文 value。揭开号码不是这个接口的事。
- 认证:
bearer - operationId:
getCustomer
参数
| 名称 | 位置 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
id | path | string | 是 | 客户中心 id |
响应
| 状态 | 说明 |
|---|---|
| 200 | 这位客户 |
| 401 | 见 Unauthorized |
| 403 | 见 Forbidden |
| 404 | not_found:不存在,或你不该知道它存在(别人的会员、未开放的表、进不了的视图)。见 NotFound |
| 429 | 见 RateLimited |
| 500 | internal:平台出错。见 Internal |
| 503 | unavailable:暂时不可用(读凭证、读授权、读库失败)。见 Unavailable |
200 响应正文(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | string | 是 | |
name | string | 是 | |
phone | string | 员工列表上的打码形式。没有电话则省略 | |
stage | string | 是 | lead、customer 或 dormant(按最近接触算) |
tags | string[] | 是 | 标签 id |
createdAt | string(date-time) | 是 | |
lastTouchAt | string(date-time) | 是 | |
identities | CustomerIdentity[] | 详情才有。masked 已打码,没有明文 value | |
identities[].kind | string | 是 | phone、email、openid 等 |
identities[].masked | string | 是 | |
identities[].verified | boolean | 是 |
PATCH /customers/{id}
改客户的姓名或标签
需要范围 customer:write。可改 name、tags(标签 id 列表;空数组表示去掉全部可改的标签)。带上 phone 是 422(号码不能改)。带上 notes 是 422(客户中心没有备注字段)。其它未知字段忽略。
改了字段时写入 customer.updated。企业微信等外来标签不能在这里摘掉,会 422。
- 认证:
bearer - operationId:
updateCustomer
参数
| 名称 | 位置 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
id | path | string | 是 | 客户中心 id |
请求正文(必填)(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
name | string | ||
tags | string[] | 标签 id。空数组表示去掉全部可改的标签 |
响应
| 状态 | 说明 |
|---|---|
| 200 | 改完后的客户 |
| 400 | invalid_request:查询或正文读不出来。见 BadRequest |
| 401 | 见 Unauthorized |
| 403 | 见 Forbidden |
| 404 | not_found:不存在,或你不该知道它存在(别人的会员、未开放的表、进不了的视图)。见 NotFound |
| 422 | 见 Unprocessable |
| 429 | 见 RateLimited |
| 500 | internal:平台出错。见 Internal |
| 503 | unavailable:暂时不可用(读凭证、读授权、读库失败)。见 Unavailable |
200 响应正文(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | string | 是 | |
name | string | 是 | |
phone | string | 员工列表上的打码形式。没有电话则省略 | |
stage | string | 是 | lead、customer 或 dormant(按最近接触算) |
tags | string[] | 是 | 标签 id |
createdAt | string(date-time) | 是 | |
lastTouchAt | string(date-time) | 是 | |
identities | CustomerIdentity[] | 详情才有。masked 已打码,没有明文 value | |
identities[].kind | string | 是 | phone、email、openid 等 |
identities[].masked | string | 是 | |
identities[].verified | boolean | 是 |
org
组织通讯录(成员与组织名)。电话与邮箱只在同时持有 org.contacts:read 时返回
GET /members
列出组织通讯录里的成员
需要范围 org.members:read。租户令牌。返回 {data:[{id, name, title, departments, status}], nextCursor}。默认 20 条,最大 100。按账号 id。
email 与 phone 只在同时持有 org.contacts:read 时出现。生产环境的通讯录是在职的人(不含机器账号);状态以目录为准。
- 认证:
bearer - operationId:
listMembers
参数
| 名称 | 位置 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
limit | query | integer | 每页条数,默认 20,最大 100 默认 20最小 1,最大 100 | |
cursor | query | string | 上一页返回的 nextCursor。第一页省略 |
响应
| 状态 | 说明 |
|---|---|
| 200 | 一页成员。没有下一页时 nextCursor 为 null |
| 400 | invalid_request:查询或正文读不出来。见 BadRequest |
| 401 | 见 Unauthorized |
| 403 | 见 Forbidden |
| 429 | 见 RateLimited |
| 500 | internal:平台出错。见 Internal |
| 503 | unavailable:暂时不可用(读凭证、读授权、读库失败)。见 Unavailable |
200 响应正文(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
data | Member[] | 是 | |
data[].id | string | 是 | 账号 id |
data[].name | string | 是 | |
data[].title | string | 是 | 职务。没有则是空字符串 |
data[].departments | string[] | 是 | 部门名称,主部门在前 |
data[].status | string | 是 | 目录里的状态,如 active、invited |
data[].email | string | ||
data[].phone | string | ||
nextCursor | string,可为 null | 是 |
GET /org
当前令牌所在组织的 id 与名称
需要范围 org.members:read。返回 {tenant, name}。名称来自组织档案;没有名称时是空字符串。
- 认证:
bearer - operationId:
getOrg
响应
| 状态 | 说明 |
|---|---|
| 200 | {tenant, name} |
| 401 | 见 Unauthorized |
| 403 | 见 Forbidden |
| 429 | 见 RateLimited |
| 500 | internal:平台出错。见 Internal |
| 503 | unavailable:暂时不可用(读凭证、读授权、读库失败)。见 Unavailable |
200 响应正文(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
tenant | string | 是 | |
name | string | 是 | 组织显示名。没有则是空字符串 |
me
会员客户模式:这位客户自己的会员、课包、预约和待确认核销。新地址是会员应用自己的域名 https://membership.apps.{domain}/open/v1/me…(同一组路由、同一把客户令牌);api.{domain} 上的旧地址照常应答到 2027-05-01,应答带 Deprecation、Sunset 与指向新地址的 Link: rel=successor-version
GET /me
这把客户令牌对应的会员(id、编号、姓名、状态)
需要范围 membership.me:read。只要客户令牌(租户令牌是 403 customer_token_required)。读操作不检查断言有多老。
members 是这个组织里 customer_id 为令牌客户的全部会员,按创建时间,不含已合并、已关闭。一个号码在多家店可以是多条会员,只要客户 id 相同。没有会员时是 []。
不返回:电话、备注、标签、教练、核销码、金额、病历。
- 认证:
bearer - operationId:
listMeMembers
响应
| 状态 | 说明 |
|---|---|
| 200 | {members: [MeMember]} |
| 401 | 见 Unauthorized |
| 403 | 见 Forbidden |
| 429 | 见 RateLimited |
| 500 | internal:平台出错。见 Internal |
| 503 | unavailable:暂时不可用(读凭证、读授权、读库失败)。见 Unavailable |
200 响应正文(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
members | MeMember[] | 是 | |
members[].id | string | 是 | |
members[].no | string | 是 | 会员编号 |
members[].name | string | 是 | |
members[].status | string | 是 | merged 和 closed 不会出现 可选值: active、frozen、expired |
GET /me/packages
这位客户名下会员的有效或冻结课包
需要范围 membership.me:read。认证与 GET /me 相同。
只列出存储状态为 active 或 frozen 的课包(已退、作废、已转让不出现)。status 是读的时候算出来的,存储仍是 active 时也可能是 not_started、expired、used_up。unlimited 仅在 kind 为 period(期卡)时为 true。courses 为空表示适用全部课程。
不返回:备注、金额、销售、流水、券、每日/每周上限。
- 认证:
bearer - operationId:
listMePackages
响应
| 状态 | 说明 |
|---|---|
| 200 | {packages: [MePackage]} |
| 401 | 见 Unauthorized |
| 403 | 见 Forbidden |
| 429 | 见 RateLimited |
| 500 | internal:平台出错。见 Internal |
| 503 | unavailable:暂时不可用(读凭证、读授权、读库失败)。见 Unavailable |
200 响应正文(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
packages | MePackage[] | 是 | |
packages[].id | string | 是 | |
packages[].memberId | string | 是 | |
packages[].name | string | 是 | |
packages[].kind | string | 是 | 可选值:count、period、count_period |
packages[].unlimited | boolean | 是 | 仅 kind 为 period 时为 true |
packages[].totalTimes | integer | 是 | |
packages[].remaining | integer | 是 | |
packages[].courses | string[] | 是 | 课程 id。空表示全部课程 |
packages[].startsOn | string | YYYY-MM-DD。未设则省略 | |
packages[].endsOn | string | YYYY-MM-DD。未设则省略 | |
packages[].state | string | 是 | 存储状态。只列出这两种 可选值: active、frozen |
packages[].status | string | 是 | 读的时候算出来 可选值: active、not_started、frozen、expired、used_up |
GET /me/bookings
这位客户在一个日期窗口里的预约,并给出此刻取消要扣几次
需要范围 membership.me:read。from、to 是门店时区的日期 YYYY-MM-DD,两端都包含。省略 from 为昨天;省略 to 为 from 加 62 天。to 早于 from、不是日期、或跨度超过 124 天:422 validation_failed。
status 有值时按精确状态过滤(未知值得到空列表,不是 422)。最多 200 条,按开始时间再按 id。
cancelCharge 只在 status 为 booked 时计算(免费取消窗口内是 0,否则是门店的晚取消扣次,0 或 1)。coachName 是员工显示名,没有则省略。
不返回:备注、会员姓名或编号、教练 id、金额、颜色、课包 id、核销 id。
- 认证:
bearer - operationId:
listMeBookings
参数
| 名称 | 位置 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
from | query | string | YYYY-MM-DD,门店时区。默认昨天 | |
to | query | string | YYYY-MM-DD,包含。默认 from 加 62 天。跨度最多 124 天 | |
status | query | string | 精确预约状态。未知值得到空列表 |
响应
| 状态 | 说明 |
|---|---|
| 200 | {bookings: [MeBooking]} |
| 401 | 见 Unauthorized |
| 403 | 见 Forbidden |
| 422 | 见 Unprocessable |
| 429 | 见 RateLimited |
| 500 | internal:平台出错。见 Internal |
| 503 | unavailable:暂时不可用(读凭证、读授权、读库失败)。见 Unavailable |
200 响应正文(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
bookings | MeBooking[] | 是 | |
bookings[].id | string | 是 | |
bookings[].memberId | string | 是 | |
bookings[].sessionId | string | 是 | |
bookings[].courseName | string | 是 | |
bookings[].coachName | string | 员工显示名。没有则省略 | |
bookings[].startsAt | string(date-time) | 是 | |
bookings[].endsAt | string(date-time) | 是 | |
bookings[].room | string | 是 | 总是出现,可能是空字符串 |
bookings[].status | string | 是 | 可选值:booked、waitlist、attended、completed、cancelled、no_show |
bookings[].cancelCharge | integer | 是 | 现在取消要扣几次。非 booked 为 0。门店设置为 0 或 1 |
POST /me/bookings
以这位客户的一名会员预约一节未开始的开放课(满员进候补)
需要范围 membership.booking:write。这是写操作:客户断言超过 10 分钟是 401 assertion_stale,请重新断言。
正文 {memberId, sessionId},都必填,否则 422 validation_failed。memberId 必须是 GET /me 里的会员,否则 404 not_found。课次不存在是 422,不是 404。走门店自己的预约规则(会员状态、课次开放且未开始、预约窗口、时间重叠、可用课包、容量)。满员时 201 且 status 为 waitlist。
已经预约过是 422;并发下唯一约束冲突是 409 conflict。请带 Idempotency-Key。
- 认证:
bearer - operationId:
createMeBooking
参数
| 名称 | 位置 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
Idempotency-Key | header | string | 除 /oauth/token 外每个 POST 都收。SDK 未传入时会生成 uuid v4。24 小时内同键同正文重放第一次的回答 最短 1,最长 255 |
请求正文(必填)(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
memberId | string | 是 | GET /me 返回的会员 id |
sessionId | string | 是 | GET /me/sessions 返回的课次 id |
响应
| 状态 | 说明 |
|---|---|
| 201 | 预约本身(MeBooking),不包裹。status 为 booked 或 waitlist |
| 400 | invalid_request:查询或正文读不出来。见 BadRequest |
| 401 | 见 Unauthorized |
| 403 | 见 Forbidden |
| 404 | not_found:不存在,或你不该知道它存在(别人的会员、未开放的表、进不了的视图)。见 NotFound |
| 409 | 见 Conflict |
| 413 | payload_too_large:正文太大。见 PayloadTooLarge |
| 422 | 见 Unprocessable |
| 429 | 见 RateLimited |
| 500 | internal:平台出错。见 Internal |
| 503 | unavailable:暂时不可用(读凭证、读授权、读库失败)。见 Unavailable |
201 响应正文(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | string | 是 | |
memberId | string | 是 | |
sessionId | string | 是 | |
courseName | string | 是 | |
coachName | string | 员工显示名。没有则省略 | |
startsAt | string(date-time) | 是 | |
endsAt | string(date-time) | 是 | |
room | string | 是 | 总是出现,可能是空字符串 |
status | string | 是 | 可选值:booked、waitlist、attended、completed、cancelled、no_show |
cancelCharge | integer | 是 | 现在取消要扣几次。非 booked 为 0。门店设置为 0 或 1 |
POST /me/bookings/{id}/cancel
取消这位客户的预约;晚取消且要扣次时必须 acceptCharge
需要范围 membership.booking:write。写操作(10 分钟后 assertion_stale)。{id} 必须是这位客户的预约,否则 404 not_found。
正文可选 {acceptCharge, reason}。可以不带正文。reason 最多 200 字,否则 422 validation_failed。
预约是 booked 且现在取消要扣次(与列表里的 cancelCharge 相同)且 acceptCharge 不是 true:409 conflict,什么都不取消。候补的 cancelCharge 为 0,不需要 acceptCharge。已到场或已完成不能取消(422)。
200 里的 charged 是实际扣掉的次数,即使带了 acceptCharge: true 也可能是 0(没有可扣的课包,或期卡)。
- 认证:
bearer - operationId:
cancelMeBooking
参数
| 名称 | 位置 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
id | path | string | 是 | 预约 id |
Idempotency-Key | header | string | 除 /oauth/token 外每个 POST 都收。SDK 未传入时会生成 uuid v4。24 小时内同键同正文重放第一次的回答 最短 1,最长 255 |
请求正文(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
acceptCharge | boolean | 列表里 cancelCharge 大于 0 时必须为 true | |
reason | string | 最多 200 字 |
响应
| 状态 | 说明 |
|---|---|
| 200 | {cancelled: true, charged}。charged 是实际扣次,可以为 0 |
| 400 | invalid_request:查询或正文读不出来。见 BadRequest |
| 401 | 见 Unauthorized |
| 403 | 见 Forbidden |
| 404 | not_found:不存在,或你不该知道它存在(别人的会员、未开放的表、进不了的视图)。见 NotFound |
| 409 | 见 Conflict |
| 413 | payload_too_large:正文太大。见 PayloadTooLarge |
| 422 | 见 Unprocessable |
| 429 | 见 RateLimited |
| 500 | internal:平台出错。见 Internal |
| 503 | unavailable:暂时不可用(读凭证、读授权、读库失败)。见 Unavailable |
200 响应正文(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
cancelled | boolean | 是 | 固定为 true |
charged | integer | 是 | 实际扣次,可以为 0 |
GET /me/sessions
这位客户还能预约的开放课次,尚未开始,最多 31 天
需要范围 membership.me:read。from、to 是门店时区日期,两端包含。省略 from 为今天;省略 to 为 from 加 6 天。日期不合法、to 早于 from、或超过 31 天:422 validation_failed。
只返回 status=open 且开始时间仍在未来的课次(今天已经开始的不算)。可选 course 为课程 id(未知 id 得到空列表)。最多 500 条,按开始时间再按课程名。
spotsLeft 为 max(0, 容量 − booked/attended/completed)。0 表示再预约会进候补。候补不占 spotsLeft。
- 认证:
bearer - operationId:
listMeSessions
参数
| 名称 | 位置 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
from | query | string | YYYY-MM-DD。默认今天(门店时区) | |
to | query | string | YYYY-MM-DD,包含。默认 from 加 6 天。跨度最多 31 天 | |
course | query | string | 课程 id。精确过滤 |
响应
| 状态 | 说明 |
|---|---|
| 200 | {sessions: [MeSession]} |
| 401 | 见 Unauthorized |
| 403 | 见 Forbidden |
| 422 | 见 Unprocessable |
| 429 | 见 RateLimited |
| 500 | internal:平台出错。见 Internal |
| 503 | unavailable:暂时不可用(读凭证、读授权、读库失败)。见 Unavailable |
200 响应正文(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
sessions | MeSession[] | 是 | |
sessions[].id | string | 是 | |
sessions[].courseId | string | 是 | |
sessions[].courseName | string | 是 | |
sessions[].coachName | string | 员工显示名。没有则省略 | |
sessions[].startsAt | string(date-time) | 是 | |
sessions[].endsAt | string(date-time) | 是 | |
sessions[].room | string | 是 | 总是出现,可能是空字符串 |
sessions[].spotsLeft | integer | 是 | max(0, 容量 − booked/attended/completed)。0 表示再预约会进候补 |
GET /me/redemption-requests
前台请这位客户确认的核销(轮询;没有推送)
需要范围 membership.me:read。没有推送,小程序轮询这一条。
state 省略或 pending:仍待确认且未过期。all:最近 30 天,最新在前;过了 expiresAt 的 pending 读出来是 expired(当场计算,后台没有定时任务)。其他 state 是 422 validation_failed。最多 100 条。
不返回:备注、预约 id、课程 id、课包 id、会员编号、谁发起、谁决定、拒绝原因、核销 id。
- 认证:
bearer - operationId:
listMeRedemptionRequests
参数
| 名称 | 位置 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
state | query | string | 默认 pending(未过期)。all 是最近 30 天,含计算出来的 expired 可选值: pending、all |
响应
| 状态 | 说明 |
|---|---|
| 200 | {requests: [MeRedemptionRequest]} |
| 401 | 见 Unauthorized |
| 403 | 见 Forbidden |
| 422 | 见 Unprocessable |
| 429 | 见 RateLimited |
| 500 | internal:平台出错。见 Internal |
| 503 | unavailable:暂时不可用(读凭证、读授权、读库失败)。见 Unavailable |
200 响应正文(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
requests | MeRedemptionRequest[] | 是 | |
requests[].id | string | 是 | |
requests[].memberId | string | 是 | |
requests[].memberName | string | 是 | |
requests[].courseName | string | 课程没有名字则省略 | |
requests[].times | integer | 是 | |
requests[].state | string | 是 | expired 是读的时候算的 可选值: pending、confirmed、declined、cancelled、expired |
requests[].expiresAt | string(date-time) | 是 | |
requests[].createdAt | string(date-time) | 是 |
POST /me/redemption-requests/{id}/confirm
确认前台的核销申请;次数只在此刻扣,记在发起的员工名下
需要范围 membership.redemption:confirm。写操作(10 分钟后 assertion_stale)。没有正文。
{id} 必须是这位客户的申请,否则 404。只有仍 pending(且未过期)能确认;否则 409 conflict。第二次确认也是 409,不会把第一次的 JSON 再给一遍。
扣次就是前台自己的核销,同一事务,幂等键 rr:<申请 id>,核销记在发起员工名下。200 是 {confirmed: true, remaining, notices}。remaining 是扣完后的课包余额;期卡扣 0,所以这不是到店次数。notices 可能为空。
与前台核销相同的拒绝是 422 validation_failed(候补、已取消、缺席、课次取消、不是上课当天、超过 7 天、会员冻结/过期/关闭、课包不可用、剩余次数不足)。
- 认证:
bearer - operationId:
confirmMeRedemption
参数
| 名称 | 位置 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
id | path | string | 是 | 核销申请 id |
Idempotency-Key | header | string | 除 /oauth/token 外每个 POST 都收。SDK 未传入时会生成 uuid v4。24 小时内同键同正文重放第一次的回答 最短 1,最长 255 |
响应
| 状态 | 说明 |
|---|---|
| 200 | {confirmed: true, remaining, notices} |
| 401 | 见 Unauthorized |
| 403 | 见 Forbidden |
| 404 | not_found:不存在,或你不该知道它存在(别人的会员、未开放的表、进不了的视图)。见 NotFound |
| 409 | 见 Conflict |
| 422 | 见 Unprocessable |
| 429 | 见 RateLimited |
| 500 | internal:平台出错。见 Internal |
| 503 | unavailable:暂时不可用(读凭证、读授权、读库失败)。见 Unavailable |
200 响应正文(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
confirmed | boolean | 是 | 固定为 true |
remaining | integer | 是 | 扣完后的课包余额。期卡扣 0 |
notices | string[] | 是 | 可能为空。否则是用完、只剩 N 次、今天到期、N 天后到期 |
POST /me/redemption-requests/{id}/decline
拒绝前台的核销申请;不扣次
需要范围 membership.redemption:confirm。写操作(10 分钟后 assertion_stale)。
正文可选 {reason},最多 200 字,否则 422 validation_failed。可以不带正文。404 / 409 与确认相同(只有仍 pending 的申请)。不扣次。
- 认证:
bearer - operationId:
declineMeRedemption
参数
| 名称 | 位置 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
id | path | string | 是 | 核销申请 id |
Idempotency-Key | header | string | 除 /oauth/token 外每个 POST 都收。SDK 未传入时会生成 uuid v4。24 小时内同键同正文重放第一次的回答 最短 1,最长 255 |
请求正文(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
reason | string | 最多 200 字 |
响应
| 状态 | 说明 |
|---|---|
| 200 | {declined: true} |
| 400 | invalid_request:查询或正文读不出来。见 BadRequest |
| 401 | 见 Unauthorized |
| 403 | 见 Forbidden |
| 404 | not_found:不存在,或你不该知道它存在(别人的会员、未开放的表、进不了的视图)。见 NotFound |
| 409 | 见 Conflict |
| 413 | payload_too_large:正文太大。见 PayloadTooLarge |
| 422 | 见 Unprocessable |
| 429 | 见 RateLimited |
| 500 | internal:平台出错。见 Internal |
| 503 | unavailable:暂时不可用(读凭证、读授权、读库失败)。见 Unavailable |
200 响应正文(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
declined | boolean | 是 | 固定为 true |
事件(Webhooks)
平台向你登记的 https 地址推送的事件。每个事件是一个 POST 请求。
app.installed
一个组织装上了这个应用
只发给这个应用自己的开发者订阅,不需要范围。data 是 {version, scopes}。信封带 tenant(正式环境也会带:租户装了你的应用,你要用这个 id 去换该组织的令牌)、environment、app。
用 X-Kwork-Signature: t=<unix>,v1=<hex HMAC-SHA256(secret, "<t>.<body>")> 验签,拒绝过期的时间戳。另有 X-Kwork-Event、X-Kwork-Delivery。
- operationId:
receiveAppInstalled
参数
| 名称 | 位置 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
X-Kwork-Signature | header | string | 是 | t=<unix>,v1=<hex HMAC-SHA256(secret, "<t>.<body>")> |
X-Kwork-Event | header | string | 是 | 事件类型,与正文 type 相同 |
X-Kwork-Delivery | header | string | 是 | 这一次投递的 id(重投会变;事件 id 在正文里,重试不变) |
请求正文(必填)(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | string | 是 | evt_…,同一事件每次重试、每个订阅都相同,按它去重 |
type | string | 是 | 固定为 "app.installed" |
createdAt | string(date-time) | 是 | |
apiVersion | string | 是 | 固定为 "2026-10-28" |
tenant | string | 租户范围的事件有。发给商店应用的正式事件也会带(租户装了你的应用) | |
environment | string | 可选值:test、live | |
app | string | ||
data | AppInstalledData | 是 | |
data.version | string | 是 | |
data.scopes | string[] | 是 |
响应
| 状态 | 说明 |
|---|---|
| 200 | 接收方已收下。非 2xx 会按退避重投 |
app.uninstalled
一个组织卸载了这个应用
只发给这个应用自己的开发者订阅,不需要范围。data 是 {}。验签同 app.installed。
- operationId:
receiveAppUninstalled
参数
| 名称 | 位置 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
X-Kwork-Signature | header | string | 是 | t=<unix>,v1=<hex HMAC-SHA256(secret, "<t>.<body>")> |
X-Kwork-Event | header | string | 是 | 事件类型,与正文 type 相同 |
X-Kwork-Delivery | header | string | 是 | 这一次投递的 id(重投会变;事件 id 在正文里,重试不变) |
请求正文(必填)(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | string | 是 | evt_…,同一事件每次重试、每个订阅都相同,按它去重 |
type | string | 是 | 固定为 "app.uninstalled" |
createdAt | string(date-time) | 是 | |
apiVersion | string | 是 | 固定为 "2026-10-28" |
tenant | string | 租户范围的事件有。发给商店应用的正式事件也会带(租户装了你的应用) | |
environment | string | 可选值:test、live | |
app | string | ||
data | AppUninstalledData | 是 |
响应
| 状态 | 说明 |
|---|---|
| 200 | 接收方已收下 |
app.upgraded
一个组织把这个应用升到了新版本
只发给这个应用自己的开发者订阅。data 是 {from, to, scopes}。
- operationId:
receiveAppUpgraded
参数
| 名称 | 位置 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
X-Kwork-Signature | header | string | 是 | t=<unix>,v1=<hex HMAC-SHA256(secret, "<t>.<body>")> |
X-Kwork-Event | header | string | 是 | 事件类型,与正文 type 相同 |
X-Kwork-Delivery | header | string | 是 | 这一次投递的 id(重投会变;事件 id 在正文里,重试不变) |
请求正文(必填)(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | string | 是 | evt_…,同一事件每次重试、每个订阅都相同,按它去重 |
type | string | 是 | 固定为 "app.upgraded" |
createdAt | string(date-time) | 是 | |
apiVersion | string | 是 | 固定为 "2026-10-28" |
tenant | string | 租户范围的事件有。发给商店应用的正式事件也会带(租户装了你的应用) | |
environment | string | 可选值:test、live | |
app | string | ||
data | AppUpgradedData | 是 | |
data.from | string | 是 | |
data.to | string | 是 | |
data.scopes | string[] | 是 |
响应
| 状态 | 说明 |
|---|---|
| 200 | 接收方已收下 |
app.scopes_changed
管理员改了同意给这个应用的权限范围
只发给这个应用自己的开发者订阅。data 是 {scopes, added, removed}。
- operationId:
receiveAppScopesChanged
参数
| 名称 | 位置 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
X-Kwork-Signature | header | string | 是 | t=<unix>,v1=<hex HMAC-SHA256(secret, "<t>.<body>")> |
X-Kwork-Event | header | string | 是 | 事件类型,与正文 type 相同 |
X-Kwork-Delivery | header | string | 是 | 这一次投递的 id(重投会变;事件 id 在正文里,重试不变) |
请求正文(必填)(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | string | 是 | evt_…,同一事件每次重试、每个订阅都相同,按它去重 |
type | string | 是 | 固定为 "app.scopes_changed" |
createdAt | string(date-time) | 是 | |
apiVersion | string | 是 | 固定为 "2026-10-28" |
tenant | string | 租户范围的事件有。发给商店应用的正式事件也会带(租户装了你的应用) | |
environment | string | 可选值:test、live | |
app | string | ||
data | AppScopesChangedData | 是 | |
data.scopes | string[] | 是 | |
data.added | string[] | 是 | |
data.removed | string[] | 是 |
响应
| 状态 | 说明 |
|---|---|
| 200 | 接收方已收下 |
dev.version.approved
平台审核通过了一个版本,可以上架了
开发者订阅,不需要租户授予的范围。data 是 {app, version, decision},decision 为 approved。同一审核结果的 id 在每次重试、每个订阅上都相同,接收方按 id 去重。
- operationId:
receiveDevVersionApproved
参数
| 名称 | 位置 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
X-Kwork-Signature | header | string | 是 | t=<unix>,v1=<hex HMAC-SHA256(secret, "<t>.<body>")> |
X-Kwork-Event | header | string | 是 | 事件类型,与正文 type 相同 |
X-Kwork-Delivery | header | string | 是 | 这一次投递的 id(重投会变;事件 id 在正文里,重试不变) |
请求正文(必填)(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | string | 是 | evt_…,同一事件每次重试、每个订阅都相同,按它去重 |
type | string | 是 | 固定为 "dev.version.approved" |
createdAt | string(date-time) | 是 | |
apiVersion | string | 是 | 固定为 "2026-10-28" |
tenant | string | 租户范围的事件有。发给商店应用的正式事件也会带(租户装了你的应用) | |
environment | string | 可选值:test、live | |
app | string | ||
data | DevVersionData | 是 | |
data.app | string | 是 | |
data.version | string | 是 | |
data.decision | string | 是 | 可选值:approved、rejected |
data.reason | string | 驳回时的审核意见 |
响应
| 状态 | 说明 |
|---|---|
| 200 | 接收方已收下 |
dev.version.rejected
平台审核驳回了一个版本
开发者订阅。data 是 {app, version, decision, reason?},decision 为 rejected,reason 是审核意见(改好后重新提交)。
- operationId:
receiveDevVersionRejected
参数
| 名称 | 位置 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
X-Kwork-Signature | header | string | 是 | t=<unix>,v1=<hex HMAC-SHA256(secret, "<t>.<body>")> |
X-Kwork-Event | header | string | 是 | 事件类型,与正文 type 相同 |
X-Kwork-Delivery | header | string | 是 | 这一次投递的 id(重投会变;事件 id 在正文里,重试不变) |
请求正文(必填)(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | string | 是 | evt_…,同一事件每次重试、每个订阅都相同,按它去重 |
type | string | 是 | 固定为 "dev.version.rejected" |
createdAt | string(date-time) | 是 | |
apiVersion | string | 是 | 固定为 "2026-10-28" |
tenant | string | 租户范围的事件有。发给商店应用的正式事件也会带(租户装了你的应用) | |
environment | string | 可选值:test、live | |
app | string | ||
data | DevVersionData | 是 | |
data.app | string | 是 | |
data.version | string | 是 | |
data.decision | string | 是 | 可选值:approved、rejected |
data.reason | string | 驳回时的审核意见 |
响应
| 状态 | 说明 |
|---|---|
| 200 | 接收方已收下 |
membership.redeem_request.created
前台请客户确认一次核销
需要范围 membership.redemption:confirm。租户集成订阅与商店应用的开发者订阅(按安装且授予了该范围的组织)都会收到。
data 含 customer(客户中心 id)、member、request、course(课程名)、times、expiresAt。没有电话、没有姓名。客户该去小程序里确认。
- operationId:
receiveMembershipRedeemRequestCreated
参数
| 名称 | 位置 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
X-Kwork-Signature | header | string | 是 | t=<unix>,v1=<hex HMAC-SHA256(secret, "<t>.<body>")> |
X-Kwork-Event | header | string | 是 | 事件类型,与正文 type 相同 |
X-Kwork-Delivery | header | string | 是 | 这一次投递的 id(重投会变;事件 id 在正文里,重试不变) |
请求正文(必填)(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | string | 是 | evt_…,同一事件每次重试、每个订阅都相同,按它去重 |
type | string | 是 | 固定为 "membership.redeem_request.created" |
createdAt | string(date-time) | 是 | |
apiVersion | string | 是 | 固定为 "2026-10-28" |
tenant | string | 租户范围的事件有。发给商店应用的正式事件也会带(租户装了你的应用) | |
environment | string | 可选值:test、live | |
app | string | ||
data | RedeemRequestCreatedData | 是 | |
data.customer | string | 是 | 客户中心 id |
data.member | string | 是 | |
data.request | string | 是 | |
data.course | string | 课程名 | |
data.times | integer | 是 | |
data.expiresAt | string(date-time) |
响应
| 状态 | 说明 |
|---|---|
| 200 | 接收方已收下 |
membership.redeem_request.cancelled
前台取消了请客户确认的核销
需要范围 membership.redemption:confirm。data 含 customer、member、request。客户不用再确认。
- operationId:
receiveMembershipRedeemRequestCancelled
参数
| 名称 | 位置 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
X-Kwork-Signature | header | string | 是 | t=<unix>,v1=<hex HMAC-SHA256(secret, "<t>.<body>")> |
X-Kwork-Event | header | string | 是 | 事件类型,与正文 type 相同 |
X-Kwork-Delivery | header | string | 是 | 这一次投递的 id(重投会变;事件 id 在正文里,重试不变) |
请求正文(必填)(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | string | 是 | evt_…,同一事件每次重试、每个订阅都相同,按它去重 |
type | string | 是 | 固定为 "membership.redeem_request.cancelled" |
createdAt | string(date-time) | 是 | |
apiVersion | string | 是 | 固定为 "2026-10-28" |
tenant | string | 租户范围的事件有。发给商店应用的正式事件也会带(租户装了你的应用) | |
environment | string | 可选值:test、live | |
app | string | ||
data | RedeemRequestCancelledData | 是 | |
data.customer | string | 是 | |
data.member | string | 是 | |
data.request | string | 是 |
响应
| 状态 | 说明 |
|---|---|
| 200 | 接收方已收下 |
membership.booking.created
客户有了新的预约或候补
需要范围 membership.me:read。前台或客户自己预约都会发。data 含 customer、member、booking、session、course(课程名)、startsAt、status(booked 或 waitlist)。
- operationId:
receiveMembershipBookingCreated
参数
| 名称 | 位置 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
X-Kwork-Signature | header | string | 是 | t=<unix>,v1=<hex HMAC-SHA256(secret, "<t>.<body>")> |
X-Kwork-Event | header | string | 是 | 事件类型,与正文 type 相同 |
X-Kwork-Delivery | header | string | 是 | 这一次投递的 id(重投会变;事件 id 在正文里,重试不变) |
请求正文(必填)(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | string | 是 | evt_…,同一事件每次重试、每个订阅都相同,按它去重 |
type | string | 是 | 固定为 "membership.booking.created" |
createdAt | string(date-time) | 是 | |
apiVersion | string | 是 | 固定为 "2026-10-28" |
tenant | string | 租户范围的事件有。发给商店应用的正式事件也会带(租户装了你的应用) | |
environment | string | 可选值:test、live | |
app | string | ||
data | BookingCreatedData | 是 | |
data.customer | string | 是 | |
data.member | string | 是 | |
data.booking | string | 是 | |
data.session | string | 是 | |
data.course | string | 是 | 课程名 |
data.startsAt | string(date-time) | 是 | |
data.status | string | 是 | booked 或 waitlist |
响应
| 状态 | 说明 |
|---|---|
| 200 | 接收方已收下 |
membership.booking.cancelled
客户的预约或候补被取消
需要范围 membership.me:read。前台、客户自己或课次取消都会发。data 含 customer、member、booking、session、course、charged(实际扣次)。
- operationId:
receiveMembershipBookingCancelled
参数
| 名称 | 位置 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
X-Kwork-Signature | header | string | 是 | t=<unix>,v1=<hex HMAC-SHA256(secret, "<t>.<body>")> |
X-Kwork-Event | header | string | 是 | 事件类型,与正文 type 相同 |
X-Kwork-Delivery | header | string | 是 | 这一次投递的 id(重投会变;事件 id 在正文里,重试不变) |
请求正文(必填)(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | string | 是 | evt_…,同一事件每次重试、每个订阅都相同,按它去重 |
type | string | 是 | 固定为 "membership.booking.cancelled" |
createdAt | string(date-time) | 是 | |
apiVersion | string | 是 | 固定为 "2026-10-28" |
tenant | string | 租户范围的事件有。发给商店应用的正式事件也会带(租户装了你的应用) | |
environment | string | 可选值:test、live | |
app | string | ||
data | BookingCancelledData | 是 | |
data.customer | string | 是 | |
data.member | string | 是 | |
data.booking | string | 是 | |
data.session | string | 是 | |
data.course | string | 是 | |
data.charged | integer | 是 |
响应
| 状态 | 说明 |
|---|---|
| 200 | 接收方已收下 |
membership.redemption.created
客户有一次核销(到店核销,或客户确认了申请)
需要范围 membership.me:read。data 含 customer、member、redemption、course、times、remaining(扣完后的课包余额)。
- operationId:
receiveMembershipRedemptionCreated
参数
| 名称 | 位置 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
X-Kwork-Signature | header | string | 是 | t=<unix>,v1=<hex HMAC-SHA256(secret, "<t>.<body>")> |
X-Kwork-Event | header | string | 是 | 事件类型,与正文 type 相同 |
X-Kwork-Delivery | header | string | 是 | 这一次投递的 id(重投会变;事件 id 在正文里,重试不变) |
请求正文(必填)(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | string | 是 | evt_…,同一事件每次重试、每个订阅都相同,按它去重 |
type | string | 是 | 固定为 "membership.redemption.created" |
createdAt | string(date-time) | 是 | |
apiVersion | string | 是 | 固定为 "2026-10-28" |
tenant | string | 租户范围的事件有。发给商店应用的正式事件也会带(租户装了你的应用) | |
environment | string | 可选值:test、live | |
app | string | ||
data | RedemptionCreatedData | 是 | |
data.customer | string | 是 | |
data.member | string | 是 | |
data.redemption | string | 是 | |
data.course | string | ||
data.times | integer | 是 | |
data.remaining | integer | 是 |
响应
| 状态 | 说明 |
|---|---|
| 200 | 接收方已收下 |
customer.created
组织里新建了一位客户
需要范围 customer:read。本批只在开放接口 POST /customers 新建时发出(员工在客户应用里新建不走这条)。
data 是 {customer, id}(同一客户中心 id),没有电话、没有姓名。
- operationId:
receiveCustomerCreated
参数
| 名称 | 位置 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
X-Kwork-Signature | header | string | 是 | t=<unix>,v1=<hex HMAC-SHA256(secret, "<t>.<body>")> |
X-Kwork-Event | header | string | 是 | 事件类型,与正文 type 相同 |
X-Kwork-Delivery | header | string | 是 | 这一次投递的 id(重投会变;事件 id 在正文里,重试不变) |
请求正文(必填)(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | string | 是 | evt_…,同一事件每次重试、每个订阅都相同,按它去重 |
type | string | 是 | 固定为 "customer.created" |
createdAt | string(date-time) | 是 | |
apiVersion | string | 是 | 固定为 "2026-10-28" |
tenant | string | 租户范围的事件有。发给商店应用的正式事件也会带(租户装了你的应用) | |
environment | string | 可选值:test、live | |
app | string | ||
data | CustomerCreatedData | 是 | |
data.customer | string | 是 | 客户中心 id |
data.id | string | 是 | 与 customer 相同 |
响应
| 状态 | 说明 |
|---|---|
| 200 | 接收方已收下 |
customer.updated
一位客户的姓名或标签改了
需要范围 customer:read。本批只在开放接口 PATCH /customers/{id} 改了字段时发出。
data 是 {customer, id, fields},fields 为 name、tags 中实际改过的项。没有电话、没有姓名。
- operationId:
receiveCustomerUpdated
参数
| 名称 | 位置 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
X-Kwork-Signature | header | string | 是 | t=<unix>,v1=<hex HMAC-SHA256(secret, "<t>.<body>")> |
X-Kwork-Event | header | string | 是 | 事件类型,与正文 type 相同 |
X-Kwork-Delivery | header | string | 是 | 这一次投递的 id(重投会变;事件 id 在正文里,重试不变) |
请求正文(必填)(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | string | 是 | evt_…,同一事件每次重试、每个订阅都相同,按它去重 |
type | string | 是 | 固定为 "customer.updated" |
createdAt | string(date-time) | 是 | |
apiVersion | string | 是 | 固定为 "2026-10-28" |
tenant | string | 租户范围的事件有。发给商店应用的正式事件也会带(租户装了你的应用) | |
environment | string | 可选值:test、live | |
app | string | ||
data | CustomerUpdatedData | 是 | |
data.customer | string | 是 | 客户中心 id |
data.id | string | 是 | 与 customer 相同 |
data.fields | string[] | 是 | 实际改过的字段,name 和/或 tags} |
响应
| 状态 | 说明 |
|---|---|
| 200 | 接收方已收下 |
通用响应
多个接口共用的响应。
BadRequest
400 invalid_request:查询或正文读不出来。换令牌时还有 unsupported_grant_type(不认识的 grant_type)和 tenant_required(应用级凭证必须带 tenant;或带了但不是这个凭证能用的组织)。多维表筛/排了视图里没有的字段也是这一条。
正文(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
error | ErrorBody | 是 | |
error.code | string | 是 | 稳定错误码。用它分支,不要解析 message。 可选值: invalid_request、invalid_grant、consent_required、consent_revoked、person_gone、unauthorized、forbidden、not_found、method_not_allowed、conflict、payload_too_large、validation_failed、rate_limited、internal、not_implemented、unavailable、token_required、invalid_token、invalid_credential、invalid_client、unsupported_grant_type、tenant_required、wrong_environment、credential_ended、address_not_allowed、not_installed、no_scope、scope_required、customer_token_required、assertion_stale、invalid_phone、ambiguous_customer、too_many_attempts、idempotency_conflict、idempotency_in_progress、condition_failed |
error.message | string | 是 | 给人看的说明,并写出下一步 |
error.requestId | string | 与 X-Request-Id 相同 | |
error.docUrl | string | 这条错误的文档,形如 https://dev.{domain}/docs/errors#scope_required | |
error.details | object | 没有内容时是 {}。ambiguous_customer 时含 candidates(客户 id 列表) |
Conflict
409。conflict:和当前状态冲突(已经预约、申请已经处理、晚取消未带 acceptCharge)。ambiguous_customer:同一号码对应多个客户,details.candidates 列出 id,下次带上 customer_id。idempotency_conflict:同一 Idempotency-Key 不同正文。idempotency_in_progress:第一次还在处理,稍后再读或换新键。
正文(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
error | ErrorBody | 是 | |
error.code | string | 是 | 稳定错误码。用它分支,不要解析 message。 可选值: invalid_request、invalid_grant、consent_required、consent_revoked、person_gone、unauthorized、forbidden、not_found、method_not_allowed、conflict、payload_too_large、validation_failed、rate_limited、internal、not_implemented、unavailable、token_required、invalid_token、invalid_credential、invalid_client、unsupported_grant_type、tenant_required、wrong_environment、credential_ended、address_not_allowed、not_installed、no_scope、scope_required、customer_token_required、assertion_stale、invalid_phone、ambiguous_customer、too_many_attempts、idempotency_conflict、idempotency_in_progress、condition_failed |
error.message | string | 是 | 给人看的说明,并写出下一步 |
error.requestId | string | 与 X-Request-Id 相同 | |
error.docUrl | string | 这条错误的文档,形如 https://dev.{domain}/docs/errors#scope_required | |
error.details | object | 没有内容时是 {}。ambiguous_customer 时含 candidates(客户 id 列表) |
Forbidden
403。scope_required:缺了操作描述里写的范围,去开发者平台或请管理员勾。customer_token_required:/me 只要客户令牌,用客户授权再调。wrong_environment:测试密钥打了正式组织,或正式密钥打了测试企业。not_installed:应用没装、关掉,或组织停用。no_scope:一把范围都没授。address_not_allowed:来源 IP 不在凭证允许名单。
正文(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
error | ErrorBody | 是 | |
error.code | string | 是 | 稳定错误码。用它分支,不要解析 message。 可选值: invalid_request、invalid_grant、consent_required、consent_revoked、person_gone、unauthorized、forbidden、not_found、method_not_allowed、conflict、payload_too_large、validation_failed、rate_limited、internal、not_implemented、unavailable、token_required、invalid_token、invalid_credential、invalid_client、unsupported_grant_type、tenant_required、wrong_environment、credential_ended、address_not_allowed、not_installed、no_scope、scope_required、customer_token_required、assertion_stale、invalid_phone、ambiguous_customer、too_many_attempts、idempotency_conflict、idempotency_in_progress、condition_failed |
error.message | string | 是 | 给人看的说明,并写出下一步 |
error.requestId | string | 与 X-Request-Id 相同 | |
error.docUrl | string | 这条错误的文档,形如 https://dev.{domain}/docs/errors#scope_required | |
error.details | object | 没有内容时是 {}。ambiguous_customer 时含 candidates(客户 id 列表) |
Internal
500 internal:平台出错。带上 X-Request-Id 报给我们。不要立刻换一种写法重试同一把幂等键。
正文(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
error | ErrorBody | 是 | |
error.code | string | 是 | 稳定错误码。用它分支,不要解析 message。 可选值: invalid_request、invalid_grant、consent_required、consent_revoked、person_gone、unauthorized、forbidden、not_found、method_not_allowed、conflict、payload_too_large、validation_failed、rate_limited、internal、not_implemented、unavailable、token_required、invalid_token、invalid_credential、invalid_client、unsupported_grant_type、tenant_required、wrong_environment、credential_ended、address_not_allowed、not_installed、no_scope、scope_required、customer_token_required、assertion_stale、invalid_phone、ambiguous_customer、too_many_attempts、idempotency_conflict、idempotency_in_progress、condition_failed |
error.message | string | 是 | 给人看的说明,并写出下一步 |
error.requestId | string | 与 X-Request-Id 相同 | |
error.docUrl | string | 这条错误的文档,形如 https://dev.{domain}/docs/errors#scope_required | |
error.details | object | 没有内容时是 {}。ambiguous_customer 时含 candidates(客户 id 列表) |
InvalidClient
401 invalid_client:client_id 不存在或 client_secret 不对。两种情况同一句,避免探测。带 WWW-Authenticate: Basic。密钥已吊销或过期是 credential_ended(也是 401)。
正文(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
error | ErrorBody | 是 | |
error.code | string | 是 | 稳定错误码。用它分支,不要解析 message。 可选值: invalid_request、invalid_grant、consent_required、consent_revoked、person_gone、unauthorized、forbidden、not_found、method_not_allowed、conflict、payload_too_large、validation_failed、rate_limited、internal、not_implemented、unavailable、token_required、invalid_token、invalid_credential、invalid_client、unsupported_grant_type、tenant_required、wrong_environment、credential_ended、address_not_allowed、not_installed、no_scope、scope_required、customer_token_required、assertion_stale、invalid_phone、ambiguous_customer、too_many_attempts、idempotency_conflict、idempotency_in_progress、condition_failed |
error.message | string | 是 | 给人看的说明,并写出下一步 |
error.requestId | string | 与 X-Request-Id 相同 | |
error.docUrl | string | 这条错误的文档,形如 https://dev.{domain}/docs/errors#scope_required | |
error.details | object | 没有内容时是 {}。ambiguous_customer 时含 candidates(客户 id 列表) |
NotFound
404 not_found:不存在,或你不该知道它存在(别人的会员、未开放的表、进不了的视图)。不要靠 403 去区分「没有」和「没权限」。
正文(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
error | ErrorBody | 是 | |
error.code | string | 是 | 稳定错误码。用它分支,不要解析 message。 可选值: invalid_request、invalid_grant、consent_required、consent_revoked、person_gone、unauthorized、forbidden、not_found、method_not_allowed、conflict、payload_too_large、validation_failed、rate_limited、internal、not_implemented、unavailable、token_required、invalid_token、invalid_credential、invalid_client、unsupported_grant_type、tenant_required、wrong_environment、credential_ended、address_not_allowed、not_installed、no_scope、scope_required、customer_token_required、assertion_stale、invalid_phone、ambiguous_customer、too_many_attempts、idempotency_conflict、idempotency_in_progress、condition_failed |
error.message | string | 是 | 给人看的说明,并写出下一步 |
error.requestId | string | 与 X-Request-Id 相同 | |
error.docUrl | string | 这条错误的文档,形如 https://dev.{domain}/docs/errors#scope_required | |
error.details | object | 没有内容时是 {}。ambiguous_customer 时含 candidates(客户 id 列表) |
NotImplemented
501 not_implemented。客户授权在这个部署不可用时会出现(代码仍可能是 unavailable)。
正文(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
error | ErrorBody | 是 | |
error.code | string | 是 | 稳定错误码。用它分支,不要解析 message。 可选值: invalid_request、invalid_grant、consent_required、consent_revoked、person_gone、unauthorized、forbidden、not_found、method_not_allowed、conflict、payload_too_large、validation_failed、rate_limited、internal、not_implemented、unavailable、token_required、invalid_token、invalid_credential、invalid_client、unsupported_grant_type、tenant_required、wrong_environment、credential_ended、address_not_allowed、not_installed、no_scope、scope_required、customer_token_required、assertion_stale、invalid_phone、ambiguous_customer、too_many_attempts、idempotency_conflict、idempotency_in_progress、condition_failed |
error.message | string | 是 | 给人看的说明,并写出下一步 |
error.requestId | string | 与 X-Request-Id 相同 | |
error.docUrl | string | 这条错误的文档,形如 https://dev.{domain}/docs/errors#scope_required | |
error.details | object | 没有内容时是 {}。ambiguous_customer 时含 candidates(客户 id 列表) |
PayloadTooLarge
413 payload_too_large:正文太大。会员写接口上限 1 MiB。
正文(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
error | ErrorBody | 是 | |
error.code | string | 是 | 稳定错误码。用它分支,不要解析 message。 可选值: invalid_request、invalid_grant、consent_required、consent_revoked、person_gone、unauthorized、forbidden、not_found、method_not_allowed、conflict、payload_too_large、validation_failed、rate_limited、internal、not_implemented、unavailable、token_required、invalid_token、invalid_credential、invalid_client、unsupported_grant_type、tenant_required、wrong_environment、credential_ended、address_not_allowed、not_installed、no_scope、scope_required、customer_token_required、assertion_stale、invalid_phone、ambiguous_customer、too_many_attempts、idempotency_conflict、idempotency_in_progress、condition_failed |
error.message | string | 是 | 给人看的说明,并写出下一步 |
error.requestId | string | 与 X-Request-Id 相同 | |
error.docUrl | string | 这条错误的文档,形如 https://dev.{domain}/docs/errors#scope_required | |
error.details | object | 没有内容时是 {}。ambiguous_customer 时含 candidates(客户 id 列表) |
RateLimited
429。rate_limited:这把凭证的调用预算用完,看 Retry-After 和 X-RateLimit-*。too_many_attempts:这个地址失败太多次,等 Retry-After(换令牌时常见 60 秒)。
正文(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
error | ErrorBody | 是 | |
error.code | string | 是 | 稳定错误码。用它分支,不要解析 message。 可选值: invalid_request、invalid_grant、consent_required、consent_revoked、person_gone、unauthorized、forbidden、not_found、method_not_allowed、conflict、payload_too_large、validation_failed、rate_limited、internal、not_implemented、unavailable、token_required、invalid_token、invalid_credential、invalid_client、unsupported_grant_type、tenant_required、wrong_environment、credential_ended、address_not_allowed、not_installed、no_scope、scope_required、customer_token_required、assertion_stale、invalid_phone、ambiguous_customer、too_many_attempts、idempotency_conflict、idempotency_in_progress、condition_failed |
error.message | string | 是 | 给人看的说明,并写出下一步 |
error.requestId | string | 与 X-Request-Id 相同 | |
error.docUrl | string | 这条错误的文档,形如 https://dev.{domain}/docs/errors#scope_required | |
error.details | object | 没有内容时是 {}。ambiguous_customer 时含 candidates(客户 id 列表) |
Unauthorized
401。token_required:没带 Bearer,先 POST /oauth/token。invalid_token:令牌无效或过期,重新换(SDK 会自动换一次再重试)。credential_ended:凭证吊销、过期,或应用关掉、范围被收光。写 /me 时还有 assertion_stale:客户断言超过 10 分钟,重新断言再写。
正文(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
error | ErrorBody | 是 | |
error.code | string | 是 | 稳定错误码。用它分支,不要解析 message。 可选值: invalid_request、invalid_grant、consent_required、consent_revoked、person_gone、unauthorized、forbidden、not_found、method_not_allowed、conflict、payload_too_large、validation_failed、rate_limited、internal、not_implemented、unavailable、token_required、invalid_token、invalid_credential、invalid_client、unsupported_grant_type、tenant_required、wrong_environment、credential_ended、address_not_allowed、not_installed、no_scope、scope_required、customer_token_required、assertion_stale、invalid_phone、ambiguous_customer、too_many_attempts、idempotency_conflict、idempotency_in_progress、condition_failed |
error.message | string | 是 | 给人看的说明,并写出下一步 |
error.requestId | string | 与 X-Request-Id 相同 | |
error.docUrl | string | 这条错误的文档,形如 https://dev.{domain}/docs/errors#scope_required | |
error.details | object | 没有内容时是 {}。ambiguous_customer 时含 candidates(客户 id 列表) |
Unavailable
503 unavailable:暂时不可用(读凭证、读授权、读库失败)。按 Retry-After 重试;没有该头时 SDK 用指数退避。
正文(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
error | ErrorBody | 是 | |
error.code | string | 是 | 稳定错误码。用它分支,不要解析 message。 可选值: invalid_request、invalid_grant、consent_required、consent_revoked、person_gone、unauthorized、forbidden、not_found、method_not_allowed、conflict、payload_too_large、validation_failed、rate_limited、internal、not_implemented、unavailable、token_required、invalid_token、invalid_credential、invalid_client、unsupported_grant_type、tenant_required、wrong_environment、credential_ended、address_not_allowed、not_installed、no_scope、scope_required、customer_token_required、assertion_stale、invalid_phone、ambiguous_customer、too_many_attempts、idempotency_conflict、idempotency_in_progress、condition_failed |
error.message | string | 是 | 给人看的说明,并写出下一步 |
error.requestId | string | 与 X-Request-Id 相同 | |
error.docUrl | string | 这条错误的文档,形如 https://dev.{domain}/docs/errors#scope_required | |
error.details | object | 没有内容时是 {}。ambiguous_customer 时含 candidates(客户 id 列表) |
Unprocessable
422。validation_failed:字段或门店规则不接受(日期窗口、课次不存在、会员状态、剩余次数不足、原因超长)。换客户令牌时电话读不出来是 invalid_phone:带国家码,或按大陆手机号发。
正文(application/json)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
error | ErrorBody | 是 | |
error.code | string | 是 | 稳定错误码。用它分支,不要解析 message。 可选值: invalid_request、invalid_grant、consent_required、consent_revoked、person_gone、unauthorized、forbidden、not_found、method_not_allowed、conflict、payload_too_large、validation_failed、rate_limited、internal、not_implemented、unavailable、token_required、invalid_token、invalid_credential、invalid_client、unsupported_grant_type、tenant_required、wrong_environment、credential_ended、address_not_allowed、not_installed、no_scope、scope_required、customer_token_required、assertion_stale、invalid_phone、ambiguous_customer、too_many_attempts、idempotency_conflict、idempotency_in_progress、condition_failed |
error.message | string | 是 | 给人看的说明,并写出下一步 |
error.requestId | string | 与 X-Request-Id 相同 | |
error.docUrl | string | 这条错误的文档,形如 https://dev.{domain}/docs/errors#scope_required | |
error.details | object | 没有内容时是 {}。ambiguous_customer 时含 candidates(客户 id 列表) |