@turnkey/core 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/dist/__clients__/core.d.ts +161 -334
  2. package/dist/__clients__/core.d.ts.map +1 -1
  3. package/dist/__clients__/core.js +416 -275
  4. package/dist/__clients__/core.js.map +1 -1
  5. package/dist/__clients__/core.mjs +366 -225
  6. package/dist/__clients__/core.mjs.map +1 -1
  7. package/dist/__generated__/sdk-client-base.d.ts +4 -2
  8. package/dist/__generated__/sdk-client-base.d.ts.map +1 -1
  9. package/dist/__generated__/sdk-client-base.js +37 -6
  10. package/dist/__generated__/sdk-client-base.js.map +1 -1
  11. package/dist/__generated__/sdk-client-base.mjs +32 -1
  12. package/dist/__generated__/sdk-client-base.mjs.map +1 -1
  13. package/dist/__generated__/version.d.ts +1 -1
  14. package/dist/__generated__/version.js +1 -1
  15. package/dist/__generated__/version.mjs +1 -1
  16. package/dist/__inputs__/public_api.types.d.ts +49 -0
  17. package/dist/__inputs__/public_api.types.d.ts.map +1 -1
  18. package/dist/__polyfills__/window.js.map +1 -1
  19. package/dist/__polyfills__/window.mjs.map +1 -1
  20. package/dist/__stampers__/api/base.d.ts +1 -1
  21. package/dist/__stampers__/api/base.d.ts.map +1 -1
  22. package/dist/__stampers__/api/base.js.map +1 -1
  23. package/dist/__stampers__/api/base.mjs.map +1 -1
  24. package/dist/__stampers__/api/mobile/stamper.d.ts +1 -1
  25. package/dist/__stampers__/api/mobile/stamper.d.ts.map +1 -1
  26. package/dist/__stampers__/api/mobile/stamper.js.map +1 -1
  27. package/dist/__stampers__/api/mobile/stamper.mjs.map +1 -1
  28. package/dist/__stampers__/api/web/stamper.d.ts +1 -1
  29. package/dist/__stampers__/api/web/stamper.d.ts.map +1 -1
  30. package/dist/__stampers__/api/web/stamper.js.map +1 -1
  31. package/dist/__stampers__/api/web/stamper.mjs.map +1 -1
  32. package/dist/__stampers__/passkey/base.d.ts +1 -1
  33. package/dist/__stampers__/passkey/base.d.ts.map +1 -1
  34. package/dist/__stampers__/passkey/base.js.map +1 -1
  35. package/dist/__stampers__/passkey/base.mjs.map +1 -1
  36. package/dist/__storage__/base.d.ts +1 -1
  37. package/dist/__storage__/base.d.ts.map +1 -1
  38. package/dist/__storage__/base.js.map +1 -1
  39. package/dist/__storage__/base.mjs.map +1 -1
  40. package/dist/__storage__/mobile/storage.d.ts +1 -1
  41. package/dist/__storage__/mobile/storage.d.ts.map +1 -1
  42. package/dist/__storage__/mobile/storage.js +4 -3
  43. package/dist/__storage__/mobile/storage.js.map +1 -1
  44. package/dist/__storage__/mobile/storage.mjs +2 -1
  45. package/dist/__storage__/mobile/storage.mjs.map +1 -1
  46. package/dist/__storage__/web/storage.d.ts +1 -1
  47. package/dist/__storage__/web/storage.d.ts.map +1 -1
  48. package/dist/__storage__/web/storage.js +4 -3
  49. package/dist/__storage__/web/storage.js.map +1 -1
  50. package/dist/__storage__/web/storage.mjs +2 -1
  51. package/dist/__storage__/web/storage.mjs.map +1 -1
  52. package/dist/__typedoc-entry__/index.d.ts +3 -0
  53. package/dist/__typedoc-entry__/index.d.ts.map +1 -0
  54. package/dist/__types__/auth.d.ts +150 -0
  55. package/dist/__types__/auth.d.ts.map +1 -0
  56. package/dist/__types__/auth.js +43 -0
  57. package/dist/__types__/auth.js.map +1 -0
  58. package/dist/__types__/auth.mjs +40 -0
  59. package/dist/__types__/auth.mjs.map +1 -0
  60. package/dist/__types__/config.d.ts +140 -0
  61. package/dist/__types__/config.d.ts.map +1 -0
  62. package/dist/__types__/error.d.ts +13 -0
  63. package/dist/__types__/error.d.ts.map +1 -0
  64. package/dist/__types__/error.js +18 -0
  65. package/dist/__types__/error.js.map +1 -0
  66. package/dist/__types__/error.mjs +16 -0
  67. package/dist/__types__/error.mjs.map +1 -0
  68. package/dist/__types__/export.d.ts +10 -0
  69. package/dist/__types__/export.d.ts.map +1 -0
  70. package/dist/__types__/external-wallets.d.ts +202 -0
  71. package/dist/__types__/external-wallets.d.ts.map +1 -0
  72. package/dist/__types__/external-wallets.js +35 -0
  73. package/dist/__types__/external-wallets.js.map +1 -0
  74. package/dist/__types__/external-wallets.mjs +35 -0
  75. package/dist/__types__/external-wallets.mjs.map +1 -0
  76. package/dist/__types__/index.d.ts +7 -0
  77. package/dist/__types__/index.d.ts.map +1 -0
  78. package/dist/__types__/method-types.d.ts +314 -0
  79. package/dist/__types__/method-types.d.ts.map +1 -0
  80. package/dist/__wallet__/base.d.ts +1 -1
  81. package/dist/__wallet__/base.d.ts.map +1 -1
  82. package/dist/__wallet__/base.js.map +1 -1
  83. package/dist/__wallet__/base.mjs.map +1 -1
  84. package/dist/__wallet__/connector.d.ts +3 -3
  85. package/dist/__wallet__/connector.d.ts.map +1 -1
  86. package/dist/__wallet__/connector.js +5 -4
  87. package/dist/__wallet__/connector.js.map +1 -1
  88. package/dist/__wallet__/connector.mjs +4 -3
  89. package/dist/__wallet__/connector.mjs.map +1 -1
  90. package/dist/__wallet__/mobile/manager.d.ts +1 -1
  91. package/dist/__wallet__/mobile/manager.d.ts.map +1 -1
  92. package/dist/__wallet__/mobile/manager.js +5 -4
  93. package/dist/__wallet__/mobile/manager.js.map +1 -1
  94. package/dist/__wallet__/mobile/manager.mjs +2 -1
  95. package/dist/__wallet__/mobile/manager.mjs.map +1 -1
  96. package/dist/__wallet__/stamper.d.ts +1 -1
  97. package/dist/__wallet__/stamper.d.ts.map +1 -1
  98. package/dist/__wallet__/stamper.js +9 -8
  99. package/dist/__wallet__/stamper.js.map +1 -1
  100. package/dist/__wallet__/stamper.mjs +2 -1
  101. package/dist/__wallet__/stamper.mjs.map +1 -1
  102. package/dist/__wallet__/wallet-connect/base.d.ts +4 -2
  103. package/dist/__wallet__/wallet-connect/base.d.ts.map +1 -1
  104. package/dist/__wallet__/wallet-connect/base.js +69 -26
  105. package/dist/__wallet__/wallet-connect/base.js.map +1 -1
  106. package/dist/__wallet__/wallet-connect/base.mjs +54 -11
  107. package/dist/__wallet__/wallet-connect/base.mjs.map +1 -1
  108. package/dist/__wallet__/wallet-connect/client.d.ts +30 -9
  109. package/dist/__wallet__/wallet-connect/client.d.ts.map +1 -1
  110. package/dist/__wallet__/wallet-connect/client.js +73 -11
  111. package/dist/__wallet__/wallet-connect/client.js.map +1 -1
  112. package/dist/__wallet__/wallet-connect/client.mjs +73 -11
  113. package/dist/__wallet__/wallet-connect/client.mjs.map +1 -1
  114. package/dist/__wallet__/web/manager.d.ts +1 -1
  115. package/dist/__wallet__/web/manager.d.ts.map +1 -1
  116. package/dist/__wallet__/web/manager.js +11 -10
  117. package/dist/__wallet__/web/manager.js.map +1 -1
  118. package/dist/__wallet__/web/manager.mjs +2 -1
  119. package/dist/__wallet__/web/manager.mjs.map +1 -1
  120. package/dist/__wallet__/web/native/ethereum.d.ts +3 -3
  121. package/dist/__wallet__/web/native/ethereum.d.ts.map +1 -1
  122. package/dist/__wallet__/web/native/ethereum.js +11 -10
  123. package/dist/__wallet__/web/native/ethereum.js.map +1 -1
  124. package/dist/__wallet__/web/native/ethereum.mjs +4 -3
  125. package/dist/__wallet__/web/native/ethereum.mjs.map +1 -1
  126. package/dist/__wallet__/web/native/solana.d.ts +3 -3
  127. package/dist/__wallet__/web/native/solana.d.ts.map +1 -1
  128. package/dist/__wallet__/web/native/solana.js +12 -11
  129. package/dist/__wallet__/web/native/solana.js.map +1 -1
  130. package/dist/__wallet__/web/native/solana.mjs +7 -6
  131. package/dist/__wallet__/web/native/solana.mjs.map +1 -1
  132. package/dist/index.d.ts +2 -1
  133. package/dist/index.d.ts.map +1 -1
  134. package/dist/index.js +29 -21
  135. package/dist/index.js.map +1 -1
  136. package/dist/index.mjs +4 -2
  137. package/dist/index.mjs.map +1 -1
  138. package/dist/turnkey-helpers.js.map +1 -1
  139. package/dist/turnkey-helpers.mjs.map +1 -1
  140. package/dist/utils.d.ts +4 -18
  141. package/dist/utils.d.ts.map +1 -1
  142. package/dist/utils.js +96 -32
  143. package/dist/utils.js.map +1 -1
  144. package/dist/utils.mjs +75 -13
  145. package/dist/utils.mjs.map +1 -1
  146. package/package.json +6 -6
  147. package/dist/__types__/base.d.ts +0 -527
  148. package/dist/__types__/base.d.ts.map +0 -1
  149. package/dist/__types__/base.js +0 -89
  150. package/dist/__types__/base.js.map +0 -1
  151. package/dist/__types__/base.mjs +0 -85
  152. package/dist/__types__/base.mjs.map +0 -1
@@ -1,7 +1,8 @@
1
1
  import { TurnkeySDKClientBase } from '../__generated__/sdk-client-base.mjs';
2
2
  import { TurnkeyErrorCodes, TurnkeyError, TurnkeyNetworkError, AuthAction } from '@turnkey/sdk-types';
3
- import { SessionKey, DEFAULT_SESSION_EXPIRATION_IN_SECONDS, StamperType, WalletSource, Chain, FilterType, OtpTypeToFilterTypeMap, OtpType, Curve, SignIntent } from '../__types__/base.mjs';
4
- import { withTurnkeyErrorHandling, isValidPasskeyName, isWeb, isReactNative, buildSignUpBody, findWalletProviderFromAddress, addressFromPublicKey, getCurveTypeFromProvider, getPublicKeyFromStampHeader, toExternalTimestamp, getAuthenticatorAddresses, isEthereumProvider, isSolanaProvider, getHashFunction, getEncodingType, getEncodedMessage, splitSignature, broadcastTransaction, getPolicySignature, googleISS, isWalletAccountArray, generateWalletAccountsFromAddressFormat } from '../utils.mjs';
3
+ import { SessionKey, DEFAULT_SESSION_EXPIRATION_IN_SECONDS, StamperType, FilterType, OtpTypeToFilterTypeMap, OtpType } from '../__types__/auth.mjs';
4
+ import { WalletSource, Chain, Curve, SignIntent } from '../__types__/external-wallets.mjs';
5
+ import { withTurnkeyErrorHandling, isValidPasskeyName, isWeb, isReactNative, buildSignUpBody, findWalletProviderFromAddress, addressFromPublicKey, getCurveTypeFromProvider, getPublicKeyFromStampHeader, mapAccountsToWallet, toExternalTimestamp, getAuthenticatorAddresses, isEthereumProvider, isSolanaProvider, getActiveSessionOrThrowIfRequired, getHashFunction, getEncodingType, getEncodedMessage, splitSignature, broadcastTransaction, getPolicySignature, googleISS, isWalletAccountArray, generateWalletAccountsFromAddressFormat } from '../utils.mjs';
5
6
  import { createStorageManager } from '../__storage__/base.mjs';
