Skill: sebastienb/qrdex (version 1.0.0)

--- name: qrdex description: Create, manage, and track QR codes using the QRdex.io REST API. Use when working with QR code generation, URL shortening with QR codes, WiFi QR codes, email/SMS/WhatsApp QR codes, scan tracking, or any QRdex.io operations. Supports all QR types (url, email, telephone, sms, whatsapp, wifi) with customizable colors and shapes. --- # QRdex Manage QR codes via the QRdex.io REST API. ## Setup Set the API key as an environment variable: ```bash export QRDEX_API_KEY="your-api-key" ``` Get a key from: QRdex.io → Team Settings → API section. API access requires Growth plan or above. ## Quick Reference Base URL: `https://qrdex.io/api/v1` All requests require `Authorization: Bearer $QRDEX_API_KEY` and `Content-Type: application/json`. ### Create a QR Code ```bash curl -X POST https://qrdex.io/api/v1/qr_codes \ -H "Authorization: Bearer $QRDEX_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "qr_code": { "title": "My Website", "qr_type": "url", "url": "https://example.com" } }' ``` ### QR Types and Required Fields | Type | Required Fields | |------|----------------| | `url` | `url` | | `email` | `email_address` (optional: `email_subject`, `message`) | | `telephone` | `telephone_number` | | `sms` | `telephone_number` (optional: `message`) | | `whatsapp` | `telephone_number` (optional: `message`) | | `wifi` | `wifi_ssid` (optional: `wifi_encryption`, `wifi_password`, `wifi_hidden`) | ### Common Optional Fields - `foreground_color` — hex color (default: `#000000`) - `background_color` — hex color (default: `#FFFFFF`) - `shape` — QR code shape (default: `rounded`) - `track_scans` — enable scan tracking (default: `true`) ### List QR Codes ```bash curl https://qrdex.io/api/v1/qr_codes \ -H "Authorization: Bearer $QRDEX_API_KEY" ``` Query params: `page`, `per_page` (max 100), `qr_type` filter. ### Get / Update / Delete ```bash # Get curl https://qrdex.io/api/v1/qr_codes/:id -H "Authorization: Bearer $QRDEX_API_KEY" # Update (partial — only send changed fields) curl -X PATCH https://qrdex.io/api/v1/qr_codes/:id \ -H "Authorization: Bearer $QRDEX_API_KEY" \ -H "Content-Type: application/json" \ -d '{"qr_code": {"title": "New Title"}}' # Delete (soft-delete) curl -X DELETE https://qrdex.io/api/v1/qr_codes/:id -H "Authorization: Bearer $QRDEX_API_KEY" ``` ### Download QR Image (SVG) ```bash curl https://qrdex.io/api/v1/qr_codes/:id/image \ -H "Authorization: Bearer $QRDEX_API_KEY" -o qr.svg ``` Returns `image/svg+xml`. Use the `image_url` field from any QR code response directly in `<img>` tags. ## Using the Python Script For programmatic use, use `scripts/qrdex_api.py`: ```bash # Set API key export QRDEX_API_KEY="your-key" # List QR codes python scripts/qrdex_api.py list # Create QR codes python scripts/qrdex_api.py create --title "My Site" --type url --url "https://example.com" python scripts/qrdex_api.py create --title "WiFi" --type wifi --ssid "Guest" --wifi-password "pass123" python scripts/qrdex_api.py create --title "Email" --type email --email "[REDACTED_SECRET]" python scripts/qrdex_api.py create --title "Chat" --type whatsapp --phone "+15551234567" --message "Hello!" # Get details python scripts/qrdex_api.py get 123 # Update python scripts/qrdex_api.py update 123 --title "Updated Title" --fg-color "#FF0000" # Delete python scripts/qrdex_api.py delete 123 # Download image python scripts/qrdex_api.py image 123 -o qr.svg ``` ## Error Handling - `401` — Invalid/missing API key - `403` — No permission - `404` — QR code not found or belongs to different team - `422` — Validation error or plan limit reached - `429` — Rate limited (100 req/min per key). Check `X-RateLimit-Remaining` header. ## API Reference For full field descriptions and response schemas, see `references/API_REFERENCE.md`.

path: references/API_REFERENCE.md
content: # QRdex API Reference

Base URL: `https://qrdex.io/api/v1`

## Authentication

All requests require: `[REDACTED_SECRET]`

API keys are scoped to a team. All QR codes created with a key belong to that team.

## Endpoints

