> 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/migration.md).

# Migrating from 1.x

Move your Apex from the Triton 1.x singleton to the 2.0 static, builder-first API — the method-by-method mapping and the one behavioral gotcha to audit.

Triton 2.0 is a breaking release, but the migration is mostly mechanical: the concepts you already know all carry over, they just wear different clothes. This page is the one place we talk about "1.x vs 2.0" — everywhere else in the docs, 2.0 is simply *Triton*. Read this once, run the find-and-replace, audit the one behavioral change that actually bites, and you're done.

## The one idea that changed

In 1.x, you logged through a **singleton**: `Triton.instance`. That instance held the shared state of your transaction — the buffer, the transaction id, your template — so everything you logged accumulated in one place.

In 2.0, there's **no instance**. You call the `Triton` class directly, and you describe each log with a **builder**:

{% code lineNumbers="true" %}

```apex
// 1.x
Triton.instance.error(TritonTypes.Area.Accounts, e);

// 2.0
Triton.error(Triton.t.area(TritonTypes.Area.Accounts).exception(e));
```

{% endcode %}

Here's the reassuring part: **all that shared state didn't go anywhere.** The buffer, the transaction id, and the template are still shared for the life of the Apex transaction — they just live as **static members on the `Triton` class** now instead of on a singleton instance. Apex statics last exactly one transaction, so you get the same "one logger, shared context" guarantee the singleton gave you, with nothing to fetch and nothing to pass around. When the transaction ends, the statics reset — no bleed between transactions.

So the shift is really two moves you'll make on every call:

1. **Drop `.instance`** — call `Triton` statically.
2. **Move the log's details onto a builder** — `Triton.t` (a fresh clone of your template) or `Triton.makeBuilder()` (a plain builder, pre-set to the `Apex` category), then chain `.area(...)`, `.summary(...)`, `.exception(...)`, and friends.

> `Triton.t` is shorthand for `Triton.fromTemplate()`. Each call hands back a fresh clone of your template with the current transaction id already stamped on it.

## Method mapping

Most 1.x calls map cleanly onto a 2.0 builder expression. The pattern is always the same: pick the level method, build from `Triton.t`, and move the old positional arguments onto builder methods.

| Triton 1.x                                                         | Triton 2.0                                                                                                                   |
| ------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
| `Triton.instance.error(area, e)`                                   | `Triton.logNow(Triton.t.area(area).exception(e))`                                                                            |
| `Triton.instance.addError(area, e)`                                | `Triton.error(Triton.t.area(area).exception(e))`                                                                             |
| `Triton.instance.error(type, area, summary, details)`              | `Triton.logNow(Triton.t.type(type).area(area).summary(summary).details(details).error())`                                    |
| `Triton.instance.addError(type, area, summary, details)`           | `Triton.error(Triton.t.type(type).area(area).summary(summary).details(details))`                                             |
| `Triton.instance.addWarning(type, area, summary, details)`         | `Triton.warning(Triton.t.type(type).area(area).summary(summary).details(details))`                                           |
| `Triton.instance.addDebug(type, area, summary, details)`           | `Triton.debug(Triton.t.type(type).area(area).summary(summary).details(details))`                                             |
| `Triton.instance.addDebug(type, area, summary, details, duration)` | `Triton.debug(Triton.t.type(type).area(area).summary(summary).details(details).attribute(TritonBuilder.DURATION, duration))` |
| `Triton.instance.event(level, type, area, summary, details)`       | `Triton.logNow(Triton.t.type(type).area(area).summary(summary).details(details).level(level))`                               |
| `Triton.instance.addEvent(level, type, area, summary, details)`    | `Triton.log(Triton.t.type(type).area(area).summary(summary).details(details).level(level))`                                  |
| `Triton.instance.addEvent(type, area, summary, details)`           | `Triton.info(Triton.t.type(type).area(area).summary(summary).details(details))`                                              |
| `Triton.instance.addDMLResult(area, results)`                      | `Triton.error(Triton.t.area(area).dmlResults(results))`                                                                      |
| `Triton.instance.addIntegrationError(area, e, request, response)`  | `Triton.error(Triton.t.area(area).exception(e).integrationPayload(request, response))`                                       |
| `Triton.instance.addLog(builder)`                                  | `Triton.log(builder)`                                                                                                        |
| `Triton.instance.setTemplate(builder)`                             | `Triton.setTemplate(builder)`                                                                                                |
| `Triton.instance.fromTemplate()`                                   | `Triton.fromTemplate()` (or just `Triton.t`)                                                                                 |
| `Triton.instance.flush()`                                          | `Triton.flush()`                                                                                                             |
| `Triton.instance.startTransaction()`                               | `Triton.startTransaction()`                                                                                                  |
| `Triton.instance.resumeTransaction(id)`                            | `Triton.resumeTransaction(id)`                                                                                               |
| `Triton.instance.stopTransaction()`                                | `Triton.stopTransaction()`                                                                                                   |