6
7
  import { CrossPlatformApiKeyStamper } from '../__stampers__/api/base.mjs';
7
8
  import { CrossPlatformPasskeyStamper } from '../__stampers__/passkey/base.mjs';
@@ -22,29 +23,30 @@ class TurnkeyClient {
22
23
  * - The resulting attestation and challenge can be used to register the passkey with Turnkey.
23
24
  *
24
25
  * @param params.name - display name for the passkey (defaults to a generated name based on the current timestamp).
25
- * @param params.stampWith - parameter to stamp the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
26
26
  * @param params.challenge - challenge string to use for passkey registration. If not provided, a new challenge will be generated.
27
- * @returns A promise that resolves to an object containing:
28
- * - attestation: attestation object returned from the passkey creation process.
29
- * - encodedChallenge: encoded challenge string used for passkey registration.
27
+ * @returns A promise that resolves to {@link CreatePasskeyResult}
30
28
  * @throws {TurnkeyError} If there is an error during passkey creation, or if the platform is unsupported.
31
29
  */
32
30
  this.createPasskey = async (params) => {
31
+ const { name: nameFromParams, challenge } = params || {};
33
32
  return withTurnkeyErrorHandling(async () => {
34
- const name = isValidPasskeyName(params?.name || `passkey-${Date.now()}`);
33
+ if (!this.passkeyStamper) {
34
+ throw new TurnkeyError("Passkey stamper is not initialized", TurnkeyErrorCodes.INTERNAL_ERROR);
35
+ }
36
+ const name = isValidPasskeyName(nameFromParams || `passkey-${Date.now()}`);
35
37
  let passkey;
36
38
  if (isWeb()) {
37
- const res = await this.passkeyStamper?.createWebPasskey({
39
+ const res = await this.passkeyStamper.createWebPasskey({
38
40
  publicKey: {
39
41
  user: {
40
42
  name,
41
43
  displayName: name,
42
44
  },
43
- ...(params?.challenge && { challenge: params.challenge }),
45
+ ...(challenge && { challenge }),
44
46
  },
45
47
  });
46
48
  if (!res) {
47
- throw new TurnkeyError("Failed to create React Native passkey", TurnkeyErrorCodes.INTERNAL_ERROR);
49
+ throw new TurnkeyError("Failed to create Web passkey", TurnkeyErrorCodes.INTERNAL_ERROR);
48
50
  }
49
51
  passkey = {
50
52
  encodedChallenge: res?.encodedChallenge,
@@ -52,7 +54,7 @@ class TurnkeyClient {
52
54
  };
53
55
  }
54
56
  else if (isReactNative()) {
55
- const res = await this.passkeyStamper?.createReactNativePasskey({
57
+ const res = await this.passkeyStamper.createReactNativePasskey({
56
58
  name,
57
59
  displayName: name,
58
60
  });
@@ -125,6 +127,7 @@ class TurnkeyClient {
125
127
  * @param params.publicKey - public key to use for authentication. If not provided, a new key pair will be generated.
126
128
  * @param params.sessionKey - session key to use for session creation (defaults to the default session key).
127
129
  * @param params.expirationSeconds - session expiration time in seconds (defaults to the configured default).
130
+ * @param params.organizationId - organization ID to target (defaults to the session's organization ID or the parent organization ID).
128
131
  * @returns A promise that resolves to a {@link PasskeyAuthResult}, which includes:
129
132
  * - `sessionToken`: the signed JWT session token.
130
133
  * - `credentialId`: an empty string.
@@ -142,7 +145,7 @@ class TurnkeyClient {
142
145
  }
143
146
  const sessionResponse = await this.httpClient.stampLogin({
144
147
  publicKey: generatedPublicKey,
145
- organizationId: this.config.organizationId,
148
+ organizationId: params?.organizationId ?? this.config.organizationId,
146
149
  expirationSeconds,
147
150
  }, StamperType.Passkey);
148
151
  await this.storeSession({
@@ -193,13 +196,14 @@ class TurnkeyClient {
193
196
  * @param params.sessionKey - session key to use for storing the session (defaults to the default session key).
194
197
  * @param params.expirationSeconds - session expiration time in seconds (defaults to the configured default).
195
198
  * @param params.challenge - challenge string to use for passkey registration. If not provided, a new challenge will be generated.
199
+ * @param params.organizationId - organization ID to target (defaults to the session's organization ID or the parent organization ID).
196
200
  * @returns A promise that resolves to a {@link PasskeyAuthResult}, which includes:
197
201
  * - `sessionToken`: the signed JWT session token.
198
202
  * - `credentialId`: the credential ID associated with the passkey created.
199
203
  * @throws {TurnkeyError} If there is an error during passkey creation, sub-organization creation, or session storage.
200
204
  */
201
205
  this.signUpWithPasskey = async (params) => {
202
- const { createSubOrgParams, passkeyDisplayName, sessionKey = SessionKey.DefaultSessionkey, expirationSeconds = DEFAULT_SESSION_EXPIRATION_IN_SECONDS, } = params || {};
206
+ const { createSubOrgParams, passkeyDisplayName, sessionKey = SessionKey.DefaultSessionkey, expirationSeconds = DEFAULT_SESSION_EXPIRATION_IN_SECONDS, organizationId, } = params || {};
203
207
  let generatedPublicKey = undefined;
204
208
  return withTurnkeyErrorHandling(async () => {
205
209
  generatedPublicKey = await this.apiKeyStamper?.createKeyPair();
@@ -242,7 +246,7 @@ class TurnkeyClient {
242
246
  this.apiKeyStamper?.setTemporaryPublicKey(generatedPublicKey);
243
247
  const sessionResponse = await this.httpClient.stampLogin({
244
248
  publicKey: newGeneratedKeyPair,
245
- organizationId: this.config.organizationId,
249
+ organizationId: organizationId ?? this.config.organizationId,
246
250
  expirationSeconds,
247
251
  });
248
252
  await this.apiKeyStamper?.deleteKeyPair(generatedPublicKey);
@@ -299,7 +303,7 @@ class TurnkeyClient {
299
303
  * - Requires the wallet manager and its connector to be initialized.
300
304
  *
301
305
  * @param walletProvider - wallet provider to connect.
302
- * @returns A promise that resolves once the wallet account is connected.
306
+ * @returns A promise that resolves with the connected wallet's address.
303
307
  * @throws {TurnkeyError} If the wallet manager is uninitialized or the connection fails.
304
308
  */
305
309
  this.connectWalletAccount = async (walletProvider) => {
@@ -307,7 +311,7 @@ class TurnkeyClient {
307
311
  if (!this.walletManager?.connector) {
308
312
  throw new TurnkeyError("Wallet connector is not initialized", TurnkeyErrorCodes.WALLET_MANAGER_COMPONENT_NOT_INITIALIZED);
309
313
  }
310
- await this.walletManager.connector.connectWalletAccount(walletProvider);
314
+ return await this.walletManager.connector.connectWalletAccount(walletProvider);
311
315
  }, {
312
316
  errorMessage: "Unable to connect wallet account",
313
317
  errorCode: TurnkeyErrorCodes.CONNECT_WALLET_ACCOUNT_ERROR,
@@ -386,6 +390,7 @@ class TurnkeyClient {
386
390
  * @param params.publicKey - optional public key to associate with the session (generated if not provided).
387
391
  * @param params.sessionKey - optional key to store the session under (defaults to the default session key).
388
392
  * @param params.expirationSeconds - optional session expiration time in seconds (defaults to the configured default).
393
+ * @param params.organizationId - organization ID to target (defaults to the session's organization ID or the parent organization ID).
389
394
  * @returns A promise that resolves to a {@link WalletAuthResult}, which includes:
390
395
  * - `sessionToken`: the signed JWT session token.
391
396
  * - `address`: the authenticated wallet address.
@@ -406,7 +411,7 @@ class TurnkeyClient {
406
411
  this.walletManager.stamper.setProvider(walletProvider.interfaceType, walletProvider);
407
412
  const sessionResponse = await this.httpClient.stampLogin({
408
413
  publicKey,
409
- organizationId: this.config.organizationId,
414
+ organizationId: params?.organizationId ?? this.config.organizationId,
410
415
  expirationSeconds,
411
416
  }, StamperType.Wallet);
412
417
  await this.storeSession({
@@ -450,6 +455,7 @@ class TurnkeyClient {
450
455
  * @param params.createSubOrgParams - parameters for creating a sub-organization (e.g., authenticators, user metadata).
451
456
  * @param params.sessionKey - session key to use for storing the session (defaults to the default session key).
452
457
  * @param params.expirationSeconds - session expiration time in seconds (defaults to the configured default).
458
+ * @param params.organizationId - organization ID to target (defaults to the session's organization ID or the parent organization ID).
453
459
  * @returns A promise that resolves to a {@link WalletAuthResult}, which includes:
454
460
  * - `sessionToken`: the signed JWT session token.
455
461
  * - `address`: the authenticated wallet address.
@@ -541,6 +547,7 @@ class TurnkeyClient {
541
547
  * @param params.createSubOrgParams - optional parameters for creating a sub-organization (e.g., authenticators, user metadata).
542
548
  * @param params.sessionKey - session key to use for storing the session (defaults to the default session key).
543
549
  * @param params.expirationSeconds - session expiration time in seconds (defaults to the configured default).
550
+ * @param params.organizationId - organization ID to target (defaults to the session's organization ID or the parent organization ID).
544
551
  * @returns A promise that resolves to an object containing:
545
552
  * - `sessionToken`: the signed JWT session token.
546
553
  * - `address`: the authenticated wallet address.
@@ -572,6 +579,10 @@ class TurnkeyClient {
572
579
  errorMessage: "Failed to create stamped request for wallet login",
573
580
  errorCode: TurnkeyErrorCodes.WALLET_LOGIN_OR_SIGNUP_ERROR,
574
581
  customErrorsByMessages: {
582
+ "WalletConnect: The connection request has expired. Please scan the QR code again.": {
583
+ message: "Your WalletConnect session expired. Please scan the QR code again.",
584
+ code: TurnkeyErrorCodes.WALLET_CONNECT_EXPIRED,
585
+ },
575
586
  "Failed to sign the message": {
576
587
  message: "Wallet auth was cancelled by the user.",
577
588
  code: TurnkeyErrorCodes.CONNECT_WALLET_CANCELLED,
@@ -682,6 +693,7 @@ class TurnkeyClient {
682
693
  *
683
694
  * @param params.otpType - type of OTP to initialize (OtpType.Email or OtpType.Sms).
684
695
  * @param params.contact - contact information for the user (e.g., email address or phone number).
696
+ * @param params.organizationId - optional organization ID to target (defaults to the session's organization ID or the parent organization ID).
685
697
  * @returns A promise that resolves to the OTP ID required for verification.
686
698
  * @throws {TurnkeyError} If there is an error during the OTP initialization process or if the maximum number of OTPs has been reached.
687
699
  */
@@ -1100,36 +1112,61 @@ class TurnkeyClient {
1100
1112
  * - Returns both embedded and connected wallets in a single array, each with their respective accounts populated.
1101
1113
  * - Optionally allows stamping the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
1102
1114
  *
1103
- * @param params.stampWith - parameter to stamp the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
1104
1115
  * @param params.walletProviders - array of wallet providers to use for fetching wallets.
1116
+ * @param params.organizationId - organization ID to target (defaults to the session's organization ID).
1117
+ * @param params.userId - user ID to target (defaults to the session's user ID).
1118
+ * @param params.connectedOnly - if true, fetches only connected wallets; if false or undefined, fetches both embedded and connected wallets.
1119
+ * @param params.stampWith - parameter to stamp the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
1105
1120
  * @returns A promise that resolves to an array of `Wallet` objects.
1106
1121
  * @throws {TurnkeyError} If no active session is found or if there is an error fetching wallets.
1107
1122
  */
1108
1123
  this.fetchWallets = async (params) => {
1109
- const { stampWith, walletProviders } = params || {};
1124
+ const { walletProviders, organizationId: organizationIdFromParams, userId: userIdFromParams, connectedOnly, stampWith, } = params || {};
1110
1125
  const session = await this.storageManager.getActiveSession();
1111
- if (!session) {
1112
- throw new TurnkeyError("No active session found. Please log in first.", TurnkeyErrorCodes.NO_SESSION_FOUND);
1126
+ if (!session && !connectedOnly) {
1127
+ throw new TurnkeyError("No active session found. Fetching embedded wallets requires a valid session. If you only need connected wallets, set the 'connectedOnly' parameter to true.", TurnkeyErrorCodes.NO_SESSION_FOUND);
1128
+ }
1129
+ // if `connectedOnly` is true, we need to make sure the walletManager is initialized
1130
+ // or else we can't fetch connected wallets, and we throw an error
1131
+ if (connectedOnly && !this.walletManager?.connector) {
1132
+ throw new TurnkeyError("Wallet connector is not initialized", TurnkeyErrorCodes.WALLET_MANAGER_COMPONENT_NOT_INITIALIZED);
1113
1133
  }
1114
1134
  return withTurnkeyErrorHandling(async () => {
1115
- const res = await this.httpClient.getWallets({ organizationId: session.organizationId }, stampWith);
1116
- if (!res || !res.wallets) {
1117
- throw new TurnkeyError("No wallets found in the response", TurnkeyErrorCodes.BAD_RESPONSE);
1118
- }
1119
- const embedded = await Promise.all(res.wallets.map(async (wallet) => {
1120
- const embeddedWallet = {
1121
- ...wallet,
1122
- source: WalletSource.Embedded,
1123
- accounts: [],
1124
- };
1125
- const accounts = await this.fetchWalletAccounts({
1126
- wallet: embeddedWallet,
1127
- ...(stampWith !== undefined && { stampWith }),
1128
- });
1129
- embeddedWallet.accounts = accounts;
1130
- return embeddedWallet;
1131
- }));
1135
+ let embedded = [];
1136
+ // if connectedOnly is true, we skip fetching embedded wallets
1137
+ if (!connectedOnly) {
1138
+ const organizationId = organizationIdFromParams || session?.organizationId;
1139
+ if (!organizationId) {
1140
+ throw new TurnkeyError("No organization ID provided and no active session found. Please log in first or pass in an organization ID.", TurnkeyErrorCodes.INVALID_REQUEST);
1141
+ }
1142
+ const userId = userIdFromParams || session?.userId;
1143
+ if (!userId) {
1144
+ throw new TurnkeyError("No user ID provided and no active session found. Please log in first or pass in a user ID.", TurnkeyErrorCodes.INVALID_REQUEST);
1145
+ }
1146
+ const res = await this.httpClient.getWalletAccounts({
1147
+ organizationId,
1148
+ includeWalletDetails: true,
1149
+ }, stampWith);
1150
+ const walletsRes = await this.httpClient.getWallets({
1151
+ organizationId,
1152
+ }, stampWith);
1153
+ if (!res || !res.accounts) {
1154
+ throw new TurnkeyError("No wallet accounts found in the response", TurnkeyErrorCodes.BAD_RESPONSE);
1155
+ }
1156
+ // create a map of walletId to EmbeddedWallet for easy lookup
1157
+ const walletMap = new Map(walletsRes.wallets.map((wallet) => [
1158
+ wallet.walletId,
1159
+ {
1160
+ ...wallet,
1161
+ source: WalletSource.Embedded,
1162
+ accounts: [],
1163
+ },
1164
+ ]));
1165
+ // map the accounts to their respective wallets
1166
+ embedded = mapAccountsToWallet(res.accounts, walletMap);
1167
+ }
1132
1168
  // if wallet connecting is disabled we return only embedded wallets
1169
+ // this will never be hit if `connectedOnly` is true because of the check above
1133
1170
  if (!this.walletManager?.connector)
1134
1171
  return embedded;
1135
1172
  const providers = walletProviders ?? (await this.fetchWalletProviders());
@@ -1143,7 +1180,11 @@ class TurnkeyClient {
1143
1180
  group.push(provider);
1144
1181
  groupedProviders.set(walletId, group);
1145
1182
  }
1146
- const connected = (await Promise.all(Array.from(groupedProviders.entries()).map(async ([walletId, grouped]) => {
1183
+ // has to be done in a for of loop so we can await each fetchWalletAccounts call individually
1184
+ // otherwise await Promise.all would cause them all to fire at once breaking passkey only set ups
1185
+ // (multiple wallet fetches at once causing "OperationError: A request is already pending.")
1186
+ let connected = [];
1187
+ for (const [walletId, grouped] of groupedProviders.entries()) {
1147
1188
  const timestamp = toExternalTimestamp();
1148
1189
  const wallet = {
1149
1190
  source: WalletSource.Connected,
@@ -1159,10 +1200,16 @@ class TurnkeyClient {
1159
1200
  wallet,
1160
1201
  walletProviders: grouped,
1161
1202
  ...(stampWith !== undefined && { stampWith }),
1203
+ ...(organizationIdFromParams !== undefined && {
1204
+ organizationId: organizationIdFromParams,
1205
+ }),
1206
+ ...(userIdFromParams !== undefined && { userId: userIdFromParams }),
1162
1207
  });
1163
1208
  wallet.accounts = accounts;
1164
- return wallet;
1165
- }))).filter((wallet) => wallet.accounts.length > 0);
1209
+ if (wallet.accounts.length > 0) {
1210
+ connected.push(wallet);
1211
+ }
1212
+ }
1166
1213
  return [...embedded, ...connected];
1167
1214
  }, {
1168
1215
  errorMessage: "Failed to fetch wallets",
@@ -1182,22 +1229,30 @@ class TurnkeyClient {
1182
1229
  * @param params.walletProviders - list of wallet providers to filter by (used for connected wallets).
1183
1230
  * @param params.paginationOptions - pagination options for embedded wallets.
1184
1231
  * @param params.stampWith - parameter to stamp the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
1232
+ * @param params.organizationId - organization ID to target (defaults to the session's organization ID).
1233
+ * @param params.userId - user ID to target (defaults to the session's user ID).
1234
+ *
1185
1235
  * @returns A promise that resolves to an array of `v1WalletAccount` objects.
1186
1236
  * @throws {TurnkeyError} If no active session is found or if there is an error fetching wallet accounts.
1187
1237
  */
1188
1238
  this.fetchWalletAccounts = async (params) => {
1189
1239
  const { wallet, stampWith, walletProviders, paginationOptions } = params;
1190
1240
  const session = await this.storageManager.getActiveSession();
1191
- if (!session) {
1192
- throw new TurnkeyError("No active session found. Please log in first.", TurnkeyErrorCodes.NO_SESSION_FOUND);
1193
- }
1241
+ const organizationId = params?.organizationId || session?.organizationId;
1242
+ const userId = params?.userId || session?.userId;
1194
1243
  return withTurnkeyErrorHandling(async () => {
1195
1244
  // this is an embedded wallet so we fetch accounts from Turnkey
1196
1245
  if (wallet.source === WalletSource.Embedded) {
1197
1246
  const embedded = [];
1247
+ if (!organizationId) {
1248
+ throw new TurnkeyError("No organization ID provided and no active session found. Please log in first or pass in an organization ID.", TurnkeyErrorCodes.INVALID_REQUEST);
1249
+ }
1250
+ if (!userId) {
1251
+ throw new TurnkeyError("No user ID provided and no active session found. Please log in first or pass in a user ID.", TurnkeyErrorCodes.INVALID_REQUEST);
1252
+ }
1198
1253
  const res = await this.httpClient.getWalletAccounts({
1199
1254
  walletId: wallet.walletId,
1200
- organizationId: session.organizationId,
1255
+ organizationId,
1201
1256
  paginationOptions: paginationOptions || { limit: "100" },
1202
1257
  }, stampWith);
1203
1258
  if (!res || !res.accounts) {
@@ -1228,17 +1283,26 @@ class TurnkeyClient {
1228
1283
  const matching = providers.filter((p) => p.info?.name?.toLowerCase().replace(/\s+/g, "-") ===
1229
1284
  wallet.walletId && p.connectedAddresses.length > 0);
1230
1285
  const sign = this.walletManager.connector.sign.bind(this.walletManager.connector);
1231
- const user = await this.fetchUser({
1232
- stampWith,
1233
- });
1234
- const { ethereum: ethereumAddresses, solana: solanaAddresses } = getAuthenticatorAddresses(user);
1286
+ let ethereumAddresses = [];
1287
+ let solanaAddresses = [];
1288
+ // we only fetch the user if we have to the organizationId and userId
1289
+ // if not that means `isAuthenticator` will always be false
1290
+ if (organizationId && userId) {
1291
+ const user = await this.fetchUser({
1292
+ userId,
1293
+ organizationId,
1294
+ stampWith,
1295
+ });
1296
+ ({ ethereum: ethereumAddresses, solana: solanaAddresses } =
1297
+ getAuthenticatorAddresses(user));
1298
+ }
1235
1299
  for (const provider of matching) {
1236
1300
  const timestamp = toExternalTimestamp();
1237
1301
  for (const address of provider.connectedAddresses) {
1238
1302
  if (isEthereumProvider(provider)) {
1239
1303
  const evmAccount = {
1240
1304
  walletAccountId: `${wallet.walletId}-${provider.interfaceType}-${address}`,
1241
- organizationId: session.organizationId,
1305
+ organizationId: organizationId ?? "",
1242
1306
  walletId: wallet.walletId,
1243
1307
  pathFormat: "PATH_FORMAT_BIP32",
1244
1308
  path: WalletSource.Connected,
@@ -1260,7 +1324,7 @@ class TurnkeyClient {
1260
1324
  if (isSolanaProvider(provider)) {
1261
1325
  const solAccount = {
1262
1326
  walletAccountId: `${wallet.walletId}-${provider.interfaceType}-${address}`,
1263
- organizationId: session.organizationId,
1327
+ organizationId: organizationId ?? "",
1264
1328
  walletId: wallet.walletId,
1265
1329
  pathFormat: "PATH_FORMAT_BIP32",
1266
1330
  path: WalletSource.Connected,
@@ -1296,17 +1360,19 @@ class TurnkeyClient {
1296
1360
  * - Supports stamping the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
1297
1361
  *
1298
1362
  * @param params.stampWith - parameter to stamp the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
1363
+ * @param params.organizationId - organization ID to target (defaults to the session's organization ID).
1299
1364
  * @returns A promise that resolves to an array of `v1PrivateKey` objects.
1300
1365
  * @throws {TurnkeyError} If no active session is found or if there is an error fetching private keys.
1301
1366
  */
1302
1367
  this.fetchPrivateKeys = async (params) => {
1303
1368
  const { stampWith } = params || {};
1304
- const session = await this.storageManager.getActiveSession();
1305
- if (!session) {
1306
- throw new TurnkeyError("No active session found. Please log in first.", TurnkeyErrorCodes.NO_SESSION_FOUND);
1369
+ const session = await getActiveSessionOrThrowIfRequired(stampWith, this.storageManager.getActiveSession);
1370
+ const organizationId = params?.organizationId || session?.organizationId;
1371
+ if (!organizationId) {
1372
+ throw new TurnkeyError("No organization ID provided and no active session found. Please log in first or pass in an organization ID.", TurnkeyErrorCodes.INVALID_REQUEST);
1307
1373
  }
1308
1374
  return withTurnkeyErrorHandling(async () => {
1309
- const res = await this.httpClient.getPrivateKeys({ organizationId: session.organizationId }, stampWith);
1375
+ const res = await this.httpClient.getPrivateKeys({ organizationId }, stampWith);
1310
1376
  if (!res) {
1311
1377
  throw new TurnkeyError("Failed to fetch private keys", TurnkeyErrorCodes.BAD_RESPONSE);
1312
1378
  }
@@ -1319,33 +1385,43 @@ class TurnkeyClient {
1319
1385
  /**
1320
1386
  * Signs a message using the specified wallet account.
1321
1387
  *
1322
- * - Supports both embedded and connected wallets.
1323
- * - For **connected wallets**:
1388
+ * Behavior differs depending on the wallet type:
1389
+ *
1390
+ * - **Connected wallets**
1324
1391
  * - Delegates signing to the wallet provider’s native signing method.
1325
- * - **Important:** For Ethereum wallets (e.g., MetaMask), signatures follow [EIP-191](https://eips.ethereum.org/EIPS/eip-191).
1326
- * The message is automatically prefixed with `"\x19Ethereum Signed Message:\n" + message length`
1327
- * before signing. As a result, this signature **cannot be used as a raw transaction signature**
1328
- * or broadcast on-chain.
1329
- * - For **embedded wallets**, uses the Turnkey API to sign the message directly.
1330
- * - Automatically handles message encoding and hashing based on the wallet account’s address format,
1392
+ * - *Ethereum*: signatures always follow [EIP-191](https://eips.ethereum.org/EIPS/eip-191).
1393
+ * - The wallet automatically prefixes messages with
1394
+ * `"\x19Ethereum Signed Message:\n" + message length` before signing.
1395
+ * - As a result, these signatures cannot be used as raw transaction signatures or broadcast on-chain.
1396
+ * - If `addEthereumPrefix` is set to `false`, an error is thrown because connected Ethereum wallets always prefix.
1397
+ * - *Other chains*: follows the native connected wallet behavior.
1398
+ *
1399
+ * - **Embedded wallets**
1400
+ * - Uses the Turnkey API to sign the message directly.
1401
+ * - Supports optional `addEthereumPrefix`:
1402
+ * - If `true` (default for Ethereum), the message is prefixed before signing.
1403
+ * - If `false`, the raw message is signed without any prefix.
1404
+ *
1405
+ * Additional details:
1406
+ * - Automatically handles encoding and hashing based on the wallet account’s address format,
1331
1407
  * unless explicitly overridden.
1408
+ * - Optionally allows stamping with a specific stamper
1409
+ * (`StamperType.Passkey`, `StamperType.ApiKey`, or `StamperType.Wallet`).
1332
1410
  *
1333
- * @param params.message - message to sign.
1411
+ * @param params.message - plaintext (UTF-8) message to sign.
1334
1412
  * @param params.walletAccount - wallet account to use for signing.
1335
- * @param params.encoding - override for the payload encoding (defaults to the encoding appropriate for the address type).
1336
- * @param params.hashFunction - override for the hash function (defaults to the hash function appropriate for the address type).
1337
- * @param params.stampWith - stamper to tag the signing request (e.g., Passkey, ApiKey, or Wallet).
1338
- * @param params.addEthereumPrefix - whether to prefix the message with Ethereum's `"\x19Ethereum Signed Message:\n"` string.
1339
- * - If `true` (default for Ethereum), the message is prefixed before signing.
1340
- * - If `false`:
1341
- * - Connected wallets will throw an error because they always prefix automatically.
1342
- * - Embedded wallets will sign the raw message without any prefix.
1343
- *
1344
- * @returns A promise resolving to a `v1SignRawPayloadResult` containing the signature and metadata.
1345
- * @throws {TurnkeyError} If signing fails, if the wallet account does not support signing, or if the response is invalid.
1413
+ * @param params.encoding - override for payload encoding (defaults to the encoding appropriate for the address format).
1414
+ * @param params.hashFunction - override for hash function (defaults to the function appropriate for the address format).
1415
+ * @param params.stampWith - optional stamper for the signing request.
1416
+ * @param params.addEthereumPrefix - whether to prefix the message with Ethereums
1417
+ * `"\x19Ethereum Signed Message:\n"` string (default: `true` for Ethereum).
1418
+ * @param params.organizationId - organization ID to target (defaults to the session's organization ID).
1419
+ *
1420
+ * @returns A promise that resolves to a `v1SignRawPayloadResult` containing the signature and metadata.
1421
+ * @throws {TurnkeyError} If signing fails, the wallet type does not support message signing, or the response is invalid.
1346
1422
  */
1347
1423
  this.signMessage = async (params) => {
1348
- const { message, walletAccount, stampWith, addEthereumPrefix } = params;
1424
+ const { message, walletAccount, stampWith, addEthereumPrefix, organizationId, } = params;
1349
1425
  const hashFunction = params.hashFunction || getHashFunction(walletAccount.addressFormat);
1350
1426
  const payloadEncoding = params.encoding || getEncodingType(walletAccount.addressFormat);
1351
1427
  return withTurnkeyErrorHandling(async () => {
@@ -1357,23 +1433,29 @@ class TurnkeyClient {
1357
1433
  }
1358
1434
  let encodedMessage = message;
1359
1435
  if (isEthereum) {
1360
- encodedMessage = getEncodedMessage(walletAccount.addressFormat, message);
1436
+ const msgBytes = toUtf8Bytes(message);
1437
+ encodedMessage = getEncodedMessage(payloadEncoding, msgBytes);
1361
1438
  }
1362
1439
  const sigHex = await walletAccount.signMessage(encodedMessage);
1363
1440
  return splitSignature(sigHex, walletAccount.addressFormat);
1364
1441
  }
1365
1442
  // this is an embedded wallet
1366
- let messageToEncode = message;
1443
+ let msgBytes = toUtf8Bytes(message);
1367
1444
  if (addEthereumPrefix && isEthereum) {
1368
- const prefix = `\x19Ethereum Signed Message:\n${toUtf8Bytes(message).length}`;
1369
- messageToEncode = prefix + message;
1370
- }
1371
- const encodedMessage = getEncodedMessage(walletAccount.addressFormat, messageToEncode);
1445
+ const prefix = `\x19Ethereum Signed Message:\n${msgBytes.length}`;
1446
+ const prefixBytes = toUtf8Bytes(prefix);
1447
+ const combined = new Uint8Array(prefixBytes.length + msgBytes.length);
1448
+ combined.set(prefixBytes, 0);
1449
+ combined.set(msgBytes, prefixBytes.length);
1450
+ msgBytes = combined;
1451
+ }
1452
+ const encodedMessage = getEncodedMessage(payloadEncoding, msgBytes);
1372
1453
  const response = await this.httpClient.signRawPayload({
1373
1454
  signWith: walletAccount.address,
1374
1455
  payload: encodedMessage,
1375
1456
  encoding: payloadEncoding,
1376
1457
  hashFunction,
1458
+ ...(organizationId && { organizationId }),
1377
1459
  }, stampWith);
1378
1460
  if (response.activity.failure) {
1379
1461
  throw new TurnkeyError("Failed to sign message, no signed payload returned", TurnkeyErrorCodes.SIGN_MESSAGE_ERROR);
@@ -1388,21 +1470,29 @@ class TurnkeyClient {
1388
1470
  /**
1389
1471
  * Signs a transaction using the specified wallet account.
1390
1472
  *
1391
- * - This function signs a blockchain transaction using the provided wallet address and transaction data.
1392
- * - Supports all Turnkey-supported blockchain networks (e.g., Ethereum, Solana, Tron).
1393
- * - Automatically determines the appropriate signing method based on the transaction type.
1394
- * - Delegates signing to the Turnkey API, which returns the signed transaction and related metadata.
1395
- * - Optionally allows stamping the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
1473
+ * Behavior differs depending on the type of wallet:
1474
+ *
1475
+ * - **Connected wallets**
1476
+ * - Ethereum: does not support raw transaction signing. Calling this function will throw an error instructing you to use `signAndSendTransaction` instead.
1477
+ * - Solana: supports raw transaction signing via the connected wallet provider.
1478
+ * - Other chains: not supported; will throw an error.
1396
1479
  *
1397
- * @param params.walletAccount - wallet account to use for signing the transaction.
1398
- * @param params.unsignedTransaction - unsigned transaction data (serialized as a string) to be signed.
1480
+ * - **Embedded wallets**
1481
+ * - Delegates signing to the Turnkey API, which returns the signed transaction.
1482
+ * - Supports all Turnkey-supported transaction types (e.g., Ethereum, Solana, Tron).
1483
+ * - Optionally allows stamping with a specific stamper (`StamperType.Passkey`, `StamperType.ApiKey`, or `StamperType.Wallet`).
1484
+ *
1485
+ * @param params.walletAccount - wallet account to use for signing.
1486
+ * @param params.unsignedTransaction - unsigned transaction data as a serialized
1487
+ * string in the canonical encoding for the given `transactionType`.
1399
1488
  * @param params.transactionType - type of transaction (e.g., "TRANSACTION_TYPE_ETHEREUM", "TRANSACTION_TYPE_SOLANA", "TRANSACTION_TYPE_TRON").
1400
- * @param params.stampWith - parameter to stamp the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
1401
- * @returns A promise that resolves to a `TSignTransactionResponse` object containing the signed transaction and any additional signing metadata.
1402
- * @throws {TurnkeyError} If there is an error signing the transaction or if the response is invalid.
1489
+ * @param params.stampWith - stamper to use for signing (`StamperType.Passkey`, `StamperType.ApiKey`, or `StamperType.Wallet`).
1490
+ * @param params.organizationId - organization ID to target (defaults to the session's organization ID).
1491
+ * @returns A promise that resolves to the signed transaction string.
1492
+ * @throws {TurnkeyError} If the wallet type is unsupported, signing fails, or the response is invalid.
1403
1493
  */
1404
1494
  this.signTransaction = async (params) => {
1405
- const { walletAccount, unsignedTransaction, transactionType, stampWith } = params;
1495
+ const { walletAccount, unsignedTransaction, transactionType, stampWith, organizationId, } = params;
1406
1496
  return withTurnkeyErrorHandling(async () => {
1407
1497
  if (walletAccount.source === WalletSource.Connected) {
1408
1498
  switch (walletAccount.chainInfo.namespace) {
@@ -1421,6 +1511,7 @@ class TurnkeyClient {
1421
1511
  signWith: walletAccount.address,
1422
1512
  unsignedTransaction,
1423
1513
  type: transactionType,
1514
+ ...(organizationId && { organizationId }),
1424
1515
  }, stampWith);
1425
1516
  return signTransaction.signedTransaction;
1426
1517
  }, {
@@ -1431,25 +1522,32 @@ class TurnkeyClient {
1431
1522
  /**
1432
1523
  * Signs and broadcasts a transaction using the specified wallet account.
1433
1524
  *
1434
- * - For **connected wallets**:
1435
- * - Calls the wallet’s native `signAndSendTransaction` method.
1436
- * - Does **not** require an `rpcUrl`.
1525
+ * Behavior differs depending on the type of wallet:
1526
+ *
1527
+ * - **Connected wallets**
1528
+ * - *Ethereum*: delegates to the wallet’s native `signAndSendTransaction` method.
1529
+ * - Does **not** require an `rpcUrl` (the wallet handles broadcasting).
1530
+ * - *Solana*: signs the transaction locally with the connected wallet, but requires an `rpcUrl` to broadcast it.
1531
+ * - Other chains: not supported; will throw an error.
1437
1532
  *
1438
- * - For **embedded wallets**:
1533
+ * - **Embedded wallets**
1439
1534
  * - Signs the transaction using the Turnkey API.
1440
- * - Requires an `rpcUrl` to broadcast the transaction.
1441
- * - Broadcasts the transaction using a JSON-RPC client.
1442
- *
1443
- * @param params.walletAccount - wallet account to use for signing and sending.
1444
- * @param params.unsignedTransaction - unsigned transaction (serialized string).
1445
- * @param params.transactionType - transaction type (e.g., "TRANSACTION_TYPE_SOLANA").
1446
- * @param params.rpcUrl - required for embedded wallets to broadcast the signed transaction.
1447
- * @param params.stampWith - optional stamper to tag the signing request.
1535
+ * - Requires an `rpcUrl` to broadcast the signed transaction, since Turnkey does not broadcast directly.
1536
+ * - Broadcasts the transaction using a JSON-RPC client and returns the resulting transaction hash/signature.
1537
+ * - Optionally allows stamping with a specific stamper (`StamperType.Passkey`, `StamperType.ApiKey`, or `StamperType.Wallet`).
1538
+ *
1539
+ * @param params.walletAccount - wallet account to use for signing and broadcasting.
1540
+ * @param params.unsignedTransaction - unsigned transaction data as a serialized
1541
+ * string in the canonical encoding for the given `transactionType`.
1542
+ * @param params.transactionType - type of transaction (e.g., `"TRANSACTION_TYPE_SOLANA"`, `"TRANSACTION_TYPE_ETHEREUM"`).
1543
+ * @param params.rpcUrl - JSON-RPC endpoint used for broadcasting (required for Solana connected wallets and all embedded wallets).
1544
+ * @param params.stampWith - optional stamper to use when signing (`StamperType.Passkey`, `StamperType.ApiKey`, or `StamperType.Wallet`).
1545
+ * @param params.organizationId - **Only for Turnkey embedded wallets**: organization ID to target (defaults to the session's organization ID).
1448
1546
  * @returns A promise that resolves to a transaction signature or hash.
1449
- * @throws {TurnkeyError} If signing or broadcasting fails.
1547
+ * @throws {TurnkeyError} If the wallet type is unsupported, or if signing/broadcasting fails.
1450
1548
  */
1451
1549
  this.signAndSendTransaction = async (params) => {
1452
- const { walletAccount, unsignedTransaction, transactionType, rpcUrl, stampWith, } = params;
1550
+ const { walletAccount, unsignedTransaction, transactionType, rpcUrl, stampWith, organizationId, } = params;
1453
1551
  return withTurnkeyErrorHandling(async () => {
1454
1552
  if (walletAccount.source === WalletSource.Connected) {
1455
1553
  // this is a connected wallet account
@@ -1484,6 +1582,7 @@ class TurnkeyClient {
1484
1582
  signWith: walletAccount.address,
1485
1583
  unsignedTransaction,
1486
1584
  type: transactionType,
1585
+ ...(organizationId && { organizationId }),
1487
1586
  }, stampWith);
1488
1587
  const signedTx = signTransactionResponse.signedTransaction;
1489
1588
  const txHash = await broadcastTransaction({
@@ -1513,16 +1612,16 @@ class TurnkeyClient {
1513
1612
  * @throws {TurnkeyError} If there is no active session, if there is no userId, or if there is an error fetching user details.
1514
1613
  */
1515
1614
  this.fetchUser = async (params) => {
1516
- const { stampWith } = params || {};
1517
- const session = await this.storageManager.getActiveSession();
1518
- if (!session) {
1519
- throw new TurnkeyError("No active session found. Please log in first.", TurnkeyErrorCodes.NO_SESSION_FOUND);
1520
- }
1521
- const userId = params?.userId || session.userId;
1615
+ const { organizationId: organizationIdFromParams, userId: userIdFromParams, stampWith, } = params || {};
1616
+ const session = await getActiveSessionOrThrowIfRequired(stampWith, this.storageManager.getActiveSession);
1617
+ const userId = userIdFromParams || session?.userId;
1522
1618
  if (!userId) {
1523
1619
  throw new TurnkeyError("User ID must be provided to fetch user", TurnkeyErrorCodes.INVALID_REQUEST);
1524
1620
  }
1525
- const organizationId = params?.organizationId || session.organizationId;
1621
+ const organizationId = organizationIdFromParams || session?.organizationId;
1622
+ if (!organizationId) {
1623
+ throw new TurnkeyError("Organization ID must be provided to fetch user", TurnkeyErrorCodes.INVALID_REQUEST);
1624
+ }
1526
1625
  return withTurnkeyErrorHandling(async () => {
1527
1626
  const userResponse = await this.httpClient.getUser({ organizationId, userId }, stampWith);
1528
1627
  if (!userResponse || !userResponse.user) {
@@ -1545,24 +1644,26 @@ class TurnkeyClient {
1545
1644
  * @param params.publicKey - the P-256 public key to use for lookup and creation.
1546
1645
  * @param params.createParams.userName - optional username to assign if creating a new user (defaults to `"Public Key User"`).
1547
1646
  * @param params.createParams.apiKeyName - optional API key name to assign if creating a new API key (defaults to `public-key-user-${publicKey}`).
1647
+ * @param params.organizationId - organization ID to specify the sub-organization (defaults to the current session's organizationId).
1648
+ * @param params.stampWith - parameter to stamp the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
1548
1649
  * @returns A promise that resolves to the existing or newly created {@link v1User}.
1549
1650
  * @throws {TurnkeyError} If there is no active session, if the input is invalid, if user retrieval fails, or if user creation fails.
1550
1651
  */
1551
1652
  this.fetchOrCreateP256ApiKeyUser = async (params) => {
1552
- const { publicKey, createParams } = params;
1653
+ const { publicKey, createParams, stampWith, organizationId: organizationIdFromParams, } = params;
1553
1654
  return withTurnkeyErrorHandling(async () => {
1554
- const session = await this.storageManager.getActiveSession();
1555
- if (!session) {
1556
- throw new TurnkeyError("No active session found. Please log in first.", TurnkeyErrorCodes.NO_SESSION_FOUND);
1655
+ const session = await getActiveSessionOrThrowIfRequired(stampWith, this.storageManager.getActiveSession);
1656
+ const organizationId = organizationIdFromParams || session?.organizationId;
1657
+ if (!organizationId) {
1658
+ throw new TurnkeyError("Organization ID is required to fetch or create P-256 API key user.", TurnkeyErrorCodes.INVALID_REQUEST);
1557
1659
  }
1558
- const organizationId = session.organizationId;
1559
1660
  // we validate their input
1560
1661
  if (!publicKey?.trim()) {
1561
1662
  throw new TurnkeyError("'publicKey' is required and cannot be empty.", TurnkeyErrorCodes.INVALID_REQUEST);
1562
1663
  }
1563
1664
  const usersResponse = await this.httpClient.getUsers({
1564
1665
  organizationId,
1565
- });
1666
+ }, stampWith);
1566
1667
  if (!usersResponse || !usersResponse.users) {
1567
1668
  throw new TurnkeyError("No users found in the response", TurnkeyErrorCodes.BAD_RESPONSE);
1568
1669
  }
@@ -1592,7 +1693,7 @@ class TurnkeyClient {
1592
1693
  oauthProviders: [],
1593
1694
  },
1594
1695
  ],
1595
- });
1696
+ }, stampWith);
1596
1697
  if (!createUserResp?.userIds ||
1597
1698
  createUserResp.userIds.length === 0 ||
1598
1699
  !createUserResp.userIds[0]) {
@@ -1602,6 +1703,7 @@ class TurnkeyClient {
1602
1703
  return await this.fetchUser({
1603
1704
  organizationId,
1604
1705
  userId: newUserId,
1706
+ stampWith,
1605
1707
  });
1606
1708
  }, {
1607
1709
  errorMessage: "Failed to get or create P-256 API key user",
@@ -1617,6 +1719,8 @@ class TurnkeyClient {
1617
1719
  * - If it does not exist, it is created and returned with its new `policyId`.
1618
1720
  *
1619
1721
  * @param params.policies - the list of policies to fetch or create.
1722
+ * @param params.stampWith - parameter to stamp the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
1723
+ * @param params.organizationId - organization ID to specify the sub-organization (defaults to the current session's organizationId).
1620
1724
  * @returns A promise that resolves to an array of objects, each containing:
1621
1725
  * - `policyId`: the unique identifier of the policy.
1622
1726
  * - `policyName`: human-readable name of the policy.
@@ -1628,20 +1732,20 @@ class TurnkeyClient {
1628
1732
  * if fetching policies fails, or if creating policies fails.
1629
1733
  */
1630
1734
  this.fetchOrCreatePolicies = async (params) => {
1631
- const { policies } = params;
1735
+ const { policies, stampWith } = params;
1632
1736
  return await withTurnkeyErrorHandling(async () => {
1633
- const session = await this.storageManager.getActiveSession();
1634
- if (!session) {
1635
- throw new TurnkeyError("No active session found. Please log in first.", TurnkeyErrorCodes.NO_SESSION_FOUND);
1636
- }
1737
+ const session = await getActiveSessionOrThrowIfRequired(stampWith, this.storageManager.getActiveSession);
1637
1738
  if (!Array.isArray(policies) || policies.length === 0) {
1638
1739
  throw new TurnkeyError("'policies' must be a non-empty array of policy definitions.", TurnkeyErrorCodes.INVALID_REQUEST);
1639
1740
  }
1640
- const organizationId = session.organizationId;
1741
+ const organizationId = params?.organizationId ?? session?.organizationId;
1742
+ if (!organizationId) {
1743
+ throw new TurnkeyError("Organization ID is required to fetch or create policies.", TurnkeyErrorCodes.INVALID_REQUEST);
1744
+ }
1641
1745
  // we first fetch existing policies
1642
1746
  const existingPoliciesResponse = await this.httpClient.getPolicies({
1643
1747
  organizationId,
1644
- });
1748
+ }, stampWith);
1645
1749
  const existingPolicies = existingPoliciesResponse.policies || [];
1646
1750
  // we create a map of existing policies by their signature
1647
1751
  // where the policySignature maps to its policyId
@@ -1674,10 +1778,10 @@ class TurnkeyClient {
1674
1778
  const createPoliciesResponse = await this.httpClient.createPolicies({
1675
1779
  organizationId,
1676
1780
  policies: missingPolicies,
1677
- });
1781
+ }, stampWith);
1678
1782
  // assign returned IDs back to the missing ones in order
1679
1783
  if (!createPoliciesResponse || !createPoliciesResponse.policyIds) {
1680
- throw new TurnkeyError("Failed to create missing delegated access policies", TurnkeyErrorCodes.CREATE_POLICY_ERROR);
1784
+ throw new TurnkeyError("Failed to create missing policies", TurnkeyErrorCodes.CREATE_POLICY_ERROR);
1681
1785
  }
1682
1786
  const newlyCreatedPolicies = missingPolicies.map((p, idx) => ({
1683
1787
  ...p,
@@ -1689,7 +1793,7 @@ class TurnkeyClient {
1689
1793
  // which includes each of their respective IDs
1690
1794
  return [...alreadyExistingPolicies, ...newlyCreatedPolicies];
1691
1795
  }, {
1692
- errorMessage: "Failed to get or create delegated access policies",
1796
+ errorMessage: "Failed to get or create policies",
1693
1797
  errorCode: TurnkeyErrorCodes.CREATE_USERS_ERROR,
1694
1798
  });
1695
1799
  };
@@ -1706,16 +1810,17 @@ class TurnkeyClient {
1706
1810
  * @param params.verificationToken - verification token from OTP email verification (required if verifying the email).
1707
1811
  * @param params.userId - user ID to update a specific user's email (defaults to the current session's userId).
1708
1812
  * @param params.stampWith - parameter to stamp the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
1813
+ * @param params.organizationId - organization ID to specify the sub-organization (defaults to the current session's organizationId).
1709
1814
  * @returns A promise that resolves to the userId of the updated user.
1710
1815
  * @throws {TurnkeyError} If there is no active session, if the userId is missing, or if there is an error updating or verifying the user email.
1711
1816
  */
1712
1817
  this.updateUserEmail = async (params) => {
1713
- const { verificationToken, email, stampWith } = params;
1714
- const session = await this.storageManager.getActiveSession();
1715
- if (!session) {
1716
- throw new TurnkeyError("No active session found. Please log in first.", TurnkeyErrorCodes.NO_SESSION_FOUND);
1818
+ const { verificationToken, email, stampWith, organizationId } = params;
1819
+ const session = await getActiveSessionOrThrowIfRequired(stampWith, this.storageManager.getActiveSession);
1820
+ const userId = params?.userId || session?.userId;
1821
+ if (!userId) {
1822
+ throw new TurnkeyError("User ID must be provided to update user email", TurnkeyErrorCodes.INVALID_REQUEST);
1717
1823
  }
1718
- const userId = params?.userId || session.userId;
1719
1824
  return withTurnkeyErrorHandling(async () => {
1720
1825
  const existingUser = await this.httpClient.proxyGetAccount({
1721
1826
  filterType: FilterType.Email,
@@ -1728,6 +1833,7 @@ class TurnkeyClient {
1728
1833
  userId: userId,
1729
1834
  userEmail: email,
1730
1835
  ...(verificationToken && { verificationToken }),
1836
+ ...(organizationId && { organizationId }),
1731
1837
  }, stampWith);
1732
1838
  if (!res || !res.userId) {
1733
1839
  throw new TurnkeyError("No user ID found in the update user email response", TurnkeyErrorCodes.BAD_RESPONSE);
@@ -1748,20 +1854,22 @@ class TurnkeyClient {
1748
1854
  *
1749
1855
  * @param params.userId - user ID to remove a specific user's email address (defaults to the current session's userId).
1750
1856
  * @param params.stampWith - parameter to stamp the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
1857
+ * @param params.organizationId - organization ID to specify the sub-organization (defaults to the current session's organizationId).
1751
1858
  * @returns A promise that resolves to the userId of the user whose email was removed.
1752
1859
  * @throws {TurnkeyError} If there is no active session, if the userId is missing, or if there is an error removing the user email.
1753
1860
  */
1754
1861
  this.removeUserEmail = async (params) => {
1755
- const { stampWith } = params || {};
1756
- const session = await this.storageManager.getActiveSession();
1757
- if (!session) {
1758
- throw new TurnkeyError("No active session found. Please log in first.", TurnkeyErrorCodes.NO_SESSION_FOUND);
1759
- }
1862
+ const { stampWith, organizationId } = params || {};
1863
+ const session = await getActiveSessionOrThrowIfRequired(stampWith, this.storageManager.getActiveSession);
1760
1864
  return withTurnkeyErrorHandling(async () => {
1761
- const userId = params?.userId || session.userId;
1865
+ const userId = params?.userId || session?.userId;
1866
+ if (!userId) {
1867
+ throw new TurnkeyError("User ID must be provided to remove user email", TurnkeyErrorCodes.INVALID_REQUEST);
1868
+ }
1762
1869
  const res = await this.httpClient.updateUserEmail({
1763
1870
  userId: userId,
1764
1871
  userEmail: "",
1872
+ ...(organizationId && { organizationId }),
1765
1873
  }, stampWith);
1766
1874
  if (!res || !res.userId) {
1767
1875
  throw new TurnkeyError("No user ID found in the remove user email response", TurnkeyErrorCodes.BAD_RESPONSE);
@@ -1785,21 +1893,23 @@ class TurnkeyClient {
1785
1893
  * @param params.verificationToken - verification token from OTP phone verification (required if verifying the phone number).
1786
1894
  * @param params.userId - user ID to update a specific user's phone number (defaults to the current session's userId).
1787
1895
  * @param params.stampWith - parameter to stamp the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
1896
+ * @param params.organizationId - organization ID to specify the sub-organization (defaults to the current session's organizationId).
1788
1897
  * @returns A promise that resolves to the userId of the updated user.
1789
1898
  * @throws {TurnkeyError} If there is no active session, if the userId is missing, or if there is an error updating or verifying the user phone number.
1790
1899
  */
1791
1900
  this.updateUserPhoneNumber = async (params) => {
1792
- const { verificationToken, phoneNumber, stampWith } = params;
1793
- const session = await this.storageManager.getActiveSession();
1794
- if (!session) {
1795
- throw new TurnkeyError("No active session found. Please log in first.", TurnkeyErrorCodes.NO_SESSION_FOUND);
1901
+ const { verificationToken, phoneNumber, stampWith, organizationId } = params;
1902
+ const session = await getActiveSessionOrThrowIfRequired(stampWith, this.storageManager.getActiveSession);
1903
+ const userId = params?.userId || session?.userId;
1904
+ if (!userId) {
1905
+ throw new TurnkeyError("User ID must be provided to update user phone number", TurnkeyErrorCodes.INVALID_REQUEST);
1796
1906
  }
1797
- const userId = params?.userId || session.userId;
1798
1907
  return withTurnkeyErrorHandling(async () => {
1799
1908
  const res = await this.httpClient.updateUserPhoneNumber({
1800
1909
  userId,
1801
1910
  userPhoneNumber: phoneNumber,
1802
1911
  ...(verificationToken && { verificationToken }),
1912
+ ...(organizationId && { organizationId }),
1803
1913
  }, stampWith);
1804
1914
  if (!res || !res.userId) {
1805
1915
  throw new TurnkeyError("Failed to update user phone number", TurnkeyErrorCodes.UPDATE_USER_PHONE_NUMBER_ERROR);
@@ -1820,20 +1930,22 @@ class TurnkeyClient {
1820
1930
  *
1821
1931
  * @param params.userId - user ID to remove a specific user's phone number (defaults to the current session's userId).
1822
1932
  * @param params.stampWith - parameter to stamp the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
1933
+ * @param params.organizationId - organization ID to specify the sub-organization (defaults to the current session's organizationId).
1823
1934
  * @returns A promise that resolves to the userId of the user whose phone number was removed.
1824
1935
  * @throws {TurnkeyError} If there is no active session, if the userId is missing, or if there is an error removing the user phone number.
1825
1936
  */
1826
1937
  this.removeUserPhoneNumber = async (params) => {
1827
- const { stampWith } = params || {};
1828
- const session = await this.storageManager.getActiveSession();
1829
- if (!session) {
1830
- throw new TurnkeyError("No active session found. Please log in first.", TurnkeyErrorCodes.NO_SESSION_FOUND);
1938
+ const { stampWith, organizationId } = params || {};
1939
+ const session = await getActiveSessionOrThrowIfRequired(stampWith, this.storageManager.getActiveSession);
1940
+ const userId = params?.userId || session?.userId;
1941
+ if (!userId) {
1942
+ throw new TurnkeyError("User ID must be provided to remove user phone number", TurnkeyErrorCodes.INVALID_REQUEST);
1831
1943
  }
1832
- const userId = params?.userId || session.userId;
1833
1944
  return withTurnkeyErrorHandling(async () => {
1834
1945
  const res = await this.httpClient.updateUserPhoneNumber({
1835
1946
  userId,
1836
1947
  userPhoneNumber: "",
1948
+ ...(organizationId && { organizationId }),
1837
1949
  }, stampWith);
1838
1950
  if (!res || !res.userId) {
1839
1951
  throw new TurnkeyError("Failed to remove user phone number", TurnkeyErrorCodes.UPDATE_USER_PHONE_NUMBER_ERROR);
@@ -1856,20 +1968,22 @@ class TurnkeyClient {
1856
1968
  * @param params.userName - new name to set for the user.
1857
1969
  * @param params.userId - user ID to update a specific user's name (defaults to the current session's userId).
1858
1970
  * @param params.stampWith - parameter to stamp the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
1971
+ * @param params.organizationId - organization ID to specify the sub-organization (defaults to the current session's organizationId).
1859
1972
  * @returns A promise that resolves to the userId of the updated user.
1860
1973
  * @throws {TurnkeyError} If there is no active session, if the userId is missing, or if there is an error updating the user name.
1861
1974
  */
1862
1975
  this.updateUserName = async (params) => {
1863
- const { userName, stampWith } = params;
1864
- const session = await this.storageManager.getActiveSession();
1865
- if (!session) {
1866
- throw new TurnkeyError("No active session found. Please log in first.", TurnkeyErrorCodes.NO_SESSION_FOUND);
1976
+ const { userName, stampWith, organizationId } = params;
1977
+ const session = await getActiveSessionOrThrowIfRequired(stampWith, this.storageManager.getActiveSession);
1978
+ const userId = params?.userId || session?.userId;
1979
+ if (!userId) {
1980
+ throw new TurnkeyError("User ID must be provided to update user name", TurnkeyErrorCodes.INVALID_REQUEST);
1867
1981
  }
1868
- const userId = params?.userId || session.userId;
1869
1982
  return withTurnkeyErrorHandling(async () => {
1870
1983
  const res = await this.httpClient.updateUserName({
1871
1984
  userId,
1872
1985
  userName,
1986
+ ...(organizationId && { organizationId }),
1873
1987
  }, stampWith);
1874
1988
  if (!res || !res.userId) {
1875
1989
  throw new TurnkeyError("No user ID found in the update user name response", TurnkeyErrorCodes.BAD_RESPONSE);
@@ -1892,6 +2006,7 @@ class TurnkeyClient {
1892
2006
  *
1893
2007
  * @param params.providerName - name of the OAuth provider to add (e.g., "Google", "Apple").
1894
2008
  * @param params.oidcToken - OIDC token for the OAuth provider.
2009
+ * @param params.organizationId - organization ID to specify the sub-organization (defaults to the current session's organizationId).
1895
2010
  * @param params.userId - user ID to add the provider for a specific user (defaults to current session's userId).
1896
2011
  * @param params.stampWith - parameter to stamp the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
1897
2012
  * @returns A promise that resolves to an array of provider IDs associated with the user.
@@ -1899,29 +2014,34 @@ class TurnkeyClient {
1899
2014
  */
1900
2015
  this.addOauthProvider = async (params) => {
1901
2016
  const { providerName, oidcToken, stampWith } = params;
1902
- const session = await this.storageManager.getActiveSession();
1903
- if (!session) {
1904
- throw new TurnkeyError("No active session found. Please log in first.", TurnkeyErrorCodes.NO_SESSION_FOUND);
1905
- }
2017
+ const session = await getActiveSessionOrThrowIfRequired(stampWith, this.storageManager.getActiveSession);
1906
2018
  return withTurnkeyErrorHandling(async () => {
1907
2019
  const accountRes = await this.httpClient.proxyGetAccount({
1908
2020
  filterType: "OIDC_TOKEN",
1909
2021
  filterValue: oidcToken,
1910
2022
  });
1911
2023
  if (!accountRes) {
1912
- throw new TurnkeyError(`Account fetch failed}`, TurnkeyErrorCodes.ACCOUNT_FETCH_ERROR);
2024
+ throw new TurnkeyError(`Account fetch failed`, TurnkeyErrorCodes.ACCOUNT_FETCH_ERROR);
1913
2025
  }
1914
2026
  if (accountRes.organizationId) {
1915
2027
  throw new TurnkeyError("Account already exists with this OIDC token", TurnkeyErrorCodes.ACCOUNT_ALREADY_EXISTS);
1916
2028
  }
1917
- const userId = params?.userId || session.userId;
1918
- const { email: oidcEmail, iss } = jwtDecode(oidcToken) || {}; // Parse the oidc token so we can get the email. Pass it in to updateUser then call createOauthProviders. This will be verified by Turnkey.
2029
+ const userId = params?.userId || session?.userId;
2030
+ if (!userId) {
2031
+ throw new TurnkeyError("User ID must be provided to add OAuth provider", TurnkeyErrorCodes.INVALID_REQUEST);
2032
+ }
2033
+ const organizationId = params?.organizationId ?? session?.organizationId;
2034
+ if (!organizationId) {
2035
+ throw new TurnkeyError("Organization ID is required to add OAuth provider", TurnkeyErrorCodes.INVALID_REQUEST);
2036
+ }
2037
+ // parse the oidc token so we can get the email. Pass it in to updateUser then call createOauthProviders. This will be verified by Turnkey.
2038
+ const { email: oidcEmail, iss } = jwtDecode(oidcToken) || {};
1919
2039
  if (iss === googleISS) {
1920
2040
  const verifiedSuborg = await this.httpClient.proxyGetAccount({
1921
2041
  filterType: "EMAIL",
1922
2042
  filterValue: oidcEmail,
1923
2043
  });
1924
- const isVerified = verifiedSuborg.organizationId === session.organizationId;
2044
+ const isVerified = verifiedSuborg.organizationId === organizationId;
1925
2045
  const user = await this.fetchUser({
1926
2046
  userId,
1927
2047
  stampWith,
@@ -1964,20 +2084,22 @@ class TurnkeyClient {
1964
2084
  * @param params.providerIds - IDs of the OAuth providers to remove.
1965
2085
  * @param params.userId - user ID to remove the provider for a specific user (defaults to the current session's userId).
1966
2086
  * @param params.stampWith - parameter to stamp the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
2087
+ * @param params.organizationId - organization ID to specify the sub-organization (defaults to the current session's organizationId).
1967
2088
  * @returns A promise that resolves to an array of provider IDs that were removed.
1968
2089
  * @throws {TurnkeyError} If there is no active session, if the userId is missing, or if there is an error removing the OAuth provider.
1969
2090
  */
1970
2091
  this.removeOauthProviders = async (params) => {
1971
- const { providerIds, stampWith } = params;
1972
- const session = await this.storageManager.getActiveSession();
1973
- if (!session) {
1974
- throw new TurnkeyError("No active session found. Please log in first.", TurnkeyErrorCodes.NO_SESSION_FOUND);
2092
+ const { providerIds, stampWith, organizationId } = params;
2093
+ const session = await getActiveSessionOrThrowIfRequired(stampWith, this.storageManager.getActiveSession);
2094
+ const userId = params?.userId || session?.userId;
2095
+ if (!userId) {
2096
+ throw new TurnkeyError("User ID must be provided to remove OAuth provider", TurnkeyErrorCodes.INVALID_REQUEST);
1975
2097
  }
1976
- const userId = params?.userId || session.userId;
1977
2098
  return withTurnkeyErrorHandling(async () => {
1978
2099
  const res = await this.httpClient.deleteOauthProviders({
1979
2100
  userId,
1980
2101
  providerIds,
2102
+ ...(organizationId && { organizationId }),
1981
2103
  }, stampWith);
1982
2104
  if (!res) {
1983
2105
  throw new TurnkeyError("Failed to remove OAuth provider", TurnkeyErrorCodes.REMOVE_OAUTH_PROVIDER_ERROR);
@@ -2001,21 +2123,21 @@ class TurnkeyClient {
2001
2123
  * @param params.displayName - display name of the passkey (defaults to the value of `name`).
2002
2124
  * @param params.userId - user ID to add the passkey for a specific user (defaults to the current session's userId).
2003
2125
  * @param params.stampWith - parameter to stamp the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
2126
+ * @param params.organizationId - organization ID to specify the sub-organization (defaults to the current session's organizationId).
2004
2127
  * @returns A promise that resolves to an array of authenticator IDs for the newly added passkey(s).
2005
2128
  * @throws {TurnkeyError} If there is no active session, if passkey creation fails, or if there is an error adding the passkey.
2006
2129
  */
2007
2130
  this.addPasskey = async (params) => {
2008
- const { stampWith } = params || {};
2131
+ const { stampWith, organizationId } = params || {};
2009
2132
  const name = params?.name || `Turnkey Passkey-${Date.now()}`;
2010
2133
  return withTurnkeyErrorHandling(async () => {
2011
- const session = await this.storageManager.getActiveSession();
2012
- if (!session) {
2013
- throw new TurnkeyError("No active session found. Please log in first.", TurnkeyErrorCodes.NO_SESSION_FOUND);
2134
+ const session = await getActiveSessionOrThrowIfRequired(stampWith, this.storageManager.getActiveSession);
2135
+ const userId = params?.userId || session?.userId;
2136
+ if (!userId) {
2137
+ throw new TurnkeyError("User ID must be provided to add passkey", TurnkeyErrorCodes.INVALID_REQUEST);
2014
2138
  }
2015
- const userId = params?.userId || session.userId;
2016
2139
  const { encodedChallenge, attestation } = await this.createPasskey({
2017
2140
  name,
2018
- ...(stampWith && { stampWith }),
2019
2141
  });
2020
2142
  if (!attestation || !encodedChallenge) {
2021
2143
  throw new TurnkeyError("Failed to create passkey challenge and attestation", TurnkeyErrorCodes.CREATE_PASSKEY_ERROR);
@@ -2029,6 +2151,7 @@ class TurnkeyClient {
2029
2151
  attestation,
2030
2152
  },
2031
2153
  ],
2154
+ ...(organizationId && { organizationId }),
2032
2155
  }, stampWith);
2033
2156
  return res?.authenticatorIds || [];
2034
2157
  }, {
@@ -2048,20 +2171,22 @@ class TurnkeyClient {
2048
2171
  * @param params.authenticatorIds - IDs of the authenticators (passkeys) to remove.
2049
2172
  * @param params.userId - user ID to remove the passkeys for a specific user (defaults to the current session's userId).
2050
2173
  * @param params.stampWith - parameter to stamp the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
2174
+ * @param params.organizationId - organization ID to specify the sub-organization (defaults to the current session's organizationId).
2051
2175
  * @returns A promise that resolves to an array of authenticator IDs that were removed.
2052
2176
  * @throws {TurnkeyError} If there is no active session, if the userId is missing, or if there is an error removing the passkeys.
2053
2177
  */
2054
2178
  this.removePasskeys = async (params) => {
2055
- const { authenticatorIds, stampWith } = params;
2056
- const session = await this.storageManager.getActiveSession();
2057
- if (!session) {
2058
- throw new TurnkeyError("No active session found. Please log in first.", TurnkeyErrorCodes.NO_SESSION_FOUND);
2179
+ const { authenticatorIds, stampWith, organizationId } = params;
2180
+ const session = await getActiveSessionOrThrowIfRequired(stampWith, this.storageManager.getActiveSession);
2181
+ const userId = params?.userId || session?.userId;
2182
+ if (!userId) {
2183
+ throw new TurnkeyError("User ID must be provided to remove passkey", TurnkeyErrorCodes.INVALID_REQUEST);
2059
2184
  }
2060
- const userId = params?.userId || session.userId;
2061
2185
  return withTurnkeyErrorHandling(async () => {
2062
2186
  const res = await this.httpClient.deleteAuthenticators({
2063
2187
  userId,
2064
2188
  authenticatorIds,
2189
+ ...(organizationId && { organizationId }),
2065
2190
  }, stampWith);
2066
2191
  if (!res) {
2067
2192
  throw new TurnkeyError("No response found in the remove passkey response", TurnkeyErrorCodes.REMOVE_PASSKEY_ERROR);
@@ -2092,10 +2217,11 @@ class TurnkeyClient {
2092
2217
  * @throws {TurnkeyError} If there is no active session or if there is an error creating the wallet.
2093
2218
  */
2094
2219
  this.createWallet = async (params) => {
2095
- const { walletName, accounts, organizationId, mnemonicLength, stampWith } = params;
2096
- const session = await this.storageManager.getActiveSession();
2097
- if (!session) {
2098
- throw new TurnkeyError("No active session found. Please log in first.", TurnkeyErrorCodes.NO_SESSION_FOUND);
2220
+ const { walletName, accounts, organizationId: organizationIdFromParams, mnemonicLength, stampWith, } = params;
2221
+ const session = await getActiveSessionOrThrowIfRequired(stampWith, this.storageManager.getActiveSession);
2222
+ const organizationId = organizationIdFromParams || session?.organizationId;
2223
+ if (!organizationId) {
2224
+ throw new TurnkeyError("Organization ID must be provided to create wallet", TurnkeyErrorCodes.INVALID_REQUEST);
2099
2225
  }
2100
2226
  let walletAccounts = [];
2101
2227
  if (accounts && !isWalletAccountArray(accounts)) {
@@ -2111,7 +2237,7 @@ class TurnkeyClient {
2111
2237
  }
2112
2238
  return withTurnkeyErrorHandling(async () => {
2113
2239
  const res = await this.httpClient.createWallet({
2114
- organizationId: organizationId || session.organizationId,
2240
+ organizationId: organizationId,
2115
2241
  walletName,
2116
2242
  accounts: walletAccounts,
2117
2243
  mnemonicLength: mnemonicLength || 12,
@@ -2143,10 +2269,11 @@ class TurnkeyClient {
2143
2269
  * @throws {TurnkeyError} If there is no active session, if the wallet does not exist, or if there is an error creating the wallet accounts.
2144
2270
  */
2145
2271
  this.createWalletAccounts = async (params) => {
2146
- const { accounts, walletId, organizationId, stampWith } = params;
2147
- const session = await this.storageManager.getActiveSession();
2148
- if (!session) {
2149
- throw new TurnkeyError("No active session found. Please log in first.", TurnkeyErrorCodes.NO_SESSION_FOUND);
2272
+ const { accounts, walletId, organizationId: organizationIdFromParams, stampWith, } = params;
2273
+ const session = await getActiveSessionOrThrowIfRequired(stampWith, this.storageManager.getActiveSession);
2274
+ const organizationId = organizationIdFromParams || session?.organizationId;
2275
+ if (!organizationId) {
2276
+ throw new TurnkeyError("Organization ID must be provided to create wallet accounts", TurnkeyErrorCodes.INVALID_REQUEST);
2150
2277
  }
2151
2278
  return withTurnkeyErrorHandling(async () => {
2152
2279
  let walletAccounts = [];
@@ -2154,7 +2281,7 @@ class TurnkeyClient {
2154
2281
  // Query existing wallet accounts to avoid duplicates
2155
2282
  const existingWalletAccounts = await this.httpClient.getWalletAccounts({
2156
2283
  walletId,
2157
- organizationId: organizationId || session.organizationId,
2284
+ organizationId: organizationId,
2158
2285
  paginationOptions: { limit: "100" },
2159
2286
  }, stampWith);
2160
2287
  walletAccounts = generateWalletAccountsFromAddressFormat({
@@ -2166,7 +2293,7 @@ class TurnkeyClient {
2166
2293
  walletAccounts = accounts;
2167
2294
  }
2168
2295
  const res = await this.httpClient.createWalletAccounts({
2169
- organizationId: organizationId || session.organizationId,
2296
+ organizationId: organizationId,
2170
2297
  walletId,
2171
2298
  accounts: walletAccounts,
2172
2299
  }, stampWith);
@@ -2197,16 +2324,17 @@ class TurnkeyClient {
2197
2324
  * @throws {TurnkeyError} If there is no active session, if the targetPublicKey is missing, or if there is an error exporting the wallet.
2198
2325
  */
2199
2326
  this.exportWallet = async (params) => {
2200
- const { walletId, targetPublicKey, stampWith, organizationId } = params;
2201
- const session = await this.storageManager.getActiveSession();
2202
- if (!session) {
2203
- throw new TurnkeyError("No active session found. Please log in first.", TurnkeyErrorCodes.NO_SESSION_FOUND);
2327
+ const { walletId, targetPublicKey, stampWith, organizationId: organizationIdFromParams, } = params;
2328
+ const session = await getActiveSessionOrThrowIfRequired(stampWith, this.storageManager.getActiveSession);
2329
+ const organizationId = organizationIdFromParams || session?.organizationId;
2330
+ if (!organizationId) {
2331
+ throw new TurnkeyError("Organization ID must be provided to export wallet", TurnkeyErrorCodes.INVALID_REQUEST);
2204
2332
  }
2205
2333
  return withTurnkeyErrorHandling(async () => {
2206
2334
  const res = await this.httpClient.exportWallet({
2207
2335
  walletId,
2208
2336
  targetPublicKey,
2209
- organizationId: organizationId || session.organizationId,
2337
+ organizationId: organizationId,
2210
2338
  }, stampWith);
2211
2339
  if (!res.exportBundle) {
2212
2340
  throw new TurnkeyError("No export bundle found in the response", TurnkeyErrorCodes.BAD_RESPONSE);
@@ -2234,16 +2362,17 @@ class TurnkeyClient {
2234
2362
  * @throws {TurnkeyError} If there is no active session, if the targetPublicKey is missing, or if there is an error exporting the private key.
2235
2363
  */
2236
2364
  this.exportPrivateKey = async (params) => {
2237
- const { privateKeyId, targetPublicKey, stampWith, organizationId } = params;
2238
- const session = await this.storageManager.getActiveSession();
2239
- if (!session) {
2240
- throw new TurnkeyError("No active session found. Please log in first.", TurnkeyErrorCodes.NO_SESSION_FOUND);
2365
+ const { privateKeyId, targetPublicKey, stampWith, organizationId: organizationIdFromParams, } = params;
2366
+ const session = await getActiveSessionOrThrowIfRequired(stampWith, this.storageManager.getActiveSession);
2367
+ const organizationId = organizationIdFromParams || session?.organizationId;
2368
+ if (!organizationId) {
2369
+ throw new TurnkeyError("Organization ID must be provided to export private key", TurnkeyErrorCodes.INVALID_REQUEST);
2241
2370
  }
2242
2371
  return withTurnkeyErrorHandling(async () => {
2243
2372
  const res = await this.httpClient.exportPrivateKey({
2244
2373
  privateKeyId,
2245
2374
  targetPublicKey,
2246
- organizationId: organizationId || session.organizationId,
2375
+ organizationId: organizationId,
2247
2376
  }, stampWith);
2248
2377
  if (!res.exportBundle) {
2249
2378
  throw new TurnkeyError("No export bundle found in the response", TurnkeyErrorCodes.BAD_RESPONSE);
@@ -2272,16 +2401,17 @@ class TurnkeyClient {
2272
2401
  *
2273
2402
  */
2274
2403
  this.exportWalletAccount = async (params) => {
2275
- const { address, targetPublicKey, stampWith, organizationId } = params;
2276
- const session = await this.storageManager.getActiveSession();
2277
- if (!session) {
2278
- throw new TurnkeyError("No active session found. Please log in first.", TurnkeyErrorCodes.NO_SESSION_FOUND);
2404
+ const { address, targetPublicKey, stampWith, organizationId: organizationIdFromParams, } = params;
2405
+ const session = await getActiveSessionOrThrowIfRequired(stampWith, this.storageManager.getActiveSession);
2406
+ const organizationId = organizationIdFromParams || session?.organizationId;
2407
+ if (!organizationId) {
2408
+ throw new TurnkeyError("Organization ID must be provided to export wallet account", TurnkeyErrorCodes.INVALID_REQUEST);
2279
2409
  }
2280
2410
  return withTurnkeyErrorHandling(async () => {
2281
2411
  const res = await this.httpClient.exportWalletAccount({
2282
2412
  address,
2283
2413
  targetPublicKey,
2284
- organizationId: organizationId || session.organizationId,
2414
+ organizationId: organizationId,
2285
2415
  }, stampWith);
2286
2416
  if (!res.exportBundle) {
2287
2417
  throw new TurnkeyError("No export bundle found in the response", TurnkeyErrorCodes.BAD_RESPONSE);
@@ -2305,21 +2435,27 @@ class TurnkeyClient {
2305
2435
  * @param params.encryptedBundle - encrypted bundle containing the wallet seed phrase and metadata.
2306
2436
  * @param params.walletName - name of the wallet to create upon import.
2307
2437
  * @param params.accounts - array of account parameters to create in the imported wallet (defaults to standard Ethereum and Solana accounts).
2438
+ * @param params.organizationId - organization ID to import the wallet under a specific sub-organization (wallet will be associated with the sub-organization).
2308
2439
  * @param params.userId - user ID to import the wallet for a specific user (defaults to the current session's userId).
2309
2440
  * @param params.stampWith - parameter to stamp the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
2310
2441
  * @returns A promise that resolves to the ID of the imported wallet.
2311
2442
  * @throws {TurnkeyError} If there is no active session, if the encrypted bundle is invalid, or if there is an error importing the wallet.
2312
2443
  */
2313
2444
  this.importWallet = async (params) => {
2314
- const { encryptedBundle, accounts, walletName, userId, stampWith } = params;
2315
- const session = await this.storageManager.getActiveSession();
2316
- if (!session) {
2317
- throw new TurnkeyError("No active session found. Please log in first.", TurnkeyErrorCodes.NO_SESSION_FOUND);
2445
+ const { encryptedBundle, accounts, walletName, organizationId: organizationIdFromParams, userId: userIdFromParams, stampWith, } = params;
2446
+ const session = await getActiveSessionOrThrowIfRequired(stampWith, this.storageManager.getActiveSession);
2447
+ const organizationId = organizationIdFromParams || session?.organizationId;
2448
+ if (!organizationId) {
2449
+ throw new TurnkeyError("Organization ID must be provided to import wallet", TurnkeyErrorCodes.INVALID_REQUEST);
2450
+ }
2451
+ const userId = userIdFromParams || session?.userId;
2452
+ if (!userId) {
2453
+ throw new TurnkeyError("User ID must be provided to import wallet", TurnkeyErrorCodes.INVALID_REQUEST);
2318
2454
  }
2319
2455
  return withTurnkeyErrorHandling(async () => {
2320
2456
  const res = await this.httpClient.importWallet({
2321
- organizationId: session.organizationId,
2322
- userId: userId || session.userId,
2457
+ organizationId: organizationId,
2458
+ userId: userId,
2323
2459
  encryptedBundle,
2324
2460
  walletName,
2325
2461
  accounts: accounts || [
@@ -2357,21 +2493,27 @@ class TurnkeyClient {
2357
2493
  * @param params.privateKeyName - name of the private key to create upon import.
2358
2494
  * @param params.curve - the cryptographic curve used to generate a given private key
2359
2495
  * @param params.addressFormat - address format of the private key to import.
2496
+ * @param params.organizationId - organization ID to import the private key under a specific sub-organization (private key will be associated with the sub-organization).
2360
2497
  * @param params.userId - user ID to import the wallet for a specific user (defaults to the current session's userId).
2361
2498
  * @param params.stampWith - parameter to stamp the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
2362
2499
  * @returns A promise that resolves to the ID of the imported wallet.
2363
2500
  * @throws {TurnkeyError} If there is no active session, if the encrypted bundle is invalid, or if there is an error importing the wallet.
2364
2501
  */
2365
2502
  this.importPrivateKey = async (params) => {
2366
- const { encryptedBundle, privateKeyName, addressFormats, curve, userId, stampWith, } = params;
2367
- const session = await this.storageManager.getActiveSession();
2368
- if (!session) {
2369
- throw new TurnkeyError("No active session found. Please log in first.", TurnkeyErrorCodes.NO_SESSION_FOUND);
2503
+ const { encryptedBundle, privateKeyName, addressFormats, curve, organizationId: organizationIdFromParams, userId: userIdFromParams, stampWith, } = params;
2504
+ const session = await getActiveSessionOrThrowIfRequired(stampWith, this.storageManager.getActiveSession);
2505
+ const organizationId = organizationIdFromParams || session?.organizationId;
2506
+ if (!organizationId) {
2507
+ throw new TurnkeyError("Organization ID must be provided to import private key", TurnkeyErrorCodes.INVALID_REQUEST);
2508
+ }
2509
+ const userId = userIdFromParams || session?.userId;
2510
+ if (!userId) {
2511
+ throw new TurnkeyError("User ID must be provided to import private key", TurnkeyErrorCodes.INVALID_REQUEST);
2370
2512
  }
2371
2513
  return withTurnkeyErrorHandling(async () => {
2372
2514
  const res = await this.httpClient.importPrivateKey({
2373
- organizationId: session.organizationId,
2374
- userId: userId || session.userId,
2515
+ organizationId,
2516
+ userId,
2375
2517
  encryptedBundle,
2376
2518
  privateKeyName,
2377
2519
  curve,
@@ -2402,18 +2544,17 @@ class TurnkeyClient {
2402
2544
  * - Optionally allows stamping the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
2403
2545
  *
2404
2546
  * @param params.deleteWithoutExport - flag to delete the sub-organization without requiring all wallets to be exported first (defaults to false).
2547
+ * @param params.organizationId - organization ID to delete a specific sub-organization (defaults to the current session's organizationId).
2405
2548
  * @param params.stampWith - parameter to stamp the request with a specific stamper.
2406
2549
  * @returns A promise that resolves to a `TDeleteSubOrganizationResponse` object containing the result of the deletion.
2407
2550
  * @throws {TurnkeyError} If there is no active session or if there is an error deleting the sub-organization.
2408
2551
  */
2409
2552
  this.deleteSubOrganization = async (params) => {
2410
- const { deleteWithoutExport = false, stampWith } = params || {};
2411
- const session = await this.storageManager.getActiveSession();
2412
- if (!session) {
2413
- throw new TurnkeyError("No active session found. Please log in first.", TurnkeyErrorCodes.NO_SESSION_FOUND);
2414
- }
2553
+ const { deleteWithoutExport = false, organizationId: organizationIdFromParams, stampWith, } = params || {};
2554
+ const session = await getActiveSessionOrThrowIfRequired(stampWith, this.storageManager.getActiveSession);
2555
+ const organizationId = organizationIdFromParams || session?.organizationId;
2415
2556
  return withTurnkeyErrorHandling(async () => {
2416
- return await this.httpClient.deleteSubOrganization({ deleteWithoutExport }, stampWith);
2557
+ return await this.httpClient.deleteSubOrganization({ deleteWithoutExport, ...(organizationId && { organizationId }) }, stampWith);
2417
2558
  }, {
2418
2559
  errorMessage: "Failed to delete sub-organization",
2419
2560
  errorCode: TurnkeyErrorCodes.DELETE_SUB_ORGANIZATION_ERROR,