Skip to main content

Documentation Index

Fetch the complete documentation index at: https://turnkey-0e7c1f5b-amir-tx-status-webhooks.mintlify.app/llms.txt

Use this file to discover all available pages before exploring further.

Overview

Traditionally, sending blockchain transactions onchain has been painful:
  • You need to fund wallets with native gas tokens, creating onboarding friction
  • Network congestion and fee spikes can cause transactions to stall or get dropped altogether
Turnkey reduces this to a couple of API calls. We handle fees and our battle-tested broadcast logic ensures inclusion even under adverse network conditions. You and your users never touch gas tokens or deal with stuck transactions.

Supported chains

EVM (sponsored):
  • Base - eip155:8453
  • Polygon - eip155:137
  • Ethereum - eip155:1
  • Arbitrum - eip155:42161
EVM testnets (sponsored):
  • Base (Sepolia) - eip155:84532
  • Polygon (Amoy) - eip155:80002
  • Ethereum (Sepolia) - eip155:11155111
  • Arbitrum (Sepolia) - eip155:421614
Solana (sponsored):
  • Solana mainnet - solana:mainnet
  • Solana devnet - solana:devnet
Interested in another chain? Reach out to us!
To access sponsored transactions, ensure that Gas Sponsorship is first enabled within your Turnkey dashboard. Then set sponsor: true and update the caip2 parameter with the corresponding chain identifier.

Concepts

Gas sponsorship (aka gas abstraction, gasless transactions, fee abstraction)

A single endpoint lets you toggle between standard and sponsored transactions. With sponsorship enabled, your users never need to hold native tokens to pay transaction fees — Turnkey covers them. Set sponsor: true to enable sponsorship, or sponsor: false to have fees paid by the sender’s wallet. Either way, Turnkey handles construction, signing, broadcast, and status monitoring. The sponsor flag only controls who pays the fee.
Gas Sponsorship is available to Enterprise clients only. Pay-as-you-go and Pro customers can access transaction construction, signing, and broadcast. If you’d like to leverage gas sponsorship, please reach out!

Spend limits

Turnkey provides tools to manage your gas sponsorship budget. You configure USD fee limits at two levels: across all orgs and per sub-org. This gives you control over both total spend and per-user spend. You can set limit values and time windows through the dashboard. You can query current gas usage and limits through our endpoints.
Turnkey provides fee sponsorship and transaction broadcasting services only. In high-fee or congested network conditions, delays or non-inclusion may occur. It is the developer’s responsibility to ensure appropriate spend limits are in place.

Policy engine

You can write policies against both sponsored and non-sponsored transactions using Turnkey’s policy DSL:
  • EVM: use the eth.tx namespace
  • Solana: use the solana.tx namespace
This means you can seamlessly switch between sponsored and non-sponsored transactions and still use the same policies. Note: Turnkey sets all fee-related fields to 0 for sponsored transactions.

Billing

Turnkey passes transaction fee costs through to you as a line item at the end of the month. You pay based on the USD value of fees at time of broadcast; Turnkey internalizes the inventory risk of token price changes. Our battle-tested fee estimation aims to be cost-efficient while ensuring quick transaction inclusion.

Advanced

Gas sponsorship smart contracts (EVM)

We could not find a satisfactory setup for gas sponsorship contracts that were both fast and safe, so we made our own. The contracts are open source and you can check them out on GitHub. Based on our benchmarks, these are the most efficient gas sponsorship contracts on the market. They achieve this through optimized logic, calldata encoding, and extensive use of assembly, which reduces gas overhead per sponsored transaction. The result: lower costs for you and faster execution for your users.

Security

Some gas sponsorship setups by other providers are subject to replay attacks. If a malicious actor compromises the provider infrastructure, they can replay the gas sponsorship request multiple times with different nonces to create multiple transactions from a single request. At Turnkey, we never cut corners on security: we perform transaction construction in enclaves, and as long as the request includes the relevant nonce or blockhash, only one transaction can be created from it. Since the user’s authenticator signs requests and the enclave verifies signatures, a malicious actor cannot modify or replay the request. This is in line with Turnkey’s core system design principle: everything can be compromised outside of the enclaves and funds will still be safe. By default, our SDKs include a special gas station nonce for sponsored transaction requests.

