# File Structure

A Transformation is made up of six files — three that run on the edge and three that run in the browser. Each file is optional; only the ones you fill in are invoked.

{% code title="Transformation layout" %}

```
playground/
├── edge/
│   ├── tagRoot        runs on the CDN edge once per event
│   ├── tagChannel     runs on the CDN edge per provider
│   └── tagInstance    runs on the CDN edge per provider instance
└── browser/
    ├── clientTagRoot      runs in the browser once per event
    ├── clientTagChannel   runs in the browser per provider
    └── clientTagInstance  runs in the browser per provider instance
```

{% endcode %}

### Edge plugin levels

Edge files run on the EdgeTag CDN edge (Cloudflare Workers), before any channel receives the event. They have the full server-side context — PII on `params.user`, host data, paid-media attribution via `params.userKey`, and your [Secrets](/playground/transformation/secrets.md).

* **`tagRoot`** — runs **once per event**, before any channel routing. Use for rules that apply to every channel: drop an event everywhere, enrich every payload, and emit an extra event based on attribution.
* **`tagChannel`** — runs **once per provider channel**. Receives `params.providerId` (for example `facebook`, `klaviyo`, `tiktok`). Use for provider-specific rules: tweak the payload only when it's going to Facebook.
* **`tagInstance`** — runs **once per provider instance**, the most granular edge level. Also receives `params.providerId`. Use when you have multiple pixels or accounts inside a single provider and need different behavior for each.

### Browser plugin levels

Browser files run in the visitor's browser before the channel's own browser tag fires. They can skip a provider's browser execution, enrich the payload with browser-only data (like `window.innerWidth` or `document.referrer`), or shape data before it reaches the channel.

* **`clientTagRoot`** — runs **once per event** in the browser, before any provider tag. Use for browser-side global rules.
* **`clientTagChannel`** — runs **once per provider channel** in the browser. Receives `params.providerId`.
* **`clientTagInstance`** — runs **once per provider instance** in the browser. Also receives `params.providerId`.

### What each file receives

| File                | Side    | `providerId`? | Params shape                                                                                                  |
| ------------------- | ------- | :-----------: | ------------------------------------------------------------------------------------------------------------- |
| `tagRoot`           | edge    |       —       | `payload`, `user`, `variables`, `platform`, `userCustomData`, `hostData`, `logger`, `userKey`, `providerData` |
| `tagChannel`        | edge    |       ✓       | same as `tagRoot`, plus `providerId`                                                                          |
| `tagInstance`       | edge    |       ✓       | same as `tagChannel`                                                                                          |
| `clientTagRoot`     | browser |       —       | `payload`, `user`, `settings`, `variables`                                                                    |
| `clientTagChannel`  | browser |       ✓       | same as `clientTagRoot`, plus `providerId`                                                                    |
| `clientTagInstance` | browser |       ✓       | same as `clientTagChannel`                                                                                    |

See [Code](/playground/transformation/code.md) for the full reference.

### Choosing the right level

Start from what you're trying to do:

* **A rule that applies to every channel** → `tagRoot` (or `clientTagRoot` for browser). You usually only need one root file.
* **A rule tied to a specific provider** → `tagChannel`. Check `params.providerId`.
* **A rule tied to a specific pixel/account inside a provider** → `tagInstance`.

It's common to have only one of these files filled in. Fan out across multiple levels only when you genuinely need different rules at different granularities.

### Enabled stages

Empty files are skipped entirely — they aren't registered and don't add latency. You can ship a Transformation with only `tagRoot` filled in, and add other levels later as your rules grow.

{% hint style="info" %}
Playground wraps your plugin code into a registered plugin object on save. You don't write wrappers, imports, or exports — only the function bodies.
{% endhint %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.edgetag.io/playground/transformation/file-structure.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
