接口约定
每个接口在状态码、错误、重试与分页上都遵守同一套约定。学一次,处处适用。
| 状态码 | 含义 |
|---|---|
200 | 成功。 |
201 | 创建了一个资源(一位客人、一次锁房或一笔费用)。 |
400 | 请求无效:JSON 格式有误,或某个参数没通过校验。 |
401 | 认证失败:API 密钥缺失或无效。 |
403 | 密钥有效,但缺少所需的权限范围。 |
404 | 该路由或资源不存在(或不属于你)。 |
405 | 路径存在,但不支持这个 HTTP 方法。 |
409 | 冲突:没有房量、Idempotency-Key 被复用,或积分余额不足。 |
429 | 触发限流。请在 Retry-After 头指示的时间之后重试。 |
500 | 服务端内部错误。 |
503 | 维护中。平台暂时不可用。请在 Retry-After 头指示的时间之后重试。 |
每次失败都会返回对应的状态码和一个 error 对象:
{
"error": {
"type": "permission",
"code": "insufficient_scope",
"message": "Missing required scope: reservations:write.",
"request_id": "req_a1b2c3d4e5"
}
}type:错误类别,取值为invalid_request、authentication、permission、not_found、conflict、rate_limit或server。code:稳定、可被程序识别的标识。请按它分支处理,而不是按message。message:给人看的说明。可以记日志,但不要解析。param:引发错误的请求字段。只在400字段校验错误时出现,其余情况会省略。estimated_end:维护窗口预计结束时间的 ISO 时间戳。只在503时出现,且只在设置了结束时间时才有。它比由它推导出的Retry-After头更精确,所以要显示「恢复时间」时优先用它。request_id:唯一标识这次请求。联系支持时请附上它。
按 code 判断,别按 message
message 的文案可能变动,code 不会。请让错误处理逻辑基于 error.code 分支(例如 no_availability、rate_plan_not_found、insufficient_points)。
你会遇到的常见 code:
| Code | 类别 | 出现时机 |
|---|---|---|
missing_api_key / invalid_api_key | authentication | 缺少 Authorization 头,或密钥未知、已停用、已过期。 |
insufficient_scope | permission | 密钥缺少该接口所需的权限范围。 |
invalid_parameters / invalid_json | invalid_request | 某个字段没通过校验,或请求体不是有效的 JSON。 |
unknown_route | not_found | 没有接口匹配这个路径。 |
no_availability | conflict | 该房型在所选日期已售罄。 |
idempotency_key_reuse | conflict | 同一个 Idempotency-Key 被用在了不同的请求体上。 |
idempotency_in_progress | conflict | 使用同一个 Idempotency-Key 的上一次请求仍在处理中。请稍后重试。 |
insufficient_points | conflict | 积分兑换超出了可用余额。 |
rate_limited | rate_limit | 超出了单把密钥的请求上限。 |
maintenance_mode | server | 平台处于维护窗口。请在 Retry-After 头指示的时间之后重试。 |
Veridien 的所有资源 id 都是 UUID,例如 7d5dc2ea-1f4e-4bfa-9a52-6c3f08b41e27。给客人看的预订编号是另一回事:confirmation_code 是一串便于人读的短代码(例如 B7XKQ4M2NZ),展示给客人和员工看,绝不用作资源 id。请求 id 以 req_ 开头。由你提供的字段,例如 payment_reference,对 Veridien 是不透明的,保留你自己系统里的格式。
POST 与 PATCH 请求是会改变状态的那一类:创建客人、锁定库存、确认预订、兑换积分、入账费用。为了让重试安全,请发送一个带唯一值的 Idempotency-Key 头(用 UUID 就很合适):
curl -X POST https://veridien.app/api/v1/holds \
-H "Authorization: Bearer vrdn_live_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 6f1d2c9a-8b3e-4a17-9c2f-1e5b7d0a4c83" \
-d '{ "room_type_id": "9b2f6c3e-8d1a-4f5b-a7c9-4e0d2b8f1a63", "rate_plan_id": "5c1e8f4a-2b7d-4a9c-8e6f-1d3b9a5c7e02", "check_in": "2026-08-01", "check_out": "2026-08-04", "guest_id": "3f8a2c1b-9d4e-4c6a-8b7f-5e2d1a9c0b34", "adults": 2 }'- 带某个 key 的第一个请求正常执行,它的响应会被保存下来。
- 用同样的 key 和同样的请求体重试,会重放保存下来的响应:不会有第二次锁房,也不会有第二笔费用。
- 用同样的 key 配不同的请求体,返回
409 idempotency_key_reuse。 - 在第一个请求还没结束时并发发送同样的 key,返回
409 idempotency_in_progress。稍等片刻再试,你就会拿到保存下来的响应。key 在操作执行前就已被占用,因此第二个请求绝不会真的执行它。 - 失败的请求会释放它的 key,因此一次正当的重试会重新执行,而不是重放那次失败。只有成功的响应会被保存:因一时售罄返回的
409 no_availability不会在房间重新空出来之后还被永远重放。
key 的作用范围是单把 API 密钥。每个逻辑操作用一个新的 Idempotency-Key。
确认接口有双重幂等
确认锁房(POST /holds/{id}/confirm)对它的 payment_reference 也是幂等的:重复确认一条已确认的预订,或重放同一个付款凭据,都会返回已有的确认结果并带上 "already_confirmed": true,而不会再次收款。
GET /reservations 用 limit 与 offset 分页。其他列表接口(/room-types、/services、/rate-plans)会一次返回该酒店的全部数据,不分页。
| 参数 | 默认值 | 最大值 | 含义 |
|---|---|---|---|
limit | 25 | 100 | 返回多少条记录。 |
offset | 0 | 10000 | 跳过多少条记录。 |
响应把结果放在 data 里,并说明是否还有更多:
{
"has_more": true,
"data": [ /* ... */ ]
}要取下一页,就把 limit 加到上一次的 offset 上。当 has_more 为 false 时停止。
每个金额都是十进制字符串,绝不是浮点数:是 "1310.40",不是 1310.4。请用十进制类型解析,不要用二进制浮点类型。
一家酒店有一种本位币,而一张账单可以同时挂着多种币种的明细(美元的房费加上一条马尔代夫拉菲亚的餐厅明细,很常见)。由此有两条规则:
不同币种的金额永远不会相加。 凡是报告余额的地方,都会有一个 balances 数组给出各币种的实际数字,那才是可信的数字:
"balances": [
{ "currency": "USD", "charges": "1310.40", "payments": "0.00", "balance": "1310.40" },
{ "currency": "MVR", "charges": "1500.00", "payments": "1500.00", "balance": "0.00" }
]只有当任何币种下都不欠款时,账单才是 settled。一笔美元欠款绝不会被一笔拉菲亚的贷方抵消。
汇总数字是一种估值,而且可能是 null。 balance、total_charges 与 folio_balance 这类字段,是按酒店本位币折算出来的便捷数字,依据的是写入每一行时冻结在该行上的汇率。因为汇率是冻结的,之后的汇率变动绝不会改变一笔已有费用的报告金额。如果某一行的币种在这家酒店没有配置汇率,它就无法折算,这些字段会是 null,而不是一个错的数字。做算术之前先处理 null。
限流按每把 API 密钥使用漏桶(令牌桶)算法。你的桶最多装 600 个令牌,并以恒定的每秒 10 个令牌(每分钟 600 个)补充。每个请求消耗一个令牌:
- 突发时可以一次把整桶用完,也就是连续 600 个请求。
- 持续吞吐等于补充速率,即每秒 10 个请求。
桶空时,请求会返回 429 并带上 Retry-After 头。请至少退避到它指示的秒数之后再重试。尽量缓存读取结果(/property、/room-types 与 /services 带有较短的 Cache-Control;/availability 与 /rates 是实时库存,有意不做缓存,所以你自己也不要缓存它们),并避免密集轮询。
每个响应,无论成功还是失败,都关联着一个 request_id(错误响应体里以及 /me 的返回中也会给出)。把它记下来。当你反馈问题时,支持人员可以凭请求 id 追踪到那次具体调用。