Every write goes through the same pipeline: Hedera.Transaction builds a
TransactionBody, signs it (the operator always signs; extra keys via
:signers), and wraps it as a SignedTransaction. Hedera.Client sends it with
cross-node retry. All field numbers are the canonical Hedera HAPI values.
The result shape
{:ok, %{precheck_code: 0, ok?: true, transaction_id: tx_id}} =
Client.transfer_hbar(client, [{from, -1}, {to, 1}])precheck_code == 0 means the node accepted it. For the consensus outcome, fetch
the receipt:
{:ok, receipt} = Client.transaction_receipt(client, tx_id)
Hedera.Receipt.success?(receipt)A created entity's id is on the receipt: receipt.account_id, receipt.token_id,
receipt.file_id, receipt.schedule_id, receipt.contract_id,
receipt.topic_id, plus new_total_supply and serial_numbers.
Multi-signature
Pass extra required signers with :signers — e.g. associating a token to an
account the operator doesn't control:
Client.associate_token(client, account, [token], signers: [account_key])The operator (fee payer) always signs; each distinct key adds one signature.
The write surface
Crypto Service — HBAR & accounts
# balanced HBAR transfer (must net to zero; amounts are tinybars)
Client.transfer_hbar(client, [{operator_id, -100}, {recipient, 100}])
# account lifecycle — the new id comes back in receipt.account_id
{:ok, r} = Client.create_account(client, key: PublicKey, initial_balance: 0)
{:ok, receipt} = Client.transaction_receipt(client, r.transaction_id)
new_account = receipt.account_id
Client.update_account(client, new_account, account_memo: "renamed")
Client.delete_account(client, new_account, transfer_account: operator_id, signers: [new_key])Token Service (HTS)
Client.create_token(client, name: "My Token", symbol: "MTK", initial_supply: 1000,
treasury: operator_id, admin_key: pub, supply_key: pub, fee_schedule_key: pub)
Client.mint_token(client, token, 500)
Client.burn_token(client, token, 100)
Client.associate_token(client, account, [token], signers: [account_key])
Client.transfer_token(client, token, [{operator_id, -10}, {recipient, 10}])
# NFTs
Client.create_token(client, token_type: :nft, supply_type: :finite, max_supply: 100,
treasury: operator_id, supply_key: pub)
Client.mint_token(client, nft, 0, metadata: ["ipfs://…"])
Client.transfer_nft(client, nft, [{operator_id, recipient, 1}])
# management: freeze/unfreeze, grant/revoke KYC, wipe, pause/unpause, update, delete,
# dissociate, and custom fee schedules
Client.update_token(client, token, name: "Renamed", admin_key: pub)
Client.update_token_fee_schedule(client, token, [
%{type: :fixed, amount: 5, collector: operator_id},
%{type: :fractional, numerator: 1, denominator: 100, minimum: 1, maximum: 50, collector: operator_id}
])File, Schedule, Smart Contract Services
{:ok, r} = Client.create_file(client, contents: "hello", keys: [pub])
Client.append_file(client, file, "more")
Client.create_schedule(client, ...) # scheduled tx + multi-sig collection
Client.sign_schedule(client, schedule_id)
Client.create_contract(client, bytecode: evm_init, gas: 100_000)
Client.call_contract(client, contract, function_parameters: abi_encoded)For read-only contract calls and account queries, see Queries; for signing Ethereum transactions, see Ethereum.
Building without sending
Hedera.Transaction.* builders return %{transaction: bytes, transaction_id: id}
if you want to inspect or route the signed bytes yourself. Each builder documents
its required and optional opts.