vevee.analytics.exportPerson()POST /api/v1/export-personGET /api/v1/export-downloadsk_live_

GDPR Art. 15 (access) + Art. 20 (portability). Produces a signed download URL for a JSON dump of everything APL holds for a user. Requires a secret key (sk_*) - backend-only.

Signature

vevee.analytics.exportPerson(distinctId: string): Promise<{
  downloadUrl: string;
  expiresAt: string;   // ISO 8601 - URL is valid for 24h
  format: 'json';
}>

Flow

// In your backend, in response to an Art. 15 request:
const { downloadUrl, expiresAt } = await vevee.analytics.exportPerson('user_12345');

// Email / show the URL to the data subject. It is a self-contained,
// time-bound bearer URL - no further auth needed to download.
await sendEmail({
  to: user.email,
  subject: 'Your data export',
  body: `Download: ${downloadUrl} (expires ${expiresAt})`,
});

What’s in the export

{
  "format": "json",
  "exportedAt": "2026-05-23T10:00:00Z",
  "appId": "app_abc",
  "mode": "live",
  "distinctId": "user_12345",
  "person":         { ...analytics_persons row... },
  "events": {
    "analytics":    [ ...analytics_events... ],
    "metering":     [ ...events... ]
  },
  "consentLog":     [ ...consent_audit_log entries... ]
}

Anonymous events are deliberately not included- they carry no person link by design and aren’t “their data” in any portable sense.

How the URL works

The download URL points at GET /api/v1/export-download?token=.... The token is an HMAC-SHA-256-signed payload - { appId, mode, distinctId, expiresAt } - signed with the ANALYTICS_EXPORT_SECRET env on the APL server. No API key is needed to download: the token is the authorisation.

The export is computed on demand when the URL is opened; no copy is staged in object storage. After expiresAt, the token is invalid; mint a new URL by calling exportPerson() again.

!
You need to configure ANALYTICS_EXPORT_SECRET (≥16 chars) on the APL deployment for this endpoint to work. If running APL self-hosted, set it in your environment alongside the other secrets.

Combine with your own data

The exporter only knows what APL holds. For a complete Art. 15 / 20 response you should also include the user’s data from your own database and any other processor in scope.

// app/api/privacy/export/route.ts
import { aplClient } from '@/lib/vevee';

export async function POST(req: Request) {
  const { userId } = await req.json();

  // Your own data:
  const own = await db.user.exportAllData(userId);

  // APL's data:
  const { downloadUrl, expiresAt } = await aplClient.analytics.exportPerson(userId);

  return Response.json({
    ownDataInline: own,
    aplDownloadUrl: downloadUrl,
    aplDownloadExpiresAt: expiresAt,
  });
}

Errors

CodeStatusWhen
requires_secret_key403POST /export-person called with a pk_* key.
invalid_or_expired_token401GET /export-download with a token that is past expiresAt or tampered with.
invalid_request400Empty or oversized distinctId.
i
Related: deletePerson() - irreversible erasure, optOut() - pause processing without deleting, and the Privacy & GDPR guide.