docs
Get startedTroubleshootingPaymentsWebhooksAgent paymentsSwop MCPOviIdentity (swop.id)SwivyRPC (preview)API reference

Concepts

Custody modes, policies, intents and step-up approval. The four ideas everything in Swivy is built on.

Preview: Swivy is not generally available

Swivy is in staging. It is not in production and Swop's own apps don't run on it yet. The SDK packages aren't published to npm; preview partners get access along with their credentials. Everything on this page can change. Request early access.

Accounts: users, logins and wallets

Swivy keeps who someone is separate from which wallets they have, so one user can have several login methods and one wallet per chain without your app inventing its own user model.

ObjectWhat it is
IdentityOne user inside a project. Its id is the stable user id; you can also store your own externalUserId on it.
Auth methodA way to log in or step up: email, external wallet, passkey, TOTP or SMS.
Managed accountAn embedded wallet Swivy created for the user: chain, address, custody mode and purpose.
Linked walletA wallet the user already had (MetaMask, Phantom and so on) connected to their identity.
Session grantDelegated, limited access to one managed account for an agent or backend job.

Managed accounts also carry a purpose: personal, agent, recovery or treasury. The same user can have a personal wallet they sign with in the browser and an agent wallet for automation on the same chain.

Custody: who holds the key

A wallet is a private key. Swivy offers two ways to hold it.

ModeHow it worksGood for
managed_tee (default)The key lives in Swivy's isolated signer, a separate service that does nothing but sign. Your app asks for a signature; the key never leaves the signer. Export and raw signing are off unless your policy allows them.Most apps. Works the same on web, iOS and Android.
split_custodyThe key is split. The user's browser keeps one share; Swivy stores only an encrypted copy of the other, which it can't open on its own. Signing happens in the browser after a passkey unlock.Browser apps that want the user to hold part of the key.

Split custody is not full threshold MPC

Swivy can't sign alone for a split_custody wallet: hosted signing returns client_reconstruction_required, and signing happens in the browser instead. Native mobile support for this mode is still being built.

Policies: what each environment allows

Every environment has a policy you set in the console or from your backend:

  • Auth and MFA: which login methods are on, and when a second factor is required.
  • Wallet creation: which chains get a wallet automatically at login.
  • Signing and export: whether export and raw signing are allowed at all, and what needs a passkey.
  • Chains: which networks exist, each with its RPC URL.
  • Sponsorship: which networks you pay gas on.

Policy lives on the environment, not in your app code, so you can tighten it without shipping a new build.

Intents: how things get signed and sent

You don't hand Swivy a private key operation. You submit an intent: "send this transaction on base-mainnet from this account". Swivy checks it against policy, signs it, optionally sponsors gas, broadcasts it through the configured RPC, records the status and sends a webhook.

const intent = await swivy.createTransactionIntentAndWait({
  accountId: 'acct_...',
  chain: 'evm',                 // adapter family: 'evm' or 'solana'
  challengeId,                  // from a passkey step-up, if policy requires it
  payload: {
    network: 'base-mainnet',    // must match a network configured on the environment
    transaction: { to: '0x1111111111111111111111111111111111111111', value: '0x0', data: '0x' },
  },
});
console.log(intent.status, intent.result);

An intent moves through these statuses:

StatusMeaning
pending_step_upPolicy needs a passkey (or other second factor) approval first.
queuedAccepted and waiting for the worker.
processingBeing signed, sponsored and broadcast.
completedDone. result has the signature or transaction hash.
failedRejected or errored. The error explains why.

Intent kinds are sign_message, sign_digest, sign_typed_data, sign_transaction and execute_transaction.

Supported network values include base-mainnet, ethereum-mainnet, arbitrum-mainnet, optimism-mainnet, polygon-mainnet, solana-mainnet and the testnets base-sepolia and solana-devnet, as long as the environment has an RPC configured for them.

Step-up: approving with a passkey

Login proves who someone is. Step-up proves they meant this action. Before a sensitive operation, your app creates a challenge, the user approves it with their passkey, and you attach the resulting challengeId to the intent.

const approval = await swivy.approveWithPasskey(passkeyAuthMethodId, 'intent_approval');
// attach approval.challenge.challengeId to the intent

Challenge purposes are intent_approval, wallet_reconstruction (the unlock before local signing in split custody), account_export, session_delegation (approving an agent grant) and recovery.

On this page