Skip to main content
skill CX Codex

hedera-token-service — Claude Code skill: Hedera tokens in JS

Teaches your agent to write Hedera token and NFT code with the Hiero JavaScript SDK. Useful when the project already lives on that network.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add hedera-dev/hedera-skills --skill hedera-token-service -a claude-code
Open on GitHub

What hedera-token-service does

The skill covers Hedera Token Service work in JavaScript and TypeScript: creating fungible tokens and NFTs, minting, burning, transfers, token association, custom fees, plus KYC, freeze, wipe and pause operations. Everything runs through @hiero-ledger/sdk — no smart contracts, since HTS tokens are network entities.

Two client setups are documented: direct, with Client.forName(...).setOperator(...), and signer-based, with Wallet and LocalProvider. Then comes the transaction lifecycle: configure, freeze, sign, execute, receipt. When the operator key is enough, you can skip freeze and sign because execute(client) handles both. Key roles get their own section: adminKey, supplyKey, freezeKey, kycKey, wipeKey, pauseKey, feeScheduleKey, metadataKey. A key you leave out at creation disables that capability permanently, so check the set before you touch mainnet.

NFTs need TokenType.NonFungibleUnique and carry no decimals or initial supply — serials appear after minting. Transfer amounts are signed: negative debits, positive credits, and they must net to zero per token. An account has to associate with a token before receiving it, unless setMaxAutomaticTokenAssociations was set when the account was created.

What it helps with

  • create a fungible token with a treasury and supply key
  • mint an NFT collection with per-serial metadata
  • move tokens and NFTs between accounts in one transaction
  • set up KYC, freeze, wipe and pause on a token
  • associate a token with a recipient account

Example prompts for the agent

  • Create an HTS token named My Token with symbol MTK and initial supply 10000 using @hiero-ledger/sdk
  • Mint an NFT with metadata ipfs://QmFirst... into my existing collection
  • Transfer 100 units of the token from sender to receiver and show me the receipt

How to install hedera-token-service

1Skills CLI (Vercel)

npx skills add hedera-dev/hedera-skills --skill hedera-token-service -a claude-code

2Manually: put the files in place

~/.claude/skills/hedera-token-service/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/hedera-token-service/

Step by step, with commands for each platform: How to install skills in Claude Code: five ways

Questions about hedera-token-service

How do I install hedera-token-service in Claude?

Run npx skills add hedera-dev/hedera-skills --skill hedera-token-service -a claude-code. The skill lands at ~/.claude/skills/hedera-token-service/SKILL.md.

What does hedera-token-service do?

It gives the agent a reference for Hedera Token Service operations in JavaScript and TypeScript: token and NFT creation, minting, burning, transfers, association, custom fees and compliance operations.

Which keys does a Hedera token need?

Roles are split across adminKey, supplyKey, freezeKey, kycKey, wipeKey, pauseKey, feeScheduleKey and metadataKey. Omit a key at creation and that operation stays unavailable for good.

Source text

SKILL.md · Apache-2.0
---
name: hedera-token-service
description: "How to create, manage, and transfer tokens on Hedera using the Hiero JavaScript SDK (@hiero-ledger/sdk). Use this skill whenever the user wants to work with fungible tokens, NFTs, token creation, minting, burning, transfers, token association, custom fees (fixed, fractional, royalty), airdrops, KYC/freeze/wipe/pause operations, or any HTS (Hedera Token Service) operation in JavaScript or TypeScript. Also trigger when users mention @hashgraph/sdk token operations, ERC-20/ERC-721 equivalents on Hedera, or tokenization on the Hedera network."
---

# Hedera Token Service (HTS) — JavaScript SDK

HTS is Hedera's native token engine. It lets you create and manage fungible tokens and NFTs without writing smart contracts. Tokens created through HTS are first-class network entities with built-in compliance controls (KYC, freeze, wipe, pause) and custom fee schedules.

## Setup

All imports come from `@hiero-ledger/sdk`. Two setup patterns exist:

### Client + setOperator (direct)
```js
import { Client, AccountId, PrivateKey } from "@hiero-ledger/sdk";

const client = Client.forName(process.env.HEDERA_NETWORK)
    .setOperator(
        AccountId.fromString(process.env.OPERATOR_ID),
        PrivateKey.fromStringECDSA(process.env.OPERATOR_KEY),
    );
```

