Skillshub attio-debug-bundle

install
source · Clone the upstream repo
git clone https://github.com/ComeOnOliver/skillshub
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/ComeOnOliver/skillshub "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/jeremylongshore/claude-code-plugins-plus-skills/attio-debug-bundle" ~/.claude/skills/comeonoliver-skillshub-attio-debug-bundle && rm -rf "$T"
manifest: skills/jeremylongshore/claude-code-plugins-plus-skills/attio-debug-bundle/SKILL.md
source content

Attio Debug Bundle

Overview

Collect all diagnostic evidence needed to debug Attio API issues or file a support ticket. Checks auth, scopes, object schema, rate limit headers, and endpoint connectivity.

Prerequisites

  • ATTIO_API_KEY
    environment variable set
  • curl
    and
    jq
    available
  • Permissions to read project environment

Instructions

Step 1: Run the Full Diagnostic Script

#!/bin/bash
# attio-debug-bundle.sh -- Collects Attio diagnostic evidence
set -euo pipefail

BUNDLE_DIR="attio-debug-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$BUNDLE_DIR"
SUMMARY="$BUNDLE_DIR/summary.txt"

echo "=== Attio Debug Bundle ===" | tee "$SUMMARY"
echo "Generated: $(date -u +%Y-%m-%dT%H:%M:%SZ)" | tee -a "$SUMMARY"
echo "" | tee -a "$SUMMARY"

# 1. Check token is set (never log the actual key)
echo "--- Token Status ---" | tee -a "$SUMMARY"
if [ -z "${ATTIO_API_KEY:-}" ]; then
  echo "ATTIO_API_KEY: NOT SET" | tee -a "$SUMMARY"
  echo "Cannot proceed without API key" | tee -a "$SUMMARY"
  exit 1
else
  echo "ATTIO_API_KEY: SET (${#ATTIO_API_KEY} chars, starts with ${ATTIO_API_KEY:0:3}...)" | tee -a "$SUMMARY"
fi

