cURL
curl --fail \
'https://verification.didit.me/v3/users/user-abc-123/generate-pdf/' \
-H 'x-api-key: YOUR_API_KEY' \
--output user-history.pdfimport requests
response = requests.get(
"https://verification.didit.me/v3/users/user-abc-123/generate-pdf/",
headers={"x-api-key": "YOUR_API_KEY"},
stream=True,
timeout=120,
)
response.raise_for_status()
with open("user-history.pdf", "wb") as fh:
for chunk in response.iter_content(chunk_size=8192):
fh.write(chunk)import { writeFile } from 'node:fs/promises';
const vendorData = encodeURIComponent('user-abc-123');
const response = await fetch(
`https://verification.didit.me/v3/users/${vendorData}/generate-pdf/`,
{ headers: { 'x-api-key': process.env.DIDIT_API_KEY } },
);
if (!response.ok) throw new Error(`PDF generation failed: HTTP ${response.status}`);
await writeFile('user-history.pdf', Buffer.from(await response.arrayBuffer()));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://verification.didit.me/v3/users/{vendor_data}/generate-pdf/",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://verification.didit.me/v3/users/{vendor_data}/generate-pdf/"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("x-api-key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://verification.didit.me/v3/users/{vendor_data}/generate-pdf/")
.header("x-api-key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://verification.didit.me/v3/users/{vendor_data}/generate-pdf/")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["x-api-key"] = '<api-key>'
response = http.request(request)
puts response.read_body"<string>"Users
Download User History PDF
Download one PDF bundling every reportable verification session of a user, keyed by vendor_data, for audits, SARs and periodic compliance exports.
GET
/
v3
/
users
/
{vendor_data}
/
generate-pdf
/
cURL
curl --fail \
'https://verification.didit.me/v3/users/user-abc-123/generate-pdf/' \
-H 'x-api-key: YOUR_API_KEY' \
--output user-history.pdfimport requests
response = requests.get(
"https://verification.didit.me/v3/users/user-abc-123/generate-pdf/",
headers={"x-api-key": "YOUR_API_KEY"},
stream=True,
timeout=120,
)
response.raise_for_status()
with open("user-history.pdf", "wb") as fh:
for chunk in response.iter_content(chunk_size=8192):
fh.write(chunk)import { writeFile } from 'node:fs/promises';
const vendorData = encodeURIComponent('user-abc-123');
const response = await fetch(
`https://verification.didit.me/v3/users/${vendorData}/generate-pdf/`,
{ headers: { 'x-api-key': process.env.DIDIT_API_KEY } },
);
if (!response.ok) throw new Error(`PDF generation failed: HTTP ${response.status}`);
await writeFile('user-history.pdf', Buffer.from(await response.arrayBuffer()));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://verification.didit.me/v3/users/{vendor_data}/generate-pdf/",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://verification.didit.me/v3/users/{vendor_data}/generate-pdf/"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("x-api-key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://verification.didit.me/v3/users/{vendor_data}/generate-pdf/")
.header("x-api-key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://verification.didit.me/v3/users/{vendor_data}/generate-pdf/")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["x-api-key"] = '<api-key>'
response = http.request(request)
puts response.read_body"<string>"Overview
Returns one PDF with the full verification history of a User entity, keyed byvendor_data.
It is the same document the Console produces from User detail → Actions → Download PDF, so compliance teams can automate exports instead of clicking once per user.
The report contains:
- A cover page with the profile summary (name, date of birth, issuing states, approved emails and phones), the latest status of every feature, the session counters and an index of the sessions included.
- One full session report per included session, in chronological order (oldest first). Each one is the same report Generate PDF returns for that session on its own.
Eligible sessions and cap
Only User Verification (KYC) sessions inApproved, Declined, In Review or Kyc Expired are included. Sessions in any other status (Not Started, In Progress, Abandoned, Expired, …) are skipped.
The report is capped at the 20 most recent reportable sessions. When a user has more, the newest 20 are included and the cover page states how many older sessions were left out. Use List sessions filtered by vendor_data when you need the complete list as JSON.
Response
The body is the PDF itself (application/pdf), not a JSON wrapper or a download URL. The Content-Disposition header is attachment; filename=user_<didit_internal_id>.pdf, where didit_internal_id is the user’s stable Didit identifier.
White-label
When white-label customization is enabled on your application, the cover and every session report carry your logo and privacy-policy URL instead of Didit branding, exactly like the per-session endpoint. Configure it at Console → Customization.Caching and latency
Nothing is cached: every call re-renders the report from the current data, so a report requested after a manual review reflects the reviewer’s decision. Two calls can produce byte-different files; archive the downloaded file when you need an immutable copy. Rendering is synchronous and downloads every stored image of every included session, so a user with many media-heavy sessions can take tens of seconds. Use a generous client read timeout (120 s recommended) and stream the body to disk. Generation stops at a server-side time budget rather than running until your client gives up: if that budget is reached, the report still returns a valid PDF with the most recent sessions and the cover states how many were omitted.Trailing slash
The canonical route ends with a trailing slash (…/generate-pdf/). A request without it is served directly with the same response: there is no 301 redirect, so no client has to follow redirects and curl does not need -L. Use the slashed URL as in the samples; it is the form the OpenAPI spec and your logs show.
Errors
| Status | When | detail |
|---|---|---|
401 | No x-api-key header (and no Authorization: Bearer token). | You must be authenticated with a valid access token to access this endpoint. |
403 | Invalid or revoked API key, or a Console user token instead of an API key. | You do not have permission to perform this action. |
403 | The user exists but none of its sessions is in a reportable status. | This user has no sessions in review, declined, approved or kyc expired to report. |
404 | No user with this vendor_data in the application behind the API key, including users that belong to another application. | User not found. |
429 | More than 50 PDF generations per minute from the same credential. The budget is shared with Generate PDF. Wait Retry-After seconds. | Session PDF generation rate limit exceeded. You can make up to 50 requests per minute. |
Examples
- curl
- Python
- JavaScript
curl --fail 'https://verification.didit.me/v3/users/user-abc-123/generate-pdf/' \
-H 'x-api-key: YOUR_API_KEY' \
--output user-history.pdf
import requests
response = requests.get(
"https://verification.didit.me/v3/users/user-abc-123/generate-pdf/",
headers={"x-api-key": "YOUR_API_KEY"},
stream=True,
timeout=120,
)
response.raise_for_status()
with open("user-history.pdf", "wb") as fh:
for chunk in response.iter_content(chunk_size=8192):
fh.write(chunk)
import { writeFile } from 'node:fs/promises';
const vendorData = encodeURIComponent('user-abc-123');
const response = await fetch(
`https://verification.didit.me/v3/users/${vendorData}/generate-pdf/`,
{ headers: { 'x-api-key': process.env.DIDIT_API_KEY } },
);
if (!response.ok) throw new Error(`PDF generation failed: HTTP ${response.status}`);
await writeFile('user-history.pdf', Buffer.from(await response.arrayBuffer()));
Permissions
Any active API key of the application can call this endpoint. Didit API keys are application-scoped, not role-scoped: the Console’s roles and permissions (read:users and the rest) decide what a person can do in the Console, and are not evaluated
for Management API traffic. A key can only export users of its own application.
Related
- Generate PDF — the same report for a single session
- User KYC history — listing and inspecting a user’s sessions as JSON
- Export PDF & CSV — the Console side of both exports
- Get user — the user’s profile and feature map as JSON
Authorizations
Path Parameters
Your unique identifier for the user — a free-form string (NOT a UUID). This is the same value you passed as vendor_data when creating the user's sessions, matched exactly as sent. URL-encode it; a value containing / cannot be addressed by this route.
Response
The rendered PDF document, returned directly as binary application/pdf — there is no JSON wrapper and no download-URL indirection. The body starts with the %PDF magic bytes. Save it to a .pdf file or stream it through to your caller.
The response is of type file.