Skip to main content

SDK Quickstart

By the end of this page you will have generated recipient keys, sent a stealth payment, and recovered it, all in TypeScript with no server involved.

Prerequisites

  • Node.js 20 or newer, or a browser with WebAssembly support.
  • A package manager: npm, pnpm, or yarn.

Install

npm install @specterpq/sdk
# or: pnpm add @specterpq/sdk
# or: yarn add @specterpq/sdk

The package ships both browser and Node WebAssembly builds and picks the right one at runtime.

The full flow

01

Initialize the WASM module

Call this once before any crypto. It loads and caches the WebAssembly module, and it is safe to call again.

import { initSpecterSdk } from '@specterpq/sdk';

await initSpecterSdk();

Verify: the call resolves without throwing. If you host the WASM yourself in a browser, pass { wasmUrl: '...' }.

02

Generate recipient keys

A recipient identity is a spending keypair and a viewing keypair.

import { generateSpecterKeys } from '@specterpq/sdk';

const recipient = generateSpecterKeys();
// recipient.spending.publicKey controls funds
// recipient.viewing.publicKey detects payments

Verify: recipient.spending.publicKey and recipient.viewing.publicKey are present. The matching secret keys exist too, but they are redacted from logs and JSON.

03

Publish a meta-address

Bundle the two public keys into one address. This is the only thing the recipient shares.

import { metaAddressFromPublicKeys } from '@specterpq/sdk';

const meta = metaAddressFromPublicKeys(
recipient.spending.publicKey,
recipient.viewing.publicKey,
{ description: 'Alice main receive profile' },
);

// Publish meta.hex to your registry or profile page.

Verify: meta.bytes.length is 2369 and meta.address.version is 1.

04

Create a stealth payment (sender)

The sender needs only the recipient's meta.hex. One call parses it, encapsulates to the viewing key, derives the stealth addresses, and computes the view tag.

import { createStealthPayment } from '@specterpq/sdk';

const payment = createStealthPayment(meta.hex);
// payment.ethAddress where to send on Ethereum
// payment.suiAddress where to send on Sui
// payment.ephemeralCiphertext announce publicly
// payment.viewTag one byte, announce publicly

Verify: payment.ethAddress starts with 0x and is 42 characters long. Send funds to it, then publish ephemeralCiphertext and viewTag to your transport.

05

Scan and recover (recipient)

The recipient checks an announcement against their viewing key. The view tag rejects almost all non-matches before any heavy work.

import { scanAnnouncement } from '@specterpq/sdk';

const scan = scanAnnouncement(
{
ephemeralCiphertext: payment.ephemeralCiphertext,
viewTag: payment.viewTag,
},
recipient.viewing,
recipient.spending.publicKey,
);

if (scan.isMatch) {
const ethAddress = scan.stealthKeys.ethAddress;
const privateKey = scan.stealthKeys.ethPrivateKey; // secret
}

Verify: scan.isMatch is true, and scan.stealthKeys.ethAddress equals payment.ethAddress from the previous step.

What you just did

You ran the complete protocol without a server:

  1. Created two ML-KEM-768 keypairs and a meta-address.
  2. Encapsulated to the viewing key to derive a one-time stealth address.
  3. Filtered by view tag, decapsulated the match, and recovered the spendable key.

The stealth address has no public link to the meta-address. That gap is the privacy.

Warning

The recovered ethPrivateKey can spend real funds. Treat it like any wallet secret: keep it out of logs, analytics, and error reports. The SDK already redacts it from JSON.stringify and console output, but your own code must not copy it into places that get shipped off the device.

Troubleshooting

SymptomCauseFix
NOT_INITIALIZED errorCrypto called before initSpecterSdk()Await initSpecterSdk() once at startup
scan.isMatch is falseView tag or address did not matchCheck scan.reason: view_tag_mismatch or address_mismatch
WASM_LOAD_FAILED in a browserWASM file not served from the expected pathPass { wasmUrl } to initSpecterSdk()
INVALID_META_ADDRESSWrong or truncated meta.hexRe-fetch the full hex string from your registry

Next steps