App Store Connect API Webhook 串接|提升 iOS CI/CD 自动化效率与通知流程
针对 iOS 开发者面临的建置等待与通知延迟问题,透过 App Store Connect API Webhook 实现即时事件推送,结合 Fastlane 与 CI/CD 工具,打造零等待成本的自动化工作流程,提升团队开发效率与发布准确度。
基于 SEO 考量,本文标题与描述经 AI 调整,原始版本请参考内文。
文章目录
[CI/CD] 使用 App Store Connect API Webhook 串接自动化工作流程
App Store Connect Webhook 应用案例分析与实际串接使用教学。
Photo by Volodymyr Hryshchenko
前言
苹果近年持续扩充 App Store Connect API,对开发者是一大福音。早期连凭证管理都得靠「Hardcore」的 Web Session(有期限、还要收简讯验证码),很难整合进 CI/CD;像商城评价也只能依赖不稳定的 RSS。
这几年几乎每年都有新功能补齐,从开发、测试到部署流程,甚至后期的评价、财务、数据报表都逐步原生支援,另外也强化了使用者管理、群组、TestFlight 等能力;让 App Store Connect API 可以更好的提升 Apple 开发者开发体验 。
WWDC 2025 Automate your development process with the App Store Connect API
2025 WWDC 更带来引颈期盼的重磅功能 — Webhook 通知:
建置版本上传状态 (The status of a build upload changes. ) 当建置版本上传状态更改时收到相关资料。
Complete / Failed / ProcessingApp 版本状态 (The status of an app version changes. ) 当 App 版本状态更改时收到相关资料。
Prepare for Submission / Ready for Review / Waiting for Review / Ready for Distribution / Rejected…TestFlight 版本状态 (New TestFlight feedback is submitted by a tester. ) 当测试人员留下回馈(当机回馈/截图回馈)时收到相关资料。
Apple-hosted 的资源包状态改变 (The status of an Apple-hosted asset pack version changes. ) 当 Apple 托管的素材包版本出现特定变更时收到相关资料。
App Store Connect API / Webhook notifications :
Webhooks enable a system to send real-time data to another system over the web.
Webhook 让一个系统能够透过网路即时将资料传送给另一个系统。
Unlike traditional APIs, where one system must make a request when receiving data, a webhook enables you to push data to the receiving system as soon as an event occurs.
与传统 API 不同的是,传统 API 需要由接收资料的一方主动发送请求,而 Webhook 则能在事件发生的当下,立即将资料推送给接收系统。
Webhooks are event-driven, meaning they are triggered by a specific action or event and immediately send the relevant data to a predefined URL, also called the “webhook URL” or “callback URL”.
Webhook 是事件驱动的,表示它们会在特定动作或事件被触发时,立即将相关资料传送到一个预先设定的 URL,该 URL 也称为「Webhook URL」或「Callback URL」。
A notification webhook is an endpoint you create on your server.
通知型 Webhook 是你在自己伺服器上建立的一个端点(endpoint)。
This webhook endpoint receives HTTP POST requests from App Store Connect.
这个 Webhook 端点会接收来自 App Store Connect 的 HTTP POST 请求。
The POST requests describe important events about your app.
这些 POST 请求会描述与你的 App 相关的重要事件。
Use the webhooks notifications endpoint to configure the notifications for events happening to your apps.
你可以使用 Webhook 通知端点,来设定当你的 App 发生各种事件时所要接收的通知。
5 个应用案例
1. 建置处理完成后触发送审
Before:
以往在实现 App CI/CD 打包送审时,打包上传后 需要等待 Apple 处理完成 才能继续送审;Fastlane 默认的做法是 Polling App Store Connect 检查上传的建置状态,直到 Complete 后才会继续执行送审 Lane。
等待时间大约 20 分钟 ,如果是 Self-hosted CI/CD 没什么差,但如果是使用云端服务,这 20 分钟的等待非常的浪费资源,以 GitHub Runner mac-OS 1 分钟 US$ 0.062, 光等待送审每次就产生无意义的花费 1.24 US$。
Ref: Build Completed Processing 通知信 搭配 Gmail Filter + Google Apps Script
在没有 Webhook 可以主动通知的年代,之前曾用过「 Build Completed Processing 通知信搭配 Gmail Filter + Google Apps Script 来触发 」可以达到效果,但是过程有点 Hardcore。
After:
有了 Webhook 之后,打包上传的步骤上传完毕即可结束。
App Store Connect Build Process Complete 后会发 Webhook 通知,我们收到通知后再继续执行送审步骤。
零干等成本
2. GitFlow Release 流程对齐 App Release 时机点
Before:
GitFlow 最后一步上线时需要把 develop 分支 merge 回 master 分支,master 分支对应的是当前线上版。
以往只能定时手动/自动执行,例如周一 PM 会发布 App,周一固定把 develop 回 master;手动执行很烦、自动执行的话假设延期?周一刚好放假?实际 App 没有 Release 但是 develop 先回 master 了。
在大多数情况不太重要,只有很极端的状况,例如这时间中间又要插 Hotfix 就会有可能有落差;但若要追求完整稳定的 CI/CD 开发流程,这也是一个值得探讨的案例。
另一条思路同上,用「 App is Ready for Sale 通知信搭配 Gmail Filter + Google Apps Script 来触发 」是可行的。
After:
有了 Webhook 之后,可以直接在收到 App 发布通知后触发 CI/CD Action (Master to Develop)。
可以确保 App 是真的上线才回 Master
3. App Release Messages
App 上线发布给使用者后,还有另一个很常见内部工作流程,发布通知给相关团队、版本包含的任务、Complete 相关任务。
Before:
同上,只能手动或定时自动或使用信件 搭配 Gmail Filter + Google Apps Script 来触发 。
After:
- 有了 Webhook 之后,可以在收到 App 发布通知后串接 Jira/Asana API 捞取对应版号的单批次 Complete 并且发布包含的任务单完成释出讯息到 Slack。
4. Build Failed / Review Rejected Notifier
前面 1,2 有提到以往有机会透过收信件通知来触发工作流程,但在团队规模很大权限控管严格的组织里, iOS 开发者只会有「开发者」后台权限,无法释出、管理 App,因此也无法收到任何 App 状态改变的信件通知;其中也包含上传的建置版本被拒或是送审被拒的通知信 。
Before:
以往只能靠好心人 (a.k.a PM) 转发信件给工程,如果好心人也没注意到可能就真的到要上线时才发现 App 被拒绝了无法上线!
After:
- 这个案例相对简单,就是收到 Webhook 通知之后,转发讯息到 Slack。
5. Testflight Feedback Notifier
类似 4. 只是改接 Testflight Feedback Webhook 通知。
Before:
以往开发者只能自己上去 App Store Connect Testflight 后台查看测试使用者的回馈或闪退问题, 非常容易被忽略(甚至曾经发生过一年前回报的建议问题一年后才有人看到) 。
After:
- 收到 Testflight Feedback Webhook 通知之后,转发讯息到 Slack 。
— — —
其他应用方式欢迎大家天马行空发想,再来将介绍如何串接使用。
App Store Connect API Webhook 设定
权限要求: 需要 Admin, Account Holder 权限才能设定 。
建立 App Store Connect API Webhook 通知
前往「使用者与存取权限 (Users and Access)」 -> 「整合 (Integrations)」
点击「其他整合 (Additional)」 下的 「Webhooks」
点击「建立 Webhook」按钮
名称:输入 Webhook 名称
承载 URL(Payload URL):输入你的 Webhook 通知接收服务网址
密钥(Secret) 字串:Webhook Request 验证密钥(可 随机产生一个字串 来用)
App:选择预接收 Webhook 通知的 App
触发事件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
TestFlight 回馈
当测试人员留下回馈时收到相关资料。
[] 当机回馈
[] 截图回馈
[] TestFlight 版本状态
当 TestFlight 版本状态更改时收到相关资料。进一步了解
[]App 版本状态
当 App 版本状态更改时收到相关资料。进一步了解
[]建置版本上传状态
当建置版本上传状态更改时收到相关资料。进一步了解
背景素材
当 Apple 托管的素材包版本出现特定变更时收到相关资料。进一步了解
[]更新App Store 发布版本
[]更新外部 TestFlight 发布版本
[]建立内部 TestFlight 发布版本
[]更新素材包版本
依照需求勾选,也可以全选收到通知后再判断要不要处理。
最后点击「新增」建立 Webhook。
测试 App Store Connect API Webhook 通知
进入 Webhook 页。
点击右上角「测试」接收测试通知。
测试通知内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Headers:
{
"content-type": "application/json",
"x-apple-jingle-correlation-key": "PNSCHDQW3MY2AX6VSRFHYYNUL4",
"x-apple-request-uuid": "7b64238e-16db-31a0-5fd5-944a7c61b45f",
"x-apple-signature": "hmacsha256=cf50020f0bbd3c5274860594f616f1806965c1f9fb765d8d278f512dff5b4c0e",
}
Body:
{
"data" : {
"type" : "webhookPingCreated",
"id" : "65726e27-cb79-47f2-a3e4-c8ced9f356e8",
"version" : 1,
"attributes" : {
"timestamp" : "2025-12-26T15:47:38.472168681Z"
}
}
}
App Store Connect API Webhook 通知发送纪录
Webhook 页下方的「最近的提交项目」会显示最近发送的 Webhook 事件。
验证 App Store Connect API Webhook 通知
建立 Webhook 时我们有输入一个「密钥(Secret) 字串」建议对请求进行验证,防止这个 Webhook URL 外泄,有心之人能随意伪造发送 Webhook 事件到你的服务上。
验证方式:
对 Request Body 用你设定的密钥字串做 HMAC-SHA256 并输出 HEX 进位字串,用这个字串比对 Request Headers 中的
x-apple-signature值的hmacsha256=后面带的字串。
实现方式 — Nodejs:
1
2
3
4
5
6
7
8
9
10
import crypto from 'crypto';
function verifyAppleWebhook(rawBody, appleSignature, secret) {
const hex = crypto
.createHmac('sha256', secret)
.update(rawBody, 'utf8')
.digest('hex');
return `hmacsha256=${hex}` === appleSignature;
}
实现方式 — Cloudflare Worker:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
function bufferToHex(buffer) {
return [...new Uint8Array(buffer)]
.map(b => b.toString(16).padStart(2, '0'))
.join('');
}
async function hmacSha256Hex(secret, message) {
const enc = new TextEncoder();
const key = await crypto.subtle.importKey(
'raw',
enc.encode(secret),
{ name: 'HMAC', hash: 'SHA-256' },
false,
['sign']
);
const signature = await crypto.subtle.sign(
'HMAC',
key,
enc.encode(message)
);
return bufferToHex(signature);
}
async function verifyAppleWebhook(request, secret) {
const appleSignature = request.headers.get('X-Apple-Signature');
const rawBody = await request.clone().text();
const calculated = await hmacSha256Hex(secret, rawBody);
return "hmacsha256="+calculated === appleSignature;
}
- Cloudflare Worker 无 crypto module,要使用 Web Crypto API(crypto.subtle)
实现方式 — Google Apps Script Web App ❌
碍于技术限制,Google Apps Script Web App doGet(e)/doPost(e) 无法从中取得 Request Headers,因此无法使用此方法对请求来源进行验证。
最多只能自己在 URL Query 加一些密钥参数做很简单的判断保护。
App Store Connect API Webhook 通知 Payload
这边有搜集了几个 App 上传、送审过程会收到的事件 Payload,提供给大家做自动化开发时可以直接参考。
Webhook 只会发送事件跟状态名称,不会给其他详细资讯,例如:版本号、拒审原因…等等; 我们需要自己再打 App Store Connect API 带上 Event Payload 里的 Relationships Link 才能取得完整资讯。
建置版本上传 — Process Complete
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"data" : {
"type" : "buildUploadStateUpdated",
"id" : "xxx-xx-xx-xx-xxx",
"version" : 1,
"attributes" : {
"oldState" : "PROCESSING",
"newState" : "COMPLETE"
},
"relationships" : {
"instance" : {
"data" : {
"type" : "buildUploads",
"id" : "xxx-xx-xx-xx-xxx"
},
"links" : {
"self" : "https://api.appstoreconnect.apple.com/v1/buildUploads/xxx-xx-xx-xx-xxx"
}
}
}
}
}
建置版本上传 — Process Failed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"data": {
"type": "buildUploadStateUpdated",
"id": "xxx-xx-xx-xx-xxx",
"version": 1,
"attributes": {
"oldState": "PROCESSING",
"newState": "FAILED"
},
"relationships": {
"instance": {
"data": {
"type": "buildUploads",
"id": "xxx-xx-xx-xx-xxx"
},
"links": {
"self": "https://api.appstoreconnect.apple.com/v1/buildUploads/xxx-xx-xx-xx-xxx"
}
}
}
}
}
多半为 Binary 遭拒,例如有用到麦克风但是没宣告之类的。
App 版本状态 — Prepare For Submission (准备提交)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
"data" : {
"type" : "appStoreVersionAppVersionStateUpdated",
"id" : "xxx-xx-xx-xx-xxx",
"version" : 1,
"attributes" : {
"newValue" : "PREPARE_FOR_SUBMISSION",
"oldValue" : "DEVELOPER_REJECTED",
"timestamp" : "2025-12-18T05:01:47.118Z"
},
"relationships" : {
"instance" : {
"data" : {
"type" : "appStoreVersions",
"id" : "xxx-xx-xx-xx-xxx"
},
"links" : {
"self" : "https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xx-xx-xx-xxx"
}
}
}
}
}
当新版本号建立好,准备提交送审时,这阶段可填写版本资讯、更新内容、选择要送审的 Build。
App 版本状态 — Ready For Review (准备送审)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
"data" : {
"type" : "appStoreVersionAppVersionStateUpdated",
"id" : "xxx-xx-xx-xx-xxx",
"version" : 1,
"attributes" : {
"newValue" : "READY_FOR_REVIEW",
"oldValue" : "PREPARE_FOR_SUBMISSION",
"timestamp" : "2025-12-18T03:41:12.516Z"
},
"relationships" : {
"instance" : {
"data" : {
"type" : "appStoreVersions",
"id" : "xxx-xx-xx-xx-xxx"
},
"links" : {
"self" : "https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xx-xx-xx-xxx"
}
}
}
}
}
送审资料确定,准备送审时。
App 版本状态 — Waiting For Review (已送审,等待审核)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
"data" : {
"type" : "appStoreVersionAppVersionStateUpdated",
"id" : "xxx-xx-xx-xx-xxx",
"version" : 1,
"attributes" : {
"newValue" : "WAITING_FOR_REVIEW",
"oldValue" : "READY_FOR_REVIEW",
"timestamp" : "2025-12-18T03:41:21.179Z"
},
"relationships" : {
"instance" : {
"data" : {
"type" : "appStoreVersions",
"id" : "xxx-xx-xx-xx-xxx"
},
"links" : {
"self" : "https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xx-xx-xx-xxx"
}
}
}
}
}
App 已完成送审,正在等待审查。
App 版本状态 — Developer Rejected (开发者拒绝)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
"data" : {
"type" : "appStoreVersionAppVersionStateUpdated",
"id" : "xxx-xx-xx-xx-xxx",
"version" : 1,
"attributes" : {
"newValue" : "DEVELOPER_REJECTED",
"oldValue" : "WAITING_FOR_REVIEW",
"timestamp" : "2025-12-18T03:50:30.552Z"
},
"relationships" : {
"instance" : {
"data" : {
"type" : "appStoreVersions",
"id" : "xxx-xx-xx-xx-xxx"
},
"links" : {
"self" : "https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xx-xx-xx-xxx"
}
}
}
}
}
开发者撤回正在送审的版本。
App 版本状态 — In Review (官方正在审核)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
"data" : {
"type" : "appStoreVersionAppVersionStateUpdated",
"id" : "xxx-xx-xx-xx-xxx",
"version" : 1,
"attributes" : {
"newValue" : "IN_REVIEW",
"oldValue" : "WAITING_FOR_REVIEW",
"timestamp" : "2025-12-18T22:05:50.038Z"
},
"relationships" : {
"instance" : {
"data" : {
"type" : "appStoreVersions",
"id" : "xxx-xx-xx-xx-xxx"
},
"links" : {
"self" : "https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xx-xx-xx-xxx"
}
}
}
}
}
App 版本状态 — Pending Developer Release (审核完毕等待发布)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
"data" : {
"type" : "appStoreVersionAppVersionStateUpdated",
"id" : "xxx-xx-xx-xx-xxx",
"version" : 1,
"attributes" : {
"newValue" : "PENDING_DEVELOPER_RELEASE",
"oldValue" : "IN_REVIEW",
"timestamp" : "2025-12-18T22:34:18.785Z"
},
"relationships" : {
"instance" : {
"data" : {
"type" : "appStoreVersions",
"id" : "xxx-xx-xx-xx-xxx"
},
"links" : {
"self" : "https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xx-xx-xx-xxx"
}
}
}
}
}
Pending Developer Release 事件的时间 减掉 Waiting For Review 事件的时间就 等于 App 送审到可发布状态中间等待的时间了。
App 版本状态 — Ready for Distribution (App 准备发布) a.k.a Ready For Sale
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
"data" : {
"type" : "appStoreVersionAppVersionStateUpdated",
"id" : "xxx-xx-xx-xx-xxx",
"version" : 1,
"attributes" : {
"newValue" : "READY_FOR_DISTRIBUTION",
"oldValue" : "PENDING_DEVELOPER_RELEASE",
"timestamp" : "2025-12-23T06:03:50.925Z"
},
"relationships" : {
"instance" : {
"data" : {
"type" : "appStoreVersions",
"id" : "xxx-xx-xx-xx-xxx"
},
"links" : {
"self" : "https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xx-xx-xx-xxx"
}
}
}
}
}
App 已准备发布 (几乎等同 Ready For Sale,没有 Ready For Sale 这个事件)。
Your app has been accepted and is ready for distribution.
To distribute your app, your agreements must be in effect. The Account Holder can accept the latest agreements in the Business section.
App Store Connect API Webhook 工作流程串接
方法 1 — 借用 Fastlane 串接 App Store Connect API
这边最快的方式是直接用 CI/CD 服务触发,然后借用 Fastlane 自带的 Spaceship 串接 App Store Connect API。
如果你本来 Fastlane 就有使用 App Store Connect API 管理 Match 凭证、送审,这个方法可以直接无痛使用;如果没有要先 参考官方文件 产生 API Key 跟安全存放在 CI/CD 服务的密钥中。
- App Store Connect
- App 有状态改变时
- 触发 Webhook
- Webhook Endpoint
可以是 自架服务/API 或是 简单用 FAAS 服务 (Cloudflare Worker / AWS Lambda / Cloud Functions / Google Apps Script)- 验证 Webhook (optional)
- 处理 Webhook 事件、转发事件请求到 CI/CD 服务上执行
e.g. 透过 GitHub API 触发 GitHub Actions..
- CI/CD Service
GitHub Actions / Bitbucket Pipeline / Gitlab Runner…- 触发 Action
- 执行 Fastlane 脚本,复用 Fastlane Spaceship 验证
- App Store Connect
- 打 App Store Connect API 取得完整资讯
- CI/CD Service
- 后续步骤,例如发通知、触发另一个 Action
Fastlane Example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# Usage:
# bundle exec fastlane appStoreConnectWebhookHandler \
# data:'{"data":{"type":"buildUploadStateUpdated","id":"xxx-xxx-xxx-xx-xxx","version":1,"attributes":{"oldState":"PROCESSING","newState":"COMPLETE"},"relationships":{"instance":{"data":{"type":"buildUploads","id":"xxx-xxx-xxx-xx-xxx"},"links":{"self":"https://api.appstoreconnect.apple.com/v1/buildUploads/xxx-xxx-xxx-xx-xxx"}}}}}'
# Notes:
# - `data:` must be a JSON string.
# - This lane is intended for local debugging (it prints the GET response).
desc "[Automation] Handle App Store Connect webhook payload and fetch related instance via ASC API"
lane :appStoreConnectWebhookHandler do \\|options\\|
begin
data = options[:data]
UI.user_error!("Missing data") if data.empty?
data = JSON.parse(data)
url = data.dig("data", "relationships", "instance", "links", "self").to_s.strip
UI.user_error!("Missing instance self url in JSON") if url.empty?
api_key = app_store_connect_api_key(
key_id: "xxxx",
issuer_id: "xxxx-xxxx-xxxx-xxxx-165aa6465141",
key_filepath: "./AuthKey_xxxx.p8",
duration: 1200, # optional (maximum 1200)
in_house: false # optional but may be required if using match/sigh
)
loadAppStoreConnectAPIKey
#
uri = URI.parse(url)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
store = OpenSSL::X509::Store.new
store.set_default_paths
http.cert_store = store
request = Net::HTTP::Get.new(uri.request_uri)
token = Spaceship::ConnectAPI.token
UI.user_error!("App Store Connect API token is not available. Make sure app_store_connect_api_key is configured correctly.") if token.nil?
request['Authorization'] = "Bearer #{token.text}"
request['Content-Type'] = 'application/json'
request['Accept'] = 'application/json'
response = http.request(request)
UI.message("📡 GET #{url} Response: [#{response.code}] #{response.message}")
UI.message(response.body)
#
response
## handle response...do next actions...
rescue => e
UI.error("❌ Failed handle App Store Connect API Webhook: #{e}")
end
end
方法 2— 在 Webhook Endpoint 自行处理
第二个方法就是直接在 Webhook Endpoint 服务上处理完所有事情,但 缺点是需要把 App Store Connect API Key 放到服务上跟要自己做 Token 验证 。
Ruby Example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
require 'jwt'
require 'net/http'
require 'time'
keyFile = File.read('./AuthKey_XXXX.p8') # replace to your .p8 private key file (donwload from app stroe connect)
privateKey = OpenSSL::PKey::EC.new(keyFile)
payload = {
iss: 'YOUR_ISSUE_ID', # replace to your issue id (get in app store connect user access->key->app store connect api page)
iat: Time.now.to_i,
exp: Time.now.to_i + 60*20,
aud: 'appstoreconnect-v1'
}
token = JWT.encode payload, privateKey, 'ES256', header_fields={kid:"YOUR_KEY_ID", typ:"JWT"} # replace to your key id (get in app store connect user access->key->app store connect api page)
puts token
decoded_token = JWT.decode token, privateKey, true, { algorithm: 'ES256' }
puts decoded_token
# 替换成 Webhook Payload 中的 relationships link
uri = URI("https://api.appstoreconnect.apple.com/v1/apps/APPID/customerReviews") # repleace APPID to your app id in app store connect -> your app -> app information -> apple id
https = Net::HTTP.new(uri.host, uri.port)
https.use_ssl = true
request = Net::HTTP::Get.new(uri)
request['Authorization'] = "Bearer #{token}";
response = https.request(request)
puts response.read_body
App Store Connect API Key 产生方式、Token 产生方式、API 使用请参考「 App Store Connect API 现已支援 读取和管理 Customer Reviews 」。
App Store Connect API Response
这里附上一些收到 Webhook Event 后再打 Relationships Link 取得完整资讯的一些 Response 范例。
建置版本上传 — Process Complete
https://api.appstoreconnect.apple.com/v1/buildUploads/xxx-xx-xx-xx-xxx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{
"data": {
"type": "buildUploads",
"id": "xx-xx-xx-xxx-xx",
"attributes": {
"cfBundleShortVersionString": "1.101.0",
"cfBundleVersion": "1",
"createdDate": "2025-12-25T08:26:43-08:00",
"state": {
"errors": [],
"warnings": [],
"infos": [],
"state": "COMPLETE"
},
"platform": "IOS",
"uploadedDate": "2025-12-25T08:28:35-08:00"
},
"relationships": {
"buildUploadFiles": {
"links": {
"self": "https://api.appstoreconnect.apple.com/v1/buildUploads/xx-xx-xx-xxx-xx/relationships/buildUploadFiles",
"related": "https://api.appstoreconnect.apple.com/v1/buildUploads/xx-xx-xx-xxx-xx/buildUploadFiles"
}
}
},
"links": {
"self": "https://api.appstoreconnect.apple.com/v1/buildUploads/xx-xx-xx-xxx-xx"
}
},
"links": {
"self": "https://api.appstoreconnect.apple.com/v1/buildUploads/xx-xx-xx-xxx-xx"
}
}
建置版本上传 — Process Failed
https://api.appstoreconnect.apple.com/v1/buildUploads/xxx-xx-xx-xx-xxx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
{
"data": {
"type": "buildUploads",
"id": "xx-xx-xx-xx-xxx",
"attributes": {
"cfBundleShortVersionString": "1.101.0",
"cfBundleVersion": "3",
"createdDate": "2025-12-12T09:03:32-08:00",
"state": {
"errors": [
{
"code": "90683",
"description": "Missing purpose string in Info.plist. Your app’s code references one or more APIs that access sensitive user data, or the app has one or more entitlements that permit such access. The Info.plist file for the “My.app” bundle should contain a NSMicrophoneUsageDescription key with a user-facing purpose string explaining clearly and completely why your app needs the data. If you’re using external libraries or SDKs, they may reference APIs that require a purpose string. While your app might not use these APIs, a purpose string is still required. For details, visit: https://developer.apple.com/documentation/uikit/protecting_the_user_s_privacy/requesting_access_to_protected_resources."
}
],
"warnings": [],
"infos": [],
"state": "FAILED"
},
"platform": "IOS",
"uploadedDate": "2025-12-12T09:05:26-08:00"
},
"relationships": {
"buildUploadFiles": {
"links": {
"self": "https://api.appstoreconnect.apple.com/v1/buildUploads/xx-xx-xx-xx-xxx/relationships/buildUploadFiles",
"related": "https://api.appstoreconnect.apple.com/v1/buildUploads/xx-xx-xx-xx-xxx/buildUploadFiles"
}
}
},
"links": {
"self": "https://api.appstoreconnect.apple.com/v1/buildUploads/xx-xx-xx-xx-xxx"
}
},
"links": {
"self": "https://api.appstoreconnect.apple.com/v1/buildUploads/xx-xx-xx-xx-xxx"
}
}
ITMS-90683 为例。
App 版本状态
https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xx-xx-xx-xxx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
{
"data": {
"type": "appStoreVersions",
"id": "xxx-xxx-xxx-xxx",
"attributes": {
"platform": "IOS",
"versionString": "1.101.0",
"appStoreState": "READY_FOR_SALE",
"appVersionState": "READY_FOR_DISTRIBUTION",
"copyright": "© 2025 ZhgChgLi.",
"reviewType": "APP_STORE",
"releaseType": "MANUAL",
"earliestReleaseDate": null,
"usesIdfa": null,
"downloadable": true,
"createdDate": "2025-12-15T19:12:55-08:00"
},
"relationships": {
"ageRatingDeclaration": {
"links": {
"self": "https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/relationships/ageRatingDeclaration",
"related": "https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/ageRatingDeclaration"
}
},
"appStoreVersionLocalizations": {
"links": {
"self": "https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/relationships/appStoreVersionLocalizations",
"related": "https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/appStoreVersionLocalizations"
}
},
"build": {
"links": {
"self": "https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/relationships/build",
"related": "https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/build"
}
},
"appStoreVersionPhasedRelease": {
"links": {
"self": "https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/relationships/appStoreVersionPhasedRelease",
"related": "https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/appStoreVersionPhasedRelease"
}
},
"gameCenterAppVersion": {
"links": {
"self": "https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/relationships/gameCenterAppVersion",
"related": "https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/gameCenterAppVersion"
}
},
"routingAppCoverage": {
"links": {
"self": "https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/relationships/routingAppCoverage",
"related": "https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/routingAppCoverage"
}
},
"appStoreReviewDetail": {
"links": {
"self": "https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/relationships/appStoreReviewDetail",
"related": "https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/appStoreReviewDetail"
}
},
"appStoreVersionSubmission": {
"links": {
"self": "https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/relationships/appStoreVersionSubmission",
"related": "https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/appStoreVersionSubmission"
}
},
"appClipDefaultExperience": {
"links": {
"self": "https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/relationships/appClipDefaultExperience",
"related": "https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/appClipDefaultExperience"
}
},
"appStoreVersionExperiments": {
"links": {
"self": "https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/relationships/appStoreVersionExperiments",
"related": "https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/appStoreVersionExperiments"
}
},
"appStoreVersionExperimentsV2": {
"links": {
"self": "https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/relationships/appStoreVersionExperimentsV2",
"related": "https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/appStoreVersionExperimentsV2"
}
},
"customerReviews": {
"links": {
"self": "https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/relationships/customerReviews",
"related": "https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/customerReviews"
}
},
"alternativeDistributionPackage": {
"links": {
"self": "https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/relationships/alternativeDistributionPackage",
"related": "https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/alternativeDistributionPackage"
}
}
},
"links": {
"self": "https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx"
}
},
"links": {
"self": "https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx"
}
}
如前述,详细 App 资讯、版号、错误原因都要打 API 才会拿到。
Done
至此我们已经可以透过 App Store Connect API Webhook 更好地完善 App CI/CD 及自动化工作流程,提升团队开发效率。
延伸阅读
有任何问题及指教欢迎 与我联络 。
本文首次发表于 Medium (点击查看原始版本),由 ZMediumToMarkdown 提供自动转换与同步技术。



](/assets/7c0974856393/1*2-Zn2ApgVYd5S5KzxMLEMQ.webp)








