Connected Records Snippets
Custom Code Snippets — Connected Records
Copy-paste examples for working with connected records inside a Custom Code workflow step. To get a connected list into your code, configure an input on the step with source Action Record Object, Connected Record, or a previous step's record output — the value arrives as an array of objects, one per related record.
Always treat the input defensively — wrap with Array.isArray(...) ? ... : [] so an empty connection or a single-record source can't crash the script.
Average a numeric field across connected records
// Input: reviews (connected_obj — array of review objects, each with `rating`)
const reviews = Array.isArray(params["reviews"]) ? params["reviews"] : [];
const ratings = Arr.pluck(reviews, "rating").map(Number).filter(n => !isNaN(n));
returnData("avg_rating", ratings.length ? Num.round(Arr.avg(ratings), 2) : null);
returnData("review_count", ratings.length);
Pick the most recent connected record
// Input: notes (connected_obj — each has `created_at`)
const notes = Array.isArray(params["notes"]) ? params["notes"] : [];
const latest = Arr.sortBy(notes, "created_at").pop();
returnData("latest_note", latest ? latest.body : null);
returnData("latest_note_at", latest ? latest.created_at : null);
Build a comma-separated list of related names
const items = Arr.pluck(params["tags"] || [], "name");
returnData("tag_list", items.length ? items.join(", ") : "—");
Roll up an order's line items into totals
// Inputs: line_items (connected_obj with price & qty), tax_rate (custom_val 0.0875)
const items = Array.isArray(params["line_items"]) ? params["line_items"] : [];
const rate = Number(params["tax_rate"]) || 0;
const subtotal = Arr.sum(items.map(i => (Number(i.price) || 0) * (Number(i.qty) || 1)));
const tax = Num.round(subtotal * rate, 2);
const total = Num.round(subtotal + tax, 2);
returnData("subtotal", Num.round(subtotal, 2));
returnData("tax", tax);
returnData("total", total);
returnData("line_count", items.length);
Count distinct categories across connected items
const items = params["products"] || [];
const categories = Arr.unique(Arr.pluck(items, "category").filter(Boolean));
returnData("distinct_categories", categories);
returnData("category_count", categories.length);
returnData("is_multi_category", categories.length > 1);
Summarize children by status
// Roll up "tasks" connected to a project: how many are open, in progress, done?
const tasks = params["tasks"] || [];
const byStatus = Arr.groupBy(tasks, "status");
returnData("open_count", (byStatus.open || []).length);
returnData("in_progress_count", (byStatus.in_progress || []).length);
returnData("done_count", (byStatus.done || []).length);
returnData("all_done", tasks.length > 0 && (byStatus.done || []).length === tasks.length);
Project completion percentage
const tasks = params["tasks"] || [];
const done = tasks.filter(t => t.status === "done").length;
const total = tasks.length;
const pct = total === 0 ? 0 : Num.round((done / total) * 100, 1);
returnData("pct_complete", pct);
returnData("project_status", pct === 100 ? "complete" : pct > 0 ? "in_progress" : "not_started");
Find overdue children
const tasks = params["tasks"] || [];
const today = DateTime.now();
const overdue = tasks.filter(t =>
t.due_date && t.status !== "done" && DateTime.diffDays(today, t.due_date) < 0
);
returnData("overdue_count", overdue.length);
returnData("overdue_titles", Arr.pluck(overdue, "title").join("; "));
returnData("has_overdue", overdue.length > 0);
Find the largest related record
// Identify the biggest invoice attached to a customer.
const invoices = params["invoices"] || [];
const sorted = Arr.sortBy(invoices, "amount").reverse();
const biggest = sorted[0] || null;
returnData("biggest_invoice_amount", biggest ? Number(biggest.amount) : 0);
returnData("biggest_invoice_id", biggest ? biggest.id : null);
Roll up child contacts into a comma-separated email list
// Useful for "send to all attendees" without rebuilding the list manually.
const contacts = params["attendees"] || [];
const emails = Arr.unique(Arr.pluck(contacts, "email").filter(Boolean));
returnData("recipient_emails", emails);
returnData("recipient_csv", emails.join(", "));
returnData("recipient_count", emails.length);
Build a one-line summary of the relation
// "3 tasks · 2 done · 1 overdue"
const tasks = params["tasks"] || [];
const done = tasks.filter(t => t.status === "done").length;
const overdue = tasks.filter(t =>
t.due_date && t.status !== "done" && DateTime.diffDays(DateTime.now(), t.due_date) < 0
).length;
const summary = [`${tasks.length} task${tasks.length === 1 ? "" : "s"}`];
if (done) summary.push(`${done} done`);
if (overdue) summary.push(`${overdue} overdue`);
returnData("summary", summary.join(" · "));
Detect duplicate child records by a field
// Surfaces when the same email appears more than once in a list.
const contacts = params["contacts"] || [];
const seen = new Set();
const dupes = [];
for (const c of contacts) {
const key = (c.email || "").toLowerCase();
if (!key) continue;
if (seen.has(key)) dupes.push(key);
seen.add(key);
}
returnData("dupe_emails", Arr.unique(dupes));
returnData("has_dupes", dupes.length > 0);
Build a flat object that merges parent + child fields
// One row per child item, with the parent's name copied onto each.
const children = params["line_items"] || [];
const flat = children.map(c => ({
parent_id: record.id,
parent_name: record.name,
item_id: c.id,
item_name: c.name,
item_qty: Number(c.qty) || 0,
item_price: Number(c.price) || 0,
item_total: Num.round((Number(c.qty) || 0) * (Number(c.price) || 0), 2)
}));
returnData("flat_rows", flat);
returnData("row_count", flat.length);
We'd love to hear your feedback.