New:Introducing Socket Scanning for VS Code Marketplace Extensions.Learn more →
Get Started

tapimo

Package Overview
Dependencies
Maintainers
7
Versions
544
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

tapimo

latest
npmnpm
Version
0.18.2
Version published
Weekly downloads
10K
-32.71%
Maintainers
7
Weekly downloads
 
Created
Source

Tempo API

Overview

The Tempo API is Tempo's next-generation data API. It is designed for clients that need a high-level surface for exploring the Tempo chain.

Usage

The Tempo API is a composable Hono app. You can run your own instance (Self-hosted) or call the hosted API directly (Hosted).

Hosted

Call the hosted Tempo API directly at https://api.tempo.xyz:

curl https://api.tempo.xyz/v1/tokens
const response = await fetch('https://api.tempo.xyz/v1/tokens/0x…')
const token = await response.json()

Explore the API interactively at the root /.

Self-hosted

Install the package:

pnpm i tapimo

Stateful features (webhook subscriptions, verified tokens) live in Postgres. Provision a database and apply migrations with the CLI:

DATABASE_URL=postgresql://… tapimo admin migrate

The API is composed from route groups (apps) imported from tapimo/apps. Create a base app with App.create and mount the apps you want:

import { data, routes } from 'tapimo/apps'
import { App, Db } from 'tapimo/server'

const app = App.create({
  db: Db.postgres({ connectionString: process.env.DATABASE_URL }),
})
  .route('/', data())
  .route('/', routes())
AppPurposeResources (non-exhaustive)
data()Chain and indexed data readsBlocks, transactions, tokens, webhooks
routes()Asset routingQuotes, chains, providers, deposits, transfers
relay()Wallet relay & fee sponsorshipRelay, sponsor
management()Tenancy and access managementOrgs, projects, members, API keys

The composed app exposes a Web fetch method, and App.listener adapts it to a Node.js request listener, so you can serve it from any runtime or framework:

createServer(App.listener(app))              // Node.js
Bun.serve({ fetch: app.fetch })              // Bun
Deno.serve({ fetch: app.fetch })             // Deno
server.all('*', (c) => app.fetch(c.request)) // Elysia
server.use(App.listener(app))                // Express
server.use((c) => app.fetch(c.req.raw))      // Hono
export const GET = app.fetch                 // Next.js
export const POST = app.fetch                // Next.js

Typed Client

Use the typed client for endpoint autocomplete and API-key/header configuration:

import { Client } from 'tapimo'

const client = Client.create({
  // API key sugar for the canonical `tempo-api-key` header.
  apiKey: 'tempo_api_key',
  // Override the API URL. Defaults to `Client.defaultUrl`.
  url: 'https://api.tempo.xyz',
  // Or pass custom headers directly.
  headers: { authorization: 'Bearer tempo_api_key' },
})

Requests are end-to-end type-safe across params, statuses, response bodies, and errors. For example, GET /v1/tokens/:token:

const response = await client.v1.tokens[':token'].$get({
  param: { token: '0x20c0000000000000000000000000000000000000' },
})

// Narrow responses by status
if (response.status !== 200) {
  response.status
  //       ^? (property) status: 400 | 401 | 402 | 403 | 404 | 429 | 502

  if (response.status === 404) {
    const json = await response.json()
    //    ^? const json: { error: { code: "token_not_found"; message: string; ... }; requestId: string }
    json.error.code
    //         ^? (property) code: "token_not_found"
  } else if (response.status === 402) {
    const challenge = await response.text()
    //    ^? const challenge: string
    // Handle the payment challenge from `WWW-Authenticate`.
  } else {
    const json = await response.json()
    //    ^? const json: { error: { code: "query_invalid" | "api_key_invalid" | "upstream_error" | ...; ... }; requestId: string }
    // Handle validation, auth, rate-limit, or upstream errors.
  }

  throw new Error('Request failed')
}

response.status
//       ^? (property) status: 200
const token = await response.json()
//    ^? const token: { address: Hex.Hex; currency: string; decimals: number; name: string; ... }
token.symbol
//    ^? (property) symbol: string

Development

Local API with local Horcrux

Clone the Horcrux backend as a sibling checkout (the checkout must include the horcrux dev local-signing backend), then prepare this checkout:

