Anthropic Claude AI
Claude is used for AI-powered call summarization in Client Portal. When a sales call produces a transcript, Claude analyzes it to extract disposition, sentiment, key points, buying signals, and objections -- replacing manual note-taking and providing consistent, structured intelligence.
Configuration
| Setting | Value |
|---|---|
| API URL | https://api.anthropic.com/v1/messages |
| Auth Header | x-api-key: {ANTHROPIC_API_KEY} |
| API Version | anthropic-version: 2023-06-01 |
| Model | claude-sonnet-4-5-20250929 (configurable via ANTHROPIC_MODEL env) |
| Max Tokens | 1024 |
| Env Variable | ANTHROPIC_API_KEY (Supabase secret) |
Edge Function: summarize-sales-call
Trigger
Called automatically by ghl-call-webhook (or ghl-sales-webhook) when a call transcript meets these conditions:
- Transcript text is longer than 50 characters (after flattening)
- Transcript does not match the voicemail greeting pattern
Can also be triggered manually via the "Summarize" button in the Sales Call Detail drawer.
Request
{
"call_id": "uuid",
"transcript": "Full call transcript text..."
}
Process Flow
AI Output Schema
Claude returns a JSON object with the following structure:
{
"disposition": "Future Potential",
"key_points": [
"Prospect runs a 12-person legal search firm in NYC",
"Currently uses in-house researchers for candidate sourcing",
"Expressed interest in patent litigation market coverage",
"Asked about pricing and timeline for validation sprint"
],
"sentiment": "interested",
"action_items": [
"Send pricing deck for Market Validation Sprint",
"Schedule follow-up call for next Tuesday"
],
"engagement_assessment": "Strong prospect -- actively looking for market intelligence solution. Decision timeline is 2-4 weeks.",
"conversation_quality": "warm",
"interest_level": 7,
"objections": [
"Concerned about overlap with existing research team"
],
"buying_signals": [
"Asked about pricing unprompted",
"Mentioned specific market they want covered",
"Asked about onboarding timeline"
]
}
Field Definitions
| Field | Type | Values | Description |
|---|---|---|---|
disposition | string | One of 9 canonical | The AI's best-fit disposition for the call |
key_points | string[] | 3--5 items | Most important facts from the conversation |
sentiment | string | interested, skeptical, not_interested, excited, neutral | Prospect's emotional tone |
action_items | string[] | Variable | Specific next steps needed |
engagement_assessment | string | Free text | 1--2 sentence commercial assessment |
conversation_quality | string | cold, neutral, warm, hot | Temperature of the conversation |
interest_level | number | 1--10 | Numeric interest rating |
objections | string[] | Variable | Concerns or pushback raised |
buying_signals | string[] | Variable | Positive indicators of purchase intent |
Disposition Rules
The system prompt instructs Claude to choose from the 9 canonical dispositions with specific guidance:
| Disposition | When to Use |
|---|---|
| Voicemail | Transcript is a voicemail greeting or automated message |
| Gatekeeper | Receptionist/assistant answered, decision-maker not reached |
| Incorrect Number | Wrong person, number not in service |
| Not Interested | Prospect explicitly declined or shut the conversation down |
| Callback Requested | Prospect literally said "call me back", "try me later" -- do NOT use as default |
| Future Potential | Any engaged conversation without a meeting booked. Correct choice for neutral-to-positive calls. When in doubt between Callback Requested and Future Potential, choose Future Potential. |
| Meeting Booked | A meeting or demo was scheduled during the call |
| Do Not Contact | Prospect explicitly asked not to be contacted again |
| No Answer | Only if transcript shows no real interaction |
AI disposition overwrites the regex-inferred disposition from the webhook handler. The ghl-call-webhook handler awaits the AI response and uses the AI disposition as the final value. BDR-confirmed dispositions from GHL's disposition workflow take highest priority and are never overwritten by AI.
Disposition Validation
The AI response is validated against the canonical list before being applied:
const VALID_DISPOSITIONS = [
"No Answer", "Voicemail", "Gatekeeper", "Incorrect Number",
"Not Interested", "Callback Requested", "Future Potential",
"Meeting Booked", "Do Not Contact",
];
const aiDisposition = VALID_DISPOSITIONS.includes(summary.disposition)
? summary.disposition
: null;
If the AI returns an invalid disposition, it is ignored and the existing disposition is preserved.
Database Updates
sales_calls Table Updates
| Column | Source | Description |
|---|---|---|
ai_summary | Full JSON | Complete AI analysis object (JSONB) |
ai_summary_text | Constructed | Plain-text summary for display |
disposition | summary.disposition | Canonical disposition (if valid) |
disposition_reason | summary.engagement_assessment | Prefixed with "AI: " |
conversation_quality | summary.conversation_quality | cold/neutral/warm/hot |
interest_level | summary.interest_level | 1--10 rating |
objections | summary.objections | Array of objection strings |
buying_signals | summary.buying_signals | Array of buying signal strings |
notes | Plain-text summary | Key points + sentiment + interest |
ai_processed_at | Timestamp | When AI processing completed |
sales_prospects Table Updates (Auto-Disposition)
After updating the call, the function automatically adjusts the prospect's engagement state:
| AI Disposition | Engagement Level | Follow-Up |
|---|---|---|
| Meeting Booked | meeting_set | Cleared (meeting is the follow-up) |
| Future Potential | engaged | 7 days from now |
| Callback Requested (warm/hot) | engaged | 2 days from now |
| Callback Requested (cold/neutral) | contacted | 3 days from now |
| Not Interested | nurturing | 14 days from now |
| Do Not Contact | closed_lost | Cleared, do_not_contact = true |
| Gatekeeper | contacted | 2 days from now |
| Voicemail | contacted | 1 day (first 3 VMs), 7 days (after) |
| Incorrect Number | closed_lost | Cleared |
| No Answer | contacted | None |
The follow-up includes context in next_follow_up_notes:
"Future potential. Interest: 7/10. Follow up in 1 week."
"Voicemail #3. Retry tomorrow."
"warm conversation. Interest: 8/10. Follow up in 2 days."
System Prompt
The full system prompt provides Claude with business context about 1 Hour Recruitment's service model:
You are an AI assistant for 1 Hour Recruitment, a company that sells
market intelligence services to boutique legal search firms.
You are analyzing a sales call transcript between a 1HR sales rep and
a prospective client (a legal search firm owner/partner).
Extract the following in JSON format: { ... }
Disposition rules: ...
- When in doubt between Callback Requested and Future Potential,
choose Future Potential.
Be concise and specific. Focus on commercial signals relevant to
closing the deal. Return ONLY valid JSON, no other text.
Error Handling
| Error | Behavior |
|---|---|
ANTHROPIC_API_KEY not set | Returns 500 with descriptive error |
| Claude API error (4xx/5xx) | Returns 500 with Claude's error message |
| JSON parse failure | Tries regex extraction of JSON from response |
| Invalid disposition | Preserves existing disposition, still saves summary |
| Auto-disposition failure | Logged, does not fail the summary response |
Manual Trigger
Users can trigger summarization from the UI via two paths:
- SummarizeCallButton (
src/components/sales/SummarizeCallButton.tsx) -- one-click on a call that already has a transcript. - PasteTranscriptDialog (
src/components/sales/PasteTranscriptDialog.tsx) -- paste a transcript manually for calls without one.
Both call the edge function with the same { call_id, transcript } payload.
Related Documentation
- GHL Webhooks -- Auto-triggers AI summarization when transcripts arrive
- GHL Overview -- Architecture of the GHL integration that feeds transcripts