support-skill

star 0

Triage and resolve your team's support tickets on the ACP Support Portal. Use this skill when the user asks to handle tickets, respond to a ticket, resolve or refund a user, reassign a ticket to another team, check pending tickets, or mentions support.virtuals.io, acp_live_ tokens, or the ACP agent support API. Covers the full ticket lifecycle: list, inspect, comment (optionally via email), change status (pending → in-progress → resolved/refunded/rejected), update internal notes, and reassign. All via the `support` CLI.

Virtual-Protocol By Virtual-Protocol schedule Updated 4/21/2026

name: support-skill description: Triage and resolve your team's support tickets on the ACP Support Portal. Use this skill when the user asks to handle tickets, respond to a ticket, resolve or refund a user, reassign a ticket to another team, check pending tickets, or mentions support.virtuals.io, acp_live_ tokens, or the ACP agent support API. Covers the full ticket lifecycle: list, inspect, comment (optionally via email), change status (pending → in-progress → resolved/refunded/rejected), update internal notes, and reassign. All via the support CLI. license: MIT metadata: version: "1.0"

ACP Support Skill

This skill lets an ACP agent manage its own team's support tickets on the ACP Support Portal (support.virtuals.io) through the support CLI. The CLI wraps the portal's Agent API — every command supports --json for machine-readable output.

Default behavior: when a user reports that a ticket needs action ("resolve FB123", "refund this user", "is the Axelrod queue backed up"), use support to do it end-to-end rather than hand-writing curl.

Setup

One-time install:

git clone https://github.com/Virtual-Protocol/support-skill
cd support-skill && npm install

Per-session auth — export the team-scoped bearer token:

export ACP_AGENT_TOKEN=acp_live_...
# Optional override (default: https://support.virtuals.io)
export ACP_BASE=https://support.virtuals.io

Tokens are team-scoped. Super-admin tokens are rejected by the API. If support me returns UNAUTHORIZED or FORBIDDEN, rotate the token.

How to Run

Run from the repo root. Always append --json for machine-readable output; errors are emitted as {"error":"...","code":"...","recovery":"..."} to stdout in --json mode and exit 1.

support <command> [subcommand] [args] --json

Commands

Command Purpose
support me Verify the token and learn which team / admin it is bound to
support ticket list [--status] [--job-id] Filter the team's tickets
support ticket get <id> Full ticket record (includes version)
support ticket comments <id> Read the comment thread
support ticket audit <id> Read the audit log
support ticket status <id> <status> [--message] [--tx-hash] Change status; emails the user
support ticket comment <id> <body> [--email] Add a comment; optionally email
support ticket notes <id> <text> Replace internal notes (never emailed)
support ticket assign <id> --reason <r> [--team <t>] [--admin <u>] Reassign; reason required

Statuses: pending, in-progress, resolved, refunded, rejected. The last three are terminal — the API will not transition out of them, only a super-admin reopens via the web UI.

Picking a terminal status

User intent Status Required fields
Issue fixed, nothing on-chain moved resolved --message (what was done)
Funds returned to the user on-chain refunded --message, --tx-hash
Not actionable (duplicate, out of scope, user error) rejected --message (explain why)

Always pass --message on terminal transitions — the email body is the user's only visibility into why the ticket was closed.

Same-status no-op

Calling status with the value the ticket is already in returns {"success": true, "ticket": {...}, "noChange": true}no audit entry, no email. Check noChange in the response before assuming a transition happened.

Email-sent flag

ticket comment --email only emails when the ticket's contactMethod === 'email' AND username is populated. If either is missing the comment persists but the response returns {"success": true, "emailSent": false}. Check emailSent — don't tell the user "emailed" based on the flag you passed.

Workflows

Triage — pick the next ticket

support me --json                              # confirm token / team
support ticket list --status in-progress --json  # work already owned first
support ticket list --status pending --json      # then new work
support ticket get FB123 --json

Acknowledge and start work

support ticket status FB123 in-progress \
  --message "Thanks — we see your report and are investigating." --json

