# TurboAuthenticatedClient (/(apis)/turboauthenticatedclient) #### getBalance() Issues a signed request to get the credit balance of a wallet measured in AR (measured in Winston Credits, or winc). ```typescript const { winc: balance } = await turbo.getBalance(); ``` #### getFreeStatus() Returns the wallet's remaining free-tier upload allowance in bytes as `{ bytesRemaining }`, so you can tell up front whether an upload will be free. `bytesRemaining` is `null` for a wallet with an unlimited allowance (an exempt/partner wallet) and `0` when the free tier is disabled on the target Turbo deployment. It is advisory — the authoritative free/charge decision is made at upload time — and is a wallet-side figure (a per-network cap may also apply). ```typescript const { bytesRemaining } = await turbo.getFreeStatus(); ``` It is also available on the `TurboUnauthenticatedClient` for any wallet by address: ```typescript const { bytesRemaining } = await turbo.getFreeStatus('a-native-address'); ``` #### getPaymentHistory() Issues a signed request for the signing wallet's own completed top-up (payment) history — both cryptocurrency and fiat top-ups — merged newest-first and keyset-paginated. It is **self-scoped**: the service reads the wallet from the signature and returns only that wallet's rows, so it is available on the `TurboAuthenticatedClient` only (there is no by-address form). It accepts `{ limit, cursor }`, where `limit` is the page size (1–100, default 50). To page, pass the previous response's `cursor` while `hasMore` is `true`. Each item is discriminated by `type`. A `'crypto'` item includes `wincCredited`, `tokenType`, `tokenQuantity`, `usdEquivalent`, `senderAddress`, `transactionId`, and `blockHeight`; a `'fiat'` item includes `wincCredited`, `paymentAmount`, `currencyType`, `paymentProvider`, `receiptId`, and `giftMessage`. Every item has an ISO-8601 UTC `date`. The history covers credit and card top-ups; it is not a full ledger of spends. ```typescript const { payments, hasMore, cursor } = await turbo.getPaymentHistory({ limit: 25, }); // Fetch the next page while more results remain if (hasMore) { const next = await turbo.getPaymentHistory({ limit: 25, cursor }); } ``` #### signer.getNativeAddress() Returns the [native address][docs/native-address] of the connected signer. ```typescript const address = await turbo.signer.getNativeAddress(); ``` #### getWincForFiat() Returns the current amount of Winston Credits including all adjustments for the provided fiat currency, amount, and optional promo codes. ```typescript const { winc, paymentAmount, quotedPaymentAmount, adjustments } = await turbo.getWincForFiat({ amount: USD(100), promoCodes: ['MY_PROMO_CODE'], // promo codes require an authenticated client }); ``` #### createCheckoutSession() Creates a Stripe checkout session for a Turbo Top Up with the provided amount, currency, owner, and optional promo codes. The returned URL can be opened in the browser, all payments are processed by Stripe. Promo codes require an authenticated client. ```typescript const { url, winc, paymentAmount, quotedPaymentAmount, adjustments } = await turbo.createCheckoutSession({ amount: USD(10.0), // $10.00 USD owner: publicArweaveAddress, promoCodes: ['MY_PROMO_CODE'], // promo codes require an authenticated client }); // open checkout session in a browser window.open(url, '_blank'); ``` #### upload() The easiest way to upload data to Turbo. The `signal` is an optional [AbortSignal] that can be used to cancel the upload or timeout the request. `dataItemOpts` is an optional object that can be used to configure tags, target, and anchor for the data item upload. ```typescript const uploadResult = await turbo.upload({ data: 'The contents of my file!', signal: AbortSignal.timeout(10_000), // cancel the upload after 10 seconds dataItemOpts: { // optional }, events: { // optional }, }); ``` #### uploadFile() Signs and uploads a raw file. There are two ways to provide the file to the SDK: 1. Using a `file` parameter 2. Using a `fileStreamFactory` and `fileSizeFactory` ##### Using file` In Web with a file input: ```typescript const selectedFile = e.target.files[0]; const uploadResult = await turbo.uploadFile({ file: selectedFile, dataItemOpts: { tags: [{ name: 'Content-Type', value: 'text/plain' }], }, events: { onUploadProgress: ({ totalBytes, processedBytes }) => { console.log('Upload progress:', { totalBytes, processedBytes }); }, onUploadError: (error) => { console.log('Upload error:', { error }); }, onUploadSuccess: () => { console.log('Upload success!'); }, }, }); ``` In NodeJS with a file path: ```typescript const filePath = path.join(__dirname, './my-unsigned-file.txt'); const fileSize = fs.stateSync(filePath).size; const uploadResult = await turbo.uploadFile({ file: filePath, dataItemOpts: { tags: [{ name: 'Content-Type', value: 'text/plain' }], }, }); ``` ##### Using fileStreamFactory` and `fileSizeFactory` Note: The provided `fileStreamFactory` should produce a NEW file data stream each time it is invoked. The `fileSizeFactory` is a function that returns the size of the file. The `signal` is an optional [AbortSignal] that can be used to cancel the upload or timeout the request. `dataItemOpts` is an optional object that can be used to configure tags, target, and anchor for the data item upload. ```typescript const filePath = path.join(__dirname, './my-unsigned-file.txt'); const fileSize = fs.stateSync(filePath).size; const uploadResult = await turbo.uploadFile({ fileStreamFactory: () => fs.createReadStream(filePath), fileSizeFactory: () => fileSize, }); ``` ##### Customize Multi-Part Upload Behavior By default, the Turbo upload methods will split files that are larger than 10 MiB into chunks and send them to the upload service multi-part endpoints. This behavior can be customized with the following inputs: - `chunkByteCount`: The maximum size in bytes for each chunk. Must be between 5 MiB and 500 MiB. Defaults to 5 MiB. - `maxChunkConcurrency`: The maximum number of chunks to upload concurrently. Defaults to 5. Reducing concurrency will slow down uploads, but reduce memory utilization and serialize network calls. Increasing it will upload faster, but can strain available resources. - `chunkingMode`: The chunking mode to use. Can be 'auto', 'force', or 'disabled'. Defaults to 'auto'. Auto behavior means chunking is enabled if the file would be split into at least three chunks. - `maxFinalizeMs`: The maximum time in milliseconds to wait for the finalization of all chunks after the last chunk is uploaded. Defaults to 1 minute per GiB of the total file size. ```typescript // Customize chunking behavior await turbo.upload({ ...params, chunkByteCount: 1024 * 1024 * 500, // Max chunk size maxChunkConcurrency: 1, // Minimize concurrency }); ``` ```typescript // Disable chunking behavior await turbo.upload({ ...params, chunkingMode: 'disabled', }); ``` ```typescript // Force chunking behavior await turbo.upload({ ...params, chunkingMode: 'force', }); ``` #### On Demand Uploads With the upload methods, you can choose to Top Up with selected crypto token on demand if the connected wallet does not have enough credits to complete the upload. This is done by providing the `OnDemandFunding` class to the `fundingMode` parameter on upload methods. The `maxTokenAmount` (optional) is the maximum amount of tokens in the token type's smallest unit value (e.g: Winston for arweave token type) to fund the wallet with. The `topUpBufferMultiplier` (optional) is the multiplier to apply to the estimated top-up amount to avoid underpayment during on-demand top-ups due to price fluctuations on longer uploads. Defaults to 1.1, meaning a 10% buffer. Note: On demand API currently only available for $ARIO (`ario`), $SOL (`solana`), $ETH on Base Network (`base-eth`) and $USDC on Base Network (`base-usdc`) token types. ```typescript const turbo = TurboFactory.authenticated({ signer: arweaveSignerWithARIO, token: 'ario', }); await turbo.upload({ ...params, fundingMode: new OnDemandFunding({ maxTokenAmount: ARIOToTokenAmount(500), // Max 500 $ARIO topUpBufferMultiplier: 1.1, // 10% buffer to avoid underpayment }), }); ``` #### x402 Uploads Another method of uploading files is via the x402 protocol. This method is optimized for agent workflows and allows for direct uploads to Arweave gateways that support the x402 protocol using an EVM wallet and base-usdc token type. ```typescript const turbo = TurboFactory.authenticated({ signer: ethereumSignerWithBaseUSDC, token: 'base-usdc', }); await turbo.uploadFile({ ...params, fundingMode: new X402Funding({ maxMUSDCAmount: 1_000_000 }), // Max 1 USDC. Opt out if too expensive }); ``` #### Raw x402 Data Uploads Using the x402 protocol, you can also upload raw data to Turbo without signing a data item. This method is ideal for quick agent workflows where the ownership of the data is not required to be tied to a specific wallet. The eventual data item on chain will be signed by Turbo's x402 EVM signer. ```typescript const turbo = TurboFactory.authenticated({ signer: ethereumSignerWithBaseUSDC, token: 'base-usdc', }); await turbo.uploadRawX402Data({ data: myRawData, maxMUSDCAmount: 1_000_000, // Max 1 USDC. Opt out if too expensive }); ``` NOTE: For free uploads under 105 KiB, this method of upload currently does not require a signature and can be used with an unauthenticated client. ```ts // Unsigned free upload of raw data under 105 KiB const turbo = TurboFactory.unauthenticated({ token: 'base-usdc' }); await turbo.uploadRawX402Data({ data: myRawData, }); ``` #### uploadFolder() Signs and uploads a folder of files. For NodeJS, the `folderPath` of the folder to upload is required. For the browser, an array of `files` is required. The `dataItemOpts` is an optional object that can be used to configure tags, target, and anchor for the data item upload. The `signal` is an optional [AbortSignal] that can be used to cancel the upload or timeout the request. The `maxConcurrentUploads` is an optional number that can be used to limit the number of concurrent uploads. The `throwOnFailure` is an optional boolean that can be used to throw an error if any upload fails. The `manifestOptions` is an optional object that can be used to configure the manifest file, including a custom index file, fallback file, or whether to disable manifests altogether. Manifests are enabled by default. ##### NodeJS Upload Folder ```typescript const folderPath = path.join(__dirname, './my-folder'); const { manifest, fileResponses, manifestResponse } = await turbo.uploadFolder({ folderPath, dataItemOpts: { // optional tags: [ { // User defined content type will overwrite file content type name: 'Content-Type', value: 'text/plain', }, { name: 'My-Custom-Tag', value: 'my-custom-value', }, ], // no timeout or AbortSignal provided }, manifestOptions: { // optional indexFile: 'custom-index.html', fallbackFile: 'custom-fallback.html', disableManifests: false, }, }); ``` ##### Browser Upload Folder ```html const folderInput = document.getElementById('folder'); folderInput.addEventListener('change', async (event) => { const selectedFiles = folderInput.files; console.log('Folder selected:', selectedFiles); const { manifest, fileResponses, manifestResponse } = await turbo.uploadFolder({ files: Array.from(selectedFiles).map((file) => file), }); console.log(manifest, fileResponses, manifestResponse); }); ``` ##### Upload Folder with Progress Events The `uploadFolder` method supports folder-level and per-file events for tracking upload progress. This is useful for building progress bars or providing feedback to users during folder uploads. ```typescript const folderPath = path.join(__dirname, './my-folder'); const { manifest, fileResponses, manifestResponse } = await turbo.uploadFolder({ folderPath, events: { // Per-file events onFileStart: ({ fileName, fileSize, fileIndex, totalFiles }) => { console.log( `Starting file ${ fileIndex + 1 }/${totalFiles}: ${fileName} (${fileSize} bytes)`, ); }, onFileProgress: ({ fileName, fileIndex, totalFiles, fileProcessedBytes, fileTotalBytes, step, }) => { const percentComplete = (fileProcessedBytes / fileTotalBytes) * 100; console.log( `File ${ fileIndex + 1 }/${totalFiles} (${fileName}) ${step}: ${percentComplete.toFixed(2)}%`, ); }, onFileComplete: ({ fileName, fileIndex, totalFiles, id }) => { console.log( `Completed file ${fileIndex + 1}/${totalFiles}: ${fileName} (${id})`, ); }, onFileError: ({ fileName, fileIndex, totalFiles, error }) => { console.error( `Error uploading file ${fileIndex + 1}/${totalFiles}: ${fileName}`, error, ); }, // Folder-level aggregate events onFolderProgress: ({ processedFiles, totalFiles, processedBytes, totalBytes, currentPhase, }) => { const percentComplete = (processedBytes / totalBytes) * 100; console.log( `Folder progress (${currentPhase}): ${processedFiles}/${totalFiles} files, ${percentComplete.toFixed( 2, )}%`, ); }, onFolderError: (error) => { console.error('Folder upload error:', error); }, onFolderSuccess: () => { console.log('Folder upload complete!'); }, }, }); ``` #### topUpWithTokens() Tops up the connected wallet with Credits by submitting a payment transaction for the token amount to the Turbo wallet and then submitting that transaction id to Turbo Payment Service for top up processing. - The `tokenAmount` is the amount of tokens in the token type's smallest unit value (e.g: Winston for arweave token type) to fund the wallet with. - The `feeMultiplier` (optional) is the multiplier to apply to the reward for the transaction to modify its chances of being mined. Credits will be added to the wallet balance after the transaction is confirmed on the given blockchain. Defaults to 1.0, meaning no multiplier. - The `turboCreditDestinationAddress` (optional) is the native address to credit the funds to. If not provided, the connected wallet's native address will be used. ##### Arweave (AR) Crypto Top Up ```typescript const turbo = TurboFactory.authenticated({ signer, token: 'arweave' }); const { winc, status, id, ...fundResult } = await turbo.topUpWithTokens({ tokenAmount: WinstonToTokenAmount(100_000_000), // 0.0001 AR feeMultiplier: 1.1, // 10% increase in reward for improved mining chances turboCreditDestinationAddress: '0xabc...123', // Any custom EVM / SOL / AR native destination address }); ``` ##### AR.IO Network (ARIO) Crypto Top Up ```typescript const turbo = TurboFactory.authenticated({ signer, token: 'ario' }); const { winc, status, id, ...fundResult } = await turbo.topUpWithTokens({ tokenAmount: ARIOToTokenAmount(100), // 100 $ARIO }); // ARIO on Base Network const { winc, status, id, ...fundResult } = await TurboFactory.authenticated({ signer, token: 'base-ario', }).topUpWithTokens({ tokenAmount: ARIOToTokenAmount(100), // 100 $ARIO }); ``` ##### USDC Crypto Top Up ```typescript // USDC on Ethereum Mainnet const { winc, status, id, ...fundResult } = await TurboFactory.authenticated({ signer, token: 'usdc', }).topUpWithTokens({ tokenAmount: USDCToTokenAmount(1), // 1 USDC }); // USDC on Base Network const { winc, status, id, ...fundResult } = await TurboFactory.authenticated({ signer, token: 'base-usdc', }).topUpWithTokens({ tokenAmount: USDCToTokenAmount(1), // 1 USDC }); // USDC on Polygon Network const { winc, status, id, ...fundResult } = await TurboFactory.authenticated({ signer, token: 'polygon-usdc', }).topUpWithTokens({ tokenAmount: USDCToTokenAmount(1), // 1 USDC }); ``` ##### Ethereum (ETH) Crypto Top Up ```typescript const turbo = TurboFactory.authenticated({ signer, token: 'ethereum' }); const { winc, status, id, ...fundResult } = await turbo.topUpWithTokens({ tokenAmount: ETHToTokenAmount(0.00001), // 0.00001 ETH }); ``` ##### Polygon (POL / MATIC) Crypto Top Up ```typescript const turbo = TurboFactory.authenticated({ signer, token: 'pol' }); const { winc, status, id, ...fundResult } = await turbo.topUpWithTokens({ tokenAmount: POLToTokenAmount(0.00001), // 0.00001 POL }); ``` ##### Eth on Base Network Crypto Top Up ```typescript const turbo = TurboFactory.authenticated({ signer, token: 'base-eth' }); const { winc, status, id, ...fundResult } = await turbo.topUpWithTokens({ tokenAmount: ETHToTokenAmount(0.00001), // 0.00001 ETH bridged on Base Network }); ``` ##### Solana (SOL) Crypto Top Up ```typescript const turbo = TurboFactory.authenticated({ signer, token: 'solana' }); const { winc, status, id, ...fundResult } = await turbo.topUpWithTokens({ tokenAmount: SOLToTokenAmount(0.00001), // 0.00001 SOL }); ``` #### shareCredits() Shares credits from the connected wallet to the provided native address and approved winc amount. This action will create a signed data item for the approval ```typescript const { approvalDataItemId, approvedWincAmount } = await turbo.shareCredits({ approvedAddress: '2cor...VUa', approvedWincAmount: 800_000_000_000, // 0.8 Credits expiresBySeconds: 3600, // Credits will expire back to original wallet in 1 hour }); ``` #### revokeCredits() Revokes all credits shared from the connected wallet to the provided native address. ```typescript const revokedApprovals = await turbo.revokeCredits({ revokedAddress: '2cor...VUa', }); ``` #### getCreditShareApprovals() Returns all given or received credit share approvals for the connected wallet or the provided native address. ```typescript const { givenApprovals, receivedApprovals } = await turbo.getCreditShareApprovals({ userAddress: '2cor...VUa', }); ``` # TurboFactory (/(apis)/turbofactory) #### unauthenticated() Creates an instance of a client that accesses Turbo's unauthenticated services. ```typescript const turbo = TurboFactory.unauthenticated(); ``` #### authenticated() Creates an instance of a client that accesses Turbo's authenticated and unauthenticated services. Requires either a signer, or private key to be provided. See the [Signers] section for all supported signers and authentication methods. ```typescript const signer = new ArweaveSigner(jwk); const turbo = TurboFactory.authenticated({ signer }); ``` #### Testnet Configuration For development and testing, you can configure the SDK to use blockchain testnets. This allows you to test your integration with free testnet tokens without spending real cryptocurrency. **Important**: The SDK defaults to mainnet. You must explicitly set the `gatewayUrl` parameter to use a testnet. ```typescript // Base Sepolia (recommended for testing) const turbo = TurboFactory.authenticated({ privateKey: process.env.BASE_SEPOLIA_PRIVATE_KEY, token: 'base-eth', gatewayUrl: 'https://sepolia.base.org', // Required for testnet paymentServiceConfig: { url: 'https://payment.services.ar-io.dev', // ar.io testnet sandbox }, uploadServiceConfig: { url: 'https://upload.services.ar-io.dev', // ar.io testnet sandbox } }); // Solana Devnet const turbo = TurboFactory.authenticated({ privateKey: bs58.encode(secretKey), token: 'solana', gatewayUrl: 'https://api.devnet.solana.com', paymentServiceConfig: { url: 'https://payment.services.ar-io.dev', }, uploadServiceConfig: { url: 'https://upload.services.ar-io.dev', } }); // Ethereum Sepolia const turbo = TurboFactory.authenticated({ privateKey: process.env.SEPOLIA_PRIVATE_KEY, token: 'ethereum', gatewayUrl: 'https://sepolia.gateway.tenderly.co', paymentServiceConfig: { url: 'https://payment.services.ar-io.dev', }, uploadServiceConfig: { url: 'https://upload.services.ar-io.dev', }, }); ``` These endpoints are the **ar.io Testnet Sandbox** — the full ar.io stack (upload, payment, ArNS, and gateway) running on testnet, with a faucet so nothing costs real money. Uploaded data is served from the sandbox gateway at `https://ar-io.dev` and is **ephemeral** (purged after ~3 days); it is never posted to mainnet Arweave. See [the ar.io Testnet Sandbox docs](https://docs.ar.io/build/testnet). **Supported Testnets**: - **ARIO staging** (`ario`) - Staging ARIO on Solana devnet; fee-free funding, claim from the [ar.io faucet](https://faucet.services.ar-io.dev) - **Base Sepolia** (`base-eth`) - Supports on-demand funding - **Solana Devnet** (`solana`) - Supports on-demand funding - **Ethereum Sepolia** (`ethereum`) - Manual top-up only - **Polygon Amoy** (`pol`) - Manual top-up only # TurboUnauthenticatedClient (/(apis)/turbounauthenticatedclient) #### getSupportedCurrencies() Returns the list of currencies supported by the Turbo Payment Service for topping up a user balance of AR Credits (measured in Winston Credits, or winc). ```typescript const currencies = await turbo.getSupportedCurrencies(); ``` #### getSupportedCountries() Returns the list of countries supported by the Turbo Payment Service's top up workflow. ```typescript const countries = await turbo.getSupportedCountries(); ``` #### getFreeStatus() Returns a wallet's remaining free-tier upload allowance in bytes as `{ bytesRemaining }` — `null` for an unlimited (exempt/partner) wallet, `0` when the free tier is disabled. Advisory: the authoritative free/charge decision is made at upload time. ```typescript const { bytesRemaining } = await turbo.getFreeStatus('a-native-address'); ``` #### getFiatToAR() Returns the current raw fiat to AR conversion rate for a specific currency as reported by third-party pricing oracles. ```typescript const fiatToAR = await turbo.getFiatToAR({ currency: 'USD' }); ``` #### getFiatRates() Returns the current fiat rates for 1 GiB of data for supported currencies, including all top-up adjustments and fees. ```typescript const rates = await turbo.getFiatRates(); ``` #### getWincForFiat() Returns the current amount of Winston Credits including all adjustments for the provided fiat currency. ```typescript const { winc, actualPaymentAmount, quotedPaymentAmount, adjustments } = await turbo.getWincForFiat({ amount: USD(100), }); ``` #### getWincForToken() Returns the current amount of Winston Credits including all adjustments for the provided token amount. ```typescript const { winc, actualTokenAmount, equivalentWincTokenAmount } = await turbo.getWincForToken({ tokenAmount: WinstonToTokenAmount(100_000_000), }); ``` #### getFiatEstimateForBytes() Get the current price from the Turbo Payment Service, denominated in the specified fiat currency, for uploading a specified number of bytes to Turbo. ```typescript const turbo = TurboFactory.unauthenticated(); const { amount } = await turbo.getFiatEstimateForBytes({ byteCount: 1024 * 1024 * 1024, currency: 'usd', // specify the currency for the price }); console.log(amount); // Estimated usd price for 1 GiB ``` **Output:** ```json { "byteCount": 1073741824, "amount": 20.58, "currency": "usd", "winc": "2402378997310" } ``` #### getTokenPriceForBytes() Get the current price from the Turbo Payment Service, denominated in the specified token, for uploading a specified number of bytes to Turbo. ```typescript const turbo = TurboFactory.unauthenticated({ token: 'solana' }); const { tokenPrice } = await turbo.getTokenPriceForBytes({ byteCount: 1024 * 1024 * 100, }); console.log(tokenPrice); // Estimated SOL Price for 100 MiB ``` #### getUploadCosts() Returns the estimated cost in Winston Credits for the provided file sizes, including all upload adjustments and fees. ```typescript const [uploadCostForFile] = await turbo.getUploadCosts({ bytes: [1024] }); const { winc, adjustments } = uploadCostForFile; ``` #### uploadSignedDataItem() Uploads a signed data item. The provided `dataItemStreamFactory` should produce a NEW signed data item stream each time is it invoked. The `dataItemSizeFactory` is a function that returns the size of the file. The `signal` is an optional [AbortSignal] that can be used to cancel the upload or timeout the request. The `events` parameter is an optional object that can be used to listen to upload progress, errors, and success (refer to the [Events] section for more details). ```typescript const filePath = path.join(__dirname, './my-signed-data-item'); const dataItemSize = fs.statSync(filePath).size; const uploadResponse = await turbo.uploadSignedDataItem({ dataItemStreamFactory: () => fs.createReadStream(filePath), dataItemSizeFactory: () => dataItemSize, signal: AbortSignal.timeout(10_000), // cancel the upload after 10 seconds events: { // track upload events only onUploadProgress: ({ totalBytes, processedBytes }) => { console.log('Upload progress:', { totalBytes, processedBytes }); }, onUploadError: (error) => { console.log('Upload error:', { error }); }, onUploadSuccess: () => { console.log('Upload success!'); }, }, }); ``` #### createCheckoutSession() Creates a Stripe checkout session for a Turbo Top Up with the provided amount, currency, owner. The returned URL can be opened in the browser, all payments are processed by Stripe. To leverage promo codes, see [TurboAuthenticatedClient]. ##### Arweave (AR) Fiat Top Up ```typescript const { url, winc, paymentAmount, quotedPaymentAmount, adjustments } = await turbo.createCheckoutSession({ amount: USD(10.0), // $10.00 USD owner: publicArweaveAddress, // promo codes require an authenticated client }); // Open checkout session in a browser window.open(url, '_blank'); ``` ##### Ethereum (ETH) Fiat Top Up ```typescript const turbo = TurboFactory.unauthenticated({ token: 'ethereum' }); const { url, winc, paymentAmount } = await turbo.createCheckoutSession({ amount: USD(10.0), // $10.00 USD owner: publicEthereumAddress, }); ``` ##### Solana (SOL) Fiat Top Up ```typescript const turbo = TurboFactory.unauthenticated({ token: 'solana' }); const { url, winc, paymentAmount } = await turbo.createCheckoutSession({ amount: USD(10.0), // $10.00 USD owner: publicSolanaAddress, }); ``` ##### Polygon (POL / MATIC) Fiat Top Up ```typescript const turbo = TurboFactory.unauthenticated({ token: 'pol' }); const { url, winc, paymentAmount } = await turbo.createCheckoutSession({ amount: USD(10.0), // $10.00 USD owner: publicPolygonAddress, }); ``` #### submitFundTransaction() Submits the transaction ID of a funding transaction to Turbo Payment Service for top up processing. The `txId` is the transaction ID of the transaction to be submitted. Use this API if you've already executed your token transfer to the Turbo wallet. Otherwise, consider using `topUpWithTokens` to execute a new token transfer to the Turbo wallet and submit its resulting transaction ID for top up processing all in one go ```typescript const turbo = TurboFactory.unauthenticated(); // defaults to arweave token type const { status, id, ...fundResult } = await turbo.submitFundTransaction({ txId: 'my-valid-arweave-fund-transaction-id', }); ``` # ANT custody: transfer & manage records (/(arns-names-paid-with-turbo-credits)/ant-custody-transfer-manage-records) Turbo can custody the ANT (Metaplex Core asset) backing your name. These methods let you take self-custody or manage resolution records. Each is authenticated with an **action-bound, single-use signature**: the wallet signs a canonical `arns\n\\n` message plus the UUID nonce, so a captured signature can't be replayed against a different operation. ```typescript // Self-custody exit: move the ANT to a Solana pubkey you control await turbo.transferArNSAnt({ antId: 'ant-id', target: 'your-solana-pubkey', }); // Set a resolution record (undername defaults to '@') await turbo.setArNSRecord({ antId: 'ant-id', undername: 'docs', // omit for the apex '@' record transactionId: 'arweave-tx-id', ttlSeconds: 900, }); // Remove a resolution record await turbo.removeArNSRecord({ antId: 'ant-id', undername: 'docs' }); ``` # Buying a name (/(arns-names-paid-with-turbo-credits)/buying-a-name) `buyArNSName(params)` is the `Buy-Name` convenience wrapper over `purchaseArNSName`. Optionally, `paidBy` delegates the charge to one or more addresses that have shared credits with you. **`processId` is optional**, and it selects who owns the ANT (Metaplex Core asset) the name resolves to: - **Omit `processId`** → **Turbo custodial provisioning** (Model A): Turbo spawns and _owns_ the ANT for you. You can take self-custody later via `transferArNSAnt` (see "ANT custody" below). - **Supply `processId`** → **user-owned ANT** (Model B): the name points at an ANT you already own; Turbo never takes custody. ```typescript // Custodial lease (Model A): omit processId → Turbo owns the ANT const receipt = await turbo.buyArNSName({ name: 'my-name', type: 'lease', years: 1, }); // Lease against your own ANT (Model B) for 1 year const receipt = await turbo.buyArNSName({ name: 'my-name', type: 'lease', years: 1, processId: 'ant-process-id', }); // Permanent buy, charged to a delegated payer const receipt = await turbo.buyArNSName({ name: 'my-name', type: 'permabuy', processId: 'ant-process-id', // optional — omit for Turbo custodial provisioning paidBy: '\', // or an array of addresses }); console.log(receipt.nonce); // capture this to poll status / retry idempotently ``` Full runnable example (buy → poll to terminal): ```typescript const turbo = TurboFactory.authenticated({ privateKey: arweaveJwk }); async function buyName() { try { const { nonce } = await turbo.buyArNSName({ name: 'my-name', type: 'lease', years: 1, processId: 'ant-process-id', }); // Poll until terminal (success => messageId, failure => failedDate) for (;;) { const status = await turbo.getArNSPurchaseStatus({ nonce }); if (status.messageId) { console.log('Purchased. ArNS write tx:', status.messageId); return status; } if (status.failedDate) { throw new Error(`Purchase failed at ${status.failedDate}`); } await new Promise((r) => setTimeout(r, 2000)); } } catch (err) { if (err instanceof InsufficientCreditsError) { console.error('Not enough Turbo Credits — top up and retry.'); } throw err; } } ``` # Connecting a signer (/(arns-names-paid-with-turbo-credits)/connecting-a-signer) ArNS purchases are authenticated per wallet. Construct the client with `TurboFactory.authenticated` using any supported identity — the credit balance is keyed to that wallet's native address: ```typescript // Arweave const turbo = TurboFactory.authenticated({ privateKey: arweaveJwk }); // Ethereum const turbo = TurboFactory.authenticated({ privateKey: ethHexadecimalPrivateKey, token: 'ethereum', }); // Solana — request nonces are signed with arbundles' HexSolanaSigner (ed25519) const turbo = TurboFactory.authenticated({ privateKey: bs58SolanaSecretKey, token: 'solana', }); ``` # Dependency note (@solana/codecs) (/(arns-names-paid-with-turbo-credits)/dependency-note-solana-codecs) ArNS/ARIO support pulls in `@solana/spl-token`, whose transitive `@solana/spl-token-metadata@0.1.6` imports `getDataEnumCodec` from `@solana/codecs@2.0.0-rc.1`. In `@solana/codecs@3+` that There is no single codecs version that satisfies both `spl-token-metadata` (needs the old `getDataEnumCodec`) and `@solana/kit` (needs `5.x`), and `spl-token-metadata` has no release that uses the renamed API — so the fix belongs at the app's dependency-resolution layer, **not** at symbol-aliasing: - **Recommended:** stop deduping `@solana/codecs` so `@solana/spl-token-metadata` keeps its own nested `2.0.0-rc.1` copy. In Vite, ensure `@solana/codecs` is **not** in `resolve.dedupe`; with pnpm/yarn, allow the nested version (avoid a hoisted-to-`6.x` override for that subtree). This is cleaner than the `getDataEnumCodec → getDiscriminatedUnionCodec` alias plugin some apps use today, and removes the need for that shim. - If you must keep a single hoisted codecs copy, a build-time alias mapping `getDataEnumCodec` to `getDiscriminatedUnionCodec` remains the fallback. # Error handling & retries (/(arns-names-paid-with-turbo-credits)/error-handling-retries) - **`InsufficientCreditsError`** (HTTP `402`) — the wallet (or delegated payer) doesn't hold enough Turbo Credits. Prompt the user to top up, then retry. It exposes `.status === 402` and is exported from the package root. - **`ProvidedInputError`** — thrown client-side (before any network call) when required per-intent params are missing/invalid (e.g. a lease `Buy-Name` without `years`, or `Extend-Lease` without a positive `years`). - **`FailedRequestError`** — any other non-2xx response; inspect `.status` (e.g. `401`, `503`). **Idempotency / retry guidance:** the `nonce` is the idempotency key. Capture `response.nonce` up front; if the network drops after the request is sent, re-poll `getArNSPurchaseStatus({ nonce })` rather than blindly re-buying. On a `402`, top up and issue a fresh purchase — the captured nonce still lets you reconcile status. ```typescript try { await turbo.buyArNSName({ name, type: 'permabuy', processId }); } catch (err) { if (err instanceof InsufficientCreditsError) { // surface a top-up flow to the user } else { throw err; } } ``` # Extend, increase undernames, upgrade (/(arns-names-paid-with-turbo-credits)/extend-increase-undernames-upgrade) Each intent has a typed wrapper that enforces its required fields: ```typescript // Extend an existing lease by N years await turbo.extendArNSLease({ name: 'my-name', years: 2 }); // Increase the undername limit await turbo.increaseArNSUndernameLimit({ name: 'my-name', increaseQty: 5 }); // Upgrade a lease to a permanent name await turbo.upgradeArNSName({ name: 'my-name' }); ``` All of them return the same `{ nonce, purchaseReceipt, arioWriteResult }` shape as `buyArNSName` and are polled the same way. `purchaseArNSName(params)` is the general form if you prefer to pass `intent` explicitly. # Polling purchase status (/(arns-names-paid-with-turbo-credits)/polling-purchase-status) `getArNSPurchaseStatus({ nonce })` is available on both the authenticated and unauthenticated clients: ```typescript const status = await turbo.getArNSPurchaseStatus({ nonce }); // status.messageId -> present on terminal success (Solana ArNS write tx id) // status.failedDate -> present on terminal failure ``` # Pricing a name (/(arns-names-paid-with-turbo-credits)/pricing-a-name) `getArNSPriceForName(params)` returns the cost in both Turbo Credits (`winc`) and `mARIO`. Params are validated client-side per intent (a `ProvidedInputError` is thrown for missing/invalid fields before any request is sent). ```typescript const { winc, mARIO } = await turbo.getArNSPriceForName({ intent: 'Buy-Name', name: 'my-name', type: 'lease', // 'lease' | 'permabuy' years: 1, // required for leases processId: 'ant-process-id', // the ANT the name resolves to }); ``` # Purchase lifecycle (/(arns-names-paid-with-turbo-credits)/purchase-lifecycle) Every purchase is identified by a client-minted **UUID `nonce`**. The nonce is: 1. **Signed** by your wallet and sent to the bundler (proving intent). 2. The **idempotency key** for the purchase. 3. The **status-lookup key** — poll `getArNSPurchaseStatus({ nonce })` until the purchase reaches a terminal state. `purchaseArNSName` returns the `nonce` on **both** `response.nonce` and `response.purchaseReceipt.nonce`. A purchase is **terminal-success** once its status carries a `messageId` (the Solana transaction id of the on-chain ArNS write) and **terminal-failure** once it carries a `failedDate`. ``` buyArNSName() ──▶ POST /arns/purchase ──▶ { nonce, purchaseReceipt, arioWriteResult } │ poll getArNSPurchaseStatus({ nonce }) │ ┌──────────────────────────────────────┴───────────────────────┐ messageId present (success) failedDate present (failure) ``` # File Upload Events (/(events)/file-upload-events) These events are available for `upload`, `uploadFile`, and `uploadSignedDataItem` methods: - `onProgress` - emitted when the overall progress changes (includes both upload and signing). Each event consists of the total bytes, processed bytes, and the step (upload or signing) - `onError` - emitted when the overall upload or signing fails (includes both upload and signing) - `onSuccess` - emitted when the overall upload or signing succeeds (includes both upload and signing) - this is the last event emitted for the upload or signing process - `onSigningProgress` - emitted when the signing progress changes. - `onSigningError` - emitted when the signing fails. - `onSigningSuccess` - emitted when the signing succeeds - `onUploadProgress` - emitted when the upload progress changes - `onUploadError` - emitted when the upload fails - `onUploadSuccess` - emitted when the upload succeeds ```typescript const uploadResult = await turbo.uploadFile({ fileStreamFactory: () => fs.createReadStream(filePath), fileSizeFactory: () => fileSize, events: { // overall events (includes signing and upload events) onProgress: ({ totalBytes, processedBytes, step }) => { console.log('Overall progress:', { totalBytes, processedBytes, step }); }, onError: ({ error, step }) => { console.log('Overall error:', { error, step }); }, onSuccess: () => { console.log('Overall success!'); }, // signing events onSigningProgress: ({ totalBytes, processedBytes }) => { console.log('Signing progress:', { totalBytes, processedBytes }); }, onSigningError: (error) => { console.log('Signing error:', { error }); }, onSigningSuccess: () => { console.log('Signing success!'); }, // upload events onUploadProgress: ({ totalBytes, processedBytes }) => { console.log('Upload progress:', { totalBytes, processedBytes }); }, onUploadError: (error) => { console.log('Upload error:', { error }); }, onUploadSuccess: () => { console.log('Upload success!'); }, }, }); ``` # Folder Upload Events (/(events)/folder-upload-events) These events are available for the `uploadFolder` method: - `onFileStart` - emitted when a file in the folder starts uploading. Includes the file name, file size, file index, and total number of files - `onFileProgress` - emitted when a file's upload or signing progress changes. Includes the file name, file index, total files, processed bytes for the file, total bytes for the file, and the current step (signing or upload) - `onFileComplete` - emitted when a file successfully completes uploading. Includes the file name, file index, total files, and the data item ID - `onFileError` - emitted when a file upload fails. Includes the file name, file index, total files, and the error - `onFolderProgress` - emitted when the overall folder upload progress changes. Includes the number of processed files, total files, processed bytes across all files, total bytes across all files, and the current phase (files or manifest) - `onFolderError` - emitted when the overall folder upload fails - `onFolderSuccess` - emitted when the folder upload successfully completes (including manifest generation) - this is the last event emitted for the folder upload process ```typescript const uploadResult = await turbo.upload({ data: 'The contents of my file!', signal: AbortSignal.timeout(10_000), // cancel the upload after 10 seconds dataItemOpts: { // optional }, events: { // overall events (includes signing and upload events) onProgress: ({ totalBytes, processedBytes, step }) => { const percentComplete = (processedBytes / totalBytes) * 100; console.log('Overall progress:', { totalBytes, processedBytes, step, percentComplete: percentComplete.toFixed(2) + '%', // eg 50.68% }); }, onError: (error) => { console.log('Overall error:', { error }); }, onSuccess: () => { console.log('Signed and upload data item!'); }, // upload events onUploadProgress: ({ totalBytes, processedBytes }) => { console.log('Upload progress:', { totalBytes, processedBytes }); }, onUploadError: (error) => { console.log('Upload error:', { error }); }, onUploadSuccess: () => { console.log('Upload success!'); }, // signing events onSigningProgress: ({ totalBytes, processedBytes }) => { console.log('Signing progress:', { totalBytes, processedBytes }); }, onSigningError: (error) => { console.log('Signing error:', { error }); }, onSigningSuccess: () => { console.log('Signing success!'); }, }, }); ``` # Arweave (/(signers)/arweave) #### Arweave JWK ```typescript const jwk = await arweave.crypto.generateJWK(); const turbo = TurboFactory.authenticated({ privateKey: jwk }); ``` #### ArweaveSigner ```typescript const signer = new ArweaveSigner(jwk); const turbo = TurboFactory.authenticated({ signer }); ``` #### ArconnectSigner ```typescript const signer = new ArconnectSigner(window.arweaveWallet); const turbo = TurboFactory.authenticated({ signer }); ``` # Base (/(signers)/base) #### Base ETH Private Key ```typescript const turbo = TurboFactory.authenticated({ privateKey: ethHexadecimalPrivateKey, token: 'base-eth', }); ``` #### Base USDC Private Key ```typescript const turbo = TurboFactory.authenticated({ privateKey: ethHexadecimalPrivateKey, token: 'base-usdc', }); ``` #### Base ARIO Private Key ```typescript const turbo = TurboFactory.authenticated({ privateKey: ethHexadecimalPrivateKey, token: 'base-ario', }); ``` # Ethereum (/(signers)/ethereum) #### EthereumSigner ```typescript const signer = new EthereumSigner(privateKey); const turbo = TurboFactory.authenticated({ signer }); ``` #### Ethereum Private Key ```typescript const turbo = TurboFactory.authenticated({ privateKey: ethHexadecimalPrivateKey, token: 'ethereum', }); ``` #### POL (MATIC) Private Key ```typescript const turbo = TurboFactory.authenticated({ privateKey: ethHexadecimalPrivateKey, token: 'pol', }); ``` # Solana (/(signers)/solana) #### HexSolanaSigner ```typescript const signer = new HexSolanaSigner(bs58.encode(secretKey)); const turbo = TurboFactory.authenticated({ signer }); ``` #### Solana Web Wallet Adapter ```typescript const turbo = TurboFactory.authenticated({ walletAdapter: window.solana, token: 'solana', }); ``` #### Solana Secret Key ```typescript const turbo = TurboFactory.authenticated({ privateKey: bs58.encode(secretKey), token: 'solana', }); ``` # Turbo SDK (/index) **For AI and LLM users**: Access the complete Turbo SDK documentation in plain text format at{" "} llm.txt for easy consumption by AI agents and language models. See [AI Agents & LLMs](/build/agents) for the full agent toolkit. The Turbo SDK provides a high-level interface for uploading data to Arweave through Turbo's optimized infrastructure. Built with TypeScript, it offers seamless integration with built-in error handling, automatic retries, and transparent pricing. ## Quick Start ### Install the SDK ```npm npm install @ardrive/turbo-sdk ``` ### Use the SDK ```javascript // Create an authenticated client const turbo = TurboFactory.authenticated({ privateKey: yourPrivateKey }); // Upload data with automatic payment const result = await turbo.uploadFile({ fileStreamFactory: () => fs.createReadStream('./my-file.pdf'), fileSizeFactory: () => fs.statSync('./my-file.pdf').size, }); console.log('Upload successful:', result); ``` ### Install the SDK ```npm npm install @ardrive/turbo-sdk ``` ### Install polyfills (required for web environments) Polyfills are required for React web environments due to the use of `crypto`, `buffer` and `process` types in the SDK's dependencies. ```npm npm install --save-dev vite-plugin-node-polyfills ``` ```js // vite.config.js plugins: [ nodePolyfills({ globals: { Buffer: true, global: true, process: true, }, }), ], }); ``` Configure your bundler (Webpack, Vite, Rollup, etc.) to provide polyfills for `crypto`, `process`, and `buffer`. Refer to your bundler's documentation for polyfill configuration. ### Use the SDK ```javascript // Create an authenticated client const turbo = TurboFactory.authenticated({ privateKey: yourPrivateKey }); // Upload data with automatic payment const fileInput = document.querySelector('input[type="file"]'); const file = fileInput.files[0]; const result = await turbo.uploadFile({ fileStreamFactory: () => file.stream(), fileSizeFactory: () => file.size, }); console.log('Upload successful:', result); ``` ```html Turbo SDK Upload Example // Polyfills are included in the minimized web bundle, so not necessary to import directly // Function to handle file upload async function uploadFile() { const fileInput = document.getElementById('fileInput'); const file = fileInput.files[0]; const privateKeyInput = document.getElementById('privateKey'); if (!file) { alert('Please select a file'); return; } if (!privateKeyInput.value) { alert('Please enter your private key'); return; } try { // Show loading state document.getElementById('status').textContent = 'Uploading...'; // Create authenticated client const turbo = TurboFactory.authenticated({ privateKey: privateKeyInput.value }); // Upload file const result = await turbo.uploadFile({ fileStreamFactory: () => file.stream(), fileSizeFactory: () => file.size, }); // Show success document.getElementById('status').innerHTML = ` Upload successful! Transaction ID: ${result.id} Data Item ID: ${result.dataItemId} `; } catch (error) { document.getElementById('status').innerHTML = ` Error: ${error.message} `; } } // Add click handler window.addEventListener('load', () => { document.getElementById('uploadBtn').addEventListener('click', uploadFile); }); Turbo SDK Upload Example Private Key (JWK): Select File: Upload to Arweave ``` ## API Reference & Documentation } title="API Reference" description="Complete API documentation for all Turbo client methods" href="/apis/turbo" /> } title="SDK Details" description="Advanced features, events, logging, and credit sharing" href="/sdks/turbo-sdk" /> ## Core Features } title="Upload Management" description="Authenticated and unauthenticated upload clients with retry logic" href="/sdks/turbo-sdk/turboauthenticatedclient" /> } title="Events & Monitoring" description="Monitor upload progress and handle events in real-time" href="/sdks/turbo-sdk/file-upload-events/" /> } title="Credit Sharing" description="Manage shared credit pools for streamlined billing" href="/sdks/turbo-sdk/turbo-credit-sharing" /> } title="Logging & Configuration" description="Configure logging for debugging and monitoring uploads" href="/sdks/turbo-sdk/logging" /> # Logging (/logging) The SDK uses winston for logging. You can set the log level using the `setLogLevel` method. ```typescript TurboFactory.setLogLevel('debug'); ``` # Turbo Credit Sharing (/turbo-credit-sharing) Users can share their purchased Credits with other users' wallets by creating Credit Share Approvals. These approvals are created by uploading a signed data item with tags indicating the recipient's wallet address, the amount of Credits to share, and an optional amount of seconds that the approval will expire in. The recipient can then use the shared Credits to pay for their own uploads to Turbo. Shared Credits cannot be re-shared by the recipient to other recipients. Only the original owner of the Credits can share or revoke Credit Share Approvals. Credits that are shared to other wallets may not be used by the original owner of the Credits for sharing or uploading unless the Credit Share Approval is revoked or expired. Approvals can be revoked at any time by similarly uploading a signed data item with tags indicating the recipient's wallet address. This will remove all approvals and prevent the recipient from using the shared Credits. All unused Credits from expired or revoked approvals are returned to the original owner of the Credits. To use the shared Credits, recipient users must provide the wallet address of the user who shared the Credits with them in the `x-paid-by` HTTP header when uploading data. This tells Turbo services to look for and use Credit Share Approvals to pay for the upload before using the signer's balance. For user convenience, during upload the Turbo CLI will use any available Credit Share Approvals found for the connected wallet before using the signing wallet's balance. To instead ignore all Credit shares and only use the signer's balance, use the `--ignore-approvals` flag. To use the signer's balance first before using Credit shares, use the `--use-signer-balance-first` flag. In contrast, the Turbo SDK layer does not provide this functionality and will only use approvals when `paidBy` is provided. The Turbo SDK provides the following methods to manage Credit Share Approvals: - `shareCredits`: Creates a Credit Share Approval for the specified wallet address and amount of Credits. - `revokeCredits`: Revokes all Credit Share Approvals for the specified wallet address. - `listShares`: Lists all Credit Share Approvals for the specified wallet address or connected wallet. - `dataItemOpts: { ...opts, paidBy: string[] }`: Upload methods now accept 'paidBy', an array of wallet addresses that have provided credit share approvals to the user from which to pay, in the order provided and as necessary, for the upload. The Turbo CLI provides the following commands to manage Credit Share Approvals: - `share-credits`: Creates a Credit Share Approval for the specified wallet address and amount of Credits. - `revoke-credits`: Revokes all Credit Share Approvals for the specified wallet address. - `list-shares`: Lists all Credit Share Approvals for the specified wallet address or connected wallet. - `paidBy: --paid-by `: Upload commands now accept '--paid-by', an array of wallet addresses that have provided credit share approvals to the user from which to pay, in the order provided and as necessary, for the upload. - `--ignore-approvals`: Ignore all Credit Share Approvals and only use the signer's balance. - `--use-signer-balance-first`: Use the signer's balance first before using Credit Share Approvals.