### Wallet + LocalProvider (signer-based)
```js
import { Wallet, LocalProvider } from "@hiero-ledger/sdk";

const provider = new LocalProvider();
const wallet = new Wallet(process.env.OPERATOR_ID, process.env.OPERATOR_KEY, provider);
```

With the signer pattern, use `freezeWithSigner(wallet)`, `signWithSigner(wallet)`, `executeWithSigner(wallet)`, and `getReceiptWithSigner(wallet)` instead of `freezeWith(client)` / `execute(client)`.

## Transaction Lifecycle

Every transaction follows this flow: **configure -> freeze -> sign -> execute -> receipt**.

```js
const tx = await new SomeTransaction()
    .setSomeField(value)
    .freezeWith(client);   // locks the transaction body

await tx.sign(privateKey); // add signatures (call multiple times for multi-sig)

const response = await tx.execute(client);
const receipt = await response.getReceipt(client);
```

When only the operator key is needed, you can skip explicit freeze/sign — `execute(client)` handles it:

```js
const response = await new SomeTransaction()
    .setSomeField(value)
    .execute(client);
const receipt = await response.getReceipt(client);
```

## Creating Tokens

### Fungible Token

```js
import {
    TokenCreateTransaction, TokenType, TokenSupplyType,
    PrivateKey, Hbar,
} from "@hiero-ledger/sdk";

const supplyKey = PrivateKey.generateECDSA();

const { tokenId } = await (
    await new TokenCreateTransaction()
        .setTokenName("My Token")
        .setTokenSymbol("MTK")
        .setDecimals(2)
        .setInitialSupply(10000)
        .setTreasuryAccountId(operatorId)
        .setAdminKey(operatorKey)
        .setSupplyKey(supplyKey)
        .execute(client)
).getReceipt(client);
```

### NFT (Non-Fungible Token)

NFTs require `TokenType.NonFungibleUnique`. They have **no decimals** and **no initial supply** — you mint individual serials after creation.

```js
const { tokenId: nftId } = await (
    await new TokenCreateTransaction()
        .setTokenName("My NFT Collection")
        .setTokenSymbol("MNFT")
        .setTokenType(TokenType.NonFungibleUnique)
        .setSupplyType(TokenSupplyType.Finite)
        .setMaxSupply(1000)
        .setTreasuryAccountId(treasuryId)
        .setAdminKey(adminKey)
        .setSupplyKey(supplyKey)
        .execute(client)
).getReceipt(client);
```

## Minting

```js
import { TokenMintTransaction } from "@hiero-ledger/sdk";

// Fungible — specify amount
await new TokenMintTransaction()
    .setTokenId(tokenId)
    .setAmount(500)
    .execute(client);

// NFT — specify metadata per serial
const { serials } = await (
    await new TokenMintTransaction()
        .setTokenId(nftId)
        .addMetadata(Buffer.from("ipfs://QmFirst..."))
        .addMetadata(Buffer.from("ipfs://QmSecond..."))
        .execute(client)
).getReceipt(client);
// serials = [Long(1), Long(2)]
```

## Token Association

Hedera accounts must **associate** with a token before they can receive it (unless the account has automatic token associations enabled via `setMaxAutomaticTokenAssociations`).

```js
import { TokenAssociateTransaction } from "@hiero-ledger/sdk";

await (
    await (
        await new TokenAssociateTransaction()
            .setAccountId(recipientId)
            .setTokenIds([tokenId])
            .freezeWith(client)
    ).sign(recipientKey)      // account owner must sign
).execute(client);
```

Alternatively, set `setMaxAutomaticTokenAssociations(-1)` on account creation to allow unlimited auto-association.

## Transfers

Use `TransferTransaction` for both fungible tokens and NFTs. Amounts are **signed**: negative debits, positive credits. They must net to zero per token.

