Skip to main content
Data Wallet is the end-user dashboard at https://wallet.emergedata.ai where your users can see which companies have access to their data and revoke consent at any time. This keeps consent transparent and user-controlled.

When it appears

  • It is created after a user completes their first successful Link consent flow.
  • Your user receives an email notifying them they can track their data access and revoke consent for a specific company.

What users can do

  • Review all connected companies and data types
  • See which connections are active vs revoked
  • Revoke consent with a single toggle

Screenshots

Data Wallet landing view Data Wallet connections overview

Handle revocations in your app

When a user revokes consent in Data Wallet, you receive a consent.revoked webhook. Use it to stop processing and delete any stored data for that user.
import express from 'express';
import crypto from 'crypto';

const app = express();
app.use(express.raw({ type: 'application/json' }));

const WEBHOOK_SECRET = process.env.EMERGE_WEBHOOK_SECRET;
if (!WEBHOOK_SECRET) {
  throw new Error('Missing EMERGE_WEBHOOK_SECRET');
}

app.post('/webhooks/emerge', async (req, res) => {
  try {
    const signature = req.headers['x-signature'] as string | undefined;
    if (!signature) {
      return res.status(401).send('Missing signature');
    }

    const expected = crypto
      .createHmac('sha256', WEBHOOK_SECRET)
      .update(req.body)
      .digest('hex');

    const signatureBuf = Buffer.from(signature, 'hex');
    const expectedBuf = Buffer.from(expected, 'hex');
    const valid =
      signatureBuf.length === expectedBuf.length &&
      crypto.timingSafeEqual(signatureBuf, expectedBuf);

    if (!valid) {
      console.error('Invalid webhook signature');
      return res.status(401).send('Invalid signature');
    }

    const payload = JSON.parse(req.body.toString('utf8')) as {
      event?: string;
      uid?: string;
      sources?: Array<{ provider?: string }>;
    };

    if (payload.event === 'consent.revoked' && payload.uid) {
      await revokeLocalAccess(payload.uid, payload.sources ?? []);
    }

    return res.status(200).send('OK');
  } catch (err) {
    console.error('Webhook error', err);
    return res.status(500).send('Server error');
  }
});

async function revokeLocalAccess(uid: string, sources: Array<{ provider?: string }>) {
  for (const source of sources) {
    if (source.provider) {
      await deleteUserData(uid, source.provider);
    }
  }
}

async function deleteUserData(uid: string, provider: string) {
  console.log(`Deleting ${provider} data for ${uid}`);
}

app.listen(3000, () => {
  console.log('Webhook listener running on http://localhost:3000');
});

Callbacks

Understand redirect behavior after consent

Webhooks

Receive consent changes like revocations

Edge cases

  • If a user opens a completed Link flow again, they are redirected to the Data Wallet instead of your redirect_uri.
  • If you keep cached data, delete it when you receive consent.revoked to stay privacy-first.