---
title: "CRM hook bundle, auto-lookup customers, log interactions from email drafts | Recipe — StudioMeyer Academy"
description: "Two mcp_tool hooks for the StudioMeyer CRM that turn customer-mention into instant context and email-draft into automatic interaction log. UserPromptSubmit, PostToolUse on Edit/Write."
canonical: "https://studiomeyer.academy/en/recipes/16.3-crm-hook-bundle"
language: en
markdown_versions: ["https://studiomeyer.academy/en/recipes/16.3-crm-hook-bundle.md"]
publisher: "StudioMeyer Academy, https://studiomeyer.academy (llms.txt: https://studiomeyer.academy/llms.txt)"
---

[← Alle Recipes](https://studiomeyer.academy/recipes)

Phase 16 · Claude Code Hooks·5 steps

# CRM hook bundle, auto-lookup customers, log interactions from email drafts

Two mcp\_tool hooks for the StudioMeyer CRM that turn customer-mention into instant context and email-draft into automatic interaction log. UserPromptSubmit, PostToolUse on Edit/Write.

5 steps0%

Du liest ohne Account. Mit [Login](https://studiomeyer.academy/login?next=/recipes/16.3-crm-hook-bundle) speichern wir Step-Fortschritt + Notes.

# CRM hook bundle, auto-lookup customers, log interactions from email drafts

The CRM MCP (`studiomeyer-crm`, hosted at `crm.studiomeyer.io`) has 33 tools. Without hooks, every "where do we stand with customer X" question means manually calling `crm_search`. Every email draft you write means manually calling `crm_log_interaction` afterwards (and you forget half the time).

Two hooks fix both.

### Step 1: UserPromptSubmit hook → auto-lookup on customer mention

When you say "draft a reply to Acme Corp" the assistant should already know who Acme is, pipeline stage, last interaction, open deals, before it writes a single word. Wire it:

```json
{
  "UserPromptSubmit": [
    {
      "hooks": [
        {
          "type": "mcp_tool",
          "server": "studiomeyer-crm",
          "tool": "crm_search",
          "input": { "query": "${user_prompt}", "limit": 3 },
          "timeout": 10,
          "statusMessage": "CRM: customer lookup..."
        }
      ]
    }
  ]
}
```

`crm_search` does fuzzy matching over company names, contact names, and notes. It returns the top 3 matches. If your prompt mentions "Acme" and there's an Acme Corp in your CRM, the tool result lands in the assistant's context before it composes a response. If there's no match, it returns empty, non-blocking.

This hook fires on EVERY user prompt, which means every prompt costs one CRM lookup. If you don't want that, use bash filtering: only fire when prompt contains capitalized multi-word patterns like "Acme Corp". For now we'll keep it simple, `crm_search` is fast (under 200ms typical) and returns no-op for non-matching queries.

### Step 2: PostToolUse hook on Edit/Write → log email drafts

When you draft an email, usually via Edit/Write on a `.md` or `.txt` file with "email" in the path, log that as an interaction:

```json
{
  "PostToolUse": [
    {
      "matcher": "Edit|Write",
      "hooks": [
        {
          "type": "mcp_tool",
          "server": "studiomeyer-crm",
          "tool": "crm_log_interaction",
          "input": {
            "type": "email-draft",
            "summary": "Drafted email at ${tool_input.file_path}",
            "tool_use_id": "${tool_use_id}"
          },
          "timeout": 10,
          "statusMessage": "CRM: logging email draft..."
        }
      ],
      "if": "Edit(*email*)|Write(*email*)|Edit(*draft*)|Write(*draft*)"
    }
  ]
}
```

The `if`\-filter narrows the matcher to file paths containing "email" or "draft". This is permission-rule syntax, pipe-separated patterns of `Tool(glob)`. Without the filter, EVERY Edit/Write would log a CRM interaction, which is way too noisy.

### Step 3: GDPR + idempotency notes

- **Idempotent.** `crm_search` is read-only. `crm_log_interaction` is NOT strictly idempotent, calling it twice creates two interaction records. The `tool_use_id` parameter is what fixes this: the CRM dedupes on `tool_use_id` so re-firing the hook on the same Edit (e.g. via re-run) doesn't duplicate the log.
- **Fast.** Both tools under 300ms typical against a warm Postgres connection.
- **Deterministic.** `crm_search` ranks results by relevance + recency. Same query, same ranking. `crm_log_interaction` is a write, output is just `{success: true, id: N}`.
- **Side-effect-free without user trigger.** `crm_search` is read-only. `crm_log_interaction` writes, but the user implicitly triggered it by drafting an email.
- **GDPR.** Customer data flows from your Claude Code session to your own CRM tenant on `crm.studiomeyer.io`. No third-party sharing. Same tenant-isolation as Memory.

### Step 4: Optional, narrow UserPromptSubmit further

If `crm_search` on every prompt feels too aggressive, swap to a bash hook that only fires on customer-name patterns:

```bash
# ~/.claude/hooks/crm-customer-pre-check.sh
#!/usr/bin/env bash
input=$(cat)
prompt=$(echo "$input" | jq -r '.prompt // .user_prompt // ""')
if echo "$prompt" | grep -qE '\b[A-Z][a-z]+ (Corp|GmbH|AG|Inc|Ltd|Solutions|Consulting)\b'; then
  echo '{"hookSpecificOutput":{"additionalContext":"Customer-name pattern detected. Suggest calling crm_search before composing response."}}'
fi
```

Then wire as `command` type in `UserPromptSubmit`. Fires only when the prompt contains a recognizable company-name pattern. Trades some recall for less noise.

### Step 5: Verify the bundle works

```bash
claude
# Type: "draft a quick reply to Acme Corp about pricing"
```

Check the statusline, "CRM: customer lookup..." should flash. Then check the dashboard at `https://crm.studiomeyer.io/dashboard`, Acme Corp's last-interaction-timestamp should NOT have changed (no Edit/Write fired yet).

Then drop the draft into a file:

```bash
# In Claude Code:
# "save the draft to /tmp/email-acme-pricing.md"
```

Check the dashboard again, Acme should now have a new "email-draft" interaction logged.

## Done

CRM is integrated into your Claude Code workflow without manual `crm_search` or `crm_log_interaction` calls. Every customer-mention pre-fetches context, every email-draft logs an interaction.

## Sources

- [Claude Code Hooks Reference](https://code.claude.com/docs/en/hooks)
- [StudioMeyer CRM MCP Documentation](https://crm.studiomeyer.io)
- Permission-rule syntax for `if`\-filter: [Claude Code Permissions Reference](https://code.claude.com/docs/en/settings)

[← Memory hook bundle, auto-persi](https://studiomeyer.academy/recipes/16.2-memory-hook-bundle) [GEO + Crew hook bundles, auto- →](https://studiomeyer.academy/recipes/16.4-geo-crew-hook-bundle)

Phase 16

mcp\_tool hooks, Memory/CRM/GEO/Crew/Academy hook bundles

[Alle Phasen →](https://studiomeyer.academy/recipes)

Concepts

[Hooks](https://studiomeyer.academy/concepts/hooks)

## Sitemap

Public pages of this site as Markdown: [sitemap](https://studiomeyer.academy/sitemap.md). Curated entry point: [llms.txt](https://studiomeyer.academy/llms.txt), full text: [llms-full.txt](https://studiomeyer.academy/llms-full.txt).