git clone https://github.com/tempoxyz/horcrux.git ../horcrux
pnpm setup:env

Set these values in the ignored repo-root .env:

HORCRUX_URL=http://127.0.0.1:3040
HORCRUX_ACCESS_CLIENT_ID=
HORCRUX_ACCESS_CLIENT_SECRET=
HORCRUX_ROUTES_TRANSFER_SUBSIDY_WALLETS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
ROUTES_SUBSIDY_MAX_AMOUNT=0.10

Start the opt-in stack with docker compose --profile subsidies up -d --build. Set TEMPO_API_HORCRUX_SOURCE if the sibling checkout is elsewhere. Compose mounts the public seeds/horcrux-wallets.json fixture read-only and starts horcrux dev with HORCRUX_DEV_WALLETS=/etc/horcrux/wallets.json. To customize the preinitialized wallets, copy the manifest, preserve its schema, use stable UUIDs, and set TEMPO_API_HORCRUX_WALLETS to that host path. Horcrux atomically initializes the fixed development team/grant and refuses metadata, key, ID, address, or grant conflicts. It preserves wallets, signed history, and replay state on restart; do not delete its database volume as a routine reinitialization step.

The fixture contains EVM, Tron, and Solana wallets and uses only publicly compromised developer keys. Never use these keys for real credentials or send real funds to any derived address. The EVM wallet is public Anvil account 0. API route subsidies still use only that EVM address through the normal Horcrux viem SDK; local Horcrux does not add API-managed Tron or Solana subsidies. Direct Horcrux HTTP clients identify all wallet families by the manifest UUID, while the viem integration can look up its EVM wallet by address. The dev backend exposes the normal EVM/Tempo, Solana, Tron, and hash signing APIs without policies or broadcasting.

Horcrux shares the API container's network namespace and listens only on loopback; unauthenticated SDK access is accepted only by an API with ENVIRONMENT=development, a loopback HTTP(S) URL, and both Cloudflare credentials absent. After changing .env or the manifest, run docker compose restart api horcrux. This profile starts a signer only: it does not start bridges, Tron, Solana, Base/Ethereum forks, or other chains. Point the relevant RPC URLs at disposable nodes yourself. Signing locally does not change where the API broadcasts. Fund the EVM address with disposable destination tokens and fee funds on the tested chain only when exercising settlement. ROUTES_SUBSIDY_MAX_AMOUNT is a per-subsidy cap, not a total spending budget.

Developers not exercising Routes subsidies can leave HORCRUX_ROUTES_TRANSFER_SUBSIDY_WALLETS empty and run the API without Horcrux access. Fee sponsorship keeps its existing setup; there is no private-key fallback for Routes subsidies.

Hosted Routes subsidy wallets

Set HORCRUX_ROUTES_TRANSFER_SUBSIDY_WALLETS to a comma-separated list of EVM wallet addresses registered in Horcrux. The first address funds new commitments; retain retiring addresses until their existing commitments settle. Address-based horcruxAccount construction is synchronous and requires no discovery request. These wallets need the destination token and transaction fee funds on each supported destination chain. Fee sponsorship, MPP settlement, and rewards signing remain separate.

The first EVM wallet also signs inbound Tempo deposit subsidies through Horcrux sign-hash. Fund it with pathUSD and its selected fee token on Tempo. Transfer reservations do not count deposit top-ups, so monitor the shared Tempo balance for both obligations. Relay fixed-rate deposit addresses use Relay's 1:1 subsidy.

The SDK is pinned to @zygis-tempo/horcrux-viem@0.1.0 on npm, aliased as @horcrux/viem. The SDK owns the Horcrux signing protocol; the API still owns inventory checks, nonce reservations, transaction persistence, and broadcast.

For production, apps/api/wrangler.background.jsonc owns HORCRUX_URL and HORCRUX_ROUTES_TRANSFER_SUBSIDY_WALLETS. CI and pnpm -C apps/api deploy run apps/api/scripts/prepare:production.ts to copy only those values into the HTTP Worker's generated wrangler.deploy.jsonc. The shared wrangler.jsonc has no production Horcrux defaults, so local development and previews cannot inherit them. The HTTP deployment keeps queue producers but leaves consumers, cron, and the reward signer binding on api-background.

