# CANISTERS CANISTERS is an independently operated canister-management app hosted directly on the Internet Computer. It is deployed using the icp CLI. Historical development tools do not describe its current hosting. CANISTERS is a per-user dashboard for Internet Computer canisters. It tracks canisters, shows cycle balances, changes controllers, tops canisters up with cycles, creates canisters paid in ICP, and holds an in-app ICP balance for the signed-in principal. Origins: https://canisters.website and https://www.canisters.website serve the frontend. Read /.well-known/ic-architecture for canister ids and /.well-known/ii-derivation-origin for the Internet Identity derivation origin. Sign in through Internet Identity against that derivation origin so the caller principal matches the principal the user has in the browser. ## Units - ICP amounts are e8s (nat64). 1 ICP = 100000000 e8s. 0.0001 ICP = 10000 e8s. - transferIcp uses a gross amount: the recipient receives amountE8s minus the 10000 e8s ledger fee. - topUpCanister uses a gross total: CMC receives icpAmountE8s minus 10020000 e8s (0.1 ICP service fee plus two 0.0001 ICP ledger fees). Total must exceed 0.1002 ICP. Example: 1 ICP total sends 0.8998 ICP to CMC, 0.1 ICP to the service account, and 0.0002 ICP to ledger fees. - createCanister takes EXTRA seed ICP, not a gross total. getCreationCostEstimate returns the full total including base creation, extra seed, 0.1 ICP service fee, and both ledger fees. Its creationFeeIcpE8s already includes the CMC transfer fee; do not add transferFeeE8s twice. - Cycles are raw cycles (nat). 1 trillion cycles = 1000000000000. - Timestamps are nanoseconds since the Unix epoch (int). - An ICP account id is a 64-character hex string (32 bytes). It is the ledger account identifier, not a principal. - A principal (PID) is a canister id or an Internet Identity principal. ## Principals - The caller principal is the signed-in user. Anonymous principal 2vxsx-fae is rejected by user methods. - getAppPrincipal() is a query and returns this backend canister's principal (the app controller PID). It does not return the user. New canisters created here are controlled by both the caller and this app principal. - getMyAccount() is a query. It returns the caller's in-app ICP account: userId (the caller principal), subaccount (32 bytes derived from that principal), and accountId (64-character hex). ICP sent to that accountId on the ICP ledger is the balance these methods spend. The app does not pull ICP from any other account. ## Calls that do not spend the user's ICP Queries only see the caller's own records. listCanisters(page) is 0-based, 20 rows per page. Its cycleBalance is a cache, and its status is not live. searchCanisters(text), getRecentCanisters() (up to 5), getLowestCyclesCanisters() (up to 5), and getTotalCycles() use that same cache. getTransactionHistory(page) is 0-based, 20 rows, newest activity as stored, only the caller's rows. listFailedCreations() lists this caller's create payments that transferred ICP but did not finish creating a canister. These updates do not transfer the user's ICP. They burn a small amount of this canister's cycles to make inter-canister calls: - getMyBalance() returns the caller's in-app ICP balance in e8s. - getIcpXdrConversionRate() returns how many cycles one ICP currently mints. No caller restriction. - getCreationCostEstimate(seedCyclesIcpE8s) prices canister creation. seedCyclesIcpE8s is extra ICP, in e8s, beyond the minimum. Pass 0 for the minimum. totalIcpRequiredE8s is the gross ICP the caller must hold. No caller restriction. - getCanisterDetails(canisterId) reads live status, cycles, and controllers. The canister must already be in the caller's tracked list. Returns null if it is not. A management-canister failure returns cached cycles, an empty controller list, and fetchFailed true. ## State-changing calls Each of these requires a signed-in caller and changes state. Do not describe them as read-only. - addCanister(canisterId, customName) starts tracking an existing canister. It does not create it, delete it, or change its controllers. - removeCanister(canisterId) stops tracking it. The canister keeps running. - renameCanister(canisterId, newName) changes the label stored here. - addController(canisterId, controller) and removeController(canisterId, controller) change the canister's controller list through the management canister. Both the signed-in caller and this backend must be current controllers or the call fails. Merely tracking a canister grants no management authority. The app backend cannot be managed through these public endpoints. removeController rejects removing the last controller. A successful change takes effect immediately. - topUpCanister(canisterId, icpAmountE8s, acceptedServiceFeeE8s) spends the caller's in-app ICP and asks the cycles minting canister to mint cycles onto canisterId. The service fee and both ledger fees are taken out of icpAmountE8s. This spend is not reversible from this API. - transferIcp(toAccountId, amountE8s, memo) sends ICP from the caller's in-app account to toAccountId. toAccountId must be 64 hex characters. The ledger fee is taken out of amountE8s. The memo argument is stored in this app's log; the ledger memo is 0. This spend is not reversible from this API. The ok value is the ledger block index (nat64). - transferCycles(fromCanisterId, toCanisterId, amount) moves cycles. It does not spend ICP. fromCanisterId must be tracked by the caller, both the caller and this app must be live controllers, and amount must be greater than 0 and within the live balance. Transfers from this backend's own cycle reserve are always rejected. The source must implement the supported withdrawCyclesTo or wallet_send method. Newly created canisters are empty and cannot send cycles until compatible code is installed. A successful transfer is not reversible from this API. - migrateCanistersFromPrincipal(oldPrincipal) copies tracked-canister rows from oldPrincipal onto the caller. It does not move ICP, cycles, or controllers. Skip it unless the user asked to recover a list from a previous principal. - dismissFailedCreation(blockIndex) deletes one of the caller's recovery rows. It does not refund ICP. ## Creating a canister createCanister(name, seedCyclesIcpE8s, acceptedServiceFeeE8s) spends the caller's in-app ICP and creates a canister. name must be non-empty. seedCyclesIcpE8s is extra ICP in e8s beyond the minimum (0 is allowed). The minimum covers the protocol creation fee of 500000000000 cycles, a 100000000000 cycle residual, a 5 percent rate buffer, and the 10000 e8s CMC ledger fee, with a 0.5 ICP base floor. An additional 10000000 e8s service fee and 10000 e8s service-transfer fee are included in totalIcpRequiredE8s. Service fees do not reduce the seed cycles. The payment is not reversible from this API. Call getMyAccount, then getMyBalance, then getCreationCostEstimate before createCanister. If the balance is below totalIcpRequiredE8s, the user must transfer ICP to the accountId from getMyAccount. This API cannot fetch that ICP itself. Only one financial operation is in progress per caller. Pending operations also prevent new creates, top-ups, and ICP withdrawals until resolved. On success, ok contains canisterId and cyclesSeeded. Controllers of the new canister are the caller and the app principal from getAppPrincipal. The new canister is added to the caller's tracked list. ## Service fees and payment recovery getServiceFee() is a public query returning serviceFeeE8s = 10000000 (0.1 ICP), ledgerFeeE8s = 10000, and feeRecipient = 57aa808d8d809d04c334b8f27172b6db981eb83bd531f3097d91c7f41da1c346. This is a legacy ICP ledger account identifier, not a principal or an ICRC owner. The fee supports app operation and goes directly to this separate account. It does not automatically convert to cycles or top up this app. New creation/top-up calls must pass acceptedServiceFeeE8s = opt (10000000 : nat64), after reviewing the fee. Omitted/null or mismatched acknowledgment is rejected before spending, so stale clients cannot silently incur the new fee. The backend transfers the CMC funding first, obtains CMC confirmation, then transfers the service fee. A confirmed CMC refund incurs no service fee; CMC/ledger costs can still apply. After successful creation or top-up, the 0.1 ICP service fee is due even if fee settlement is temporarily unavailable. The backend reserves it by blocking conflicting spends while the operation is pending. The transaction history has separate CMC funding and service-fee rows; each includes its own ledger fee. getMyPayment() is a caller-scoped query returning the latest payment or null, including operation id, kind, totalE8s, cmcAmountE8s, status, cmcBlock, feeBlock, outcome and lastError. A completed receipt remains until the user explicitly starts another operation. Status values: - funding: a CMC transfer is unsent or its result is uncertain. - notifying: CMC payment confirmed, CMC notification pending. - collectingFee: canister operation succeeded (see outcome), service fee pending. - complete: canister operation and service fee completed. - refunded: CMC confirmed a refund; no service fee due. After an error, timeout, or lost response, read getMyPayment() BEFORE taking another spending action. retryMyPayment(operationId) resumes the same operation and never resends confirmed transfers. Retrying a complete receipt returns the recorded outcome without spending. It does not start a fresh operation. Do not repeat createCanister or topUpCanister to recover a failed attempt. There is no automatic polling or payment timer; recovery is user initiated. cancelMyPayment(operationId) only cancels a definitely unsent funding operation. An uncertain payment cannot be cancelled. reconcileMyPayment(operationId, blockIndex) verifies an exact ledger transfer against the persisted request (source, destination, amount, fee, memo, timestamp); it never transfers ICP. Use it if the ledger's deduplication window expired after a lost response, then retryMyPayment. If no matching block can be found, contact the operator for reconciliation; never fabricate a block or issue a new debit. Transfer timestamps never reset on ambiguous errors. Legacy create payments made before service fees: listFailedCreations, retryCreateCanister(blockIndex, name), and claimCreatePayment(blockIndex, name) remain available and do not retroactively charge a service fee. They are for legacy payments only. New operations use getMyPayment/retryMyPayment. CMC refunds return to the caller's in-app account. ## ICP MCP workflow and cycle conservation Connect an assistant using the ICP MCP Connector described at https://internetcomputer.org/icp-mcp/ and give it https://canisters.website. Discovery is /.well-known/ic-architecture, the backend's public candid:service metadata, then getApiDoc(). Public guidance is also at /llms.txt. Use the published /.well-known/ii-derivation-origin to obtain the user's Internet Identity delegation before protected calls. A different principal has a different balance and tracked list. Before a new spend: query getMyPayment, read getMyAccount, fetch getMyBalance once, and obtain getCreationCostEstimate for creation or getServiceFee for a top-up. Show the total, service fee, network fees, destination, and expected cycles to the user. State-changing calls require the user's authorization. Zero seed is the conservative creation default; do not choose a large seed without the user's instruction. New canisters are controlled by the caller and this backend, with no installed Wasm. Prefer cached query methods for browsing. Fetch live balance/status only when needed or on explicit refresh, and refresh once after a mutation. The CMC rate is cached for one hour; cycle amounts and creation cost are estimates and can change with the rate. Do not poll update methods or repeatedly retry pending payments in a loop. The app attaches zero of its reserve cycles to CMC creation/top-ups and retains the 100B minimum initial-cycle target. ## Do not call withdrawCyclesTo is an internal cycle-transfer endpoint; a compatible source must implement it and authorize the calling app. Any other caller is rejected. It attaches cycles and moves them.