PDFGate's official Python SDK
The PDFGate Python SDK provides convenient access to the PDFGate API from applications written in Python. It includes typed parameter objects, synchronous and asynchronous clients, response helpers, webhook management, and webhook signature verification for common PDF generation, processing, and signing workflows.
📘 Documentation: https://pdfgate.com/documentation
🔑 Dashboard & API keys: https://dashboard.pdfgate.com
pdfgate
Table of Contents
- Installation
- Quick start
- Sync & Async
- Responses
- Managing Webhooks
- Webhook Verification
- Examples
- Development
- Support
- License
Installation
pip install pdfgate
Quick start
import os
from pdfgate import PDFGate, GeneratePDFParams
client = PDFGate(api_key=os.environ["PDFGATE_API_KEY"])
params = GeneratePDFParams(url="https://example.com")
document = client.generate_pdf(params)
print(document["id"])
Sync & Async
There are sync and async versions of all methods, the only difference is that the method name has an async suffix:
document_response = client.get_document(GetDocumentParams(document_id=document_id))
# VS
document_response = await client.get_document_async(GetDocumentParams(document_id=document_id))
Other than that, nothing changes and the interfaces are the same.
Responses
The SDK returns a PDFGateDocument for all processing endpoints:
generate_pdfflatten_pdfadd_form_fieldscompress_pdfwatermark_pdfprotect_pdfupload_file
To get raw PDF bytes, call get_file with a document ID.
delete_document returns None.
Envelope methods (create_envelope, get_envelope, send_envelope,
void_envelope) return a PDFGateEnvelope; delete_envelope returns None.
create_embed_link returns an EmbedLinkResponse with the signing url and
its expires_at.
Recipient methods (create_recipient, get_recipient, update_recipient,
request_recipient_signature, remove_recipient_signature) return a
PDFGateRecipient; list_recipients returns a RecipientListResponse with a
recipients list; delete_recipient returns None.
Webhook management methods (create_webhook, get_webhook) return a
WebhookResponse; delete_webhook returns None.
Every method has an async counterpart (e.g. add_form_fields_async,
delete_document_async, create_webhook_async).
Managing Webhooks
Register, retrieve, and delete webhook endpoints that receive PDFGate event
notifications. The secret returned by create_webhook is shown only once —
store it to verify incoming payloads.
from pdfgate import (
CreateWebhookParams,
DeleteWebhookParams,
GetWebhookParams,
WebhookEventType,
)
created = client.create_webhook(
CreateWebhookParams(
url="https://example.com/pdfgate-callback",
event_types=[
WebhookEventType.ENVELOPE_COMPLETED,
WebhookEventType.ENVELOPE_SENT,
],
description="Production signing events",
)
)
webhook_id = created["id"]
secret = created["secret"]
fetched = client.get_webhook(GetWebhookParams(webhook_id=webhook_id))
client.delete_webhook(DeleteWebhookParams(webhook_id=webhook_id))
The subscribable events are exposed via WebhookEventType:
ENVELOPE_SENT, ENVELOPE_COMPLETED, ENVELOPE_EXPIRED,
ENVELOPE_DOCUMENT_COMPLETED, ENVELOPE_RECIPIENT_ACTIVATED (occurs when
it becomes a recipient's turn to sign on a document with a signing order),
RECIPIENT_SIGNATURE_ENROLLED (a recipient completed the enrolment of a
stored signature) and RECIPIENT_SIGNATURE_REVOKED (an active stored
signature was removed from a recipient).
The webhook URL must be publicly accessible (localhost is not supported).
Webhook Verification
Use verify_signature with the raw request body and the
x-pdfgate-signature header value received from PDFGate:
from pdfgate import verify_signature
event = verify_signature(
secret="whsecret_...",
signature_header=request.headers["x-pdfgate-signature"],
payload=request_body,
)
event_id = event["event_id"]
Examples
Generate PDF
params = GeneratePDFParams(html="<h1>Hello from PDFGate!</h1>")
document = client.generate_pdf(params)
print(document["id"])
Get document metadata
document_response = client.get_document(GetDocumentParams(document_id=document_id))
print(document_response["id"])
print(document_response["status"])
print(document_response.get("file_url"))
Download a stored PDF file
file_content = client.get_file(GetFileParams(document_id=document_id))
with open("output.pdf", "wb") as f:
f.write(file_content)
Upload a PDF file
file_param = FileParam.pdf(name="input.pdf", data=pdf_file_bytes)
document_response = client.upload_file(UploadFileParams(file=file_param))
If both file and url are provided, file is prioritized and the request is sent as multipart.
Flatten a PDF (make form-fields non-editable)
flatten_pdf_params = FlattenPDFParams(
document_id=document_id,
# Optional: flatten only these fields and leave the rest interactive.
# Omit field_names to flatten the whole document.
field_names=["signature", "date"],
)
flattened_document = client.flatten_pdf(flatten_pdf_params)
Add form fields to a PDF
from pdfgate import (
AddFormFieldsParams,
DocumentFieldType,
FieldOverride,
ManualFormField,
)
response = client.add_form_fields(
AddFormFieldsParams(
document_id=document_id,
# Customize placeholder fields detected in the PDF, keyed by field name.
field_overrides={"signature": FieldOverride(role="signer", optional=False)},
# Or place fields at explicit positions on a given page.
fields=[
ManualFormField(
name="signed_on",
type=DocumentFieldType.DATE,
page=1,
x=100,
y=650,
width=160,
height=24,
)
],
)
)
Delete a stored document
from pdfgate import DeleteDocumentParams
client.delete_document(DeleteDocumentParams(document_id=document_id))
A document referenced by a draft or in-progress envelope cannot be deleted until those envelopes are completed or expired.
Compress a PDF
compress_pdf_params = CompressPDFParams(
document_id=document_id
)
response = client.compress_pdf(compress_pdf_params)
Watermark a PDF
watermark_pdf_params = WatermarkPDFParams(
document_id=document_id,
type=WatermarkType.IMAGE,
watermark=FileParam(name="watermark.jpg", data=jpg_file),
)
watermarked_pdf = client.watermark_pdf(watermark_pdf_params)
For a text watermark you can upload a custom font file (TTF/OTF), which overrides the
built-in font:
watermark_pdf_params = WatermarkPDFParams(
document_id=document_id,
type=WatermarkType.TEXT,
text="Confidential",
font_file=FileParam(name="custom.ttf", data=ttf_file, type="font/ttf"),
rotate=30,
opacity=0.3,
)
watermarked_pdf = client.watermark_pdf(watermark_pdf_params)
Protect (encrypt) a PDF
protect_pdf_params = ProtectPDFParams(
document_id=document_id,
user_password="user-password",
owner_password="owner-password",
)
response = client.protect_pdf(protect_pdf_params)
Extract PDF form fields values
html_form = """
<form>
<input type="text" name="first_name" value="John" />
<input type="text" name="last_name" value="Doe" />
</form>
"""
generate_pdf_params = GeneratePDFParams(
html=html_form, enable_form_fields=True
)
document_response = client.generate_pdf(generate_pdf_params)
document_id = document_response["id"]
extract_form_params = ExtractPDFFormDataParams(document_id=document_id)
response = client.extract_pdf_form_data(extract_form_params)
Embedded signing
Recipients marked embedded sign inside your own application instead of the
hosted signing UI, and receive no emails from PDFGate. After sending the
envelope, create a short-lived signing link and render it in an iframe.
from pdfgate import (
CreateEmbedLinkParams,
CreateEnvelopeParams,
EnvelopeDocument,
EnvelopeRecipient,
SendEnvelopeParams,
)
envelope = client.create_envelope(
CreateEnvelopeParams(
requester_name="John Doe",
documents=[
EnvelopeDocument(
source_document_id=document_id,
name="Employment Agreement",
recipients=[
EnvelopeRecipient(
email="anna@example.com",
name="Anna Smith",
role="signer",
embedded=True,
signing_order=1,
),
EnvelopeRecipient(
email="bob@example.com",
name="Bob Jones",
role="signer",
signing_order=2,
),
],
)
],
)
)
client.send_envelope(SendEnvelopeParams(envelope_id=envelope["id"]))
link = client.create_embed_link(
CreateEmbedLinkParams(
envelope_id=envelope["id"],
document_id=document_id,
recipient_id=envelope["documents"][0]["recipients"][0]["recipient_id"],
return_url="https://yourapp.com/signing/done",
)
)
print(link["url"], link["expires_at"])
signing_order starts from 1: recipients sign one after another in this order
and a recipient is activated once everyone with a lower value has signed;
provide it for every recipient of a document or for none — omitted, all
recipients can sign immediately. On documents with a signing order the embed
link can only be created once it is the recipient's turn (the API returns an
error before that); the envelope.recipient.activated webhook signals that
moment.
auto_sign applies the stored signature of the referenced recipient on their
behalf when their turn to sign comes, with no signing session or email. It
requires recipient_id pointing to a recipient with an active stored
signature (see Automatic signing) and cannot be combined
with embedded. Every required field of the recipient must already have a
value before the document is sent, except signature and auto-filled date
fields. send_signed_copy controls whether the recipient is emailed the
signed document when the document completes; it defaults to True for
recipients who sign through the emailed signing link and False for embedded
and automatic signing recipients.
The envelope must be in in_progress status and the link expires after 10
minutes, so create it when the signer is ready (one link per signing session).
When the session ends the iframe redirects to return_url with event
(signing_complete, voided, expired, or not_found), envelopeId,
documentId, and recipientId appended as query parameters; existing
return_url query parameters are preserved.
Managing recipients
Store recipients in your account and reference them by recipient_id when
creating envelopes, as an alternative to passing email and name inline.
Emails are not unique; every create_recipient call creates a new recipient,
so list existing recipients first when reuse is intended.
from pdfgate import (
CreateRecipientParams,
GetRecipientParams,
ListRecipientsParams,
UpdateRecipientParams,
)
recipient = client.create_recipient(
CreateRecipientParams(
email="anna@example.com",
name="Anna Smith",
metadata={"customerId": "cus_123"},
)
)
recipients = client.list_recipients(
ListRecipientsParams(email="anna@example.com")
)["recipients"]
fetched = client.get_recipient(GetRecipientParams(recipient_id=recipient["id"]))
updated = client.update_recipient(
UpdateRecipientParams(recipient_id=recipient["id"], name="Anna Smith-Jones")
)
The recipient email cannot be changed after creation. Updating a recipient does not affect existing envelopes; they keep the recipient name they were created with.
from pdfgate import DeleteRecipientParams
client.delete_recipient(DeleteRecipientParams(recipient_id=recipient["id"]))
Deleting a recipient removes its personal data from the directory; envelopes already sent are not affected. A recipient with an active stored signature or a pending enrolment cannot be deleted until the signature is removed.
envelope = client.create_envelope(
CreateEnvelopeParams(
requester_name="John Doe",
documents=[
EnvelopeDocument(
source_document_id=document_id,
name="Employment Agreement",
recipients=[
EnvelopeRecipient(recipient_id=recipient["id"], role="signer")
],
)
],
)
)
Automatic signing
A recipient can store a reusable signature once, and later envelopes apply it on their behalf with no signing session. Request the enrolment, and PDFGate emails the recipient a link where they verify their email with a one-time code, accept the authorization text and type, draw or upload their signature. The link is valid for 7 days.
from pdfgate import (
CreateEnvelopeParams,
EnvelopeDocument,
EnvelopeRecipient,
GetRecipientParams,
RemoveRecipientSignatureParams,
RequestRecipientSignatureParams,
)
pending = client.request_recipient_signature(
RequestRecipientSignatureParams(recipient_id=recipient["id"])
)
print(pending["signature"]["status"]) # "pending"
# Once the recipient completes the enrolment (webhook event
# `recipient.signature.enrolled`), the signature is active.
enrolled = client.get_recipient(GetRecipientParams(recipient_id=recipient["id"]))
print(enrolled["signature"]["status"], enrolled["signature"]["image"])
# "active", PNG data URL
# Sign automatically: the recipient is signed at send, or when their turn
# in the signing order comes. No signing email is sent.
envelope = client.create_envelope(
CreateEnvelopeParams(
requester_name="HR",
documents=[
EnvelopeDocument(
source_document_id=document_id,
name="Employment Agreement",
recipients=[
EnvelopeRecipient(
email="anna@example.com",
name="Anna Smith",
role="employee",
signing_order=1,
),
EnvelopeRecipient(
recipient_id=recipient["id"],
role="ceo",
signing_order=2,
auto_sign=True,
),
],
)
],
)
)
# Remove the stored signature or cancel a pending enrolment.
client.remove_recipient_signature(
RemoveRecipientSignatureParams(recipient_id=recipient["id"])
)
The signature object on a recipient carries status (pending, active or
revoked, exposed via StoredSignatureStatus), requested_at,
adoption_method (SignatureAdoptionMethod), enrolled_at, revoked_at,
last_used_at and use_count. The image is returned by get_recipient
only while the signature is active. Removing an active signature fires the
recipient.signature.revoked webhook event, and envelopes already waiting on
it fall back to a signing link emailed to the recipient. The audit log records
every automatic signature with the enrolment details.
Development
Before doing anything, install pre-commit by running:
hatch run dev:pre-commit install
This will run several checks every time you try to git commit including:
- linting and formatting with
Ruff - type checking with
mypy
If you are on VS Code, it's recommended to install the Ruff extension so you'll get formatting on the fly.
Hatch is used as a build system and for dependency management, so all main actions are configured to be run with Hatch.
Tests
Unit tests:
hatch run test:test
Acceptance tests hit the PDFGate API so they are slower, and require an API key that is expected to be set as an env var named PDFGATE_API_KEY. You can set it on your Bash/zsh/fish profile or inline as in:
PDFGATE_API_KEY="test_123" hatch run test:test_acceptance
Manually run Ruff
Linting:
hatch run dev:lint
Formatting:
hatch run dev:ruff format .
Type checking
Run mypy manually:
hatch run dev:check
Docs
Docs are built using MkDocs, they live in the docs/ folder, and in the code. If you make any changes, and would like to see them live before publishing them, spin up a server locally with:
hatch run docs:serve
Changes to docs/** and mkdocs.yml trigger a new deployment of the docs site. If you change the code's documentation and want to manually update the docs site you can do it from the Actions tab of the repo or by running:
hatch run docs:mkdocs gh-deploy
Support
For support, contact support@pdfgate.com.
License
pdfgate is distributed under the terms of the MIT license.
Metadata
Release files for pdfgate 1.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| pdfgate-1.3.0.tar.gz | 43.8 kB | With provenance | Details for pdfgate-1.3.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| pdfgate-1.3.0-py3-none-any.whl | Python 3 | none | any | Details for pdfgate-1.3.0-py3-none-any.whl |
Total release size: 72.8 kB
Release files / pdfgate-1.3.0.tar.gz
| Download URL | pdfgate-1.3.0.tar.gz |
|---|---|
| Size | 43.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4292a76be2c709457113d3e030675a47eb703d4ee7be431de097de895cba60c0
|
|
BLAKE2b-256 checksum How to use checksums |
3ba87abc2b241f5ea132db6457342078d88dd8d9b98fb0c138d88cb99d1b66b7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.
Transparency logRelease files / pdfgate-1.3.0-py3-none-any.whl
| Download URL | pdfgate-1.3.0-py3-none-any.whl |
|---|---|
| Size | 29.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
cd3fed711765fc9f0641e2edfed44ff5c9d34b569e044f32973f3798ae208a77
|
|
BLAKE2b-256 checksum How to use checksums |
93f77f2c66e12cc0f399c1a938e7980da5b85aa23ce3a58bd3925773138fd524
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.
Transparency logSecurity for pdfgate 1.3.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for pdfgate-1.3.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.