9.1.0 you can supply your own trace ID, log it next to your own request IDs, and hand it to Portal support.
Overview
A trace ID is a UUID v4 sent as theX-Portal-Trace-Id HTTP header. The SDK attaches it to requests it makes to Portal-owned hosts, and maps it to the reqId field on MPC signing metadata so the same value covers both the API call and the MPC operation it triggers.
There are two ways a trace ID comes into existence:
- The SDK generates one. This is the default and requires no code changes.
- You supply one. Pass it through
RequestOptions,SendAssetParams, orrawSignto stitch Portal’s traces into your own observability.
Automatic tracing
You do not have to do anything. When you do not supply a trace ID, the SDK generates a fresh UUID for each request. Tracing is always on, and there is no way to disable it.Which requests carry the header
The header is attached only when the request targets a Portal-owned host. The SDK checks the URL withisPortalOwnedUrl before setting the header, which is true for:
- Hosts containing
portalhq— for exampleapi.portalhq.ioandmpc.portalhq.io - Local development hosts —
localhost,*.localhost,127.0.0.1, and10.0.2.2(the Android emulator’s host loopback)
- Third-party RPC endpoints configured through your gateway config do not receive the header. If you inspect traffic to your own Alchemy or Infura endpoint and see no
X-Portal-Trace-Id, that is expected behavior, not a bug. - Google Drive requests made while storing or fetching a backup share do not receive the header.
Two consequences worth internalizing: you will not see the header on traffic to the third-party RPC providers you configure, and the SDK will not hand a correlation ID to them. The host check is a substring match on
portalhq, so a custom host that happens to contain portalhq is treated as Portal-owned.isPortalOwnedUrl is public, so you can check a URL yourself:
Supplying your own trace ID
Three public entry points accept a trace ID.Provider requests
RequestOptions gained a traceId property:
RequestOptions.traceId is always mapped to the MPC signing metadata reqId, even when the RPC URL itself is a third-party host that does not receive the header. That is the non-obvious payoff: one value correlates the API request and the MPC signing operation it triggers.
Sending assets
SendAssetParams gained a traceId property:
Raw signing
rawSign gained a four-argument overload. The original three-argument signature is unchanged:
Multi-step flows share one ID
When an operation makes several requests, all of them carry the same trace ID.sendAsset resolves one trace ID at the start of the call and reuses it for the build-transaction request, the MPC signing operation, and the broadcast. Pass traceId in SendAssetParams and the whole flow is correlated under your value:
createWallet, backupWallet, recoverWallet, and wallet ejection — and the EIP-7702 upgrade in portal.evmAccountType.upgradeTo7702 also share a single trace ID across all of their sub-requests. These operations generate the ID internally and do not accept one from the caller.
Reading the ID back
BackupWalletResponse gained shareIds and traceId:
traceId defaults to an empty string, not null. Check it with isNotEmpty() rather than a null check.Helpers
Theio.portalhq.android.utils package exposes the tracing primitives the SDK uses internally.
resolveTraceId exists so your own functions can take an optional trace ID and always have a concrete value to log:
Map extensions are for code that builds or inspects header maps directly — an OkHttp interceptor that stamps your own outbound requests with the same ID, for example:
Sharing a trace ID with Portal support
Log the trace ID alongside your own request IDs so that when something goes wrong you can look up both sides of the same event. Include the ID in support tickets — it is the fastest way for Portal to find the exact request.Signature changes in 9.1.0
ThreadingtraceId through the transport and API layers touched a lot of signatures. Almost none of it is breaking.
If you only call Portal’s APIs from Kotlin,
9.1.0 requires no changes. Every Api and EvmAccountTypeApi method that gained a traceId parameter did so through an overload, or — for ejectClient and storedClientBackupShare — through a default value plus @JvmOverloads, so those original signatures survive in both source and bytecode. PortalRequests is the exception: its methods gained a defaulted traceId without @JvmOverloads, so Kotlin source callers are unaffected but the pre-9.1.0 JVM descriptors are not preserved — see Transport layer below.The overload-pair pattern
MostApi methods kept their original arity and gained a new one alongside it:
EvmAccountTypeApi follows the same pattern for getStatus, buildAuthorizationList, and buildAuthorizationTransaction.
EvmAccountTypePortalDependency — the small interface you implement if you supply your own Portal dependency to EvmAccountType — gained a defaulted overload, so existing implementations still compile:
Two methods changed in place
ejectClient and storedClientBackupShare gained traceId in place with a default value rather than through a hand-written overload:
@JvmOverloads, so the compiler also emits the original zero-traceId arity into the bytecode. The 9.0.x JVM descriptors are preserved, which means existing calls keep resolving and no recompile is required:
Transport layer
EveryPortalRequests method gained a trailing defaulted traceId:
Other affected types
Next Steps
- Turn on SDK logs to see requests as they happen — Configure log level
- Learn how Portal surfaces failures — Error handling
- Review the signing methods that accept a trace ID — Sign a transaction
- Call Portal’s APIs directly — Portal API methods