> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rhinestone.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Custom signing

> Inspect intent messages and coordinate signatures across custom owner implementations.

Use `getTransactionMessages` to inspect the EIP-712 payloads for a prepared transaction. Let the SDK create the final signatures: validator contributions, account envelopes, session-key claims, and the selected quote are part of the signed result.

## Inspect the messages

```ts theme={null}
const prepared = await rhinestoneAccount.prepareTransaction(transaction);
const { origin, destination, targetExecution } =
  rhinestoneAccount.getTransactionMessages(prepared);
```

Use these values to render an approval screen, audit the requested operation, or send metadata to an external coordinator. Pass `{ intentId }` as the second argument to inspect a quote other than `prepared.quotes.best`.

<Warning>
  Do not sign these messages independently and construct a `submitTransaction`
  payload by hand. Raw EIP-712 signatures do not include every validator and
  account-specific envelope assembled by the SDK.
</Warning>

## Provide a custom signer

The accounts configured in `owners` are viem `Account` or `WebAuthnAccount` objects. Implement their signing methods with your own HSM, MPC service, custody provider, or remote approval flow. The SDK calls those methods while preserving the account's required encoding.

For the normal single-process flow:

```ts theme={null}
const signed = await rhinestoneAccount.signTransaction(prepared);
const result = await rhinestoneAccount.submitTransaction(signed);
await rhinestoneAccount.waitForExecution(result);
```

## Collect owner approvals independently

ECDSA, passkey, and multi-factor smart-account owners can approve the same prepared transaction in separate processes. Each process must receive the same serialized prepared transaction and sign the same quote:

```ts theme={null}
const signatureA = await rhinestoneAccount.signTransaction(prepared, {
  owner: ownerA,
  intentId: prepared.quotes.best.intentId,
});

const signatureB = await rhinestoneAccount.signTransaction(prepared, {
  owner: ownerB,
  intentId: prepared.quotes.best.intentId,
});

const signed = await rhinestoneAccount.assembleTransaction(prepared, [
  signatureA,
  signatureB,
]);
const result = await rhinestoneAccount.submitTransaction(signed);
```

For a multi-factor account, also pass the factor's `validatorId` with each owner. `assembleTransaction` deduplicates and orders contributions, then rejects unknown owners, mixed intent IDs, and signatures below the configured threshold.

## Constraints

* Keep the prepared transaction unchanged across signers. Preparing again can select a different quote or payload.
* Submit before the selected quote expires. Re-prepare and collect new signatures after expiry.
* Keep the SDK's local owner and threshold configuration synchronized with on-chain changes.
* Independent assembly is not supported for EOA account mode, session keys, K1/ERC-7739 flows, or transactions requiring a target-execution signature. Use the standard `signTransaction` flow for those cases.

See the generated references for [`getTransactionMessages`](/wallets/custom-signer/sdk-reference/account/transactions/get-transaction-messages), [`signTransaction`](/wallets/custom-signer/sdk-reference/account/transactions/sign-transaction), and [`assembleTransaction`](/wallets/custom-signer/sdk-reference/account/transactions/assemble-transaction).
