> ## Documentation Index
> Fetch the complete documentation index at: https://hedera-0c6e0218-add-json-rpc-release-notes.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Create and Manage Hooks

Hooks are created by adding `HookCreationDetails` to account or contract creation and update transactions. Once attached, hooks can be deleted through update transactions and their storage can be modified via [`HookStoreTransaction`](/hedera/sdks-and-apis/sdks/accounts-and-hbar/create-a-hookstore-transaction). This page covers the utility classes, hook creation, update, and deletion across all supported transaction types.

***

## Utility Classes

### HookCreationDetails

Every hook requires a `HookCreationDetails` object that specifies the extension point, a unique hook ID, the EVM implementation contract, and an optional admin key.

| Property           | Type                 | Required | Description                                                                                                                      |
| :----------------- | :------------------- | :------- | :------------------------------------------------------------------------------------------------------------------------------- |
| **extensionPoint** | `HookExtensionPoint` | Yes      | The type of hook. Currently only `ACCOUNT_ALLOWANCE_HOOK` is supported.                                                          |
| **hookId**         | `long`               | Yes      | A 64-bit identifier for the hook, unique within the owning entity. Does not need to be sequential.                               |
| **evmHook**        | `EvmHook`            | Yes      | The hook implementation, including the deployed contract ID and optional initial storage updates.                                |
| **adminKey**       | `Key`                | No       | If set, this key (in addition to the entity's key) can authorize hook deletion, replacement, and `HookStoreTransaction` updates. |

### HookExtensionPoint

```
enum HookExtensionPoint {
    ACCOUNT_ALLOWANCE_HOOK
}
```

### EvmHook

| Property           | Type                         | Required | Description                                                                          |
| :----------------- | :--------------------------- | :------- | :----------------------------------------------------------------------------------- |
| **contractId**     | `ContractId`                 | Yes      | The ID of a previously deployed contract whose bytecode implements the hook's logic. |
| **storageUpdates** | `list<EvmHookStorageUpdate>` | No       | Initial storage slot values to set when the hook is created.                         |

### EvmHookStorageUpdate

`EvmHookStorageUpdate` is an abstract type with two concrete implementations:

#### EvmHookStorageSlot

Directly sets a 32-byte key/value pair in the hook's storage.

| Property  | Type    | Description                                                        |
| :-------- | :------ | :----------------------------------------------------------------- |
| **key**   | `bytes` | 32-byte storage slot key.                                          |
| **value** | `bytes` | 32-byte storage slot value. Set to empty bytes to delete the slot. |

#### EvmHookMappingEntries

Updates entries within a Solidity mapping at a specific storage slot.

| Property        | Type                        | Description                                                 |
| :-------------- | :-------------------------- | :---------------------------------------------------------- |
| **mappingSlot** | `bytes`                     | The slot corresponding to the Solidity mapping declaration. |
| **entries**     | `list<EvmHookMappingEntry>` | The entries to update in the mapping.                       |

#### EvmHookMappingEntry

| Property     | Type    | Required                   | Description                                                                                                                                        |
| :----------- | :------ | :------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------- |
| **key**      | `bytes` | One of `key` or `preimage` | 32-byte key of the mapping entry.                                                                                                                  |
| **preimage** | `bytes` | One of `key` or `preimage` | The bytes whose Keccak256 hash forms the mapping key. Useful when block stream consumers need to follow the metaprotocol without inverting hashes. |
| **value**    | `bytes` | Yes                        | 32-byte value of the mapping entry. Set to empty bytes to delete.                                                                                  |

### HookId and HookEntityId

Used by `HookStoreTransaction` to identify a specific hook on a specific entity.

| Class            | Property     | Type                    | Description                                                                                                                           |
| :--------------- | :----------- | :---------------------- | :------------------------------------------------------------------------------------------------------------------------------------ |
| **HookId**       | `entityId`   | `HookEntityId`          | The entity that owns the hook.                                                                                                        |
| **HookId**       | `hookId`     | `long`                  | The hook's 64-bit identifier.                                                                                                         |
| **HookEntityId** | `accountId`  | `AccountId` (optional)  | The account that owns the hook. One of `accountId` or `contractId` must be set. (JavaScript SDK currently only supports `accountId`.) |
| **HookEntityId** | `contractId` | `ContractId` (optional) | The contract that owns the hook. One of `accountId` or `contractId` must be set. (Not supported in the JavaScript SDK.)               |

***

## Creating Hooks with New Entities

Hooks can be attached at entity creation time using `AccountCreateTransaction` or `ContractCreateTransaction`.

### AccountCreateTransaction Methods

| Method              | Type                        | Description                                                                                                                 |
| :------------------ | :-------------------------- | :-------------------------------------------------------------------------------------------------------------------------- |
| `addHook(<hook>)`   | `HookCreationDetails`       | Adds a hook to be created with the new account.                                                                             |
| `setHooks(<hooks>)` | `list<HookCreationDetails>` | Sets all hooks to be created with the new account.                                                                          |
| `getHooks()`        | `list<HookCreationDetails>` | Returns the list of hooks to be created. In JavaScript, use the `.hooks` property accessor instead of calling `getHooks()`. |

### ContractCreateTransaction Methods

| Method              | Type                        | Description                                                                                                                 |
| :------------------ | :-------------------------- | :-------------------------------------------------------------------------------------------------------------------------- |
| `addHook(<hook>)`   | `HookCreationDetails`       | Adds a hook to be created with the new contract.                                                                            |
| `setHooks(<hooks>)` | `list<HookCreationDetails>` | Sets all hooks to be created with the new contract.                                                                         |
| `getHooks()`        | `list<HookCreationDetails>` | Returns the list of hooks to be created. In JavaScript, use the `.hooks` property accessor instead of calling `getHooks()`. |

### Example: Create an Account with a Hook

<CodeGroup>
  ```java Java theme={null}
  import com.hedera.hashgraph.sdk.*;

  // Step 1: Deploy the hook contract (standard ContractCreateTransaction)
  ContractId contractId = /* ... your deployed hook contract ... */;

  // Step 2: Define the EVM hook
  EvmHook evmHook = new EvmHook(contractId);

  // Step 3: Create hook details
  HookCreationDetails hookDetails = new HookCreationDetails(
      HookExtensionPoint.ACCOUNT_ALLOWANCE_HOOK,
      1002L,
      evmHook,
      adminKey.getPublicKey()
  );

  // Step 4: Create the account with the hook attached
  TransactionResponse response = new AccountCreateTransaction()
      .setKey(accountKey.getPublicKey())
      .setInitialBalance(Hbar.from(1))
      .addHook(hookDetails)
      .setMaxTransactionFee(new Hbar(10))
      .freezeWith(client)
      .sign(accountKey)
      .execute(client);

  AccountId accountId = response.getReceipt(client).accountId;
  System.out.println("Account created with hook: " + accountId);
  ```

  ```javascript JavaScript theme={null}
  import {
      AccountCreateTransaction,
      HookCreationDetails,
      EvmHook,
      HookExtensionPoint,
      Hbar,
      Long,
      PrivateKey,
  } from "@hiero-ledger/sdk";

  // Step 1: Deploy the hook contract (standard ContractCreateTransaction)
  const contractId = /* ... your deployed hook contract ... */;

  // Step 2: Define the EVM hook
  const evmHook = new EvmHook({ contractId });

  // Step 3: Create hook details
  const hookDetails = new HookCreationDetails({
      extensionPoint: HookExtensionPoint.ACCOUNT_ALLOWANCE_HOOK,
      hookId: Long.fromInt(1002),
      evmHook: evmHook,
      adminKey: adminKey.publicKey,
  });

  // Step 4: Create the account with the hook attached
  const response = await (
      await new AccountCreateTransaction()
          .setKeyWithoutAlias(accountKey.publicKey)
          .setInitialBalance(new Hbar(1))
          .addHook(hookDetails)
          .setMaxTransactionFee(new Hbar(10))
          .freezeWith(client)
          .sign(accountKey)
  ).execute(client);

  const { accountId } = await response.getReceipt(client);
  console.log(`Account created with hook: ${accountId}`);
  ```

  ```go Go theme={null}
  import (
      "fmt"
      hedera "github.com/hiero-ledger/hiero-sdk-go/v2/sdk"
  )

  // Step 1: Deploy the hook contract (standard ContractCreateTransaction)
  // contractId := ... your deployed hook contract ...

  // Step 2: Create hook details
  hookDetails := hedera.NewHookCreationDetails().
      SetExtensionPoint(hedera.ACCOUNT_ALLOWANCE_HOOK).
      SetHookId(1002).
      SetEvmHook(*hedera.NewEvmHook().SetContractId(&contractId)).
      SetAdminKey(adminKey.PublicKey())

  // Step 3: Create the account with the hook attached
  response, err := hedera.NewAccountCreateTransaction().
      SetKey(accountKey.PublicKey()).
      SetInitialBalance(hedera.NewHbar(1)).
      AddHook(hookDetails).
      SetMaxTransactionFee(hedera.NewHbar(10)).
      FreezeWith(client).
      Sign(accountKey).
      Execute(client)

  receipt, err := response.GetReceipt(client)
  fmt.Printf("Account created with hook: %v\n", receipt.AccountID)
  ```
</CodeGroup>

### Example: Create an Account with a Hook and Initial Storage

You can initialize hook storage at creation time by including `EvmHookStorageUpdate` entries in the `EvmHook`.

```javascript theme={null}
import {
    AccountCreateTransaction,
    HookCreationDetails,
    EvmHook,
    EvmHookStorageSlot,
    HookExtensionPoint,
    Long,
} from "@hiero-ledger/sdk";

// Create initial storage: set slot key 0x01 to value 100 (0x64)
const storageUpdates = [
    new EvmHookStorageSlot()
        .setKey(new Uint8Array([0x01]))
        .setValue(new Uint8Array([0x64])),
];

const evmHook = new EvmHook({
    contractId,
    storageUpdates,
});

const hookDetails = new HookCreationDetails({
    extensionPoint: HookExtensionPoint.ACCOUNT_ALLOWANCE_HOOK,
    hookId: Long.fromInt(1002),
    evmHook: evmHook,
    adminKey: adminKey.publicKey,
});

const response = await (
    await new AccountCreateTransaction()
        .setKeyWithoutAlias(accountKey.publicKey)
        .setInitialBalance(new Hbar(1))
        .addHook(hookDetails)
        .setMaxTransactionFee(new Hbar(10))
        .freezeWith(client)
        .sign(accountKey)
).execute(client);

const { accountId } = await response.getReceipt(client);
console.log(`Account with initialized hook: ${accountId}`);
```

***

## Adding and Deleting Hooks on Existing Entities

Use `AccountUpdateTransaction` or `ContractUpdateTransaction` to add new hooks or remove existing hooks from an entity.

### AccountUpdateTransaction Methods

| Method                        | Type                        | Description                                                                                                                                    |
| :---------------------------- | :-------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------- |
| `addHookToCreate(<hook>)`     | `HookCreationDetails`       | Adds a hook to be created on the existing account.                                                                                             |
| `setHooksToCreate(<hooks>)`   | `list<HookCreationDetails>` | Sets all hooks to be created on the existing account.                                                                                          |
| `addHookToDelete(<hookId>)`   | `long`                      | Marks a hook for deletion by its hook ID.                                                                                                      |
| `setHooksToDelete(<hookIds>)` | `list<long>`                | Marks multiple hooks for deletion.                                                                                                             |
| `getHooksToCreate()`          | `list<HookCreationDetails>` | Returns the list of hooks to be created. In JavaScript, use the `.hooksToCreate` property accessor instead of calling `getHooksToCreate()`.    |
| `getHooksToDelete()`          | `list<long>`                | Returns the list of hook IDs to be deleted. In JavaScript, use the `.hooksToDelete` property accessor instead of calling `getHooksToDelete()`. |

### ContractUpdateTransaction Methods

| Method                        | Type                        | Description                                                                                                                                    |
| :---------------------------- | :-------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------- |
| `addHookToCreate(<hook>)`     | `HookCreationDetails`       | Adds a hook to be created on the existing contract.                                                                                            |
| `setHooksToCreate(<hooks>)`   | `list<HookCreationDetails>` | Sets all hooks to be created on the existing contract.                                                                                         |
| `addHookToDelete(<hookId>)`   | `long`                      | Marks a hook for deletion by its hook ID.                                                                                                      |
| `setHooksToDelete(<hookIds>)` | `list<long>`                | Marks multiple hooks for deletion.                                                                                                             |
| `getHooksToCreate()`          | `list<HookCreationDetails>` | Returns the list of hooks to be created. In JavaScript, use the `.hooksToCreate` property accessor instead of calling `getHooksToCreate()`.    |
| `getHooksToDelete()`          | `list<long>`                | Returns the list of hook IDs to be deleted. In JavaScript, use the `.hooksToDelete` property accessor instead of calling `getHooksToDelete()`. |

### Example: Add Hooks to an Existing Account

<CodeGroup>
  ```java Java theme={null}
  HookCreationDetails hook1 = new HookCreationDetails(
      HookExtensionPoint.ACCOUNT_ALLOWANCE_HOOK,
      1L,
      new EvmHook(contractId),
      adminKey.getPublicKey()
  );

  HookCreationDetails hook2 = new HookCreationDetails(
      HookExtensionPoint.ACCOUNT_ALLOWANCE_HOOK,
      2L,
      new EvmHook(contractId),
      adminKey.getPublicKey()
  );

  new AccountUpdateTransaction()
      .setAccountId(accountId)
      .addHookToCreate(hook1)
      .addHookToCreate(hook2)
      .setMaxTransactionFee(new Hbar(10))
      .freezeWith(client)
      .sign(accountKey)
      .execute(client)
      .getReceipt(client);
  ```

  ```javascript JavaScript theme={null}
  import {
      AccountUpdateTransaction,
      HookCreationDetails,
      EvmHook,
      HookExtensionPoint,
      Long,
  } from "@hiero-ledger/sdk";

  const hook1 = new HookCreationDetails({
      extensionPoint: HookExtensionPoint.ACCOUNT_ALLOWANCE_HOOK,
      hookId: Long.fromInt(1),
      evmHook: new EvmHook({ contractId }),
      adminKey: adminKey.publicKey,
  });

  const hook2 = new HookCreationDetails({
      extensionPoint: HookExtensionPoint.ACCOUNT_ALLOWANCE_HOOK,
      hookId: Long.fromInt(2),
      evmHook: new EvmHook({ contractId }),
      adminKey: adminKey.publicKey,
  });

  await (
      await (
          await new AccountUpdateTransaction()
              .setAccountId(accountId)
              .addHookToCreate(hook1)
              .addHookToCreate(hook2)
              .setMaxTransactionFee(new Hbar(10))
              .freezeWith(client)
              .sign(accountKey)
      ).execute(client)
  ).getReceipt(client);

  console.log("Successfully added hooks to account");
  ```

  ```go Go theme={null}
  hook1 := hedera.NewHookCreationDetails().
      SetExtensionPoint(hedera.ACCOUNT_ALLOWANCE_HOOK).
      SetHookId(1).
      SetEvmHook(*hedera.NewEvmHook().SetContractId(&contractId)).
      SetAdminKey(adminKey.PublicKey())

  hook2 := hedera.NewHookCreationDetails().
      SetExtensionPoint(hedera.ACCOUNT_ALLOWANCE_HOOK).
      SetHookId(2).
      SetEvmHook(*hedera.NewEvmHook().SetContractId(&contractId)).
      SetAdminKey(adminKey.PublicKey())

  response, err := hedera.NewAccountUpdateTransaction().
      SetAccountID(accountID).
      AddHookToCreate(hook1).
      AddHookToCreate(hook2).
      SetMaxTransactionFee(hedera.NewHbar(10)).
      FreezeWith(client).
      Sign(accountKey).
      Execute(client)

  _, err = response.GetReceipt(client)
  fmt.Println("Successfully added hooks to account")
  ```
</CodeGroup>

### Example: Delete Hooks from an Account

<Note>
  A hook can only be deleted when it has zero storage slots. Clear all storage first using [`HookStoreTransaction`](/hedera/sdks-and-apis/sdks/accounts-and-hbar/create-a-hookstore-transaction) (set values to empty bytes). If storage slots remain, the deletion fails with `HOOK_DELETION_REQUIRES_EMPTY_STORAGE`.

  An account cannot be deleted while it has hooks attached. `CryptoDelete` fails with `TRANSACTION_REQUIRES_ZERO_HOOKS`.
</Note>

<CodeGroup>
  ```java Java theme={null}
  new AccountUpdateTransaction()
      .setAccountId(accountId)
      .addHookToDelete(1L)
      .addHookToDelete(2L)
      .setMaxTransactionFee(new Hbar(10))
      .freezeWith(client)
      .sign(accountKey)
      .execute(client)
      .getReceipt(client);

  System.out.println("Successfully deleted hooks (IDs: 1, 2)");
  ```

  ```javascript JavaScript theme={null}
  import { AccountUpdateTransaction, Long } from "@hiero-ledger/sdk";

  await (
      await (
          await new AccountUpdateTransaction()
              .setAccountId(accountId)
              .addHookToDelete(Long.fromNumber(1))
              .addHookToDelete(Long.fromNumber(2))
              .setMaxTransactionFee(new Hbar(10))
              .freezeWith(client)
              .sign(accountKey)
      ).execute(client)
  ).getReceipt(client);

  console.log("Successfully deleted hooks (IDs: 1, 2)");
  ```

  ```go Go theme={null}
  response, err := hedera.NewAccountUpdateTransaction().
      SetAccountID(accountID).
      AddHookToDelete(1).
      AddHookToDelete(2).
      SetMaxTransactionFee(hedera.NewHbar(10)).
      FreezeWith(client).
      Sign(accountKey).
      Execute(client)

  _, err = response.GetReceipt(client)
  fmt.Println("Successfully deleted hooks (IDs: 1, 2)")
  ```
</CodeGroup>

### Atomic Delete and Recreate

To support atomic hook updates (e.g., for compliance), you can delete and recreate a hook with the same ID in a single update transaction. Deletions are processed first, then creations.

```javascript theme={null}
await (
    await (
        await new AccountUpdateTransaction()
            .setAccountId(accountId)
            .addHookToDelete(Long.fromNumber(1))          // Processed first
            .addHookToCreate(newHookDetailsWithId1)        // Processed second
            .setMaxTransactionFee(new Hbar(10))
            .freezeWith(client)
            .sign(accountKey)
    ).execute(client)
).getReceipt(client);
```

***

## Transaction Signing Requirements

| Scenario                            | Required Signatures                                               |
| :---------------------------------- | :---------------------------------------------------------------- |
| **Create hook on new account**      | Account key (from `AccountCreateTransaction`) + transaction payer |
| **Create hook on existing account** | Account key + transaction payer                                   |
| **Delete hook (no admin key)**      | Account/entity key + transaction payer                            |
| **Delete hook (with admin key)**    | Admin key OR entity key + transaction payer                       |

***

## Error Codes

| Status Code                            | Description                                                                                               |
| :------------------------------------- | :-------------------------------------------------------------------------------------------------------- |
| `HOOK_ID_REPEATED_IN_CREATION_DETAILS` | The same hook ID appears more than once in the `hook_creation_details` list.                              |
| `HOOK_ID_IN_USE`                       | An update transaction tried to create a hook with an ID already occupied on the entity.                   |
| `HOOK_NOT_FOUND`                       | An update transaction tried to delete a hook ID that does not exist on the entity.                        |
| `HOOK_DELETED`                         | Attempted to delete a hook that was already previously deleted.                                           |
| `INVALID_HOOK_CREATION_SPEC`           | The hook creation details are invalid (e.g., missing contract ID).                                        |
| `HOOK_DELETION_REQUIRES_EMPTY_STORAGE` | Cannot delete a hook that still has storage slots in use. Clear storage first via `HookStoreTransaction`. |
| `TRANSACTION_REQUIRES_ZERO_HOOKS`      | Cannot delete an account or contract that still has hooks attached. Remove all hooks first.               |