```js
import { TransferTransaction } from "@hiero-ledger/sdk";

// Fungible transfer
await new TransferTransaction()
    .addTokenTransfer(tokenId, senderId, -100)
    .addTokenTransfer(tokenId, receiverId, 100)
    .execute(client);

// NFT transfer (tokenId, serial, sender, receiver)
await new TransferTransaction()
    .addNftTransfer(nftId, 1, senderId, receiverId)
    .execute(client);

// Mix Hbar + token transfers in one transaction
await new TransferTransaction()
    .addHbarTransfer(senderId, new Hbar(-5))
    .addHbarTransfer(receiverId, new Hbar(5))
    .addTokenTransfer(tokenId, senderId, -50)
    .addTokenTransfer(tokenId, receiverId, 50)
    .execute(client);
```

## Key Roles

Each key is optional. If omitted at creation, that capability is permanently disabled.

| Key | Purpose |
|-----|---------|
| `adminKey` | Update/delete the token; rotate other keys |
| `supplyKey` | Mint and burn |
| `freezeKey` | Freeze/unfreeze accounts from transferring this token |
| `kycKey` | Grant/revoke KYC status on accounts |
| `wipeKey` | Wipe token balance from an account |
| `pauseKey` | Pause/unpause all token operations globally |
| `feeScheduleKey` | Update the custom fee schedule |
| `metadataKey` | Update token or NFT metadata |

## Compliance Operations

```js
// Grant KYC (required when token has kycKey)
await new TokenGrantKycTransaction()
    .setTokenId(tokenId).setAccountId(accountId).execute(client);

// Freeze an account
await new TokenFreezeTransaction()
    .setTokenId(tokenId).setAccountId(accountId).execute(client);

// Unfreeze
await new TokenUnfreezeTransaction()
    .setTokenId(tokenId).setAccountId(accountId).execute(client);

// Wipe tokens from an account
await new TokenWipeTransaction()
    .setTokenId(tokenId).setAccountId(accountId).setAmount(10).execute(client);

// Pause all transfers
await new TokenPauseTransaction().setTokenId(tokenId).execute(client);

// Unpause
await new TokenUnpauseTransaction().setTokenId(tokenId).execute(client);
```

## Burning & Deleting

```js
import { TokenBurnTransaction, TokenDeleteTransaction } from "@hiero-ledger/sdk";

// Burn fungible
await new TokenBurnTransaction()
    .setTokenId(tokenId).setAmount(100).execute(client);

// Burn NFT serials
await new TokenBurnTransaction()
    .setTokenId(nftId).setSerials([1, 2]).execute(client);

// Delete entire token (requires adminKey)
await new TokenDeleteTransaction()
    .setTokenId(tokenId).execute(client);
```

## Querying Token Info

```js
import { TokenInfoQuery, TokenNftInfoQuery, NftId } from "@hiero-ledger/sdk";

const info = await new TokenInfoQuery().setTokenId(tokenId).execute(client);
console.log(info.name, info.symbol, info.totalSupply.toString());

const nftInfo = await new TokenNftInfoQuery()
    .setNftId(new NftId(nftId, 1))
    .execute(client);
console.log(nftInfo.accountId.toString()); // current owner
```

## Airdrops

Airdrops distribute tokens to multiple recipients. If a recipient can't auto-associate, the airdrop becomes **pending** and must be claimed.

```js
import {
    TokenAirdropTransaction, TokenClaimAirdropTransaction,
    TokenCancelAirdropTransaction, TokenRejectTransaction, NftId,
} from "@hiero-ledger/sdk";

// Send airdrop
const record = await (
    await (
        await new TokenAirdropTransaction()
            .addTokenTransfer(tokenId, treasuryId, -300)
            .addTokenTransfer(tokenId, recipient1, 100)
            .addTokenTransfer(tokenId, recipient2, 100)
            .addTokenTransfer(tokenId, recipient3, 100)
            .addNftTransfer(nftId, 1, treasuryId, recipient1)
            .freezeWith(client)
            .sign(treasuryKey)
    ).execute(client)
).getRecord(client);

// Check pending airdrops
const { newPendingAirdrops } = record;

// Recipient claims a pending airdrop
await (
    await new TokenClaimAirdropTransaction()
        .addPendingAirdropId(newPendingAirdrops[0].airdropId)
        .freezeWith(client)
        .sign(recipientKey)
).execute(client);

// Sender cancels a pending airdrop
await new TokenCancelAirdropTransaction()
    .addPendingAirdropId(newPendingAirdrops[1].airdropId)
    .execute(client);

// Recipient rejects tokens they already received
await (
    await new TokenRejectTransaction()
        .setOwnerId(recipientId)
        .addTokenId(tokenId)           // reject fungible
        .addNftId(new NftId(nftId, 1)) // reject NFT
        .freezeWith(client)
        .sign(recipientKey)
).execute(client);
```

