Hi.Events v1.11.2 API 使用详解

目标:面向需要与 Hi.Events v1.11.2 做系统集成的开发者,完整讲解其 REST API 的认证方式、端点分布与业务调用链。本文全部请求与响应均在 v1.11.2-beta 实测环境逐条执行并照抄回显(认证、组织者、活动、票务产品、公开下单、订单、购票人、统计、Webhook 九个域全链路走通),错误形态

Hi.EventsREST API票务自托管2026/10/23

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公开/令牌登录、刷新、登出、注册、找回密码
组织者/organizersJWT组织者 CRUD、统计、订单、邮件模板
活动/events、/organizers/{id}/eventsJWT活动 CRUD、状态流转、分类、图片、问卷
票务产品/events/{id}/products、/product-categoriesJWT票种/商品 CRUD、价格、库存
公开购票/public/events/{id}、/public/.../order无(会话 Cookie)活动详情、产品列表、创建/完成订单、候补、促销码
订单管理/events/{id}/ordersJWT订单列表、退款、标记支付、发票、导出
购票人/events/{id}/attendeesJWT购票人列表、签到、重发票
统计/stats、/events/{id}/stats、/organizers/{id}/statsJWT销量/收入/订单来源日报
Webhook/events/{id}/webhooks、/organizers/{id}/webhooksJWT事件订阅、密钥管理

限流约定: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 会得到 422 The 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 字段链(实测踩坑记录):创建产品缺字段时报错逐层暴露,全部补齐才能成功——

  1. 缺 type → The type field is required(价格类型枚举:FREE/PAID/DONATION/TIERED/REGISTRATION);
  2. 缺 product_type → The product type field is required(枚举:TICKET 票券 / GENERAL 商品);
  3. 缺 product_category_id → The product category id field is required;
  4. PAID 类型缺 prices 数组 → The prices field is required(价格单位为「分」,每项含 label/price/initial_quantity_available);
  5. 库存字段名是 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 ticketsproducts 数组条目数与购票人数不匹配:每个数组元素代表 1 位购票人;quantity=2 需拆成 2 个元素(或 qty 各 1)
HTTP 500payload 结构错误(如缺 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)。

八、参考链接