Merge API can't fetch file from server that requires custom headers

Which product are you using?
PDF.js Express Plus

PDF.js Express Version

Detailed description of issue
My application needs to merge annotations into a PDF. The annotations are extracted to xfdf form from a WebViewer session using annotationManager.exportAnnotations(). The PDFs are stored on our server. Our server uses the HTTP Authorization header for authentication. In the code below, I’ve followed the guide “Merging XFDF using the Express REST API” as closely as possible, adding the ‘headers’ field to the form data sent to the API, but I always get the error { error: { code: 21, message: 'Cannot retrieve file from URL.' } }. I get the same result when I used the @pdftron/jspdf-express-utils helper library.

I can confirm that this code works for unsecured documents because I get the proper output when replacing the url with a link to a dummy PDF on the W3’s web site. I’m also sure that the url is correct, because copying and pasting the url into

curl -H "Authorization: <myauthorizationcode>" "<url>"

returns the expected binary PDF file. I can also load the document properly in a WebViewer session with the code:

const headers = { 'Authorization': "myauthorizationcode" } instance.UI.loadDocument(url, { filename: "<url>", customHeaders: headers });

Incidentally, when making the call to the merge API, I don’t see anything appear in the access logs for my website, so I’m not sure how it decides that it “Cannot retrieve file”. In any event, here is an example of the headers that it would see returned from my server:

X-Powered-By: Express
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE, PATCH, HEAD
Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With, Range, Content-Disposition, upload-length, upload-name, upload-offset, continuationtoken
Access-Control-Expose-Headers: Accept-Ranges, Content-Encoding, Content-Length, Content-Range
Content-disposition: attachment; filename="<nameofpdf>.pdf"
Content-type: application/pdf
Content-length: 411233
Date: Thu, 14 Dec 2023 17:38:50 GMT
Connection: keep-alive
Keep-Alive: timeout=5

Expected behaviour
The API should return a link to the merged file.

Does your issue happen with every document, or just one?
Every document

Link to document
For security reasons, I cannot.

Code snippet

const FormData = require('form-data')
const fetch = require('node-fetch')

const xfdf = "a valid xfdf document"

(async function() {
	const form = new FormData();
	form.append('xfdf', xfdf);
	form.append('file', 'https://<>/api/attachment/<attachmentid>/1');
	form.append('headers', JSON.stringify({
		Authorization: '<myauthorizationcode>'

  const response = await fetch('', {
    method: 'post',
    body: form
  }).then(resp => resp.json());


Hi there,

We have a guide on adding custom headers:

The code 21 error seems to indicate that the URL provided is invalid/inaccessible

Can you also confirm that in the network requests you are sending the correct headers/authorization code?

Best regards,
Kevin Kim