The transaction and template methods are the easy ones — same names, just without `.instance`. The logging methods are where the shape changes, and where the one real gotcha lives.

The `event` rows deserve a word: **`event`/`addEvent` map to a Level, not a category.** In 1.x they stamped an `Event` category on the log; in 2.0 that category is gone, so the mapping simply omits `.category(...)` and lets the builder's `Apex` default apply — these are Apex APIs, so categorize by the call site, and set `.category(...)` only when that call site genuinely bridges another technology. The level follows the overload: the forms that took a `level` keep it via `.level(level)`, the four-argument `addEvent` (no level) becomes `Triton.info(...)`, and the bare `event` published immediately, so it goes to `Triton.logNow(...)` like the other bare methods. The same goes for any legacy severity-shaped categorization: a "debug" log is a `DEBUG`-**Level** log (`Triton.debug(...)`), a "warning" is `Triton.warning(...)` — the `addWarning`/`addDebug` rows above already land there.

## The behavioral change to audit for

This is the part that can quietly change what your code does, so read it carefully.

**In 1.x, the method name decided when the log published:**

* Bare methods — `error(...)`, `warning(...)`, `debug(...)` — published **immediately**.
* `add*` methods — `addError(...)`, `addWarning(...)`, `addDebug(...)` — **buffered**, waiting for a `flush()`.

**In 2.0, the level methods all buffer.** `Triton.error(...)`, `Triton.warning(...)`, `Triton.info(...)`, and the rest add to the buffer and wait for a flush. You opt *into* immediate publishing explicitly with **`Triton.logNow(...)`**.

That inversion is why the mapping table sends the old **bare** methods (`error`, `warning`, …) to **`Triton.logNow(...)`** and the old **`add*`** methods to the **level methods** (`Triton.error`, …).

{% hint style="warning" %}
**Audit every old bare-method call.** If you had code relying on `Triton.instance.error(...)` persisting a log the instant it ran — before a later exception, a callout, or a governor limit could kill the transaction — the naive rename to `Triton.error(...)` will now **buffer** that log instead. If nothing flushes before the transaction dies, the log is lost. Either switch those to `Triton.logNow(...)`, or make sure there's a reliable `Triton.flush()` (ideally in a `finally` block) covering them.
{% endhint %}

The flip side is a nice win: buffering is now the default, so routine progress logs batch up cheaply and you only pay the platform-event cost when you deliberately flush. See [Buffering, Flushing & Deferred Publishing](/core-concepts/buffering-flushing.md) for the full model.

## Prefer `.exception(e)` over hand-set fields

In 1.x it was common to spell out an error's type, summary, and stack trace by hand. In 2.0, let the builder do it. **`.exception(e)`** pulls the type from the exception (falling back to `Backend`), sets the level to `ERROR`, copies the message into the summary, captures the stack trace and details, and flags the log for issue creation — all in one call:

{% code lineNumbers="true" %}

```apex
// Don't hand-assemble the error
Triton.error(Triton.t
    .area(TritonTypes.Area.OpportunityManagement)
    .type(TritonTypes.Type.Backend)
    .summary(e.getMessage())
    .details(e.getStackTraceString()));

// Do this instead
Triton.error(Triton.t
    .area(TritonTypes.Area.OpportunityManagement)
    .exception(e));
```

{% endcode %}

