From merge-unified
Validate a Merge Unified API integration end-to-end. Use when a developer says "validate my Merge integration", "test my Merge setup", "is my Merge connection working", "debug my Merge API call", "verify my Linked Account", "check my Merge scopes", "my Merge API returns empty", "Merge sync not working", "check my account_token", or after completing /merge-unified:onboarding. Runs diagnostic checks against the Merge API and outputs a pass/fail checklist with actionable fixes.
How this skill is triggered — by the user, by Claude, or both
Slash command
/merge-unified:integration-validatorThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Run diagnostic checks against a developer's Merge integration and output a pass/fail report. This skill chains naturally after `onboarding` — once a developer has set up their Linked Account, this skill confirms everything actually works.
Run diagnostic checks against a developer's Merge integration and output a pass/fail report. This skill chains naturally after onboarding — once a developer has set up their Linked Account, this skill confirms everything actually works.
Activate when:
/merge-unified:onboarding completes, proactively suggest this skillDo NOT activate for:
onboarding)When this skill activates for the first time in a conversation, say:
I'm the Merge Integration Validator (v0.1.0). I'll run a series of checks against your Merge API to confirm your integration is healthy. I'll need your API key, account_token, and the category you're integrating.
Ask the developer for (skip anything already known from context):
test_xxx or production_xxxaccount_token for the Linked Account to validatehris, ats, crm, accounting, ticketing, filestorage, knowledgebase, or mktgWarn: "I'll generate code that makes real API calls. If you're using a production key, these are read-only (GET) calls — no data will be modified."
Generate a diagnostic script in the developer's language that runs all checks from references/diagnostic-endpoints.md. The script should:
import os
import requests
API_KEY = os.environ.get("MERGE_API_KEY", "YOUR_API_KEY")
ACCOUNT_TOKEN = os.environ.get("MERGE_ACCOUNT_TOKEN", "YOUR_ACCOUNT_TOKEN")
CATEGORY = "hris" # replace with actual category
BASE = "https://api.merge.dev/api"
headers = {
"Authorization": f"Bearer {API_KEY}",
"X-Account-Token": ACCOUNT_TOKEN,
}
results = []
# Check 1: API key + account_token valid (combined auth check)
def check_auth():
r = requests.get(f"{BASE}/{CATEGORY}/v1/account-details", headers=headers)
if r.status_code == 200:
data = r.json()
return "PASS", f"Auth valid — integration: {data.get('integration_name', 'unknown')}, status: {data.get('status', 'unknown')}"
elif r.status_code == 401:
# Try without account_token to isolate which credential is wrong
r2 = requests.get(f"{BASE}/{CATEGORY}/v1/account-details", headers={"Authorization": f"Bearer {API_KEY}"})
if r2.status_code == 401:
return "FAIL", "API key rejected (401). Verify at https://app.merge.dev/keys. Check env: test_ vs production_"
else:
return "FAIL", "API key is valid but account_token is rejected. Exchange a fresh public_token or check the stored token."
else:
return "FAIL", f"Unexpected status {r.status_code}: {r.text[:200]}"
# Check 3: Sync status
def check_sync_status():
r = requests.get(f"{BASE}/{CATEGORY}/v1/sync-status", headers=headers)
if r.status_code == 200:
data = r.json()
models = data.get("results", [])
synced = [m for m in models if m.get("status") == "DONE"]
syncing = [m for m in models if m.get("status") in ("SYNCING", "PARTIALLY_SYNCED")]
failed = [m for m in models if m.get("status") == "FAILED"]
if failed:
names = ", ".join(m.get("model_name", "?") for m in failed)
return "FAIL", f"Sync failed for: {names}. Check Linked Account page in dashboard."
elif syncing:
names = ", ".join(m.get("model_name", "?") for m in syncing)
return "WARN", f"Still syncing: {names}. Wait and re-run."
else:
return "PASS", f"{len(synced)} models synced successfully"
else:
return "FAIL", f"Could not fetch sync status: {r.status_code}"
# Check 4: Data exists (primary model)
def check_data_exists():
primary_models = {
"hris": "employees", "ats": "candidates", "crm": "contacts",
"accounting": "invoices", "ticketing": "tickets",
"filestorage": "files", "knowledgebase": "articles", "mktg": "campaigns",
}
model = primary_models.get(CATEGORY, "")
r = requests.get(f"{BASE}/{CATEGORY}/v1/{model}?page_size=1", headers=headers)
if r.status_code == 200:
data = r.json()
count = len(data.get("results", []))
if count > 0:
return "PASS", f"Data found — {model} endpoint returned results"
else:
return "WARN", "Empty results. Check: (1) scopes enabled at /configuration/common-model-scopes, (2) initial sync completed, (3) source provider has data for this model"
else:
return "FAIL", f"{model} endpoint returned {r.status_code}: {r.text[:200]}"
# Check 5: Pagination works
def check_pagination():
primary_models = {
"hris": "employees", "ats": "candidates", "crm": "contacts",
"accounting": "invoices", "ticketing": "tickets",
"filestorage": "files", "knowledgebase": "articles", "mktg": "campaigns",
}
model = primary_models.get(CATEGORY, "")
r = requests.get(f"{BASE}/{CATEGORY}/v1/{model}?page_size=1", headers=headers)
if r.status_code != 200:
return "SKIP", "Skipped (primary model check failed)"
data = r.json()
cursor = data.get("next")
if cursor is None:
return "PASS", "Only one page of data (or empty) — pagination not needed yet"
r2 = requests.get(f"{BASE}/{CATEGORY}/v1/{model}?page_size=1&cursor={cursor}", headers=headers)
if r2.status_code == 200:
return "PASS", "Pagination works — fetched page 2 successfully"
else:
return "FAIL", f"Page 2 failed: {r2.status_code}"
# Run all checks
checks = [
("Auth (API key + account_token)", check_auth),
("Sync Status", check_sync_status),
("Data Exists", check_data_exists),
("Pagination", check_pagination),
]
print("=" * 50)
print("Merge Integration Validator")
print("=" * 50)
all_pass = True
for name, fn in checks:
status, msg = fn()
icon = {"PASS": "✅", "FAIL": "❌", "WARN": "⚠️", "SKIP": "⏭️"}.get(status, "?")
print(f"\n{icon} {status}: {name}")
print(f" {msg}")
if status == "FAIL":
all_pass = False
print("\n" + "=" * 50)
if all_pass:
print("All checks passed. Your Merge integration is healthy.")
else:
print("Some checks failed. Fix the issues above and re-run.")
exit(0 if all_pass else 1)
After the script runs, explain each result:
If all basic checks pass, offer these additional validations:
SYMPTOM: All checks fail with 401.
CAUSE: API key environment mismatch (test key against production data or vice versa).
FIX: Verify key at https://app.merge.dev/keys. Test keys start with test_, production with production_.
SYMPTOM: API key passes but account_token fails. CAUSE: Token was for a different key environment, or the Linked Account was deleted. FIX: Check the Linked Account in the dashboard. If it's gone, re-trigger the onboarding flow.
SYMPTOM: Sync status shows DONE but data is empty. CAUSE: Common Model scopes not enabled for this model. FIX: Go to https://app.merge.dev/configuration/common-model-scopes and enable the relevant model.
SYMPTOM: Pagination cursor returns 400. CAUSE: Cursor expired or malformed. FIX: Cursors are ephemeral. Always fetch a fresh first page, then paginate from there.
references/diagnostic-endpoints.mdCreates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub merge-api/merge-unified-skills --plugin merge-unified