# 2. API connectivity and auth test
echo "" | tee -a "$SUMMARY"
echo "--- API Connectivity ---" | tee -a "$SUMMARY"
HTTP_CODE=$(curl -s -o "$BUNDLE_DIR/objects-response.json" -w "%{http_code}" \
  -H "Authorization: Bearer ${ATTIO_API_KEY}" \
  https://api.attio.com/v2/objects)
echo "GET /v2/objects: HTTP $HTTP_CODE" | tee -a "$SUMMARY"

if [ "$HTTP_CODE" = "200" ]; then
  echo "AUTH: OK" | tee -a "$SUMMARY"
  jq -r '.data[] | "  - " + .api_slug + " (" + .singular_noun + ")"' \
    "$BUNDLE_DIR/objects-response.json" | tee -a "$SUMMARY"
elif [ "$HTTP_CODE" = "401" ]; then
  echo "AUTH: FAILED -- token invalid or revoked" | tee -a "$SUMMARY"
elif [ "$HTTP_CODE" = "403" ]; then
  echo "AUTH: INSUFFICIENT SCOPES" | tee -a "$SUMMARY"
  jq '.message' "$BUNDLE_DIR/objects-response.json" 2>/dev/null | tee -a "$SUMMARY"
fi

# 3. Rate limit headers
echo "" | tee -a "$SUMMARY"
echo "--- Rate Limit Status ---" | tee -a "$SUMMARY"
curl -s -D "$BUNDLE_DIR/headers.txt" -o /dev/null \
  -H "Authorization: Bearer ${ATTIO_API_KEY}" \
  https://api.attio.com/v2/objects
grep -i "x-ratelimit\|retry-after" "$BUNDLE_DIR/headers.txt" 2>/dev/null | tee -a "$SUMMARY" || echo "No rate limit headers found" | tee -a "$SUMMARY"

# 4. List attributes for core objects
echo "" | tee -a "$SUMMARY"
echo "--- Object Schemas ---" | tee -a "$SUMMARY"
for obj in people companies; do
  echo "  $obj attributes:" | tee -a "$SUMMARY"
  curl -s "https://api.attio.com/v2/objects/$obj/attributes" \
    -H "Authorization: Bearer ${ATTIO_API_KEY}" \
    | jq -r '.data[] | "    " + .api_slug + " (" + .type + ", required=" + (.is_required|tostring) + ")"' \
    2>/dev/null | tee -a "$SUMMARY" || echo "    FAILED" | tee -a "$SUMMARY"
done

# 5. List available lists
echo "" | tee -a "$SUMMARY"
echo "--- Lists ---" | tee -a "$SUMMARY"
curl -s https://api.attio.com/v2/lists \
  -H "Authorization: Bearer ${ATTIO_API_KEY}" \
  | jq -r '.data[] | "  - " + .api_slug + " (" + .name + ")"' \
  2>/dev/null | tee -a "$SUMMARY" || echo "  FAILED (may need list_entry:read scope)" | tee -a "$SUMMARY"

# 6. Webhooks
echo "" | tee -a "$SUMMARY"
echo "--- Webhooks ---" | tee -a "$SUMMARY"
curl -s https://api.attio.com/v2/webhooks \
  -H "Authorization: Bearer ${ATTIO_API_KEY}" \
  | jq -r '.data[] | "  - " + .id.webhook_id + " -> " + .target_url' \
  2>/dev/null | tee -a "$SUMMARY" || echo "  FAILED (may need webhook:read-write scope)" | tee -a "$SUMMARY"

# 7. Environment info
echo "" | tee -a "$SUMMARY"
echo "--- Environment ---" | tee -a "$SUMMARY"
echo "Node: $(node --version 2>/dev/null || echo 'not installed')" | tee -a "$SUMMARY"
echo "OS: $(uname -s) $(uname -r)" | tee -a "$SUMMARY"

# 8. Status page
echo "" | tee -a "$SUMMARY"
echo "--- Attio Status ---" | tee -a "$SUMMARY"
curl -s https://status.attio.com/api/v2/status.json \
  | jq -r '.status.description' 2>/dev/null | tee -a "$SUMMARY" || echo "Could not reach status page" | tee -a "$SUMMARY"

# Package
tar -czf "$BUNDLE_DIR.tar.gz" "$BUNDLE_DIR"
rm -rf "$BUNDLE_DIR"
echo ""
echo "Bundle created: $BUNDLE_DIR.tar.gz"

Step 2: Review Before Sharing

Always check the bundle for leaked secrets before sharing:

tar -tzf attio-debug-*.tar.gz  # List files
tar -xzf attio-debug-*.tar.gz && cat */summary.txt

Safe to share: HTTP status codes, object slugs, attribute types, rate limit headers, error messages.

Never share: Full API key, record IDs with PII, webhook secrets.

Step 3: Quick Single-Command Diagnostic

For fast triage without a full bundle:

# One-liner: auth + objects + rate limit check
curl -s -w "\n--- HTTP %{http_code} ---\n" \
  -H "Authorization: Bearer ${ATTIO_API_KEY}" \
  https://api.attio.com/v2/objects | jq '{objects: [.data[].api_slug], count: (.data|length)}'

Programmatic Health Check

interface AttioDiagnostic {
  auth: "ok" | "failed" | "insufficient_scopes";
  objects: string[];
  lists: string[];
  latencyMs: number;
  rateLimitRemaining?: number;
}

async function diagnoseAttio(): Promise<AttioDiagnostic> {
  const start = Date.now();
  try {
    const res = await fetch("https://api.attio.com/v2/objects", {
      headers: { Authorization: `Bearer ${process.env.ATTIO_API_KEY}` },
    });

    const latencyMs = Date.now() - start;

    if (res.status === 401) return { auth: "failed", objects: [], lists: [], latencyMs };
    if (res.status === 403) return { auth: "insufficient_scopes", objects: [], lists: [], latencyMs };

    const data = await res.json();
    return {
      auth: "ok",
      objects: data.data.map((o: any) => o.api_slug),
      lists: [], // fetch separately if needed
      latencyMs,
      rateLimitRemaining: parseInt(res.headers.get("x-ratelimit-remaining") || "0"),
    };
  } catch {
    return { auth: "failed", objects: [], lists: [], latencyMs: Date.now() - start };
  }
}

Error Handling

Diagnostic resultMeaningAction
HTTP 200, objects listedAuth and connectivity OKIssue is in your code or data
HTTP 401Token invalidRegenerate in Attio dashboard
HTTP 403Missing scopesAdd scopes to token
HTTP 429Rate limited right nowWait for
Retry-After
, see
attio-rate-limits
Connection errorNetwork/firewall issueCheck DNS, proxy, firewall rules

Resources

Next Steps

For rate limit issues, see

attio-rate-limits
. For error codes, see
attio-common-errors
.