While you're migrating, this is a good moment to collapse those hand-built error logs down to `.exception(e)`. Fewer lines, and you pick up issue-creation flagging for free. Same idea for bulk DML: reach for `.dmlResults(results)` instead of assembling failure summaries yourself.

## Use real enum values

Migrating is a good time to make sure every `Category`, `Type`, and `Area` you pass is an actual `TritonTypes` enum value rather than an ad-hoc string that drifted in over time. Consistent values are the whole point of the taxonomy — filters and dashboards only line up when everyone uses the same names.

{% hint style="warning" %}
**`TritonTypes.Category.Integration` no longer exists in 2.0** — Apex referencing it won't compile. Drop the `.category(TritonTypes.Category.Integration)` call and keep `.integrationPayload(request, response)`: the builder defaults to the `Apex` category, and these logs now also capture the Apex execution context. (The Flow and LWC bridges still accept the string `'Integration'` and quietly map it to that context's default category.) See [Integration Logs](/common-apex-usage-patters/integration-logs.md) for the current pattern.
{% endhint %}

{% hint style="warning" %}
**The `Warning`, `Event`, and `Debug` categories no longer exist in 2.0** — Apex referencing `TritonTypes.Category.Warning`, `.Event`, or `.Debug` won't compile. Category now answers only "what technology produced this?" (`Apex` · `Flow` · `LWC` · `Aura`); severity belongs in the log **Level**. Deployed Flows and LWCs still passing the old strings keep logging — they're re-categorized to `Flow`/`LWC` with a diagnostic note appended to Details. If you have `Log_Level__mdt` rules keyed on Category = `Event`/`Warning`/`Debug`, re-key them to a real origin Category or to a Level — they stop matching logs from upgraded producers. Historical logs keep their stored category strings.
{% endhint %}

The shipped `TritonTypes.Area` values are:

`OpportunityManagement` · `LeadConversion` · `Community` · `RestAPI` · `Accounts` · `LWC` · `Flow`

So use the real thing:

{% code lineNumbers="true" %}

```apex
Triton.error(Triton.t.area(TritonTypes.Area.OpportunityManagement).exception(e));
Triton.info(Triton.t.area(TritonTypes.Area.Accounts).summary('Rollups recalculated'));
Triton.warning(Triton.t.area(TritonTypes.Area.RestAPI).summary('Callout retried'));
```

{% endcode %}

`Area` and `Type` are meant to grow with your org — but **don't extend the shipped enums to do it.** If you were passing ad-hoc strings like `'InvoiceSync'`, promote them to `public static final String` constants in your own classes and pass those; editing `TritonTypes.cls` gets clobbered on every Triton upgrade. See [Custom Classifications](https://github.com/Pharos-AI/triton-docs/tree/release/2.0/core-concepts/custom-classifications.md) for the upgrade-safe pattern, and [Enums](/core-concepts/enums.md) for the built-in value sets.

## Migration checklist

1. **Replace `Triton.instance.<method>(...)`** with the static, builder-first equivalents from the mapping table above.
2. **Audit old bare methods** (`error`, `warning`, `debug`, …) that relied on immediate publishing — send them to `Triton.logNow(...)` or ensure a `flush()` covers them.
3. **Confirm `flush()` coverage.** Now that level methods buffer, make sure every code path that logs also flushes — end of a trigger handler, end of a batch `execute`, in `finally` blocks.
4. **Collapse hand-built error logs** to `.exception(e)`, and bulk-DML logs to `.dmlResults(...)`.
5. **Set a template early** with `Triton.setTemplate(...)` and log from `Triton.t`, so shared fields like category and area stop repeating.
6. **Swap ad-hoc strings for stable values** — the built-in `TritonTypes` enums where they fit, and your own `String`-constant classes (see [Custom Classifications](https://github.com/Pharos-AI/triton-docs/tree/release/2.0/core-concepts/custom-classifications.md)) for org-specific Type/Area values. Don't edit the shipped enums.
7. **Re-assign the permission sets** (`Triton_Read` / `Triton_Write`) after deploying so the new fields are visible — see the [2.0 release notes](/release-notes/2.0.md).

Once you're through the list, delete this page from your mental model: everything else in the docs is written for 2.0, and 2.0 is just *Triton* now. Start with [Core Concepts](/core-concepts.md).
