Overview
ctx.thisRow provides convenient methods to read and write data on the currently executing row. This is the most common way to interact with your data within column formulas.
ctx.thisRow is not available in webhook columns, as there is no “current row” context during webhook
execution.
Methods
get()
Get a value from the current row by column name.
ctx.thisRow.get(columnName: string): any
Parameters:
columnName - The name of the column to read from
Returns: The value stored in that column for the current row
Example:
const email = ctx.thisRow.get("email");
const companyName = ctx.thisRow.get("company");
Foreign Key Navigation
For foreign key columns, use the referenced sheet name instead of the column name, and use dot notation to access fields on the related record:
// If "company_id" is a foreign key to the "Companies" sheet
const companyName = ctx.thisRow.get("Companies.name");
const companyWebsite = ctx.thisRow.get("Companies.website");
// You can chain through multiple relationships
const ownerEmail = ctx.thisRow.get("Companies.Owner.email");
set()
Set one or more values on the current row.
ctx.thisRow.set(values: Record<string, any>): void
Parameters:
values - An object mapping column names to new values
Example:
// Set a single value
ctx.thisRow.set({ status: "processed" });
// Set multiple values at once
ctx.thisRow.set({
status: "enriched",
lastUpdated: new Date().toISOString(),
score: 95,
});
Using ctx.thisRow.set() with multiple fields is more efficient than creating separate “helper columns” for each
intermediate value.
run()
Manually trigger execution of the current row’s formulas.
Example:
// After updating values, re-run formulas
ctx.thisRow.set({ input: "new value" });
ctx.thisRow.run();
Get all rows from a related sheet that have a foreign key pointing to the current row.
ctx.thisRow.getRelatedRows(sheetName: string): Row[]
Parameters:
sheetName - The name of the sheet to query for related rows
Returns: Array of Row objects that reference the current row
Example:
// Get all jobs posted by this company
const jobs = ctx.thisRow.getRelatedRows("Jobs");
// Calculate metrics
const openJobs = jobs.filter((job) => job.get("status") === "open");
const totalApplicants = jobs.reduce((sum, job) => sum + job.get("applicant_count"), 0);
ctx.thisRow.set({
open_jobs_count: openJobs.length,
total_applicants: totalApplicants,
});
Row Object
When you work with rows (from getRelatedRows or other methods), each Row object has the following properties and methods:
Properties
row.id - UUID of the row
row.index - Numeric index of the row (0-based)
row.wasCreated - Boolean indicating if this row was newly created (via addRow)
row.wasUpdated - Boolean indicating if this row was updated (via addRow with unique column match)
Methods
row.get(columnName) - Get a value from this row
row.set(values) - Set values on this row
row.run(options?) - Trigger execution of this row’s formulas
options.excludeColumns - Array of column names to skip during execution
Example:
const relatedJobs = ctx.thisRow.getRelatedRows("Jobs");
for (const job of relatedJobs) {
console.log(`Job ${job.index}: ${job.get("title")}`);
// Update the job
job.set({ lastChecked: new Date().toISOString() });
// Re-run formulas, but skip expensive columns
job.run({ excludeColumns: ["ai_analysis"] });
}
Common Patterns
Conditional Enrichment Based on Lead Status
// Only enrich qualified leads
const status = ctx.thisRow.get("Status");
const companySize = ctx.thisRow.get("Employee Count");
if (status === "Qualified" && companySize >= 50) {
// Perform deep enrichment for qualified prospects
const linkedinUrl = ctx.thisRow.get("LinkedIn URL");
const data = await services.company.linkedin.enrich({
url: linkedinUrl,
enrichLevel: "extended",
});
ctx.thisRow.set({
"Status": "Enriched",
"Industry": data.industry,
"Active Jobs": data.active_job_postings_count,
"Growth Signal": data.active_job_postings_count > 10 ? "High Growth" : "Stable",
"Priority": data.active_job_postings_count > 10 ? "High" : "Medium",
});
}
// Aggregate all contacts associated with this account
const contacts = ctx.thisRow.getRelatedRows("Contacts");
// Calculate account metrics
const decisionMakers = contacts.filter((c) =>
c.get("Title")?.match(/(CEO|CTO|CFO|VP|Vice President|Director)/i)
);
const hasEmail = contacts.filter((c) => c.get("Email")).length;
const hasPhone = contacts.filter((c) => c.get("Phone")).length;
ctx.thisRow.set({
"Total Contacts": contacts.length,
"Decision Makers": decisionMakers.length,
"Contacts with Email": hasEmail,
"Contacts with Phone": hasPhone,
"Account Completeness": Math.round((hasEmail / contacts.length) * 100) + "%",
});
// Extract decision makers from company and create contact records
const companyName = ctx.thisRow.get("Company Name");
const companyLinkedin = ctx.thisRow.get("Company LinkedIn");
const employees = await services.company.getEmployees({
linkedinCompanyUrl: companyLinkedin,
});
// Filter for decision makers
const decisionMakers = employees.filter(emp =>
emp.title?.match(/(CEO|CTO|CFO|VP|Vice President|Director|Head of)/i)
);
// Create contact records for each decision maker
for (const dm of decisionMakers.slice(0, 10)) {
await ctx.sheet("Contacts").addRow({
"Name": dm.name,
"Title": dm.title,
"Company": companyName,
"LinkedIn URL": dm.linkedinUrl,
"Source": "Company Employee List",
});
}
ctx.thisRow.set({
"Contacts Created": decisionMakers.slice(0, 10).length,
"Status": "Contacts Extracted",
});