Skip to main content
Starting in 7.3.0, the iOS SDK attaches an X-Portal-Trace-Id header to every HTTP request it makes. Portal’s logs are indexed by that value, so a single trace ID lets you — and Portal support — follow one user-facing action across every request it produced.

Overview

A trace ID is a lowercased UUID v4 sent as the X-Portal-Trace-Id request header. You do not have to do anything to get one: the SDK generates a trace ID for every request. What you gain by supplying your own is correlation — the same value appears in your logs and in Portal’s, so a support ticket goes from “a signing request failed sometime Tuesday” to an exact record.

Automatic tracing

The header is added in three places inside the SDK, so every request path is covered:
iOS attaches the header unconditionally — there is no host allowlist. Requests to custom RPC endpoints you configure with withRpcConfig carry it too, because they are built with the same PortalAPIRequest type. The value is an opaque UUID and contains no client, wallet, or key data, but be aware that a third-party or self-hosted RPC provider will see it. This differs from the Android SDK, which filters the header to Portal-owned hosts.

Supplying your own trace ID

Three public entry points accept a trace ID. Wherever you leave it nil, the SDK generates one for you.

Provider requests

RequestOptions carries the trace ID for portal.request(...) and everything built on top of it:
For requests that need a signature, RequestOptions.traceId is also forwarded into MPC signing metadata as reqId. That means the same value tags both the HTTP request and the MPC operation it triggers — one lookup covers the API call and the signing work behind it. That is the payoff, and it is not something you can reconstruct after the fact.

Sending assets

SendAssetParams gained a traceId:

Raw signing

rawSign takes the trace ID as an argument. Unlike the other two, it is not defaulted — on this overload you must pass a value, even if that value is nil:
Existing calls keep compiling. PortalProtocol ships back-compatible extensions for rawSign(message:chainId:) and rawSign(message:chainId:signatureApprovalMemo:), both of which pass traceId: nil.

Multi-step flows share one ID

When an SDK operation makes several requests, all of them carry the same trace ID. This is what makes tracing worth using — you get one handle for the whole flow instead of one per request.
MPC and EIP-7702 operations do not accept a trace ID. They generate one per operation and share it across their sub-requests, but you cannot supply your own. If you need to correlate an MPC flow with your own logs, see Reading the ID back.
Generate one trace ID per user-facing action — “send USDC”, “sign in” — not per request. A trace ID that maps one-to-one onto HTTP requests tells you nothing you did not already know.

Reading the id back

PortalMpcBackupResponse gained a traceId property. It is the only place the SDK hands a generated trace ID back to you:
That type is returned by PortalMpcProtocol.backup(_:usingProgressCallback:). Note that portal.backupWallet(_:usingProgressCallback:) returns a (cipherText:storageCallback:) tuple and does not surface the trace ID — so if you go through Portal rather than PortalMpc directly, the backup trace ID is not available to you.
No other response type exposes a trace ID. For every flow other than PortalMpc.backup, the way to know the trace ID is to supply it yourself.

Helpers

Two symbols are public:
Use generateTraceId() rather than rolling your own. It is the same generator the SDK uses internally, and it produces the lowercase UUID v4 format that Portal’s logs expect. Generate the ID once, log it, then thread it through the whole action:
The same ID reaches buildEip155Transaction, the eth_sendTransaction provider request, and the MPC signing operation behind it. For a provider request, pass it through RequestOptions:
portal.buildEip155Transaction(chainId:params:) does not accept a trace ID, so in the example above the build request carries a generated one and only the eth_sendTransaction leg carries yours. If you want a single trace ID across build, sign, and broadcast, use sendAsset with SendAssetParams(traceId:) instead of building and requesting separately.
See Configure log level if you also want the SDK’s own output alongside your trace IDs.

Sharing a trace id with Portal support

Log the trace ID next to your own request identifier, so you can look up either from the other. When something fails, include the trace ID in your support ticket or bug report — it is the fastest path from a user complaint to the exact requests Portal handled.
A trace ID is not a secret, but it is a correlation handle. Do not log it in a system that stores user PII under a different retention policy than your Portal logs — that turns a debugging aid into a way to join two datasets you meant to keep separate.

Signature changes in 7.3.0

Threading traceId through the SDK changed several protocol requirements.
If you call Portal’s APIs, 7.3.0 requires no changes — every new parameter is either defaulted or paired with a back-compatible extension. If you implement any Portal protocol — most commonly a hand-rolled test mock — several gained new requirements.
Two call-site changes are genuinely breaking: PortalApi.init gained a parameter. enclaveMPCHost was inserted as the third parameter, before provider:. It is defaulted, so labelled calls are unaffected — but if you construct PortalApi directly and pass arguments positionally, check your call site.
PortalMpcBackupResponse gained a memberwise init. init(cipherText:shareIds:traceId:) replaces the two-argument form. Test fixtures that build this type need the extra argument:

Next Steps