# Cell References

A **cell reference** is a `{{Tab!Ref}}` placeholder you put inside an `api`
source's request. When the automation runs, Sheetgo reads that cell from the
automation's Google Sheets destination and substitutes the value before the
request goes out — so the request changes as your sheet changes, with no API
call needed.

```
GET https://api.example.com/events?since={{Log!A:last|date|fmt:iso_dt}}
```

## Which `{{ }}` am I looking at?

Three unrelated syntaxes share the double-brace shape. They are not
interchangeable.

| Syntax | Example | Where it works |
|---|---|---|
| **Cell reference** | `{{Sheet1!A1\|date\|fmt:iso_d}}` | `api` source — `endpoint`, `headers`, `body` |
| **Smart tag** | `{{Customer}}` | `document`, `share`, `mail_merge` destinations |
| **Body template** | `{{DATA}}`, `{{for row in ROWS}}` | `webhook` destination `body` |

A cell reference always contains a `!`. A smart tag is a bare column name. The
body template uses the reserved names `DATA` and `ROWS` and the block tags
`{{for}}`, `{{if}}` and `{{except-last}}`.

## Grammar

```
{{ <tab>!<ref> [|<filter>]* }}
```

* **`<tab>`** — a tab in the destination spreadsheet. Quote it with single
  quotes if it contains a space, a special character, or starts with a digit:
  `{{'My Sheet'!A1}}`, `{{'2026 Sales'!A1}}`. Double an apostrophe inside a
  quoted name: `{{'O''Brien'!C3}}`. When in doubt, quote it — the quoted form
  always works.
* **`<ref>`** — a single cell (`A1`, `B7`, `AB42`) or `A:last`, the last
  non-empty cell in column A. Case-insensitive. Ranges (`A1:B5`), whole columns
  (`A:A`), whole rows, named ranges and formulas are **not** supported.
* **`|<filter>`** — optional, chainable, applied left to right.

## Filters

**Render mode** — must be the *first* filter if used. Default is `F`.

| Filter | Value you get | Cell displaying `$1,234.50` |
|---|---|---|
| `F` *(default)* | the formatted display text | `"$1,234.50"` |
| `U` | the raw underlying value | `1234.5` |

`F` and `U` after another filter are invalid, and the placeholder is left as
literal text.

**String** — `trim` (strip whitespace), `upper`, `lower`.

**Numeric** — `int`, `float`. Both **fail the run** on a non-numeric value.

**Date input** — promote the value to a date, always doing work:

| Filter | Behaviour |
|---|---|
| `date` | auto-detect, US-leaning on ambiguous input |
| `date_us` | month first — `01/02/2026` is 2 January |
| `date_eu` | day first — `01/02/2026` is 1 February |
| `date_iso` | strict ISO 8601 only |

Accepts strings, Sheets serial numbers, and existing dates. On its own a date
filter renders ISO datetime; chain a `fmt:*` to change that.

**Date output** — `fmt:*`, a **no-op unless a date input filter came first**:

`fmt:day`, `fmt:month`, `fmt:year`, `fmt:timestamp` (Unix seconds),
`fmt:timestamp_ms` (Unix milliseconds), `fmt:serial` (Sheets serial),
`fmt:iso_dt`, `fmt:iso_d`, `fmt:us_dt`, `fmt:us_d`, `fmt:eu_dt`, `fmt:eu_d`.
`_dt` includes the time, `_d` is date only.

## Where they go

| Location | Substitution |
|---|---|
| `endpoint` | URL-encoded per component. Keep placeholders out of the scheme and host. |
| `headers` | verbatim, with CR/LF stripped |
| `body` | **verbatim, no escaping** — you are responsible for valid JSON. `"{{A1}}"` yields a quoted string; `{{A1\|U}}` unquoted yields a bare JSON value. |

## When something goes wrong

| Situation | Behaviour |
|---|---|
| Invalid syntax, unknown filter, unbalanced braces | treated as **literal text** — your API receives the raw `{{...}}` |
| Tab does not exist | run **fails**, naming the placeholder and its location |
| Empty cell, out-of-bounds cell, `:last` on an empty column | substituted as `""`, warning logged, run continues |
| Numeric or date conversion fails | run **fails** |
| Network failure reading the sheet | retried 3× with backoff, then the run fails |

## ⚠️ Requires a Google Sheets destination

Substitution reads from the automation's destination, so it only happens when
that destination is a Google Sheets file. With any other destination the
placeholders are sent to your API as literal text, with no warning.

## ⚠️ Never resolves alongside a `webhook` destination

A `webhook` destination must be an automation's only destination, so such an
automation has no Sheets destination to read from. Cell references in it will
always be sent literally. Pair cell references with a `file` destination
instead.


---

More: start page https://dev-api.sheetgo.com/rest/reference.md, everything in one file https://dev-api.sheetgo.com/rest/llms-full.txt, OpenAPI https://dev-api.sheetgo.com/rest/openapi.json.
