PRIV ProtocolPRIV Docs
Compliance SDK

DSAR Handler

API reference for the DSARHandler class - automate GDPR data subject access requests.

DSAR Handler

The DSARHandler automates GDPR Data Subject Access Requests (DSAR), including data access, portability, and erasure.


What is DSAR?

Under GDPR, users have the right to:

RightDSAR TypeDescription
AccessaccessRequest a copy of their data
PortabilityportabilityExport data in machine-readable format
Erasureerasure"Right to be forgotten"
RectificationrectificationCorrect inaccurate data
RestrictionrestrictionLimit data processing
ObjectionobjectionObject to specific processing

Initialization

import { DSARHandler, createDSARHandler } from '@priv/compliance-sdk';

const dsar = new DSARHandler({
  siteId: 'your-site-id',
  api: {
    endpoint: 'https://api.priv.io/v1',
    apiKey: 'your-api-key',
  },
  requestExpiryDays: 30,
});

Configuration

DSARHandlerConfig

interface DSARHandlerConfig {
  /** Unique site identifier (required) */
  siteId: string;

  /** API configuration */
  api?: {
    endpoint: string;
    apiKey: string;
    timeout?: number; // Default: 10000ms
  };

  /** Days until request expires */
  requestExpiryDays?: number; // Default: 30

  /** Callback when request created */
  onRequestCreated?: (request: DSARRequest) => void;

  /** Callback when request completed */
  onRequestCompleted?: (request: DSARRequest) => void;

  /** Custom data collectors for exports */
  dataCollectors?: DataCollector[];
}

Methods

createRequest(type, email)

Create a new DSAR request.

const request = await dsar.createRequest('access', 'user@example.com');

console.log('Request ID:', request.id);
console.log('Verification Token:', request.verificationToken);

// Send verification email to user
sendVerificationEmail(request.email, request.verificationToken);

Parameters:

NameTypeDescription
typeDSARRequestTypeType of request
emailstringUser's email address

Returns: Promise<DSARRequest>

Request Types:

type DSARRequestType =
  | 'access'       // Copy of data
  | 'portability'  // Export data
  | 'erasure'      // Delete data
  | 'rectification'// Correct data
  | 'restriction'  // Limit processing
  | 'objection';   // Object to processing

getRequest(requestId)

Retrieve a DSAR request by ID.

const request = await dsar.getRequest('dsar-abc123');

if (request) {
  console.log('Status:', request.status);
  console.log('Type:', request.type);
  console.log('Created:', new Date(request.requestedAt));
}

Parameters:

NameTypeDescription
requestIdstringRequest identifier

Returns: Promise<DSARRequest | null>


verifyRequest(requestId, token)

Verify a request using the verification token (sent via email).

// User clicks verification link: /verify-dsar?id=xxx&token=yyy
const isValid = await dsar.verifyRequest(requestId, token);

if (isValid) {
  // Proceed to process the request
  await dsar.processRequest(requestId);
} else {
  console.error('Invalid or expired verification');
}

Parameters:

NameTypeDescription
requestIdstringRequest identifier
tokenstringVerification token

Returns: Promise<boolean>


processRequest(requestId)

Process a verified DSAR request.

try {
  const completed = await dsar.processRequest(requestId);

  if (completed.type === 'access' || completed.type === 'portability') {
    // Data is available
    const data = completed.data;
    sendDataToUser(completed.email, data);
  } else if (completed.type === 'erasure') {
    // Data has been deleted
    notifyUserOfDeletion(completed.email);
  }
} catch (error) {
  console.error('Failed to process request:', error);
}

Parameters:

NameTypeDescription
requestIdstringRequest identifier

Returns: Promise<DSARRequest>

Behavior by Type:

TypeAction
accessCollects and returns user data
portabilityCollects and returns data in portable format
erasureDeletes all user data
OthersMarks as completed (manual processing)

exportData(userIdHash)

Export all data for a specific user.

const data = await dsar.exportData('abc123...');

console.log('Export Date:', new Date(data.exportedAt));
console.log('Consent:', data.consent.current);
console.log('History:', data.consent.history.length, 'events');
console.log('Custom Data:', data.customData);

Parameters:

NameTypeDescription
userIdHashstringUser identifier hash

Returns: Promise<DSARExportData>

Export Format:

interface DSARExportData {
  exportedAt: number;
  dataSubject: {
    idHash: string;
    email?: string;
  };
  consent: {
    current: ConsentPreferences | null;
    history: AuditLogEntry[];
  };
  customData?: Record<string, unknown>;
}

deleteData(userIdHash)

Delete all data for a user (erasure request).