This writes an audit entry AND emails the user (when contactMethod=email). version is read automatically from the current ticket.

Resolve without moving money

support ticket comment FB123 "The swap completed on retry — no further action needed." --email --json
support ticket status FB123 resolved --message "Closed — swap completed on retry." --json

Refund with on-chain proof

Execute the refund transaction out-of-band first, then:

support ticket status FB999 refunded \
  --message "Refund sent on-chain. Allow a few minutes for confirmation." \
  --tx-hash 0xabc123... --json

The tx-hash renders in the user's refund email. Do NOT attempt ticket comment --email after a refund — terminal tickets reject outbound email.

Reject (duplicate, out of scope, user error)

support ticket status FB500 rejected \
  --message "Duplicate of FB111. Please reply there if the issue recurs." --json

Always include --message — it's the user's only visibility into why.

Reassign to another team

support ticket assign FB123 \
  --team "Butler Agent" \
  --reason "Issue is with the Butler trading loop, not Axelrod swaps." --json

Rate-limited to 10/hour per token. After reassignment the ticket is no longer visible to this token.

Internal-only note (never emailed)

support ticket notes FB123 "User confirmed on-chain receipt; closing after final check." --json

notes REPLACES the field — read first if intent is to append.

Error Handling

All non-success exits include a machine-readable code in --json mode:

Code Meaning What to do
MISSING_TOKEN ACP_AGENT_TOKEN not set Export the token and retry
UNAUTHORIZED Token invalid / revoked Rotate the token; do not retry
FORBIDDEN Super-admin token or no team Use a team-scoped token
NOT_FOUND Ticket missing or not on your team Skip the ticket
CONFLICT Terminal status OR version mismatch Re-fetch + retry for versions; escalate for terminal
KEY_REUSED Idempotency-Key used on different endpoint Fresh key, retry
RATE_LIMITED 30/min general, 5/min email, 10/hr reassign Back off, retry after window
SERVER_ERROR 5xx Exponential backoff
NETWORK_ERROR Connect timeout / DNS / TLS (no HTTP response) Transient — retry with the same --idempotency-key (server may have committed)
BAD_STATUS / BAD_INPUT Local validation failed Fix input, retry

The CLI auto-reads version before every mutation, so version mismatches only happen when two writers race. On CONFLICT with a currentVersion recovery hint, retry once.

Retrying safely

By default, every mutation auto-generates a fresh Idempotency-Key per invocation. That means a retry after a NETWORK_ERROR (connect timeout, transport error — when the server may or may not have committed) will re-execute and can double-send emails or double-apply notes.

For retry safety, pass --idempotency-key <key> with a caller-stable value and reuse the same key across retries. The server caches the original response for 24h per (token, key) pair and replays it instead of re-executing.

KEY="resolve-FB123-$(date +%Y-%m-%d)"
support ticket status FB123 resolved --message "Fixed" --idempotency-key "$KEY" --json
# on network error, retry with the same key — server replays, does not re-execute:
support ticket status FB123 resolved --message "Fixed" --idempotency-key "$KEY" --json

Keys are scoped to (token, METHOD path). Reusing a key across different endpoints returns KEY_REUSED (422) — pick a fresh key per operation.

Guardrails

  • Terminal is terminal. Once a ticket is resolved, refunded, or rejected, the API will not transition it back. Do not loop retrying — surface to a human.
  • Plain text only. Comments and messages render as escaped HTML in the email template. Never send markup.
  • Emails are irreversible. ticket status on an email-contact ticket and ticket comment --email both send real user mail. Confirm the body before calling.
  • Do not reassign to clear a queue. Reassignment is for genuine misrouting; the rate limit is there because reassign-churn is a known anti-pattern.

Reference

  • references/api.md — full endpoint / request / response reference for the underlying API.
Install via CLI
npx skills add https://github.com/Virtual-Protocol/virtuals-support-skill --skill support-skill
Repository Details
star Stars 0
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator
Virtual-Protocol
Virtual-Protocol Explore all skills →