Skip to content
Start free trial

Claude Code Skill

Install the HARi skill in Claude Code and manage your entire CRM through conversation. Create contacts, build pipelines, send emails, configure automations — Claude knows the full API.

Download Skill v1.26.0

Size: 55 KB · Released: 2 June 2026

9 reference files covering every part of the HARi API. Claude loads only what’s needed per conversation.

FileCovers
SKILL.mdMain entry point — auth, routing, safety gates, core rules
records.mdCRUD operations, filtering, search, restore, purge, audit log
schema.mdEntities, fields (25 types), relations, solutions
workflows.mdAutomation, pipelines, views, process templates, scoring
communications.mdEmail, inbox, mailboxes, templates, snippets, embed forms
dashboards.mdCharts, goals, activity feed, data hygiene, notifications
ai.mdAI enrichment, AI search, chat, next actions, recap
admin.mdUsers, API keys, webhooks, integrations, GDPR, billing
advisory.mdGuided setup, gap analysis, UI navigation, user-level detection

Click the download button above, or use the terminal:

Terminal window
curl -L https://haricrm.com/downloads/hari-claude-skill.zip -o hari-skill.zip

Unzip into the .claude/skills/hari/ folder at the root of your project. Claude Code discovers skills in this directory automatically.

Terminal window
# Navigate to your project root
cd ~/my-project
# Create the skills directory and extract
mkdir -p .claude/skills/hari
unzip hari-skill.zip -d .claude/skills/hari/

Your project should look like this:

my-project/
├── .claude/
│ └── skills/
│ └── hari/ ← skill lives here
│ ├── SKILL.md
│ ├── records.md
│ ├── schema.md
│ ├── advisory.md
│ └── ... (9 files total)
├── src/
└── ...

Open Claude Code in your project and type /hari to invoke the skill directly, or just ask Claude anything about your CRM data — it will activate automatically.