### POST /qr_codes — Create

Request body (nested in `qr_code` object):

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `title` | string | Yes | Display name |
| `qr_type` | string | Yes | `url`, `email`, `telephone`, `sms`, `whatsapp`, `wifi` |
| `url` | string | If type=url | Target URL |
| `telephone_number` | string | If type=telephone/sms/whatsapp | Phone number |
| `email_address` | string | If type=email | Email address |
| `email_subject` | string | No | Subject for email type |
| `message` | string | No | Body for sms/whatsapp/email |
| `wifi_ssid` | string | If type=wifi | Network name |
| `wifi_encryption` | string | No | `WPA`, `WEP`, or `nopass` |
| `wifi_password` | string | No | WiFi password |
| `wifi_hidden` | boolean | No | Hidden network flag |
| `foreground_color` | string | No | Hex color (default: `#000000`) |
| `background_color` | string | No | Hex color (default: `#FFFFFF`) |
| `shape` | string | No | QR shape (default: `rounded`) |
| `track_scans` | boolean | No | Enable tracking (default: `true`) |

Response (201):

```json
{
  "data": {
    "id": 123,
    "title": "My QR",
    "qr_type": "url",
    "short_url": "abc12xyz",
    "full_short_url": "https://qrdex.io/abc12xyz",
    "scans_count": 0,
    "track_scans": true,
    "foreground_color": "#000000",
    "background_color": "#FFFFFF",
    "shape": "rounded",
    "created_at": "2025-01-15T10:30:00Z",
    "updated_at": "2025-01-15T10:30:00Z",
    "image_url": "https://qrdex.io/api/v1/qr_codes/123/image",
    "url": "https://example.com",
    "redirect_url": "https://example.com",
    "has_logo": false
  }
}
```

### GET /qr_codes — List

Query params:

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `page` | integer | 1 | Page number |
| `per_page` | integer | 25 | Results per page (max 100) |
| `qr_type` | string | — | Filter: `url`, `email`, `telephone`, `sms`, `whatsapp`, `wifi` |

Response includes `meta` with pagination: `page`, `per_page`, `total`, `total_pages`.

### GET /qr_codes/:id — Get Single

Returns full QR code object including all type-specific fields.

### GET /qr_codes/:id/image — SVG Image

Returns `image/svg+xml`. Respects foreground/background color settings.

### PATCH /qr_codes/:id — Update

Same body params as Create, all optional. Only provided fields are updated.

### DELETE /qr_codes/:id — Soft Delete

Returns `{"data": {"id": 123, "deleted": true}}`.

## Error Responses

```json
{"error": "Description of what went wrong."}
```

| Code | Meaning |
|------|---------|
| 200 | OK |
| 201 | Created |
| 401 | Invalid or missing API key |
| 403 | No permission |
| 404 | QR code not found |
| 422 | Validation error or plan limit reached |
| 429 | Rate limited (100 req/min). Check `X-RateLimit-Remaining` header. |


path: scripts/qrdex_api.py
content: #!/usr/bin/env python3
"""QRdex.io API client CLI. Requires: requests (pip install requests)."""
import os
import sys
import json
import argparse

try:
    import requests
except ImportError:
    print("Error: pip install requests", file=sys.stderr)
    sys.exit(1)

BASE = "https://qrdex.io/api/v1"


def headers():
    key = os.environ.get("QRDEX_API_KEY")
    if not key:
        print("Error: Set QRDEX_API_KEY env var", file=sys.stderr)
        sys.exit(1)
    return {"Authorization": f"Bearer {key}", "Content-Type": "application/json"}


def api(method, path, **kwargs):
    r = getattr(requests, method)(f"{BASE}{path}", headers=headers(), **kwargs)
    if r.status_code == 204:
        return {}
    if r.headers.get("content-type", "").startswith("image/"):
        return r.content
    try:
        data = r.json()
    except Exception:
        data = {"raw": r.text}
    if r.status_code >= 400:
        print(f"Error {r.status_code}: {json.dumps(data, indent=2)}", file=sys.stderr)
        sys.exit(1)
    return data


def cmd_list(args):
    params = {}
    if args.page:
        params["page"] = args.page
    if args.per_page:
        params["per_page"] = args.per_page
    if args.qr_type:
        params["qr_type"] = args.qr_type
    data = api("get", "/qr_codes", params=params)
    for qr in data.get("data", []):
        print(f"  [{qr['id']}] {qr['title']}  ({qr['qr_type']})  {qr['full_short_url']}  scans={qr['scans_count']}")
    meta = data.get("meta", {})
    print(f"\nPage {meta.get('page')}/{meta.get('total_pages')} ({meta.get('total')} total)")