Keep HORCRUX_ACCESS_CLIENT_ID and HORCRUX_ACCESS_CLIENT_SECRET as Cloudflare secrets on both production Workers before rollout. They are not stored in Wrangler configuration or read from GitHub Actions. If the URL or wallet list was previously stored as a Cloudflare secret, remove that secret binding before deploying the same name as a plain-text Wrangler variable.

For PR and main-tracking previews, configure these once in the API repository's GitHub Actions settings. CI uploads them to each preview Worker under the same names without the _DEV suffix; no manual Worker configuration is needed.

GitHub Actions settingNameValue
Secret or variableHORCRUX_URL_DEVCloudflare-accessible runtime API base URL of the Horcrux instance containing the dev wallet (not its admin URL)
Secret or variableHORCRUX_ROUTES_TRANSFER_SUBSIDY_WALLETS_DEVComma-separated EVM wallet addresses, active first, not UUIDs
Secret or variableHORCRUX_ROUTES_TRANSFER_SUBSIDY_TRON_WALLETS_DEVComma-separated Tron address and Horcrux UUID pairs, active first
SecretHORCRUX_ACCESS_CLIENT_ID_DEVDev Cloudflare Access client ID
SecretHORCRUX_ACCESS_CLIENT_SECRET_DEVDev Cloudflare Access client secret

Secrets take precedence when both a secret and variable have the same name. The credential pair must be complete or absent. Setting a wallet list requires the URL and both credentials. Removing both wallet settings disables transfer subsidies on the next preview deployment; drain pending commitments first. Absent credentials leave existing Worker credentials unchanged. Use only dev wallets and an identity authorized for those wallets. CI's disposable local signer needs none of these settings.

Grant the workload access to each subsidy wallet and permit its destination-chain ERC-20 transfers. ROUTES_SUBSIDY_MAX_AMOUNT remains the operational USD limit. An empty wallet list disables transfer subsidies; enabled subsidies with incomplete Horcrux credentials fail closed. There is no private-key fallback.

For Tempo USDT0 to Tron USDT, set HORCRUX_ROUTES_TRANSFER_SUBSIDY_TRON_WALLETS to comma-separated TronAddress:HorcruxWalletUUID pairs, active first. Fund those wallets with Tron USDT and TRX. Set ROUTES_TRON_SUBSIDY_FEE_LIMIT_SUN to the maximum native fee reserved for each TRC-20 transfer. The API persists Tron transaction bytes before Horcrux signing and reuses the same signature and transaction on retries. Keep retiring wallets configured until their pending transfers settle. The production wallet mapping belongs in apps/api/wrangler.background.jsonc; the deploy script copies it into the HTTP Worker when present. An empty mapping leaves Tron transfer subsidies disabled.

This replaces ROUTES_TRANSFER_SUBSIDY_ACCOUNT and ROUTES_TRANSFER_SUBSIDY_PRIVATE_KEY. For migration, register the existing wallets in Horcrux or drain their unsigned commitments before switching addresses. After cutover, remove the unused private-key secrets from Cloudflare and GitHub; deployment workflows no longer upload them. Persisted signed transactions still replay through the existing nonce and settlement records. Token top-up billing is unchanged; destination gas remains paid by Tempo.

Run the configuration tests with disposable local infrastructure:

pnpm test:local apps/api/src/RoutesTransferSubsidies.test.ts --run

Quickprompt

Paste into your agent:

Read https://github.com/tempoxyz/api/blob/main/README.md, then spin up the
Tempo API container and provide me links to the services.

Prerequisites

  • OrbStack: runs the Docker Compose stack and serves the *.tempo.local domains
  • pnpm

Commands

pnpm install        # Install dependencies
pnpm dev            # Start the local stack (Docker Compose)
pnpm test           # Run tests against live resources
pnpm test:local     # Run tests against local resources

Services

pnpm dev spins up:

Reward campaign administration is available locally, but scheduled settlement is production-only; the development stack intentionally has no production signer or reviewed factory binding.

ServicePurposeURL
apiTempo APIhttp://api.tempo.local
consoleDeveloper Consolehttp://console.tempo.local
postgresDatabaselocalhost:5432
tempoTempo Nodehttp://rpc.tempo.local:8545
tidxIndexerhttp://tidx.tempo.local:8080
stripeStripe CLI (Webhook Forwarder)-

FAQs

Package last updated on 02 Oct 2026

Related posts