---
title: "Signed Download Tokens"
canonical: "https://test.abhinandan.one/node-sdk-download-tokens"
markdown_url: "https://test.abhinandan.one/node-sdk-download-tokens.md"
publisher: "Primitive SDKs"
kind: "guide"
content_type: "reference"
category: "Node.js SDK"
description: "Issue and verify short-lived HMAC download tokens that scope access to a single email's raw bytes or attachment bundle in the Node.js SDK."
keywords: ["generateDownloadToken", "verifyDownloadToken", "VerifyDownloadTokenResult", "signed download token", "download-tokens", "@primitivedotdev/sdk/webhook"]
last_modified: "2026-08-11T18:54:54.340872+00:00"
published_at: "2026-08-11T18:54:54.181968+00:00"
sections:
  - {anchor: "mint-and-verify-a-token", title: "Mint and verify a token"}
  - {anchor: "step-generate-the-token-where-you-have-the-email-and-the-secret", title: "Generate the token where you have the email and the secret"}
  - {anchor: "step-embed-the-token-in-a-url-your-own-route-serves", title: "Embed the token in a URL your own route serves"}
  - {anchor: "step-verify-before-serving-any-bytes", title: "Verify before serving any bytes"}
  - {anchor: "next-steps", title: "Next steps"}
---

> Documentation index: https://test.abhinandan.one/llms.txt

# Signed Download Tokens

Generate short-lived, HMAC-signed tokens that scope a download URL to one email's raw MIME bytes or attachment bundle, and verify them before serving the content.

Signed download tokens let you mint a short-lived, HMAC-signed token that scopes access to one email's raw bytes or attachment bundle, so you can hand out a fetchable link without exposing your webhook secret. Use them when a browser or downstream service needs to retrieve that content later from a route you serve.

Both helpers are exported from the `webhook` subpath of `@primitivedotdev/sdk`, the SDK entry point that also carries signature verification:

```typescript
import {
  generateDownloadToken,
  verifyDownloadToken,
} from "@primitivedotdev/sdk/webhook";
```

> **Note:** `generateDownloadToken` takes a `GenerateDownloadTokenOptions` object and `verifyDownloadToken` takes `VerifyDownloadTokenOptions` and returns a `VerifyDownloadTokenResult`. All three types are exported alongside the functions, so your editor shows the exact required fields; the SDK is the authoritative reference for them.
>
> ```typescript
> import type {
>   GenerateDownloadTokenOptions,
>   VerifyDownloadTokenOptions,
>   VerifyDownloadTokenResult,
> } from "@primitivedotdev/sdk/webhook";
> ```

> **Tip:** Checking whether an inbound email's raw content arrived inline or must be fetched from Primitive's own download URL is a different job. See [Parsing Raw Email (.eml)](https://test.abhinandan.one/node-sdk-parsing-email.md) for `isRawIncluded` and `decodeRawEmail`. Download tokens are for URLs you mint and serve yourself.

## Mint and verify a token

Generate the token in the code that already holds the email and the secret, then verify it in the route that serves the bytes.

### 1. Generate the token where you have the email and the secret

```typescript
// server-side: any module that already has the inbound email in hand
import { generateDownloadToken } from "@primitivedotdev/sdk/webhook";

const token = generateDownloadToken({
  emailId: email.id,
  secret: process.env.PRIMITIVE_WEBHOOK_SECRET!,
});
```

Fill in the remaining fields from `GenerateDownloadTokenOptions`; your editor lists them and marks which are required.

### 2. Embed the token in a URL your own route serves

```typescript
const downloadUrl = `https://yourapp.example.com/downloads/${email.id}?token=${token}`;
```

Hand this URL to whatever needs the content later. The token, not the path, is what gates access.

### 3. Verify before serving any bytes

```typescript
// app/downloads/[emailId]/route.ts (Next.js route handler)
import { verifyDownloadToken } from "@primitivedotdev/sdk/webhook";

export const runtime = "nodejs";

export async function GET(
  req: Request,
  { params }: { params: { emailId: string } },
) {
  const token = new URL(req.url).searchParams.get("token") ?? "";

  const result = verifyDownloadToken({
    token,
    emailId: params.emailId,
    secret: process.env.PRIMITIVE_WEBHOOK_SECRET!,
  });

  // Inspect `result` (a VerifyDownloadTokenResult) and reject before you
  // read anything off disk or call Primitive's download endpoint.
  // ... stream the raw bytes or attachment bundle here ...
}
```

> **Warning:** Verify on every request. Do not cache a "verified once" flag for a token past its lifetime, and do not skip the email-id check to serve a multi-email download page. A token is scoped to a single email; mint one per email.

> **Tip:** Both functions take the secret directly, so they work outside a `receive()` call, for example in a standalone download route that never sees the original inbound webhook request. Use the same `PRIMITIVE_WEBHOOK_SECRET` you use for [webhook signature verification](https://test.abhinandan.one/node-sdk-webhook-signing.md).
