> For the complete documentation index, see [llms.txt](https://triton.pharos.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://triton.pharos.ai/flows.md).

# Logging in Flow

Add structured logging to Salesforce Flows with Triton's invocable Log action — no code required, with deferred batching to protect your platform-event budget.

Flows are where a lot of business logic lives these days — and where a lot of it silently breaks. A screen flow fails three steps in, or an autolaunched flow makes a bad decision, and all you have is a terse debug email. Triton brings the same structured logging you use in Apex to Flow, entirely through a **no-code invocable action**.

{% hint style="info" %}
Screen flows run in user context, so anyone running one needs the `Triton End User` permission set to use the Log action. Record-triggered and autolaunched flows run in system context by default and need no permission set at all. See [Configure Permissions](/installing-pharos-triton.md#configure-permissions) for the full model.
{% endhint %}

## The Log action

Triton ships an invocable Apex action — labeled **Log**, under the **TritonLogging** category — that you drop into any Flow. It builds a proper `pharos__Log__c` record from parameters you set right in Flow Builder. No Apex, no formulas — just an action element.

Let's walk through a real example: a **Partner Intake** screen flow that captures form data, assigns a lead score by partner type, and creates a lead.

## Step 1 — Log first, log early

Make a logging action the **very first element** in the flow. Two reasons: you always get a record even if the flow blows up downstream, and — crucially — that first log becomes the **parent** that every later log nests under.

1. **Click (+)** and drop an **Action** element onto the canvas.
2. **Search** for "triton" and select the **Log** action.

## Step 2 — Configure the parameters

Give the action a clear label (e.g. "Log Partner Intake Start") and set its parameters.

| Parameter                        | Required | What it does                                                                                                                                                        |
| -------------------------------- | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Area**                         |     ✅    | Functional area from a business perspective — `Accounts`, `LeadConversion`, etc. See [Enums](/core-concepts/enums.md).                                              |
| **Summary**                      |     ✅    | The one-line headline for the record.                                                                                                                               |
| **Interview GUID**               |     ✅    | The running Flow instance id. **Always pass `$Flow.InterviewGuid`** (see below).                                                                                    |
| **Category**                     |          | Leave as `Flow` for most logging. Drives how Pharos renders the record; invalid values fall back to `Flow`.                                                         |
| **Type**                         |          | Technical classification — e.g. `Screen Flow`, `Autolaunched Flow`.                                                                                                 |
| **Details**                      |          | The long form: record ids, decision variables, user inputs — anything that explains what happened.                                                                  |
| **Level**                        |          | Severity (see [Log Levels](/core-concepts/log-levels.md)). Defaults to `INFO`; invalid values fall back to `INFO`. An `ERROR` level automatically creates an issue. |
| **Flow API Name**                |          | The **plain** API name of this flow — no version suffix.                                                                                                            |
| **Flow Version**                 |          | This flow's version number, e.g. `4`. Stored separately, in `pharos__Flow_Version__c`.                                                                              |
| **Operation**                    |          | Optional name of the operation being performed.                                                                                                                     |
| **Transaction ID**               |          | Pass an existing id to **resume** a transaction and group these logs with logs from another context.                                                                |
| **Additional Fields**            |          | A JSON map of extra field values — see [below](#pass-extra-fields-with-additional-fields).                                                                          |
| **Stacktrace / Full Stacktrace** |          | Supplied automatically by the X-Ray machine; you don't set these by hand.                                                                                           |

### Always pass `$Flow.InterviewGuid`

The **Interview GUID** is the single most important parameter after Area and Summary. It's the id of the running Flow instance, and passing `$Flow.InterviewGuid` lets Pharos link your custom logs to Salesforce's own Flow diagnostics — enriching every record with:

* stack traces for error tracking,
* user information,
* related object details,
* installed package information,
* pending jobs in the flow queue,
* total active session information.

Make it a rule: every Log action gets `$Flow.InterviewGuid`.

### Flow API Name and Flow Version

Pass these as two parameters, not one: the **plain** API name in **Flow API Name**, and the version number on its own in **Flow Version**. That's the same split Salesforce's own Flow error logs use, and Triton stores the version in `pharos__Flow_Version__c`.

Keeping the version out of the name matters for filtering. Component-scoped [`Log_Level__mdt`](/core-concepts/log-levels.md) rules match the Flow API Name in full — case-insensitively, but otherwise exactly — so a rule naming `My_Flow` never matches a log whose Flow API Name is `My_Flow-4`.

Flow hands **Flow Version** over as text and Triton parses it into a number. A value it can't parse never fails the log: Triton appends `Unable to parse flow version: <value>. Flow Version will not be set.` to **Details** and saves the log without the version.

### Use constants for the repeated parameters

`Area`, `Category`, and `Type` are the same across most of your log actions. Define them once as **constants** and reference them everywhere — change the value in one place instead of hunting through every action.

Even a 4-action flow benefits: without constants, changing the Area means editing four elements; with constants, one. Adopt a naming convention — prefix log resources with `Log_` — so they're easy to filter in the toolbox.

### Use text templates for details

The **Details** parameter is your troubleshooting lifeline, so make it rich: record ids, decision variables, user inputs, anything that shaped the flow's path. **Text templates** are the right tool for mixing static text with dynamic flow data.

{% hint style="info" %}
Prefix detail templates with `Log_Details_` and use the **Plain Text** view — rich-text formatting is irrelevant here and just gets in the way.
{% endhint %}

## Step 3 — Log throughout the flow

With the opening log in place, add more at the moments that matter:

1. **Flow start** — the initiation and its input parameters.
2. **User interactions** — form submissions, screen inputs, decisions.
3. **Data operations** — before and after DML, callouts, transformations.
4. **Decision points** — which branch the flow took.
5. **Fault paths** — detailed logging wherever a fault connector lands.

{% hint style="warning" %}
**Do** build a fresh Log action each time. **Don't** clone-and-paste existing ones — cloning drags along confusing element names and stale parameter mappings that cause subtle, hard-to-spot bugs.
{% endhint %}

## Step 4 — Read the output

Run the flow (ours is rigged to fail at lead creation) and open the **Partner Intake Flow Start** log. Because every action passed `$Flow.InterviewGuid`, the whole run is grouped under that first parent log:

Click any entry to see the summary, details, and custom context you configured:

{% hint style="info" %}
Notice the final **Error Occurred** entry — *you didn't create that one*. Triton automatically captures unhandled Flow exceptions and files them under your trace, giving you the failure alongside your own breadcrumbs with zero extra configuration.
{% endhint %}

Open that error and you land in the standard Pharos Flow debug view — the flow's state, error details, and decision pathways, all in one place:

## Pass extra fields with Additional Fields

Need to set fields the standard parameters don't cover? Build a JSON map in a text template and pass it to **Additional Fields**:

<figure><img src="https://3094374025-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFlq7dXDt5Z9LFxymJV0A%2Fuploads%2Fgit-blob-e73153619897833b9f50e9a66bdb0816da2127c3%2FScreenshot_2024-09-08_at_5_06_01_PM.png?alt=media" alt=""><figcaption><p><em>A well-formed JSON object of extra field values.</em></p></figcaption></figure>

Each key is a field API name, each value the value to set. Malformed JSON won't break your flow — Triton catches the parse failure and appends a note to the details instead of throwing.

## Deferred flush & batching

Here's something Flow logging does for you quietly. Flows can run at enormous volume, and publishing a platform event per log would torch your allocations. So instead of publishing inline, the Log action **drains** its buffered logs and hands them to a batch (`TritonFlowFlushBatch`) that publishes them together, slightly later, off the hot path.

The upshot: your Flow logs are **not** published the instant the action runs — they're buffered in Org Cache and flushed by a short-delayed batch that coalesces logs across concurrent Flow runs into as few platform events as possible. You don't configure any of this; it's automatic, and it's why heavy Flow logging stays cheap. (The underlying `drain()` mechanism is described in [Buffering, Flushing & Deferred Publishing](/core-concepts/buffering-flushing.md).)

## Best practices

* **Log first, log at every critical point** — start, user input, before/after data ops, decisions, and every fault path.
* **Always pass `$Flow.InterviewGuid`** — it's what enables grouping and automatic error enrichment.
* **Use constants** for `Area`, `Category`, `Type`; **use text templates** (`Log_Details_…`) for details.
* **Name consistently** — prefix actions, constants, and templates with `Log_`.
* **Set `ERROR` on fault paths** so failures create issues automatically.
* **Reuse a Transaction ID** when a Flow is part of a larger process — resume the id from Apex or LWC to fold the Flow's logs into one cross-technology trace.

## Where to go next

A Flow rarely runs in isolation — it's often kicked off by Apex, which was kicked off by an LWC. To trace a whole journey across all three layers as one story, see [End-to-End Logging](/end-to-end.md).
