Propose a write to a connected account (reply to a review, update a listing, set keywords, upload screenshots, change Apple Ads bids / keywords / negatives); always start with dry_run=true.
curl --request POST \
--url https://api.appgoat.ai/v1/actions \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '
{
"type": "review.reply",
"dry_run": true,
"source": "playbook:reply-to-every-review",
"params": {
"app": "1234567890",
"review_id": "00000043-a2fa-4f01-0000-000000000000",
"text": "Thanks for the detailed report! Version 2.4.1 fixes the login loop."
}
}
'const options = {
method: 'POST',
headers: {'x-api-key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
type: 'review.reply',
dry_run: true,
source: 'playbook:reply-to-every-review',
params: {
app: '1234567890',
review_id: '00000043-a2fa-4f01-0000-000000000000',
text: 'Thanks for the detailed report! Version 2.4.1 fixes the login loop.'
}
})
};
fetch('https://api.appgoat.ai/v1/actions', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.appgoat.ai/v1/actions"
payload = {
"type": "review.reply",
"dry_run": True,
"source": "playbook:reply-to-every-review",
"params": {
"app": "1234567890",
"review_id": "00000043-a2fa-4f01-0000-000000000000",
"text": "Thanks for the detailed report! Version 2.4.1 fixes the login loop."
}
}
headers = {
"x-api-key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"data": {
"id": "act_Qm3xXk2Lw9PaZr0cVb",
"type": "review.reply",
"connection_id": "<string>",
"status": "draft",
"params": {},
"diff": {
"target": {},
"changes": [
{
"field": "<string>",
"before": "<unknown>",
"after": "<unknown>"
}
],
"summary": "<string>",
"notes": [
"<string>"
]
},
"guardrail": {
"status": "passed",
"score": 123,
"checks": {},
"warnings": [
"<string>"
],
"checks_failed": [
{
"id": "<string>",
"severity": "block",
"message": "<string>"
}
],
"model": "<string>"
},
"result": "<unknown>",
"error": "<string>",
"created_by": "<string>",
"approved_by": "<string>",
"rejected_reason": "<string>",
"credits": 123,
"created_at": "2023-11-07T05:31:56Z",
"applied_at": "2023-11-07T05:31:56Z",
"expires_at": "2023-11-07T05:31:56Z",
"approve_url": "<string>",
"audit": [
{
"event": "<string>",
"actor": "<string>",
"at": "2023-11-07T05:31:56Z",
"detail": {}
}
]
},
"meta": {
"credits_used": 123,
"credits_remaining": 123,
"request_id": "<string>",
"freshness": {
"fetched_at": "2023-11-07T05:31:56Z",
"age_seconds": 123,
"stale": true,
"refreshing": true,
"job_id": "<string>"
},
"next_cursor": "<string>",
"store": "apple",
"warnings": [
"<string>"
]
}
}{
"error": {
"code": "invalid_param",
"message": "limit must be ≤ 100",
"hint": "Fix the parameter and retry.",
"docs": "https://appgoat.ai/docs/errors#invalid_param",
"param": "limit"
}
}{
"error": {
"code": "invalid_param",
"message": "<string>",
"hint": "<string>",
"docs": "<string>",
"param": "<string>"
}
}{
"error": {
"code": "invalid_param",
"message": "<string>",
"hint": "<string>",
"docs": "<string>",
"param": "<string>"
}
}{
"error": {
"code": "invalid_param",
"message": "<string>",
"hint": "<string>",
"docs": "<string>",
"param": "<string>"
}
}{
"error": {
"code": "invalid_param",
"message": "<string>",
"hint": "<string>",
"docs": "<string>",
"param": "<string>"
}
}Actions
Create action
Computes the before → after diff from the live account and runs guardrails (policy / tone / claims
probabilities for text, plus deterministic checks like character limits and bid jumps). Free.
dry_run: true→ adraft(24 h) with the diff: show it to the user. To go ahead, callPOST /v1/actions/{id}/approve.- otherwise →
pending_approval(a person approves it in the dashboard, link inapprove_url), orappliedimmediately when the org has an auto-apply rule for this type that the guardrail score clears (Studio, 1 credit). Blocking check failures (over a character limit, nothing to change, not editable) return 400 withchecks(a dry run returns them inguardrail.checks_failedinstead).
Params per type (app = a linked App Store id or package name, locale like en-US):
review.reply(asc, play_console):{ app, review_id, text }— review ids from GET /v1/connections//reviews. Play replies ≤ 350 chars.listing.update(asc, play_console):{ app, locale, fields: { name|title, subtitle, description, promotional_text, whats_new, marketing_url, support_url, privacy_policy_url } }(Play: title, short_description, full_description, video).keywords.set(asc):{ app, locale, keywords: [..] | "a,b,c", mode?: replace|add|remove }— 100 bytes comma-joined.screenshots.upload(asc, play_console):{ app, locale, display_type: APP_IPHONE_67 … (asc) | image_type: phoneScreenshots … (play), images: [{ url }], replace?: true }.ads.keyword.bid(apple_ads):{ campaign_id, ad_group_id, keywords: [{ id, bid?, status?: ENABLED|PAUSED }], max_increase?: 1 }.ads.keyword.add(apple_ads):{ campaign_id, ad_group_id, keywords: [{ text, match_type?: EXACT|BROAD, bid? }] }.ads.negative.add(apple_ads):{ campaign_id, ad_group_id?, keywords: [{ text, match_type? }] }.
POST
/
actions
Propose a write to a connected account (reply to a review, update a listing, set keywords, upload screenshots, change Apple Ads bids / keywords / negatives); always start with dry_run=true.
curl --request POST \
--url https://api.appgoat.ai/v1/actions \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '
{
"type": "review.reply",
"dry_run": true,
"source": "playbook:reply-to-every-review",
"params": {
"app": "1234567890",
"review_id": "00000043-a2fa-4f01-0000-000000000000",
"text": "Thanks for the detailed report! Version 2.4.1 fixes the login loop."
}
}
'const options = {
method: 'POST',
headers: {'x-api-key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
type: 'review.reply',
dry_run: true,
source: 'playbook:reply-to-every-review',
params: {
app: '1234567890',
review_id: '00000043-a2fa-4f01-0000-000000000000',
text: 'Thanks for the detailed report! Version 2.4.1 fixes the login loop.'
}
})
};
fetch('https://api.appgoat.ai/v1/actions', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.appgoat.ai/v1/actions"
payload = {
"type": "review.reply",
"dry_run": True,
"source": "playbook:reply-to-every-review",
"params": {
"app": "1234567890",
"review_id": "00000043-a2fa-4f01-0000-000000000000",
"text": "Thanks for the detailed report! Version 2.4.1 fixes the login loop."
}
}
headers = {
"x-api-key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"data": {
"id": "act_Qm3xXk2Lw9PaZr0cVb",
"type": "review.reply",
"connection_id": "<string>",
"status": "draft",
"params": {},
"diff": {
"target": {},
"changes": [
{
"field": "<string>",
"before": "<unknown>",
"after": "<unknown>"
}
],
"summary": "<string>",
"notes": [
"<string>"
]
},
"guardrail": {
"status": "passed",
"score": 123,
"checks": {},
"warnings": [
"<string>"
],
"checks_failed": [
{
"id": "<string>",
"severity": "block",
"message": "<string>"
}
],
"model": "<string>"
},
"result": "<unknown>",
"error": "<string>",
"created_by": "<string>",
"approved_by": "<string>",
"rejected_reason": "<string>",
"credits": 123,
"created_at": "2023-11-07T05:31:56Z",
"applied_at": "2023-11-07T05:31:56Z",
"expires_at": "2023-11-07T05:31:56Z",
"approve_url": "<string>",
"audit": [
{
"event": "<string>",
"actor": "<string>",
"at": "2023-11-07T05:31:56Z",
"detail": {}
}
]
},
"meta": {
"credits_used": 123,
"credits_remaining": 123,
"request_id": "<string>",
"freshness": {
"fetched_at": "2023-11-07T05:31:56Z",
"age_seconds": 123,
"stale": true,
"refreshing": true,
"job_id": "<string>"
},
"next_cursor": "<string>",
"store": "apple",
"warnings": [
"<string>"
]
}
}{
"error": {
"code": "invalid_param",
"message": "limit must be ≤ 100",
"hint": "Fix the parameter and retry.",
"docs": "https://appgoat.ai/docs/errors#invalid_param",
"param": "limit"
}
}{
"error": {
"code": "invalid_param",
"message": "<string>",
"hint": "<string>",
"docs": "<string>",
"param": "<string>"
}
}{
"error": {
"code": "invalid_param",
"message": "<string>",
"hint": "<string>",
"docs": "<string>",
"param": "<string>"
}
}{
"error": {
"code": "invalid_param",
"message": "<string>",
"hint": "<string>",
"docs": "<string>",
"param": "<string>"
}
}{
"error": {
"code": "invalid_param",
"message": "<string>",
"hint": "<string>",
"docs": "<string>",
"param": "<string>"
}
}Authorizations
Create a key at https://dash.appgoat.ai.
Body
application/json
Available options:
review.reply, listing.update, keywords.set, screenshots.upload, ads.keyword.bid, ads.keyword.add, ads.negative.add Connection id; optional when exactly one active connection fits the type (and params.app).
Who proposes it: api, mcp or playbook: (shown in the approvals list).