openfort-backend-wallets
Openfort Backend Wallets (Developer Custody)
Backend wallets are server-controlled EOAs for automated blockchain operations — no user interaction required. Private keys are stored in hardware-backed secure enclaves and never leave the secure environment.
When to use backend wallets (vs embedded wallets):
- Server-side automation: treasury ops, batch minting, payroll, airdrops
- AI agent wallets: autonomous trading, payment processing
- Programmatic signing: no browser or user present
- Cross-border payments: automated stablecoin disbursement
When to use embedded wallets instead:
- User-facing wallets where the user controls the key
- Browser/mobile signing flows with user approval
Setup
npm install @openfort/openfort-node
# EVM peer dependency (required for sendTransaction):
npm install viem
# Solana peer dependencies (required for Solana operations):
npm install @solana/kit @solana-program/system @solana-program/compute-budget @solana-program/token @solana/kora @solana/transaction-confirmation
Environment Variables
OPENFORT_API_KEY=sk_test_... # Secret API key (required)
OPENFORT_WALLET_SECRET=ws_... # EC P-256 private key for signing ops (required for mutations)
OPENFORT_PUBLISHABLE_KEY=pk_test_... # Required for Solana operations (Kora gasless)
OPENFORT_BASE_URL=https://api.openfort.io # Optional, defaults to production
Initialize
import Openfort from '@openfort/openfort-node'
const openfort = new Openfort(process.env.OPENFORT_API_KEY!, {
walletSecret: process.env.OPENFORT_WALLET_SECRET!,
publishableKey: process.env.OPENFORT_PUBLISHABLE_KEY,
})
// Or use env vars directly (auto-detected):
// OPENFORT_API_KEY, OPENFORT_WALLET_SECRET, OPENFORT_PUBLISHABLE_KEY
const openfort = new Openfort()
Authentication Model
All mutating backend wallet requests (POST, DELETE, PUT on /accounts/backend/*) are authenticated with two layers:
- API Key — Bearer token in
Authorizationheader (sk_test_...orsk_live_...) - Wallet Auth (X-Wallet-Auth) — Signed with the wallet secret. The SDK generates this automatically.
The SDK handles auth generation transparently — just provide walletSecret at init.
Important: Wallet-auth requests are not retried on failure. All other requests use automatic retry with exponential backoff.
EVM Backend Wallets
Create
const account = await openfort.accounts.evm.backend.create({
wallet: 'pla_...', // Optional — associates the wallet with a player
})
// account.id — 'acc_...'
// account.address — '0x...' (viem Address type)
// account.walletId — 'wal_...'
// account.custody — 'Developer'
List & Get
// List all EVM backend wallets (paginated)
const { accounts, total, nextPageToken } = await openfort.accounts.evm.backend.list({
limit: 50, // 1-100, default 10, optional
skip: 0, // optional
})
// Get by ID or address
const account = await openfort.accounts.evm.backend.get({ id: 'acc_...' })
// OR
const account = await openfort.accounts.evm.backend.get({ address: '0x...' })
// Throws AccountNotFoundError if not found
Send Transaction (EVM — Gasless with EIP-7702)
sendTransaction handles the full EIP-7702 delegation + gasless flow automatically:
- First call on a chain: registers EIP-7702 delegation on-chain (upgrades EOA to smart account), then sends transaction
- Subsequent calls: skips delegation, sends directly
- Multiple interactions are batched atomically in a single transaction (enabled by smart account delegation)
const account = await openfort.accounts.evm.backend.create()
const result = await openfort.accounts.evm.backend.sendTransaction({
account: account, // Required — account object from create() or get()
chainId: 84532, // Required — target chain ID (resolved via viem/chains)
interactions: [ // Required — array of contract calls (batched atomically)
{
to: '0xRecipientAddress', // Required — destination address
value: '0', // Optional, default '0' — Wei amount as string
data: '0x', // Optional, default '0x' — calldata
},
// Add more interactions for atomic batching
],
policy: 'pol_...', // Optional — fee sponsorship ID for gasless tx
rpcUrl: 'https://sepolia.base.org', // Optional — custom RPC (required for chains not in viem/chains)
})
console.log('TX Hash:', result.response?.transactionHash)
console.log('Status:', result.response?.status)
console.log('Gas Used:', result.response?.gasUsed)
Note: If
chainIdis not found inviem/chainsand norpcUrlis provided, aDelegationErroris thrown.
Sign Data
The account object supports multiple signing methods:
const account = await openfort.accounts.evm.backend.get({ address: '0x...' })
// Sign a raw hash (32-byte hex)
const sig1 = await account.sign({ hash: '0xabcdef...' })
// Sign a human-readable message (EIP-191 personal_sign)
const sig2 = await account.signMessage({ message: 'Hello World' })
// Sign a serializable transaction
const sig3 = await account.signTransaction({
to: '0x...',
value: 100n,
chainId: 84532,
})
// Sign EIP-712 typed data
const sig4 = await account.signTypedData({
domain: { name: 'MyApp', version: '1', chainId: 84532 },
types: { Transfer: [{ name: 'to', type: 'address' }, { name: 'amount', type: 'uint256' }] },
primaryType: 'Transfer',
message: { to: '0x...', amount: 100n },
})
Or use the lower-level API directly:
const signature = await openfort.accounts.evm.backend.sign({
id: account.id,
data: '0x...', // hex-encoded data to sign
})
Update to Delegated Account (EIP-7702)
Manually register EIP-7702 delegation without sending a transaction:
const delegatedAccount = await openfort.accounts.evm.backend.update({
walletId: account.walletId,
accountType: 'Delegated Account', // Required for EIP-7702 upgrade
chainId: 84532,
implementationType: 'Calibur',
accountId: account.id,
})
Import Private Key
The SDK handles E2E encryption internally — just provide the raw private key:
const imported = await openfort.accounts.evm.backend.import({
privateKey: '0xYourPrivateKeyHex', // hex string (with or without 0x prefix)
})
// imported.id, imported.address
Under the hood: The SDK encrypts your private key with RSA-OAEP (SHA-256) using the server's public key before transit. The server holds the corresponding private key in a KMS HSM (non-extractable).
Low-level encryption helpers (advanced)
For manual encryption workflows (e.g., custom import pipelines), the SDK also exports:
import {
generateRSAKeyPair,
encryptForImport,
decryptExportedPrivateKey,
IMPORT_ENCRYPTION_PUBLIC_KEY,
} from '@openfort/openfort-node'
// These are synchronous functions:
const keyPair = generateRSAKeyPair() // Returns { publicKey, privateKeyPem }
const encrypted = encryptForImport('0xKey', IMPORT_ENCRYPTION_PUBLIC_KEY) // Returns base64 string
const decrypted = decryptExportedPrivateKey(encryptedBase64, keyPair.privateKeyPem) // Returns hex string
Export Private Key
The SDK handles E2E decryption internally — returns the private key directly:
const privateKey = await openfort.accounts.evm.backend.export({
id: account.id,
})
// privateKey is hex string (no 0x prefix)
Delete
await openfort.accounts.evm.backend.delete(account.id)
// Permanently deletes wallet and private key — irreversible
Solana Backend Wallets
All Solana transactions are gasless by default via Kora fee payer protocol. The user's wallet never needs SOL for gas. Requires
publishableKeyto be configured.
Create
const account = await openfort.accounts.solana.backend.create({
wallet: 'pla_...', // Optional — associates the wallet with a player
})
// account.id — 'acc_...'
// account.address — Base58 Solana address
// account.custody — 'Developer'
List & Get
const { accounts, total, nextPageToken } = await openfort.accounts.solana.backend.list({
limit: 50, // 1-100, default 10, optional
skip: 0, // optional
})
const account = await openfort.accounts.solana.backend.get({
address: 'Base58Address...',
})
// Also: get({ id: 'acc_...' })
// Throws AccountNotFoundError if not found
Transfer SOL
const result = await account.transfer({
to: 'FDx9mfVqTvXUaSPQDELwDtGgMqxirmAFsEK2s4YsKfsc',
amount: 1_000_000n, // In lamports (1 SOL = 1_000_000_000 lamports)
cluster: 'devnet', // 'devnet' | 'mainnet-beta'
// token defaults to 'sol'
computeUnitLimit: 200_000, // Optional — auto-estimated via simulation if omitted
computeUnitPrice: 50_000n, // Optional — micro-lamports priority fee
})
console.log('Signature:', result.signature)
Transfer SPL Tokens (USDC, etc.)
// By token name
const usdcResult = await account.transfer({
to: 'FDx9...',
amount: 1_000_000n, // In token base units (USDC: 6 decimals)
token: 'usdc',
cluster: 'devnet',
})
// By mint address
const splResult = await account.transfer({
to: 'FDx9...',
amount: 2_000_000n,
token: '4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU', // Mint address
cluster: 'devnet',
})
Send Transaction with Instructions
import { getTransferSolInstruction } from '@solana-program/system'
import { address, createNoopSigner } from '@solana/kit'
const account = await openfort.accounts.solana.backend.get({
address: 'o24A5URLU3JNKg7AoeUrPsfsAo1NQeeAB4uQViAkpjq',
})
const ix = getTransferSolInstruction({
source: createNoopSigner(address(account.address)),
destination: address('FDx9mfVqTvXUaSPQDELwDtGgMqxirmAFsEK2s4YsKfsc'),
amount: 10n,
})
const result = await openfort.accounts.solana.backend.sendTransaction({
account,
cluster: 'devnet',
instructions: [ix],
// computeUnitLimit — auto-estimated via simulation if omitted
// computeUnitPrice — defaults to 50_000n micro-lamports
// rpcUrl, wsUrl — custom endpoints (optional)
})
console.log('Signature:', result.signature)
Gasless flow under the hood:
- Kora provides fee payer address + blockhash
- Transaction built with Kora as fee payer
- Compute budget auto-estimated via
simulateTransaction(falls back to 200k CU) - User signs → Kora co-signs → RPC submits
- Confirmed via WebSocket subscription (60s timeout)
Send Raw Transaction (Pre-built Base64)
const result = await openfort.accounts.solana.backend.sendRawTransaction({
account,
cluster: 'devnet',
transaction: base64EncodedTransaction,
})
// Internally decompiles, extracts instructions, re-wraps in gasless flow
Sign Data (Solana)
// Lower-level API
const signature = await openfort.accounts.solana.backend.sign(
account.id,
'hex_encoded_data',
)
// Account object methods
const sig1 = await account.signMessage({ message: 'Hello Solana' })
const sig2 = await account.signTransaction({ transaction: base64Tx })
Import / Export (Solana)
Same simplified flow as EVM — encryption is handled internally. Solana import accepts base58, hex with 0x, or raw hex. Auto-expands 32-byte seeds to 64-byte keypairs. Export returns base58 (standard Solana format).
// Import
const imported = await openfort.accounts.solana.backend.import({
privateKey: '4YFq9y5f5hi77Bq8kDCE6VgqoAq...', // base58, hex with 0x, or raw hex
})
// Export
const privateKey = await openfort.accounts.solana.backend.export({
id: account.id,
})
// privateKey is base58-encoded (standard Solana format)
Gas Sponsorship (Fee Sponsorship)
Gasless transactions require a two-step setup: create a policy (rules), then create a fee sponsorship (strategy) linked to that policy. This can be done via the SDK or the Openfort Dashboard.
Strategy Types
| Strategy | Description |
|---|---|
pay_for_user |
Developer fully sponsors gas — user pays nothing |
charge_custom_tokens |
User pays in ERC-20 tokens (fixed or dynamic exchange rate) |
fixed_rate |
User pays a fixed token amount per transaction |
Create via SDK (Programmatic)
// Step 1: Create a policy with criteria rules
const policy = await openfort.policies.create({
scope: 'project', // 'project' (all accounts) or 'account' (single account)
description: 'Sponsor gas on Base for USDC contract',
rules: [{
action: 'accept',
operation: 'sponsorEvmTransaction',
criteria: [
{ type: 'evmNetwork', operator: 'in', chainIds: [8453] },
{ type: 'evmAddress', operator: 'in', addresses: ['0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'] },
],
}],
})
// Step 2: Create a fee sponsorship linked to that policy
const sponsorship = await openfort.feeSponsorship.create({
name: 'Base USDC Gas Sponsorship',
strategy: { sponsorSchema: 'pay_for_user' },
policyId: policy.id,
})
// Step 3: Use sponsorship ID in transactions
await openfort.accounts.evm.backend.sendTransaction({
account,
chainId: 8453,
interactions: [{ to: '0x...', value: '0', data: '0x...' }],
policy: sponsorship.id, // pol_... from sponsorship
})
Create via Dashboard
- Go to Openfort Dashboard → Policies
- Create a policy with your desired criteria rules
- Go to Fee Sponsorship → Create and link to the policy
- Copy the fee sponsorship ID (
pol_...) - Use it in your code:
await openfort.accounts.evm.backend.sendTransaction({
account,
chainId: 84532,
interactions: [{ to: '0x...', value: '0', data: '0x' }],
policy: process.env.OPENFORT_FEE_SPONSORSHIP_ID!, // pol_... from dashboard
})
Auto-discovery: When no explicit
policyis passed tosendTransaction, project-scoped fee sponsorships are auto-discovered and the first matching one is applied.
Fee Sponsorship CRUD
// List
const sponsorships = await openfort.feeSponsorship.list()
// Get
const sponsorship = await openfort.feeSponsorship.get('pol_...')
// Update
await openfort.feeSponsorship.update('pol_...', { name: 'New Name' })
// Enable / Disable
await openfort.feeSponsorship.disable('pol_...')
await openfort.feeSponsorship.enable('pol_...')
// Delete (soft delete)
await openfort.feeSponsorship.delete('pol_...')
Charge Custom Tokens Strategy
const sponsorship = await openfort.feeSponsorship.create({
name: 'Pay gas with USDC',
strategy: {
sponsorSchema: 'charge_custom_tokens',
tokenContract: 'con_...', // Contract ID from dashboard
tokenContractAmount: '1000000', // Amount in token base units
},
policyId: policy.id,
})
Policy Engine
Policies define rules that control which operations are allowed or rejected. They are evaluated server-side before any signing or transaction execution.
Key concepts:
- Scope:
'project'(all accounts) or'account'(single account) - Priority: Higher priority policies evaluated first
- Fail-closed: No matching rule = operation rejected
- Rules: Each policy has 1-10 rules with
action('accept'|'reject'),operation, andcriteria(AND logic)
Create Policy
const policy = await openfort.policies.create({
scope: 'project',
description: 'Allow USDC transfers under 10k on Base',
enabled: true,
priority: 10,
rules: [
{
action: 'accept',
operation: 'sendEvmTransaction',
criteria: [
{ type: 'evmNetwork', operator: 'in', chainIds: [8453] },
{ type: 'evmAddress', operator: 'in', addresses: ['0xUSDC_CONTRACT'] },
{ type: 'ethValue', operator: '<=', ethValue: '10000000000' },
],
},
],
})
Policy CRUD
// List policies
const policies = await openfort.policies.list({ scope: 'project', enabled: true })
// Get by ID
const policy = await openfort.policies.get('ply_...')
// Update (replaces all rules)
await openfort.policies.update('ply_...', {
enabled: false,
rules: [/* new rules */],
})
// Delete (soft delete)
await openfort.policies.delete('ply_...')
// Dry-run evaluation — check if an operation would be allowed without executing
const decision = await openfort.policies.evaluate({ /* operation details */ })
EVM Operations & Criteria
Operations: signEvmTransaction, sendEvmTransaction, signEvmTypedData, signEvmMessage, signEvmHash, sponsorEvmTransaction
| Criteria Type | Operators | Fields |
|---|---|---|
ethValue |
<=, >=, <, > |
ethValue: string (wei) |
evmAddress |
in, not in |
addresses: Address[] |
evmNetwork |
in, not in |
chainIds: number[] |
evmData |
in, not in, <, <=, >, >=, ==, match |
data: string (regex or hex) |
evmMessage |
match |
message: string (RE2 regex) |
evmTypedDataVerifyingContract |
in, not in |
contracts: Address[] |
evmTypedDataField |
in, <=, match |
field: string, value: string |
Solana Operations & Criteria
Operations: signSolTransaction, sendSolTransaction, signSolMessage, sponsorSolTransaction
| Criteria Type | Operators | Fields |
|---|---|---|
solAddress |
in, not in |
addresses: string[] (base58) |
solValue |
<=, >= |
value: string (lamports) |
splAddress |
in, not in |
addresses: string[] (base58) |
splValue |
<=, >= |
value: string (token units) |
mintAddress |
==, in |
mint: string or mints: string[] |
programId |
in, not in |
programIds: string[] (base58) |
solNetwork |
in, not in |
networks: ('mainnet-beta' | 'devnet' | 'testnet')[] |
solMessage |
match |
message: string (RE2 regex) |
solData |
in, not in, <, <=, >, >=, ==, match |
data: string |
Validation Schemas
The SDK exports Zod schemas for client-side validation before API calls:
import {
CreatePolicyBodySchema,
UpdatePolicyBodySchema,
RuleSchema,
} from '@openfort/openfort-node'
// Validate before sending
const parsed = CreatePolicyBodySchema.parse(myPolicyInput)
Webhooks
Verify webhook signatures from Openfort using timing-safe comparison:
// In your webhook handler (e.g., Express route)
app.post('/webhook', async (req, res) => {
const signature = req.headers['x-openfort-signature'] as string
const body = req.body // raw string body
try {
const event = await openfort.constructWebhookEvent(body, signature)
// event is the parsed webhook payload
console.log('Webhook event:', event)
res.status(200).send('OK')
} catch (error) {
console.error('Invalid webhook signature')
res.status(400).send('Invalid signature')
}
})
Transaction Intent Flow (Legacy / Advanced)
For more control over the transaction lifecycle, use transaction intents directly:
// 1. Create transaction intent
const txIntent = await openfort.transactionIntents.create({
chainId: 84532,
account: 'acc_...',
policy: 'pol_...', // Optional — fee sponsorship ID (from SDK or dashboard)
interactions: [
{ to: '0x...', value: '0', data: '0x...' },
],
})
// 2. If signature needed
if (txIntent.nextAction?.payload?.signableHash) {
const signature = await account.sign({
hash: txIntent.nextAction.payload.signableHash,
})
// 3. Submit signature
const result = await openfort.transactionIntents.signature(txIntent.id, {
signature,
})
console.log('TX Hash:', result.response?.transactionHash)
}
// Estimate gas before creating
const estimate = await openfort.transactionIntents.estimateCost({
chainId: 84532,
interactions: [{ to: '0x...', value: '0', data: '0x...' }],
})
Full Lifecycle Example
import Openfort from '@openfort/openfort-node'
const openfort = new Openfort(process.env.OPENFORT_API_KEY!, {
walletSecret: process.env.OPENFORT_WALLET_SECRET!,
publishableKey: process.env.OPENFORT_PUBLISHABLE_KEY,
})
// 1. Create wallet
const wallet = await openfort.accounts.evm.backend.create()
console.log('Created:', wallet.address)
// 2. Set up gas sponsorship (policy + fee sponsorship)
const policy = await openfort.policies.create({
scope: 'account',
accountId: wallet.id,
description: 'Only allow USDC on Base',
rules: [{
action: 'accept',
operation: 'sponsorEvmTransaction',
criteria: [
{ type: 'evmNetwork', operator: 'in', chainIds: [8453] },
{ type: 'evmAddress', operator: 'in', addresses: ['0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'] },
],
}],
})
const sponsorship = await openfort.feeSponsorship.create({
name: 'USDC Escrow Sponsorship',
strategy: { sponsorSchema: 'pay_for_user' },
policyId: policy.id,
})
// 3. Send gasless transaction
const tx = await openfort.accounts.evm.backend.sendTransaction({
account: wallet,
chainId: 8453, // Base mainnet
interactions: [{
to: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', // USDC on Base
value: '0',
data: '0x...transfer_calldata',
}],
policy: sponsorship.id,
})
console.log('TX:', tx.response?.transactionHash)
// 4. Export key if needed
const pk = await openfort.accounts.evm.backend.export({ id: wallet.id })
// pk is hex string (no 0x prefix)
Error Handling
SDK Error Classes
import {
AccountNotFoundError,
DelegationError,
EncryptionError,
MissingWalletSecretError,
MissingPublishableKeyError,
MissingAPIKeyError,
InvalidAPIKeyFormatError,
InvalidWalletSecretFormatError,
InvalidPublishableKeyFormatError,
UserInputValidationError,
TimeoutError,
} from '@openfort/openfort-node'
| Error | When |
|---|---|
AccountNotFoundError |
.get() with non-existent ID/address |
DelegationError |
Chain ID not in viem/chains and no rpcUrl provided; or gasless flow failure |
EncryptionError |
RSA encryption/decryption failure during import/export |
MissingWalletSecretError |
Signing operation attempted without walletSecret configured |
MissingPublishableKeyError |
Solana operation attempted without publishableKey configured |
MissingAPIKeyError |
No API key provided at init |
InvalidAPIKeyFormatError |
API key doesn't match sk_test_* or sk_live_* |
InvalidWalletSecretFormatError |
Wallet secret is not a valid EC P-256 key |
InvalidPublishableKeyFormatError |
Publishable key doesn't match pk_test_* or pk_live_* |
UserInputValidationError |
Invalid parameters (e.g., missing viem peer dependency) |
TimeoutError |
Operation timed out (e.g., Solana 60s confirmation timeout) |
API Error Classes
import {
APIError,
NetworkError,
ValidationError,
UnknownError,
} from '@openfort/openfort-node'
| Error | Fields | When |
|---|---|---|
APIError |
statusCode, errorType, errorMessage, correlationId, errorLink |
HTTP error from Openfort API |
NetworkError |
networkDetails |
DNS failure, timeout, IP blocked, gateway error |
ValidationError |
field, value |
Server-side input validation failure |
UnknownError |
— | Unclassifiable error |
APIError.errorType values: 'unauthorized', 'forbidden', 'not_found', 'bad_request', 'conflict', 'rate_limited', 'bad_gateway', 'service_unavailable', 'unexpected_error'
Robust Error Handling Pattern
import {
AccountNotFoundError,
DelegationError,
MissingWalletSecretError,
APIError,
NetworkError,
} from '@openfort/openfort-node'
try {
const result = await openfort.accounts.evm.backend.sendTransaction({ ... })
} catch (error) {
if (error instanceof AccountNotFoundError) {
// Wallet doesn't exist — create it or check ID
} else if (error instanceof DelegationError) {
// Provide rpcUrl for unsupported chains
} else if (error instanceof MissingWalletSecretError) {
// Configure walletSecret in Openfort constructor
} else if (error instanceof APIError) {
console.error(`API ${error.errorType}: ${error.errorMessage} [${error.correlationId}]`)
// correlationId is useful for Openfort support debugging
} else if (error instanceof NetworkError) {
console.error('Network issue:', error.networkDetails)
// Retried automatically (3x exponential backoff) unless wallet-auth request
} else {
throw error
}
}
Retry & Reliability
The SDK includes built-in retry with exponential backoff:
- Retried: network errors, 5xx responses, idempotent requests (GET, HEAD, DELETE, PUT)
- NOT retried: 4xx errors, wallet-auth requests
- Solana confirmation: timeout via WebSocket subscription
Account Object Quick Reference
EVM Account
const account = await openfort.accounts.evm.backend.get({ address: '0x...' })
account.id // 'acc_...'
account.address // '0x...' (viem Address)
account.walletId // 'wal_...'
account.custody // 'Developer'
account.sign({ hash }) // Sign raw 32-byte hash → Hex
account.signMessage({ message }) // EIP-191 personal_sign → Hex
account.signTransaction(tx) // Sign serializable tx → Hex
account.signTypedData(params) // EIP-712 typed data → Hex
Solana Account
const account = await openfort.accounts.solana.backend.get({ address: '...' })
account.id // 'acc_...'
account.address // Base58 string
account.custody // 'Developer'
account.signMessage({ message }) // Sign UTF-8 message → string
account.signTransaction({ transaction }) // Sign base64 tx → string
account.transfer({ to, amount, cluster, token? }) // SOL/SPL transfer → { signature }
account.sendRawTransaction({ cluster, transaction }) // Pre-built base64 tx → { signature }
SDK API Surface Overview
Beyond backend wallets, the @openfort/openfort-node SDK exposes:
| Namespace | Purpose |
|---|---|
openfort.accounts.evm.backend.* |
EVM backend wallet operations |
openfort.accounts.solana.backend.* |
Solana backend wallet operations |
openfort.accounts.evm.embedded.* |
Pre-generate embedded EVM wallets |
openfort.accounts.solana.embedded.* |
Pre-generate embedded Solana wallets |
openfort.policies.* |
Policy engine CRUD + evaluation |
openfort.feeSponsorship.* |
Gas sponsorship CRUD + enable/disable |
openfort.transactionIntents.* |
Transaction lifecycle + gas estimation |
openfort.iam.* |
User management + session verification |
openfort.paymasters.* |
ERC-4337 paymaster management |
openfort.contracts.* |
Smart contract registry |
openfort.subscriptions.* |
Event subscriptions |
openfort.triggers.* |
Trigger management |
openfort.sessions.* |
Session key management |
openfort.players.* |
Player management (deprecated → use iam.users) |
openfort.auth.* |
Third-party auth verification |
openfort.constructWebhookEvent() |
Webhook signature verification |