diff --git a/skills/happyfox/README.md b/skills/happyfox/README.md new file mode 100644 index 00000000..27593720 --- /dev/null +++ b/skills/happyfox/README.md @@ -0,0 +1,72 @@ +# HappyFox + +Interact with HappyFox help desk - create tickets, add updates, manage ticket status, and query tickets using the HappyFox REST API. Use when working with HappyFox support tickets or help desk operations. + +## Triggers + +This skill is activated by the following keywords: + +- `happyfox` +- `help desk` +- `support ticket` + +## Details + +The skill supports two environment variable prefixes. Use whichever matches your configuration: + +### Option 1: HFOX Prefix (supports custom domains) +- `HFOX_API_KEY`: Your HappyFox API key +- `HFOX_AUTH_CODE`: Your HappyFox auth code +- `HFOX_BASE_URL`: Full base URL (e.g., `https://support.example.com` or `https://acme.happyfox.com`) + +### Option 2: HAPPYFOX Prefix (standard HappyFox domains) +- `HAPPYFOX_API_KEY`: Your HappyFox API key +- `HAPPYFOX_AUTH_CODE`: Your HappyFox auth code +- `HAPPYFOX_SUBDOMAIN`: Your HappyFox subdomain (e.g., `acme` for `acme.happyfox.com`) + + +You can use `curl` with basic authentication to interact with HappyFox's REST API. +ALWAYS use the HappyFox API for operations instead of a web browser. +Before performing any HappyFox operations, run the credential detection script to set up the unified `$HF_*` variables. + + +## Features + +- **Create Tickets**: Create new support tickets with contacts, categories, and custom fields +- **Update Tickets**: Add staff replies, change status, priority, assignee, and tags +- **Private Notes**: Add internal notes visible only to staff +- **Query Tickets**: List, filter, and search tickets by various criteria +- **Manage Categories**: Move tickets between categories +- **Custom Fields**: Support for ticket and contact custom fields + +## Important Concepts + +### Authentication + +HappyFox uses HTTP Basic Authentication where: +- Username = API Key +- Password = Auth Code + +### Ticket Identifiers + +- **Ticket Number**: The numeric ID used in API endpoints (e.g., `3`) +- **Display ID**: Human-readable ID shown in the UI (e.g., `#DC00000003`) + +### Status Behaviors + +| Behavior | Description | +|----------|-------------| +| `pending` | Ticket is open and active | +| `completed` | Ticket is closed/resolved | + +### Custom Field Format + +Custom fields use a specific format in API payloads: +- Ticket custom fields: `t-cf-` +- Contact custom fields: `c-cf-` + +## Documentation + +- [HappyFox API Overview](https://support.happyfox.com/kb/article/360-api-for-happyfox/) +- [Tickets API Endpoints](https://support.happyfox.com/kb/article/1039-tickets-endpoint/) +- [Creating API Credentials](https://support.happyfox.com/kb/article/476-create-api-key-auth-code-happyfox/) diff --git a/skills/happyfox/SKILL.md b/skills/happyfox/SKILL.md new file mode 100644 index 00000000..3c74c792 --- /dev/null +++ b/skills/happyfox/SKILL.md @@ -0,0 +1,648 @@ +--- +name: happyfox +description: Interact with HappyFox help desk - create tickets, add updates, manage ticket status, and query tickets using the HappyFox REST API. Use when working with HappyFox support tickets or help desk operations. +triggers: +- happyfox +- help desk +- support ticket +--- + +# HappyFox + + +Before performing any HappyFox operations, detect which environment variable prefix is configured and set up the credentials. The skill supports two prefixes: `HFOX_*` and `HAPPYFOX_*`. + +Run this detection script first: + +```bash +# Detect and export HappyFox credentials +if [ -n "$HFOX_API_KEY" ] && [ -n "$HFOX_AUTH_CODE" ]; then + export HF_API_KEY="$HFOX_API_KEY" + export HF_AUTH_CODE="$HFOX_AUTH_CODE" + export HF_BASE_URL="${HFOX_BASE_URL:-}" + echo "Using HFOX_* credentials" +elif [ -n "$HAPPYFOX_API_KEY" ] && [ -n "$HAPPYFOX_AUTH_CODE" ]; then + export HF_API_KEY="$HAPPYFOX_API_KEY" + export HF_AUTH_CODE="$HAPPYFOX_AUTH_CODE" + export HF_BASE_URL="${HAPPYFOX_SUBDOMAIN:+$HF_BASE_URL}" + echo "Using HAPPYFOX_* credentials" +else + echo "ERROR: No HappyFox credentials found!" + echo "Please set either:" + echo " - HFOX_API_KEY, HFOX_AUTH_CODE, and HFOX_BASE_URL" + echo " - HAPPYFOX_API_KEY, HAPPYFOX_AUTH_CODE, and HAPPYFOX_SUBDOMAIN" +fi + +# Verify credentials are set +[ -n "$HF_API_KEY" ] && echo "HF_API_KEY: configured" || echo "HF_API_KEY: NOT SET" +[ -n "$HF_AUTH_CODE" ] && echo "HF_AUTH_CODE: configured" || echo "HF_AUTH_CODE: NOT SET" +[ -n "$HF_BASE_URL" ] && echo "HF_BASE_URL: $HF_BASE_URL" || echo "HF_BASE_URL: NOT SET (will need to specify manually)" +``` + +After running the detection, use `$HF_API_KEY`, `$HF_AUTH_CODE`, and `$HF_BASE_URL` in all API calls. + +If credentials are missing, ask the user to provide them before proceeding. + + +## Environment Variables + +The skill supports two naming conventions for environment variables: + +| Variable | HFOX Prefix | HAPPYFOX Prefix | +|----------|-------------|-----------------| +| API Key | `HFOX_API_KEY` | `HAPPYFOX_API_KEY` | +| Auth Code | `HFOX_AUTH_CODE` | `HAPPYFOX_AUTH_CODE` | +| Base URL / Subdomain | `HFOX_BASE_URL` (full URL) | `HAPPYFOX_SUBDOMAIN` (just subdomain) | + +**HFOX_BASE_URL** should be the full base URL (e.g., `https://support.example.com`) +**HAPPYFOX_SUBDOMAIN** should be just the subdomain (e.g., `acme` for `acme.happyfox.com`) + +## Authentication + +HappyFox uses HTTP Basic Authentication with the API key and auth code. All requests require: +- API Key: `$HF_API_KEY` +- Auth Code: `$HF_AUTH_CODE` +- Base URL: `$HF_BASE_URL` (e.g., `https://acme.happyfox.com` or custom domain like `https://support.example.com`) + +> **Note**: If your HappyFox account uses a custom domain (e.g., `support.example.com`), use `HFOX_BASE_URL` with the full URL. If using the standard HappyFox domain and hosted in EU, use `.happyfox.net` instead of `.happyfox.com`. + +## API Base URL + +``` +$HF_BASE_URL/api/1.1/json/ +``` + +## Common Operations + +### List All Tickets + +```bash +curl -s -u "$HF_API_KEY:$HF_AUTH_CODE" \ + "$HF_BASE_URL/api/1.1/json/tickets/" | jq +``` + +### Get Paginated Tickets + +```bash +# Get 50 tickets per page (max), page 1 +curl -s -u "$HF_API_KEY:$HF_AUTH_CODE" \ + "$HF_BASE_URL/api/1.1/json/tickets/?size=50&page=1" | jq +``` + +### Get Ticket Details + +```bash +# Replace TICKET_NUMBER with the ticket number (e.g., 3) +curl -s -u "$HF_API_KEY:$HF_AUTH_CODE" \ + "$HF_BASE_URL/api/1.1/json/ticket/TICKET_NUMBER/" | jq +``` + +**Note on attachments:** Attachment URLs in the response are signed S3 URLs with expiration times (typically ~15 minutes). If you need to download attachments, do so immediately after fetching ticket details. If URLs have expired, re-fetch the ticket to get fresh signed URLs. + +### Search/Filter Tickets + +```bash +# Filter by status (pending tickets) +curl -s -u "$HF_API_KEY:$HF_AUTH_CODE" \ + "$HF_BASE_URL/api/1.1/json/tickets/?status=_pending" | jq + +# Filter by assignee +curl -s -u "$HF_API_KEY:$HF_AUTH_CODE" \ + "$HF_BASE_URL/api/1.1/json/tickets/?status=_all&q=assignee:john" | jq + +# Filter by date created +curl -s -u "$HF_API_KEY:$HF_AUTH_CODE" \ + "$HF_BASE_URL/api/1.1/json/tickets/?status=_all&q=created-after:\"2024/01/01\"" | jq + +# Filter by contact email +curl -s -u "$HF_API_KEY:$HF_AUTH_CODE" \ + "$HF_BASE_URL/api/1.1/json/tickets/?status=_all&q=contact:\"user@example.com\"" | jq +``` + +### Create a Ticket + +```bash +curl -s -X POST -u "$HF_API_KEY:$HF_AUTH_CODE" \ + -H "Content-Type: application/json" \ + "$HF_BASE_URL/api/1.1/json/tickets/" \ + -d '{ + "name": "John Doe", + "email": "john@example.com", + "category": 1, + "subject": "Help with account access", + "text": "I cannot log in to my account. Please help.", + "priority": 2 + }' | jq +``` + +**Required fields:** +- `name`: Contact name (required for new contacts) +- `email`: Contact email (required for new contacts) +- `category`: Category ID (must be a public category) +- `subject`: Ticket subject +- `text` or `html`: Ticket message content + +**Optional fields:** +- `priority`: Priority ID +- `assignee`: Agent ID (use `null` for unassigned) +- `tags`: Comma-separated tags +- `cc`, `bcc`: Comma-separated email addresses +- `due_date`: Format `yyyy-mm-dd` or `dd/mm/yyyy` +- `visible_only_staff`: `true`/`false` for private tickets +- **Ticket custom fields:** `t-cf-: ` (see below). + +**Setting custom fields at create time:** custom fields are passed as **form-field-style keys** named `t-cf-`, *not* nested under a `custom_fields` object. For enumeration fields (`type: "choice"`), `` is the choice's numeric ID; for text/number fields it's the raw value. + +```bash +curl -s -X POST -u "$HF_API_KEY:$HF_AUTH_CODE" \ + -H "Content-Type: application/json" \ + "$HF_BASE_URL/api/1.1/json/tickets/" \ + -d '{ + "name": "John Doe", + "email": "john@example.com", + "category": 1, + "subject": "Help with account access", + "html": "

