> 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/common-apex-usage-patters/apex-rest-logging.md).

# Apex REST Logging

Logging an inbound REST request at your custom Apex endpoint — capture the payload, open a transaction, and thread its id through everything downstream.

[Integration Logs](/common-apex-usage-patters/integration-logs.md) covered the *outbound* case — Salesforce calling someone else. This is the mirror image: an external system calling a custom REST endpoint you expose with `@RestResource`. Same payload-capture idea, pointed the other way.

Your goal at the front door is threefold: **log the incoming request** (with its payload), **open a transaction** so everything the request triggers is grouped together, and **pass that transaction id downstream** so async work correlates back to the original call.

{% code lineNumbers="true" %}

```apex
@RestResource(urlMapping='/Account/*')
global with sharing class MyRestResource {

    @HttpPost
    global static String doPost(String name, String phone, String website) {
        String transactionId = Triton.startTransaction();
        RestRequest req = RestContext.request;
        RestResponse res = RestContext.response;

        // A plain Apex log for the inbound request — with the payload attached
        Triton.log(Triton.t
            .area(TritonTypes.Area.Accounts)
            .summary('Inbound account creation request')
            .details(String.format('POST received with name={0}, phone={1}, website={2}',
                new String[]{ name, phone, website }))
            .integrationPayload(req, res));
        Triton.flush();

        try {
            Account account = new Account(Name = name, Phone = phone, Website = website);

            // Hand the transaction id to downstream async work so its logs correlate
            AccountProcessing.doAsyncProcessing(account, transactionId);

            insert account;
            return account.Id;
        } catch (Exception e) {
            Triton.logNow(Triton.t
                .area(TritonTypes.Area.Accounts)
                .exception(e));
        }
        return null;
    }
}
```

{% endcode %}

What each move buys you:

* **`Triton.startTransaction()`** returns a fresh id and opens the correlation scope. The first log becomes the parent; everything after nests under it.
* **`.integrationPayload(req, res)`** captures the inbound `RestRequest`/`RestResponse` for the payload viewer — the same tool you get on outbound integration logs.
* **Passing `transactionId` to `AccountProcessing.doAsyncProcessing(...)`** lets that code call `Triton.resumeTransaction(transactionId)` on the other side, so its logs join this same tree even though they run in a separate Salesforce transaction.
* **`.exception(e)`** on the catch publishes the failure immediately with `logNow`, flagged for issue creation.

## The resulting log structure

Say the `insert` throws a `DmlException`, and `doAsyncProcessing()` does some logging of its own. You get one clean tree:

* **Parent** — Apex log for the inbound request, with the payload attached
  * **Child** — Apex error log from the try/catch
  * **Children** — any logs emitted inside `AccountProcessing.doAsyncProcessing()`

{% hint style="info" %}
The order of the async children depends on how quickly Salesforce schedules `AccountProcessing` — Queueable vs. Batch, and whether it runs right away. Grouping by transaction id keeps them together regardless of timing.
{% endhint %}

Threading a transaction id from an entry point through downstream Apex, async jobs, and even LWC or Flow is the backbone of end-to-end tracing. For the full picture — including span-based tracing and platform-cache-backed correlation — see [Transactions & Cache](/end-to-end/transactions-and-cache.md) and [End-to-End Logging](/end-to-end.md).
