Common patterns for building AI agents that operate Rex. These patterns work with any LLM framework โ Claude, GPT, LangChain, CrewAI, or custom agents.
Automatically qualify new leads by researching their company and scoring fit.
Trigger: contact.created webhook event
Workflow:
PATCH /contacts/:id)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),
}),
});
}
}
After a meeting, summarize the conversation and create action items.
Trigger: activity.created webhook where type === "meeting"
Workflow:
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,
}),
});
}
Generate a daily pipeline report and post it to Slack.
Trigger: Cron schedule (daily at 9am)
Workflow:
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);
}
Use read-only keys unless the agent needs to modify data. Create a separate read scope key for reporting agents.
Log agent actions as activities. When an agent modifies data, log a note explaining what it did and why. This creates an audit trail.
Use schema discovery. Call GET /schema at agent startup to understand the current data model, including custom fields. Don't hardcode field names.
Handle rate limits gracefully. Agents should respect Retry-After headers and back off when hitting limits.
Be idempotent. Use upsert endpoints (POST /contacts/upsert) instead of create endpoints when possible to avoid duplicates from retries.