I cannot log in to my account.

", + "priority": 2, + "t-cf-1": 3, + "t-cf-7": "conv_abc123" + }' | jq +``` + +If a custom field is marked `required: true` (or `compulsory_on_completed: true`) and omitted, the create call fails with a 422-style body: + +```json +{"error":[{"field":"t-cf-1","errors":["This field is required"]}]} +``` + +To discover the available custom field IDs, their types, and the valid choice IDs for enumeration fields, use **List Ticket Custom Fields** below (`GET /ticket_custom_fields/`). The same fields appear on each ticket's `GET /ticket//` response as `custom_fields[]` with `{id, name, type, value, value_id}` — useful for inspecting what's already set on an existing ticket. + +## Write-safety: avoid duplicate writes + + +HappyFox's v1.1 REST API has **no endpoint to edit or delete an individual update / private note** — once posted, a note can only be removed by deleting the entire ticket (almost never the right tool) or by a human in the staff UI. This means there is no "undo" for write operations. + +**Never retry a POST just because the previous output looked truncated or empty in your terminal.** The right reflex on an ambiguous write: + +1. Check the curl exit code (`$?`) — `0` means the HTTP transaction completed. +2. **GET the resource** (e.g., `GET /ticket//`) to confirm whether the write landed (look for a new `update_id`, or compare to a pre-write max). +3. Only retry if the GET confirms the write did NOT land. + +Recommended pattern for any write: + +```bash +# Capture pre-state +PRE=$(curl -s -u "$HF_API_KEY:$HF_AUTH_CODE" \ + "$HF_BASE_URL/api/1.1/json/ticket/$TID/" | jq '[.updates[].update_id] | max') + +# Single attempt — do NOT loop +curl -s -X POST -u "$HF_API_KEY:$HF_AUTH_CODE" \ + -H "Content-Type: application/json" \ + -d @payload.json \ + "$HF_BASE_URL/api/1.1/json/ticket/$TID/staff_pvtnote/" > /dev/null + +# Verify with GET +curl -s -u "$HF_API_KEY:$HF_AUTH_CODE" \ + "$HF_BASE_URL/api/1.1/json/ticket/$TID/" | \ + jq --argjson pre "$PRE" '[.updates[] | select(.update_id > $pre)]' +``` + +The probed-and-confirmed dead-end endpoints (all return 404 or 405): `POST/DELETE/PUT` on `/ticket//{update,updates,staff_pvtnote,note,notes}//[delete/|destroy/]`, and `POST` on `/ticket//{update,delete_update,update_delete,staff_pvtnote}/delete/` with `{update_id, staff_id}` body. + + +## Endpoints that do NOT exist on v1.1 + +Save yourself the probes — these all return 404 (or the generic "Cannot find the requested object"): + +| Endpoint | Notes | +|---|---| +| `/ticket//linked_objects/` | Native integrations' linked-object metadata is **not** exposed via API | +| `/ticket//external_links/` | Same | +| `/ticket//integrations/` | Same | +| `/ticket//linked_tickets/`, `/related_tickets/`, `/linked_issues/` | Same | +| `/integrations/` (global) | Same | +| Update edit / delete (any path tried) | See "Write-safety" section above | + +If you need to discover whether a ticket is linked to an external system via a HappyFox integration (e.g., the Linear integration), the link is typically visible only in the staff UI's sidebar or by querying the *destination* system's API. + +## Message Formatting: HTML Only, No Markdown + + +HappyFox **renders HTML** in the `html` field of replies and private notes, but **does NOT render Markdown**. Raw Markdown syntax (`##`, `**bold**`, `[text](url)`, etc.) appears verbatim as literal characters in the UI. + +**Empirically verified renderings:** + +| Element | Renders? | Use for | +|---|---|---| +| `

`, `
`, `

` | ✅ | Structure / line breaks | +| `text` | ✅ Clickable | Always wrap URLs — don't rely on auto-linking | +| ``, `` | ✅ Bold | Emphasis | +| ``, `` | ✅ Italic | Disclosures, asides | +| `