diff --git a/sdks/golang.mdx b/sdks/golang.mdx
index 64792fe8..b8f37e86 100644
--- a/sdks/golang.mdx
+++ b/sdks/golang.mdx
@@ -1,6 +1,388 @@
---
-title: "Golang"
-description: "Turnkey offers native tooling for interacting with the API using Golang. See [https://github.com/tkhq/go-sdk](https://github.com/tkhq/go-sdk) for more details."
+title: "Go"
+description: "The Turnkey Go SDK provides a native interface for interacting with the Turnkey API, including API key signing, wallet management, and HPKE encryption for secure enclave communication."
+sidebarTitle: "Go"
mode: wide
---
+## Overview
+
+The [Turnkey Go SDK](https://github.com/tkhq/go-sdk) provides a complete implementation for interacting with the Turnkey API from Go applications. It includes:
+
+- **Swagger-generated API client** with 300+ typed request/response models
+- **Request signing** supporting P-256, secp256k1, and ED25519 API keys
+- **HPKE encryption** for secure wallet export/import with Turnkey's enclaves
+- **Nitro attestation verification** for validating enclave proofs
+
+
+Go is the only Turnkey SDK that supports **ED25519 API key signing**, which is useful for chains like Near, Aptos, and Sui that use ED25519 natively.
+
+
+## Installation
+
+```bash
+go get github.com/tkhq/go-sdk
+```
+
+## Authentication
+
+The Go SDK uses API keys to sign requests via the `X-Stamp` header. API keys can be generated on the [Turnkey Dashboard](https://app.turnkey.com) or via the CLI.
+
+### Loading an API Key
+
+```go
+import (
+ sdk "github.com/tkhq/go-sdk"
+ "github.com/tkhq/go-sdk/pkg/apikey"
+ "github.com/tkhq/go-sdk/pkg/store/local"
+)
+
+// Load from ~/.config/turnkey/keys/ directory
+client, err := sdk.New(sdk.WithAPIKeyName("default"))
+
+// Or load from a Turnkey private key string directly
+apiKey, err := apikey.FromTurnkeyPrivateKey(privateKeyHex, apikey.SchemeP256)
+client, err := sdk.New(sdk.WithAPIKey(apiKey))
+```
+
+### Supported Signature Schemes
+
+| Scheme | Constant | Use Case |
+|--------|----------|----------|
+| P-256 (secp256r1) | `apikey.SchemeP256` | Default, most common |
+| secp256k1 | `apikey.SchemeSECP256K1` | Ethereum-compatible |
+| ED25519 | `apikey.SchemeED25519` | Near, Aptos, Sui |
+
+## Client Setup
+
+```go
+import (
+ sdk "github.com/tkhq/go-sdk"
+)
+
+client, err := sdk.New(
+ sdk.WithAPIKeyName("default"),
+ // Optional: custom transport config
+ // sdk.WithTransportConfig(customConfig),
+)
+if err != nil {
+ log.Fatalf("Failed to create client: %v", err)
+}
+```
+
+
+**Important**: You must pass `client.Authenticator` to every API call. The Go SDK does not set a global default authenticator.
+
+```go
+// ✅ Correct - pass Authenticator explicitly
+resp, err := client.V0().Sessions.GetWhoami(params, client.Authenticator)
+
+// ❌ Wrong - missing Authenticator will fail
+resp, err := client.V0().Sessions.GetWhoami(params, nil)
+```
+
+
+## Usage Examples
+
+### Get Whoami
+
+The simplest API call to verify your credentials:
+
+```go
+import (
+ sdk "github.com/tkhq/go-sdk"
+ "github.com/tkhq/go-sdk/pkg/api/client/sessions"
+ "github.com/tkhq/go-sdk/pkg/api/models"
+)
+
+client, _ := sdk.New(sdk.WithAPIKeyName("default"))
+
+params := sessions.NewGetWhoamiParams().WithBody(&models.GetWhoamiRequest{
+ OrganizationID: client.DefaultOrganization(),
+})
+
+resp, err := client.V0().Sessions.GetWhoami(params, client.Authenticator)
+if err != nil {
+ log.Fatalf("Whoami failed: %v", err)
+}
+
+fmt.Printf("User ID: %s\n", *resp.Payload.UserID)
+fmt.Printf("Organization ID: %s\n", *resp.Payload.OrganizationID)
+```
+
+### Create Wallet
+
+```go
+import (
+ "github.com/tkhq/go-sdk/pkg/api/client/wallets"
+ "github.com/tkhq/go-sdk/pkg/api/models"
+ "github.com/tkhq/go-sdk/pkg/util"
+)
+
+walletName := "My Wallet"
+path := "m/44'/60'/0'/0/0"
+
+params := wallets.NewCreateWalletParams().WithBody(&models.CreateWalletRequest{
+ OrganizationID: client.DefaultOrganization(),
+ TimestampMs: util.RequestTimestamp(),
+ Type: (*string)(models.ActivityTypeCreateWallet.Pointer()),
+ Parameters: &models.CreateWalletIntent{
+ WalletName: &walletName,
+ Accounts: []*models.WalletAccountParams{{
+ AddressFormat: models.AddressFormatEthereum.Pointer(),
+ Curve: models.CurveSecp256k1.Pointer(),
+ Path: &path,
+ PathFormat: models.PathFormatBip32.Pointer(),
+ }},
+ },
+})
+
+resp, err := client.V0().Wallets.CreateWallet(params, client.Authenticator)
+if err != nil {
+ log.Fatalf("Failed to create wallet: %v", err)
+}
+
+walletID := resp.Payload.Activity.Result.CreateWalletResult.WalletID
+address := resp.Payload.Activity.Result.CreateWalletResult.Addresses[0]
+fmt.Printf("Wallet ID: %s\n", *walletID)
+fmt.Printf("Address: %s\n", *address)
+```
+
+### Sign a Message
+
+```go
+import (
+ "github.com/tkhq/go-sdk/pkg/api/client/signing"
+ "github.com/tkhq/go-sdk/pkg/api/models"
+)
+
+params := signing.NewSignRawPayloadParams().WithBody(&models.SignRawPayloadRequest{
+ OrganizationID: client.DefaultOrganization(),
+ TimestampMs: util.RequestTimestamp(),
+ Type: (*string)(models.ActivityTypeSignRawPayloadV2.Pointer()),
+ Parameters: &models.SignRawPayloadIntentV2{
+ SignWith: &address,
+ Payload: stringPtr("hello world"),
+ Encoding: models.PayloadEncodingTextUtf8.Pointer(),
+ HashFunction: models.HashFunctionKeccak256.Pointer(),
+ },
+})
+
+resp, err := client.V0().Signing.SignRawPayload(params, client.Authenticator)
+if err != nil {
+ log.Fatalf("Failed to sign: %v", err)
+}
+
+signature := resp.Payload.Activity.Result.SignRawPayloadResult
+fmt.Printf("R: %s\n", *signature.R)
+fmt.Printf("S: %s\n", *signature.S)
+fmt.Printf("V: %s\n", *signature.V)
+```
+
+### Email OTP Authentication
+
+```go
+import (
+ "github.com/tkhq/go-sdk/pkg/api/client/sessions"
+ "github.com/tkhq/go-sdk/pkg/api/models"
+)
+
+// Step 1: Initialize OTP
+initParams := sessions.NewInitOtpParams().WithBody(&models.InitOtpRequest{
+ OrganizationID: &orgID,
+ TimestampMs: util.RequestTimestamp(),
+ Type: (*string)(models.ActivityTypeInitOtp.Pointer()),
+ Parameters: &models.InitOtpIntent{
+ OtpType: models.OtpTypeOtpTypeEmail.Pointer(),
+ Contact: &email,
+ },
+})
+
+initResp, err := client.V0().Sessions.InitOtp(initParams, client.Authenticator)
+// Handle error, extract otpID from result
+
+// Step 2: User receives code, then verify
+verifyParams := sessions.NewOtpParams().WithBody(&models.OtpRequest{
+ OrganizationID: &orgID,
+ TimestampMs: util.RequestTimestamp(),
+ Type: (*string)(models.ActivityTypeOtp.Pointer()),
+ Parameters: &models.OtpIntent{
+ OtpID: &otpID,
+ OtpCode: &userProvidedCode,
+ },
+})
+
+verifyResp, err := client.V0().Sessions.Otp(verifyParams, client.Authenticator)
+
+// Step 3: Create authenticated session
+authParams := sessions.NewOtpAuthParams().WithBody(&models.OtpAuthRequest{
+ OrganizationID: &orgID,
+ TimestampMs: util.RequestTimestamp(),
+ Type: (*string)(models.ActivityTypeOtpAuth.Pointer()),
+ Parameters: &models.OtpAuthIntent{
+ OtpID: &otpID,
+ TargetPublicKey: &targetPublicKey,
+ SessionLengthSeconds: int64Ptr(3600),
+ },
+})
+
+authResp, err := client.V0().Sessions.OtpAuth(authParams, client.Authenticator)
+```
+
+## Activity Polling
+
+
+**The Go SDK does not auto-poll for activity completion.** Unlike TypeScript and Rust, you must implement polling yourself for activities that may be pending.
+
+
+```go
+import (
+ "time"
+ "github.com/tkhq/go-sdk/pkg/api/client/activities"
+ "github.com/tkhq/go-sdk/pkg/api/models"
+)
+
+func pollForCompletion(client *sdk.Client, activityID, orgID string) (*models.V1Activity, error) {
+ maxAttempts := 10
+ for i := 0; i < maxAttempts; i++ {
+ params := activities.NewGetActivityParams().WithBody(&models.GetActivityRequest{
+ OrganizationID: &orgID,
+ ActivityID: &activityID,
+ })
+
+ resp, err := client.V0().Activities.GetActivity(params, client.Authenticator)
+ if err != nil {
+ return nil, err
+ }
+
+ status := resp.Payload.Activity.Status
+ switch *status {
+ case models.ActivityStatusActivityStatusCompleted:
+ return resp.Payload.Activity, nil
+ case models.ActivityStatusActivityStatusFailed:
+ return nil, fmt.Errorf("activity failed")
+ case models.ActivityStatusActivityStatusPending:
+ time.Sleep(time.Second)
+ continue
+ default:
+ return nil, fmt.Errorf("unexpected status: %s", *status)
+ }
+ }
+ return nil, fmt.Errorf("polling timeout")
+}
+```
+
+## HPKE Encryption (Wallet Export)
+
+The Go SDK includes full HPKE support for encrypting/decrypting wallet exports:
+
+```go
+import (
+ "github.com/tkhq/go-sdk/pkg/encryptionkey"
+ "github.com/tkhq/go-sdk/pkg/enclave_encrypt"
+)
+
+// 1. Generate a client-side encryption key
+encryptionKey, err := encryptionkey.New(userID, organizationID)
+
+// 2. Export wallet (encrypted to your target key)
+params := wallets.NewExportWalletParams().WithBody(&models.ExportWalletRequest{
+ OrganizationID: &orgID,
+ TimestampMs: util.RequestTimestamp(),
+ Type: (*string)(models.ActivityTypeExportWallet.Pointer()),
+ Parameters: &models.ExportWalletIntent{
+ WalletID: &walletID,
+ TargetPublicKey: &encryptionKey.TkPublicKey,
+ },
+})
+
+resp, err := client.V0().Wallets.ExportWallet(params, client.Authenticator)
+exportBundle := resp.Payload.Activity.Result.ExportWalletResult.ExportBundle
+
+// 3. Decrypt locally
+kemPrivateKey, _ := encryptionkey.DecodeTurnkeyPrivateKey(encryptionKey.GetPrivateKey())
+signerKey, _ := hexToPublicKey(encryptionkey.SignerProductionPublicKey)
+encryptClient, _ := enclave_encrypt.NewEnclaveEncryptClientFromTargetKey(signerKey, *kemPrivateKey)
+mnemonic, err := encryptClient.Decrypt([]byte(*exportBundle), organizationID)
+```
+
+## Nitro Attestation Verification
+
+The Go SDK can verify AWS Nitro attestation documents from Turnkey's enclaves:
+
+```go
+import (
+ "github.com/tkhq/go-sdk/pkg/proofs"
+)
+
+// Verify an attestation document
+doc, err := proofs.VerifyNitroAttestation(attestationBytes, timestamp)
+if err != nil {
+ log.Fatalf("Attestation verification failed: %v", err)
+}
+
+// Access verified enclave measurements
+fmt.Printf("PCR0: %x\n", doc.PCR0)
+```
+
+## Ethereum Integration
+
+The Go SDK includes examples for integrating with go-ethereum:
+
+```go
+import (
+ "github.com/ethereum/go-ethereum/accounts/abi/bind"
+ "github.com/ethereum/go-ethereum/core/types"
+)
+
+// Create a Turnkey-backed SignerFn for go-ethereum
+func MakeTurnkeySignerFn(client *sdk.Client, signWith string, chainID *big.Int, orgID string) bind.SignerFn {
+ return func(from common.Address, tx *types.Transaction) (*types.Transaction, error) {
+ // Build unsigned EIP-1559 payload
+ unsignedPayload := []any{
+ tx.ChainId(), tx.Nonce(), tx.GasTipCap(), tx.GasFeeCap(),
+ tx.Gas(), tx.To(), tx.Value(), tx.Data(), tx.AccessList(),
+ }
+ rlpBytes, _ := rlp.EncodeToBytes(unsignedPayload)
+ unsigned := append([]byte{types.DynamicFeeTxType}, rlpBytes...)
+
+ // Sign with Turnkey
+ params := signing.NewSignTransactionParams().WithBody(&models.SignTransactionRequest{
+ OrganizationID: &orgID,
+ TimestampMs: util.RequestTimestamp(),
+ Type: (*string)(models.ActivityTypeSignTransactionV2.Pointer()),
+ Parameters: &models.SignTransactionIntentV2{
+ SignWith: &signWith,
+ Type: models.TransactionTypeEthereum.Pointer(),
+ UnsignedTransaction: stringPtr(hex.EncodeToString(unsigned)),
+ },
+ })
+
+ resp, _ := client.V0().Signing.SignTransaction(params, client.Authenticator)
+ rawSigned, _ := hex.DecodeString(*resp.Payload.Activity.Result.SignTransactionResult.SignedTransaction)
+
+ finalTx := new(types.Transaction)
+ finalTx.UnmarshalBinary(rawSigned)
+ return finalTx, nil
+ }
+}
+```
+
+## Examples
+
+The SDK includes working examples in the [`examples/`](https://github.com/tkhq/go-sdk/tree/main/examples) directory:
+
+| Example | Description |
+|---------|-------------|
+| [`whoami`](https://github.com/tkhq/go-sdk/tree/main/examples/whoami) | Basic API call to verify credentials |
+| [`wallets`](https://github.com/tkhq/go-sdk/tree/main/examples/wallets) | Wallet creation and export |
+| [`signing`](https://github.com/tkhq/go-sdk/tree/main/examples/signing) | Transaction and payload signing |
+| [`delegated_access`](https://github.com/tkhq/go-sdk/tree/main/examples/delegated_access) | Sub-organization and policy setup |
+| [`email_otp`](https://github.com/tkhq/go-sdk/tree/main/examples/email_otp) | OTP authentication flow |
+| [`go-ethereum`](https://github.com/tkhq/go-sdk/tree/main/examples/go-ethereum) | Ethereum integration patterns |
+
+## Resources
+
+- [GitHub Repository](https://github.com/tkhq/go-sdk)
+- [pkg.go.dev Documentation](https://pkg.go.dev/github.com/tkhq/go-sdk)
+- [API Reference](/api-reference/overview)
diff --git a/sdks/introduction.mdx b/sdks/introduction.mdx
index 336c4015..adfc6a1a 100644
--- a/sdks/introduction.mdx
+++ b/sdks/introduction.mdx
@@ -34,9 +34,16 @@ Turnkey also has several [wrappers for popular web3 libraries](/category/web3-li
## Server Side SDKs
-| | TypeScript | Go | Ruby | Rust | Python |
-|---------------------|------------|-----|------|------|--------|
-| **Authentication** | | | | | |
-| **Wallet Management** | | | | | |
-| **Policy Management** | | | | | |
+| | TypeScript | Go | Ruby | Rust | Python |
+|------------------------------|------------|-----|------|------|--------|
+| **Authentication** | | | | | |
+| **Wallet Management** | | | | | |
+| **Policy Management** | | | | | |
+| **Activity Auto-Polling** | | | | | * |
+| **HPKE (Export Decrypt)** | | | | | |
+| **Nitro Attestation** | | | | | |
+| **Gas Station** | | | | | |
+| **ED25519 API Key Signing** | | | | | |
+
+\* Python's activity polling uses synchronous `time.sleep()` which blocks the thread. See the [Python SDK docs](/sdks/python#activity-polling) for async workarounds.
diff --git a/sdks/python.mdx b/sdks/python.mdx
index 60162c77..c259a9c4 100644
--- a/sdks/python.mdx
+++ b/sdks/python.mdx
@@ -1,26 +1,381 @@
---
title: "Python"
-description: "Turnkey offers support for interacting with the API using Python. See [https://github.com/tkhq/python-sdk](https://github.com/tkhq/python-sdk) for more details."
+description: "The Turnkey Python SDK provides a type-safe HTTP client with Pydantic v2 models, request stamping for authentication, and automatic activity polling."
+sidebarTitle: "Python"
mode: wide
---
-# Turnkey Python SDK
+## Overview
-This repository contains support for interacting with the Turnkey API using Python.
+The [Turnkey Python SDK](https://github.com/tkhq/python-sdk) is a full-featured SDK for interacting with the Turnkey API from Python applications. It consists of three pip-installable packages:
-Unlike other languages ([Typescript](https://github.com/tkhq/sdk), [Ruby](https://github.com/tkhq/ruby-sdk)), we do not yet offer a full SDK for Rust.
+| Package | PyPI | Description |
+|---------|------|-------------|
+| `turnkey-http` | [turnkey-http](https://pypi.org/project/turnkey-http/) | HTTP client with all API endpoints |
+| `turnkey-api-key-stamper` | [turnkey-api-key-stamper](https://pypi.org/project/turnkey-api-key-stamper/) | P-256 ECDSA request signing |
+| `turnkey-sdk-types` | [turnkey-sdk-types](https://pypi.org/project/turnkey-sdk-types/) | Pydantic v2 type definitions |
-If you are working on a project in Python and would benefit from a Python SDK please open an issue or get in touch with us at [hello@turnkey.com](mailto:hello@turnkey.com) to discuss prioritizing this.
+### Key Features
-## Stamper
+- **100+ typed API methods** with full Pydantic v2 model definitions
+- **Automatic activity polling** with configurable retries
+- **Code generation** from OpenAPI specification
+- **Stamp-then-send** pattern for flexible request handling
-The Stamper utility stamps requests to the Turnkey API and authenticates your calls. To use it, fill out the fields at the top of the stamper script:
+## Installation
+
+```bash
+pip install turnkey-http turnkey-api-key-stamper
+```
+
+Or with Poetry:
+
+```bash
+poetry add turnkey-http turnkey-api-key-stamper
+```
+
+
+**Python 3.8+** is required. The SDK uses Pydantic v2 for type definitions.
+
+
+## Authentication
+
+The SDK uses the `ApiKeyStamper` class to sign requests with your API credentials:
```python
-ENDPOINT = "https://api.turnkey.com/public/v1/whoami"
-API_PUBLIC_KEY = ""
-API_PRIVATE_KEY = ""
-ORG_ID = ""
+from turnkey_api_key_stamper import ApiKeyStamper, ApiKeyStamperConfig
+
+config = ApiKeyStamperConfig(
+ api_public_key="your-api-public-key", # 02... compressed public key
+ api_private_key="your-api-private-key" # hex-encoded private key
+)
+
+stamper = ApiKeyStamper(config)
+```
+
+
+The Python SDK only supports **P-256 (secp256r1)** API key signing. secp256k1 and ED25519 are not supported.
+
+
+### Key Validation
+
+The stamper validates that your public key matches the private key at initialization time, preventing configuration errors.
+
+## Client Setup
+
+```python
+from turnkey_http import TurnkeyClient
+from turnkey_api_key_stamper import ApiKeyStamper, ApiKeyStamperConfig
+
+# Configure the stamper
+config = ApiKeyStamperConfig(
+ api_public_key="your-api-public-key",
+ api_private_key="your-api-private-key"
+)
+stamper = ApiKeyStamper(config)
+
+# Create the client
+client = TurnkeyClient(
+ base_url="https://api.turnkey.com",
+ stamper=stamper,
+ organization_id="your-organization-id",
+ default_timeout=30, # seconds
+ polling_interval_ms=1000, # milliseconds between polls
+ max_polling_retries=10 # increase from default 3 for production
+)
+```
+
+### Configuration Parameters
+
+| Parameter | Default | Description |
+|-----------|---------|-------------|
+| `base_url` | Required | API base URL (`https://api.turnkey.com`) |
+| `stamper` | Required | `ApiKeyStamper` instance for request signing |
+| `organization_id` | Required | Your organization ID |
+| `default_timeout` | `30` | HTTP request timeout in seconds |
+| `polling_interval_ms` | `1000` | Delay between activity polls |
+| `max_polling_retries` | `3` | Max poll attempts before timeout |
+
+
+**Increase `max_polling_retries` for production.** The default of 3 retries is often too low for activities that require consensus or have network delays. We recommend at least 10 retries.
+
+
+## Usage Examples
+
+### Get Whoami
+
+```python
+from turnkey_http import TurnkeyClient
+
+response = client.get_whoami()
+
+print(f"User ID: {response.user_id}")
+print(f"Organization ID: {response.organization_id}")
+print(f"Username: {response.username}")
```
-You can find the script and examples in the [python-sdk repo](https://github.com/tkhq/python-sdk).
+### Create Wallet
+
+```python
+from turnkey_sdk_types import (
+ CreateWalletBody,
+ v1WalletAccountParams,
+ v1Curve,
+ v1PathFormat,
+ v1AddressFormat,
+)
+
+response = client.create_wallet(CreateWalletBody(
+ wallet_name="My Wallet",
+ accounts=[v1WalletAccountParams(
+ curve=v1Curve.CURVE_SECP256K1,
+ path_format=v1PathFormat.PATH_FORMAT_BIP32,
+ path="m/44'/60'/0'/0/0",
+ address_format=v1AddressFormat.ADDRESS_FORMAT_ETHEREUM,
+ )]
+))
+
+print(f"Wallet ID: {response.wallet_id}")
+print(f"Address: {response.addresses[0]}")
+```
+
+### Sign a Message
+
+```python
+from turnkey_sdk_types import (
+ SignRawPayloadBody,
+ v1PayloadEncoding,
+ v1HashFunction,
+)
+
+response = client.sign_raw_payload(SignRawPayloadBody(
+ sign_with=address,
+ payload="hello world",
+ encoding=v1PayloadEncoding.PAYLOAD_ENCODING_TEXT_UTF8,
+ hash_function=v1HashFunction.HASH_FUNCTION_KECCAK256,
+))
+
+print(f"R: {response.r}")
+print(f"S: {response.s}")
+print(f"V: {response.v}")
+```
+
+### Email OTP Authentication
+
+```python
+from turnkey_sdk_types import (
+ InitOtpBody,
+ OtpBody,
+ OtpAuthBody,
+ v1OtpType,
+)
+
+# Step 1: Initialize OTP
+init_response = client.init_otp(InitOtpBody(
+ otp_type=v1OtpType.OTP_TYPE_EMAIL,
+ contact=email,
+ otp_length=6,
+ alphanumeric=True,
+))
+
+otp_id = init_response.otp_id
+print(f"OTP sent, ID: {otp_id}")
+
+# Step 2: User receives code, then verify
+verify_response = client.otp(OtpBody(
+ otp_id=otp_id,
+ otp_code=user_provided_code,
+))
+
+# Step 3: Create authenticated session
+auth_response = client.otp_auth(OtpAuthBody(
+ otp_id=otp_id,
+ target_public_key=target_public_key,
+ session_length_seconds=3600,
+))
+
+print(f"Session created: {auth_response.credential_bundle}")
+```
+
+### Stamp-Then-Send Pattern
+
+For more control, you can stamp a request without sending it:
+
+```python
+from turnkey_sdk_types import CreateWalletBody
+
+# Create a signed request without sending
+signed_request = client.stamp_create_wallet(CreateWalletBody(
+ wallet_name="My Wallet",
+ accounts=[...]
+))
+
+print(f"URL: {signed_request.url}")
+print(f"Body: {signed_request.body}")
+print(f"Stamp Header: {signed_request.stamp.stamp_header_name}")
+print(f"Stamp Value: {signed_request.stamp.stamp_header_value}")
+
+# Later: send the signed request
+response = client.send_signed_request(signed_request, CreateWalletResponse)
+```
+
+This is useful for:
+- Signing on one machine and sending from another
+- Queuing requests for batch processing
+- Debugging request construction
+
+## Activity Polling
+
+The Python SDK automatically polls for activity completion. Activities return when they reach a terminal status:
+
+- `ACTIVITY_STATUS_COMPLETED` — Success
+- `ACTIVITY_STATUS_FAILED` — Failure
+- `ACTIVITY_STATUS_CONSENSUS_NEEDED` — Requires approval
+- `ACTIVITY_STATUS_REJECTED` — Rejected by policy
+
+
+**Blocking Behavior**: The Python SDK uses synchronous `time.sleep()` for polling. This **blocks the thread** until the activity completes or times out.
+
+This is a problem for async Python frameworks like **FastAPI**, **Starlette**, or **Django async views**. Consider running Turnkey calls in a thread pool or using `asyncio.to_thread()`:
+
+```python
+import asyncio
+from turnkey_http import TurnkeyClient
+
+async def create_wallet_async(client: TurnkeyClient, body):
+ return await asyncio.to_thread(client.create_wallet, body)
+```
+
+
+### Result Flattening
+
+When an activity completes, result fields are flattened into the response for convenience:
+
+```python
+response = client.create_wallet(CreateWalletBody(...))
+
+# These are flattened from activity.result.create_wallet_result
+print(response.wallet_id) # Direct access
+print(response.addresses) # Direct access
+
+# You can also access the full activity
+print(response.activity.status)
+print(response.activity.id)
+```
+
+## Limitations
+
+### No Async Support
+
+The SDK uses the synchronous `requests` library. There is no `asyncio` support. For async applications, wrap calls in `asyncio.to_thread()`.
+
+### No HPKE (Wallet Export Decryption)
+
+The Python SDK can call the wallet export API, but **cannot decrypt the result locally**. The export bundle is encrypted with HPKE, and Python lacks the decryption implementation.
+
+For wallet export workflows, use the TypeScript, Go, or Rust SDKs which have full HPKE support.
+
+### P-256 Only
+
+Only P-256 API key signing is supported. secp256k1 and ED25519 API keys cannot be used with the Python SDK.
+
+### Polling Defaults
+
+The default `max_polling_retries=3` is low for production. Activities requiring consensus or experiencing network delays may timeout. Increase to at least 10.
+
+## Error Handling
+
+```python
+from turnkey_sdk_types import TurnkeyNetworkError, TurnkeyError
+
+try:
+ response = client.create_wallet(CreateWalletBody(...))
+except TurnkeyNetworkError as e:
+ print(f"Network error: {e}")
+ print(f"Status code: {e.status_code}")
+ print(f"Error code: {e.code}")
+except TurnkeyError as e:
+ print(f"Turnkey error: {e}")
+ print(f"Cause: {e.cause}")
+```
+
+### Error Types
+
+| Exception | Description |
+|-----------|-------------|
+| `TurnkeyError` | Base exception for all Turnkey errors |
+| `TurnkeyNetworkError` | HTTP errors with status code |
+
+## Integration with Web Frameworks
+
+### Flask
+
+```python
+from flask import Flask, jsonify
+from turnkey_http import TurnkeyClient
+
+app = Flask(__name__)
+client = TurnkeyClient(...)
+
+@app.route("/whoami")
+def whoami():
+ response = client.get_whoami()
+ return jsonify({
+ "user_id": response.user_id,
+ "organization_id": response.organization_id,
+ })
+```
+
+### FastAPI (with thread pool)
+
+```python
+from fastapi import FastAPI
+import asyncio
+from turnkey_http import TurnkeyClient
+
+app = FastAPI()
+client = TurnkeyClient(...)
+
+@app.get("/whoami")
+async def whoami():
+ response = await asyncio.to_thread(client.get_whoami)
+ return {
+ "user_id": response.user_id,
+ "organization_id": response.organization_id,
+ }
+```
+
+### Django
+
+```python
+# settings.py or utils.py
+from turnkey_http import TurnkeyClient
+from turnkey_api_key_stamper import ApiKeyStamper, ApiKeyStamperConfig
+
+TURNKEY_CLIENT = TurnkeyClient(
+ base_url="https://api.turnkey.com",
+ stamper=ApiKeyStamper(ApiKeyStamperConfig(
+ api_public_key=os.environ["TURNKEY_API_PUBLIC_KEY"],
+ api_private_key=os.environ["TURNKEY_API_PRIVATE_KEY"],
+ )),
+ organization_id=os.environ["TURNKEY_ORGANIZATION_ID"],
+)
+
+# views.py
+from django.http import JsonResponse
+from .settings import TURNKEY_CLIENT
+
+def whoami(request):
+ response = TURNKEY_CLIENT.get_whoami()
+ return JsonResponse({
+ "user_id": response.user_id,
+ "organization_id": response.organization_id,
+ })
+```
+
+## Resources
+
+- [GitHub Repository](https://github.com/tkhq/python-sdk)
+- [PyPI - turnkey-http](https://pypi.org/project/turnkey-http/)
+- [PyPI - turnkey-api-key-stamper](https://pypi.org/project/turnkey-api-key-stamper/)
+- [API Reference](/api-reference/overview)
diff --git a/sdks/rust.mdx b/sdks/rust.mdx
index e5a2fa62..aa21195a 100644
--- a/sdks/rust.mdx
+++ b/sdks/rust.mdx
@@ -1,7 +1,427 @@
---
title: "Rust"
-description: "Turnkey offers native tooling for interacting with the API using Rust. See [https://github.com/tkhq/rust-sdk](https://github.com/tkhq/rust-sdk) for more details."
+description: "The Turnkey Rust SDK provides an async HTTP client with automatic activity polling, HPKE encryption for secure enclave communication, and AWS Nitro attestation verification."
+sidebarTitle: "Rust"
mode: wide
---
+## Overview
+The [Turnkey Rust SDK](https://github.com/tkhq/rust-sdk) provides a complete, production-ready implementation for interacting with the Turnkey API. The SDK is organized as a Cargo workspace with four crates:
+
+| Crate | crates.io | Description |
+|-------|-----------|-------------|
+| `turnkey_client` | [turnkey_client](https://crates.io/crates/turnkey_client) | Async HTTP client with all API endpoints |
+| `turnkey_api_key_stamper` | [turnkey_api_key_stamper](https://crates.io/crates/turnkey_api_key_stamper) | P-256 and secp256k1 API key signing |
+| `turnkey_enclave_encrypt` | [turnkey_enclave_encrypt](https://crates.io/crates/turnkey_enclave_encrypt) | HPKE encryption for wallet import/export |
+| `turnkey_proofs` | [turnkey_proofs](https://crates.io/crates/turnkey_proofs) | AWS Nitro attestation verification |
+
+### Key Features
+
+- **Full API coverage** for wallets, policies, signing, OTP, OAuth, and gas station
+- **Automatic activity polling** with configurable exponential backoff
+- **Strongly typed** with code generation from protobuf definitions
+- **`#![forbid(unsafe_code)]`** for memory safety guarantees
+
+## Installation
+
+Add the crates to your `Cargo.toml`:
+
+```toml
+[dependencies]
+turnkey_client = "0.6"
+turnkey_api_key_stamper = "0.6"
+
+# Optional: for wallet export/import
+turnkey_enclave_encrypt = "0.6"
+
+# Optional: for attestation verification
+turnkey_proofs = "0.6"
+```
+
+Or install via Cargo:
+
+```bash
+cargo add turnkey_client turnkey_api_key_stamper
+```
+
+## Authentication
+
+The Rust SDK uses the `Stamp` trait for request signing. Two implementations are provided:
+
+### P-256 (Default)
+
+```rust
+use turnkey_api_key_stamper::TurnkeyP256ApiKey;
+
+// Generate a new key
+let api_key = TurnkeyP256ApiKey::generate();
+
+// Load from hex strings
+let api_key = TurnkeyP256ApiKey::from_strings(
+ "your-private-key-hex",
+ Some("your-public-key-hex".to_string()),
+)?;
+
+// Load from PEM files (tkcli format)
+let api_key = TurnkeyP256ApiKey::from_files(
+ "path/to/private.pem",
+ "path/to/public.pem",
+)?;
+```
+
+### secp256k1 (Alternative)
+
+```rust
+use turnkey_api_key_stamper::TurnkeySecp256k1ApiKey;
+
+let api_key = TurnkeySecp256k1ApiKey::generate();
+```
+
+## Client Setup
+
+The SDK uses a builder pattern for client configuration:
+
+```rust
+use turnkey_client::TurnkeyClient;
+use turnkey_api_key_stamper::TurnkeyP256ApiKey;
+use std::time::Duration;
+
+let api_key = TurnkeyP256ApiKey::from_strings(
+ std::env::var("TURNKEY_API_PRIVATE_KEY")?,
+ Some(std::env::var("TURNKEY_API_PUBLIC_KEY")?),
+)?;
+
+let client = TurnkeyClient::builder()
+ .api_key(api_key)
+ .base_url("https://api.turnkey.com")
+ .organization_id("your-organization-id")
+ .timeout(Duration::from_secs(30))
+ .retry_config(RetryConfig::default())
+ .build()?;
+```
+
+### Retry Configuration
+
+The SDK includes built-in exponential backoff for activity polling:
+
+```rust
+use turnkey_client::RetryConfig;
+
+let config = RetryConfig {
+ initial_delay: Duration::from_millis(500),
+ multiplier: 2.0,
+ max_delay: Duration::from_secs(5),
+ max_retries: 5,
+};
+
+let client = TurnkeyClient::builder()
+ .api_key(api_key)
+ .retry_config(config)
+ .build()?;
+```
+
+## Usage Examples
+
+### Get Whoami
+
+```rust
+use turnkey_client::TurnkeyClient;
+
+#[tokio::main]
+async fn main() -> Result<(), Box> {
+ let client = TurnkeyClient::builder()
+ .api_key(api_key)
+ .organization_id(&org_id)
+ .build()?;
+
+ let response = client.get_whoami(org_id.clone()).await?;
+
+ println!("User ID: {:?}", response.user_id);
+ println!("Organization ID: {:?}", response.organization_id);
+
+ Ok(())
+}
+```
+
+### Create Wallet
+
+```rust
+use turnkey_client::generated::*;
+
+let result = client.create_wallet(
+ org_id.clone(),
+ client.current_timestamp(),
+ CreateWalletIntent {
+ wallet_name: "My Wallet".to_string(),
+ accounts: vec![WalletAccountParams {
+ curve: Curve::Secp256k1,
+ path_format: PathFormat::Bip32,
+ path: "m/44'/60'/0'/0/0".to_string(),
+ address_format: AddressFormat::Ethereum,
+ }],
+ mnemonic_length: None,
+ },
+).await?;
+
+println!("Wallet ID: {:?}", result.result.wallet_id);
+println!("Address: {:?}", result.result.addresses.first());
+```
+
+### Sign a Message
+
+```rust
+use turnkey_client::generated::*;
+
+let signature = client.sign_raw_payload(
+ org_id.clone(),
+ client.current_timestamp(),
+ SignRawPayloadIntentV2 {
+ sign_with: address.clone(),
+ payload: "hello world".to_string(),
+ encoding: PayloadEncoding::TextUtf8,
+ hash_function: HashFunction::Keccak256,
+ },
+).await?;
+
+println!("R: {:?}", signature.result.r);
+println!("S: {:?}", signature.result.s);
+println!("V: {:?}", signature.result.v);
+```
+
+### Email OTP Authentication
+
+```rust
+use turnkey_client::generated::*;
+
+// Step 1: Initialize OTP
+let init_result = client.init_otp(
+ org_id.clone(),
+ client.current_timestamp(),
+ InitOtpIntent {
+ otp_type: OtpType::OtpTypeEmail,
+ contact: email.clone(),
+ email_customization: None,
+ sms_customization: None,
+ user_identifier: None,
+ send_from_email_address: None,
+ redirect_url: None,
+ alphanumeric: Some(true),
+ otp_length: Some(6),
+ },
+).await?;
+
+let otp_id = init_result.result.otp_id;
+
+// Step 2: User receives code, then verify
+let verify_result = client.otp(
+ org_id.clone(),
+ client.current_timestamp(),
+ OtpIntent {
+ otp_id: otp_id.clone(),
+ otp_code: user_provided_code,
+ },
+).await?;
+
+// Step 3: Create authenticated session
+let auth_result = client.otp_auth(
+ org_id.clone(),
+ client.current_timestamp(),
+ OtpAuthIntent {
+ otp_id: otp_id.clone(),
+ target_public_key: target_public_key.clone(),
+ api_key_name: None,
+ expiration_seconds: Some("3600".to_string()),
+ session_length_seconds: None,
+ invalidate_existing: None,
+ },
+).await?;
+```
+
+## Activity Auto-Polling
+
+
+The Rust SDK **automatically polls** for activity completion with configurable exponential backoff. You don't need to implement polling yourself.
+
+
+Activities return an `ActivityResult` that includes metadata:
+
+```rust
+pub struct ActivityResult {
+ pub result: T,
+ pub activity_id: String,
+ pub status: ActivityStatus,
+ pub app_proofs: Vec,
+}
+```
+
+The client handles all polling internally:
+- Starts with a 500ms delay
+- Uses exponential backoff (2x multiplier)
+- Retries up to 5 times by default
+- Returns error on timeout or failure
+
+## HPKE Encryption (Wallet Export/Import)
+
+The `turnkey_enclave_encrypt` crate provides HPKE encryption for secure wallet operations:
+
+### Export Wallet
+
+```rust
+use turnkey_enclave_encrypt::{ExportClient, QuorumPublicKey};
+
+// 1. Create export client with production quorum key
+let mut export_client = ExportClient::new(&QuorumPublicKey::production_signer());
+
+// 2. Request export (target_public_key goes to Turnkey)
+let export_result = client.export_wallet(
+ org_id.clone(),
+ client.current_timestamp(),
+ ExportWalletIntent {
+ wallet_id: wallet_id.clone(),
+ target_public_key: export_client.target_public_key()?,
+ language: None,
+ },
+).await?;
+
+// 3. Decrypt the bundle locally
+let mnemonic = export_client.decrypt_wallet_mnemonic_phrase(
+ export_result.result.export_bundle,
+ org_id.clone(),
+)?;
+
+println!("Mnemonic: {}", mnemonic);
+```
+
+### Import Wallet
+
+```rust
+use turnkey_enclave_encrypt::{ImportClient, QuorumPublicKey};
+
+let mut import_client = ImportClient::new(&QuorumPublicKey::production_signer());
+
+// Encrypt mnemonic for import
+let encrypted_bundle = import_client.encrypt_wallet_mnemonic(
+ mnemonic,
+ org_id.clone(),
+ target_enclave_public_key,
+)?;
+
+// Send to Turnkey
+let import_result = client.import_wallet(
+ org_id.clone(),
+ client.current_timestamp(),
+ ImportWalletIntent {
+ wallet_name: "Imported Wallet".to_string(),
+ encrypted_bundle,
+ accounts: vec![...],
+ user_id: None,
+ },
+).await?;
+```
+
+## Nitro Attestation Verification
+
+The `turnkey_proofs` crate verifies AWS Nitro attestation documents:
+
+```rust
+use turnkey_proofs::{verify, verify_app_proof_signature, get_boot_proof_for_app_proof};
+
+// Enable app proofs on the client
+let client = TurnkeyClient::builder()
+ .api_key(api_key)
+ .build()?
+ .with_app_proofs();
+
+// Create an activity and get proofs
+let result = client.create_wallet(...).await?;
+
+// Verify each app proof
+for app_proof in result.app_proofs {
+ // Verify the app proof signature
+ verify_app_proof_signature(&app_proof)?;
+
+ // Fetch and verify the full proof chain
+ let boot_proof_response = get_boot_proof_for_app_proof(
+ &client,
+ org_id.clone(),
+ &app_proof
+ ).await?;
+
+ let boot_proof = boot_proof_response.boot_proof.unwrap();
+ verify(&app_proof, &boot_proof)?;
+
+ println!("Proof verified successfully");
+}
+```
+
+### Parse Attestation Documents
+
+```rust
+use turnkey_proofs::parse_and_verify_aws_nitro_attestation;
+
+let attestation_doc = parse_and_verify_aws_nitro_attestation(
+ &encoded_attestation,
+ Some(validation_time),
+)?;
+
+println!("PCR0: {:?}", attestation_doc.pcrs.get(&0));
+println!("Public Key: {:?}", attestation_doc.public_key);
+```
+
+## Gas Station
+
+The Rust SDK supports Turnkey's gas station for sponsored transactions:
+
+```rust
+use turnkey_client::generated::*;
+
+// Create a gas station configuration
+let result = client.create_gas_station(
+ org_id.clone(),
+ client.current_timestamp(),
+ CreateGasStationIntent {
+ name: "My Gas Station".to_string(),
+ funding_wallet_id: wallet_id.clone(),
+ chain_ids: vec!["1".to_string(), "137".to_string()],
+ },
+).await?;
+```
+
+## TVC CLI (Experimental)
+
+The Rust SDK repository includes an experimental Turnkey Verified Cloud (TVC) CLI tool for verifiable deployments. See the [`tvc/`](https://github.com/tkhq/rust-sdk/tree/main/tvc) directory for more information.
+
+## Code Quality
+
+The Rust SDK maintains high code quality standards:
+
+```rust
+#![forbid(unsafe_code)]
+#![deny(clippy::all, clippy::unwrap_used)]
+#![warn(missing_docs, clippy::pedantic)]
+```
+
+- **No unsafe code** — memory safety guaranteed
+- **Comprehensive linting** — clippy with pedantic warnings
+- **Strong typing** — all API types generated from protobuf
+- **Test coverage** — unit tests and integration tests with wiremock
+
+## Examples
+
+The SDK includes working examples in the [`examples/`](https://github.com/tkhq/rust-sdk/tree/main/examples) directory:
+
+| Example | Description |
+|---------|-------------|
+| [`whoami`](https://github.com/tkhq/rust-sdk/tree/main/examples/whoami) | Basic API call to verify credentials |
+| [`wallet`](https://github.com/tkhq/rust-sdk/tree/main/examples/wallet) | Wallet creation, signing, and export |
+| [`proofs`](https://github.com/tkhq/rust-sdk/tree/main/examples/proofs) | Attestation verification |
+| [`sub_organization`](https://github.com/tkhq/rust-sdk/tree/main/examples/sub_organization) | Sub-org creation |
+
+## Resources
+
+- [GitHub Repository](https://github.com/tkhq/rust-sdk)
+- [crates.io - turnkey_client](https://crates.io/crates/turnkey_client)
+- [crates.io - turnkey_api_key_stamper](https://crates.io/crates/turnkey_api_key_stamper)
+- [docs.rs Documentation](https://docs.rs/turnkey_client)
+- [API Reference](/api-reference/overview)