# Automate Rilldata Gmail CSV Reports into Google Sheets with Make.com (Incrementally)

---

Many ad platforms and analytics tools can email you a scheduled CSV report, but they won't write it to a spreadsheet for you. If you build dashboards in Looker Studio or Power BI, you want that data sitting in Google Sheets, growing each day, without duplicates.

In this post I'll show how I built a [Make.com](http://Make.com) scenario that:

- Finds a scheduled report email in Gmail by sender and subject
- Downloads the CSV attachment
- Keeps only the rows for one account (filtering on column A, *Account Name*)
- Compares the rows to what's already in the sheet
- **Adds new rows and updates changed rows**, so re-running never creates duplicates

## The problem with "just append"

The obvious approach is: email arrives, parse CSV, add every row. It breaks quickly:

- Scheduled reports often **overlap**. Today's file repeats yesterday's dates.
- Platforms **restate numbers** for recent days, so an old row can come back with different values.
- Appending everything gives you duplicates and wrong totals.

So the scenario needs to be *incremental*: add what's new, update what changed, ignore what's identical.

## The approach: a signature column

The trick is a helper column in the sheet, which I call **Signature**. For every row it stores all the values joined into one string:

```plaintext
[Account|Campaign|2026-11-01|1200|34|...]
```

Then the scenario compares each incoming CSV row to the signatures already in the sheet:

| Situation | What it means | Action |
| --- | --- | --- |
| The first three fields (the *key*) are not found in any signature | Brand-new row | **Add a row** |
| The key is found, but the full signature is different | Numbers were restated | **Update the row** |
| The full signature is found | Nothing changed | **Do nothing** |

The key is the combination of fields that uniquely identifies a row. For a daily campaign report that is usually *Account + Campaign + Date*. Choose whatever is unique in your report.

## Prepare the Google Sheet

1. Create a tab (for example `ReportData`).
2. Put your CSV headers in row 1, in the same order as the CSV columns.
3. In the column right after your data, add the header **Signature**. For a 15-column CSV that's column **P**.

> **Don't skip the Signature header.** If the cell is empty, Make can't resolve that column in its filter and you'll get an error like `Unable to parse range ... undefined2:undefined1000000`.

## The scenario, module by module

The scenario uses only native modules, with no iterator or custom code.

![](https://cdn.hashnode.com/uploads/covers/6af6a1bd8517d1ad2006baa1/adb40ea2-1fbc-40cb-b179-47faf10df8c1.png align="center")

### 1\. Gmail: Search emails

Use a Gmail search query to find the latest matching email:

```plaintext
from:noreply@example.com
subject:"Your Scheduled Report Name"
has:attachment
filename:csv
```

Set **Limit** to `1` so you only process the latest report. The `subject:"..."` form with quotes matches any subject that *contains* that phrase.

### 2\. Gmail: List email attachments

- **Message ID:** `{{1.id}}` from the previous module
- **Include:** attachments
- **Return attachment data:** Yes

Add a filter on the link that only continues when the message ID exists, so the scenario stops cleanly when no email is found.

### 3\. Google Sheets: Search Rows

Read the existing rows from your tab, with no filter. This is only used to collect the Signature column.

### 4\. Tools: Text aggregator

Feed it from the Search Rows module and map the **Signature** column as the value. Use a newline as the row separator. The output is a single text block containing every signature already in the sheet.

### 5\. CSV: Parse CSV

- **CSV:** the attachment data from step 2
- **Delimiter:** comma
- **Contains headers:** Yes

The parsed fields come out as `col1`, `col2`, `col3`, and so on, one bundle per CSV row.

### 6\. Router: two routes

Both routes start with the same filter, so only your chosen account gets through:

```plaintext
{{col1}}  Equal to (case insensitive)  Your Account Name
```

**Route A: new rows**

Filter (all conditions must be true):

- `col1` equals your account name
- the text aggregator output **does not contain** `[{{col1}}|{{col2}}|{{col3}}|`

Then add a **Google Sheets: Add a Row** module. Map `col1`...`col15` to columns A to O, and map this to the Signature column:

```plaintext
[{{col1}}|{{col2}}|...|{{col15}}]
```

**Route B: changed rows**

Filter:

- `col1` equals your account name
- the aggregator output **contains** `[{{col1}}|{{col2}}|{{col3}}|`
- the aggregator output **does not contain** the *full* signature

Then two modules:

1. **Google Sheets: Search Rows** with a filter on the Signature column containing the key prefix, and limit `1`. This finds the row number to update.
2. **Google Sheets: Update a Row** with the row number from the search, writing the new values and the new signature.

## Gotchas I hit

- **"Module Not Found" after importing a blueprint.** My first version used an Iterator on the attachments array. Module names and versions in a hand-written blueprint have to match what your Make region actually offers. The fix was to copy the module identifiers from a working scenario and use *List email attachments* directly.
- `undefined2:undefined1000000` **range error.** This came from the Signature column having no header, so the filter could not resolve it. Add the header, then reselect the column in the filter dropdown.
- **Reselect connections after importing.** A blueprint carries connection IDs from the account it was exported from. If Make doesn't recognise them, pick your own Gmail and Google connections in each module.
- **Prefer the dropdown over typed IDs.** If a Sheets module complains, choose the spreadsheet and sheet by name from the dropdown instead of pasting an ID.
- **Pick a real unique key.** If two different rows share the same first three fields, one will keep overwriting the other. Add more fields to the key if needed.

## Scheduling

Set the scenario to run on a schedule that matches your report. If the report arrives once a day, running every 15 minutes wastes operations, since the key check will skip identical rows anyway. A daily run an hour or two after the email normally arrives is enough.

## Testing checklist

1. Run once with the email in your inbox and confirm rows are added.
2. Run it again. Nothing should change, and no duplicates should appear.
3. Edit one number in the Sheet's Signature-tracked row, or wait for a restated report, and confirm the **Update** route fires.
4. Send a test email with a different account name and confirm its rows are ignored.

## Wrapping up

With a Signature column and a three-way comparison, a plain "email to sheet" automation becomes a reliable incremental data pipeline: no duplicates, restated numbers handled, and only the account you care about in the sheet. From there, connect the tab to Looker Studio, Power BI, or any dashboard you like.

If you adapt this for your own reports, the two decisions that matter most are your **key fields** and your **account filter**. Get those right and everything else is plumbing.