$ claude
> List my top 10 contacts by deal value
Reading .claude/skills/hari/SKILL.md...
Reading .claude/skills/hari/records.md...
Here are your top 10 contacts:
1. Sarah Chen — HK$450,000 (3 opportunities)
2. James Wong — HK$380,000 (2 opportunities)
3. ...
CategoryExample prompts
Manage records”Create a contact for Alice at Acme Corp”, “Show me all open deals above HK$100k”
Build your data model”I need to track projects with tasks”, “Add a money field for budget”
Set up automations”When a deal reaches Proposal stage, notify the manager”
Email & templates”Send a follow-up to all contacts I met last week”, “Create a PDF quote template”
Dashboards”Show me pipeline value by stage”, “How many new contacts this month?”
AI enrichment”Enrich this company with AI”, “Fill in missing data for all new contacts”
  • Claude Code — Available as CLI, desktop app, or IDE extension. Get Claude Code
  • A HARi CRM account — The skill needs your credentials or API key. Start free trial
  • New formula functions: SWITCH, TRIM, DISPLAYschema.md documents three functions Claude can now use when building computed (type: formula) fields. SWITCH(input, case1, val1, …, [default]) is a multi-arm conditional that replaces deeply-nested ternaries; TRIM(s) is a null-safe whitespace strip (handy around CONCAT-ed names); DISPLAY(relation) resolves a belongs_to relation to its target’s display name without hard-coding a field. The skill’s function catalog table gains rows (with exact semantics) and datediff is now documented as a signed day count.
  • “Why your formula registration may be rejected” contract — a new section explains that the platform validates every formula expression before saving and returns a structured HTTP 422 on failure ({ error, kind, offending, allowed, suggestion, position, message }). It documents how to react to each field — pick a real function from allowed, apply the edit-distance suggestion, locate the problem at position — and a table of the kind values that actually fire (unknown_function, unknown_variable, unloaded_relation, parse_error, expression_too_long, runtime_type_error). This stops AI tools from blindly retrying with another hallucinated function name.
  • “Relations in formulas” section — clarifies the belongs_to vs has_many split: read a single related record with company.name / company["name"] / DISPLAY(company), aggregate a collection with sum(pluck(...)) / count(...) / where(...), and always declare the relation in options.formula_context or hit the unloaded_relation diagnosis.
  • Doc + capability-surfacing only; no API or contract change (the functions and the 422 contract already shipped server-side).
  • PDF workflow recipe in communications.md — the existing Templates & Documents section documented the API surface (endpoints, page_settings, header/footer, attachment() Twig function, save_to_field, type integer mapping) but did not show the assistant how to actually author a usable PDF template from a user request. The new section walks through the full flow in 7 steps: pick the right type (decision table mapping user phrases to the integer), create with entity for record-context resolution, author body with dompdf-safe HTML/CSS (no flex/grid, table-based layout, font + image sizing rules, page-break control), embed images correctly with the three field shapes (branding.logo_data_uri pre-resolved, bare UUID via attachment(), signed URL via raw <img src> — both work because PdfImageEmbedder catches the path substring), add Header/Footer in the dedicated top-level fields with <span class="page-number"> / <span class="page-count"> counters that only resolve inside header/footer, configure page_settings with four named presets (invoice / contract / photo report / compact label), test with /render before downloading, and ship via direct stream or save_to_field. Closes with a 12-row pitfalls table mapping symptom → root cause → fix (broken images, layout collapse, header/footer not repeating, page-counter empty, page-break mid-row, CJK fonts, save_to_field 422/403, etc.). Doc clarification only; no API or capability change.
  • Count questions are answered directly, never refusedrecords.md gains an “Answering count questions” section that tells the assistant exactly how to handle “How many contacts do I have?”, “How many open deals?”, and similar. Every list response (GET /api/records/{entity}) already returns a total field counting all matching records across all pages, so the assistant calls GET /api/records/{entity}?size=1 (tiny payload, just the count), reads total, and reports it — optionally with the same filters used for a scoped question. The section explicitly forbids the old failure mode where the assistant replied “I don’t have access to your live data” to a trivial count, which was wrong because the count is available on every list call. Doc clarification only; no API surface, capability, or contract change.
  • Email-troubleshooting preflight rulecommunications.md now opens the Email Settings section with an explicit PREFLIGHT block: any time a user reports “an email/alert/notification did not send,” the first call is GET /api/email/settings. If configured: true, the transport is working and the skill MUST NOT propose “Connect Plunk”, “Configure SMTP”, “switch provider”, or “set up email.” Instead it pivots to the actual failure modes: the workflow action shape, the recipient (empty placeholder, unsubscribed, blocked), the template id, the workflow run log, and the email send log. workflows.md adds a parallel 6-step diagnostic order under the send_email action. Closes the gap where the assistant defaulted to transport-configuration suggestions on tenants that already had SMTP / Plunk working — wasted user attention and undermined trust (“the AI did not even check”). Behaviour change only; no API surface or contract change.
  • PDF templates ship with first-class page setupcommunications.md now documents the three new optional fields on _template for type=2 (PDF) templates: header (Twig fragment), footer (Twig fragment), and page_settings (JSONB with paper, orientation, margin_top/right/bottom/left). Paper is allow-listed against A3 | A4 | A5 | letter | legal | tabloid | executive; orientation against portrait | landscape; margins are regex-validated (<number><cm|mm|in|px|pt>). The header/footer are placed in fixed-position blocks that repeat on every page, with two pre-styled counter helpers — <span class="page-number"></span> and <span class="page-count"></span> — using dompdf’s counter(page) / counter(pages). Defaults (A4 portrait, top/bottom = 2cm or 3cm/2.5cm when header/footer are set, left/right = 1.5cm) apply when keys are omitted. Legacy templates whose body already contains a <!DOCTYPE html> wrapper and have no header/footer/custom settings pass through unchanged.
  • attachment(uuid) Twig function — the right way to embed record images in templatescommunications.md documents the new server-side Twig function available in body, subject, header, and footer renders. It validates the UUID, verifies the attachment lives under the calling tenant’s uploads/{slug}/ prefix (tenant ownership guard), fetches bytes from storage, and returns a data:image/...;base64,... URI ready for <img src="…">. Per-request cache prevents duplicate S3 fetches. Template authors should never hard-code /api/attachments/{uuid} URLs — the internal API path is an implementation detail and attachment() shields templates from contract changes. A short note in records.md’s attachments section now points at the function. Legacy templates that still use the URL form keep working — the same PdfImageEmbedder that already ran in /pdf now also runs in /render, so the editor’s iframe preview matches the PDF byte-for-byte for image content.
  • save_to_field parameter on POST /api/templates/{id}/pdf — pass save_to_field: "<field_name>" alongside entity + record_id to upload the rendered PDF to tenant storage, track it in _attachment, and link the new attachment UUID into the named field of the source record (single attach for file, append for file_list). The new attachment UUID comes back in the X-Attachment-Id response header. The server enforces: field exists (400 if not), field type is file or file_list only (400 for image / image_list / text / anything else — PDFs are documents, not images), and the caller has UPDATE permission on the entity (403 if not). Use this for one-shot “Generate signed contract and attach to the deal” flows.
  • Template type integer mapping documented0 = generic, 1 = email, 2 = pdf, 3 = notification (SMALLINT column). The previous skill prose said type: "email|pdf|docx" which never matched the wire format. Same edit corrects the body field name: API write payloads use body (plain string), not body_html — that’s the snapshot column name on the internal _template_version history table and was never an API surface.
  • WhatsApp send-from-CRM documentedcommunications.md gains a new section covering the personal-WhatsApp pairing flow (POST /api/whatsapp/pair/start → poll /pair/qr + /pair/status), 1-on-1 text send (POST /api/whatsapp/send), the inbox-side reply path (POST /api/inbox/{messageId}/reply with channel: "whatsapp"), the chat-list + chat-filter endpoints (GET /api/whatsapp/chats, GET /api/whatsapp/chat-filter, PUT /api/whatsapp/chat-filter), and the disconnect flow. Explicitly out of scope and called out as such: group sends, attachments (images, documents, voice notes), workflow triggers on inbound WhatsApp, and quick-reply templates. The skill safety gate covers WhatsApp dispatches the same way it covers email sends — single confirmation per batch, with yes-whatsapp session-level bypass.
  • Formula fields support concat(...) and CONCAT(...) — variadic string-join helpers registered on the server-side FormulaEvaluator. Lets users write display formulas in the SQL-familiar form: CONCAT(last_name, " ", first_name) for a Full Name column, or concat("INV-", invoice_number) for prefixed identifiers. Null values inside the argument list become empty strings, numbers cast to their PHP string form, and both single- and double-quoted string literals work. Previously the only string-concatenation option was Symfony ExpressionLanguage’s ~ operator (last_name ~ " " ~ first_name), which most users coming from spreadsheets or SQL didn’t discover — formulas would silently evaluate to null and the column would render blank. schema.md now documents both forms side-by-side and lists concat / CONCAT in the helper table.
  • Inline campaign filter source (type: 'inline_filter') — campaigns can now reference a filter directly on the campaign instead of requiring a saved view round-trip. The skill documents the new shape, the one-hop relation filter syntax (<relation_name>.<column> where <relation_name> comes from _meta_relation.name — NOT the related entity name and NOT the FK column), the email_fields array for multi-email-field expansion (one candidate per row × email field), and the first-source-wins attribution rule. Cross-source / cross-entity dedup is automatic via the existing _campaign_send_log UNIQUE(campaign_id, lower(email)) constraint — same email shared between a contact and a company source ships exactly once.
  • Campaign recipient inspection (GET /api/email/campaigns/{id}/recipients)communications.md now documents the new paginated send-log endpoint. Returns _campaign_send_log rows with full pagination {page, limit, total, pages} and a status_counts block (sent / pending / sending / failed / cancelled / bounced / opened / clicked / unsubscribed) computed across the whole campaign — not just the visible page — so the campaign UI can render tab badges in a single round-trip. Optional ?status= filter narrows the rows on the page only; counts stay stable. AI-readable, so Claude can answer questions like “who actually got the THINKDROP 39 send?” without guessing from the top-level recipient_count aggregate.
  • Campaign reopen (POST /api/email/campaigns/{id}/reopen) — admin-only revive endpoint for campaigns that ended up in a terminal cancelled or failed state. Flips the campaign back to draft AND wipes its _campaign_send_log rows so the recipient list re-resolves on the next send (Layer-1 gates — blocklist, unsubscribe, dedup — fire on fresh data, not stale snapshots from the failed run). Returns 200 {reopened, status}, 409 if the campaign is not in a terminal state, 404 if not found. The skill marks it AI-cannot-call — users trigger it from the campaign UI’s “Reopen as draft” button.
  • Campaign abort endpoint documentedcommunications.md now documents the new admin-only POST /api/email/campaigns/{id}/abort for stopping a campaign that’s currently in sending status. Closes the gap where neither DELETE (drafts only) nor cancel-schedule (scheduled only) could halt an in-flight send. The skill marks the endpoint as AI-cannot-call — admins trigger it from the Campaigns UI (“Abort campaign” button on rows in sending status). Returns 200 {aborted, status, cancelled_recipients, preserved_sent} on success, 409 if not in sending, 404 if not found.
  • Tool denial protocol — when the platform’s AI gates (capability allowlist, rate limit, recipient visibility check, loop detector, AI freeze) reject a tool call, the skill now instructs the AI Assistant to surface the structured denial message + remediation directly to the user instead of retrying. Defense-in-depth alongside the C-07 + C-22 + C-23 + C-26 server gates: code remains the primary boundary, but the skill makes friendly cooperation the default. The denial bubble in the in-app AI chat now deep-links to the relevant /settings/ai-permissions#capability-{name} anchor so admins can fix the underlying permission with one click.
  • Email send caution — the new “Email send caution” section in communications.md codifies the per-user rate limits (2/hour, 5/day, 10/week) and reinforces the existing rule against looping compose over recipient lists (use the campaign pipeline instead). Documents the structured 429 rate_limited and 403 capability_denied responses so the AI knows exactly what to say to the user when blocked.
  • Lead entity retired in favour of contact.lifecycle_stage — DECIDE-05 ruled that the historical split between Lead and Contact creates more friction than value for SMEs. The lead entity is no longer in fresh-install schemas; existing tenants migrate via bin/migrate-lead-to-contact.php (per-tenant CLI, opt-in during a maintenance window). Prospects now live on contact with the new lifecycle_stage select field — values lead, qualified, customer, lost. The skill’s records.md opens with a v1.19.0 banner explaining the change so Claude doesn’t try to query a lead entity that no longer exists. Common queries that used to filter entity=lead should now filter entity=contact with ?filter=lifecycle_stage,eq,lead (or ?filter=lifecycle_stage,in,lead,qualified for the broader sales-pipeline view). The “Active Leads” view that ships with fresh CRM installs is entity=contact with that filter pre-applied.
  • Compose vs campaign — decision tree added to the skillcommunications.md now opens with a mandatory decision tree distinguishing 1-to-1 transactional sends (POST /api/email/compose) from bulk/marketing sends (campaign pipeline: POST /api/email/campaigns then /{id}/send). Looping compose over a list is now explicitly called out as forbidden — it bypassed opt-out enforcement, deduplication, and campaign counters, and on one customer tenant this produced 432 untracked sends with the campaign UI showing 0/432. The skill now points Claude at the right endpoint before the send happens.
  • Campaign pipeline endpoints documented for the first time — previous versions of the skill only mentioned compose. The new section covers draft creation, recipient preview, async send, progress polling, statistics, exclusions, test sends, spam-check, and the associated subscription-list / blocklist / sending-domain admin endpoints.
  • Server-side enforcement (landing with this release) — compose now rejects sends to blocklisted or globally-unsubscribed recipients, throttles bulk bursts with 429 compose_burst, and honors the Idempotency-Key header (24 h window). The skill documents these so Claude knows how to recover from a throttle without retrying against the brick wall.
  • send_email workflow action — server-side template rendering — supplying a template_id now renders the saved template body on the server side (fixes a silent empty-body bug where template_id alone produced emails with no content). Three valid shapes are now documented: inline HTML, template render (with optional variables), and template override (body_html wins).
  • workflows.md corrected — the misleading { "type": "send_email", "template_id": "<uuid>" } example has been fixed to include the required to field, and a new contract block documents all three valid shapes explicitly.
  • Silent version-check at session start — the skill now fetches https://haricrm.com/downloads/hari-claude-skill-version.json on its first call of a conversation and informs the user once if a newer version is available. Fully silent on network errors; never blocks API work.
  • Entity sort_order and batch reorder API — new PUT /api/schema/entities/reorder endpoint accepts an ordered array of entity names and sets sort_order on each. Entities are now displayed in sidebar and schema lists by sort_order ASC, name ASC. Individual entities can also be updated via PUT /api/schema/entities/{name} with sort_order field.
  • Comprehensive solution export/import documentation — the Solutions section in schema.md is now a full operational guide: what gets exported (entities, fields, relations, forms, views, BPFs, workflows, charts, templates, seed data), step-by-step cross-tenant migration workflow (export → ZIP → import), what happens during import (new entities created, existing entity/field metadata updated, dependencies auto-detected), version management (semver, upgrade vs downgrade), and troubleshooting table for common errors. Previously the section only listed endpoint names without explaining the workflow or edge cases.
  • New field type secret — stores TEXT, renders masked in edit (password input + Eye/EyeOff reveal toggle) and display (fixed 9-dot mask + Show/Copy buttons with 2s confirmation). Not encrypted — presentational mask only. For API keys, tokens, passwords that a user types into a record.
  • Field display style picker (options.display_style) — Schema Editor UI now writes a per-field style key that the renderer resolves in order options.widgetoptions.display_style → default. Options: selectdropdown / cards / pipeline / radio, multiselectcheckboxes / chip_grid / tags, percentnumber / progress_bar, booleancheckbox / toggle. Ships CardSelectField wired to the cards key; radio and toggle widgets fall through to defaults until the widget pairs are added.
  • Visible-when condition builder — Form Editor gets an Eye/EyeOff icon on every tab row and section header. Inline builder: field picker → operator → value. 10 operators: eq, neq, gt, lt, gte, lte, contains, not_contains, is_null, is_not_null. Numeric operators wrap both operands with Number() and return false when either side is NaN — so rules work correctly even when form values arrive as strings. in/nin remain valid in stored JSONB but aren’t yet exposed in the builder UI.
  • Formula field editing in the Schema Editortype: 'formula' fields now show a dedicated formula textarea plus a format picker (currency / percent / integer / text) next to the existing relation-context picker. Stored as options.formula + options.formula_format. No more hand-editing the JSON options blob.
  • Two new built-in sidebar components layered on the v1.10.0 sidebar DSL:
    • ActivityTimeline — condensed 3–5 item vertical timeline of the record’s most recent activity entries (coloured dot + summary + relative timestamp). Clamped limit 1–10.
    • RelatedRecordsList — inline list of records from a child entity filtered by FK, with count badge and “View all” link when truncated. Props: entity_name, relation, limit, display_field.
  • Record page right sidebar — forms can now declare a layout.sidebar block with ordered cells[] (bare field name, {type:"field"}, {type:"heading"}, {type:"spacer"}, {type:"component"}). Built-in sidebar component KeyFacts renders a compact read-only label/value stack reusing the same display components as the main form (colored pills, currency formatting, relation links). Width is clamped 240–360 px (default 280). Desktop-only; mobile falls back to single column.
  • Relation-aware formula fields — formulas can now reach beyond the current record’s scalars. Declare options.formula_context: ["transactions"] and reference related records directly: sum(pluck(where(transactions, 'deal_stage', 'eq', 'closed'), 'prix_bien')). Works for both belongs_to relations (exposed as an associative array — company.name) and reverse has_many (exposed as a list). Naive plural matching accepts transaction, transactions, transactiones. Backend batches one query per relation per list/get call — never N+1.
  • Collection helpers in formulas — new read-only functions in Symfony ExpressionLanguage: count, sum, min, max, avg, first, last, pluck(arr, field), where(arr, field, op, value) with ops eq · neq · gt · gte · lt · lte · in · nin · is_null · is_not_null · contains, plus today_minus_days(n) for date-range comparisons. Safety caps: max expression length 2000 chars, max related rows 5000 per relation, related records filtered to _state = 0.
  • Rewritten formulas referencedocs/reference/field-types.md §4 now matches the actual engine (Symfony ExpressionLanguage, bare field-name identifiers, no curly braces), documents collection helpers, relation-context syntax, safety limits, and known gaps (no reactive preview, no dependency-ordering between formulas, no writable computed values, no irregular-plural matching).
  • Section title size — sections now accept a label_size property (100 / 125 / 150 / 175 / 200) that scales the section label as a percentage of the default 12px. Configurable visually in the Form Editor alongside colour / icon / collapse / divider.
  • Comprehensive Form Layouts referenceschema.md rewritten with a full section-presentation reference table, brand-aligned colour palette, complete lucide icon list, field-entry formats (subgrids, components, spacers), full-layout PUT pattern for updates, and per-user personalisation flow. Claude sessions can now drive the Form Editor end-to-end from the skill without external context.
  • Section dividers — sections now accept a divider property ("none", "line", "thick") that renders a visible separator above the section on the record page. Thick dividers are tinted with the section’s colour when one is set. Configurable visually from the Form Editor.
  • Form layouts — documented the complete endpoint set (GET/POST /api/schema/entities/{entity}/forms, PUT /api/schema/forms/{id}, per-user personalization) so Claude can create and update detail/create/quick_view forms directly.
  • Section presentation metadata — sections now accept optional color (hex left-border accent), icon (lucide name), and collapsed (absent = not collapsible; present = click-to-toggle). Documented supported icon set and validation rules.
  • header_image_field on entities — documented how to pin an image/file field as the hero thumbnail on the record detail page. Validation: referenced field must exist and be of type image or file.
  • Entity update endpoint — corrected from PATCH to PUT /api/schema/entities/{name} and expanded the body spec.
  • Phone field country code selector — phone fields on embed forms now show a dropdown with 19 country codes (flag + dial code). Default auto-detected from browser locale. Configurable per field via options.default_country_code.
  • Embed form field-level validation errors — submissions now return per-field error details instead of a generic message. Frontend shows inline errors with red highlighting.
  • Embed form field type docs — documented all field type rendering behaviors (phone, select, email, boolean, textarea) and validation error response format.
  • Schema field types cleaned up — removed internal DB column types from skill reference, replaced with API-relevant behavior notes.
  • Added version field to SKILL.md frontmatter for tracking.
  • Find-or-Create patternfor_each now supports else_actions for zero-match branching
  • New update_trigger action — write back to the triggering record from inside for_each
  • create_record now returns the new ID, chainable via {{last_created_id}}
  • New placeholder namespaces: {{trigger.field}}, {{item.field}} inside for_each
  • Complete action types reference with JSON schemas and recipes
  • Fixed for_each placeholder resolution (sub-actions now resolve against iterated row)
  • Added 25 field types including money/multi-currency support
  • Added UI navigation discovery (API-first, never hardcoded)
  • Added currency settings and exchange rate endpoints
  • Improved advisory with disambiguation for ambiguous terms
  • Added communication safety gate for outbound actions
  • Added solution gate for schema changes
  • Added embed forms and OAuth endpoints
  • Initial release with full CRUD, schema, workflows, AI, dashboards