AI Agent Patterns

Common patterns for building AI agents that operate Rex. These patterns work with any LLM framework โ€” Claude, GPT, LangChain, CrewAI, or custom agents.

Pattern 1: Lead Qualification Agent

Automatically qualify new leads by researching their company and scoring fit.

Trigger: contact.created webhook event

Workflow:

  1. Receive webhook โ†’ extract contact email and company domain
  2. Enrich the contact via a third-party API (Apollo, Clearbit, etc.)
  3. Update the contact with enrichment data (PATCH /contacts/:id)
  4. Score the lead against your ICP criteria
  5. If qualified, move to prospect stage and create a follow-up task
async function qualifyLead(contact: Contact) {
  // 1. Enrich
  const enrichment = await enrichFromApollo(contact.email);

  // 2. Update contact with enrichment data
  await rex(`/contacts/${contact.id}`, {
    method: "PATCH",
    body: JSON.stringify({
      title: enrichment.title,
      metadata: {
        company_size: enrichment.company_size,
        industry: enrichment.industry,
        funding: enrichment.funding,
      },
    }),
  });

  // 3. Score
  const score = calculateICPScore(enrichment);

  // 4. If qualified, promote and create task
  if (score >= 70) {
    await rex(`/contacts/${contact.id}`, {
      method: "PATCH",
      body: JSON.stringify({ stage: "prospect" }),
    });

    await rex("/tasks", {
      method: "POST",
      body: JSON.stringify({
        title: `Follow up with ${contact.first_name} ${contact.last_name}`,
        contact_id: contact.id,
        priority: "high",
        due_at: inDays(2),
      }),
    });
  }
}

Pattern 2: Meeting Follow-Up Agent

After a meeting, summarize the conversation and create action items.

Trigger: activity.created webhook where type === "meeting"

Workflow:

  1. Receive meeting activity webhook
  2. Pass the meeting notes to an LLM for summarization
  3. Extract action items from the summary
  4. Create tasks for each action item
  5. Log a note activity with the summary
async function handleMeetingFollowUp(activity: Activity) {
  // 1. Summarize with LLM
  const summary = await llm.generate(`
    Summarize this meeting and extract action items:
    ${activity.body}
  `);

  // 2. Create tasks for action items
  for (const action of summary.actionItems) {
    await rex("/tasks", {
      method: "POST",
      body: JSON.stringify({
        title: action.title,
        contact_id: activity.contact_id,
        deal_id: activity.deal_id,
        priority: action.priority,
        due_at: action.dueDate,
      }),
    });
  }

  // 3. Log the summary as a note
  await rex("/activities", {
    method: "POST",
    body: JSON.stringify({
      type: "note",
      subject: "Meeting Summary (AI-generated)",
      body: summary.text,
      contact_id: activity.contact_id,
      deal_id: activity.deal_id,
    }),
  });
}

Pattern 3: Pipeline Reporter

Generate a daily pipeline report and post it to Slack.

Trigger: Cron schedule (daily at 9am)

Workflow:

  1. Fetch all open deals
  2. Group by pipeline stage
  3. Calculate totals and movement since yesterday
  4. Format and post to Slack
async function dailyPipelineReport() {
  const deals = await fetchAll("/deals?status=open");
  const pipelines = await rex("/pipelines");

  // Group deals by stage
  const byStage = new Map<string, Deal[]>();
  for (const deal of deals) {
    const stage = deal.stage_id;
    if (!byStage.has(stage)) byStage.set(stage, []);
    byStage.get(stage)!.push(deal);
  }

  // Build report
  let report = "๐Ÿ“Š *Daily Pipeline Report*\n\n";
  for (const pipeline of pipelines.data) {
    report += `*${pipeline.name}*\n`;
    for (const stage of pipeline.stages) {
      const stageDeals = byStage.get(stage.id) || [];
      const total = stageDeals.reduce((sum, d) => sum + d.value, 0);
      report += `  ${stage.name}: ${stageDeals.length} deals ($${(total / 100).toLocaleString()})\n`;
    }
    report += "\n";
  }

  await postToSlack(report);
}

Best practices for AI agents

  1. Use read-only keys unless the agent needs to modify data. Create a separate read scope key for reporting agents.

  2. Log agent actions as activities. When an agent modifies data, log a note explaining what it did and why. This creates an audit trail.

  3. Use schema discovery. Call GET /schema at agent startup to understand the current data model, including custom fields. Don't hardcode field names.

  4. Handle rate limits gracefully. Agents should respect Retry-After headers and back off when hitting limits.

  5. Be idempotent. Use upsert endpoints (POST /contacts/upsert) instead of create endpoints when possible to avoid duplicates from retries.

What's next?

Was this page helpful?

ยท