def cmd_create(args):
    qr = {"title": args.title, "qr_type": args.type}
    if args.url:
        qr["url"] = args.url
    if args.phone:
        qr["telephone_number"] = args.phone
    if args.email:
        qr["email_address"] = args.email
    if args.subject:
        qr["email_subject"] = args.subject
    if args.message:
        qr["message"] = args.message
    if args.ssid:
        qr["wifi_ssid"] = args.ssid
    if args.wifi_encryption:
        qr["wifi_encryption"] = args.wifi_encryption
    if args.wifi_password:
        qr["wifi_password"] = args.wifi_password
    if args.wifi_hidden:
        qr["wifi_hidden"] = True
    if args.fg_color:
        qr["foreground_color"] = args.fg_color
    if args.bg_color:
        qr["background_color"] = args.bg_color
    if args.shape:
        qr["shape"] = args.shape
    data = api("post", "/qr_codes", json={"qr_code": qr})
    d = data["data"]
    print(f"Created [{d['id']}] {d['title']}")
    print(f"  URL: {d['full_short_url']}")
    print(f"  Image: {d['image_url']}")


def cmd_get(args):
    data = api("get", f"/qr_codes/{args.id}")
    print(json.dumps(data["data"], indent=2))


def cmd_update(args):
    qr = {}
    if args.title:
        qr["title"] = args.title
    if args.url:
        qr["url"] = args.url
    if args.fg_color:
        qr["foreground_color"] = args.fg_color
    if args.bg_color:
        qr["background_color"] = args.bg_color
    if args.shape:
        qr["shape"] = args.shape
    if not qr:
        print("Nothing to update", file=sys.stderr)
        sys.exit(1)
    data = api("patch", f"/qr_codes/{args.id}", json={"qr_code": qr})
    print(f"Updated [{data['data']['id']}] {data['data']['title']}")


def cmd_delete(args):
    data = api("delete", f"/qr_codes/{args.id}")
    print(f"Deleted [{data.get('data', {}).get('id')}]")


def cmd_image(args):
    content = api("get", f"/qr_codes/{args.id}/image")
    out = args.output or f"qr_{args.id}.svg"
    with open(out, "wb") as f:
        f.write(content)
    print(f"Saved to {out}")


def main():
    p = argparse.ArgumentParser(description="QRdex.io API client")
    sub = p.add_subparsers(dest="command", required=True)

    ls = sub.add_parser("list", help="List QR codes")
    ls.add_argument("--page", type=int)
    ls.add_argument("--per-page", type=int)
    ls.add_argument("--qr-type", choices=["url", "email", "telephone", "sms", "whatsapp", "wifi"])

    cr = sub.add_parser("create", help="Create a QR code")
    cr.add_argument("--title", required=True)
    cr.add_argument("--type", required=True, choices=["url", "email", "telephone", "sms", "whatsapp", "wifi"])
    cr.add_argument("--url")
    cr.add_argument("--phone")
    cr.add_argument("--email")
    cr.add_argument("--subject")
    cr.add_argument("--message")
    cr.add_argument("--ssid")
    cr.add_argument("--wifi-encryption", choices=["WPA", "WEP", "nopass"])
    cr.add_argument("--wifi-password")
    cr.add_argument("--wifi-hidden", action="store_true")
    cr.add_argument("--fg-color")
    cr.add_argument("--bg-color")
    cr.add_argument("--shape")

    gt = sub.add_parser("get", help="Get QR code details")
    gt.add_argument("id", type=int)

    up = sub.add_parser("update", help="Update a QR code")
    up.add_argument("id", type=int)
    up.add_argument("--title")
    up.add_argument("--url")
    up.add_argument("--fg-color")
    up.add_argument("--bg-color")
    up.add_argument("--shape")

    dl = sub.add_parser("delete", help="Delete a QR code")
    dl.add_argument("id", type=int)

    im = sub.add_parser("image", help="Download QR image")
    im.add_argument("id", type=int)
    im.add_argument("-o", "--output")

    args = p.parse_args()
    {"list": cmd_list, "create": cmd_create, "get": cmd_get,
     "update": cmd_update, "delete": cmd_delete, "image": cmd_image}[args.command](args)


if __name__ == "__main__":
    main()


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (HIGH)