@acedatacloud/skills 2026.621.4 → 2026.621.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@acedatacloud/skills",
3
- "version": "2026.621.4",
3
+ "version": "2026.621.5",
4
4
  "description": "Agent Skills for AceDataCloud AI services — music, image, video generation, LLM chat, web search. Compatible with Claude Code, GitHub Copilot, Gemini CLI, OpenAI Codex, and 30+ AI coding agents.",
5
5
  "keywords": [
6
6
  "agent-skills",
@@ -0,0 +1,75 @@
1
+ ---
2
+ name: cloudflare
3
+ description: Manage Cloudflare zones, DNS records, cache purge and Workers via the API v4. Use when the user mentions Cloudflare, a DNS record on a Cloudflare-hosted domain, purging / clearing the CDN cache, a zone's settings, WAF / firewall rules, or listing Workers.
4
+ when_to_use: |
5
+ Trigger when the user wants to list Cloudflare zones, read or change
6
+ DNS records, purge the cache, inspect Workers, or review firewall
7
+ rules. The connector stores a scoped Cloudflare API token; confirm
8
+ before any write (DNS create/update/delete, cache purge) and prefer
9
+ the smallest-blast-radius action.
10
+ connections: [cloudflare]
11
+ allowed_tools: [Bash]
12
+ license: Apache-2.0
13
+ metadata:
14
+ author: acedatacloud
15
+ version: "1.0"
16
+ ---
17
+
18
+ Call the **Cloudflare API v4** with `curl + jq`. The user's **scoped** token is
19
+ in `$CLOUDFLARE_API_TOKEN` (optionally `$CLOUDFLARE_ACCOUNT_ID` /
20
+ `$CLOUDFLARE_ZONE_ID`); every call needs `Authorization: Bearer
21
+ $CLOUDFLARE_API_TOKEN`. Base URL: `https://api.cloudflare.com/client/v4`.
22
+
23
+ Every response has `{"success": bool, "errors": [...], "result": ...}`. On
24
+ `success:false` show `.errors` verbatim. `403`/`9109` means the token lacks the
25
+ permission for that resource → the user must re-mint the token with the right
26
+ scope (zone DNS edit, cache purge, etc.).
27
+
28
+ ```bash
29
+ AUTH=(-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN")
30
+ API="https://api.cloudflare.com/client/v4"
31
+ # Zones the token can see
32
+ curl -sS "${AUTH[@]}" "$API/zones" | jq '.result[] | {id, name, status, plan: .plan.name}'
33
+ ```
34
+
35
+ ## DNS records
36
+
37
+ ```bash
38
+ ZONE="${CLOUDFLARE_ZONE_ID:?set or pick from the zones list}"
39
+ # List (filter with ?type=A&name=foo.example.com)
40
+ curl -sS "${AUTH[@]}" "$API/zones/$ZONE/dns_records?per_page=100" \
41
+ | jq '.result[] | {id, type, name, content, proxied, ttl}'
42
+
43
+ # Create (confirm first)
44
+ curl -sS -X POST "${AUTH[@]}" -H "Content-Type: application/json" \
45
+ -d '{"type":"CNAME","name":"www","content":"example.com","proxied":true}' \
46
+ "$API/zones/$ZONE/dns_records" | jq '.success, .result.id'
47
+
48
+ # Update PATCH /dns_records/{id} ; delete DELETE /dns_records/{id}
49
+ ```
50
+
51
+ ## Purge cache (confirm first)
52
+
53
+ ```bash
54
+ # Targeted purge by URL (preferred); use {"purge_everything":true} only if asked
55
+ curl -sS -X POST "${AUTH[@]}" -H "Content-Type: application/json" \
56
+ -d '{"files":["https://example.com/path"]}' \
57
+ "$API/zones/$ZONE/purge_cache" | jq '.success'
58
+ ```
59
+
60
+ ## Workers & firewall
61
+
62
+ ```bash
63
+ ACCT="${CLOUDFLARE_ACCOUNT_ID:?needed for account-scoped resources}"
64
+ curl -sS "${AUTH[@]}" "$API/accounts/$ACCT/workers/scripts" | jq '.result[] | {id, modified_on}'
65
+ # Firewall / WAF custom rules live under /zones/$ZONE/firewall/rules and rulesets.
66
+ ```
67
+
68
+ ## Gotchas
69
+
70
+ - Insist on a **scoped API token**, never the legacy Global API Key (the connect
71
+ form's help says so) — a Global Key can do anything on the account.
72
+ - `proxied:true` = orange-cloud (CDN/WAF on); `false` = DNS-only. Changing it can
73
+ break TLS/origin expectations — confirm intent.
74
+ - Account-scoped calls (Workers, some analytics) need `$CLOUDFLARE_ACCOUNT_ID`;
75
+ zone-scoped calls need a zone id (pick from `/zones` if env unset).
@@ -0,0 +1,68 @@
1
+ ---
2
+ name: figma
3
+ description: Read Figma design files, nodes, rendered images and comments via the Figma REST API. Use when the user mentions Figma, a figma.com file link, implementing a design as code, extracting design tokens / colors / spacing, or summarizing comments on a design.
4
+ when_to_use: |
5
+ Trigger when the user shares a Figma file URL or wants to read a
6
+ design — get the document tree / a specific frame, render a node to
7
+ PNG/SVG to "see" it, extract styles / tokens, or read comments.
8
+ Read-only. The file key comes from the Figma URL the user pastes.
9
+ connections: [figma]
10
+ allowed_tools: [Bash]
11
+ license: Apache-2.0
12
+ metadata:
13
+ author: acedatacloud
14
+ version: "1.0"
15
+ ---
16
+
17
+ Read **Figma** via `curl + jq`. The user's OAuth bearer token is in
18
+ `$FIGMA_TOKEN`; every call needs `Authorization: Bearer $FIGMA_TOKEN`. Base URL:
19
+ `https://api.figma.com/v1`.
20
+
21
+ Failures are `{"status":<code>,"err":"..."}` — show `err` verbatim. `403` means
22
+ the token lacks the scope or the file isn't shared with the user. `404` = bad
23
+ file key.
24
+
25
+ The **file key** is the `figma.com/file/<KEY>/...` or `figma.com/design/<KEY>/...`
26
+ segment of a pasted URL. A **node id** is in `?node-id=1-23` (Figma shows `1:23`;
27
+ the API also accepts `1:23`).
28
+
29
+ ```bash
30
+ F="https://api.figma.com/v1"; AUTH=(-H "Authorization: Bearer $FIGMA_TOKEN")
31
+ # Who am I (account card)
32
+ curl -sS "${AUTH[@]}" "$F/me" | jq '{handle, email}'
33
+ # File document tree (name + top-level frames). Big files: prefer /nodes below.
34
+ curl -sS "${AUTH[@]}" "$F/files/FILE_KEY?depth=2" \
35
+ | jq '{name, pages: [.document.children[] | {name, frames: [.children[]?.name]}]}'
36
+ ```
37
+
38
+ ## Read specific nodes & render images
39
+
40
+ ```bash
41
+ KEY="FILE_KEY"
42
+ # Just the nodes you care about (faster than the whole file)
43
+ curl -sS "${AUTH[@]}" "$F/files/$KEY/nodes?ids=1:23,1:45" \
44
+ | jq '.nodes | to_entries[] | {id: .key, name: .value.document.name, type: .value.document.type}'
45
+
46
+ # Render nodes to images — returns temporary CDN URLs (this is the "see it" tool)
47
+ curl -sS "${AUTH[@]}" "$F/images/$KEY?ids=1:23&format=png&scale=2" \
48
+ | jq '.images' # { "1:23": "https://...png" }
49
+ ```
50
+
51
+ For design-to-code, render the frame to PNG (to view) and read its node JSON
52
+ (layout/fills/typography) to extract exact colors, spacing and text.
53
+
54
+ ## Comments & projects
55
+
56
+ ```bash
57
+ curl -sS "${AUTH[@]}" "$F/files/FILE_KEY/comments" \
58
+ | jq '.comments[] | {user: .user.handle, at: .created_at, message}'
59
+ # Team projects → files (needs a team id from the Figma URL /team/<id>/...)
60
+ curl -sS "${AUTH[@]}" "$F/teams/TEAM_ID/projects" | jq '.projects'
61
+ ```
62
+
63
+ ## Gotchas
64
+
65
+ - Node ids: Figma URLs use `1-23` (dash); the API wants `1:23` (colon). Convert.
66
+ - `/images` URLs are **temporary** — download/use them promptly, don't store.
67
+ - `depth=` limits tree traversal; omit it only for small files or you'll pull
68
+ megabytes of node JSON.
@@ -0,0 +1,65 @@
1
+ ---
2
+ name: google-ads
3
+ description: Query Google Ads campaigns, ad groups, keywords and spend via the Google Ads API (GAQL searchStream). Use when the user mentions Google Ads, ad campaigns, ad spend / cost, impressions / clicks / conversions on ads, or campaign performance.
4
+ when_to_use: |
5
+ Trigger when the user wants Google Ads reporting — list accessible
6
+ customers, campaign / ad-group / keyword performance, spend and
7
+ conversions. Read via GAQL. Needs the OAuth token PLUS a platform
8
+ developer token and a login-customer-id; if those env vars are
9
+ absent the connector isn't fully provisioned yet — tell the user.
10
+ connections: [google/ads]
11
+ allowed_tools: [Bash]
12
+ license: Apache-2.0
13
+ metadata:
14
+ author: acedatacloud
15
+ version: "1.0"
16
+ ---
17
+
18
+ Query the **Google Ads API** via `curl + jq`. Three credentials are needed:
19
+
20
+ - `$GOOGLE_ADS_TOKEN` — the user's OAuth bearer (`adwords` scope) →
21
+ `Authorization: Bearer $GOOGLE_ADS_TOKEN`
22
+ - `$GOOGLE_ADS_DEVELOPER_TOKEN` — the platform's developer token (injected
23
+ server-side) → header `developer-token: $GOOGLE_ADS_DEVELOPER_TOKEN`
24
+ - `login-customer-id` — the manager (MCC) id under which calls are made; use the
25
+ target customer id, or `$GOOGLE_ADS_LOGIN_CUSTOMER_ID` if set (digits only, no
26
+ dashes).
27
+
28
+ > **API version:** the base is `https://googleads.googleapis.com/<vNN>`. Google
29
+ > ships a new `vNN` every ~4 months and retires old ones — set `VER` to the
30
+ > **current** supported version (check developers.google.com/google-ads/api
31
+ > release notes); the example uses `v18`.
32
+
33
+ If `$GOOGLE_ADS_DEVELOPER_TOKEN` is empty, the connector isn't fully provisioned —
34
+ say so rather than calling the API (it would 401/DEVELOPER_TOKEN_NOT_APPROVED).
35
+
36
+ ```bash
37
+ VER="v18"; BASE="https://googleads.googleapis.com/$VER"
38
+ AUTH=(-H "Authorization: Bearer $GOOGLE_ADS_TOKEN" -H "developer-token: $GOOGLE_ADS_DEVELOPER_TOKEN")
39
+ # Customers the OAuth user can access (ids are returned as customers/<id>)
40
+ curl -sS "${AUTH[@]}" "$BASE/customers:listAccessibleCustomers" | jq '.resourceNames'
41
+ ```
42
+
43
+ ## Report with GAQL (searchStream)
44
+
45
+ ```bash
46
+ CID="1234567890" # target customer id, digits only
47
+ curl -sS "${AUTH[@]}" -H "login-customer-id: ${GOOGLE_ADS_LOGIN_CUSTOMER_ID:-$CID}" \
48
+ -H "Content-Type: application/json" -d '{
49
+ "query":"SELECT campaign.name, metrics.cost_micros, metrics.clicks, metrics.conversions FROM campaign WHERE segments.date DURING LAST_30_DAYS ORDER BY metrics.cost_micros DESC"
50
+ }' "$BASE/customers/$CID/googleAds:searchStream" \
51
+ | jq '.[].results[]? | {campaign: .campaign.name, cost_usd: (.metrics.costMicros|tonumber/1e6), clicks: .metrics.clicks, conv: .metrics.conversions}'
52
+ ```
53
+
54
+ GAQL resources: `campaign`, `ad_group`, `ad_group_criterion` (keywords),
55
+ `customer`. Cost is `metrics.cost_micros` (÷ 1,000,000 = account currency).
56
+
57
+ ## Gotchas
58
+
59
+ - **Three headers, not one.** Missing `developer-token` or `login-customer-id`
60
+ is the #1 cause of 401/403 here.
61
+ - Customer ids are **digits only** in URLs/headers (strip the dashes from
62
+ `123-456-7890`).
63
+ - `searchStream` returns an array of chunks each with `.results[]` — flatten with
64
+ `.[].results[]?`.
65
+ - Cost is in **micros** of the account currency; divide by 1e6.
@@ -0,0 +1,68 @@
1
+ ---
2
+ name: google-analytics
3
+ description: Query Google Analytics 4 (GA4) reports via the Analytics Data API v1 and list properties via the Admin API. Use when the user mentions Google Analytics, GA4, website traffic / sessions / users, top pages or sources, conversions, or a realtime report.
4
+ when_to_use: |
5
+ Trigger when the user wants GA4 metrics — sessions, users, top
6
+ pages / channels / countries, conversions, or live realtime users —
7
+ for one of their GA4 properties. Read-only (`analytics.readonly`).
8
+ List properties first to get the numeric property id.
9
+ connections: [google/analytics]
10
+ allowed_tools: [Bash]
11
+ license: Apache-2.0
12
+ metadata:
13
+ author: acedatacloud
14
+ version: "1.0"
15
+ ---
16
+
17
+ Query **Google Analytics 4** via `curl + jq`. The user's OAuth bearer token is in
18
+ `$GOOGLE_ANALYTICS_TOKEN` (scope `analytics.readonly`); every call needs
19
+ `Authorization: Bearer $GOOGLE_ANALYTICS_TOKEN`. Two APIs: the **Admin API**
20
+ (`analyticsadmin.googleapis.com/v1beta`) to discover properties, and the **Data
21
+ API** (`analyticsdata.googleapis.com/v1beta`) to run reports.
22
+
23
+ Failures are `{"error":{"code","message","status"}}` — show verbatim. `401` =
24
+ re-install.
25
+
26
+ ```bash
27
+ AUTH=(-H "Authorization: Bearer $GOOGLE_ANALYTICS_TOKEN")
28
+ # List the GA4 properties the user can access (via their account summaries)
29
+ curl -sS "${AUTH[@]}" "https://analyticsadmin.googleapis.com/v1beta/accountSummaries" \
30
+ | jq '.accountSummaries[]?.propertySummaries[]? | {property, displayName}'
31
+ ```
32
+
33
+ `property` looks like `properties/123456789` — the number is the `PROPERTY_ID`.
34
+
35
+ ## Run a report
36
+
37
+ ```bash
38
+ PID="123456789"
39
+ curl -sS -X POST "${AUTH[@]}" -H "Content-Type: application/json" -d '{
40
+ "dateRanges":[{"startDate":"28daysAgo","endDate":"today"}],
41
+ "dimensions":[{"name":"pagePath"}],
42
+ "metrics":[{"name":"screenPageViews"},{"name":"activeUsers"}],
43
+ "orderBys":[{"metric":{"metricName":"screenPageViews"},"desc":true}],
44
+ "limit":10
45
+ }' "https://analyticsdata.googleapis.com/v1beta/properties/$PID:runReport" \
46
+ | jq '.rows[] | {page: .dimensionValues[0].value, views: .metricValues[0].value, users: .metricValues[1].value}'
47
+ ```
48
+
49
+ Swap dimensions/metrics for other reports: `sessionDefaultChannelGroup` +
50
+ `sessions` (acquisition), `country` + `activeUsers` (geo), `eventName` +
51
+ `eventCount` / `conversions` (events). Dates accept `NdaysAgo`, `today`,
52
+ `yesterday`, or `YYYY-MM-DD`.
53
+
54
+ ## Realtime
55
+
56
+ ```bash
57
+ curl -sS -X POST "${AUTH[@]}" -H "Content-Type: application/json" \
58
+ -d '{"dimensions":[{"name":"country"}],"metrics":[{"name":"activeUsers"}]}' \
59
+ "https://analyticsdata.googleapis.com/v1beta/properties/$PID:runRealtimeReport" | jq '.rows'
60
+ ```
61
+
62
+ ## Gotchas
63
+
64
+ - This is **GA4 only** (Data API v1). Old Universal Analytics (UA) is shut down —
65
+ don't use the legacy `analytics/v3` endpoints.
66
+ - Valid dimension/metric API names matter (`screenPageViews`, not "Pageviews").
67
+ If a name 400s, it's the wrong API id — check the GA4 dimensions & metrics list.
68
+ - Reports are capped by `limit` (default 10k rows); page with `offset`.
@@ -0,0 +1,61 @@
1
+ ---
2
+ name: google-docs
3
+ description: Read and edit Google Docs via the Docs v1 REST API. Use when the user mentions a Google Doc, reading a document's text, creating a doc, inserting or replacing text, or exporting a doc's content.
4
+ when_to_use: |
5
+ Trigger when the user wants to read a Google Doc's content, create a
6
+ new doc, or insert / replace text in one. The connector grants
7
+ `documents.readonly` by default; the user opts in to `documents`
8
+ (read + write) at install — confirm before edits. Find the doc id
9
+ from a Drive URL or a google-drive search.
10
+ connections: [google/docs]
11
+ allowed_tools: [Bash]
12
+ license: Apache-2.0
13
+ metadata:
14
+ author: acedatacloud
15
+ version: "1.0"
16
+ ---
17
+
18
+ Drive **Google Docs** via `curl + jq`. The user's OAuth bearer token is in
19
+ `$GOOGLE_DOCS_TOKEN`; every call needs `Authorization: Bearer
20
+ $GOOGLE_DOCS_TOKEN`. Base URL: `https://docs.googleapis.com/v1/documents`. The
21
+ token carries `documents.readonly` (+ identity); writes need `documents`.
22
+
23
+ Failures are `{"error":{"code","message","status"}}` — show verbatim. `401` =
24
+ re-install. `403 PERMISSION_DENIED` on a write = read-only scope.
25
+
26
+ The doc id is the `…/document/d/<ID>/edit` segment of the URL.
27
+
28
+ ```bash
29
+ D="https://docs.googleapis.com/v1/documents"; AUTH=(-H "Authorization: Bearer $GOOGLE_DOCS_TOKEN")
30
+ # Read the document. The body is a tree of structural elements; pull plain text:
31
+ curl -sS "${AUTH[@]}" "$D/DOC_ID" \
32
+ | jq -r '.title, ([.body.content[]?.paragraph?.elements[]?.textRun?.content] | join(""))'
33
+ ```
34
+
35
+ ## Create & edit
36
+
37
+ ```bash
38
+ # Create an empty doc
39
+ curl -sS -X POST "${AUTH[@]}" -H "Content-Type: application/json" \
40
+ -d '{"title":"Meeting notes 2026-06-21"}' "$D" | jq '{documentId, title}'
41
+
42
+ # Insert text at the start (index 1) via batchUpdate (confirm first)
43
+ curl -sS -X POST "${AUTH[@]}" -H "Content-Type: application/json" \
44
+ -d '{"requests":[{"insertText":{"location":{"index":1},"text":"Hello\n"}}]}' \
45
+ "$D/DOC_ID:batchUpdate" | jq '.documentId'
46
+
47
+ # Replace all occurrences of a placeholder
48
+ curl -sS -X POST "${AUTH[@]}" -H "Content-Type: application/json" \
49
+ -d '{"requests":[{"replaceAllText":{"containsText":{"text":"{{name}}","matchCase":true},"replaceText":"Alex"}}]}' \
50
+ "$D/DOC_ID:batchUpdate" | jq '.replies'
51
+ ```
52
+
53
+ ## Gotchas
54
+
55
+ - The document model is **index-based**: text edits target character indices, and
56
+ every insert shifts later indices — for multiple inserts, apply them
57
+ back-to-front or recompute indices between calls.
58
+ - To get clean Markdown/plain text, walk `body.content[].paragraph.elements[]
59
+ .textRun.content` (the jq above). Tables / lists need deeper traversal.
60
+ - Creating a doc puts it in the user's Drive root; use the `google-drive` skill to
61
+ move/share it.
@@ -0,0 +1,70 @@
1
+ ---
2
+ name: google-search-console
3
+ description: Query Google Search Console via the Search Console API v1 — search analytics (clicks / impressions / CTR / position), sites, sitemaps and URL inspection. Use when the user mentions Search Console, organic search performance, top queries / pages, indexing status, or sitemaps.
4
+ when_to_use: |
5
+ Trigger when the user wants Search Console data — top queries or
6
+ pages by clicks / impressions / CTR / position, performance by date
7
+ or country, the list of verified sites, sitemaps, or URL inspection.
8
+ Read-only (`webmasters.readonly`). List sites first to get the
9
+ property URL.
10
+ connections: [google/search-console]
11
+ allowed_tools: [Bash]
12
+ license: Apache-2.0
13
+ metadata:
14
+ author: acedatacloud
15
+ version: "1.0"
16
+ ---
17
+
18
+ Query **Google Search Console** via `curl + jq`. The user's OAuth bearer token is
19
+ in `$GOOGLE_SEARCH_CONSOLE_TOKEN` (scope `webmasters.readonly`); every call needs
20
+ `Authorization: Bearer $GOOGLE_SEARCH_CONSOLE_TOKEN`. Base:
21
+ `https://searchconsole.googleapis.com`.
22
+
23
+ Failures are `{"error":{"code","message","status"}}` — show verbatim. `401` =
24
+ re-install. `403` = the token's account doesn't own/verify that site.
25
+
26
+ ```bash
27
+ AUTH=(-H "Authorization: Bearer $GOOGLE_SEARCH_CONSOLE_TOKEN")
28
+ # Verified sites (siteUrl is the property — URL-encode it in later calls)
29
+ curl -sS "${AUTH[@]}" "https://searchconsole.googleapis.com/webmasters/v3/sites" \
30
+ | jq '.siteEntry[] | {siteUrl, permissionLevel}'
31
+ ```
32
+
33
+ ## Search analytics
34
+
35
+ ```bash
36
+ # Top queries by clicks for the last 28 days. siteUrl must be URL-encoded
37
+ # (https%3A%2F%2Fexample.com%2F or sc-domain%3Aexample.com).
38
+ SITE="https%3A%2F%2Fexample.com%2F"
39
+ curl -sS -X POST "${AUTH[@]}" -H "Content-Type: application/json" -d '{
40
+ "startDate":"2026-05-24","endDate":"2026-06-21",
41
+ "dimensions":["query"],"rowLimit":10
42
+ }' "https://searchconsole.googleapis.com/webmasters/v3/sites/$SITE/searchAnalytics/query" \
43
+ | jq '.rows[] | {query: .keys[0], clicks, impressions, ctr, position}'
44
+ ```
45
+
46
+ Swap `dimensions` for `["page"]`, `["country"]`, `["date"]`, or combine
47
+ `["query","page"]`. Add `"dimensionFilterGroups"` to filter by page/country.
48
+
49
+ ## Sitemaps & URL inspection
50
+
51
+ ```bash
52
+ # Submitted sitemaps
53
+ curl -sS "${AUTH[@]}" "https://searchconsole.googleapis.com/webmasters/v3/sites/$SITE/sitemaps" \
54
+ | jq '.sitemap[] | {path, lastDownloaded, errors, warnings}'
55
+
56
+ # Is a URL indexed?
57
+ curl -sS -X POST "${AUTH[@]}" -H "Content-Type: application/json" \
58
+ -d '{"inspectionUrl":"https://example.com/page","siteUrl":"https://example.com/"}' \
59
+ "https://searchconsole.googleapis.com/v1/urlInspection/index:inspect" \
60
+ | jq '.inspectionResult.indexStatusResult | {verdict, coverageState, lastCrawlTime}'
61
+ ```
62
+
63
+ ## Gotchas
64
+
65
+ - **Two property shapes:** URL-prefix (`https://example.com/`) vs Domain
66
+ (`sc-domain:example.com`). Use exactly the `siteUrl` from the sites list,
67
+ URL-encoded.
68
+ - Data lags ~2–3 days; the most recent dates may be partial/empty — set `endDate`
69
+ a couple days back for stable numbers.
70
+ - `ctr` is 0–1, `position` is average rank (lower = better).
@@ -0,0 +1,75 @@
1
+ ---
2
+ name: google-sheets
3
+ description: Read and edit Google Sheets via the Sheets v4 REST API. Use when the user mentions Google Sheets, a spreadsheet, cells / ranges / tabs, reading or appending rows, updating values, or creating a new spreadsheet.
4
+ when_to_use: |
5
+ Trigger when the user wants to read a range from a Sheet, append or
6
+ update rows, add a tab, or create a spreadsheet. The connector grants
7
+ `spreadsheets.readonly` by default; the user opts in to the broader
8
+ `spreadsheets` scope (read + write) at install — confirm before
9
+ writes. Find the spreadsheet id from a Drive URL or a google-drive
10
+ search.
11
+ connections: [google/sheets]
12
+ allowed_tools: [Bash]
13
+ license: Apache-2.0
14
+ metadata:
15
+ author: acedatacloud
16
+ version: "1.0"
17
+ ---
18
+
19
+ Drive **Google Sheets** via `curl + jq`. The user's OAuth bearer token is in
20
+ `$GOOGLE_SHEETS_TOKEN`; every call needs `Authorization: Bearer
21
+ $GOOGLE_SHEETS_TOKEN`. Base URL: `https://sheets.googleapis.com/v4/spreadsheets`.
22
+ The token carries `spreadsheets.readonly` (+ identity); writes need the broader
23
+ `spreadsheets` scope.
24
+
25
+ Failures are `{"error":{"code","message","status"}}` — show verbatim. `401` =
26
+ re-install. `403 PERMISSION_DENIED` on a write = read-only scope → re-connect
27
+ with read+write.
28
+
29
+ The spreadsheet id is the `…/spreadsheets/d/<ID>/edit` segment of the URL.
30
+
31
+ ```bash
32
+ S="https://sheets.googleapis.com/v4/spreadsheets"; AUTH=(-H "Authorization: Bearer $GOOGLE_SHEETS_TOKEN")
33
+ # Tabs + title
34
+ curl -sS "${AUTH[@]}" "$S/SPREADSHEET_ID?fields=properties.title,sheets.properties(title,sheetId)" \
35
+ | jq '{title: .properties.title, tabs: [.sheets[].properties.title]}'
36
+ ```
37
+
38
+ ## Read / append / update values
39
+
40
+ ```bash
41
+ ID="SPREADSHEET_ID"
42
+ # Read a range (A1 notation; values are rows of cells)
43
+ curl -sS "${AUTH[@]}" "$S/$ID/values/Sheet1!A1:D20" | jq '.values'
44
+
45
+ # Append rows (confirm first). valueInputOption=USER_ENTERED parses formulas/dates.
46
+ curl -sS -X POST "${AUTH[@]}" -H "Content-Type: application/json" \
47
+ -d '{"values":[["2026-06-21","Acme",4200]]}' \
48
+ "$S/$ID/values/Sheet1!A1:append?valueInputOption=USER_ENTERED" | jq '.updates'
49
+
50
+ # Update a fixed range: PUT /values/{range}?valueInputOption=USER_ENTERED
51
+ curl -sS -X PUT "${AUTH[@]}" -H "Content-Type: application/json" \
52
+ -d '{"values":[["done"]]}' \
53
+ "$S/$ID/values/Sheet1!E2?valueInputOption=USER_ENTERED" | jq '.updatedCells'
54
+ ```
55
+
56
+ ## Create a spreadsheet / add a tab
57
+
58
+ ```bash
59
+ # New spreadsheet
60
+ curl -sS -X POST "${AUTH[@]}" -H "Content-Type: application/json" \
61
+ -d '{"properties":{"title":"Report 2026"}}' "$S" | jq '{spreadsheetId, spreadsheetUrl}'
62
+
63
+ # Add a tab via batchUpdate (addSheet request)
64
+ curl -sS -X POST "${AUTH[@]}" -H "Content-Type: application/json" \
65
+ -d '{"requests":[{"addSheet":{"properties":{"title":"Q3"}}}]}' \
66
+ "$S/$ID:batchUpdate" | jq '.replies'
67
+ ```
68
+
69
+ ## Gotchas
70
+
71
+ - **A1 notation:** `Sheet1!A1:D20`; quote tab names with spaces as `'My Tab'!A1`.
72
+ - `valueInputOption=RAW` stores strings as-is; `USER_ENTERED` interprets them like
73
+ the UI (numbers, dates, `=FORMULA`). Pick deliberately.
74
+ - `values` is row-major (`[[row1...],[row2...]]`); a missing trailing cell just
75
+ comes back short — don't assume rectangular.
@@ -0,0 +1,71 @@
1
+ ---
2
+ name: microsoft-excel
3
+ description: Read and edit live Excel workbooks stored in OneDrive / SharePoint via the Microsoft Graph workbook API. Use when the user mentions an Excel file, a spreadsheet on OneDrive, reading a range or table, appending rows, updating cells, or listing worksheets in a cloud .xlsx.
4
+ when_to_use: |
5
+ Trigger when the user wants to read or write a cloud Excel workbook
6
+ (one that lives in their OneDrive / SharePoint) — read a used range,
7
+ read/update cells, add rows to a table, list worksheets. The
8
+ connector grants `Files.ReadWrite` (or `Files.Read` read-only). Find
9
+ the file id first via OneDrive search; confirm before writes.
10
+ connections: [microsoft/excel]
11
+ allowed_tools: [Bash]
12
+ license: Apache-2.0
13
+ metadata:
14
+ author: acedatacloud
15
+ version: "1.0"
16
+ ---
17
+
18
+ Drive **live Excel workbooks** via the Microsoft Graph workbook API with
19
+ `curl + jq`. The user's OAuth bearer token is in `$MICROSOFT_EXCEL_TOKEN`; every
20
+ call needs `Authorization: Bearer $MICROSOFT_EXCEL_TOKEN`. Base URL:
21
+ `https://graph.microsoft.com/v1.0`. The workbook API operates on a **drive item**
22
+ (an .xlsx in OneDrive/SharePoint), so you need its `ITEM_ID`.
23
+
24
+ Failures are `{"error":{"code","message"}}` — show `message` verbatim. `401` =
25
+ token expired (re-install). `403` on a write = the user granted read-only.
26
+
27
+ ```bash
28
+ G="https://graph.microsoft.com/v1.0"; AUTH=(-H "Authorization: Bearer $MICROSOFT_EXCEL_TOKEN")
29
+ # Find the workbook by name (then take .id as ITEM_ID)
30
+ curl -sS "${AUTH[@]}" "$G/me/drive/root/search(q='budget.xlsx')" \
31
+ | jq '.value[] | {id, name, webUrl}'
32
+ # Worksheets in the workbook
33
+ curl -sS "${AUTH[@]}" "$G/me/drive/items/ITEM_ID/workbook/worksheets" \
34
+ | jq '.value[] | {id, name, position}'
35
+ ```
36
+
37
+ ## Read & write ranges
38
+
39
+ ```bash
40
+ ITEM="ITEM_ID"; WS="Sheet1"
41
+ # Used range (values + formulas + formats)
42
+ curl -sS "${AUTH[@]}" \
43
+ "$G/me/drive/items/$ITEM/workbook/worksheets/$WS/usedRange" \
44
+ | jq '{address, values: .values}'
45
+
46
+ # Read a specific range
47
+ curl -sS "${AUTH[@]}" \
48
+ "$G/me/drive/items/$ITEM/workbook/worksheets/$WS/range(address='A1:C5')" | jq '.values'
49
+
50
+ # Update a range (confirm first). values is a 2-D array matching the address shape.
51
+ curl -sS -X PATCH "${AUTH[@]}" -H "Content-Type: application/json" \
52
+ -d '{"values":[["Q3",1200],["Q4",1580]]}' \
53
+ "$G/me/drive/items/$ITEM/workbook/worksheets/$WS/range(address='A2:B3')" | jq '.address'
54
+ ```
55
+
56
+ ## Append a row to a table
57
+
58
+ ```bash
59
+ curl -sS -X POST "${AUTH[@]}" -H "Content-Type: application/json" \
60
+ -d '{"values":[["2026-06-21","Acme",4200]]}' \
61
+ "$G/me/drive/items/$ITEM/workbook/tables/Table1/rows/add" | jq '.index'
62
+ ```
63
+
64
+ ## Gotchas
65
+
66
+ - This is for **cloud** workbooks (OneDrive/SharePoint). A local .xlsx the user
67
+ uploaded into chat is a different job (parse the file directly), not this skill.
68
+ - Range `address` is a string like `Sheet1!A1:C5` or just `A1:C5` when scoped to a
69
+ worksheet; `values` must be a 2-D array matching its dimensions.
70
+ - For long-running edits Graph supports a **workbook session** header
71
+ (`workbook-session-id`); for a few cells the default sessionless mode is fine.
@@ -0,0 +1,56 @@
1
+ ---
2
+ name: microsoft-teams
3
+ description: Read and send Microsoft Teams chat messages via Microsoft Graph v1.0. Use when the user mentions Microsoft Teams chats, a 1:1 or group chat, reading recent Teams messages, or sending a Teams chat message. (Channel messages are not covered — they need admin-consented scopes.)
4
+ when_to_use: |
5
+ Trigger when the user wants to list their Teams chats, read recent
6
+ messages in a chat, or send a chat message. Scoped to **chats**
7
+ (1:1 / group) which use delegated `Chat.Read` / `Chat.ReadWrite` /
8
+ `ChatMessage.Send` — no admin consent. Confirm before sending; do
9
+ not attempt channel-message reads (different, admin-gated scopes).
10
+ connections: [microsoft/teams]
11
+ allowed_tools: [Bash]
12
+ license: Apache-2.0
13
+ metadata:
14
+ author: acedatacloud
15
+ version: "1.0"
16
+ ---
17
+
18
+ Drive **Microsoft Teams chats** via Microsoft Graph with `curl + jq`. The user's
19
+ OAuth bearer token is in `$MICROSOFT_TEAMS_TOKEN`; every call needs
20
+ `Authorization: Bearer $MICROSOFT_TEAMS_TOKEN`. Base URL:
21
+ `https://graph.microsoft.com/v1.0`.
22
+
23
+ Failures are `{"error":{"code","message"}}` — show `message` verbatim. `401` =
24
+ re-install. `403`/`Forbidden` on send = the user granted read-only
25
+ (`Chat.Read`) → re-connect with `Chat.ReadWrite` + `ChatMessage.Send`.
26
+
27
+ ```bash
28
+ G="https://graph.microsoft.com/v1.0"; AUTH=(-H "Authorization: Bearer $MICROSOFT_TEAMS_TOKEN")
29
+ # My chats (1:1 + group), most recent first; expand members for names
30
+ curl -sS "${AUTH[@]}" "$G/me/chats?\$top=20&\$expand=members" \
31
+ | jq '.value[] | {id, chatType, topic, members: [.members[].displayName]}'
32
+ ```
33
+
34
+ ## Read & send chat messages
35
+
36
+ ```bash
37
+ CHAT="CHAT_ID"
38
+ # Recent messages
39
+ curl -sS "${AUTH[@]}" "$G/chats/$CHAT/messages?\$top=20" \
40
+ | jq '.value[] | {from: .from.user.displayName, created: .createdDateTime, text: .body.content}'
41
+
42
+ # Send a message (confirm content with the user first). contentType html|text.
43
+ curl -sS -X POST "${AUTH[@]}" -H "Content-Type: application/json" \
44
+ -d '{"body":{"contentType":"html","content":"Hi from the assistant 👋"}}' \
45
+ "$G/chats/$CHAT/messages" | jq '{id, created: .createdDateTime}'
46
+ ```
47
+
48
+ ## Gotchas
49
+
50
+ - **Chats only.** Reading Teams **channel** messages needs
51
+ `ChannelMessage.Read.All` — a Microsoft "protected API" requiring tenant-admin
52
+ consent — which this connector deliberately does not request. Don't try
53
+ `/teams/{id}/channels/.../messages`; it will 403.
54
+ - `body.content` for HTML messages contains markup — strip tags when
55
+ summarizing.
56
+ - OData `$top`/`$expand` need the `$` escaped in the shell; quote the URL.
@@ -0,0 +1,58 @@
1
+ ---
2
+ name: microsoft-todo
3
+ description: Read and manage Microsoft To Do task lists and tasks via Microsoft Graph v1.0. Use when the user mentions Microsoft To Do, their Outlook tasks, todo / pending items, due dates or reminders, adding or completing a task, or organising task lists.
4
+ when_to_use: |
5
+ Trigger when the user wants to inspect or manage Microsoft To Do —
6
+ list task lists, surface pending / overdue items, add todos, mark
7
+ complete, or update / delete tasks. The connector grants
8
+ `Tasks.ReadWrite` (or `Tasks.Read` if the user chose read-only) —
9
+ confirm before destructive writes.
10
+ connections: [microsoft/todo]
11
+ allowed_tools: [Bash]
12
+ license: Apache-2.0
13
+ metadata:
14
+ author: acedatacloud
15
+ version: "1.0"
16
+ ---
17
+
18
+ Drive **Microsoft To Do** via Microsoft Graph with `curl + jq`. The user's OAuth
19
+ bearer token is in `$MICROSOFT_TODO_TOKEN`; every call needs
20
+ `Authorization: Bearer $MICROSOFT_TODO_TOKEN`. Base URL:
21
+ `https://graph.microsoft.com/v1.0`.
22
+
23
+ Failures are `{"error":{"code","message"}}` — show `message` verbatim. `401`
24
+ means the token expired (re-install). `403`/`ErrorAccessDenied` on a write means
25
+ the user only granted `Tasks.Read` → ask them to re-connect with read+write.
26
+
27
+ ```bash
28
+ G="https://graph.microsoft.com/v1.0"; AUTH=(-H "Authorization: Bearer $MICROSOFT_TODO_TOKEN")
29
+ # Task lists
30
+ curl -sS "${AUTH[@]}" "$G/me/todo/lists" | jq '.value[] | {id, displayName, wellknownListName}'
31
+ ```
32
+
33
+ ## Tasks
34
+
35
+ ```bash
36
+ LIST="LIST_ID"
37
+ # Open tasks in a list (filter notStarted/inProgress; $top caps page size)
38
+ curl -sS "${AUTH[@]}" \
39
+ "$G/me/todo/lists/$LIST/tasks?\$filter=status ne 'completed'&\$top=50" \
40
+ | jq '.value[] | {id, title, status, due: .dueDateTime.dateTime}'
41
+
42
+ # Create (confirm first). dueDateTime/reminderDateTime are optional.
43
+ curl -sS -X POST "${AUTH[@]}" -H "Content-Type: application/json" \
44
+ -d '{"title":"Follow up with Alex","dueDateTime":{"dateTime":"2026-06-30T17:00:00","timeZone":"UTC"}}' \
45
+ "$G/me/todo/lists/$LIST/tasks" | jq '{id, title, status}'
46
+
47
+ # Complete: PATCH the task with {"status":"completed"}
48
+ curl -sS -X PATCH "${AUTH[@]}" -H "Content-Type: application/json" \
49
+ -d '{"status":"completed"}' "$G/me/todo/lists/$LIST/tasks/TASK_ID" | jq '{title, status}'
50
+ ```
51
+
52
+ ## Gotchas
53
+
54
+ - OData params (`$filter`, `$top`, `$select`) need the `$` escaped in the shell
55
+ (`\$filter`) and URL-encoded spaces — quote the whole URL.
56
+ - `wellknownListName: "defaultList"` is the user's default "Tasks" list — a good
57
+ fallback when they don't name a list.
58
+ - Pagination via `@odata.nextLink`; follow it for "all tasks".
@@ -0,0 +1,80 @@
1
+ ---
2
+ name: sentry
3
+ description: Monitor errors, issues, releases and projects in Sentry via the REST API v0. Use when the user mentions Sentry, error tracking, an unresolved issue, a stack trace / crash, error rate, a release, or wants to triage / resolve / assign issues across their Sentry projects.
4
+ when_to_use: |
5
+ Trigger when the user wants to list or search Sentry issues, read a
6
+ crash's latest event + stack trace, list projects or releases, or
7
+ resolve / assign an issue. The connector stores a Sentry auth token
8
+ with the granted org scope — confirm before any write (resolve /
9
+ assign / delete), and prefer read-only triage first.
10
+ connections: [sentry]
11
+ allowed_tools: [Bash]
12
+ license: Apache-2.0
13
+ metadata:
14
+ author: acedatacloud
15
+ version: "1.0"
16
+ ---
17
+
18
+ Call the **Sentry REST API v0** with `curl + jq`. The user's auth token is in
19
+ `$SENTRY_AUTH_TOKEN` and their org slug in `$SENTRY_ORG_SLUG`; every call needs
20
+ `Authorization: Bearer $SENTRY_AUTH_TOKEN`. Base URL: `https://sentry.io/api/0`
21
+ (SaaS). For a self-hosted Sentry the user supplies `$SENTRY_BASE_URL` — use it
22
+ instead when set.
23
+
24
+ Errors come back as JSON with a `detail` field — show it verbatim. `401` means
25
+ the token is invalid → the user must re-connect Sentry. `403` means the token
26
+ lacks the scope for that action (e.g. write) → ask them to re-connect with a
27
+ broader token.
28
+
29
+ Always confirm the org + list projects first:
30
+
31
+ ```bash
32
+ BASE="${SENTRY_BASE_URL:-https://sentry.io/api/0}"
33
+ curl -sS -H "Authorization: Bearer $SENTRY_AUTH_TOKEN" \
34
+ "$BASE/organizations/$SENTRY_ORG_SLUG/projects/" \
35
+ | jq '.[] | {slug, name, platform}'
36
+ ```
37
+
38
+ ## Search & read issues
39
+
40
+ ```bash
41
+ # Unresolved issues for a project, most frequent first.
42
+ curl -sS -H "Authorization: Bearer $SENTRY_AUTH_TOKEN" \
43
+ "$BASE/projects/$SENTRY_ORG_SLUG/PROJECT_SLUG/issues/?query=is:unresolved&sort=freq" \
44
+ | jq '.[] | {id, shortId, title, count, userCount, lastSeen}'
45
+
46
+ # A single issue + its latest event (stack trace, tags, breadcrumbs).
47
+ curl -sS -H "Authorization: Bearer $SENTRY_AUTH_TOKEN" \
48
+ "$BASE/issues/ISSUE_ID/" | jq '{shortId, title, status, count, firstSeen, lastSeen}'
49
+ curl -sS -H "Authorization: Bearer $SENTRY_AUTH_TOKEN" \
50
+ "$BASE/issues/ISSUE_ID/events/latest/" \
51
+ | jq '{message, culprit, entries: [.entries[] | .type]}'
52
+ ```
53
+
54
+ `query=` uses Sentry's search syntax: `is:unresolved`, `is:assigned`,
55
+ `environment:production`, `release:1.2.3`, free-text, etc.
56
+
57
+ ## Releases
58
+
59
+ ```bash
60
+ curl -sS -H "Authorization: Bearer $SENTRY_AUTH_TOKEN" \
61
+ "$BASE/organizations/$SENTRY_ORG_SLUG/releases/?per_page=20" \
62
+ | jq '.[] | {version, dateCreated, newGroups, projects: [.projects[].slug]}'
63
+ ```
64
+
65
+ ## Triage (writes — confirm first)
66
+
67
+ ```bash
68
+ # Resolve (or set status: unresolved | ignored). Assign with assignedTo (username/email).
69
+ curl -sS -X PUT -H "Authorization: Bearer $SENTRY_AUTH_TOKEN" \
70
+ -H "Content-Type: application/json" -d '{"status":"resolved"}' \
71
+ "$BASE/issues/ISSUE_ID/" | jq '{shortId, status}'
72
+ ```
73
+
74
+ ## Gotchas
75
+
76
+ - **Pagination** is `Link` header cursor-based (`per_page` max 100). For "all
77
+ issues" follow the `rel="next"; results="true"` cursor.
78
+ - **Org/project slugs**, not numeric ids, in the issue-search URL; the issue
79
+ detail/triage routes take the numeric/short issue id.
80
+ - Don't dump tokens or PII from event payloads back to the user unprompted.
@@ -0,0 +1,63 @@
1
+ ---
2
+ name: tiktok
3
+ description: Upload videos to the user's TikTok inbox/drafts and read their TikTok profile via the Content Posting API. Use when the user wants to publish or post a generated video to TikTok, send a video to their TikTok drafts, or check their TikTok account.
4
+ when_to_use: |
5
+ Trigger when the user wants to push a video (e.g. one generated by
6
+ the video skills) to TikTok. v1 uploads to the user's **inbox /
7
+ drafts** (they finish posting in the TikTok app) — the safe,
8
+ pre-audit path. Confirm before uploading; the video must be a public
9
+ URL on a verified domain (or a file you upload in chunks).
10
+ connections: [tiktok]
11
+ allowed_tools: [Bash]
12
+ license: Apache-2.0
13
+ metadata:
14
+ author: acedatacloud
15
+ version: "1.0"
16
+ ---
17
+
18
+ Call the **TikTok Content Posting API** with `curl + jq`. The user's OAuth bearer
19
+ token is in `$TIKTOK_TOKEN`; every call needs `Authorization: Bearer
20
+ $TIKTOK_TOKEN`. Base URL: `https://open.tiktokapis.com/v2`.
21
+
22
+ Responses wrap everything in `{"data":...,"error":{"code","message","log_id"}}` —
23
+ `error.code == "ok"` means success; otherwise show `error.message` verbatim.
24
+ `401`/`access_token_invalid` = re-connect TikTok.
25
+
26
+ ```bash
27
+ T="https://open.tiktokapis.com/v2"; AUTH=(-H "Authorization: Bearer $TIKTOK_TOKEN")
28
+ # Profile (account card)
29
+ curl -sS "${AUTH[@]}" "$T/user/info/?fields=open_id,display_name,avatar_url" \
30
+ | jq '.data.user'
31
+ ```
32
+
33
+ ## Upload a video to the user's inbox / drafts (v1)
34
+
35
+ The simplest path is `PULL_FROM_URL`: TikTok fetches the video from a public URL
36
+ on a **verified domain** (AceData's `cdn.acedata.cloud` is verified for this app).
37
+
38
+ ```bash
39
+ VIDEO_URL="https://cdn.acedata.cloud/...mp4" # must be on a verified domain
40
+ curl -sS -X POST "${AUTH[@]}" -H "Content-Type: application/json" -d "$(jq -n --arg u "$VIDEO_URL" \
41
+ '{source_info:{source:"PULL_FROM_URL", video_url:$u}}')" \
42
+ "$T/post/publish/inbox/video/init/" | jq '{publish_id: .data.publish_id, error}'
43
+ ```
44
+
45
+ This drops the video into the user's TikTok **inbox** — they open the TikTok app
46
+ to add caption / sound / privacy and post. Poll status:
47
+
48
+ ```bash
49
+ curl -sS -X POST "${AUTH[@]}" -H "Content-Type: application/json" \
50
+ -d '{"publish_id":"PUBLISH_ID"}' "$T/post/publish/status/fetch/" \
51
+ | jq '.data | {status, fail_reason}'
52
+ ```
53
+
54
+ ## Gotchas
55
+
56
+ - **v1 = inbox upload only.** True "Direct Post" (set caption + privacy via API,
57
+ posts immediately) needs the `video.publish` scope and a passed Content Posting
58
+ audit — not available until then. Don't call `creator_info` /
59
+ `/post/publish/video/init/` (Direct Post) here; they require that scope.
60
+ - `PULL_FROM_URL` only works from a **domain verified** in the TikTok developer
61
+ app. If the video isn't on a verified domain, use the chunked `FILE_UPLOAD`
62
+ flow instead (init → PUT byte ranges → status).
63
+ - Unaudited apps are rate-limited and may restrict visibility to `SELF_ONLY`.
@@ -0,0 +1,77 @@
1
+ ---
2
+ name: vercel
3
+ description: Inspect Vercel projects, deployments, build logs and domains via the Vercel REST API. Use when the user mentions Vercel, a deployment that failed / is building, build or runtime logs, a preview URL, project domains, or wants to check / redeploy a Vercel project.
4
+ when_to_use: |
5
+ Trigger when the user wants to list Vercel projects, list recent
6
+ deployments, read a deployment's build logs to diagnose a failure,
7
+ check domains, or trigger a redeploy. The connector stores a Vercel
8
+ access token with the granted scope; treat env-var values as secret
9
+ (never echo them) and confirm before any redeploy / mutation.
10
+ connections: [vercel]
11
+ allowed_tools: [Bash]
12
+ license: Apache-2.0
13
+ metadata:
14
+ author: acedatacloud
15
+ version: "1.0"
16
+ ---
17
+
18
+ Call the **Vercel REST API** with `curl + jq`. The user's token is in
19
+ `$VERCEL_ACCESS_TOKEN`; every call needs `Authorization: Bearer
20
+ $VERCEL_ACCESS_TOKEN`. Base URL: `https://api.vercel.com`. If the resource is
21
+ team-scoped, append `?teamId=$VERCEL_TEAM_ID` (set only when the user connected a
22
+ team token).
23
+
24
+ Errors come back as `{"error":{"code","message"}}` — show `message` verbatim.
25
+ `403 forbidden` usually means the token can't see that team/project →
26
+ re-connect with the right scope.
27
+
28
+ Helper for the optional team param:
29
+
30
+ ```bash
31
+ TEAM=""; [ -n "$VERCEL_TEAM_ID" ] && TEAM="?teamId=$VERCEL_TEAM_ID"
32
+ AUTH=(-H "Authorization: Bearer $VERCEL_ACCESS_TOKEN")
33
+ ```
34
+
35
+ ## Projects & deployments
36
+
37
+ ```bash
38
+ # Projects
39
+ curl -sS "${AUTH[@]}" "https://api.vercel.com/v9/projects$TEAM" \
40
+ | jq '.projects[] | {name, framework, latestProduction: .latestDeployments[0].url}'
41
+
42
+ # Recent deployments (optionally filter by ?projectId=… or &state=ERROR)
43
+ curl -sS "${AUTH[@]}" "https://api.vercel.com/v6/deployments${TEAM:-?}&limit=20" \
44
+ | jq '.deployments[] | {uid, name, url, state, readyState, created}'
45
+ ```
46
+
47
+ ## Diagnose a failed build
48
+
49
+ ```bash
50
+ # Deployment detail
51
+ curl -sS "${AUTH[@]}" "https://api.vercel.com/v13/deployments/DEPLOYMENT_ID${TEAM:+&teamId=$VERCEL_TEAM_ID}" \
52
+ | jq '{name, url, state: .readyState, error: .errorMessage}'
53
+
54
+ # Build / runtime events (the actual logs)
55
+ curl -sS "${AUTH[@]}" "https://api.vercel.com/v3/deployments/DEPLOYMENT_ID/events${TEAM:+?teamId=$VERCEL_TEAM_ID}" \
56
+ | jq -r '.[] | select(.type=="stdout" or .type=="stderr") | .payload.text'
57
+ ```
58
+
59
+ ## Domains & redeploy
60
+
61
+ ```bash
62
+ # Project domains
63
+ curl -sS "${AUTH[@]}" "https://api.vercel.com/v9/projects/PROJECT_ID/domains${TEAM:+?teamId=$VERCEL_TEAM_ID}" \
64
+ | jq '.domains[] | {name, verified}'
65
+ ```
66
+
67
+ To **redeploy**, POST to `https://api.vercel.com/v13/deployments` with a
68
+ `deploymentId` (or git source) body — **confirm with the user first**, it ships
69
+ to production.
70
+
71
+ ## Gotchas
72
+
73
+ - **Never print env-var values.** Listing project env vars returns metadata;
74
+ the `/v1/.../env/{id}` decrypt route returns secrets — don't call it unless the
75
+ user explicitly asks, and even then summarize, don't echo.
76
+ - Deployment `state`/`readyState`: `QUEUED → BUILDING → READY | ERROR | CANCELED`.
77
+ - `created`/`ready` are epoch ms — divide by 1000 for human time.