Overview
The deleteAccount action permanently deletes a NEAR account and transfers its remaining balance to a beneficiary account. This action is irreversible.
Signature
deleteAccount(args: {
beneficiaryAccountId: string
}): DeleteAccountAction
Parameters
The account ID that will receive the remaining balance from the deleted account
Returns
A DeleteAccountAction object:
{
actionType: 'DeleteAccount',
beneficiaryAccountId: string
}
Basic Example
import { deleteAccount } from '@near-api-ts/universal';
const action = deleteAccount({
beneficiaryAccountId: 'alice.near'
});
await signer.executeTransaction({
intent: {
receiverAccountId: 'temp.alice.near', // Account to delete
action
}
});
console.log('Account deleted successfully');
This action is IRREVERSIBLE. Once an account is deleted:
- The account name becomes available for re-registration
- All access keys are removed
- The account cannot sign transactions
- All remaining balance is transferred to the beneficiary
Delete Temporary Account
import { deleteAccount } from '@near-api-ts/universal';
// Delete a temporary account created for testing
await signer.executeTransaction({
intent: {
receiverAccountId: 'test-123.alice.near',
action: deleteAccount({
beneficiaryAccountId: 'alice.near'
})
}
});
Delete with Key Cleanup
import {
deleteKey,
deleteAccount
} from '@near-api-ts/universal';
// Get all keys except the signing key
const keys = await client.getAccessKeys({
accountId: 'temp.alice.near'
});
const otherKeys = keys
.map(k => k.publicKey)
.filter(pk => pk !== signerPublicKey);
// Delete other keys first, then delete account
await signer.executeTransaction({
intent: {
receiverAccountId: 'temp.alice.near',
actions: [
...otherKeys.map(pk => deleteKey({ publicKey: pk })),
deleteAccount({ beneficiaryAccountId: 'alice.near' })
]
}
});
Cleanup After Test
import { deleteAccount } from '@near-api-ts/universal';
async function cleanupTestAccounts(parentAccount: string) {
const testAccounts = [
'test-1',
'test-2',
'test-3'
];
for (const name of testAccounts) {
const accountId = `${name}.${parentAccount}`;
try {
await signer.executeTransaction({
intent: {
receiverAccountId: accountId,
action: deleteAccount({
beneficiaryAccountId: parentAccount
})
}
});
console.log(`Deleted ${accountId}`);
} catch (error) {
console.error(`Failed to delete ${accountId}:`, error);
}
}
}
await cleanupTestAccounts('alice.near');
Delete Contract Account
import { deleteAccount } from '@near-api-ts/universal';
// Delete a contract account and recover storage costs
await signer.executeTransaction({
intent: {
receiverAccountId: 'old-contract.alice.near',
action: deleteAccount({
beneficiaryAccountId: 'alice.near'
})
}
});
console.log('Contract account deleted, balance recovered');
Deleting a contract account with stored state will transfer the storage costs back to the beneficiary. This can be useful for recovering NEAR locked in storage.
Conditional Deletion
import { deleteAccount } from '@near-api-ts/universal';
// Check account state before deletion
const accountInfo = await client.getAccountInfo({
accountId: 'temp.alice.near'
});
const balanceInNear = Number(accountInfo.amount) / 1e24;
if (balanceInNear < 0.01) {
// Safe to delete - minimal balance
await signer.executeTransaction({
intent: {
receiverAccountId: 'temp.alice.near',
action: deleteAccount({
beneficiaryAccountId: 'alice.near'
})
}
});
} else {
console.log('Account has significant balance, skipping deletion');
}
Error Handling
import { deleteAccount, isNatError } from '@near-api-ts/universal';
try {
await signer.executeTransaction({
intent: {
receiverAccountId: 'temp.alice.near',
action: deleteAccount({
beneficiaryAccountId: 'alice.near'
})
}
});
console.log('Account deleted successfully');
} catch (error) {
if (isNatError(error)) {
if (error.kind === 'CreateAction.DeleteAccount.Args.InvalidSchema') {
console.error('Invalid beneficiary account ID');
} else if (error.kind === 'MemorySigner.ExecuteTransaction.Rpc.Transaction.Receiver.NotFound') {
console.error('Account to delete does not exist');
}
}
}
Safe Variant
import { safeDeleteAccount } from '@near-api-ts/universal';
const actionResult = safeDeleteAccount({
beneficiaryAccountId: 'alice.near'
});
if (!actionResult.ok) {
console.error('Invalid delete account action:', actionResult.error.kind);
return;
}
const result = await signer.safeExecuteTransaction({
intent: {
receiverAccountId: 'temp.alice.near',
action: actionResult.value
}
});
if (result.ok) {
console.log('Account deleted:', result.value.transactionHash);
} else {
console.error('Deletion failed:', result.error.kind);
}
With Confirmation
import { deleteAccount } from '@near-api-ts/universal';
async function deleteAccountWithConfirmation(
accountToDelete: string,
beneficiary: string
) {
const confirm = await promptUser(
`Are you sure you want to delete ${accountToDelete}? This cannot be undone. (yes/no)`
);
if (confirm.toLowerCase() !== 'yes') {
console.log('Deletion cancelled');
return;
}
// Get account info to show balance
const accountInfo = await client.getAccountInfo({
accountId: accountToDelete
});
const balanceInNear = Number(accountInfo.amount) / 1e24;
console.log(`Account balance: ${balanceInNear} NEAR`);
console.log(`Balance will be transferred to: ${beneficiary}`);
const finalConfirm = await promptUser('Proceed? (yes/no)');
if (finalConfirm.toLowerCase() !== 'yes') {
console.log('Deletion cancelled');
return;
}
await signer.executeTransaction({
intent: {
receiverAccountId: accountToDelete,
action: deleteAccount({ beneficiaryAccountId: beneficiary })
}
});
console.log('Account deleted successfully');
}
Common Errors
CreateAction.DeleteAccount.Args.InvalidSchema - Invalid beneficiary account ID format
CreateAction.DeleteAccount.Internal - Internal error
MemorySigner.ExecuteTransaction.Rpc.Transaction.Receiver.NotFound - Account to delete doesn’t exist
- Beneficiary account doesn’t exist - Beneficiary must exist before deletion
- Cannot delete account with active contract - Some contract states may prevent deletion
Important Notes
Account Deletion is Permanent:
- The account name becomes available for anyone to register
- All data and state are destroyed
- Access keys become useless
- Cannot be recovered or undone
Balance Transfer:
- All remaining NEAR balance is transferred to the beneficiary
- This includes storage staking refunds
- Gas costs for the transaction are deducted first
Name Availability:
After deletion, the account name can be registered by anyone. If you want to reserve it, you need to re-create it immediately or use a different strategy.
Best Practices
DO:
- Verify the beneficiary account exists
- Check the account balance before deletion
- Use confirmation prompts in user-facing applications
- Clean up access keys if needed
- Test on testnet first
- Document why accounts are being deleted
DON’T:
- Don’t delete accounts without user confirmation
- Don’t use non-existent beneficiaries
- Don’t delete accounts with significant balances accidentally
- Don’t forget that deletion is irreversible
- Don’t delete parent accounts if subaccounts still exist
Use Cases
- Temporary Accounts: Delete test or temporary accounts after use
- Storage Recovery: Recover NEAR locked in storage by deleting unused accounts
- Account Migration: Delete old accounts after migrating to new ones
- Cleanup: Remove unused subaccounts to reduce maintenance
- Testing: Clean up after integration tests
Alternatives to Deletion
Before deleting, consider:
- Transfer Ownership: Transfer access keys instead of deleting
- Remove Keys: Delete access keys but keep the account
- Archive: Keep the account but transfer out the balance
- Rename Strategy: Use subaccounts instead of top-level accounts
See Also