- Posted on
- • Artificial Intelligence
Artificial Intelligence CRM Integrations
- Author
-
-
- User
- linuxbash
- Posts by this author
- Posts by this author
-
AI CRM Integrations from the Linux Command Line: Automate, Enrich, and Accelerate
If your CRM feels like a data graveyard—full of leads, tickets, and notes that never turn into action—you’re not alone. The value is there; the friction is too. AI can bridge that gap. And you don’t need a full-blown platform rewrite to get started. With a Linux box, Bash, and a few APIs, you can enrich leads, triage tickets, summarize conversations, and de-duplicate records—automatically.
This post shows you how to wire up practical AI workflows to your CRM using shell scripts you can schedule and monitor. You’ll see real commands, safe patterns, and distro-friendly installation steps.
Why this matters (and why do it in Bash)
Faster, better outcomes: AI can summarize calls, score leads, and route tickets in seconds.
Low cost to try: Most CRMs expose REST APIs; most AI providers do, too. A few lines of Bash with
curlandjqgo a long way.Transparent and controllable: CLI-first integrations are easy to audit, log, and reason about. Perfect for ops-minded teams.
Portable: Same scripts run on Ubuntu, Fedora/RHEL, or openSUSE, on-prem or in the cloud.
What you’ll need
A CRM with an HTTP API and a token (examples below use placeholder endpoints).
An AI API key (e.g., OpenAI or Hugging Face Inference API). Replace placeholders with your provider of choice.
Basic Linux CLI tools.
Install prerequisites
- Debian/Ubuntu:
sudo apt update
sudo apt install -y curl jq cron
sudo systemctl enable --now cron
- Fedora/RHEL/CentOS:
sudo dnf install -y curl jq cronie
sudo systemctl enable --now crond
- openSUSE Leap/Tumbleweed:
sudo zypper refresh
sudo zypper install -y curl jq cron
sudo systemctl enable --now cron
Configure environment variables
Add to your shell profile (~/.bashrc) or CI secrets. Reload your shell after saving.
export CRM_BASE="https://api.example-crm.com/v1"
export CRM_TOKEN="your_crm_token_here"
# Option A: OpenAI
export OPENAI_API_KEY="sk-..."
# Option B: Hugging Face Inference API
export HF_TOKEN="hf_..."
Tip: Use a secrets manager or systemd environment files for production. Avoid committing secrets.
1) AI-powered lead enrichment (company summary, tech stack, buying signals)
Problem: SDRs burn time researching accounts.
Goal: Pull new leads from your CRM, ask an LLM for a structured enrichment, and write it back.
Script: enrich the latest leads and attach a JSON note to each record.
#!/usr/bin/env bash
set -euo pipefail
: "${CRM_BASE:?Set CRM_BASE}"
: "${CRM_TOKEN:?Set CRM_TOKEN}"
: "${OPENAI_API_KEY:?Set OPENAI_API_KEY}" # using OpenAI in this example
fetch_leads() {
curl -sS -H "Authorization: Bearer $CRM_TOKEN" \
"$CRM_BASE/leads?needs_enrichment=true&limit=5"
}
update_lead() {
local id="$1" data_json="$2"
curl -sS -X PATCH "$CRM_BASE/leads/$id" \
-H "Authorization: Bearer $CRM_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"ai_enrichment\": $data_json}" >/dev/null
}
enrich() {
local company="$1" website="$2" description="$3"
local prompt
prompt=$(cat <<EOF
You are a CRM enrichment assistant. Return strict JSON with keys:
summary (string, 1–2 sentences),
key_contacts (array of strings, placeholders if unknown),
tech_stack (array of strings, guess if likely),
buying_signals (array of strings).
Company: $company
Website: $website
Context: $description
EOF
)
curl -sS https://api.openai.com/v1/chat/completions \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d @- <<JSON | jq -r '.choices[0].message.content'
{
"model": "gpt-4o-mini",
"temperature": 0.2,
"response_format": { "type": "json_object" },
"messages": [
{"role":"system","content":"Only output valid JSON."},
{"role":"user","content": $(jq -Rn --arg p "$prompt" '$p')}
]
}
JSON
}
main() {
leads_json="$(fetch_leads)"
echo "$leads_json" | jq -c '.leads[]' | while read -r lead; do
id=$(jq -r '.id' <<<"$lead")
company=$(jq -r '.company_name' <<<"$lead")
website=$(jq -r '.website // ""' <<<"$lead")
desc=$(jq -r '.notes // ""' <<<"$lead")
echo "Enriching lead $id: $company"
enrichment="$(enrich "$company" "$website" "$desc")" || {
echo "Enrichment failed for $id" >&2; continue;
}
# Validate JSON before writing back
if jq -e . >/dev/null 2>&1 <<<"$enrichment"; then
update_lead "$id" "$enrichment"
echo "Updated $id"
else
echo "Invalid JSON for $id, skipping" >&2
fi
done
}
main "$@"
Run:
bash enrich_leads.sh
What you get:
A structured summary on each lead (searchable and reportable).
Fast context for SDRs without tab-hopping.
2) Sentiment-based ticket triage (prioritize angry customers automatically)
Problem: Support queues grow faster than your team.
Goal: Detect sentiment and urgency to bump priority or route automatically.
Example using Hugging Face’s sentiment model:
#!/usr/bin/env bash
set -euo pipefail
: "${CRM_BASE:?Set CRM_BASE}"
: "${CRM_TOKEN:?Set CRM_TOKEN}"
: "${HF_TOKEN:?Set HF_TOKEN}"
fetch_open_tickets() {
curl -sS -H "Authorization: Bearer $CRM_TOKEN" \
"$CRM_BASE/tickets?status=open&untriaged=true&limit=10"
}
set_priority() {
local id="$1" prio="$2"
curl -sS -X PATCH "$CRM_BASE/tickets/$id" \
-H "Authorization: Bearer $CRM_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"priority\": \"$prio\", \"triaged_by_ai\": true}" >/dev/null
}
sentiment() {
local text="$1"
curl -sS -H "Authorization: Bearer $HF_TOKEN" \
-H "Content-Type: application/json" \
https://api-inference.huggingface.co/models/distilbert-base-uncased-finetuned-sst-2-english \
-d "$(jq -n --arg t "$text" '$t')" \
| jq -r '.[0].label'
}
main() {
tickets="$(fetch_open_tickets)"
echo "$tickets" | jq -c '.tickets[]' | while read -r t; do
id=$(jq -r '.id' <<<"$t")
body=$(jq -r '.body' <<<"$t")
label="$(sentiment "$body")"
case "$label" in
NEGATIVE) prio="high" ;;
POSITIVE) prio="normal" ;;
NEUTRAL|*) prio="normal" ;;
esac
echo "Ticket $id sentiment=$label -> priority=$prio"
set_priority "$id" "$prio"
done
}
main "$@"
You can extend this to detect intent (billing vs. technical) using a classification prompt in your LLM and auto-route by team.
3) AI-assisted deduplication and canonicalization (cleaner CRM, happier reports)
Problem: “Acme Inc.”, “ACME Incorporated”, and “acme.com” end up as three accounts.
Goal: Ask an LLM if two records refer to the same entity and produce a canonical name.
#!/usr/bin/env bash
set -euo pipefail
: "${CRM_BASE:?Set CRM_BASE}"
: "${CRM_TOKEN:?Set CRM_TOKEN}"
: "${OPENAI_API_KEY:?Set OPENAI_API_KEY}"
fetch_potential_dupes() {
curl -sS -H "Authorization: Bearer $CRM_TOKEN" \
"$CRM_BASE/accounts/potential_duplicates?limit=10"
}
merge_accounts() {
local winner_id="$1" loser_id="$2" canonical="$3"
curl -sS -X POST "$CRM_BASE/accounts/merge" \
-H "Authorization: Bearer $CRM_TOKEN" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg w "$winner_id" --arg l "$loser_id" --arg c "$canonical" \
'{winner_id:$w, loser_id:$l, canonical_name:$c, merged_by_ai:true}')"
}
judge_dupe() {
local a_json="$1" b_json="$2"
local prompt
prompt=$(cat <<'EOF'
Return strict JSON:
{
"same_entity": true|false,
"confidence": 0..1,
"canonical_name": "string"
}
Decide if A and B refer to the same company. Prefer concise, standardized canonical names.
EOF
)
curl -sS https://api.openai.com/v1/chat/completions \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d @- <<JSON | jq -r '.choices[0].message.content'
{
"model": "gpt-4o-mini",
"temperature": 0.0,
"response_format": { "type": "json_object" },
"messages": [
{"role": "system", "content": "Only output valid JSON."},
{"role": "user", "content":
$(jq -n \
--arg p "$prompt" \
--argjson A "$a_json" \
--argjson B "$b_json" \
'$p + "\nA: " + ( $A|tostring ) + "\nB: " + ( $B|tostring )')
}
]
}
JSON
}
main() {
pairs="$(fetch_potential_dupes)"
echo "$pairs" | jq -c '.pairs[]' | while read -r pair; do
a=$(jq -c '.a' <<<"$pair")
b=$(jq -c '.b' <<<"$pair")
a_id=$(jq -r '.a.id' <<<"$pair")
b_id=$(jq -r '.b.id' <<<"$pair")
verdict="$(judge_dupe "$a" "$b")"
same=$(jq -r '.same_entity' <<<"$verdict")
conf=$(jq -r '.confidence' <<<"$verdict")
canon=$(jq -r '.canonical_name' <<<"$verdict")
echo "Pair $a_id vs $b_id -> same=$same conf=$conf canon=\"$canon\""
if [[ "$same" == "true" && $(awk "BEGIN{print ($conf>=0.8)}") -eq 1 ]]; then
# Choose winner by record age or completeness (example: prefer 'a')
merge_accounts "$a_id" "$b_id" "$canon" >/dev/null
echo "Merged $b_id into $a_id"
fi
done
}
main "$@"
Use confidence thresholds and log merges for audit. Consider human-in-the-loop approval for borderline cases.
4) Summarize calls/emails and propose next actions (notes your team will actually read)
Problem: Long notes don’t get read; follow-ups get missed.
Goal: Summarize the latest interactions and create a crisp next-step checklist.
#!/usr/bin/env bash
set -euo pipefail
: "${CRM_BASE:?Set CRM_BASE}"
: "${CRM_TOKEN:?Set CRM_TOKEN}"
: "${OPENAI_API_KEY:?Set OPENAI_API_KEY}"
fetch_recent_notes() {
curl -sS -H "Authorization: Bearer $CRM_TOKEN" \
"$CRM_BASE/interactions?type=call,email&summarized=false&limit=10"
}
attach_summary() {
local id="$1" summary_json="$2"
curl -sS -X PATCH "$CRM_BASE/interactions/$id" \
-H "Authorization: Bearer $CRM_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"ai_summary\": $summary_json, \"summarized\": true}" >/dev/null
}
summarize() {
local text="$1"
local prompt
prompt=$(cat <<'EOF'
Return strict JSON with:
summary: string (<=120 words),
objections: array of strings,
next_actions: array of strings (prioritized, actionable),
urgency: "low"|"normal"|"high".
EOF
)
curl -sS https://api.openai.com/v1/chat/completions \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d @- <<JSON | jq -r '.choices[0].message.content'
{
"model": "gpt-4o-mini",
"temperature": 0.2,
"response_format": { "type": "json_object" },
"messages": [
{"role":"system","content":"Only output valid JSON."},
{"role":"user","content": $(jq -Rn --arg p "$prompt" --arg t "$text" '$p + "\nTEXT:\n" + $t')}
]
}
JSON
}
main() {
items="$(fetch_recent_notes)"
echo "$items" | jq -c '.items[]' | while read -r it; do
id=$(jq -r '.id' <<<"$it")
body=$(jq -r '.body' <<<"$it")
echo "Summarizing $id ..."
out="$(summarize "$body")"
if jq -e . >/dev/null 2>&1 <<<"$out"; then
attach_summary "$id" "$out"
echo "Updated $id"
else
echo "Bad JSON for $id" >&2
fi
done
}
main "$@"
Now reps can scan summary, see objections, and act on a clear to-do list.
Scheduling and reliability
- Cron: run each workflow on a schedule.
Add to crontab (edit with crontab -e):
# Enrich new leads every hour
0 * * * * /usr/bin/bash /opt/crm/enrich_leads.sh >> /var/log/crm/enrich.log 2>&1
# Triage tickets every 5 minutes
*/5 * * * * /usr/bin/bash /opt/crm/triage_tickets.sh >> /var/log/crm/triage.log 2>&1
# Dedup nightly
15 2 * * * /usr/bin/bash /opt/crm/dedupe.sh >> /var/log/crm/dedupe.log 2>&1
# Summarize interactions hourly
5 * * * * /usr/bin/bash /opt/crm/summarize_notes.sh >> /var/log/crm/summarize.log 2>&1
State and idempotency:
- Store “last processed ID” in a file:
~/.cache/crm_last_id. Filter API queries usingupdated_sinceorafter_id. - Use provider/request idempotency keys if available.
- Log JSON responses for traceability.
- Store “last processed ID” in a file:
Observability:
- Grep logs for failures and alert on error rates.
- Consider systemd units + timers for richer journald logs.
Data governance and safety tips
Don’t ship raw PII to AI without legal basis and customer consent.
Mask or hash sensitive fields when feasible.
Prefer provider features for data privacy (no-training modes, regional hosting).
Rate limit and backoff to avoid API bans.
Keep a manual override path for merges or high-impact changes.
Real-world patterns that work
SaaS sales: Automatically enrich leads from web signups, add buying signals to the record, and alert Slack if “high intent” shows.
MSP/support: Sentiment/intent routing reduces first-response times; exec escalations trip “high” instantly.
B2B ops: Weekly dedupe plus canonicalization improves pipeline accuracy and forecasting.
Your next step (CTA)
Pick one workflow above—ideally the smallest one with the biggest pain relief—and ship it this week:
1) Install curl/jq/cron using apt, dnf, or zypper.
2) Set your CRM and AI API tokens.
3) Drop the script in /opt/crm/, run it once manually, then add a cron entry.
4) Watch the logs, tune prompts, and iterate.
When you can do it in Bash, you can do it anywhere—productionize later, but unlock value now.