RPCs

Turnkey’s send transaction and transaction status endpoints eliminate the need for third-party RPC providers. You save costs and reduce latency because we holistically incorporate internal data and minimize external calls.

SDK Overview

The SDK primarily abstracts three endpoints: eth_send_transaction, get_send_transaction_status, and get_gas_usage.
You can sign and broadcast transactions in two primary ways:
  1. Using the React handler (handleSendTransaction) from @turnkey/react-wallet-kit This gives you:
    • modals
    • spinner + chain logo
    • success screen
    • explorer link
    • built-in polling
  2. Using low-level functions in @turnkey/core You manually call:
    • ethSendTransaction OR solSendTransaction → submit
    • pollTransactionStatus → wait for inclusion
  3. Using server-side @turnkey/sdk-server This is the right choice for Node.js backends. It exposes the same methods via the server SDK client.
This page walks you through the @turnkey/core flow with full code examples. For using the React handler, see Sending Sponsored Transactions (React).

Using @turnkey/core directly

For custom frameworks or full manual control (client-side). You will call:

ethSendTransaction(params)

→ returns { sendTransactionStatusId }

solSendTransaction(params)

→ returns { sendTransactionStatusId }

pollTransactionStatus(params)

→ returns chain-specific status (eth.txHash or sol.signature)

Step 1 — Create a client

If you’re on a Node.js backend, use @turnkey/sdk-server and initialize the server client like this:
import { Turnkey } from "@turnkey/sdk-server";

const client = new Turnkey({
  apiBaseUrl: "https://api.turnkey.com/",
  apiPrivateKey: process.env.TURNKEY_API_PRIVATE_KEY,
  apiPublicKey: process.env.TURNKEY_API_PUBLIC_KEY,
  defaultOrganizationId: process.env.TURNKEY_ORGANIZATION_ID,
}).apiClient();
For @turnkey/core, create the client like this:
import { Turnkey } from "@turnkey/core";

const client = new Turnkey({
  apiBaseUrl: "https://api.turnkey.com",
  defaultOrganizationId: process.env.TURNKEY_ORG_ID,
});

Step 2 — Submit the transaction (Ethereum)

const sendTransactionStatusId = await client.ethSendTransaction({
  transaction: {
    from: walletAccount.address,
    to: "0xRecipient",
    caip2: "eip155:8453",
    sponsor: true,
    value: "0",
    data: "0x",
    nonce: "0",
  },
});
OR (Solana):
const sendTransactionStatusId = await client.solSendTransaction({
  transaction: {
    signWith: walletAccount.address, // Solana address
    unsignedTransaction: "<base64-serialized-unsigned-solana-tx>",
    caip2: "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1", // devnet
    sponsor: true,
    // recentBlockhash: "<recent blockhash>", // optional
  },
});

Step 3 — Wait for inclusion

const pollResult = await client?.pollTransactionStatus({
  sendTransactionStatusId,
});

if (!pollResult) {
  throw new TurnkeyError(
    "Polling returned no result",
    TurnkeyErrorCodes.SIGN_AND_SEND_TRANSACTION_ERROR,
  );
}

const txHash = pollResult.eth?.txHash; // Ethereum
const signature = pollResult.sol?.signature; // Solana
const transactionId = txHash ?? signature;

if (!transactionId) {
  throw new TurnkeyError(
    "Missing transaction id in transaction result",
    TurnkeyErrorCodes.SIGN_AND_SEND_TRANSACTION_ERROR,
  );
}
console.log(transactionId);

  • ethSendTransaction Implementation here
  • solSendTransaction Implementation here
  • pollTransactionStatus Implementation here

Checking Gas Usage

You can configure gas limits for both sub-orgs and all orgs. We recommend checking sub-org gas usage against the limit on the client side so your application can handle edge cases when approaching or exceeding the gas limit. You may also want to monitor your all org gas usage regularly to see if you are approaching your gas limit.
const resp = await httpClient?.getGasUsage({})
if (resp?.usageUsd! > resp?.windowLimitUsd!) { // you can also configure this to be a threshold
  console.error("Gas usage limit exceeded for sponsored transactions");
  return
}