Skip to main content

Getting Started

Engine is self-hosted with minimal configuration.

Setup

Requirements

Start the database

A Postgres DB instance is required to run Engine. AWS RDS or Google Cloud SQL are recommended for production use.

Or run Postgres locally to get started quickly:

docker run -p 5432:5432 -e POSTGRES_PASSWORD=postgres -d postgres

This step provides a local Postgres DB URL for the next step:

POSTGRES_CONNECTION_URL="postgresql://postgres:postgres@host.docker.internal:5432/postgres?sslmode=disable"

Start the Engine server

Download and run the Docker container:

docker run \
-e THIRDWEB_API_SECRET_KEY="<thirdweb_secret_key>" \
-e ADMIN_WALLET_ADDRESS="<admin_wallet_address>" \
-e POSTGRES_CONNECTION_URL="<postgres_connection_url>" \
-e ENABLE_HTTPS=true \
-p 3005:3005 \
--pull=always \
--cpus="0.5" \
thirdweb/engine:latest

Provide the following environment variables:

VariableDescription
THIRDWEB_SECRET_KEYA thirdweb secret key created on the API Keys page.
ADMIN_WALLET_ADDRESSThe wallet address that will configure Engine from the thirdweb dashboard.
POSTGRES_CONNECTION_URLPostgres connection string: postgresql://[user[:password]@][host][:port][/dbname][?param1=value1&...]
ENABLE_HTTPSSelf-sign a certificate to serve API requests on HTTPS. Set to true if running Engine locally to manage it from the thirdweb dashboard. (Default: false)

Your server is running when this log line appears:

Server listening on: 0.0.0.0:3005
tip

Here's a few pointers:

  • ENABLE_HTTPS=true is only required when running Engine locally. Remove this line for production use.
  • Make sure your Engine instance port (default: 3005) is publicly accessible. All configuration and API requests require authentication.

Dashboard management

Manage your Engine instance from the thirdweb dashboard:

  1. Navigate to https://localhost:3005/json.

    • The "Your connection is not private" page will appear.
    • Select Show advanced and select Proceed to localhost (unsafe).
    • A JSON file will be rendered. This one-time step is required to allow your browser to connect to your local Engine instance.
  2. Navigate to the Engine dashboard page.

  3. Sign in with the <admin_wallet_address> wallet.

  4. Select Set Engine instance URL to get started..

  5. Add your publicly accessible Engine URL.

    • If Engine is running locally, provide the URL https://localhost:3005.

The dashboard is organized in tabs:

  • Overview: Manage backend wallets and transactions.
  • Explorer: Interactively call API methods.
  • Configuration: Manage backend wallet and webhook settings.
  • Permissions: Manage admins and access tokens.

Using Engine

Create an access token

The Engine API requires a access token to authenticate requests. To create an access token:

  1. Navigate to the Engine dashboard page.
  2. Select the Permissions tab.
  3. Under Access Tokens, select Create Access Token.
  4. Provide an access token in the Authorization header when calling the Engine API:
    • Authorization: Bearer <access_token>

See Authentication for advanced configuration.

Add a backend wallet

Engine uses your backend wallets to interact with the blockchain. To create a backend wallet:

  1. Navigate to the Engine dashboard page.
  2. Navigate to the Overview tab.
  3. Under Backend Wallets, select Create.

A local wallet is generated, and the encrypted private key is stored in your database.

See Backend Wallets for details on other wallet configuration or importing wallets.

tip

Send gas funds to this backend wallet in order to call contract write methods.

Send blockchain transactions

Call the blockchain with a backend wallet with one API call. Here are some examples.

Get a wallet's balance

const resp = await fetch(
"<engine_url>/backend-wallet/<chain>/<backend_wallet_address>/get-balance",
{
headers: {
Authorization: "Bearer <access_token>",
},
},
);

const { result } = await resp.json();
console.log("Balance:", result.value);

Call a contract read method

This code example does not require gas funds and returns the function result.

const resp = await fetch(
"<engine_url>/contract/<chain>/<contract_address>/read?function_name=balanceOf&args=0x3EcDBF3B911d0e9052b64850693888b008e18373",
{
headers: {
Authorization: "Bearer <access_token>",
},
},
);

const { result } = await resp.json();
console.log("ERC-20 balance:", result);

Call a contract write method

This code example calls a write method on a contract. It requires gas funds and returns a queueId to query for the result.

const resp = await fetch(
"<engine_url>/contract/<chain>/<contract_address>/write",
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer <access_token>",
"x-backend-wallet-address": "<backend_wallet_address>",
},
body: JSON.stringify({
function_name: "transferFrom",
args: [
"0x1946267d81Fb8aDeeEa28e6B98bcD446c8248473",
"0x3EcDBF3B911d0e9052b64850693888b008e18373",
"0",
],
}),
},
);

const { result } = await resp.json();
// queueId is a reference to the transaction queued by Engine.
console.log("Queue ID:", result.queueId);

Deploy a contract

This code example deploys a thirdweb NFT drop contract. It requires gas funds and returns a queueId to query for the result.

const resp = await fetch("<engine_url>/deploy/<chain>/prebuilts/nft-drop", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer <access_token>",
"x-backend-wallet-address": "<backend_wallet_address>",
},
body: JSON.stringify({
contractMetadata: {
name: "thirdweb Engine example",
symbol: "eng",
primary_sale_recipient: "0x3EcDBF3B911d0e9052b64850693888b008e18373",
},
}),
});

const { result } = await resp.json();
// queueId is a reference to the transaction queued by Engine.
console.log("Queue ID:", result.queueId);

Engine can enable your application to airdrop NFTs, send funds between wallets, update on-chain game state, and more.

Wait for a transaction to complete

Trigger an action when transactions complete by configuring webhooks to call your backend server.

Alternatively, query or poll the transaction/status/<queue_id> endpoint:

const resp = await fetch("<engine_url>/transaction/status/<queue_id>", {
method: "GET",
headers: {
Authorization: "Bearer <access_token>",
},
});

const { result } = await resp.json();
// status can be: processed, queued, sent, errored, mined, cancelled, retried
const isComplete = ["mined", "errored", "cancelled"].includes(result.status);