await dsar.deleteData('abc123...');

// All user data is now deleted:
// - Consent preferences
// - Audit log entries
// - DSAR requests
// - Custom data (via data collectors)

Parameters:

NameTypeDescription
userIdHashstringUser identifier hash

Returns: Promise<void>


downloadExport(data, filename?)

Trigger a download of exported data.

const data = await dsar.exportData(userHash);

// Download as JSON file
dsar.downloadExport(data, 'my-data-export.json');

Parameters:

NameTypeDescription
dataDSARExportDataExported data
filenamestringOptional filename

Returns: void


registerDataCollector(collector)

Register a custom data collector for exports.

dsar.registerDataCollector({
  name: 'orders',
  collect: async (userIdHash) => {
    // Fetch user's orders from your database
    const orders = await db.orders.findByUser(userIdHash);
    return { orders };
  },
});

dsar.registerDataCollector({
  name: 'preferences',
  collect: async (userIdHash) => {
    return {
      theme: localStorage.getItem('theme'),
      language: localStorage.getItem('language'),
    };
  },
});

Parameters:

NameTypeDescription
collectorDataCollectorCustom collector

DataCollector Interface:

interface DataCollector {
  name: string;
  collect: (userIdHash: string) => Promise<Record<string, unknown>>;
}

getPendingRequests()

Get all pending DSAR requests.

const pending = dsar.getPendingRequests();

pending.forEach((req) => {
  console.log(`${req.id}: ${req.type} - ${req.status}`);
});

Returns: ReadonlyArray<DSARRequest>


DSAR Request Object

interface DSARRequest {
  /** Unique request ID */
  id: string;

  /** Request type */
  type: DSARRequestType;

  /** Current status */
  status: DSARStatus;

  /** User identifier hash */
  userIdHash: string;

  /** User's email */
  email: string;

  /** Request creation timestamp */
  requestedAt: number;

  /** Completion timestamp */
  completedAt?: number;

  /** Request expiration timestamp */
  expiresAt: number;

  /** Verification token */
  verificationToken?: string;

  /** Exported data (for access/portability) */
  data?: DSARExportData;
}

type DSARStatus =
  | 'pending'     // Awaiting verification
  | 'processing'  // Being processed
  | 'completed'   // Fulfilled
  | 'rejected'    // Rejected
  | 'expired';    // Expired

Full Example: DSAR Flow

1. User Submits Request

// Frontend form submission
async function handleDSARSubmit(type: DSARRequestType, email: string) {
  const request = await dsar.createRequest(type, email);

  // Send verification email
  await sendEmail({
    to: email,
    subject: 'Verify Your Data Request',
    body: `Click to verify: ${baseUrl}/verify?id=${request.id}&token=${request.verificationToken}`,
  });

  return { success: true, requestId: request.id };
}

2. User Verifies Email

// Verification page handler
async function handleVerification(requestId: string, token: string) {
  const isValid = await dsar.verifyRequest(requestId, token);

  if (!isValid) {
    throw new Error('Invalid or expired verification link');
  }

  // Auto-process or queue for manual review
  const request = await dsar.getRequest(requestId);

  if (request?.type === 'access' || request?.type === 'portability') {
    // Auto-process data export
    const completed = await dsar.processRequest(requestId);
    return { status: 'completed', data: completed.data };
  } else {
    // Queue for manual review
    return { status: 'pending_review' };
  }
}

3. Deliver Data

// For access/portability requests
async function deliverData(requestId: string) {
  const request = await dsar.getRequest(requestId);

  if (request?.status === 'completed' && request.data) {
    // Option 1: Direct download
    dsar.downloadExport(request.data);

    // Option 2: Email the data
    await sendEmail({
      to: request.email,
      subject: 'Your Data Export',
      attachments: [{
        filename: 'data-export.json',
        content: JSON.stringify(request.data, null, 2),
      }],
    });
  }
}

4. Handle Erasure

// For erasure requests
async function handleErasure(requestId: string) {
  const request = await dsar.getRequest(requestId);

  if (request?.type === 'erasure') {
    // Process deletion
    await dsar.processRequest(requestId);

    // Notify user
    await sendEmail({
      to: request.email,
      subject: 'Your Data Has Been Deleted',
      body: 'Your personal data has been removed from our systems.',
    });
  }
}

GDPR Compliance Notes

  1. 30-Day Deadline: GDPR requires responding within 30 days
  2. Verification: Always verify the requester's identity
  3. Audit Trail: All DSAR actions are logged automatically
  4. Data Portability: Export in machine-readable format (JSON)
  5. Third Parties: Remember to notify data processors

Next Steps