| name | sdr-reply-classifier |
| description | SDR inbox triage: classify reply intent (interested, not now, objection, referral, OOO), generate context-aware responses, update CRM, and notify Slack — sub-5-minute reply cadence |
SDR Reply Classifier Skill
When to activate
- You have incoming email replies from a cold outreach sequence
- You need to triage a full inbox and generate draft responses quickly
- You want to build an automated reply-handling pipeline (n8n / Make / Zapier)
- Sub-5-minute reply cadence is a goal (3x higher booking rate than 30-minute manual)
- You want to route replies to the right action without reading every email manually
When NOT to use
- Inbound marketing leads — different intent, use a dedicated inbound routing flow
- Customer support replies — not an SDR function
- Internal emails — scope only outbound sequences
- Newsletters or mass mail — not replies to outreach
Instructions
Intent classification schema
import { generateObject } from 'ai'
import { anthropic } from '@ai-sdk/anthropic'
import { z } from 'zod'
const ReplyIntent = z.enum([
'interested',
'not_now',
'not_interested',
'objection_price',
'objection_competitor',
'objection_timing',
'objection_not_relevant',
'question',
'referral',
'meeting_reschedule',
'ooo',
'spam_filter',
'unknown',
])
async function classifyReply(replyBody: string, originalEmail: string, prospectContext: string) {
const { object } = await generateObject({
model: anthropic('claude-sonnet-4-6'),
schema: z.object({
intent: ReplyIntent,
confidence: z.number().min(0).max(1),
sentiment: z.enum(['positive', 'neutral', 'negative']),
keySignals: z.array(z.string()).max(3),
urgency: z.enum(['immediate', 'this_week', 'this_month', 'low']),
extractedData: z.object({
returnDate: z.string().optional(),
referredContact: z.string().optional(),
objectionText: z.string().optional(),
requestedInfo: z.string().optional(),
}),
recommendedAction: z.enum([
'book_meeting', 'send_resources', 'add_to_nurture',
'mark_opted_out', 'follow_up_in_X_days', 'route_to_human',
'update_contact_info', 'stop_sequence',
]),
followUpDays: z.number().optional(),
}),
prompt: `Classify this email reply from a cold outreach sequence.
ORIGINAL OUTREACH:
${originalEmail}
PROSPECT CONTEXT:
${prospectContext}
REPLY:
${replyBody}
Classify the intent precisely. Extract any key data (return dates, referral names, specific objections).
Recommend the most appropriate next action.`,
})
return object
}
Response generation by intent
async function generateResponse(
intent: z.infer<typeof ReplyIntent>,
prospect: ProspectContext,
reply: string,
senderContext: SenderContext
): Promise<string | null> {
const RESPOND_IMMEDIATELY = ['interested', 'question', 'objection_price',
'objection_competitor', 'objection_timing', 'objection_not_relevant', 'referral']
if (!RESPOND_IMMEDIATELY.includes(intent)) return null
const prompts: Record<string, string> = {
interested: `
Write a reply to ${prospect.name}'s positive response.
They seem interested. Next step: book a 15-minute discovery call.
Their company: ${prospect.company}. Their role: ${prospect.title}.
Rules:
- Acknowledge their interest warmly but concisely (1 sentence)
- Propose a specific day/time (or use Calendly link: ${senderContext.calendlyUrl})
- Offer an alternative if that doesn't work
- 4-5 sentences total, conversational
- Don't oversell — they said yes, just confirm the meeting
`,
objection_price: `
Write a reply to a price objection from at .
Their objection: ""
Rules:
- Acknowledge the concern without being defensive
- Reframe with ROI: time saved, revenue impact, or risk avoided
- If possible, suggest a lower-commitment entry point (pilot, trial, smaller scope)
- Offer to walk through the ROI calculation on a call
- 4-6 sentences, confident not pushy
- Don't apologise for pricing
`,
: ,
: ,
: ,
}
{ text } = ({
: (),
: prompts[intent],
})
text
}
Full triage pipeline
async function triageInbox(replies: IncomingReply[]): Promise<TriageResult[]> {
const results: TriageResult[] = []
for (const reply of replies) {
console.log(`\nTriaging reply from ${reply.from}...`)
const classification = await classifyReply(
reply.body,
reply.originalEmail,
await getProspectContext(reply.from)
)
console.log(`→ Intent: ${classification.intent} (${Math.round(classification.confidence * 100)}% confidence)`)
let response: string | null = null
let crmAction: string = ''
switch (classification.intent) {
case 'interested':
response = await (, reply., reply., )
crmAction =
:
(reply.)
crmAction =
:
days = classification. ??
(reply., days)
crmAction =
:
returnDate = classification..
(returnDate) (reply., returnDate)
crmAction =
:
referred = classification..
(referred) (referred, { : reply. })
response = (, reply., reply., )
crmAction =
:
response = (classification., reply., reply., )
crmAction =
}
(reply., {
: classification.,
: ().(),
: ,
})
([, , ].(classification.)) {
({
: ,
: ,
: classification.,
: response,
})
}
results.({
: reply.,
: classification.,
: classification.,
: response,
crmAction,
})
}
results
}
Objection handling matrix
For each objection, Claude generates a response using:
1. Acknowledge — validate their concern (1 sentence)
2. Reframe — shift the lens (1-2 sentences)
3. Evidence — proof point or question (1 sentence)
4. Next step — soft CTA (1 sentence)
OBJECTION: "We already use [Competitor]"
FRAMEWORK:
- Acknowledge: "Makes sense — [Competitor] does solid work in [area]."
- Reframe: "Most teams we talk to use [Competitor] for [X] but find gaps in [Y]."
- Evidence: "Curious — are you getting [specific outcome] with them today?"
- Next step: "Worth a 15-minute compare? I can show you the gaps specifically."
OBJECTION: "Not in budget right now"
FRAMEWORK:
- Acknowledge: "Totally understand — budgets are tight."
- Reframe: "The teams that get the most from us usually start small to prove ROI before expanding."
- Evidence: "[Customer] started with a pilot and saved [X hours/month] in the first quarter."
- Next step: "Would a smaller pilot scope work, or is it worth revisiting in [month]?"
OBJECTION: "Not relevant to us"
FRAMEWORK:
- Acknowledge: "Fair — sounds like the way I framed it wasn't the right angle."
- Reframe: "Can I ask — are you dealing with [specific pain] at all?"
- Evidence: [If yes] "That's actually exactly where we help."
- Next step: "If not, I won't waste your time — but curious what [pain area] looks like for you."
OBJECTION: "Send me more info"
RISK: This is often a polite no. Don't just send a 10-page PDF.
FRAMEWORK:
- Clarify: "Happy to — what specific question would you like the info to answer?"
- Then: Send targeted answer, not a brochure
- Follow up: Call 2 days after sending (not email)
Batch triage prompt (no-code version)
You are triaging replies to a cold outreach sequence.
For each reply below, output:
1. Intent: [interested | not_now | not_interested | objection_price | objection_competitor |
objection_timing | question | referral | ooo | unknown]
2. Confidence: [0-100]
3. Recommended action: [book_meeting | send_resources | stop_sequence | nurture_90d | route_human]
4. Draft response: [3-5 sentence reply, or "no response needed"]
---
REPLY 1 (from: jane@acme.com):
[paste reply]
REPLY 2 (from: bob@startup.io):
[paste reply]
---
Format output as a table.
Example
Inbox: 12 replies from this week's outreach sequence. SDR has 20 minutes before a call.
Output:
Slack gets notified of the 3 hot replies. CRM updated automatically. SDR reviews and sends 3 draft responses in 8 minutes total.