PDF Merge and Compress
2 endpoints · Core API
Merge several PDFs (5 by default, up to 20 per account) into one, in the order you send them, and optionally compress the result. Use it to prepare a loan kit or any multi-document pack as a single PDF before starting eSign.
How it works
The call is asynchronous: you get 202 Accepted with a request_id straight away, and the result PDF is POSTed to your webhook_url as base64 when processing finishes. You can also collect it from the status endpoint while it is held.
What you can do
- Merge 2 to 5 PDFs by default, or up to 20 if your account allows it (50 MB total)
- Compress the merged PDF, or a single PDF, at low, medium or high level
Notes
Merging or compressing invalidates existing digital signatures, so merge first and sign after. Files are held only temporarily and deleted once the result is delivered.
Merge and/or compress PDFs (async — returns 202, result by webhook)
Merges PDFs in the order given and, with compress: true, compresses the result.
A single file with compress: true compresses that file only.
Asynchronous. Every valid request returns 202 Accepted with a request_id.
When processing finishes, the result is POSTed to webhook_url with the PDF as base64
(event: pdf.process.completed), or an error (event: pdf.process.failed). A failed webhook
is retried after 30 s, 2 min and 10 min. The PDF can also be collected from
GET /api/v1/pdf/process/{request_id} while it is still held.
Limits: 1–5 files by default (your account can be set up for more, up to 20; ask your account manager), 50 MB in total, not password-protected. reference_id must be unique —
sending it again returns the original request (duplicate: true) and is not charged again.
webhook_url must be https and must accept large bodies (up to ~70 MB).
Compression levels: compression makes the file smaller by reducing the quality of images and
scanned pages inside the PDF. Typed text stays sharp at every level. The higher the level, the smaller the file,
but images look less detailed.
low: light compression (images kept at 300 dpi, print quality). The file shrinks the least, and
images and scans still print clearly. Use it when the document will be printed or has fine detail, such as
small print on scanned pages, stamps or signatures.
medium (default): balanced compression (150 dpi). The file is noticeably smaller and still easy to
read on screen and in ordinary prints. Suits most documents, such as agreements, forms and KYC packs.
high: strong compression (72 dpi, screen quality). The file is the smallest, but images and scans may
look blurry when you zoom in or print. Use it only when file size matters most, for example email attachments
or portal upload limits.
How much the file shrinks depends on its content. PDFs with many photos or scanned pages shrink a lot,
while PDFs that are mostly typed text are already small and shrink very little. If compression cannot make the
file smaller, you receive the original (uncompressed) PDF and a warning in the response.
Storage: files are held temporarily in encrypted private storage only while the request is in progress, and deleted as soon as the webhook is delivered (or 1 hour after delivery finally fails).
Notes: merging or compressing invalidates existing digital signatures — merge first, then eSign. Compression may flatten form fields. One flat charge per successful request.
Headers
- X-API-KEYstringrequiredYour secret API key for the environment you are calling (Stage and Live use different keys).
- X-API-APP-IDstringrequiredThe App ID issued together with the key.
- Content-Typestringrequiredapplication/json
Body parameters
application/json- reference_idstringrequiredRequired. Your unique reference for this request (max 100 characters). Sending the same value again returns the original request instead of starting a second job.minLength 1
- webhook_urlstringrequiredRequired. HTTPS URL that receives the result (max 500 characters). The success payload carries the PDF as base64, so the endpoint must accept large request bodies (up to ~70 MB).minLength 1
- compressbooleanoptionalOptional. Compress the result. Default false. Required to be true when only one file is sent.default false
- compression_levelstringoptionalOptional. How much to shrink the file. Only images and scanned pages lose quality; typed text stays sharp. low = slightly smaller, print quality (300 dpi); medium = noticeably smaller, good for screen and ordinary print (150 dpi, default); high = smallest file, images may look blurry when zoomed or printed (72 dpi). Ignored when compress is false.nullabledefault medium
- output_file_namestringoptionalOptional. Name of the result file. Path characters are removed and .pdf is forced. Default merged.pdf (or compressed.pdf for a single file).nullable
- filesarray<object>requiredRequired. 1 to 5 PDFs by default (your account may be set up for more, up to 20), merged in array order. Total decoded size max 50 MB.
Each item: 2 fields
- file_namestringoptionalOptional. Used only in error and warning messages.nullable
- datastring (byte)requiredRequired. Base64-encoded PDF. Must not be password-protected.
Sample request body
Responses
202Accepted
- successbooleanoptionalNo description.
- statusCodeinteger (int32)optionalNo description.default 200
- messagestringoptionalNo description.nullabledefault XXXXX
- dataobjectoptionalReturned with 202 Accepted when a PDF Process request is queued.
4 fields
- request_idstringoptionalNo description.nullable
- reference_idstringoptionalNo description.nullable
- statusstringoptionalCurrent status — accepted, processing, completed, delivered, delivery_failed, expired or failed.nullable
- duplicatebooleanoptionalTrue when this reference_id was already submitted; the original request is returned.
- messagesarray<string>optionalNo description.nullable
- request_idstringoptionalNo description.nullable
- isBillablebooleanoptionalWhether this request was charged: true if it was, false if not. Errors always show false. Flows that finish later (ITR Fetch, SHCIL eStamp, DigiLocker, eSign) show false here, because the charge is made when the result is ready. eSign charges each signer when they complete signing. DigiLocker deducts the credit when the link is created and refunds it automatically if the link expires before the user completes it.
400Bad Request
- successbooleanoptionalNo description.
- statusCodeinteger (int32)optionalNo description.default 200
- messagestringoptionalNo description.nullabledefault XXXXX
- dataanyoptionalNo description.nullable
- messagesarray<string>optionalNo description.nullable
- request_idstringoptionalNo description.nullable
- isBillablebooleanoptionalWhether this request was charged: true if it was, false if not. Errors always show false. Flows that finish later (ITR Fetch, SHCIL eStamp, DigiLocker, eSign) show false here, because the charge is made when the result is ready. eSign charges each signer when they complete signing. DigiLocker deducts the credit when the link is created and refunds it automatically if the link expires before the user completes it.
402Payment Requiredsame body as 400
403Forbiddensame body as 400
413Content Too Largesame body as 400
Code samplescurl · Node.js · Python
Get the status of a PDF Process request (includes the PDF while it is held)
Returns the same body as the webhook. data.pdf (base64) is included while the result is held:
status completed (webhook not yet delivered or retrying) and delivery_failed (for 1 hour).
After a successful webhook the status is delivered and the PDF has been deleted.
Other values: accepted, processing, expired, failed (see error.message).
Reading the status is not charged.
Headers
- X-API-KEYstringrequiredYour secret API key for the environment you are calling (Stage and Live use different keys).
- X-API-APP-IDstringrequiredThe App ID issued together with the key.
Path parameters
- requestIdstring (uuid)requiredNo description.
Responses
200OK
- successbooleanoptionalNo description.
- statusCodeinteger (int32)optionalNo description.default 200
- messagestringoptionalNo description.nullabledefault XXXXX
- dataobjectoptionalThe result of a PDF Process request. Sent as the webhook body and returned (inside the standard response envelope) by the status endpoint.
7 fields
- eventstringoptionalpdf.process.completed or pdf.process.failed (webhook); null on the status endpoint.nullable
- request_idstringoptionalNo description.nullable
- reference_idstringoptionalNo description.nullable
- statusstringoptionalaccepted, processing, completed, delivered, delivery_failed, expired or failed.nullable
- messagestringoptionalHuman-readable note, e.g. why the PDF is no longer available.nullable
- dataobjectoptionalNo description.
10 fields
- file_namestringoptionalNo description.nullable
- pdfstring (byte)optionalThe result PDF as base64. Present only while the result is still held (see status values).nullable
- page_countinteger (int32)optionalNo description.
- file_countinteger (int32)optionalNo description.
- input_size_bytesinteger (int64)optionalNo description.
- output_size_bytesinteger (int64)optionalNo description.
- reduction_percentnumber (double)optionalNo description.
- mergedbooleanoptionalNo description.
- compression_appliedstringoptionalnone, low, medium or high — what was actually applied (none if compression gave no gain).nullable
- warningsarray<string>optionalNo description.nullable
- errorobjectoptionalNo description.
1 field
- messagestringoptionalNo description.nullable
- messagesarray<string>optionalNo description.nullable
- request_idstringoptionalNo description.nullable
- isBillablebooleanoptionalWhether this request was charged: true if it was, false if not. Errors always show false. Flows that finish later (ITR Fetch, SHCIL eStamp, DigiLocker, eSign) show false here, because the charge is made when the result is ready. eSign charges each signer when they complete signing. DigiLocker deducts the credit when the link is created and refunds it automatically if the link expires before the user completes it.
404Not Found
- successbooleanoptionalNo description.
- statusCodeinteger (int32)optionalNo description.default 200
- messagestringoptionalNo description.nullabledefault XXXXX
- dataanyoptionalNo description.nullable
- messagesarray<string>optionalNo description.nullable
- request_idstringoptionalNo description.nullable
- isBillablebooleanoptionalWhether this request was charged: true if it was, false if not. Errors always show false. Flows that finish later (ITR Fetch, SHCIL eStamp, DigiLocker, eSign) show false here, because the charge is made when the result is ready. eSign charges each signer when they complete signing. DigiLocker deducts the credit when the link is created and refunds it automatically if the link expires before the user completes it.