Hi.Events v1.11.2 API 使用详解
目标:面向需要与 Hi.Events v1.11.2 做系统集成的开发者,完整讲解其 REST API 的认证方式、端点分布与业务调用链。本文全部请求与响应均在 v1.11.2-beta 实测环境逐条执行并照抄回显(认证、组织者、活动、票务产品、公开下单、订单、购票人、统计、Webhook 九个域全链路走通),错误形态
Hi.Events v1.11.2 API 使用详解
目标:面向需要与 Hi.Events v1.11.2 做系统集成的开发者,完整讲解其 REST API 的认证方式、端点分布与业务调用链。本文全部请求与响应均在 v1.11.2-beta 实测环境逐条执行并照抄回显(认证、组织者、活动、票务产品、公开下单、订单、购票人、统计、Webhook 九个域全链路走通),错误形态均为真实触发记录。
一、环境信息
| 项目 | 实测值 |
|---|---|
| Hi.Events 版本 | v1.11.2-beta |
| API 风格 | REST + JSON,JWT Bearer 认证 |
| 端点总数 | 210 个管理端路由(GET 85 / POST 69 / PUT 27 / DELETE 24 / PATCH 5)+ 30 个公开路由(/public 前缀组) |
| 协议形态 | 所有业务端点注册在服务根路径;统一入口(nginx/all-in-one 80 端口)将 /api 前缀剥掉后转发给后端 |
| 实测载体 | B2 篇 Docker Compose 栈(http://<主机>:8129,下文以 http://localhost:8129 示例) |
| 令牌有效期 | expires_in=604800(7 天,实测登录响应) |
二、前置条件
- 已完成部署并注册管理员账号(见 B1/B2 篇);
- 客户端工具:
curl(本文全部示例)或任意 HTTP 客户端; - 每个请求必须带
Accept: application/json头,否则部分端点不返回 JSON; - API Base URL 为
http://<主机>:<端口>/api(统一入口形态)或http://<主机>:8000(源码形态直连后端,nginx 剥前缀前的原始形态)。
三、API 总览
| 域 | 端点前缀 | 认证 | 典型能力 |
|---|---|---|---|
| 认证与账户 | /auth/*、/me | 公开/令牌 | 登录、刷新、登出、注册、找回密码 |
| 组织者 | /organizers | JWT | 组织者 CRUD、统计、订单、邮件模板 |
| 活动 | /events、/organizers/{id}/events | JWT | 活动 CRUD、状态流转、分类、图片、问卷 |
| 票务产品 | /events/{id}/products、/product-categories | JWT | 票种/商品 CRUD、价格、库存 |
| 公开购票 | /public/events/{id}、/public/.../order | 无(会话 Cookie) | 活动详情、产品列表、创建/完成订单、候补、促销码 |
| 订单管理 | /events/{id}/orders | JWT | 订单列表、退款、标记支付、发票、导出 |
| 购票人 | /events/{id}/attendees | JWT | 购票人列表、签到、重发票 |
| 统计 | /stats、/events/{id}/stats、/organizers/{id}/stats | JWT | 销量/收入/订单来源日报 |
| Webhook | /events/{id}/webhooks、/organizers/{id}/webhooks | JWT | 事件订阅、密钥管理 |
限流约定:API 全局限流默认 180 次/分钟(按登录用户 ID 或来源 IP 计数),超限返回 429 {"message": "Too Many Attempts."}(实测,调整方法见配置参数详解篇 5.3)。
认证演进说明:v1.11.2 管理端 API 仅支持 JWT(路由文件实测 0 处 api-key 引用);「个人 API Key」为 v2 线新增(develop 分支已可见),生产集成暂按 JWT 设计。
四、认证与首次调用
4.1 登录获取令牌
# curl -s -X POST http://localhost:8129/api/auth/login \
-H 'Accept: application/json' -H 'Content-Type: application/json' \
-d '{"email":"admin@example.com","password":"q1w2e3r4"}'
{"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgxMjkvYXV0aC9sb2dpbiIsImlhdCI6MTc5Mjc0NjYwNiwiZXhwIjoxNzkzMzUxNDA2LCJuYmYiOjE3OTI3NDY2MDYsImp0aSI6IlVaOEZ6Q1JjSWtqZTJ4ZXMiLCJzdWIiOiIxIiwicHJ2IjoiYjViMWFmMWFlZjUxZTIyZTVjYmUzOTRjMzc0NTcyMmFiODBhZDZjYyIsImFjY291bnRfaWQiOjEsInJvbGUiOiJBRE1JTiJ9.21uC4bdEa247urAv6gZ5D4g-Gvm7HNWVYNblUdDeuoA","token_type":"bearer","expires_in":604800,"user":{"id":1,"timezone":"America\/Vancouver","first_name":"Admin","last_name":"Demo","full_name":"Admin Demo","email":"admin@example.com","is_email_verified":true,"has_pending_email_chang...(节选)}
后续请求统一携带 Authorization: Bearer <token>。示例环境变量化:
# TOKEN='<上一步的 token>'
4.2 未带令牌的错误形态(实测)
# curl -s http://localhost:8129/api/events -H 'Accept: application/json'
{"message":"Unauthenticated."}
HTTP 状态码 401。
4.3 当前用户与令牌刷新(实测)
# curl -s http://localhost:8129/api/me -H 'Accept: application/json' -H "Authorization: Bearer $TOKEN"
{"data":{"id":1,"timezone":"America\/Vancouver","first_name":"Admin","last_name":"Demo","full_name":"Admin Demo","email":"admin@example.com","is_email_verified":true,"has_pending_email_change":false,"locale":"en","marketing_opted_in_at":null,"role":"ADMIN","is_account_owner":true,"last_login_at":"2026-10-23 09:10:06","status":"ACTIVE","account_id":1}}
刷新令牌(旧令牌仍有效期内可换发新令牌):
# curl -s -X POST http://localhost:8129/api/auth/refresh -H 'Accept: application/json' -H "Authorization: Bearer $TOKEN"
{"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgxMjkvYXV0aC9yZWZyZXNoIiwi...(节选)","token_type":"bearer","expires_in":604800,"user":{...},"accounts":[]}
五、各 API 域详解
5.1 组织者域
创建组织者:
# curl -s -X POST http://localhost:8129/api/organizers \
-H 'Accept: application/json' -H 'Content-Type: application/json' -H "Authorization: Bearer $TOKEN" \
-d '{"name":"Demo Organizer","email":"organizer@example.com","currency":"USD","timezone":"Asia/Shanghai"}'
{"data":{"id":1,"name":"Demo Organizer","email":"organizer@example.com","phone":null,"website":null,"description":null,"timezone":"Asia\/Shanghai","currency":"USD","slug":"demo-organizer","status":"DRAFT","images":[]}}
字段名为
currency(如USD)——传currency_code会得到 422The currency field is required(实测踩坑)。
列出组织者:GET /api/organizers,返回结构同上(data 数组 + 分页 meta)。
5.2 活动域
创建活动:
# curl -s -X POST http://localhost:8129/api/organizers/1/events \
-H 'Accept: application/json' -H 'Content-Type: application/json' -H "Authorization: Bearer $TOKEN" \
-d '{"title":"Demo Conference 2026","start_date":"2026-12-01T01:00:00Z","end_date":"2026-12-02T10:00:00Z","currency":"USD","description":"Demo event created via REST API","category":"OTHER","timezone":"Asia/Shanghai"}'
{"data":{"id":1,"title":"Demo Conference 2026","category":"OTHER","description":"Demo event created via REST API","start_date":"2026-12-01T01:00:00.000000Z","end_date":"2026-12-02T10:00:00.000000Z","status":"DRAFT","lifecycle_status":"UPCOMING","currency":"USD","timezone":"Asia\/Shanghai","slug":"demo-conference-2026"}}
发布活动(DRAFT → LIVE):
# curl -s -X POST http://localhost:8129/api/events/1/status \
-H 'Accept: application/json' -H 'Content-Type: application/json' -H "Authorization: Bearer $TOKEN" \
-d '{"status":"LIVE"}'
{"data":{"id":1,"title":"Demo Conference 2026","category":"OTHER",...,"status":"LIVE","lifecycle_status":"UPCOMING","currency":"USD","timezone":"Asia\/Shanghai","slug":"demo-conference-2026"}}
活动状态机为 DRAFT / LIVE / ARCHIVED;只有 LIVE 状态的活动才对公开购票接口可见。
列出活动:GET /api/events(实测返回 data 数组,含上面创建的活动)。
5.3 票务产品域
先建票务分类:
# curl -s -X POST http://localhost:8129/api/events/1/product-categories \
-H 'Accept: application/json' -H 'Content-Type: application/json' -H "Authorization: Bearer $TOKEN" \
-d '{"name":"Admission","description":"Admission tickets","is_hidden":false}'
{"data":{"id":2,"name":"Admission","description":"Admission tickets","is_hidden":false,"order":0,"no_products_message":"There are no products available in this category"}}
创建免费票(免费票用 type: "FREE",无需 prices 数组):
# curl -s -X POST http://localhost:8129/api/events/1/products \
-H 'Accept: application/json' -H 'Content-Type: application/json' -H "Authorization: Bearer $TOKEN" \
-d '{"title":"General Admission","type":"FREE","product_type":"TICKET","product_category_id":2,"initial_quantity_available":100}'
{"data":{"id":1,"title":"General Admission","type":"FREE","product_type":"TICKET","order":1,"description":null,"price":0,"max_per_order":100,"min_per_order":0,"quantity_sold":0,...,"prices":[{"id":1,"label":"General Admission","price":0,...,"price_including_taxes_and_fees":0}],"product_category_id":2,...}}
创建付费票(价格为最小货币单位「分」,19900 = $199.00):
# curl -s -X POST http://localhost:8129/api/events/1/products \
-H 'Accept: application/json' -H 'Content-Type: application/json' -H "Authorization: Bearer $TOKEN" \
-d '{"title":"VIP Pass","type":"PAID","product_type":"TICKET","product_category_id":2,"initial_quantity_available":50,"sale_start_date":"2026-10-23T00:00:00Z","sale_end_date":"2026-11-30T23:59:59Z","prices":[{"label":"VIP Pass","price":19900,"initial_quantity_available":50}]}'
{"data":{"id":2,"title":"VIP Pass","type":"PAID","product_type":"TICKET","order":2,"description":null,"price":19900,...,"sale_start_date":"Fri Oct 23 2026 00:00:00 GMT+0000","sale_end_date":"Mon Nov 30 2026 23:59:59 GMT+0000",...,"prices":[{"id":2,"label":"VIP Pass","price":19900,...}],"product_category_id":2,...}}
422 字段链(实测踩坑记录):创建产品缺字段时报错逐层暴露,全部补齐才能成功——
- 缺
type→The type field is required(价格类型枚举:FREE/PAID/DONATION/TIERED/REGISTRATION); - 缺
product_type→The product type field is required(枚举:TICKET 票券 / GENERAL 商品); - 缺
product_category_id→The product category id field is required; - PAID 类型缺
prices数组 →The prices field is required(价格单位为「分」,每项含 label/price/initial_quantity_available); - 库存字段名是
initial_quantity_available(不是 quantity_available)。
产品价格类型完整枚举:PAID / FREE / DONATION(捐赠)/ TIERED(阶梯价)/ REGISTRATION(报名登记);产品类型:TICKET / GENERAL。
5.4 公开下单域(/public)
公开接口无需 JWT,但完成订单必须携带创建订单时返回的会话 Cookie(服务端按 session 绑定订单)。
查询公开活动详情与票品(实测节选):
# curl -s http://localhost:8129/api/public/events/1 -H 'Accept: application/json'
{"data":{"id":1,"title":"Demo Conference 2026","category":"OTHER","description":"Demo event created via REST API","description_preview":"Demo event created via REST API","start_date":"2026-12-01T01:00:00.000000Z","end_date":"2026-12-02T10:00:00.000000Z","currency":"USD","slug":"demo-conference-2026","status":"LIVE","lifecycle_status":"UPCOMING","ti...(节选)}
# curl -s http://localhost:8129/api/public/events/1/products -H 'Accept: application/json'
(返回可见票品数组,结构与 5.3 的产品响应一致)
第一步:创建预留订单(实测):
# curl -s -c /tmp/he-cookies.txt -X POST http://localhost:8129/api/public/events/1/order \
-H 'Accept: application/json' -H 'Content-Type: application/json' \
-d '{"tickets":[{"ticket_id":1,"quantity":2}]}'
{"data":{"short_id":"o_T9hHnBQ7FEqva","total_before_additions":0,"total_tax":0,"total_gross":0,"total_fee":0,"status":"RESERVED","refund_status":null,"payment_status":null,"currency":"USD","reserved_until":"2026-10-23 09:29:27","is_expired":false,"first_name":null,"last_name":null,"email":null,"public_id":"O-WE2D76D","is_payment_required":false,"promo_code":null,"taxes_and_fees_rollup":[],"order_items":[{"id":1,"order_id":1,"total_before_additions":0,...,"quantity":2,"product_id":1,"product_price_id":1,"item_name":"General Admission",...}],"session_identifier":"4f4b84d885ba733bbbb646d9ebc241bdfd847305"}}
订单进入 RESERVED,reserved_until 为预留截止时间。
第二步:完成订单(带同会话 Cookie + 购票人信息,实测):
# curl -s -b /tmp/he-cookies.txt -X PUT http://localhost:8129/api/public/events/1/order/o_T9hHnBQ7FEqva \
-H 'Accept: application/json' -H 'Content-Type: application/json' \
-d '{"order":{"first_name":"Zhang","last_name":"San","email":"buyer@example.com","is_terms_accepted":true},"products":[{"product_id":1,"product_price_id":1,"quantities":[{"quantity":1,"price_id":1,"price":0,"product_price_id":1}],"first_name":"Zhang","last_name":"San","email":"buyer@example.com","email_confirmation":"buyer@example.com"}]}'
{"data":{"short_id":"o_Xzs2fVdPWuG4r","total_before_additions":0,"total_tax":0,"total_gross":0,"total_fee":0,"status":"COMPLETED","refund_status":null,"payment_status":"NO_PAYMENT_REQUIRED","currency":"USD","reserved_until":"2026-10-23 09:31:38","is_expired":false,"first_name":"Zhang","last_name":"San","email":"buyer@example.com","public_id":"O-LYNWWCY","is_payment_required":false,"promo_code":null,"taxes_and_fees_rollup":[],"order_items":[{"id":3,"order_id":3,...,"quantity":1,"product_id":1,"product_price_id":1,"item_name":"General Admission",...}]}}
完成订单的错误形态实录(每条均为真实触发):
| 现象 | 原因 |
|---|---|
HTTP 403 {"message":"could not verify your session"} | 完成订单未携带创建订单时的会话 Cookie——curl 必须用 -c 存、-b 带(同会话绑定) |
HTTP 409 attendees does not match tickets | products 数组条目数与购票人数不匹配:每个数组元素代表 1 位购票人;quantity=2 需拆成 2 个元素(或 qty 各 1) |
| HTTP 500 | payload 结构错误(如缺 product_price_id),不是业务校验缺失——按 5.3/本节的字段结构补全 |
| HTTP 422 | 缺每位购票人的 first_name/last_name/email/email_confirmation 或缺 is_terms_accepted |
5.5 订单与购票人管理域
订单列表(含分页与允许的排序字段,实测节选):
# curl -s "http://localhost:8129/api/events/1/orders" -H 'Accept: application/json' -H "Authorization: Bearer $TOKEN"
{"data":[{"id":3,"short_id":"o_Xzs2fVdPWuG4r","total_before_additions":0,"total_gross":0,"total_tax":0,"total_fee":0,"total_refunded":0,"status":"COMPLETED","refund_status":null,"payment_status":"NO_PAYMENT_REQUIRED","currency":"USD","first_name":"Zhang","last_name":"San","email":"buyer@example.com","created_at":"2026-10-23T09:16:38.000000Z","public_id":"O-LYNWWCY","is_partially_refunded":false,"is_fully_refunded":false,"is_free_order":true,"is_manually_created":false,...,"order_items":[...],"attendees":[{"id":1,...,"status":"ACTIVE",...}]}],"links":{...},"meta":{...}}
购票人列表(实测节选,含公开 ID 与签到状态):
# curl -s http://localhost:8129/api/events/1/attendees -H 'Accept: application/json' -H "Authorization: Bearer $TOKEN"
{"data":[{"id":1,"order_id":3,"product_id":1,"product_price_id":1,"event_id":1,"email":"buyer@example.com","status":"ACTIVE","first_name":"Zhang","last_name":"San","public_id":"A-X45IGN4","short_id":"a_B8XHGLNcKVVDI","locale":"en","notes":null,"check_ins":[],"order":{...}}],...}
现场签到(实测端点):POST /api/events/{event_id}/attendees/{attendee_public_id}/check_in;另有签到列表(check-in lists)域支持批量核验。重发门票:POST /api/events/{event_id}/attendees/{attendee_public_id}/resend-ticket。
订单操作端点(域内可用,本文未逐一触发的标注如下):退款 POST /orders/{id}/refund、取消 POST /orders/{id}/cancel、标记支付 POST /orders/{id}/mark-as-paid、重发确认 POST /orders/{id}/resend_confirmation、发票 GET /orders/{id}/invoice、导出 POST /events/{id}/orders/export。
5.6 统计域
# curl -s http://localhost:8129/api/events/1/stats -H 'Accept: application/json' -H "Authorization: Bearer $TOKEN"
{"data":{"daily_stats":[{"date":"2026-10-23 09:16:51","total_fees":0,"total_tax":0,"total_sales_gross":0,"products_sold":1,"orders_created":1,"attendees_registered":1,"total_refunded":0},{"date":"2026-10-24 09:16:51",...,"products_sold":0,...},...(按日展开节选)}}
同域端点:账户级 GET /api/stats、组织者级 GET /api/organizers/{id}/stats、候补统计 GET /api/events/{id}/waitlist/stats。
5.7 Webhook 域
创建 Webhook(实测,返回明文 secret 用于验签):
# curl -s -X POST http://localhost:8129/api/events/1/webhooks \
-H 'Accept: application/json' -H 'Content-Type: application/json' -H "Authorization: Bearer $TOKEN" \
-d '{"url":"https://example.com/hook","event_types":["order.created","attendee.created"]}'
{"data":{"id":1,"url":"https:\/\/example.com\/hook","event_types":["order.created","attendee.created"],"status":"ENABLED","last_triggered_at":null,"last_response_body":null,"last_response_code":null,"secret":"z8BmpyeInho5YzLCu59JoakhMADslgxY"}}
secret仅在创建/查看时返回,用于接收端验签;- Webhook 分发走独立队列
webhook-queue(Worker 必须消费该队列,见配置参数详解篇 5.11); - SSRF 防护(实测):URL 指向内网地址(127.0.0.1/内网网段)的 Webhook 创建会被拒绝;确需推内网接收端时配置
APP_ALLOWED_INTERNAL_WEBHOOK_HOSTS白名单; - 组织者级 Webhook:
POST /api/organizers/{id}/webhooks。
六、分页/过滤/排序通用约定
列表接口统一返回 data / links / meta 三段(Laravel 分页器,实测 meta 结构):
"meta": {"current_page": 1, "from": 1, "last_page": 1, "links": [...], "path": "http://localhost:8129/events/1/orders", "per_page": 25, "to": 1, "total": 1, "allowed_filters": {...}, "allowed_sorts": {"created_at": {"asc": "Oldest First", "desc": "Newest First"}, "first_name": {...}, "total_gross": {...}, "email": {...}, "public_id": {...}}, "default_sort": "created_at", "default_sort_direction": "desc"}
- 分页参数:
?page=2&per_page=50(默认 per_page=25); - 排序:
?sort=created_at&direction=asc,可用字段见各接口 meta 的allowed_sorts; - 过滤:
allowed_filters同样在 meta 中声明,按字段名传查询参数即可; - 响应包裹:单资源为
{"data": {...}},列表为{"data": [...], "links": {...}, "meta": {...}}。
七、常见问题
Q1:请求返回 HTML 而不是 JSON
缺少 Accept: application/json 头,或把 API 打到了前端 SSR 端口(5678 对任意路径都渲染页面壳)。API 一律走统一入口的 /api 前缀。
Q2:登录成功但其他接口 401
Authorization: Bearer <token> 格式错误(Bearer 拼写、多余空格)或令牌已过期(7 天)。用 /api/auth/refresh 换发新令牌。
Q3:公开下单成功,完成订单 403 could not verify your session
创建订单(POST)与完成订单(PUT)必须同会话:curl 加 -c cookies.txt 与 -b cookies.txt。换会话后需重新走创建流程。
Q4:The currency field is required
字段名是 currency(值为 ISO 4217 代码如 USD),不是 currency_code(实测踩坑)。
Q5:创建付费票后价格为 0 或单位不对
prices[].price 单位是「最小货币单位」(分):$199.00 传 19900(实测 VIP Pass)。展示层价格由系统换算。
Q6:个人 API Key 怎么创建
v1.11.2 不支持(路由实测 0 处引用),属 v2 线能力;当前集成统一用 JWT(登录获取 + refresh 续期)。
Q7:限流 429 Too Many Attempts.
全局限流默认 180 次/分钟按用户/IP 计数;批量集成前调大 APP_API_RATE_LIMIT_PER_MINUTE(实测该键生效,见配置参数详解篇 5.3)。
八、参考链接
- 官方部署文档:https://hi.events/docs/getting-started/deploying
- API 路由源码(v1.11.2 tag):https://github.com/HiEventsDev/Hi.Events/blob/v1.11.2-beta/routes/api.php
- GitHub 仓库:https://github.com/HiEventsDev/Hi.Events
- 本专栏关联篇章:《Hi.Events v1.11.2 介绍与选型指南》《Ubuntu 22.04 安装 Hi.Events v1.11.2 指南》《Hi.Events v1.11.2 Docker Compose 部署指南》《Hi.Events v1.11.2 配置参数详解》