## Custom Fees

HTS supports three fee types. See `references/custom-fees.md` for full details.

```js
import {
    CustomFixedFee, CustomFractionalFee, CustomRoyaltyFee, Hbar,
} from "@hiero-ledger/sdk";

const fixedFee = new CustomFixedFee()
    .setFeeCollectorAccountId(collectorId)
    .setHbarAmount(new Hbar(1));

const fractionalFee = new CustomFractionalFee()
    .setFeeCollectorAccountId(collectorId)
    .setNumerator(1).setDenominator(100)  // 1%
    .setMin(1).setMax(1000);

const royaltyFee = new CustomRoyaltyFee()
    .setFeeCollectorAccountId(collectorId)
    .setNumerator(5).setDenominator(100)  // 5%
    .setFallbackFee(
        new CustomFixedFee().setHbarAmount(new Hbar(2))
    );

await new TokenCreateTransaction()
    .setCustomFees([fixedFee, fractionalFee])
    // ... other fields
    .execute(client);
```

## Common Gotchas

1. **Association before transfer**: Recipients must associate with the token or have auto-association slots. Without it, transfers fail with `TOKEN_NOT_ASSOCIATED_TO_ACCOUNT`.

2. **Signed amounts**: `addTokenTransfer` uses signed amounts. Sender gets negative, receiver gets positive. They must net to zero.

3. **NFTs have no decimals or initial supply**: Set `TokenType.NonFungibleUnique` and mint serials individually.

4. **Keys are permanent if no adminKey**: Without an admin key, you cannot update or delete the token, and cannot change any other keys.

5. **KYC gate**: If a token has a `kycKey`, accounts must receive KYC approval before they can transact with that token.

6. **Supply key for mint/burn**: You need a supply key set at creation to later mint or burn.

7. **Treasury auto-association**: The treasury account is automatically associated with the token.

8. **Freeze before multi-sig**: When multiple parties need to sign, call `freezeWith(client)` first, then chain `.sign(key)` calls.

## Reference Files

- `references/api-reference.md` — Complete list of all HTS transaction and query classes with their methods
- `references/custom-fees.md` — Detailed guide to fixed, fractional, and royalty fees

© hedera-dev · Apache-2.0 · hedera-dev/hedera-skills

Similar skills

Everything for Backend and APIs →

From the same set13

Hedera Hackathon PRD Turns your hackathon idea into a PRD: asks about the track, writes HACKATHON-PRD.md, then predicts your score against the judging rubric. CX Codex +1 549installs hedera-consensus-service A skill that teaches your agent Hedera Consensus Service in JavaScript: create topics, submit messages, subscribe to the stream via mirror node. CX Codex +1 547installs Session Management Keeps a work log and a tech debt tracker across agent sessions, so a restart still shows what got done and what is pending. CX Codex +1 538installs Quality Gates This skill wires up automatic code checks: lint and typecheck after every edit, a local CI run before push, and a five-stage deploy checklist. CX Codex +1 536installs Hedera Agent Kit v4 Migration Walks a project from [email protected] to v4: swaps packages, rewrites imports and ports custom tools to BaseTool. CX Codex +1 484installs agent-kit-plugin This skill guides the creation of Hedera Agent Kit plugins and tools. Use it when adding token, account, consensus, smart contract, or custom Hedera functionality. CX Codex +1 463installs create-harness-spec Turns a Hedera feature idea into a runnable hedera-harness recipe: PRD, spec file and validators. Useful when you need a spec you can actually run. CX Codex +1 465installs harness-spec-anatomy Defines what a hedera-harness recipe is made of: the files, the v3 defaults, the run stages and the blind evaluate-checklist rule. For anyone authoring or auditing recipes. CX Codex +1 464installs

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor