{"id":"8d19629c-8881-4c23-8cb7-4793ef842dcc","entityType":"agent","slug":"clawhub-flashlabs-ai-flashrev-ai-enrich","name":"FlashRev AI Enrich","canonicalUrl":"https://www.xpersona.co/agent/clawhub-flashlabs-ai-flashrev-ai-enrich","canonicalPath":"/agent/clawhub-flashlabs-ai-flashrev-ai-enrich","generatedAt":"2026-10-11T07:41:54.213Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"editorial-content","verified":true,"confidence":"high","updatedAt":"2026-10-11T04:26:16.691Z","emptyReason":null},"description":"Use this skill when an AI agent needs to enrich a CSV lead list through the flashrev-ai-enrich npm CLI. Triggers on list enrichment, filling missing company... Skill: FlashRev AI Enrich Owner: flashlabs-ai Summary: Use this skill when an AI agent needs to enrich a CSV lead list through the flashrev-ai-enrich npm CLI. Triggers on list enrichment, filling missing company... Tags: latest:1.3.0 Version history: v1.3.0 | 2026-07-16T13:34:41.757Z | user Add contact waterfall enrichment: unify email and phone lookup, prioritize existing contact data before provider fallback, unloc","descriptionLabel":"Technical summary","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 1.2K downloads reported by the source. Last updated 10/11/2026.","installCommand":"clawhub skill install s17b1rwbqjt2myvdy8p31kem2986cv83:flashrev-ai-enrich","sourceUrl":"https://clawhub.ai/flashlabs-ai/flashrev-ai-enrich","homepage":"https://clawhub.ai/flashlabs-ai/skills/flashrev-ai-enrich","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/flashlabs-ai/flashrev-ai-enrich","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/flashlabs-ai/skills/flashrev-ai-enrich","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":61,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"Use this skill when an AI agent needs to enrich a CSV lead list through the flashrev-ai-enrich npm CLI. Triggers on list enrichment, filling missing company... "},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-11T04:26:16.691Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-11T04:26:16.691Z","emptyReason":null},"stars":null,"forks":null,"downloads":1160,"packageName":null,"latestVersion":"1.3.0","tractionLabel":"1.2K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-11T04:26:16.627Z","emptyReason":null},"lastUpdatedAt":"2026-10-11T04:26:16.691Z","lastCrawledAt":"2026-10-11T04:26:16.627Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-12T04:26:16.627Z","lastVerifiedAt":null,"highlights":[{"version":"1.3.0","createdAt":"2026-07-16T13:34:41.757Z","changelog":"Add contact waterfall enrichment: unify email and phone lookup, prioritize existing contact data before provider fallback, unlock and bill only contact types that return successfully, and allow person_id-only rows to continue enrichment instead of returning no_data.","fileCount":5,"zipByteSize":15627},{"version":"1.1.0","createdAt":"2026-07-15T13:56:29.783Z","changelog":"Add enrich_contact_waterfall: unified email+phone lookup via FlashRev People contact data first, Apollo fallback for missing types; per-type pay-on-success billing; person_id-only rows no longer return no_data.","fileCount":5,"zipByteSize":13065},{"version":"1.0.2","createdAt":"2026-05-30T03:54:46.231Z","changelog":"Fix customer_api SSRF rejection from batch-fatal (HTTP 403) to per-row failure (HTTP 400). Previously one blocked URL anywhere in the CSV would terminate the entire batch, contradicting the documented per-row failure semantics. Verified end-to-end against production backend with a mixed good/SSRF/file:// 3-row CSV.","fileCount":5,"zipByteSize":11005},{"version":"1.0.1","createdAt":"2026-05-30T03:17:23.669Z","changelog":"Security hardening for customer_api: SSRF blocklist (localhost / RFC1918 / 169.254.169.254 cloud metadata / IPv6 loopback / non-http schemes) + explicit SSRF & data-exfiltration warnings in SKILL.md and references. New --allow-internal-targets opt-out flag.","fileCount":5,"zipByteSize":11206},{"version":"1.0.0","createdAt":"2026-05-29T13:43:49.533Z","changelog":"Initial release","fileCount":5,"zipByteSize":9072}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s17b1rwbqjt2myvdy8p31kem2986cv83:flashrev-ai-enrich","setupComplexity":"low","setupSteps":["Setup complexity is LOW. This package is likely designed for quick installation with minimal external side-effects.","Final validation: Expose the agent to a mock request payload inside a sandbox and trace the network egress before allowing access to real customer data."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-flashlabs-ai-flashrev-ai-enrich/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-flashlabs-ai-flashrev-ai-enrich/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-flashlabs-ai-flashrev-ai-enrich/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-flashlabs-ai-flashrev-ai-enrich/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-flashlabs-ai-flashrev-ai-enrich/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-flashlabs-ai-flashrev-ai-enrich/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-11T07:41:54.210Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-flashlabs-ai-flashrev-ai-enrich/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-flashlabs-ai-flashrev-ai-enrich/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-flashlabs-ai-flashrev-ai-enrich/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-flashlabs-ai-flashrev-ai-enrich/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"high","updatedAt":"2026-10-11T04:26:16.691Z","emptyReason":null},"readme":"Skill: FlashRev AI Enrich\n\nOwner: flashlabs-ai\n\nSummary: Use this skill when an AI agent needs to enrich a CSV lead list through the flashrev-ai-enrich npm CLI. Triggers on list enrichment, filling missing company...\n\nTags: latest:1.3.0\n\nVersion history:\n\nv1.3.0 | 2026-07-16T13:34:41.757Z | user\n\nAdd contact waterfall enrichment: unify email and phone lookup, prioritize existing contact data before provider fallback, unlock and bill only contact types that return successfully, and allow person_id-only rows to continue enrichment instead of returning no_data.\n\nv1.1.0 | 2026-07-15T13:56:29.783Z | user\n\nAdd enrich_contact_waterfall: unified email+phone lookup via FlashRev People contact data first, Apollo fallback for missing types; per-type pay-on-success billing; person_id-only rows no longer return no_data.\n\nv1.0.2 | 2026-05-30T03:54:46.231Z | user\n\nFix customer_api SSRF rejection from batch-fatal (HTTP 403) to per-row failure (HTTP 400). Previously one blocked URL anywhere in the CSV would terminate the entire batch, contradicting the documented per-row failure semantics. Verified end-to-end against production backend with a mixed good/SSRF/file:// 3-row CSV.\n\nv1.0.1 | 2026-05-30T03:17:23.669Z | user\n\nSecurity hardening for customer_api: SSRF blocklist (localhost / RFC1918 / 169.254.169.254 cloud metadata / IPv6 loopback / non-http schemes) + explicit SSRF & data-exfiltration warnings in SKILL.md and references. New --allow-internal-targets opt-out flag.\n\nv1.0.0 | 2026-05-29T13:43:49.533Z | user\n\nInitial release\n\nArchive index:\n\nArchive v1.3.0: 5 files, 15627 bytes\n\nFiles: agents/openai.yaml (1055b), references/api_contract.md (6592b), skill-card.md (2834b), SKILL.md (27250b), _meta.json (137b)\n\nFile v1.3.0:SKILL.md\n\n---\nname: flashrev-ai-enrich\ndescription: Use this skill when an AI agent needs to enrich a CSV lead list through the flashrev-ai-enrich npm CLI. Triggers on list enrichment, filling missing company or person fields, verifying or unlocking emails and phones, finding CEOs, executives, LinkedIn posts, matching companies or people to FlashRev IDs, Google search/news/maps lookups, scraping a page, or running an LLM over each row. Agents must run with `FLASHREV_ENRICH_AI_MODE=1`, call `schema --json`, use only live `funcName` values, and invoke each command with explicit `--capability FUNC_NAME --map ... --output ...`. For broad person enrich requests, run a profile + contact pipeline when supported; for contact-only requests, run only the requested contact capability. Avoid `--prompt` unless explicitly requested. Dry-run and sample preview are required before live runs unless already authorized.\n---\n\n# FlashRev AI Enrich\n\nUse the `flashrev-ai-enrich` CLI to enrich CSV lead lists through FlashRev. The CLI does not send outreach messages. It reads CSV files, maps CSV columns to FlashRev capability inputs, validates the job with dry-run, previews enriched sample rows, then writes an enriched CSV.\n\n## Commands\n\n```\nflashrev-ai-enrich init [--force]                              Write default config\nflashrev-ai-enrich doctor [--no-api]                            Self-check Node / config / API\nflashrev-ai-enrich tokens [--json]                              Show balance / total / used / plan\nflashrev-ai-enrich token-history [--from YYYY-MM-DD] [--to YYYY-MM-DD] [--limit N] [--json]\n                                                                Show consumption log (auto-paginates)\nflashrev-ai-enrich schema [--advanced|--all] [--json]           List production-backed capabilities (synced from backend at runtime).\n                                                                Default view shows recommended product entries and category summaries;\n                                                                --advanced lists all atoms by category, --all shows the raw registry\nflashrev-ai-enrich plan --source X.csv [--goal contact|person|company|identity|all] [--contact-type email|phone|both] [--json] [--emit-jobs|--no-emit-jobs]\n                                                                Recommend concrete follow-up capabilities from the CSV's non-empty\n                                                                columns; JSON is read-only unless --emit-jobs is passed. 0 tokens\nflashrev-ai-enrich dry-run  --source leads.csv (--capability ID | --job planned.job.json | --prompt \"...\") [--map ...] [--output ...] [--json]\n                                                                Validate job and show run plan without calling backend. --json returns approvalReasons/wouldOverwrite\nflashrev-ai-enrich run      --source leads.csv [--out X.csv] (--capability ID | --job F | --prompt \"...\") [--yes] [--concurrency N] [--sample-size N] [--sample-only] [--report-json [PATH]] [--guided] [--overwrite]\n                                                                Real enrichment with sample preview. In AI mode stdout is JSON and progress is stderr\n```\n\n## Agent Execution Contract\n\nAgent runtimes should treat this CLI as a schema-first structured tool, not a natural-language router.\n\nUse this order for every non-trivial enrichment job:\n\n1. Set `FLASHREV_ENRICH_AI_MODE=1`.\n2. Run `flashrev-ai-enrich doctor --no-api`.\n3. Run `flashrev-ai-enrich schema --json`.\n4. Select one or more `funcName` values returned by the live schema. Each CLI command still runs exactly one `--capability`.\n   For broad requests, prefer `flashrev-ai-enrich plan --source X.csv --json` (0 tokens): it returns\n   ranked follow-up jobs with inferred `--map` values, argv, `approvalReasons[]`, output paths, and\n   overwrite/high-volume boundaries — no need to parse human recommendation text. Add `--emit-jobs`\n   only after file creation is acceptable.\n5. Run `flashrev-ai-enrich tokens --json`.\n6. Build explicit `--map` and `--output` flags from the CSV headers and the selected schema for each selected capability, or use job files emitted by `plan --emit-jobs`.\n7. Run `flashrev-ai-enrich dry-run` for each selected capability before that capability's live run (`--json` is automatic in ai-mode). Read `approvalReasons[]`, `requiresApproval`, and `wouldOverwrite`.\n8. Ask for approval unless the user already authorized `--yes`. Always stop for approval at the boundaries flagged by `plan --json` or `dry-run --json` (`approvalReasons[]` — contact cleartext unlock, `customer_api` egress, `--allow-internal-targets`, overwriting existing files, high-volume runs).\n9. For agent-mediated approval, first run `flashrev-ai-enrich run --sample-only --sample-size 10 ... --yes` after the user approves spending sample tokens. Show the JSON `sample` to the user and continue with the full `run` only after approval.\n10. Run `flashrev-ai-enrich run`, chaining each capability from the previous output CSV when multiple capabilities are needed. In AI mode, stdout is the structured result (output path, status counts, exact token spend); use `--report-json path` when a file copy is needed.\n11. Report the final output path, per-capability status counts, row errors, and actual token spend from the run report / CLI summary and token history.\n\nRules for agents:\n\n- Do not use `--prompt` by default. Use it only when the user explicitly requests prompt routing or does not want to choose a capability.\n- Never invent capability IDs, input fields, or output fields. Use only the live `schema --json` response.\n- Prefer explicit `--capability ID` even when the user's request is written in natural language.\n- If a requested capability is missing from the live schema, say it is unavailable in the connected environment instead of guessing a hidden backend route.\n- For a broad request such as \"enrich this CSV\" or \"complete these people\", enrich both person profile fields and contact fields when the CSV has a person identifier.\n- For contact-only planning, use `plan --contact-type email|phone|both` when the user asks for one contact channel or both.\n- If the request needs `customer_api`, confirm the destination domain before live `run`.\n- If the request needs `--allow-internal-targets`, get separate explicit approval before using that flag.\n\n## Supported starting signals\n\nUse this section to decide whether the user's CSV has enough information to start enrichment. Treat it as product-level guidance; the live `schema --json` response remains authoritative for exact capability inputs, rules, and output fields.\n\nPerson enrich can start from:\n\n| Signal strength | Accepted inputs |\n|---|---|\n| Best | `flashrev_person_id`, `person_linkedin_url`, `linkedin_url` |\n| Strong | `email`, `phone` |\n| Common | `full_name + company_name`, `first_name + last_name + company_name` |\n| Better common | Name plus company plus `job_title` |\n| Company context | Name plus `company_website`, `company_domain`, or `company_linkedin` |\n| Lowest confidence | `company_name + job_title`; use top-one selection only after the user accepts ambiguity, then pass `--param allow_low_confidence_match=true` |\n\nCompany enrich can start from:\n\n| Signal strength | Accepted inputs |\n|---|---|\n| Best | `flashrev_company_id`, `company_website`, `company_domain`, `company_linkedin` |\n| Common | `company_name`, preferably with `company_country`, `company_city`, or `industry` when available |\n\nContact enrich can start from:\n\n| Signal strength | Accepted inputs |\n|---|---|\n| Best | `flashrev_person_id`, `person_linkedin_url`, `linkedin_url` |\n| Strong | `email`, `phone` |\n| Common | `full_name + company_name`, `first_name + last_name + company_name`, or name plus company website/domain/LinkedIn |\n| Role search | `company_name + job_title`; use only after the user accepts top-one selection, then pass `--param allow_low_confidence_match=true` |\n\nRules:\n\n- Do not require normal users to provide FlashRev IDs; prefer LinkedIn, email, phone, or name plus company when IDs are absent.\n- Treat email, phone, LinkedIn, and name plus company as identity signals for broad person enrichment, not as proof that the user only wants contact fields.\n- If the user explicitly asks for contact-only output, route to the contact capabilities instead of profile enrichment.\n- If no supported starting signal is present, ask for LinkedIn URL, email, phone, name plus company, or company plus job title before running enrichment.\n\n## Required confirmations before real `run`\n\n1. User has a FlashRev account with available tokens (`flashrev-ai-enrich tokens` → `remaining > 0`).\n2. `FLASHREV_API_KEY` env var is set (generated from https://info.flashlabs.ai/settings/privateApps).\n3. Source CSV path and output CSV path are confirmed.\n4. `--capability ID` from live `flashrev-ai-enrich schema --json` is confirmed. Use `--prompt \"<intent>\"` only when the user explicitly asks for prompt routing.\n5. Input mappings (`--map flashrev_field=csv_column`) cover at least one capability rule. Skipped only when `--prompt` is explicitly used and the LLM returns valid mappings (still subject to rule validation afterwards).\n6. Output mappings (`--output csv_col=response_field`) or `--output-fields` are confirmed. Skipped only when `--prompt` is explicitly used and the LLM returned mappings, but always required for dynamic-output capabilities (e.g., `run_llm`, `scrape_and_extract`).\n7. `dry-run --json` first to validate mappings, row count, planned API calls, effective concurrency, `approvalReasons[]`, and `wouldOverwrite`.\n8. For agent workflows, prefer `run --sample-only --json --yes` after the user approves sample-token spend; show the returned `sample` JSON and continue to the full run only after approval.\n9. Do not proceed past the sample preview (default 10 rows, configurable via `--sample-size N`) unless the user approves or `--yes` is set.\n10. Existing output files require explicit approval and `--overwrite`.\n11. `customer_api` and `--allow-internal-targets` each require separate explicit approval.\n\n## Input modes\n\n### A. CSV mode (typical batch)\n\n```bash\nflashrev-ai-enrich run \\\n  --source leads.csv --out leads.enriched.csv \\\n  --capability enrich_email \\\n  --map first_name=first_name --map last_name=last_name --map company_name=company \\\n  --output verified_email=verified_business_email \\\n  --yes\n```\n\n`--map` connects CSV column → capability input field; `--output` connects CSV output column → backend response field.\n\n### B. Inline mode (single row test, no CSV)\n\n```bash\nflashrev-ai-enrich run \\\n  --capability verify_email \\\n  --input email=ada@example.com \\\n  --output ok=deliverable_email \\\n  --out out.csv --yes\n```\n\nIn inline mode the `--input key=value` pairs are auto-mapped (no need for `--map`).\n\n### C. Job file (for repeatable presets)\n\n```bash\nflashrev-ai-enrich run --source leads.csv --out out.csv --job enrich.job.json --yes\n```\n\nJob file shape:\n```json\n{\n  \"capability\": \"enrich_email\",\n  \"inputMapping\": {\n    \"first_name\":  \"first_name\",\n    \"last_name\":   \"last_name\",\n    \"company_name\": \"company\"\n  },\n  \"outputs\": {\n    \"verified_business_email\":  \"verified_business_email\",\n    \"all_verified_business_emails\": \"all_verified_business_emails\"\n  }\n}\n```\n\n## Default person enrichment routing\n\nWhen the user asks broadly to \"enrich\" a people CSV, do not stop at contact lookup. Treat the default outcome as person profile plus contact data when the CSV has a usable person identifier.\n\nPreferred broad-person flow (plan-driven):\n\n1. If the live schema exposes `enrich_person`, run it first — it resolves identity from any supported signal (phone, email, LinkedIn, name + company) and returns a People-detail-aligned full profile: stable anchors (`person_id`, `person_linkedin_url`), current role and company, location, skills, work/education history, plus contact data (`business_emails` / `personal_emails` / `phones`, `unlock_status` / `unlock_types`, `contact_billing_status`). **`enrich_person` unlocks email/phone contacts BY DEFAULT and this can spend contact credits (Credit orgs) or tokens (Token orgs)** — always dry-run and sample-preview before a live batch. Treat phone/email/LinkedIn as identity signals for this step, not as contact-only requests. If only `enrich_person_basic` is exposed, use that instead; if neither exists yet, run the fallback pipeline from the table below.\n2. Run `flashrev-ai-enrich plan --source <previous out>.csv --json` and execute the planned jobs the user approves (`dry-run --job` then `run --sample-only --job`, then full `run --job`). Stop for approval on every `requiresApproval: true` entry and inspect `approvalReasons[]`.\n\nContact-only requests can use `plan --contact-type email|phone|both --json` or route directly to `enrich_email` / `enrich_phone` / `enrich_contact_waterfall`.\n\n`enrich_person` contact-unlock rules:\n\n- Contacts are ON by default. There is no agent-facing `include_contacts` input — do not invent one. Profile-only mode is controlled by backend Nacos configuration (`flashrev.open-enrich.person.include-contacts-default` / `-company-overrides`); if the user insists on \"profile only, no contacts\", explain that this is an ops-side backend switch and only run if the environment is already configured profile-only (rows then report `contact_billing_status=disabled`).\n- To narrow contact types while contacts are enabled, pass a static param instead of a fake CSV column: `--param requested_types=email` or `--param requested_types=phone` (blank/omitted = both).\n- `company_name`/`company_website`/`company_linkedin` + `job_title` is low-confidence top-candidate matching. Do not run it automatically. First ask the user to confirm they accept top1 person selection and possible contact unlock cost; only then pass `--param allow_low_confidence_match=true`.\n- `contact_billing_status` per row: `ok` (all requested types billed or cached), `partial_billing_blocked` / `billing_blocked` (insufficient credits/tokens — cleartext is never returned for a blocked type), `disabled` (Nacos profile-only mode).\n\nFallback decision table (also used when the user names the outcome explicitly):\n\n| User intent | Default capabilities |\n|---|---|\n| Broad person enrich, complete people, enrich this CSV | `enrich_person` when live schema exposes it; otherwise `enrich_contact_waterfall` + `get_person_job_title` + `get_person_current_company` + `get_person_location` |\n| Contact info, emails and phones | `enrich_contact_waterfall` only |\n| Emails only (contact-only) | `enrich_email` only |\n| Phones or mobiles only (contact-only) | `enrich_phone` only |\n| Full profile plus one contact type | `enrich_person` with `--param requested_types=email` (or `phone`) |\n| Profile only, no contacts requested | `enrich_person` — but profile-only mode is a backend Nacos switch, not a request flag; see the contact-unlock rules above |\n\nApply these rules:\n\n- Use only capabilities exposed by the live `schema --json` response. If one capability in the default pipeline is missing, skip that step and report it.\n- Prefer `person_linkedin_url` / `linkedin_url` inputs when the CSV has a LinkedIn column. Otherwise use the strongest identity group available from the live schema.\n- Chain outputs through intermediate CSV files and make the final file the last step's `--out`.\n- Preserve earlier run results by giving each follow-up run distinct status and error columns, for example `job_title_enrich_status`, `current_company_enrich_status`, and `location_enrich_status`.\n- Keep `flashrev_enrich_status` for the first/main contact run unless the user asks for custom status columns.\n\n## Contact enrichment routing\n\nUse explicit contact capabilities from the live schema:\n\n| User intent | Capability |\n|---|---|\n| Emails only | `enrich_email` |\n| Phones or mobiles only | `enrich_phone` |\n| Emails and phones | `enrich_contact_waterfall`, only when live schema exposes it |\n| Emails and phones, no waterfall in schema | Ask before running `enrich_email` and `enrich_phone` sequentially |\n\n`enrich_email` and `enrich_phone` are still valid single-type contact capabilities. They use the backend contact lookup path for the requested contact type, so agents should keep using them for single-type requests instead of forcing `enrich_contact_waterfall`.\n\nDo not require users to provide `flashrev_person_id`; normal users usually do not have it. Use the strongest available identity input:\n\n| Input strength | Accepted inputs |\n|---|---|\n| Best | `person_linkedin_url` |\n| Strong | `email`, `phone`, `flashrev_person_id` |\n| Common | `full_name + company_name`, `first_name + last_name + company_name` |\n| Better common | Name plus company plus `job_title` |\n| Company role search | `company_name + job_title`, optionally with `company_website` or `company_linkedin`; requires user confirmation and `--param allow_low_confidence_match=true` |\n| Explicit opt-in only | `job_title` only; use only if the live schema exposes an explicit low-confidence opt-in field and the user accepts top-one selection |\n\nIf none of those identity inputs are available, ask the user for a LinkedIn URL, email, phone, name plus company, or company plus job title before running contact enrichment.\n\n### D. Prompt routing mode (ad-hoc human use; costs 1 extra token)\n\nSkip `--capability` and describe the intent in natural language. The CLI sends the prompt + CSV columns + capability registry to `run_llm`, which returns JSON `{ funcName, inputMapping, outputMapping, reasoning }`; the CLI prints a Routing-decision block and then runs the resulting job through the normal dry-run / sample / run pipeline.\n\n```bash\nflashrev-ai-enrich run --source leads.csv --out leads.enriched.csv \\\n  --prompt \"for each row, take the email column and verify it is a deliverable business email\" \\\n  --yes\n```\n\nRules of thumb when writing prompts:\n\n- Name the CSV column explicitly (\"take the **email** column\"); vague prompts make the LLM return empty mappings.\n- Describe the business outcome, not the capability name (\"find the CEO\" beats \"use get_company_ceo\").\n- One capability per prompt — the LLM picks exactly one funcName.\n- `--map` / `--output` on the command line override the LLM's choices; use them to lock specific columns while letting the LLM pick the capability.\n- `--capability X --prompt \"...\"` together: `--capability` wins, `--prompt` is ignored with a stderr warning (no routing token charged).\n- Unroutable prompts (e.g., \"make me a sandwich\") exit non-zero with the LLM's reasoning printed; zero rows run.\n\nAgents must skip prompt routing unless the user explicitly requests it. `schema --json` plus explicit `--capability ID` is cheaper, faster, and deterministic.\n\nFor contact lookup, agents should use the contact routing table above and pass explicit `--capability`, never prompt routing.\n\n## Status semantics (output CSV columns)\n\nEvery output CSV gets `flashrev_enrich_status` and `flashrev_enrich_error` columns:\n\n| status | meaning |\n|---|---|\n| `success` | Got business data; charged per capability `unitPriceToken`. |\n| `cached` | Hit contact-unlock dedup (same person/contact type already unlocked). 0 tokens. |\n| `no_data` | Backend returned 200 but the requested output fields are empty / null. 0 tokens. |\n| `failed` | HTTP error from backend, retries exhausted. 0 tokens. |\n\n`Failed` count > 0 with `Tokens used` > 0 means some rows got SOMETHING from backend (charged) but not the specific output fields the user asked for.\n\n## Cost reporting\n\n`Summary` line in `run` output prints `(balance before → balance after)` — that delta is the **authoritative** amount charged for the row enrichments. Each row's individual `cost.tokens` reported by backend may be slightly off under high concurrency (known limitation; `token-history` is always exact).\n\nCredit-based organizations: `enrich_person` / contact waterfall unlocks charge email/phone **credits** instead of tokens. The run summary prints a `Credits used` line (and `--report-json` includes `creditsUsed`) whenever credits were spent; per-row details come back in `cost.credits` / `cost.emailCredits` / `cost.phoneCredits`.\n\nWhen `--prompt` is used, the Routing-decision block prints its own `routing cost: 1 token(s)` line. That 1 token is **not** included in the `Summary` `balance before → after` delta, since routing happens before the balance snapshot. Use `token-history` for the authoritative total after the run.\n\n## Special capability: `customer_api`\n\n`customer_api` does NOT call FlashRev backend — the CLI fetches the user-provided URL locally and parses the response. 0 tokens.\n\nInputs (via `--map <field>=<csv_col>` or `--input <field>=<value>`):\n\n| field | required | default | notes |\n|---|---|---|---|\n| `url` | yes | — | target URL (alias: `endpoint`) |\n| `method` | no | `GET` | HTTP method |\n| `headers` | no | `{}` | JSON object of HTTP headers |\n| `body` | no | — | string (sent as-is) or object (JSON-serialized; Content-Type defaults to application/json) |\n| `params` | no | — | object of query-string params; appended to `url` |\n| `timeout` | no | `30000` | milliseconds before AbortError |\n\nThe response JSON (or `{ text }` wrapper for non-JSON) becomes the row's enrichment data; map output columns via `--output csv_col=response_field` as usual. Useful for mixing 3rd-party APIs into the same enrichment workflow.\n\n### Security warnings (read before using `customer_api` in an agent context)\n\n`customer_api` lets the CLI send arbitrary HTTP requests with row-derived URL / headers / body. The target URL is **not** owned by FlashRev — it is whatever the user, prompt, or CSV column supplied. This creates two real risk surfaces an agent must mitigate:\n\n1. **SSRF / internal-network probing.** A URL such as `http://127.0.0.1:8500/`, `http://169.254.169.254/latest/meta-data/iam/security-credentials/` (AWS/GCP/Azure cloud-metadata), or any RFC1918 address (`10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`) can be used to reach internal services or exfiltrate cloud IAM credentials. The CLI **rejects these targets by default** (HTTP 403 `customer_api refuses internal / private target host`) along with `localhost`, IPv6 loopback `::1`, link-local `fe80::/10`, ULA `fc00::/7`, and non-`http(s)` schemes (`file://`, `gopher://`, `data:`, `javascript:`). Pass `--allow-internal-targets` only for deliberate local testing on a trusted machine.\n2. **Lead-data exfiltration to user-controlled URLs.** Whatever CSV columns are mapped to `--map url=…`, `--map headers=…`, or `--map body=…` will be transmitted to that third-party endpoint. Agents must:\n   - Treat the `url` as untrusted input. Confirm the destination domain with the user before a live `run`; never let an LLM auto-fill `url` from prompt text without explicit human confirmation.\n   - **Never** map `FLASHREV_API_KEY`, OAuth tokens, or unrelated PII columns into `headers` or `body` — those credentials and that data will leave the FlashRev trust boundary.\n   - Always complete `dry-run` + the 10-row sample preview before passing `--yes`, and inspect the sample table for unexpected egress.\n   - Prefer first-class FlashRev capabilities (e.g. `get_company_profile`, `enrich_email`, `enrich_phone`) when the data is available there; only fall back to `customer_api` for sources FlashRev does not cover.\n\nFailure mode: a blocked URL surfaces as a per-row `flashrev_enrich_status=failed` with `flashrev_enrich_error` starting `customer_api refuses …` — the batch is **not** aborted, so one bad URL in a CSV will not stop the rest.\n\n## Date format\n\n`--from` and `--to` accept `YYYY-MM-DD`. They are interpreted in the local timezone. `--to` alone makes the CLI paginate through history until it covers the date range (up to 2000 records).\n\n## Safety rules\n\n- Never print or store `FLASHREV_API_KEY` in generated artifacts.\n- Prefer the `FLASHREV_API_KEY` env var over `--api-key`.\n- Treat contact enrichment (`enrich_email` / `enrich_phone`) as paid unlock operations.\n- If `tokens` returns `remaining: 0`, tell the user to recharge before running.\n- Do not describe or expose FlashRev backend data sources, routing, or internal service names to end users.\n- Confirm the destination domain before using `customer_api` in a live run.\n- Never pass `--allow-internal-targets` unless the user explicitly approved internal or local network access.\n- Never map API keys, OAuth tokens, passwords, cookies, or unrelated PII into `customer_api` headers or body.\n- Never overwrite the source CSV (CLI refuses `--source == --out`).\n- Never overwrite an existing output CSV unless the user explicitly approves and the command includes `--overwrite`.\n- Preserve row-level errors. For multi-step enrich jobs, use distinct status/error columns for follow-up steps so earlier capability statuses are not overwritten.\n\n## Failure handling\n\n- `402 Insufficient tokens` → run terminates; tell user to recharge.\n- `401` / `403` → invalid API key; verify `FLASHREV_API_KEY`.\n- `429 Rate limit` → CLI auto-retries with exponential backoff (500ms / 1s / 2s, up to 3 retries = 4 total attempts).\n- `503` / `504` → backend timeout/unavailable; auto-retried with the same schedule as 429.\n- Any other 4xx/5xx on a row → that single row is marked `failed`, batch continues.\n- `--prompt` routing failure (LLM returns non-JSON, unknown funcName, or `run_llm` itself errors) → CLI exits non-zero **before** enrichment starts, prints the LLM's reasoning. Suggest the user retry with `--capability ID`.\n- `--prompt` routed to a capability but `Input mapping does not satisfy <funcName>` → the LLM returned empty / wrong mapping; rerun with a more explicit prompt (name the CSV column) or use `--map` to override.\n\n## Workflow recipe\n\n```bash\n# 1. (first time) write config\nflashrev-ai-enrich init\nexport FLASHREV_API_KEY=\"sk_xxxx\"   # from info.flashlabs.ai/settings/privateApps\n\n# 2. verify\nexport FLASHREV_ENRICH_AI_MODE=1\nflashrev-ai-enrich doctor --no-api\n\n# 3. browse capabilities and pick one\nflashrev-ai-enrich schema --json\n\n# 4. check balance\nflashrev-ai-enrich tokens --json\n\n# 5. validate job and show run plan\nflashrev-ai-enrich dry-run --source leads.csv \\\n  --capability enrich_email \\\n  --map first_name=first_name --map last_name=last_name --map company_name=company\n\n# 6. real run with sample preview\nflashrev-ai-enrich run --source leads.csv --out out.csv \\\n  --capability enrich_email \\\n  --map first_name=first_name --map last_name=last_name --map company_name=company \\\n  --output verified_email=verified_business_email\n# (preview shown, type 'y' to continue, or pass --yes to auto-confirm)\n\n# 7. audit spend\nflashrev-ai-enrich token-history --from 2026-05-01\n```\n\n### Shortcut for ad-hoc human use (prompt routing)\n\nWhen the user does not know the capability name and is willing to spend 1 extra token to let the LLM pick:\n\n```bash\n# dry-run only routes (1 token) — no enrichment\nflashrev-ai-enrich dry-run --source leads.csv \\\n  --prompt \"find the CEO of each company\"\n\n# real run: 1 routing token + N rows\nflashrev-ai-enrich run --source leads.csv --out out.csv \\\n  --prompt \"find the CEO of each company\" --yes\n```\n\nAgents should skip this and pass `--capability` directly.\n\nFile v1.3.0:_meta.json\n\n{\n  \"ownerId\": \"kn728zt3jt22zg85rat5nantrd86d3dy\",\n  \"slug\": \"flashrev-ai-enrich\",\n  \"version\": \"1.3.0\",\n  \"publishedAt\": 1784208881757\n}\n\nFile v1.3.0:references/api_contract.md\n\n# FlashRev AI Enrich API Contract\n\nThe wire format this CLI relies on. Anything beyond what is documented here is FlashRev internal and may change without notice.\n\n## Base URL & auth\n\n| Item | Value |\n|---|---|\n| Base URL | `https://open-ai-api.flashlabs.ai` |\n| Auth header | `X-API-Key: <private app key>` |\n| Key issuance | https://info.flashlabs.ai/settings/privateApps |\n| Path prefix | All CLI-facing routes share the `/flashrev/...` prefix |\n\nThe API key is exchanged for an authenticated session by the FlashRev gateway. The CLI never sees or handles internal tokens.\n\n## Endpoints used by CLI\n\n### 1. Token balance — `GET /flashrev/api/v2/oauth/me`\n\nResponse:\n```json\n{\n  \"code\": 200,\n  \"data\": {\n    \"companyId\": 1000001,\n    \"newCreditFlag\": \"Y\",\n    \"limit\": {\n      \"tokenTotal\": 1121000,\n      \"tokenCost\": 917206.5,\n      \"tokenCategoryRemain\": { \"SUBSCRIPTION\": 0, \"GIFT\": 0, \"ADDON\": 289974 }\n    },\n    \"vip\": { \"packageName\": \"...\", \"...\": \"...\" }\n  }\n}\n```\n\nCLI computes `remaining = tokenTotal - tokenCost`.\n\n### 2. Token history — `POST /flashrev/api/v2/commodity/token/transaction/list`\n\nRequest body:\n```json\n{ \"page\": 1, \"pageSize\": 100, \"transactionType\": 2 }\n```\n\n`transactionType: 2` = consumption (1 = top-up).\n\nResponse:\n```json\n{\n  \"code\": 200,\n  \"data\": {\n    \"list\": [\n      { \"createdAt\": \"2026-05-29 06:59:56\", \"featId\": \"unlock_contact\",\n        \"featName\": \"Verify Email Address\", \"tokenAmount\": 1, \"unit\": \"Run\",\n        \"quantity\": 1, \"transactionType\": 2 }\n    ],\n    \"total\": 100, \"page\": 1, \"pageSize\": 100\n  }\n}\n```\n\nThe endpoint does not currently accept date filters; the CLI paginates and filters locally by `createdAt`. CLI cap: 20 pages × 100 = 2000 records.\n\n### 3. Capability registry — `GET /flashrev/api/v1/enrich/configs`\n\nReturns the active capability list with pricing and shape.\n\n```json\n{\n  \"code\": 200,\n  \"data\": [\n    {\n      \"funcName\": \"enrich_email\",\n      \"displayName\": \"Enrich Person -> Get Emails\",\n      \"featId\": \"unlock_contact\",\n      \"unitPriceToken\": 2,\n      \"concurrency\": 10,\n      \"inputColumn\": [\n        { \"key\": \"first_name\", \"name\": \"First Name\" },\n        { \"key\": \"last_name\",  \"name\": \"Last Name\" }\n      ],\n      \"outputColumn\": [\"verified_business_email\", \"all_verified_business_emails\", \"...\"],\n      \"rules\": [\n        [\"first_name\", \"last_name\", \"company_name\"],\n        [\"person_linkedin_url\"],\n        [\"email\"]\n      ]\n    }\n  ]\n}\n```\n\n### 4. Enrich — `POST /flashrev/api/v1/enrich/run`\n\nPer-row enrichment, synchronous.\n\nRequest body (snake_case on the wire):\n```json\n{\n  \"func_name\": \"enrich_email\",\n  \"input\": {\n    \"first_name\": \"Ada\",\n    \"last_name\": \"Lovelace\",\n    \"company_name\": \"Acme\"\n  },\n  \"row_id\": \"row-42\"\n}\n```\n\n`row_id` is optional and used by the CLI for tracing only.\n\nResponse (success):\n```json\n{\n  \"code\": 200,\n  \"msg\": \"OK\",\n  \"data\": {\n    \"code\": 200,\n    \"msg\": \"Successful\",\n    \"data\": {\n      \"verified_business_email\": \"ada@acme.com\",\n      \"all_verified_business_emails\": [\"ada@acme.com\"],\n      \"verified_personal_email\": \"\"\n    },\n    \"cost\": { \"tokens\": 2, \"cached\": false }\n  }\n}\n```\n\nThe response uses a nested `data` wrapper; the CLI's `normalizeEnrichResponse` handles both flat and nested shapes.\n\n`cost.tokens` may be slightly inaccurate under high concurrency; the values returned by `token/transaction/list` are always exact.\n\n### 5. Error codes\n\nHTTP-level (gateway / transport):\n\n| Code | Meaning |\n|---|---|\n| 401 | Invalid or revoked `X-API-Key` |\n| 402 | Insufficient tokens |\n| 429 | Rate limit exceeded (CLI auto-retries with exponential backoff) |\n| 503 | Service temporarily unavailable |\n| 504 | Upstream timeout |\n\nBusiness-level (HTTP 200 but inner `code != 200`):\n\n| Inner code | Meaning |\n|---|---|\n| 200 + `data` populated | Real enrichment, charged at `unitPriceToken` |\n| 200 + `data` empty / requested fields blank | No data for this lead; CLI marks `no_data`, not charged |\n| 422 | Input validation failed (e.g., missing required input combo) |\n| 4xx other | Request rejected |\n\n## Deduction semantics\n\n- **Pre-check**: The balance is checked before any downstream call. Insufficient → `402` immediately, no charge.\n- **Rate limit**: A per-`funcName` quota is enforced server-side. Overflow → `429`; the CLI retries with backoff.\n- **Charge on success only**: A row is billed only when the response carries real business data. Empty / 4xx / 5xx responses are not billed.\n- **Dedup**: Contact-unlock capabilities (`enrich_email`, `enrich_phone`) consult an unlock cache. Repeat unlocks of the same person return cached data at 0 tokens (`cost.cached: true`).\n\n## customer_api\n\n`customer_api` is a special capability whose backend route is empty. The CLI fetches the user-provided URL locally and parses the response. Token cost is always 0. Use it to mix third-party data sources into the same enrichment workflow.\n\n### Security guardrails\n\nBecause `customer_api` issues HTTP requests from the user's machine with row-derived URL / headers / body, it is the only capability that can both reach **internal infrastructure** and **exfiltrate CSV lead data to a third-party endpoint**. The CLI applies a hard guardrail before any network IO:\n\n- **Scheme allowlist** — only `http://` and `https://` are accepted. `file://`, `gopher://`, `data:`, `javascript:` etc. fail with HTTP 400.\n- **Host blocklist (default ON)** — the URL is rejected (HTTP 403) when the hostname is `localhost` / `0.0.0.0`, or an IP literal inside `127.0.0.0/8`, `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`, `169.254.0.0/16` (link-local, incl. AWS/GCP/Azure metadata `169.254.169.254`), `0.0.0.0/8`, multicast/reserved `224.0.0.0/4`, IPv6 `::`, `::1`, `fe80::/10`, `fc00::/7`, or IPv4-mapped IPv6 (`::ffff:a.b.c.d`) where the embedded v4 hits any of the above. Pass `--allow-internal-targets` per-run to bypass — intended for deliberate local testing only.\n- **Residual risk** — the CLI does not resolve DNS, so a public hostname that later resolves to a private IP (DNS rebinding) is **not** caught here. Operators running this CLI in sensitive environments should pin egress via OS firewall / network ACLs in addition to this guardrail.\n\nThe guardrail addresses SSRF + cloud-metadata exfiltration. Lead-data exfiltration via an attacker-controlled **public** URL is *not* blocked by code — that decision is policy. The skill documentation directs agents to never auto-fill `url` from prompt text, never map credentials or unrelated PII into `headers` / `body`, and to require human confirmation of the destination domain before a live `run`.\n\nFile v1.3.0:skill-card.md\n\n## Description:\n\nUse this skill when an AI agent needs to enrich a CSV lead list through the flashrev-ai-enrich npm CLI.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[flashlabs-ai](https://clawhub.ai/user/flashlabs-ai)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and business operations teams use this skill to guide agents through schema-first FlashRev CSV enrichment for lead, company, person, contact, search, scrape, and row-level LLM workflows. It emphasizes live capability discovery, explicit mapping, dry-run validation, sample review, and token or credit spend reporting before full runs.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Contact enrichment can unlock paid lead data and consume tokens or credits.\n\nMitigation: Run token checks, dry-runs, and sample previews before live enrichment, and continue only after the user approves the spend and reviewed sample.\n\nRisk: The FLASHREV_API_KEY could be exposed through files, logs, generated artifacts, or mapped request fields.\n\nMitigation: Keep FLASHREV_API_KEY in the environment, never print it, and never map API keys, OAuth tokens, passwords, cookies, or unrelated PII into customer_api headers or body.\n\nRisk: The customer_api capability can send lead data to third-party URLs and can be used for internal-network requests.\n\nMitigation: Use customer_api only after confirming the destination domain and mapped fields; require separate explicit approval before --allow-internal-targets.\n\nRisk: DNS rebinding or unrestricted outbound access may bypass documented host guardrails in sensitive corporate or cloud environments.\n\nMitigation: Restrict outbound network access with operating-system firewall rules or network ACLs when running this skill in sensitive environments.\n\n## Reference(s):\n\n- [FlashRev AI Enrich API Contract](references/api_contract.md)\n- [FlashRev AI Enrich on ClawHub](https://clawhub.ai/flashlabs-ai/skills/flashrev-ai-enrich)\n- [FlashRev Private App Key Settings](https://info.flashlabs.ai/settings/privateApps)\n\n## Skill Output:\n\n**Output Type(s):** [Guidance, Shell commands, Configuration, Markdown, JSON]\n\n**Output Format:** [Markdown guidance with shell commands and JSON report handling]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Guides agents to produce enriched CSV outputs through the FlashRev CLI, with dry-run, sample preview, status counts, row errors, and token or credit spend reporting.]\n\n## Skill Version(s):\n\n1.3.0 (source: server release metadata)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nFile v1.3.0:agents/openai.yaml\n\ndisplay_name: FlashRev AI Enrich\nshort_description: Enrich FlashRev CSV lead lists through the FlashRev CLI with schema-first capability selection.\ndefault_prompt: >\n  Use flashrev-ai-enrich in AI mode. Run doctor --no-api, schema --json,\n  tokens --json, plan --json for broad jobs, and dry-run --json before live\n  enrichment. Select only capabilities returned by the live schema and use\n  explicit --capability, not --prompt, unless the user explicitly asks for\n  prompt routing. Treat plan --json as read-only unless --emit-jobs is approved.\n  For contact-only planning, use --contact-type email|phone|both. Before full\n  live runs, use run --sample-only --json --yes after the user approves sample\n  token spend, show the returned sample, then continue only after approval. In\n  AI mode, run stdout is the structured JSON report and progress is stderr.\n  Stop on approvalReasons such as contact cleartext unlock, customer_api egress,\n  --allow-internal-targets, output overwrite, and high-volume runs. Use\n  --overwrite only after explicit approval.\n\nArchive v1.1.0: 5 files, 13065 bytes\n\nFiles: agents/openai.yaml (779b), references/api_contract.md (6592b), skill-card.md (2859b), SKILL.md (19213b), _meta.json (137b)\n\nFile v1.1.0:SKILL.md\n\n---\nname: flashrev-ai-enrich\ndescription: Use this skill when an AI agent needs to enrich a CSV lead list through the flashrev-ai-enrich npm CLI. Triggers on list enrichment, filling missing company or person fields, verifying or unlocking emails and phones, finding CEOs, executives, LinkedIn posts, matching companies or people to FlashRev IDs, Google search/news/maps lookups, scraping a page, or running an LLM over each row. Agents must run with `FLASHREV_ENRICH_AI_MODE=1`, call `schema --json`, use only live `funcName` values, and invoke each command with explicit `--capability FUNC_NAME --map ... --output ...`. For broad person enrich requests, run a profile + contact pipeline when supported; for contact-only requests, run only the requested contact capability. Avoid `--prompt` unless explicitly requested. Dry-run and sample preview are required before live runs unless already authorized.\n---\n\n# FlashRev AI Enrich\n\nUse the `flashrev-ai-enrich` CLI to enrich CSV lead lists through FlashRev. The CLI does not send outreach messages. It reads CSV files, maps CSV columns to FlashRev capability inputs, validates the job with dry-run, previews enriched sample rows, then writes an enriched CSV.\n\n## Commands\n\n```\nflashrev-ai-enrich init [--force]                              Write default config\nflashrev-ai-enrich doctor [--no-api]                            Self-check Node / config / API\nflashrev-ai-enrich tokens [--json]                              Show balance / total / used / plan\nflashrev-ai-enrich token-history [--from YYYY-MM-DD] [--to YYYY-MM-DD] [--limit N] [--json]\n                                                                Show consumption log (auto-paginates)\nflashrev-ai-enrich schema [--json]                              List production-backed capabilities (synced from backend at runtime)\nflashrev-ai-enrich dry-run  --source leads.csv (--capability ID | --prompt \"...\") [--map ...] [--output ...]\n                                                                Validate job and show run plan without calling backend\nflashrev-ai-enrich run      --source leads.csv --out X.csv (--capability ID | --prompt \"...\") [--yes] [--concurrency N] [--sample-size N]\n                                                                Real enrichment with sample preview. --prompt routes to a funcName via run_llm (1 extra token)\n```\n\n## Agent Execution Contract\n\nAgent runtimes should treat this CLI as a schema-first structured tool, not a natural-language router.\n\nUse this order for every non-trivial enrichment job:\n\n1. Set `FLASHREV_ENRICH_AI_MODE=1`.\n2. Run `flashrev-ai-enrich doctor --no-api`.\n3. Run `flashrev-ai-enrich schema --json`.\n4. Select one or more `funcName` values returned by the live schema. Each CLI command still runs exactly one `--capability`.\n5. Run `flashrev-ai-enrich tokens --json`.\n6. Build explicit `--map` and `--output` flags from the CSV headers and the selected schema for each selected capability.\n7. Run `flashrev-ai-enrich dry-run` for each selected capability before that capability's live run.\n8. Ask for approval unless the user already authorized `--yes`.\n9. Run `flashrev-ai-enrich run`, chaining each capability from the previous output CSV when multiple capabilities are needed.\n10. Report the final output path, per-capability status counts, row errors, and actual token spend from the CLI summary and token history.\n\nRules for agents:\n\n- Do not use `--prompt` by default. Use it only when the user explicitly requests prompt routing or does not want to choose a capability.\n- Never invent capability IDs, input fields, or output fields. Use only the live `schema --json` response.\n- Prefer explicit `--capability ID` even when the user's request is written in natural language.\n- If a requested capability is missing from the live schema, say it is unavailable in the connected environment instead of guessing a hidden backend route.\n- For a broad request such as \"enrich this CSV\" or \"complete these people\", enrich both person profile fields and contact fields when the CSV has a person identifier.\n- If the request needs `customer_api`, confirm the destination domain before live `run`.\n- If the request needs `--allow-internal-targets`, get separate explicit approval before using that flag.\n\n## Required confirmations before real `run`\n\n1. User has a FlashRev account with available tokens (`flashrev-ai-enrich tokens` → `remaining > 0`).\n2. `FLASHREV_API_KEY` env var is set (generated from https://info.flashlabs.ai/settings/privateApps).\n3. Source CSV path and output CSV path are confirmed.\n4. `--capability ID` from live `flashrev-ai-enrich schema --json` is confirmed. Use `--prompt \"<intent>\"` only when the user explicitly asks for prompt routing.\n5. Input mappings (`--map flashrev_field=csv_column`) cover at least one capability rule. Skipped only when `--prompt` is explicitly used and the LLM returns valid mappings (still subject to rule validation afterwards).\n6. Output mappings (`--output csv_col=response_field`) or `--output-fields` are confirmed. Skipped only when `--prompt` is explicitly used and the LLM returned mappings, but always required for dynamic-output capabilities (e.g., `run_llm`, `scrape_and_extract`).\n7. `dry-run` first to validate mappings, row count, planned API calls, and effective concurrency.\n8. Do not proceed past the sample preview (default 10 rows, configurable via `--sample-size N`) unless the user approves or `--yes` is set.\n9. `customer_api` and `--allow-internal-targets` each require separate explicit approval.\n\n## Input modes\n\n### A. CSV mode (typical batch)\n\n```bash\nflashrev-ai-enrich run \\\n  --source leads.csv --out leads.enriched.csv \\\n  --capability enrich_email \\\n  --map first_name=first_name --map last_name=last_name --map company_name=company \\\n  --output verified_email=verified_business_email \\\n  --yes\n```\n\n`--map` connects CSV column → capability input field; `--output` connects CSV output column → backend response field.\n\n### B. Inline mode (single row test, no CSV)\n\n```bash\nflashrev-ai-enrich run \\\n  --capability verify_email \\\n  --input email=ada@example.com \\\n  --output ok=deliverable_email \\\n  --out out.csv --yes\n```\n\nIn inline mode the `--input key=value` pairs are auto-mapped (no need for `--map`).\n\n### C. Job file (for repeatable presets)\n\n```bash\nflashrev-ai-enrich run --source leads.csv --out out.csv --job enrich.job.json --yes\n```\n\nJob file shape:\n```json\n{\n  \"capability\": \"enrich_email\",\n  \"inputMapping\": {\n    \"first_name\":  \"first_name\",\n    \"last_name\":   \"last_name\",\n    \"company_name\": \"company\"\n  },\n  \"outputs\": {\n    \"verified_business_email\":  \"verified_business_email\",\n    \"all_verified_business_emails\": \"all_verified_business_emails\"\n  }\n}\n```\n\n## Default person enrichment routing\n\nWhen the user asks broadly to \"enrich\" a people CSV, do not stop at contact lookup. Treat the default outcome as person profile plus contact data when the CSV has a usable person identifier.\n\nUse this decision table:\n\n| User intent | Default capabilities |\n|---|---|\n| Broad person enrich, complete people, enrich this CSV | `enrich_contact_waterfall` + `get_person_job_title` + `get_person_current_company` + `get_person_location` |\n| Contact info, emails and phones | `enrich_contact_waterfall` only |\n| Emails only | `enrich_email` only |\n| Phones or mobiles only | `enrich_phone` only |\n| Profile only, no contacts requested | `get_person_job_title` + `get_person_current_company` + `get_person_location` |\n\nApply these rules:\n\n- Use only capabilities exposed by the live `schema --json` response. If one capability in the default pipeline is missing, skip that step and report it.\n- Prefer `person_linkedin_url` / `linkedin_url` inputs when the CSV has a LinkedIn column. Otherwise use the strongest identity group available from the live schema.\n- Chain outputs through intermediate CSV files and make the final file the last step's `--out`.\n- Preserve earlier run results by giving each follow-up run distinct status and error columns, for example `job_title_enrich_status`, `current_company_enrich_status`, and `location_enrich_status`.\n- Keep `flashrev_enrich_status` for the first/main contact run unless the user asks for custom status columns.\n\n## Contact enrichment routing\n\nUse explicit contact capabilities from the live schema:\n\n| User intent | Capability |\n|---|---|\n| Emails only | `enrich_email` |\n| Phones or mobiles only | `enrich_phone` |\n| Emails and phones | `enrich_contact_waterfall`, only when live schema exposes it |\n| Emails and phones, no waterfall in schema | Ask before running `enrich_email` and `enrich_phone` sequentially |\n\n`enrich_email` and `enrich_phone` are still valid single-type contact capabilities. They use the backend contact lookup path for the requested contact type, so agents should keep using them for single-type requests instead of forcing `enrich_contact_waterfall`.\n\nDo not require users to provide `flashrev_person_id`; normal users usually do not have it. Use the strongest available identity input:\n\n| Input strength | Accepted inputs |\n|---|---|\n| Best | `person_linkedin_url` |\n| Strong | `email`, `phone`, `flashrev_person_id` |\n| Common | `full_name + company_name`, `first_name + last_name + company_name` |\n| Better common | Name plus company plus `job_title` |\n| Company role search | `company_name + job_title`, optionally with `company_website` or `company_linkedin` |\n| Lowest confidence | `job_title` only; use only when the user accepts top-one selection |\n\nIf none of those identity inputs are available, ask the user for a LinkedIn URL, email, phone, name plus company, or company plus job title before running contact enrichment.\n\n### D. Prompt routing mode (ad-hoc human use; costs 1 extra token)\n\nSkip `--capability` and describe the intent in natural language. The CLI sends the prompt + CSV columns + capability registry to `run_llm`, which returns JSON `{ funcName, inputMapping, outputMapping, reasoning }`; the CLI prints a Routing-decision block and then runs the resulting job through the normal dry-run / sample / run pipeline.\n\n```bash\nflashrev-ai-enrich run --source leads.csv --out leads.enriched.csv \\\n  --prompt \"for each row, take the email column and verify it is a deliverable business email\" \\\n  --yes\n```\n\nRules of thumb when writing prompts:\n\n- Name the CSV column explicitly (\"take the **email** column\"); vague prompts make the LLM return empty mappings.\n- Describe the business outcome, not the capability name (\"find the CEO\" beats \"use get_company_ceo\").\n- One capability per prompt — the LLM picks exactly one funcName.\n- `--map` / `--output` on the command line override the LLM's choices; use them to lock specific columns while letting the LLM pick the capability.\n- `--capability X --prompt \"...\"` together: `--capability` wins, `--prompt` is ignored with a stderr warning (no routing token charged).\n- Unroutable prompts (e.g., \"make me a sandwich\") exit non-zero with the LLM's reasoning printed; zero rows run.\n\nAgents must skip prompt routing unless the user explicitly requests it. `schema --json` plus explicit `--capability ID` is cheaper, faster, and deterministic.\n\nFor contact lookup, agents should use the contact routing table above and pass explicit `--capability`, never prompt routing.\n\n## Status semantics (output CSV columns)\n\nEvery output CSV gets `flashrev_enrich_status` and `flashrev_enrich_error` columns:\n\n| status | meaning |\n|---|---|\n| `success` | Got business data; charged per capability `unitPriceToken`. |\n| `cached` | Hit contact-unlock dedup (same person/contact type already unlocked). 0 tokens. |\n| `no_data` | Backend returned 200 but the requested output fields are empty / null. 0 tokens. |\n| `failed` | HTTP error from backend, retries exhausted. 0 tokens. |\n\n`Failed` count > 0 with `Tokens used` > 0 means some rows got SOMETHING from backend (charged) but not the specific output fields the user asked for.\n\n## Cost reporting\n\n`Summary` line in `run` output prints `(balance before → balance after)` — that delta is the **authoritative** amount charged for the row enrichments. Each row's individual `cost.tokens` reported by backend may be slightly off under high concurrency (known limitation; `token-history` is always exact).\n\nWhen `--prompt` is used, the Routing-decision block prints its own `routing cost: 1 token(s)` line. That 1 token is **not** included in the `Summary` `balance before → after` delta, since routing happens before the balance snapshot. Use `token-history` for the authoritative total after the run.\n\n## Special capability: `customer_api`\n\n`customer_api` does NOT call FlashRev backend — the CLI fetches the user-provided URL locally and parses the response. 0 tokens.\n\nInputs (via `--map <field>=<csv_col>` or `--input <field>=<value>`):\n\n| field | required | default | notes |\n|---|---|---|---|\n| `url` | yes | — | target URL (alias: `endpoint`) |\n| `method` | no | `GET` | HTTP method |\n| `headers` | no | `{}` | JSON object of HTTP headers |\n| `body` | no | — | string (sent as-is) or object (JSON-serialized; Content-Type defaults to application/json) |\n| `params` | no | — | object of query-string params; appended to `url` |\n| `timeout` | no | `30000` | milliseconds before AbortError |\n\nThe response JSON (or `{ text }` wrapper for non-JSON) becomes the row's enrichment data; map output columns via `--output csv_col=response_field` as usual. Useful for mixing 3rd-party APIs into the same enrichment workflow.\n\n### Security warnings (read before using `customer_api` in an agent context)\n\n`customer_api` lets the CLI send arbitrary HTTP requests with row-derived URL / headers / body. The target URL is **not** owned by FlashRev — it is whatever the user, prompt, or CSV column supplied. This creates two real risk surfaces an agent must mitigate:\n\n1. **SSRF / internal-network probing.** A URL such as `http://127.0.0.1:8500/`, `http://169.254.169.254/latest/meta-data/iam/security-credentials/` (AWS/GCP/Azure cloud-metadata), or any RFC1918 address (`10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`) can be used to reach internal services or exfiltrate cloud IAM credentials. The CLI **rejects these targets by default** (HTTP 403 `customer_api refuses internal / private target host`) along with `localhost`, IPv6 loopback `::1`, link-local `fe80::/10`, ULA `fc00::/7`, and non-`http(s)` schemes (`file://`, `gopher://`, `data:`, `javascript:`). Pass `--allow-internal-targets` only for deliberate local testing on a trusted machine.\n2. **Lead-data exfiltration to user-controlled URLs.** Whatever CSV columns are mapped to `--map url=…`, `--map headers=…`, or `--map body=…` will be transmitted to that third-party endpoint. Agents must:\n   - Treat the `url` as untrusted input. Confirm the destination domain with the user before a live `run`; never let an LLM auto-fill `url` from prompt text without explicit human confirmation.\n   - **Never** map `FLASHREV_API_KEY`, OAuth tokens, or unrelated PII columns into `headers` or `body` — those credentials and that data will leave the FlashRev trust boundary.\n   - Always complete `dry-run` + the 10-row sample preview before passing `--yes`, and inspect the sample table for unexpected egress.\n   - Prefer first-class FlashRev capabilities (e.g. `get_company_profile`, `enrich_email`, `enrich_phone`) when the data is available there; only fall back to `customer_api` for sources FlashRev does not cover.\n\nFailure mode: a blocked URL surfaces as a per-row `flashrev_enrich_status=failed` with `flashrev_enrich_error` starting `customer_api refuses …` — the batch is **not** aborted, so one bad URL in a CSV will not stop the rest.\n\n## Date format\n\n`--from` and `--to` accept `YYYY-MM-DD`. They are interpreted in the local timezone. `--to` alone makes the CLI paginate through history until it covers the date range (up to 2000 records).\n\n## Safety rules\n\n- Never print or store `FLASHREV_API_KEY` in generated artifacts.\n- Prefer the `FLASHREV_API_KEY` env var over `--api-key`.\n- Treat contact enrichment (`enrich_email` / `enrich_phone`) as paid unlock operations.\n- If `tokens` returns `remaining: 0`, tell the user to recharge before running.\n- Do not describe or expose FlashRev backend data sources, routing, or internal service names to end users.\n- Confirm the destination domain before using `customer_api` in a live run.\n- Never pass `--allow-internal-targets` unless the user explicitly approved internal or local network access.\n- Never map API keys, OAuth tokens, passwords, cookies, or unrelated PII into `customer_api` headers or body.\n- Never overwrite the source CSV (CLI refuses `--source == --out`).\n- Preserve row-level errors. For multi-step enrich jobs, use distinct status/error columns for follow-up steps so earlier capability statuses are not overwritten.\n\n## Failure handling\n\n- `402 Insufficient tokens` → run terminates; tell user to recharge.\n- `401` / `403` → invalid API key; verify `FLASHREV_API_KEY`.\n- `429 Rate limit` → CLI auto-retries with exponential backoff (500ms / 1s / 2s, up to 3 retries = 4 total attempts).\n- `503` / `504` → backend timeout/unavailable; auto-retried with the same schedule as 429.\n- Any other 4xx/5xx on a row → that single row is marked `failed`, batch continues.\n- `--prompt` routing failure (LLM returns non-JSON, unknown funcName, or `run_llm` itself errors) → CLI exits non-zero **before** enrichment starts, prints the LLM's reasoning. Suggest the user retry with `--capability ID`.\n- `--prompt` routed to a capability but `Input mapping does not satisfy <funcName>` → the LLM returned empty / wrong mapping; rerun with a more explicit prompt (name the CSV column) or use `--map` to override.\n\n## Workflow recipe\n\n```bash\n# 1. (first time) write config\nflashrev-ai-enrich init\nexport FLASHREV_API_KEY=\"sk_xxxx\"   # from info.flashlabs.ai/settings/privateApps\n\n# 2. verify\nexport FLASHREV_ENRICH_AI_MODE=1\nflashrev-ai-enrich doctor --no-api\n\n# 3. browse capabilities and pick one\nflashrev-ai-enrich schema --json\n\n# 4. check balance\nflashrev-ai-enrich tokens --json\n\n# 5. validate job and show run plan\nflashrev-ai-enrich dry-run --source leads.csv \\\n  --capability enrich_email \\\n  --map first_name=first_name --map last_name=last_name --map company_name=company\n\n# 6. real run with sample preview\nflashrev-ai-enrich run --source leads.csv --out out.csv \\\n  --capability enrich_email \\\n  --map first_name=first_name --map last_name=last_name --map company_name=company \\\n  --output verified_email=verified_business_email\n# (preview shown, type 'y' to continue, or pass --yes to auto-confirm)\n\n# 7. audit spend\nflashrev-ai-enrich token-history --from 2026-05-01\n```\n\n### Shortcut for ad-hoc human use (prompt routing)\n\nWhen the user does not know the capability name and is willing to spend 1 extra token to let the LLM pick:\n\n```bash\n# dry-run only routes (1 token) — no enrichment\nflashrev-ai-enrich dry-run --source leads.csv \\\n  --prompt \"find the CEO of each company\"\n\n# real run: 1 routing token + N rows\nflashrev-ai-enrich run --source leads.csv --out out.csv \\\n  --prompt \"find the CEO of each company\" --yes\n```\n\nAgents should skip this and pass `--capability` directly.\n\nFile v1.1.0:_meta.json\n\n{\n  \"ownerId\": \"kn728zt3jt22zg85rat5nantrd86d3dy\",\n  \"slug\": \"flashrev-ai-enrich\",\n  \"version\": \"1.1.0\",\n  \"publishedAt\": 1784123789783\n}\n\nFile v1.1.0:references/api_contract.md\n\n# FlashRev AI Enrich API Contract\n\nThe wire format this CLI relies on. Anything beyond what is documented here is FlashRev internal and may change without notice.\n\n## Base URL & auth\n\n| Item | Value |\n|---|---|\n| Base URL | `https://open-ai-api.flashlabs.ai` |\n| Auth header | `X-API-Key: <private app key>` |\n| Key issuance | https://info.flashlabs.ai/settings/privateApps |\n| Path prefix | All CLI-facing routes share the `/flashrev/...` prefix |\n\nThe API key is exchanged for an authenticated session by the FlashRev gateway. The CLI never sees or handles internal tokens.\n\n## Endpoints used by CLI\n\n### 1. Token balance — `GET /flashrev/api/v2/oauth/me`\n\nResponse:\n```json\n{\n  \"code\": 200,\n  \"data\": {\n    \"companyId\": 1000001,\n    \"newCreditFlag\": \"Y\",\n    \"limit\": {\n      \"tokenTotal\": 1121000,\n      \"tokenCost\": 917206.5,\n      \"tokenCategoryRemain\": { \"SUBSCRIPTION\": 0, \"GIFT\": 0, \"ADDON\": 289974 }\n    },\n    \"vip\": { \"packageName\": \"...\", \"...\": \"...\" }\n  }\n}\n```\n\nCLI computes `remaining = tokenTotal - tokenCost`.\n\n### 2. Token history — `POST /flashrev/api/v2/commodity/token/transaction/list`\n\nRequest body:\n```json\n{ \"page\": 1, \"pageSize\": 100, \"transactionType\": 2 }\n```\n\n`transactionType: 2` = consumption (1 = top-up).\n\nResponse:\n```json\n{\n  \"code\": 200,\n  \"data\": {\n    \"list\": [\n      { \"createdAt\": \"2026-05-29 06:59:56\", \"featId\": \"unlock_contact\",\n        \"featName\": \"Verify Email Address\", \"tokenAmount\": 1, \"unit\": \"Run\",\n        \"quantity\": 1, \"transactionType\": 2 }\n    ],\n    \"total\": 100, \"page\": 1, \"pageSize\": 100\n  }\n}\n```\n\nThe endpoint does not currently accept date filters; the CLI paginates and filters locally by `createdAt`. CLI cap: 20 pages × 100 = 2000 records.\n\n### 3. Capability registry — `GET /flashrev/api/v1/enrich/configs`\n\nReturns the active capability list with pricing and shape.\n\n```json\n{\n  \"code\": 200,\n  \"data\": [\n    {\n      \"funcName\": \"enrich_email\",\n      \"displayName\": \"Enrich Person -> Get Emails\",\n      \"featId\": \"unlock_contact\",\n      \"unitPriceToken\": 2,\n      \"concurrency\": 10,\n      \"inputColumn\": [\n        { \"key\": \"first_name\", \"name\": \"First Name\" },\n        { \"key\": \"last_name\",  \"name\": \"Last Name\" }\n      ],\n      \"outputColumn\": [\"verified_business_email\", \"all_verified_business_emails\", \"...\"],\n      \"rules\": [\n        [\"first_name\", \"last_name\", \"company_name\"],\n        [\"person_linkedin_url\"],\n        [\"email\"]\n      ]\n    }\n  ]\n}\n```\n\n### 4. Enrich — `POST /flashrev/api/v1/enrich/run`\n\nPer-row enrichment, synchronous.\n\nRequest body (snake_case on the wire):\n```json\n{\n  \"func_name\": \"enrich_email\",\n  \"input\": {\n    \"first_name\": \"Ada\",\n    \"last_name\": \"Lovelace\",\n    \"company_name\": \"Acme\"\n  },\n  \"row_id\": \"row-42\"\n}\n```\n\n`row_id` is optional and used by the CLI for tracing only.\n\nResponse (success):\n```json\n{\n  \"code\": 200,\n  \"msg\": \"OK\",\n  \"data\": {\n    \"code\": 200,\n    \"msg\": \"Successful\",\n    \"data\": {\n      \"verified_business_email\": \"ada@acme.com\",\n      \"all_verified_business_emails\": [\"ada@acme.com\"],\n      \"verified_personal_email\": \"\"\n    },\n    \"cost\": { \"tokens\": 2, \"cached\": false }\n  }\n}\n```\n\nThe response uses a nested `data` wrapper; the CLI's `normalizeEnrichResponse` handles both flat and nested shapes.\n\n`cost.tokens` may be slightly inaccurate under high concurrency; the values returned by `token/transaction/list` are always exact.\n\n### 5. Error codes\n\nHTTP-level (gateway / transport):\n\n| Code | Meaning |\n|---|---|\n| 401 | Invalid or revoked `X-API-Key` |\n| 402 | Insufficient tokens |\n| 429 | Rate limit exceeded (CLI auto-retries with exponential backoff) |\n| 503 | Service temporarily unavailable |\n| 504 | Upstream timeout |\n\nBusiness-level (HTTP 200 but inner `code != 200`):\n\n| Inner code | Meaning |\n|---|---|\n| 200 + `data` populated | Real enrichment, charged at `unitPriceToken` |\n| 200 + `data` empty / requested fields blank | No data for this lead; CLI marks `no_data`, not charged |\n| 422 | Input validation failed (e.g., missing required input combo) |\n| 4xx other | Request rejected |\n\n## Deduction semantics\n\n- **Pre-check**: The balance is checked before any downstream call. Insufficient → `402` immediately, no charge.\n- **Rate limit**: A per-`funcName` quota is enforced server-side. Overflow → `429`; the CLI retries with backoff.\n- **Charge on success only**: A row is billed only when the response carries real business data. Empty / 4xx / 5xx responses are not billed.\n- **Dedup**: Contact-unlock capabilities (`enrich_email`, `enrich_phone`) consult an unlock cache. Repeat unlocks of the same person return cached data at 0 tokens (`cost.cached: true`).\n\n## customer_api\n\n`customer_api` is a special capability whose backend route is empty. The CLI fetches the user-provided URL locally and parses the response. Token cost is always 0. Use it to mix third-party data sources into the same enrichment workflow.\n\n### Security guardrails\n\nBecause `customer_api` issues HTTP requests from the user's machine with row-derived URL / headers / body, it is the only capability that can both reach **internal infrastructure** and **exfiltrate CSV lead data to a third-party endpoint**. The CLI applies a hard guardrail before any network IO:\n\n- **Scheme allowlist** — only `http://` and `https://` are accepted. `file://`, `gopher://`, `data:`, `javascript:` etc. fail with HTTP 400.\n- **Host blocklist (default ON)** — the URL is rejected (HTTP 403) when the hostname is `localhost` / `0.0.0.0`, or an IP literal inside `127.0.0.0/8`, `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`, `169.254.0.0/16` (link-local, incl. AWS/GCP/Azure metadata `169.254.169.254`), `0.0.0.0/8`, multicast/reserved `224.0.0.0/4`, IPv6 `::`, `::1`, `fe80::/10`, `fc00::/7`, or IPv4-mapped IPv6 (`::ffff:a.b.c.d`) where the embedded v4 hits any of the above. Pass `--allow-internal-targets` per-run to bypass — intended for deliberate local testing only.\n- **Residual risk** — the CLI does not resolve DNS, so a public hostname that later resolves to a private IP (DNS rebinding) is **not** caught here. Operators running this CLI in sensitive environments should pin egress via OS firewall / network ACLs in addition to this guardrail.\n\nThe guardrail addresses SSRF + cloud-metadata exfiltration. Lead-data exfiltration via an attacker-controlled **public** URL is *not* blocked by code — that decision is policy. The skill documentation directs agents to never auto-fill `url` from prompt text, never map credentials or unrelated PII into `headers` / `body`, and to require human confirmation of the destination domain before a live `run`.\n\nFile v1.1.0:skill-card.md\n\n## Description: <br>\nFlashRev AI Enrich guides agents through schema-first CSV lead enrichment with the flashrev-ai-enrich CLI, including dry-runs, sample previews, live capability selection, and result reporting. <br>\n\nThis skill is ready for commercial/non-commercial use. <br>\n\n## Publisher: <br>\n[flashlabs-ai](https://clawhub.ai/user/flashlabs-ai) <br>\n\n### License/Terms of Use: <br>\nMIT-0 <br>\n\n\n## Use Case: <br>\nDevelopers and operators use this skill to enrich CSV lead lists with company, person, contact, search, scrape, or row-level LLM outputs while preserving approval, cost, and data-destination checks. <br>\n\n### Deployment Geography for Use: <br>\nGlobal <br>\n\n## Known Risks and Mitigations: <br>\nRisk: Selected CSV lead data may be sent to FlashRev or explicitly approved third-party endpoints. <br>\nMitigation: Confirm source and output files, mapped columns, token cost, and destination domain before live runs. <br>\nRisk: The customer_api capability can send row-derived requests to user-controlled URLs. <br>\nMitigation: Require destination-domain confirmation, complete dry-run and sample preview first, and never map API keys, cookies, passwords, OAuth tokens, or unrelated personal data into headers or bodies. <br>\nRisk: Internal-network access through customer_api could expose local services or cloud metadata if bypass controls are approved. <br>\nMitigation: Keep internal target blocking enabled by default and use --allow-internal-targets only with separate explicit approval for deliberate trusted local testing. <br>\nRisk: Contact enrichment and other live capabilities can consume paid tokens. <br>\nMitigation: Check token balance, run dry-run, require sample-preview approval, and report final spend from the CLI summary and token history. <br>\n\n\n## Reference(s): <br>\n- [FlashRev AI Enrich API Contract](artifact/references/api_contract.md) <br>\n- [FlashRev private app settings](https://info.flashlabs.ai/settings/privateApps) <br>\n- [ClawHub skill page](https://clawhub.ai/flashlabs-ai/skills/flashrev-ai-enrich) <br>\n\n\n## Skill Output: <br>\n**Output Type(s):** [text, markdown, shell commands, configuration, guidance] <br>\n**Output Format:** [Markdown guidance with inline shell commands and JSON/YAML configuration examples] <br>\n**Output Parameters:** [1D] <br>\n**Other Properties Related to Output:** [Produces explicit CLI commands, mapping guidance, output file paths, status counts, row errors, and token-spend summaries; it does not send outreach messages.] <br>\n\n## Skill Version(s): <br>\n1.1.0 (source: server release evidence) <br>\n\n## Ethical Considerations: <br>\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment. <br>\n\nFile v1.1.0:agents/openai.yaml\n\ndisplay_name: FlashRev AI Enrich\nshort_description: Enrich FlashRev CSV lead lists through the FlashRev CLI with schema-first capability selection.\ndefault_prompt: >\n  Use flashrev-ai-enrich in AI mode. Run doctor --no-api, schema --json,\n  tokens --json, and dry-run before live enrichment. Select only capabilities\n  returned by the live schema and use explicit --capability, not --prompt,\n  unless the user explicitly asks for prompt routing. For broad person enrich\n  requests, chain profile and contact capabilities instead of only looking up\n  contacts, and keep separate status/error columns for each follow-up step. Ask\n  for approval before any live run unless the user already authorized --yes.\n  Require separate approval for customer_api or --allow-internal-targets.\n\nArchive v1.0.2: 5 files, 11005 bytes\n\nFiles: agents/openai.yaml (226b), references/api_contract.md (6592b), skill-card.md (2508b), SKILL.md (14115b), _meta.json (137b)\n\nFile v1.0.2:SKILL.md\n\n---\nname: flashrev-ai-enrich\ndescription: Use this skill when an AI agent needs to enrich a CSV lead list through the flashrev-ai-enrich npm CLI (v1.0+). Triggers on requests involving list enrichment, filling missing company/person fields, verifying emails or phones, unlocking contact emails or phone numbers, finding company CEOs / executives / industry / employees / LinkedIn posts, matching companies or people to FlashRev IDs, Google search / news / maps lookups, scraping a single page, or running an LLM over each row. The CLI is a structured tool — agents should call `flashrev-ai-enrich schema` to discover the 34 capabilities, then invoke `run` with `--capability <funcName> --map ...` directly; `--prompt \"...\"` exists for ad-hoc human users and costs 1 extra token per invocation. All enrichment decisions and token deductions are owned by the FlashRev backend; the CLI never calls external data providers directly except for the special `customer_api` capability. Dry-run estimates and the 10-row sample preview must be completed before live runs unless the user passed `--yes`. Agents should invoke with `FLASHREV_ENRICH_AI_MODE=1` (or `--ai-mode`) so list outputs (`tokens` / `schema` / `token-history`) and error envelopes are JSON-structured.\n---\n\n# FlashRev AI Enrich\n\nUse the `flashrev-ai-enrich` CLI to enrich CSV lead lists through FlashRev. The CLI does not send outreach messages. It reads CSV files, maps CSV columns to FlashRev capability inputs, estimates token cost via dry-run, previews enriched sample rows, then writes an enriched CSV.\n\n## Commands\n\n```\nflashrev-ai-enrich init [--force]                              Write default config\nflashrev-ai-enrich doctor [--no-api]                            Self-check Node / config / API\nflashrev-ai-enrich tokens [--json]                              Show balance / total / used / plan\nflashrev-ai-enrich token-history [--from YYYY-MM-DD] [--to YYYY-MM-DD] [--limit N] [--json]\n                                                                Show consumption log (auto-paginates)\nflashrev-ai-enrich schema [--json]                              List 34 capabilities (synced from backend at runtime)\nflashrev-ai-enrich dry-run  --source leads.csv (--capability ID | --prompt \"...\") [--map ...] [--output ...]\n                                                                Estimate without calling backend\nflashrev-ai-enrich run      --source leads.csv --out X.csv (--capability ID | --prompt \"...\") [--yes] [--concurrency N] [--sample-size N]\n                                                                Real enrichment with sample preview. --prompt routes to a funcName via run_llm (1 extra token)\n```\n\n## Required confirmations before real `run`\n\n1. User has a FlashRev account with available tokens (`flashrev-ai-enrich tokens` → `remaining > 0`).\n2. `FLASHREV_API_KEY` env var is set (generated from https://info.flashlabs.ai/settings/privateApps).\n3. Source CSV path and output CSV path are confirmed.\n4. Either `--capability ID` (from `flashrev-ai-enrich schema`) or `--prompt \"<intent>\"` is confirmed. Agents should prefer `--capability ID` directly; `--prompt` is for ad-hoc human use because it costs 1 extra token to route through `run_llm`.\n5. Input mappings (`--map flashrev_field=csv_column`) cover at least one capability rule. Skipped only when `--prompt` is used and the LLM returns valid mappings (still subject to rule validation afterwards).\n6. Output mappings (`--output csv_col=response_field`) or `--output-fields` are confirmed. Skipped under `--prompt` if the LLM returned mappings, but always required for dynamic-output capabilities (e.g., `run_llm`, `scrape_and_extract`).\n7. `dry-run` first to see estimated token cost and effective concurrency.\n8. Do not proceed past the sample preview (default 10 rows, configurable via `--sample-size N`) unless the user approves or `--yes` is set.\n\n## Input modes\n\n### A. CSV mode (typical batch)\n\n```bash\nflashrev-ai-enrich run \\\n  --source leads.csv --out leads.enriched.csv \\\n  --capability enrich_email \\\n  --map first_name=first_name --map last_name=last_name --map company_name=company \\\n  --output verified_email=verified_business_email \\\n  --yes\n```\n\n`--map` connects CSV column → capability input field; `--output` connects CSV output column → backend response field.\n\n### B. Inline mode (single row test, no CSV)\n\n```bash\nflashrev-ai-enrich run \\\n  --capability verify_email \\\n  --input email=ada@example.com \\\n  --output ok=deliverable_email \\\n  --out out.csv --yes\n```\n\nIn inline mode the `--input key=value` pairs are auto-mapped (no need for `--map`).\n\n### C. Job file (for repeatable presets)\n\n```bash\nflashrev-ai-enrich run --source leads.csv --out out.csv --job enrich.job.json --yes\n```\n\nJob file shape:\n```json\n{\n  \"capability\": \"enrich_email\",\n  \"inputMapping\": {\n    \"first_name\":  \"first_name\",\n    \"last_name\":   \"last_name\",\n    \"company_name\": \"company\"\n  },\n  \"outputs\": {\n    \"verified_business_email\":  \"verified_business_email\",\n    \"all_verified_business_emails\": \"all_verified_business_emails\"\n  }\n}\n```\n\n### D. Prompt routing mode (ad-hoc human use; costs 1 extra token)\n\nSkip `--capability` and describe the intent in natural language. The CLI sends the prompt + CSV columns + capability registry to `run_llm`, which returns JSON `{ funcName, inputMapping, outputMapping, reasoning }`; the CLI prints a Routing-decision block and then runs the resulting job through the normal dry-run / sample / run pipeline.\n\n```bash\nflashrev-ai-enrich run --source leads.csv --out leads.enriched.csv \\\n  --prompt \"for each row, take the email column and verify it is a deliverable business email\" \\\n  --yes\n```\n\nRules of thumb when writing prompts:\n\n- Name the CSV column explicitly (\"take the **email** column\"); vague prompts make the LLM return empty mappings.\n- Describe the business outcome, not the capability name (\"find the CEO\" beats \"use get_company_ceo\").\n- One capability per prompt — the LLM picks exactly one funcName.\n- `--map` / `--output` on the command line override the LLM's choices; use them to lock specific columns while letting the LLM pick the capability.\n- `--capability X --prompt \"...\"` together: `--capability` wins, `--prompt` is ignored with a stderr warning (no routing token charged).\n- Unroutable prompts (e.g., \"make me a sandwich\") exit non-zero with the LLM's reasoning printed; zero rows run.\n\nAgents calling this CLI should usually skip prompt routing entirely — `schema` + explicit `--capability ID` is cheaper, faster, and deterministic. Prompt routing is for humans at a terminal.\n\n## Status semantics (output CSV columns)\n\nEvery output CSV gets `flashrev_enrich_status` and `flashrev_enrich_error` columns:\n\n| status | meaning |\n|---|---|\n| `success` | Got business data; charged per capability `unitPriceToken`. |\n| `cached` | Hit `unlock_contact` dedup (same `person_id` already unlocked). 0 tokens. |\n| `no_data` | Backend returned 200 but the requested output fields are empty / null. 0 tokens. |\n| `failed` | HTTP error from backend, retries exhausted. 0 tokens. |\n\n`Failed` count > 0 with `Tokens used` > 0 means some rows got SOMETHING from backend (charged) but not the specific output fields the user asked for.\n\n## Cost reporting\n\n`Summary` line in `run` output prints `(balance before → balance after)` — that delta is the **authoritative** amount charged for the row enrichments. Each row's individual `cost.tokens` reported by backend may be slightly off under high concurrency (known limitation; `token-history` is always exact).\n\nWhen `--prompt` is used, the Routing-decision block prints its own `routing cost: 1 token(s)` line. That 1 token is **not** included in the `Summary` `balance before → after` delta, since routing happens before the balance snapshot. Total user cost per `--prompt` run = 1 routing token + (rows × capability unitPriceToken).\n\n## Special capability: `customer_api`\n\n`customer_api` does NOT call FlashRev backend — the CLI fetches the user-provided URL locally and parses the response. 0 tokens.\n\nInputs (via `--map <field>=<csv_col>` or `--input <field>=<value>`):\n\n| field | required | default | notes |\n|---|---|---|---|\n| `url` | yes | — | target URL (alias: `endpoint`) |\n| `method` | no | `GET` | HTTP method |\n| `headers` | no | `{}` | JSON object of HTTP headers |\n| `body` | no | — | string (sent as-is) or object (JSON-serialized; Content-Type defaults to application/json) |\n| `params` | no | — | object of query-string params; appended to `url` |\n| `timeout` | no | `30000` | milliseconds before AbortError |\n\nThe response JSON (or `{ text }` wrapper for non-JSON) becomes the row's enrichment data; map output columns via `--output csv_col=response_field` as usual. Useful for mixing 3rd-party APIs into the same enrichment workflow.\n\n### Security warnings (read before using `customer_api` in an agent context)\n\n`customer_api` lets the CLI send arbitrary HTTP requests with row-derived URL / headers / body. The target URL is **not** owned by FlashRev — it is whatever the user, prompt, or CSV column supplied. This creates two real risk surfaces an agent must mitigate:\n\n1. **SSRF / internal-network probing.** A URL such as `http://127.0.0.1:8500/`, `http://169.254.169.254/latest/meta-data/iam/security-credentials/` (AWS/GCP/Azure cloud-metadata), or any RFC1918 address (`10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`) can be used to reach internal services or exfiltrate cloud IAM credentials. The CLI **rejects these targets by default** (HTTP 403 `customer_api refuses internal / private target host`) along with `localhost`, IPv6 loopback `::1`, link-local `fe80::/10`, ULA `fc00::/7`, and non-`http(s)` schemes (`file://`, `gopher://`, `data:`, `javascript:`). Pass `--allow-internal-targets` only for deliberate local testing on a trusted machine.\n2. **Lead-data exfiltration to user-controlled URLs.** Whatever CSV columns are mapped to `--map url=…`, `--map headers=…`, or `--map body=…` will be transmitted to that third-party endpoint. Agents must:\n   - Treat the `url` as untrusted input. Confirm the destination domain with the user before a live `run`; never let an LLM auto-fill `url` from prompt text without explicit human confirmation.\n   - **Never** map `FLASHREV_API_KEY`, OAuth tokens, or unrelated PII columns into `headers` or `body` — those credentials and that data will leave the FlashRev trust boundary.\n   - Always complete `dry-run` + the 10-row sample preview before passing `--yes`, and inspect the sample table for unexpected egress.\n   - Prefer first-class FlashRev capabilities (e.g. `get_company_profile`, `enrich_email`) when the data is available there; only fall back to `customer_api` for sources FlashRev does not cover.\n\nFailure mode: a blocked URL surfaces as a per-row `flashrev_enrich_status=failed` with `flashrev_enrich_error` starting `customer_api refuses …` — the batch is **not** aborted, so one bad URL in a CSV will not stop the rest.\n\n## Date format\n\n`--from` and `--to` accept `YYYY-MM-DD`. They are interpreted in the local timezone. `--to` alone makes the CLI paginate through history until it covers the date range (up to 2000 records).\n\n## Safety rules\n\n- Never print or store `FLASHREV_API_KEY` in generated artifacts.\n- Prefer the `FLASHREV_API_KEY` env var over `--api-key`.\n- Treat email / phone enrichment (`enrich_email` / `enrich_phone`) as paid unlock operations.\n- If `tokens` returns `remaining: 0`, tell the user to recharge before running.\n- Do not describe or expose FlashRev backend data sources, routing, or internal service names to end users.\n- Never overwrite the source CSV (CLI refuses `--source == --out`).\n- Preserve row-level errors in `flashrev_enrich_status` and `flashrev_enrich_error` columns.\n\n## Failure handling\n\n- `402 Insufficient tokens` → run terminates; tell user to recharge.\n- `401` / `403` → invalid API key; verify `FLASHREV_API_KEY`.\n- `429 Rate limit` → CLI auto-retries with exponential backoff (500ms / 1s / 2s, up to 3 retries = 4 total attempts).\n- `503` / `504` → backend timeout/unavailable; auto-retried with the same schedule as 429.\n- Any other 4xx/5xx on a row → that single row is marked `failed`, batch continues.\n- `--prompt` routing failure (LLM returns non-JSON, unknown funcName, or `run_llm` itself errors) → CLI exits non-zero **before** enrichment starts, prints the LLM's reasoning. Suggest the user retry with `--capability ID`.\n- `--prompt` routed to a capability but `Input mapping does not satisfy <funcName>` → the LLM returned empty / wrong mapping; rerun with a more explicit prompt (name the CSV column) or use `--map` to override.\n\n## Workflow recipe\n\n```bash\n# 1. (first time) write config\nflashrev-ai-enrich init\nexport FLASHREV_API_KEY=\"sk_xxxx\"   # from info.flashlabs.ai/settings/privateApps\n\n# 2. verify\nflashrev-ai-enrich doctor\n\n# 3. browse capabilities and pick one\nflashrev-ai-enrich schema | less\n\n# 4. (optional) check balance\nflashrev-ai-enrich tokens\n\n# 5. estimate cost\nflashrev-ai-enrich dry-run --source leads.csv \\\n  --capability enrich_email \\\n  --map first_name=first_name --map last_name=last_name --map company_name=company\n\n# 6. real run with sample preview\nflashrev-ai-enrich run --source leads.csv --out out.csv \\\n  --capability enrich_email \\\n  --map first_name=first_name --map last_name=last_name --map company_name=company \\\n  --output verified_email=verified_business_email\n# (preview shown, type 'y' to continue, or pass --yes to auto-confirm)\n\n# 7. audit spend\nflashrev-ai-enrich token-history --from 2026-05-01\n```\n\n### Shortcut for ad-hoc human use (prompt routing)\n\nWhen the user does not know the capability name and is willing to spend 1 extra token to let the LLM pick:\n\n```bash\n# dry-run only routes (1 token) — no enrichment\nflashrev-ai-enrich dry-run --source leads.csv \\\n  --prompt \"find the CEO of each company\"\n\n# real run: 1 routing token + N rows\nflashrev-ai-enrich run --source leads.csv --out out.csv \\\n  --prompt \"find the CEO of each company\" --yes\n```\n\nAgents should skip this and pass `--capability` directly.\n\nFile v1.0.2:_meta.json\n\n{\n  \"ownerId\": \"kn728zt3jt22zg85rat5nantrd86d3dy\",\n  \"slug\": \"flashrev-ai-enrich\",\n  \"version\": \"1.0.2\",\n  \"publishedAt\": 1780113286231\n}\n\nFile v1.0.2:references/api_contract.md\n\n# FlashRev AI Enrich API Contract\n\nThe wire format this CLI relies on. Anything beyond what is documented here is FlashRev internal and may change without notice.\n\n## Base URL & auth\n\n| Item | Value |\n|---|---|\n| Base URL | `https://open-ai-api.flashlabs.ai` |\n| Auth header | `X-API-Key: <private app key>` |\n| Key issuance | https://info.flashlabs.ai/settings/privateApps |\n| Path prefix | All CLI-facing routes share the `/flashrev/...` prefix |\n\nThe API key is exchanged for an authenticated session by the FlashRev gateway. The CLI never sees or handles internal tokens.\n\n## Endpoints used by CLI\n\n### 1. Token balance — `GET /flashrev/api/v2/oauth/me`\n\nResponse:\n```json\n{\n  \"code\": 200,\n  \"data\": {\n    \"companyId\": 1000001,\n    \"newCreditFlag\": \"Y\",\n    \"limit\": {\n      \"tokenTotal\": 1121000,\n      \"tokenCost\": 917206.5,\n      \"tokenCategoryRemain\": { \"SUBSCRIPTION\": 0, \"GIFT\": 0, \"ADDON\": 289974 }\n    },\n    \"vip\": { \"packageName\": \"...\", \"...\": \"...\" }\n  }\n}\n```\n\nCLI computes `remaining = tokenTotal - tokenCost`.\n\n### 2. Token history — `POST /flashrev/api/v2/commodity/token/transaction/list`\n\nRequest body:\n```json\n{ \"page\": 1, \"pageSize\": 100, \"transactionType\": 2 }\n```\n\n`transactionType: 2` = consumption (1 = top-up).\n\nResponse:\n```json\n{\n  \"code\": 200,\n  \"data\": {\n    \"list\": [\n      { \"createdAt\": \"2026-05-29 06:59:56\", \"featId\": \"unlock_contact\",\n        \"featName\": \"Verify Email Address\", \"tokenAmount\": 1, \"unit\": \"Run\",\n        \"quantity\": 1, \"transactionType\": 2 }\n    ],\n    \"total\": 100, \"page\": 1, \"pageSize\": 100\n  }\n}\n```\n\nThe endpoint does not currently accept date filters; the CLI paginates and filters locally by `createdAt`. CLI cap: 20 pages × 100 = 2000 records.\n\n### 3. Capability registry — `GET /flashrev/api/v1/enrich/configs`\n\nReturns the active capability list with pricing and shape.\n\n```json\n{\n  \"code\": 200,\n  \"data\": [\n    {\n      \"funcName\": \"enrich_email\",\n      \"displayName\": \"Enrich Person -> Get Emails\",\n      \"featId\": \"unlock_contact\",\n      \"unitPriceToken\": 2,\n      \"concurrency\": 10,\n      \"inputColumn\": [\n        { \"key\": \"first_name\", \"name\": \"First Name\" },\n        { \"key\": \"last_name\",  \"name\": \"Last Name\" }\n      ],\n      \"outputColumn\": [\"verified_business_email\", \"all_verified_business_emails\", \"...\"],\n      \"rules\": [\n        [\"first_name\", \"last_name\", \"company_name\"],\n        [\"person_linkedin_url\"],\n        [\"email\"]\n      ]\n    }\n  ]\n}\n```\n\n### 4. Enrich — `POST /flashrev/api/v1/enrich/run`\n\nPer-row enrichment, synchronous.\n\nRequest body (snake_case on the wire):\n```json\n{\n  \"func_name\": \"enrich_email\",\n  \"input\": {\n    \"first_name\": \"Ada\",\n    \"last_name\": \"Lovelace\",\n    \"company_name\": \"Acme\"\n  },\n  \"row_id\": \"row-42\"\n}\n```\n\n`row_id` is optional and used by the CLI for tracing only.\n\nResponse (success):\n```json\n{\n  \"code\": 200,\n  \"msg\": \"OK\",\n  \"data\": {\n    \"code\": 200,\n    \"msg\": \"Successful\",\n    \"data\": {\n      \"verified_business_email\": \"ada@acme.com\",\n      \"all_verified_business_emails\": [\"ada@acme.com\"],\n      \"verified_personal_email\": \"\"\n    },\n    \"cost\": { \"tokens\": 2, \"cached\": false }\n  }\n}\n```\n\nThe response uses a nested `data` wrapper; the CLI's `normalizeEnrichResponse` handles both flat and nested shapes.\n\n`cost.tokens` may be slightly inaccurate under high concurrency; the values returned by `token/transaction/list` are always exact.\n\n### 5. Error codes\n\nHTTP-level (gateway / transport):\n\n| Code | Meaning |\n|---|---|\n| 401 | Invalid or revoked `X-API-Key` |\n| 402 | Insufficient tokens |\n| 429 | Rate limit exceeded (CLI auto-retries with exponential backoff) |\n| 503 | Service temporarily unavailable |\n| 504 | Upstream timeout |\n\nBusiness-level (HTTP 200 but inner `code != 200`):\n\n| Inner code | Meaning |\n|---|---|\n| 200 + `data` populated | Real enrichment, charged at `unitPriceToken` |\n| 200 + `data` empty / requested fields blank | No data for this lead; CLI marks `no_data`, not charged |\n| 422 | Input validation failed (e.g., missing required input combo) |\n| 4xx other | Request rejected |\n\n## Deduction semantics\n\n- **Pre-check**: The balance is checked before any downstream call. Insufficient → `402` immediately, no charge.\n- **Rate limit**: A per-`funcName` quota is enforced server-side. Overflow → `429`; the CLI retries with backoff.\n- **Charge on success only**: A row is billed only when the response carries real business data. Empty / 4xx / 5xx responses are not billed.\n- **Dedup**: Contact-unlock capabilities (`enrich_email`, `enrich_phone`) consult an unlock cache. Repeat unlocks of the same person return cached data at 0 tokens (`cost.cached: true`).\n\n## customer_api\n\n`customer_api` is a special capability whose backend route is empty. The CLI fetches the user-provided URL locally and parses the response. Token cost is always 0. Use it to mix third-party data sources into the same enrichment workflow.\n\n### Security guardrails\n\nBecause `customer_api` issues HTTP requests from the user's machine with row-derived URL / headers / body, it is the only capability that can both reach **internal infrastructure** and **exfiltrate CSV lead data to a third-party endpoint**. The CLI applies a hard guardrail before any network IO:\n\n- **Scheme allowlist** — only `http://` and `https://` are accepted. `file://`, `gopher://`, `data:`, `javascript:` etc. fail with HTTP 400.\n- **Host blocklist (default ON)** — the URL is rejected (HTTP 403) when the hostname is `localhost` / `0.0.0.0`, or an IP literal inside `127.0.0.0/8`, `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`, `169.254.0.0/16` (link-local, incl. AWS/GCP/Azure metadata `169.254.169.254`), `0.0.0.0/8`, multicast/reserved `224.0.0.0/4`, IPv6 `::`, `::1`, `fe80::/10`, `fc00::/7`, or IPv4-mapped IPv6 (`::ffff:a.b.c.d`) where the embedded v4 hits any of the above. Pass `--allow-internal-targets` per-run to bypass — intended for deliberate local testing only.\n- **Residual risk** — the CLI does not resolve DNS, so a public hostname that later resolves to a private IP (DNS rebinding) is **not** caught here. Operators running this CLI in sensitive environments should pin egress via OS firewall / network ACLs in addition to this guardrail.\n\nThe guardrail addresses SSRF + cloud-metadata exfiltration. Lead-data exfiltration via an attacker-controlled **public** URL is *not* blocked by code — that decision is policy. The skill documentation directs agents to never auto-fill `url` from prompt text, never map credentials or unrelated PII into `headers` / `body`, and to require human confirmation of the destination domain before a live `run`.\n\nFile v1.0.2:skill-card.md\n\n## Description: <br>\nEnrich CSV lead lists through the flashrev-ai-enrich CLI with dry-run estimates, sample previews, token checks, and mapped outputs. <br>\n\nThis skill is ready for commercial/non-commercial use. <br>\n\n## Publisher: <br>\n[flashlabs-ai](https://clawhub.ai/user/flashlabs-ai) <br>\n\n### License/Terms of Use: <br>\nMIT-0 <br>\n\n\n## Use Case: <br>\nExternal users, sales operations teams, and agents use this skill to enrich CSV lead lists with FlashRev data while confirming credentials, token balance, input mappings, cost estimates, and sample output before live runs. <br>\n\n### Deployment Geography for Use: <br>\nGlobal <br>\n\n## Known Risks and Mitigations: <br>\nRisk: The CLI uses a sensitive FlashRev API key and processes CSV contact or business data. <br>\nMitigation: Use the FLASHREV_API_KEY environment variable, avoid printing or storing the key in generated artifacts, and confirm which CSV columns will be sent before live enrichment. <br>\nRisk: Paid enrichment can consume FlashRev tokens, especially when prompt routing or unlock capabilities are used. <br>\nMitigation: Check token balance, run dry-run estimates, review the sample preview, and avoid --yes unless the user has already approved the cost and output path. <br>\nRisk: The customer_api capability can send mapped CSV fields to user-selected third-party URLs. <br>\nMitigation: Confirm the destination domain with the user, avoid mapping credentials or unrelated PII into headers or bodies, and prefer first-party FlashRev capabilities when they cover the requested data. <br>\n\n\n## Reference(s): <br>\n- [FlashRev AI Enrich API Contract](references/api_contract.md) <br>\n- [ClawHub Skill Page](https://clawhub.ai/flashlabs-ai/flashrev-ai-enrich) <br>\n\n\n## Skill Output: <br>\n**Output Type(s):** [guidance, shell commands, configuration, text, CSV files] <br>\n**Output Format:** [Markdown guidance with CLI commands, JSON-capable status output, and enriched CSV output files] <br>\n**Output Parameters:** [1D] <br>\n**Other Properties Related to Output:** [Requires a FlashRev API key and available tokens; live runs should follow dry-run and sample-preview confirmation.] <br>\n\n## Skill Version(s): <br>\n1.0.2 (source: server release evidence) <br>\n\n## Ethical Considerations: <br>\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment. <br>\n\nFile v1.0.2:agents/openai.yaml\n\ndisplay_name: FlashRev AI Enrich\nshort_description: Enrich FlashRev CSV lead lists with dry-run estimates and sample previews.\ndefault_prompt: Run FlashRev AI Enrich on a CSV using a dry-run first, then enrich after approval.\n\nArchive v1.0.1: 5 files, 11206 bytes\n\nFiles: agents/openai.yaml (226b), references/api_contract.md (6592b), skill-card.md (2951b), SKILL.md (14115b), _meta.json (137b)\n\nFile v1.0.1:SKILL.md\n\n---\nname: flashrev-ai-enrich\ndescription: Use this skill when an AI agent needs to enrich a CSV lead list through the flashrev-ai-enrich npm CLI (v1.0+). Triggers on requests involving list enrichment, filling missing company/person fields, verifying emails or phones, unlocking contact emails or phone numbers, finding company CEOs / executives / industry / employees / LinkedIn posts, matching companies or people to FlashRev IDs, Google search / news / maps lookups, scraping a single page, or running an LLM over each row. The CLI is a structured tool — agents should call `flashrev-ai-enrich schema` to discover the 34 capabilities, then invoke `run` with `--capability <funcName> --map ...` directly; `--prompt \"...\"` exists for ad-hoc human users and costs 1 extra token per invocation. All enrichment decisions and token deductions are owned by the FlashRev backend; the CLI never calls external data providers directly except for the special `customer_api` capability. Dry-run estimates and the 10-row sample preview must be completed before live runs unless the user passed `--yes`. Agents should invoke with `FLASHREV_ENRICH_AI_MODE=1` (or `--ai-mode`) so list outputs (`tokens` / `schema` / `token-history`) and error envelopes are JSON-structured.\n---\n\n# FlashRev AI Enrich\n\nUse the `flashrev-ai-enrich` CLI to enrich CSV lead lists through FlashRev. The CLI does not send outreach messages. It reads CSV files, maps CSV columns to FlashRev capability inputs, estimates token cost via dry-run, previews enriched sample rows, then writes an enriched CSV.\n\n## Commands\n\n```\nflashrev-ai-enrich init [--force]                              Write default config\nflashrev-ai-enrich doctor [--no-api]                            Self-check Node / config / API\nflashrev-ai-enrich tokens [--json]                              Show balance / total / used / plan\nflashrev-ai-enrich token-history [--from YYYY-MM-DD] [--to YYYY-MM-DD] [--limit N] [--json]\n                                                                Show consumption log (auto-paginates)\nflashrev-ai-enrich schema [--json]                              List 34 capabilities (synced from backend at runtime)\nflashrev-ai-enrich dry-run  --source leads.csv (--capability ID | --prompt \"...\") [--map ...] [--output ...]\n                                                                Estimate without calling backend\nflashrev-ai-enrich run      --source leads.csv --out X.csv (--capability ID | --prompt \"...\") [--yes] [--concurrency N] [--sample-size N]\n                                                                Real enrichment with sample preview. --prompt routes to a funcName via run_llm (1 extra token)\n```\n\n## Required confirmations before real `run`\n\n1. User has a FlashRev account with available tokens (`flashrev-ai-enrich tokens` → `remaining > 0`).\n2. `FLASHREV_API_KEY` env var is set (generated from https://info.flashlabs.ai/settings/privateApps).\n3. Source CSV path and output CSV path are confirmed.\n4. Either `--capability ID` (from `flashrev-ai-enrich schema`) or `--prompt \"<intent>\"` is confirmed. Agents should prefer `--capability ID` directly; `--prompt` is for ad-hoc human use because it costs 1 extra token to route through `run_llm`.\n5. Input mappings (`--map flashrev_field=csv_column`) cover at least one capability rule. Skipped only when `--prompt` is used and the LLM returns valid mappings (still subject to rule validation afterwards).\n6. Output mappings (`--output csv_col=response_field`) or `--output-fields` are confirmed. Skipped under `--prompt` if the LLM returned mappings, but always required for dynamic-output capabilities (e.g., `run_llm`, `scrape_and_extract`).\n7. `dry-run` first to see estimated token cost and effective concurrency.\n8. Do not proceed past the sample preview (default 10 rows, configurable via `--sample-size N`) unless the user approves or `--yes` is set.\n\n## Input modes\n\n### A. CSV mode (typical batch)\n\n```bash\nflashrev-ai-enrich run \\\n  --source leads.csv --out leads.enriched.csv \\\n  --capability enrich_email \\\n  --map first_name=first_name --map last_name=last_name --map company_name=company \\\n  --output verified_email=verified_business_email \\\n  --yes\n```\n\n`--map` connects CSV column → capability input field; `--output` connects CSV output column → backend response field.\n\n### B. Inline mode (single row test, no CSV)\n\n```bash\nflashrev-ai-enrich run \\\n  --capability verify_email \\\n  --input email=ada@example.com \\\n  --output ok=deliverable_email \\\n  --out out.csv --yes\n```\n\nIn inline mode the `--input key=value` pairs are auto-mapped (no need for `--map`).\n\n### C. Job file (for repeatable presets)\n\n```bash\nflashrev-ai-enrich run --source leads.csv --out out.csv --job enrich.job.json --yes\n```\n\nJob file shape:\n```json\n{\n  \"capability\": \"enrich_email\",\n  \"inputMapping\": {\n    \"first_name\":  \"first_name\",\n    \"last_name\":   \"last_name\",\n    \"company_name\": \"company\"\n  },\n  \"outputs\": {\n    \"verified_business_email\":  \"verified_business_email\",\n    \"all_verified_business_emails\": \"all_verified_business_emails\"\n  }\n}\n```\n\n### D. Prompt routing mode (ad-hoc human use; costs 1 extra token)\n\nSkip `--capability` and describe the intent in natural language. The CLI sends the prompt + CSV columns + capability registry to `run_llm`, which returns JSON `{ funcName, inputMapping, outputMapping, reasoning }`; the CLI prints a Routing-decision block and then runs the resulting job through the normal dry-run / sample / run pipeline.\n\n```bash\nflashrev-ai-enrich run --source leads.csv --out leads.enriched.csv \\\n  --prompt \"for each row, take the email column and verify it is a deliverable business email\" \\\n  --yes\n```\n\nRules of thumb when writing prompts:\n\n- Name the CSV column explicitly (\"take the **email** column\"); vague prompts make the LLM return empty mappings.\n- Describe the business outcome, not the capability name (\"find the CEO\" beats \"use get_company_ceo\").\n- One capability per prompt — the LLM picks exactly one funcName.\n- `--map` / `--output` on the command line override the LLM's choices; use them to lock specific columns while letting the LLM pick the capability.\n- `--capability X --prompt \"...\"` together: `--capability` wins, `--prompt` is ignored with a stderr warning (no routing token charged).\n- Unroutable prompts (e.g., \"make me a sandwich\") exit non-zero with the LLM's reasoning printed; zero rows run.\n\nAgents calling this CLI should usually skip prompt routing entirely — `schema` + explicit `--capability ID` is cheaper, faster, and deterministic. Prompt routing is for humans at a terminal.\n\n## Status semantics (output CSV columns)\n\nEvery output CSV gets `flashrev_enrich_status` and `flashrev_enrich_error` columns:\n\n| status | meaning |\n|---|---|\n| `success` | Got business data; charged per capability `unitPriceToken`. |\n| `cached` | Hit `unlock_contact` dedup (same `person_id` already unlocked). 0 tokens. |\n| `no_data` | Backend returned 200 but the requested output fields are empty / null. 0 tokens. |\n| `failed` | HTTP error from backend, retries exhausted. 0 tokens. |\n\n`Failed` count > 0 with `Tokens used` > 0 means some rows got SOMETHING from backend (charged) but not the specific output fields the user asked for.\n\n## Cost reporting\n\n`Summary` line in `run` output prints `(balance before → balance after)` — that delta is the **authoritative** amount charged for the row enrichments. Each row's individual `cost.tokens` reported by backend may be slightly off under high concurrency (known limitation; `token-history` is always exact).\n\nWhen `--prompt` is used, the Routing-decision block prints its own `routing cost: 1 token(s)` line. That 1 token is **not** included in the `Summary` `balance before → after` delta, since routing happens before the balance snapshot. Total user cost per `--prompt` run = 1 routing token + (rows × capability unitPriceToken).\n\n## Special capability: `customer_api`\n\n`customer_api` does NOT call FlashRev backend — the CLI fetches the user-provided URL locally and parses the response. 0 tokens.\n\nInputs (via `--map <field>=<csv_col>` or `--input <field>=<value>`):\n\n| field | required | default | notes |\n|---|---|---|---|\n| `url` | yes | — | target URL (alias: `endpoint`) |\n| `method` | no | `GET` | HTTP method |\n| `headers` | no | `{}` | JSON object of HTTP headers |\n| `body` | no | — | string (sent as-is) or object (JSON-serialized; Content-Type defaults to application/json) |\n| `params` | no | — | object of query-string params; appended to `url` |\n| `timeout` | no | `30000` | milliseconds before AbortError |\n\nThe response JSON (or `{ text }` wrapper for non-JSON) becomes the row's enrichment data; map output columns via `--output csv_col=response_field` as usual. Useful for mixing 3rd-party APIs into the same enrichment workflow.\n\n### Security warnings (read before using `customer_api` in an agent context)\n\n`customer_api` lets the CLI send arbitrary HTTP requests with row-derived URL / headers / body. The target URL is **not** owned by FlashRev — it is whatever the user, prompt, or CSV column supplied. This creates two real risk surfaces an agent must mitigate:\n\n1. **SSRF / internal-network probing.** A URL such as `http://127.0.0.1:8500/`, `http://169.254.169.254/latest/meta-data/iam/security-credentials/` (AWS/GCP/Azure cloud-metadata), or any RFC1918 address (`10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`) can be used to reach internal services or exfiltrate cloud IAM credentials. The CLI **rejects these targets by default** (HTTP 403 `customer_api refuses internal / private target host`) along with `localhost`, IPv6 loopback `::1`, link-local `fe80::/10`, ULA `fc00::/7`, and non-`http(s)` schemes (`file://`, `gopher://`, `data:`, `javascript:`). Pass `--allow-internal-targets` only for deliberate local testing on a trusted machine.\n2. **Lead-data exfiltration to user-controlled URLs.** Whatever CSV columns are mapped to `--map url=…`, `--map headers=…`, or `--map body=…` will be transmitted to that third-party endpoint. Agents must:\n   - Treat the `url` as untrusted input. Confirm the destination domain with the user before a live `run`; never let an LLM auto-fill `url` from prompt text without explicit human confirmation.\n   - **Never** map `FLASHREV_API_KEY`, OAuth tokens, or unrelated PII columns into `headers` or `body` — those credentials and that data will leave the FlashRev trust boundary.\n   - Always complete `dry-run` + the 10-row sample preview before passing `--yes`, and inspect the sample table for unexpected egress.\n   - Prefer first-class FlashRev capabilities (e.g. `get_company_profile`, `enrich_email`) when the data is available there; only fall back to `customer_api` for sources FlashRev does not cover.\n\nFailure mode: a blocked URL surfaces as a per-row `flashrev_enrich_status=failed` with `flashrev_enrich_error` starting `customer_api refuses …` — the batch is **not** aborted, so one bad URL in a CSV will not stop the rest.\n\n## Date format\n\n`--from` and `--to` accept `YYYY-MM-DD`. They are interpreted in the local timezone. `--to` alone makes the CLI paginate through history until it covers the date range (up to 2000 records).\n\n## Safety rules\n\n- Never print or store `FLASHREV_API_KEY` in generated artifacts.\n- Prefer the `FLASHREV_API_KEY` env var over `--api-key`.\n- Treat email / phone enrichment (`enrich_email` / `enrich_phone`) as paid unlock operations.\n- If `tokens` returns `remaining: 0`, tell the user to recharge before running.\n- Do not describe or expose FlashRev backend data sources, routing, or internal service names to end users.\n- Never overwrite the source CSV (CLI refuses `--source == --out`).\n- Preserve row-level errors in `flashrev_enrich_status` and `flashrev_enrich_error` columns.\n\n## Failure handling\n\n- `402 Insufficient tokens` → run terminates; tell user to recharge.\n- `401` / `403` → invalid API key; verify `FLASHREV_API_KEY`.\n- `429 Rate limit` → CLI auto-retries with exponential backoff (500ms / 1s / 2s, up to 3 retries = 4 total attempts).\n- `503` / `504` → backend timeout/unavailable; auto-retried with the same schedule as 429.\n- Any other 4xx/5xx on a row → that single row is marked `failed`, batch continues.\n- `--prompt` routing failure (LLM returns non-JSON, unknown funcName, or `run_llm` itself errors) → CLI exits non-zero **before** enrichment starts, prints the LLM's reasoning. Suggest the user retry with `--capability ID`.\n- `--prompt` routed to a capability but `Input mapping does not satisfy <funcName>` → the LLM returned empty / wrong mapping; rerun with a more explicit prompt (name the CSV column) or use `--map` to override.\n\n## Workflow recipe\n\n```bash\n# 1. (first time) write config\nflashrev-ai-enrich init\nexport FLASHREV_API_KEY=\"sk_xxxx\"   # from info.flashlabs.ai/settings/privateApps\n\n# 2. verify\nflashrev-ai-enrich doctor\n\n# 3. browse capabilities and pick one\nflashrev-ai-enrich schema | less\n\n# 4. (optional) check balance\nflashrev-ai-enrich tokens\n\n# 5. estimate cost\nflashrev-ai-enrich dry-run --source leads.csv \\\n  --capability enrich_email \\\n  --map first_name=first_name --map last_name=last_name --map company_name=company\n\n# 6. real run with sample preview\nflashrev-ai-enrich run --source leads.csv --out out.csv \\\n  --capability enrich_email \\\n  --map first_name=first_name --map last_name=last_name --map company_name=company \\\n  --output verified_email=verified_business_email\n# (preview shown, type 'y' to continue, or pass --yes to auto-confirm)\n\n# 7. audit spend\nflashrev-ai-enrich token-history --from 2026-05-01\n```\n\n### Shortcut for ad-hoc human use (prompt routing)\n\nWhen the user does not know the capability name and is willing to spend 1 extra token to let the LLM pick:\n\n```bash\n# dry-run only routes (1 token) — no enrichment\nflashrev-ai-enrich dry-run --source leads.csv \\\n  --prompt \"find the CEO of each company\"\n\n# real run: 1 routing token + N rows\nflashrev-ai-enrich run --source leads.csv --out out.csv \\\n  --prompt \"find the CEO of each company\" --yes\n```\n\nAgents should skip this and pass `--capability` directly.\n\nFile v1.0.1:_meta.json\n\n{\n  \"ownerId\": \"kn728zt3jt22zg85rat5nantrd86d3dy\",\n  \"slug\": \"flashrev-ai-enrich\",\n  \"version\": \"1.0.1\",\n  \"publishedAt\": 1780111043669\n}\n\nFile v1.0.1:references/api_contract.md\n\n# FlashRev AI Enrich API Contract\n\nThe wire format this CLI relies on. Anything beyond what is documented here is FlashRev internal and may change without notice.\n\n## Base URL & auth\n\n| Item | Value |\n|---|---|\n| Base URL | `https://open-ai-api.flashlabs.ai` |\n| Auth header | `X-API-Key: <private app key>` |\n| Key issuance | https://info.flashlabs.ai/settings/privateApps |\n| Path prefix | All CLI-facing routes share the `/flashrev/...` prefix |\n\nThe API key is exchanged for an authenticated session by the FlashRev gateway. The CLI never sees or handles internal tokens.\n\n## Endpoints used by CLI\n\n### 1. Token balance — `GET /flashrev/api/v2/oauth/me`\n\nResponse:\n```json\n{\n  \"code\": 200,\n  \"data\": {\n    \"companyId\": 1000001,\n    \"newCreditFlag\": \"Y\",\n    \"limit\": {\n      \"tokenTotal\": 1121000,\n      \"tokenCost\": 917206.5,\n      \"tokenCategoryRemain\": { \"SUBSCRIPTION\": 0, \"GIFT\": 0, \"ADDON\": 289974 }\n    },\n    \"vip\": { \"packageName\": \"...\", \"...\": \"...\" }\n  }\n}\n```\n\nCLI computes `remaining = tokenTotal - tokenCost`.\n\n### 2. Token history — `POST /flashrev/api/v2/commodity/token/transaction/list`\n\nRequest body:\n```json\n{ \"page\": 1, \"pageSize\": 100, \"transactionType\": 2 }\n```\n\n`transactionType: 2` = consumption (1 = top-up).\n\nResponse:\n```json\n{\n  \"code\": 200,\n  \"data\": {\n    \"list\": [\n      { \"createdAt\": \"2026-05-29 06:59:56\", \"featId\": \"unlock_contact\",\n        \"featName\": \"Verify Email Address\", \"tokenAmount\": 1, \"unit\": \"Run\",\n        \"quantity\": 1, \"transactionType\": 2 }\n    ],\n    \"total\": 100, \"page\": 1, \"pageSize\": 100\n  }\n}\n```\n\nThe endpoint does not currently accept date filters; the CLI paginates and filters locally by `createdAt`. CLI cap: 20 pages × 100 = 2000 records.\n\n### 3. Capability registry — `GET /flashrev/api/v1/enrich/configs`\n\nReturns the active capability list with pricing and shape.\n\n```json\n{\n  \"code\": 200,\n  \"data\": [\n    {\n      \"funcName\": \"enrich_email\",\n      \"displayName\": \"Enrich Person -> Get Emails\",\n      \"featId\": \"unlock_contact\",\n      \"unitPriceToken\": 2,\n      \"concurrency\": 10,\n      \"inputColumn\": [\n        { \"key\": \"first_name\", \"name\": \"First Name\" },\n        { \"key\": \"last_name\",  \"name\": \"Last Name\" }\n      ],\n      \"outputColumn\": [\"verified_business_email\", \"all_verified_business_emails\", \"...\"],\n      \"rules\": [\n        [\"first_name\", \"last_name\", \"company_name\"],\n        [\"person_linkedin_url\"],\n        [\"email\"]\n      ]\n    }\n  ]\n}\n```\n\n### 4. Enrich — `POST /flashrev/api/v1/enrich/run`\n\nPer-row enrichment, synchronous.\n\nRequest body (snake_case on the wire):\n```json\n{\n  \"func_name\": \"enrich_email\",\n  \"input\": {\n    \"first_name\": \"Ada\",\n    \"last_name\": \"Lovelace\",\n    \"company_name\": \"Acme\"\n  },\n  \"row_id\": \"row-42\"\n}\n```\n\n`row_id` is optional and used by the CLI for tracing only.\n\nResponse (success):\n```json\n{\n  \"code\": 200,\n  \"msg\": \"OK\",\n  \"data\": {\n    \"code\": 200,\n    \"msg\": \"Successful\",\n    \"data\": {\n      \"verified_business_email\": \"ada@acme.com\",\n      \"all_verified_business_emails\": [\"ada@acme.com\"],\n      \"verified_personal_email\": \"\"\n    },\n    \"cost\": { \"tokens\": 2, \"cached\": false }\n  }\n}\n```\n\nThe response uses a nested `data` wrapper; the CLI's `normalizeEnrichResponse` handles both flat and nested shapes.\n\n`cost.tokens` may be slightly inaccurate under high concurrency; the values returned by `token/transaction/list` are always exact.\n\n### 5. Error codes\n\nHTTP-level (gateway / transport):\n\n| Code | Meaning |\n|---|---|\n| 401 | Invalid or revoked `X-API-Key` |\n| 402 | Insufficient tokens |\n| 429 | Rate limit exceeded (CLI auto-retries with exponential backoff) |\n| 503 | Service temporarily unavailable |\n| 504 | Upstream timeout |\n\nBusiness-level (HTTP 200 but inner `code != 200`):\n\n| Inner code | Meaning |\n|---|---|\n| 200 + `data` populated | Real enrichment, charged at `unitPriceToken` |\n| 200 + `data` empty / requested fields blank | No data for this lead; CLI marks `no_data`, not charged |\n| 422 | Input validation failed (e.g., missing required input combo) |\n| 4xx other | Request rejected |\n\n## Deduction semantics\n\n- **Pre-check**: The balance is checked before any downstream call. Insufficient → `402` immediately, no charge.\n- **Rate limit**: A per-`funcName` quota is enforced server-side. Overflow → `429`; the CLI retries with backoff.\n- **Charge on success only**: A row is billed only when the response carries real business data. Empty / 4xx / 5xx responses are not billed.\n- **Dedup**: Contact-unlock capabilities (`enrich_email`, `enrich_phone`) consult an unlock cache. Repeat unlocks of the same person return cached data at 0 tokens (`cost.cached: true`).\n\n## customer_api\n\n`customer_api` is a special capability whose backend route is empty. The CLI fetches the user-provided URL locally and parses the response. Token cost is always 0. Use it to mix third-party data sources into the same enrichment workflow.\n\n### Security guardrails\n\nBecause `customer_api` issues HTTP requests from the user's machine with row-derived URL / headers / body, it is the only capability that can both reach **internal infrastructure** and **exfiltrate CSV lead data to a third-party endpoint**. The CLI applies a hard guardrail before any network IO:\n\n- **Scheme allowlist** — only `http://` and `https://` are accepted. `file://`, `gopher://`, `data:`, `javascript:` etc. fail with HTTP 400.\n- **Host blocklist (default ON)** — the URL is rejected (HTTP 403) when the hostname is `localhost` / `0.0.0.0`, or an IP literal inside `127.0.0.0/8`, `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`, `169.254.0.0/16` (link-local, incl. AWS/GCP/Azure metadata `169.254.169.254`), `0.0.0.0/8`, multicast/reserved `224.0.0.0/4`, IPv6 `::`, `::1`, `fe80::/10`, `fc00::/7`, or IPv4-mapped IPv6 (`::ffff:a.b.c.d`) where the embedded v4 hits any of the above. Pass `--allow-internal-targets` per-run to bypass — intended for deliberate local testing only.\n- **Residual risk** — the CLI does not resolve DNS, so a public hostname that later resolves to a private IP (DNS rebinding) is **not** caught here. Operators running this CLI in sensitive environments should pin egress via OS firewall / network ACLs in addition to this guardrail.\n\nThe guardrail addresses SSRF + cloud-metadata exfiltration. Lead-data exfiltration via an attacker-controlled **public** URL is *not* blocked by code — that decision is policy. The skill documentation directs agents to never auto-fill `url` from prompt text, never map credentials or unrelated PII into `headers` / `body`, and to require human confirmation of the destination domain before a live `run`.\n\nFile v1.0.1:skill-card.md\n\n## Description: <br>\nFlashRev AI Enrich helps an agent enrich CSV lead lists through the flashrev-ai-enrich CLI, including capability discovery, token checks, dry-run estimates, sample previews, and enriched CSV output. <br>\n\nThis skill is ready for commercial/non-commercial use. <br>\n\n## Publisher: <br>\n[flashlabs-ai](https://clawhub.ai/user/flashlabs-ai) <br>\n\n### License/Terms of Use: <br>\nMIT-0 <br>\n\n\n## Use Case: <br>\nDevelopers, operations teams, and agents use this skill to enrich lead-list CSV files with FlashRev data while checking token balance, estimating spend, previewing sample rows, and preserving row-level status fields before a live run. <br>\n\n### Deployment Geography for Use: <br>\nGlobal <br>\n\n## Known Risks and Mitigations: <br>\nRisk: The skill requires sensitive FlashRev API credentials and may operate on lead-list data. <br>\nMitigation: Use FLASHREV_API_KEY from the environment, do not print or store the key in generated artifacts, and confirm source and output CSV paths before running. <br>\nRisk: Live enrichment can consume paid FlashRev tokens, especially for email or phone unlock operations. <br>\nMitigation: Check token balance, run dry-run estimates, review the sample preview, and avoid prompt routing unless the user accepts the extra routing token cost. <br>\nRisk: The customer_api capability can send row-derived URLs, headers, or bodies to third-party endpoints and may expose lead data. <br>\nMitigation: Confirm the destination domain with the user, avoid mapping credentials or unrelated PII into headers or body, and inspect the dry-run and sample preview before a live run. <br>\nRisk: customer_api can be misused for internal-network requests when internal target blocking is bypassed. <br>\nMitigation: Keep the default SSRF blocklist enabled and use --allow-internal-targets only for deliberate local testing on a trusted machine. <br>\n\n\n## Reference(s): <br>\n- [FlashRev AI Enrich API Contract](references/api_contract.md) <br>\n- [FlashRev Private App Settings](https://info.flashlabs.ai/settings/privateApps) <br>\n- [FlashRev AI Enrich on ClawHub](https://clawhub.ai/flashlabs-ai/flashrev-ai-enrich) <br>\n\n\n## Skill Output: <br>\n**Output Type(s):** [guidance, shell commands, configuration, markdown, code] <br>\n**Output Format:** [Markdown guidance with CLI commands, JSON job examples, and CSV output expectations] <br>\n**Output Parameters:** [1D] <br>\n**Other Properties Related to Output:** [The CLI produces enriched CSV files with flashrev_enrich_status and flashrev_enrich_error columns; AI-mode command outputs can be JSON-structured.] <br>\n\n## Skill Version(s): <br>\n1.0.1 (source: server release evidence) <br>\n\n## Ethical Considerations: <br>\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment. <br>\n\nFile v1.0.1:agents/openai.yaml\n\ndisplay_name: FlashRev AI Enrich\nshort_description: Enrich FlashRev CSV lead lists with dry-run estimates and sample previews.\ndefault_prompt: Run FlashRev AI Enrich on a CSV using a dry-run first, then enrich after approval.\n\nArchive v1.0.0: 5 files, 9072 bytes\n\nFiles: agents/openai.yaml (226b), references/api_contract.md (4880b), skill-card.md (2238b), SKILL.md (11942b), _meta.json (137b)\n\nFile v1.0.0:SKILL.md\n\n---\nname: flashrev-ai-enrich\ndescription: Use this skill when an AI agent needs to enrich a CSV lead list through the flashrev-ai-enrich npm CLI (v1.0+). Triggers on requests involving list enrichment, filling missing company/person fields, verifying emails or phones, unlocking contact emails or phone numbers, finding company CEOs / executives / industry / employees / LinkedIn posts, matching companies or people to FlashRev IDs, Google search / news / maps lookups, scraping a single page, or running an LLM over each row. The CLI is a structured tool — agents should call `flashrev-ai-enrich schema` to discover the 34 capabilities, then invoke `run` with `--capability <funcName> --map ...` directly; `--prompt \"...\"` exists for ad-hoc human users and costs 1 extra token per invocation. All enrichment decisions and token deductions are owned by the FlashRev backend; the CLI never calls external data providers directly except for the special `customer_api` capability. Dry-run estimates and the 10-row sample preview must be completed before live runs unless the user passed `--yes`. Agents should invoke with `FLASHREV_ENRICH_AI_MODE=1` (or `--ai-mode`) so list outputs (`tokens` / `schema` / `token-history`) and error envelopes are JSON-structured.\n---\n\n# FlashRev AI Enrich\n\nUse the `flashrev-ai-enrich` CLI to enrich CSV lead lists through FlashRev. The CLI does not send outreach messages. It reads CSV files, maps CSV columns to FlashRev capability inputs, estimates token cost via dry-run, previews enriched sample rows, then writes an enriched CSV.\n\n## Commands\n\n```\nflashrev-ai-enrich init [--force]                              Write default config\nflashrev-ai-enrich doctor [--no-api]                            Self-check Node / config / API\nflashrev-ai-enrich tokens [--json]                              Show balance / total / used / plan\nflashrev-ai-enrich token-history [--from YYYY-MM-DD] [--to YYYY-MM-DD] [--limit N] [--json]\n                                                                Show consumption log (auto-paginates)\nflashrev-ai-enrich schema [--json]                              List 34 capabilities (synced from backend at runtime)\nflashrev-ai-enrich dry-run  --source leads.csv (--capability ID | --prompt \"...\") [--map ...] [--output ...]\n                                                                Estimate without calling backend\nflashrev-ai-enrich run      --source leads.csv --out X.csv (--capability ID | --prompt \"...\") [--yes] [--concurrency N] [--sample-size N]\n                                                                Real enrichment with sample preview. --prompt routes to a funcName via run_llm (1 extra token)\n```\n\n## Required confirmations before real `run`\n\n1. User has a FlashRev account with available tokens (`flashrev-ai-enrich tokens` → `remaining > 0`).\n2. `FLASHREV_API_KEY` env var is set (generated from https://info.flashlabs.ai/settings/privateApps).\n3. Source CSV path and output CSV path are confirmed.\n4. Either `--capability ID` (from `flashrev-ai-enrich schema`) or `--prompt \"<intent>\"` is confirmed. Agents should prefer `--capability ID` directly; `--prompt` is for ad-hoc human use because it costs 1 extra token to route through `run_llm`.\n5. Input mappings (`--map flashrev_field=csv_column`) cover at least one capability rule. Skipped only when `--prompt` is used and the LLM returns valid mappings (still subject to rule validation afterwards).\n6. Output mappings (`--output csv_col=response_field`) or `--output-fields` are confirmed. Skipped under `--prompt` if the LLM returned mappings, but always required for dynamic-output capabilities (e.g., `run_llm`, `scrape_and_extract`).\n7. `dry-run` first to see estimated token cost and effective concurrency.\n8. Do not proceed past the sample preview (default 10 rows, configurable via `--sample-size N`) unless the user approves or `--yes` is set.\n\n## Input modes\n\n### A. CSV mode (typical batch)\n\n```bash\nflashrev-ai-enrich run \\\n  --source leads.csv --out leads.enriched.csv \\\n  --capability enrich_email \\\n  --map first_name=first_name --map last_name=last_name --map company_name=company \\\n  --output verified_email=verified_business_email \\\n  --yes\n```\n\n`--map` connects CSV column → capability input field; `--output` connects CSV output column → backend response field.\n\n### B. Inline mode (single row test, no CSV)\n\n```bash\nflashrev-ai-enrich run \\\n  --capability verify_email \\\n  --input email=ada@example.com \\\n  --output ok=deliverable_email \\\n  --out out.csv --yes\n```\n\nIn inline mode the `--input key=value` pairs are auto-mapped (no need for `--map`).\n\n### C. Job file (for repeatable presets)\n\n```bash\nflashrev-ai-enrich run --source leads.csv --out out.csv --job enrich.job.json --yes\n```\n\nJob file shape:\n```json\n{\n  \"capability\": \"enrich_email\",\n  \"inputMapping\": {\n    \"first_name\":  \"first_name\",\n    \"last_name\":   \"last_name\",\n    \"company_name\": \"company\"\n  },\n  \"outputs\": {\n    \"verified_business_email\":  \"verified_business_email\",\n    \"all_verified_business_emails\": \"all_verified_business_emails\"\n  }\n}\n```\n\n### D. Prompt routing mode (ad-hoc human use; costs 1 extra token)\n\nSkip `--capability` and describe the intent in natural language. The CLI sends the prompt + CSV columns + capability registry to `run_llm`, which returns JSON `{ funcName, inputMapping, outputMapping, reasoning }`; the CLI prints a Routing-decision block and then runs the resulting job through the normal dry-run / sample / run pipeline.\n\n```bash\nflashrev-ai-enrich run --source leads.csv --out leads.enriched.csv \\\n  --prompt \"for each row, take the email column and verify it is a deliverable business email\" \\\n  --yes\n```\n\nRules of thumb when writing prompts:\n\n- Name the CSV column explicitly (\"take the **email** column\"); vague prompts make the LLM return empty mappings.\n- Describe the business outcome, not the capability name (\"find the CEO\" beats \"use get_company_ceo\").\n- One capability per prompt — the LLM picks exactly one funcName.\n- `--map` / `--output` on the command line override the LLM's choices; use them to lock specific columns while letting the LLM pick the capability.\n- `--capability X --prompt \"...\"` together: `--capability` wins, `--prompt` is ignored with a stderr warning (no routing token charged).\n- Unroutable prompts (e.g., \"make me a sandwich\") exit non-zero with the LLM's reasoning printed; zero rows run.\n\nAgents calling this CLI should usually skip prompt routing entirely — `schema` + explicit `--capability ID` is cheaper, faster, and deterministic. Prompt routing is for humans at a terminal.\n\n## Status semantics (output CSV columns)\n\nEvery output CSV gets `flashrev_enrich_status` and `flashrev_enrich_error` columns:\n\n| status | meaning |\n|---|---|\n| `success` | Got business data; charged per capability `unitPriceToken`. |\n| `cached` | Hit `unlock_contact` dedup (same `person_id` already unlocked). 0 tokens. |\n| `no_data` | Backend returned 200 but the requested output fields are empty / null. 0 tokens. |\n| `failed` | HTTP error from backend, retries exhausted. 0 tokens. |\n\n`Failed` count > 0 with `Tokens used` > 0 means some rows got SOMETHING from backend (charged) but not the specific output fields the user asked for.\n\n## Cost reporting\n\n`Summary` line in `run` output prints `(balance before → balance after)` — that delta is the **authoritative** amount charged for the row enrichments. Each row's individual `cost.tokens` reported by backend may be slightly off under high concurrency (known limitation; `token-history` is always exact).\n\nWhen `--prompt` is used, the Routing-decision block prints its own `routing cost: 1 token(s)` line. That 1 token is **not** included in the `Summary` `balance before → after` delta, since routing happens before the balance snapshot. Total user cost per `--prompt` run = 1 routing token + (rows × capability unitPriceToken).\n\n## Special capability: `customer_api`\n\n`customer_api` does NOT call FlashRev backend — the CLI fetches the user-provided URL locally and parses the response. 0 tokens.\n\nInputs (via `--map <field>=<csv_col>` or `--input <field>=<value>`):\n\n| field | required | default | notes |\n|---|---|---|---|\n| `url` | yes | — | target URL (alias: `endpoint`) |\n| `method` | no | `GET` | HTTP method |\n| `headers` | no | `{}` | JSON object of HTTP headers |\n| `body` | no | — | string (sent as-is) or object (JSON-serialized; Content-Type defaults to application/json) |\n| `params` | no | — | object of query-string params; appended to `url` |\n| `timeout` | no | `30000` | milliseconds before AbortError |\n\nThe response JSON (or `{ text }` wrapper for non-JSON) becomes the row's enrichment data; map output columns via `--output csv_col=response_field` as usual. Useful for mixing 3rd-party APIs into the same enrichment workflow.\n\n## Date format\n\n`--from` and `--to` accept `YYYY-MM-DD`. They are interpreted in the local timezone. `--to` alone makes the CLI paginate through history until it covers the date range (up to 2000 records).\n\n## Safety rules\n\n- Never print or store `FLASHREV_API_KEY` in generated artifacts.\n- Prefer the `FLASHREV_API_KEY` env var over `--api-key`.\n- Treat email / phone enrichment (`enrich_email` / `enrich_phone`) as paid unlock operations.\n- If `tokens` returns `remaining: 0`, tell the user to recharge before running.\n- Do not describe or expose FlashRev backend data sources, routing, or internal service names to end users.\n- Never overwrite the source CSV (CLI refuses `--source == --out`).\n- Preserve row-level errors in `flashrev_enrich_status` and `flashrev_enrich_error` columns.\n\n## Failure handling\n\n- `402 Insufficient tokens` → run terminates; tell user to recharge.\n- `401` / `403` → invalid API key; verify `FLASHREV_API_KEY`.\n- `429 Rate limit` → CLI auto-retries with exponential backoff (500ms / 1s / 2s, up to 3 retries = 4 total attempts).\n- `503` / `504` → backend timeout/unavailable; auto-retried with the same schedule as 429.\n- Any other 4xx/5xx on a row → that single row is marked `failed`, batch continues.\n- `--prompt` routing failure (LLM returns non-JSON, unknown funcName, or `run_llm` itself errors) → CLI exits non-zero **before** enrichment starts, prints the LLM's reasoning. Suggest the user retry with `--capability ID`.\n- `--prompt` routed to a capability but `Input mapping does not satisfy <funcName>` → the LLM returned empty / wrong mapping; rerun with a more explicit prompt (name the CSV column) or use `--map` to override.\n\n## Workflow recipe\n\n```bash\n# 1. (first time) write config\nflashrev-ai-enrich init\nexport FLASHREV_API_KEY=\"sk_xxxx\"   # from info.flashlabs.ai/settings/privateApps\n\n# 2. verify\nflashrev-ai-enrich doctor\n\n# 3. browse capabilities and pick one\nflashrev-ai-enrich schema | less\n\n# 4. (optional) check balance\nflashrev-ai-enrich tokens\n\n# 5. estimate cost\nflashrev-ai-enrich dry-run --source leads.csv \\\n  --capability enrich_email \\\n  --map first_name=first_name --map last_name=last_name --map company_name=company\n\n# 6. real run with sample preview\nflashrev-ai-enrich run --source leads.csv --out out.csv \\\n  --capability enrich_email \\\n  --map first_name=first_name --map last_name=last_name --map company_name=company \\\n  --output verified_email=verified_business_email\n# (preview shown, type 'y' to continue, or pass --yes to auto-confirm)\n\n# 7. audit spend\nflashrev-ai-enrich token-history --from 2026-05-01\n```\n\n### Shortcut for ad-hoc human use (prompt routing)\n\nWhen the user does not know the capability name and is willing to spend 1 extra token to let the LLM pick:\n\n```bash\n# dry-run only routes (1 token) — no enrichment\nflashrev-ai-enrich dry-run --source leads.csv \\\n  --prompt \"find the CEO of each company\"\n\n# real run: 1 routing token + N rows\nflashrev-ai-enrich run --source leads.csv --out out.csv \\\n  --prompt \"find the CEO of each company\" --yes\n```\n\nAgents should skip this and pass `--capability` directly.\n\nFile v1.0.0:_meta.json\n\n{\n  \"ownerId\": \"kn728zt3jt22zg85rat5nantrd86d3dy\",\n  \"slug\": \"flashrev-ai-enrich\",\n  \"version\": \"1.0.0\",\n  \"publishedAt\": 1780062229533\n}\n\nFile v1.0.0:references/api_contract.md\n\n# FlashRev AI Enrich API Contract\n\nThe wire format this CLI relies on. Anything beyond what is documented here is FlashRev internal and may change without notice.\n\n## Base URL & auth\n\n| Item | Value |\n|---|---|\n| Base URL | `https://open-ai-api.flashlabs.ai` |\n| Auth header | `X-API-Key: <private app key>` |\n| Key issuance | https://info.flashlabs.ai/settings/privateApps |\n| Path prefix | All CLI-facing routes share the `/flashrev/...` prefix |\n\nThe API key is exchanged for an authenticated session by the FlashRev gateway. The CLI never sees or handles internal tokens.\n\n## Endpoints used by CLI\n\n### 1. Token balance — `GET /flashrev/api/v2/oauth/me`\n\nResponse:\n```json\n{\n  \"code\": 200,\n  \"data\": {\n    \"companyId\": 1000001,\n    \"newCreditFlag\": \"Y\",\n    \"limit\": {\n      \"tokenTotal\": 1121000,\n      \"tokenCost\": 917206.5,\n      \"tokenCategoryRemain\": { \"SUBSCRIPTION\": 0, \"GIFT\": 0, \"ADDON\": 289974 }\n    },\n    \"vip\": { \"packageName\": \"...\", \"...\": \"...\" }\n  }\n}\n```\n\nCLI computes `remaining = tokenTotal - tokenCost`.\n\n### 2. Token history — `POST /flashrev/api/v2/commodity/token/transaction/list`\n\nRequest body:\n```json\n{ \"page\": 1, \"pageSize\": 100, \"transactionType\": 2 }\n```\n\n`transactionType: 2` = consumption (1 = top-up).\n\nResponse:\n```json\n{\n  \"code\": 200,\n  \"data\": {\n    \"list\": [\n      { \"createdAt\": \"2026-05-29 06:59:56\", \"featId\": \"unlock_contact\",\n        \"featName\": \"Verify Email Address\", \"tokenAmount\": 1, \"unit\": \"Run\",\n        \"quantity\": 1, \"transactionType\": 2 }\n    ],\n    \"total\": 100, \"page\": 1, \"pageSize\": 100\n  }\n}\n```\n\nThe endpoint does not currently accept date filters; the CLI paginates and filters locally by `createdAt`. CLI cap: 20 pages × 100 = 2000 records.\n\n### 3. Capability registry — `GET /flashrev/api/v1/enrich/configs`\n\nReturns the active capability list with pricing and shape.\n\n```json\n{\n  \"code\": 200,\n  \"data\": [\n    {\n      \"funcName\": \"enrich_email\",\n      \"displayName\": \"Enrich Person -> Get Emails\",\n      \"featId\": \"unlock_contact\",\n      \"unitPriceToken\": 2,\n      \"concurrency\": 10,\n      \"inputColumn\": [\n        { \"key\": \"first_name\", \"name\": \"First Name\" },\n        { \"key\": \"last_name\",  \"name\": \"Last Name\" }\n      ],\n      \"outputColumn\": [\"verified_business_email\", \"all_verified_business_emails\", \"...\"],\n      \"rules\": [\n        [\"first_name\", \"last_name\", \"company_name\"],\n        [\"person_linkedin_url\"],\n        [\"email\"]\n      ]\n    }\n  ]\n}\n```\n\n### 4. Enrich — `POST /flashrev/api/v1/enrich/run`\n\nPer-row enrichment, synchronous.\n\nRequest body (snake_case on the wire):\n```json\n{\n  \"func_name\": \"enrich_email\",\n  \"input\": {\n    \"first_name\": \"Ada\",\n    \"last_name\": \"Lovelace\",\n    \"company_name\": \"Acme\"\n  },\n  \"row_id\": \"row-42\"\n}\n```\n\n`row_id` is optional and used by the CLI for tracing only.\n\nResponse (success):\n```json\n{\n  \"code\": 200,\n  \"msg\": \"OK\",\n  \"data\": {\n    \"code\": 200,\n    \"msg\": \"Successful\",\n    \"data\": {\n      \"verified_business_email\": \"ada@acme.com\",\n      \"all_verified_business_emails\": [\"ada@acme.com\"],\n      \"verified_personal_email\": \"\"\n    },\n    \"cost\": { \"tokens\": 2, \"cached\": false }\n  }\n}\n```\n\nThe response uses a nested `data` wrapper; the CLI's `normalizeEnrichResponse` handles both flat and nested shapes.\n\n`cost.tokens` may be slightly inaccurate under high concurrency; the values returned by `token/transaction/list` are always exact.\n\n### 5. Error codes\n\nHTTP-level (gateway / transport):\n\n| Code | Meaning |\n|---|---|\n| 401 | Invalid or revoked `X-API-Key` |\n| 402 | Insufficient tokens |\n| 429 | Rate limit exceeded (CLI auto-retries with exponential backoff) |\n| 503 | Service temporarily unavailable |\n| 504 | Upstream timeout |\n\nBusiness-level (HTTP 200 but inner `code != 200`):\n\n| Inner code | Meaning |\n|---|---|\n| 200 + `data` populated | Real enrichment, charged at `unitPriceToken` |\n| 200 + `data` empty / requested fields blank | No data for this lead; CLI marks `no_data`, not charged |\n| 422 | Input validation failed (e.g., missing required input combo) |\n| 4xx other | Request rejected |\n\n## Deduction semantics\n\n- **Pre-check**: The balance is checked before any downstream call. Insufficient → `402` immediately, no charge.\n- **Rate limit**: A per-`funcName` quota is enforced server-side. Overflow → `429`; the CLI retries with backoff.\n- **Charge on success only**: A row is billed only when the response carries real business data. Empty / 4xx / 5xx responses are not billed.\n- **Dedup**: Contact-unlock capabilities (`enrich_email`, `enrich_phone`) consult an unlock cache. Repeat unlocks of the same person return cached data at 0 tokens (`cost.cached: true`).\n\n## customer_api\n\n`customer_api` is a special capability whose backend route is empty. The CLI fetches the user-provided URL locally and parses the response. Token cost is always 0. Use it to mix third-party data sources into the same enrichment workflow.\n\nFile v1.0.0:skill-card.md\n\n## Description: <br>\nEnrich FlashRev CSV lead lists with dry-run estimates and sample previews. <br>\n\nThis skill is ready for commercial/non-commercial use. <br>\n\n## Publisher: <br>\n[flashlabs-ai](https://clawhub.ai/user/flashlabs-ai) <br>\n\n### License/Terms of Use: <br>\nMIT-0 <br>\n\n\n## Use Case: <br>\nDevelopers, sales operations teams, and agent users use this skill to enrich CSV lead lists through the FlashRev CLI, estimate token costs, preview sample rows, and write enriched CSV outputs after approval. <br>\n\n### Deployment Geography for Use: <br>\nGlobal <br>\n\n## Known Risks and Mitigations: <br>\nRisk: The documented customer_api capability can make local web requests to user-provided URLs. <br>\nMitigation: Confirm the exact destination URL before use, avoid localhost, private-network, and cloud-metadata addresses, and do not pass API keys or unrelated CSV columns in headers, bodies, or query parameters. <br>\nRisk: The skill uses a private FlashRev API key and may process sensitive lead data. <br>\nMitigation: Use the FLASHREV_API_KEY environment variable, never print or store the key in generated artifacts, and review dry-run and sample output before any paid live run. <br>\n\n\n## Reference(s): <br>\n- [FlashRev AI Enrich API Contract](artifact/references/api_contract.md) <br>\n- [FlashRev AI Enrich on ClawHub](https://clawhub.ai/flashlabs-ai/flashrev-ai-enrich) <br>\n- [FlashRev Private App Settings](https://info.flashlabs.ai/settings/privateApps) <br>\n\n\n## Skill Output: <br>\n**Output Type(s):** [shell commands, configuration, guidance, CSV files] <br>\n**Output Format:** [Markdown guidance with inline shell commands and JSON or CSV mapping examples] <br>\n**Output Parameters:** [1D] <br>\n**Other Properties Related to Output:** [Live runs should use dry-run estimates and sample preview before enrichment; AI mode can return JSON-structured command outputs.] <br>\n\n## Skill Version(s): <br>\n1.0.0 (source: server release metadata) <br>\n\n## Ethical Considerations: <br>\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment. <br>\n\nFile v1.0.0:agents/openai.yaml\n\ndisplay_name: FlashRev AI Enrich\nshort_description: Enrich FlashRev CSV lead lists with dry-run estimates and sample previews.\ndefault_prompt: Run FlashRev AI Enrich on a CSV using a dry-run first, then enrich after approval.","readmeExcerpt":"Skill: FlashRev AI Enrich Owner: flashlabs-ai Summary: Use this skill when an AI agent needs to enrich a CSV lead list through the flashrev-ai-enrich npm CLI. Triggers on list enrichment, filling missing company... Tags: latest:1.3.0 Version history: v1.3.0 | 2026-07-16T13:34:41.757Z | user Add contact waterfall enrichment: unify email and phone lookup, prioritize existing contact data before provider fallback, unloc","codeSnippets":[],"executableExamples":[{"language":"text","snippet":"flashrev-ai-enrich init [--force]                              Write default config\nflashrev-ai-enrich doctor [--no-api]                            Self-check Node / config / API\nflashrev-ai-enrich tokens [--json]                              Show balance / total / used / plan\nflashrev-ai-enrich token-history [--from YYYY-MM-DD] [--to YYYY-MM-DD] [--limit N] [--json]\n                                                                Show consumption log (auto-paginates)\nflashrev-ai-enrich schema [--advanced|--all] [--json]           List production-backed capabilities (synced from backend at runtime).\n                                                                Default view shows recommended product entries and category summaries;\n                                                                --advanced lists all atoms by category, --all shows the raw registry\nflashrev-ai-enrich plan --source X.csv [--goal contact|person|company|identity|all] [--contact-type email|phone|both] [--json] [--emit-jobs|--no-emit-jobs]\n                                                                Recommend concrete follow-up capabilities from the CSV's non-empty\n                                                                columns; JSON is read-only unless --emit-jobs is passed. 0 tokens\nflashrev-ai-enrich dry-run  --source leads.csv (--capability ID | --job planned.job.json | --prompt \"...\") [--map ...] [--output ...] [--json]\n                                                                Validate job and show run plan without calling backend. --json returns approvalReasons/wouldOverwrite\nflashrev-ai-enrich run      --source leads.csv [--out X.csv] (--capability ID | --job F | --prompt \"...\") [--yes] [--concurrency N] [--sample-size N] [--sample-only] [--report-json [PATH]] [--guided] [--overwrite]\n                                                                Real enrichment with sample preview. In AI mode stdout is JSON and progress is stderr"},{"language":"bash","snippet":"flashrev-ai-enrich run \\\n  --source leads.csv --out leads.enriched.csv \\\n  --capability enrich_email \\\n  --map first_name=first_name --map last_name=last_name --map company_name=company \\\n  --output verified_email=verified_business_email \\\n  --yes"},{"language":"bash","snippet":"flashrev-ai-enrich run \\\n  --capability verify_email \\\n  --input email=ada@example.com \\\n  --output ok=deliverable_email \\\n  --out out.csv --yes"},{"language":"bash","snippet":"flashrev-ai-enrich run --source leads.csv --out out.csv --job enrich.job.json --yes"},{"language":"json","snippet":"{\n  \"capability\": \"enrich_email\",\n  \"inputMapping\": {\n    \"first_name\":  \"first_name\",\n    \"last_name\":   \"last_name\",\n    \"company_name\": \"company\"\n  },\n  \"outputs\": {\n    \"verified_business_email\":  \"verified_business_email\",\n    \"all_verified_business_emails\": \"all_verified_business_emails\"\n  }\n}"},{"language":"bash","snippet":"flashrev-ai-enrich run --source leads.csv --out leads.enriched.csv \\\n  --prompt \"for each row, take the email column and verify it is a deliverable business email\" \\\n  --yes"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: flashrev-ai-enrich\ndescription: Use this skill when an AI agent needs to enrich a CSV lead list through the flashrev-ai-enrich npm CLI. Triggers on list enrichment, filling missing company or person fields, verifying or unlocking emails and phones, finding CEOs, executives, LinkedIn posts, matching companies or people to FlashRev IDs, Google search/news/maps lookups, scraping a page, or running an LLM over each row. Agents must run with `FLASHREV_ENRICH_AI_MODE=1`, call `schema --json`, use only live `funcName` values, and invoke each command with explicit `--capability FUNC_NAME --map ... --output ...`. For broad person enrich requests, run a profile + contact pipeline when supported; for contact-only requests, run only the requested contact capability. Avoid `--prompt` unless explicitly requested. Dry-run and sample preview are required before live runs unless already authorized.\n---\n\n# FlashRev AI Enrich\n\nUse the `flashrev-ai-enrich` CLI to enrich CSV lead lists through FlashRev. The CLI does not send outreach messages. It reads CSV files, maps CSV columns to FlashRev capability inputs, validates the job with dry-run, previews enriched sample rows, then writes an enriched CSV.\n\n## Commands\n\n```\nflashrev-ai-enrich init [--force]                              Write default config\nflashrev-ai-enrich doctor [--no-api]                            Self-check Node / config / API\nflashrev-ai-enrich tokens [--json]                              Show balance / total / used / plan\nflashrev-ai-enrich token-history [--from YYYY-MM-DD] [--to YYYY-MM-DD] [--limit N] [--json]\n                                                                Show consumption log (auto-paginates)\nflashrev-ai-enrich schema [--advanced|--all] [--json]           List production-backed capabilities (synced from backend at runtime).\n                                                                Default view shows recommended product entries and category summaries;\n                                                                --advanced lists all atoms by category, --all shows the raw registry\nflashrev-ai-enrich plan --source X.csv [--goal contact|person|company|identity|all] [--contact-type email|phone|both] [--json] [--emit-jobs|--no-emit-jobs]\n                                                                Recommend concrete follow-up capabilities from the CSV's non-empty\n                                                                columns; JSON is read-only unless --emit-jobs is passed. 0 tokens\nflashrev-ai-enrich dry-run  --source leads.csv (--capability ID | --job planned.job.json | --prompt \"...\") [--map ...] [--output ...] [--json]\n                                                                Validate job and show run plan without calling backend. --json returns approvalReasons/wouldOverwrite\nflashrev-ai-enrich run      --source leads.csv [--out X.csv] (--capability ID | --job F | --prompt \"...\") [--yes] [--concurrency N] [--sample-size N] [--sample-only] [--repo"},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn728zt3jt22zg85rat5nantrd86d3dy\",\n  \"slug\": \"flashrev-ai-enrich\",\n  \"version\": \"1.3.0\",\n  \"publishedAt\": 1784208881757\n}"},{"path":"references/api_contract.md","content":"# FlashRev AI Enrich API Contract\n\nThe wire format this CLI relies on. Anything beyond what is documented here is FlashRev internal and may change without notice.\n\n## Base URL & auth\n\n| Item | Value |\n|---|---|\n| Base URL | `https://open-ai-api.flashlabs.ai` |\n| Auth header | `X-API-Key: <private app key>` |\n| Key issuance | https://info.flashlabs.ai/settings/privateApps |\n| Path prefix | All CLI-facing routes share the `/flashrev/...` prefix |\n\nThe API key is exchanged for an authenticated session by the FlashRev gateway. The CLI never sees or handles internal tokens.\n\n## Endpoints used by CLI\n\n### 1. Token balance — `GET /flashrev/api/v2/oauth/me`\n\nResponse:\n```json\n{\n  \"code\": 200,\n  \"data\": {\n    \"companyId\": 1000001,\n    \"newCreditFlag\": \"Y\",\n    \"limit\": {\n      \"tokenTotal\": 1121000,\n      \"tokenCost\": 917206.5,\n      \"tokenCategoryRemain\": { \"SUBSCRIPTION\": 0, \"GIFT\": 0, \"ADDON\": 289974 }\n    },\n    \"vip\": { \"packageName\": \"...\", \"...\": \"...\" }\n  }\n}\n```\n\nCLI computes `remaining = tokenTotal - tokenCost`.\n\n### 2. Token history — `POST /flashrev/api/v2/commodity/token/transaction/list`\n\nRequest body:\n```json\n{ \"page\": 1, \"pageSize\": 100, \"transactionType\": 2 }\n```\n\n`transactionType: 2` = consumption (1 = top-up).\n\nResponse:\n```json\n{\n  \"code\": 200,\n  \"data\": {\n    \"list\": [\n      { \"createdAt\": \"2026-05-29 06:59:56\", \"featId\": \"unlock_contact\",\n        \"featName\": \"Verify Email Address\", \"tokenAmount\": 1, \"unit\": \"Run\",\n        \"quantity\": 1, \"transactionType\": 2 }\n    ],\n    \"total\": 100, \"page\": 1, \"pageSize\": 100\n  }\n}\n```\n\nThe endpoint does not currently accept date filters; the CLI paginates and filters locally by `createdAt`. CLI cap: 20 pages × 100 = 2000 records.\n\n### 3. Capability registry — `GET /flashrev/api/v1/enrich/configs`\n\nReturns the active capability list with pricing and shape.\n\n```json\n{\n  \"code\": 200,\n  \"data\": [\n    {\n      \"funcName\": \"enrich_email\",\n      \"displayName\": \"Enrich Person -> Get Emails\",\n      \"featId\": \"unlock_contact\",\n      \"unitPriceToken\": 2,\n      \"concurrency\": 10,\n      \"inputColumn\": [\n        { \"key\": \"first_name\", \"name\": \"First Name\" },\n        { \"key\": \"last_name\",  \"name\": \"Last Name\" }\n      ],\n      \"outputColumn\": [\"verified_business_email\", \"all_verified_business_emails\", \"...\"],\n      \"rules\": [\n        [\"first_name\", \"last_name\", \"company_name\"],\n        [\"person_linkedin_url\"],\n        [\"email\"]\n      ]\n    }\n  ]\n}\n```\n\n### 4. Enrich — `POST /flashrev/api/v1/enrich/run`\n\nPer-row enrichment, synchronous.\n\nRequest body (snake_case on the wire):\n```json\n{\n  \"func_name\": \"enrich_email\",\n  \"input\": {\n    \"first_name\": \"Ada\",\n    \"last_name\": \"Lovelace\",\n    \"company_name\": \"Acme\"\n  },\n  \"row_id\": \"row-42\"\n}\n```\n\n`row_id` is optional and used by the CLI for tracing only.\n\nResponse (success):\n```json\n{\n  \"code\": 200,\n  \"msg\": \"OK\",\n  \"data\": {\n    \"code\": 200,\n    \"msg\": \"Successful\",\n    \"data\": {\n      \"verified_business_email\": \"ada@acme.com\",\n      \"all_verified_business_emails\": [\"ada"},{"path":"skill-card.md","content":"## Description:\n\nUse this skill when an AI agent needs to enrich a CSV lead list through the flashrev-ai-enrich npm CLI.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[flashlabs-ai](https://clawhub.ai/user/flashlabs-ai)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and business operations teams use this skill to guide agents through schema-first FlashRev CSV enrichment for lead, company, person, contact, search, scrape, and row-level LLM workflows. It emphasizes live capability discovery, explicit mapping, dry-run validation, sample review, and token or credit spend reporting before full runs.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Contact enrichment can unlock paid lead data and consume tokens or credits.\n\nMitigation: Run token checks, dry-runs, and sample previews before live enrichment, and continue only after the user approves the spend and reviewed sample.\n\nRisk: The FLASHREV_API_KEY could be exposed through files, logs, generated artifacts, or mapped request fields.\n\nMitigation: Keep FLASHREV_API_KEY in the environment, never print it, and never map API keys, OAuth tokens, passwords, cookies, or unrelated PII into customer_api headers or body.\n\nRisk: The customer_api capability can send lead data to third-party URLs and can be used for internal-network requests.\n\nMitigation: Use customer_api only after confirming the destination domain and mapped fields; require separate explicit approval before --allow-internal-targets.\n\nRisk: DNS rebinding or unrestricted outbound access may bypass documented host guardrails in sensitive corporate or cloud environments.\n\nMitigation: Restrict outbound network access with operating-system firewall rules or network ACLs when running this skill in sensitive environments.\n\n## Reference(s):\n\n- [FlashRev AI Enrich API Contract](references/api_contract.md)\n- [FlashRev AI Enrich on ClawHub](https://clawhub.ai/flashlabs-ai/skills/flashrev-ai-enrich)\n- [FlashRev Private App Key Settings](https://info.flashlabs.ai/settings/privateApps)\n\n## Skill Output:\n\n**Output Type(s):** [Guidance, Shell commands, Configuration, Markdown, JSON]\n\n**Output Format:** [Markdown guidance with shell commands and JSON report handling]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Guides agents to produce enriched CSV outputs through the FlashRev CLI, with dry-run, sample preview, status counts, row errors, and token or credit spend reporting.]\n\n## Skill Version(s):\n\n1.3.0 (source: server release metadata)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment."},{"path":"agents/openai.yaml","content":"display_name: FlashRev AI Enrich\nshort_description: Enrich FlashRev CSV lead lists through the FlashRev CLI with schema-first capability selection.\ndefault_prompt: >\n  Use flashrev-ai-enrich in AI mode. Run doctor --no-api, schema --json,\n  tokens --json, plan --json for broad jobs, and dry-run --json before live\n  enrichment. Select only capabilities returned by the live schema and use\n  explicit --capability, not --prompt, unless the user explicitly asks for\n  prompt routing. Treat plan --json as read-only unless --emit-jobs is approved.\n  For contact-only planning, use --contact-type email|phone|both. Before full\n  live runs, use run --sample-only --json --yes after the user approves sample\n  token spend, show the returned sample, then continue only after approval. In\n  AI mode, run stdout is the structured JSON report and progress is stderr.\n  Stop on approvalReasons such as contact cleartext unlock, customer_api egress,\n  --allow-internal-targets, output overwrite, and high-volume runs. Use\n  --overwrite only after explicit approval."}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":"Use this skill when an AI agent needs to enrich a CSV lead list through the flashrev-ai-enrich npm CLI. Triggers on list enrichment, filling missing company... Skill: FlashRev AI Enrich Owner: flashlabs-ai Summary: Use this skill when an AI agent needs to enrich a CSV lead list through the flashrev-ai-enrich npm CLI. Triggers on list enrichment, filling missing company... Tags: latest:1.3.0 Version history: v1.3.0 | 2026-07-16T13:34:41.757Z | user Add contact waterfall enrichment: unify email and phone lookup, prioritize existing contact data before provider fallback, unloc","editorialQuality":{"score":100,"threshold":65,"status":"ready","wordCount":1427,"uniquenessScore":47,"reasons":[]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-11T04:26:16.691Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-11T04:26:16.691Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-11T07:41:54.213Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}