SIGIL · INTEGRATION DOCS← Back to site
[ SDK ]

@sigil-oss/connect

Official TypeScript SDK for the Sigil deep-link protocol. Build envelopes, launch requests, and handle results — either as a simple await or via manual URI construction. See the Payload format page.

[ SOURCE ]

github.com/sigil-oss/sigil.connect — TypeScript types, changelog, and full API reference.

Install

TERMINAL
npm install @sigil-oss/connect

Connect request

buildSigilUrl wraps the request in an envelope, base64url-encodes it, and returns a sigil://v1/request?d=… URI ready to open.

TYPESCRIPT
import { buildSigilUrl, createEnvelope, createConnectRequest } from '@sigil-oss/connect';

const url = buildSigilUrl(
  createEnvelope(
    createConnectRequest({
      type: 'connect',
      dapp: { name: 'My App', origin: 'https://myapp.example' },
      permissions: ['transfer', 'sign_message'],
    }),
    { callback: 'https://myapp.example/api/sigil/callback' },
  )
);

window.location.href = url;

Transfer request

TYPESCRIPT
import { buildSigilUrl, createEnvelope, createTransferRequest } from '@sigil-oss/connect';

const url = buildSigilUrl(
  createEnvelope(
    createTransferRequest({
      type: 'transfer',
      dapp: { name: 'My App', origin: 'https://myapp.example' },
      to: 'NQZBXKZP4MTLDUVWXYZK8MFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
      amount: 1_500_000,
    }),
    { callback: 'https://myapp.example/api/sigil/callback' },
  )
);

window.location.href = url;

Redirect URI — no server needed

Pass redirect_uri instead of (or alongside) callback. After the user acts, Sigil opens redirect_uri?result=<base64url JSON> in the default browser. Read the result client-side — no endpoint, no infra.

TYPESCRIPT
import { sigilRequest, createSignMessageRequest } from '@sigil-oss/connect';

// One await — no server, no redirect boilerplate.
// Sigil opens /__sigil__ after the user acts; handleRedirect() there
// broadcasts the result back and this Promise resolves.
const result = await sigilRequest(
  createSignMessageRequest({
    type: 'sign_message',
    dapp: { name: 'My App', origin: 'https://myapp.example' },
    message: 'Sign in to My App · ' + new Date().toISOString(),
  }),
);

if (result.status === 'signed') {
  console.log('identity:', result.identity);
}
[ RESULT IS IN THE URL ]

With redirect_uri, the signed result lands in the browser URL bar and history. Use callback for anything involving real money or sensitive data that must stay server-side.

Parsing the server callback

Sigil POSTs a JSON body to your callback URL from the Rust layer. parseCallback validates the shape and narrows the type. Always verify the nonce before acting on the result.

TYPESCRIPT
import { parseCallback, type SigilResult } from '@sigil-oss/connect';

// POST /api/sigil/callback
app.post('/api/sigil/callback', express.json(), async (req, res) => {
  const result: SigilResult = parseCallback(req.body);

  // Always verify the nonce against what you sent
  if (result.nonce !== pendingNonces.get(result.nonce)) {
    return res.status(400).json({ error: 'nonce_mismatch' });
  }

  if (result.status === 'connected') {
    console.log('Paired identity:', result.identity);
    console.log('Granted permissions:', result.permissions);
  } else if (result.status === 'signed') {
    console.log('Transaction hash:', result.tx_hash);
  } else if (result.status === 'rejected') {
    console.log('Rejected, reason:', result.reason);
  }

  res.sendStatus(200);
});

Async API — await the result

sigilRequest() launches Sigil via a link click (the page stays alive), then returns a Promise that resolves when the user acts. Internally it uses BroadcastChannel — no server, no polling.

TYPESCRIPT
import { sigilRequest, createSignMessageRequest } from '@sigil-oss/connect';

// One await — no server, no redirect boilerplate
const result = await sigilRequest(
  createSignMessageRequest({
    type: 'sign_message',
    dapp: { name: 'My App', origin: 'https://myapp.example' },
    message: 'Sign in to My App · ' + new Date().toISOString(),
  }),
  // Sigil opens /__sigil__ after the user acts; handleRedirect() broadcasts
  // the result back over a BroadcastChannel and this Promise resolves.
  { callbackPath: '/__sigil__' },
);

if (result.status === 'signed') {
  console.log('identity:', result.identity);
  console.log('signature:', result.signature);
}

handleRedirect — callback page

Mount this at the callbackPath route in your app (default /__sigil__). It reads the ?result= param, broadcasts it to the waiting sigilRequest() Promise, and closes the tab.

TYPESCRIPT
// Mount this at the callbackPath route in your app — e.g. pages/__sigil__.tsx
import { handleRedirect } from '@sigil-oss/connect';

// Call it on page load. It reads ?result=…, posts to the BroadcastChannel
// that sigilRequest() is listening on, then closes the tab.
handleRedirect();