Troubleshooting
If requests fail, run zero doctor check-connector --env-name PANDADOC_TOKEN or zero doctor check-connector --url https://api.pandadoc.com/public/v1/documents --method GET
Authentication
PandaDoc uses a custom API-Key auth scheme on the standard Authorization header — it is NOT Bearer. All examples below use:
Authorization: API-Key $PANDADOC_TOKEN
Base URL: https://api.pandadoc.com/public/v1
Environment Variables
| Variable | Description |
|---|---|
PANDADOC_TOKEN |
PandaDoc API key (Production or Sandbox). Only Org Admins can generate keys. |
Documents
1. List Documents
List documents in the workspace. Supports filtering by status, template_id, tag, q (name search), created_from, created_to, pagination (page, count max 100).
curl -s "https://api.pandadoc.com/public/v1/documents?count=20" --header "Authorization: API-Key $PANDADOC_TOKEN" | jq '.results[] | {id, name, status, date_created, date_modified}'
2. Get Document Details
Replace <document-id> with the document's UUID.
curl -s "https://api.pandadoc.com/public/v1/documents/<document-id>/details" --header "Authorization: API-Key $PANDADOC_TOKEN" | jq '{id, name, status, recipients, metadata, date_created, date_completed}'
3. Get Document Status (lightweight)
curl -s "https://api.pandadoc.com/public/v1/documents/<document-id>" --header "Authorization: API-Key $PANDADOC_TOKEN" | jq '{id, name, status, date_modified}'
4. Create Document from Template
Write to /tmp/pandadoc_create_from_template.json:
{
"name": "Sample Contract",
"template_uuid": "<template-id>",
"recipients": [
{
"email": "jane@example.com",
"first_name": "Jane",
"last_name": "Doe",
"role": "Client"
}
],
"tokens": [
{ "name": "Client.FirstName", "value": "Jane" },
{ "name": "Client.Company", "value": "Acme Corp" }
],
"fields": {
"Amount": { "value": "5000" }
},
"metadata": {
"source": "vm0"
},
"tags": ["api-created"]
}
curl -s -X POST "https://api.pandadoc.com/public/v1/documents" --header "Authorization: API-Key $PANDADOC_TOKEN" --header "Content-Type: application/json" -d @/tmp/pandadoc_create_from_template.json | jq '{id, name, status, uuid}'
Document creation is asynchronous. Poll the status endpoint until it is document.draft before sending.
5. Create Document from a PDF URL
Write to /tmp/pandadoc_create_from_pdf.json:
{
"name": "Contract from PDF",
"url": "https://example.com/contract.pdf",
"recipients": [
{
"email": "jane@example.com",
"first_name": "Jane",
"last_name": "Doe",
"role": "Client"
}
],
"parse_form_fields": false,
"fields": {},
"tags": ["api-created"]
}
curl -s -X POST "https://api.pandadoc.com/public/v1/documents" --header "Authorization: API-Key $PANDADOC_TOKEN" --header "Content-Type: application/json" -d @/tmp/pandadoc_create_from_pdf.json | jq '{id, name, status}'
6. Send Document for Signature
Poll GET /documents/<document-id> until status == "document.draft", then send.
Write to /tmp/pandadoc_send.json:
{
"message": "Please review and sign this document.",
"subject": "Please sign: Sample Contract",
"silent": false
}
curl -s -X POST "https://api.pandadoc.com/public/v1/documents/<document-id>/send" --header "Authorization: API-Key $PANDADOC_TOKEN" --header "Content-Type: application/json" -d @/tmp/pandadoc_send.json | jq '{id, name, status}'
Set silent: true to skip email notifications (you deliver the sign link yourself).
7. Download Signed PDF
Downloads the PDF (signed if completed, current state otherwise).
curl -s "https://api.pandadoc.com/public/v1/documents/<document-id>/download" --header "Authorization: API-Key $PANDADOC_TOKEN" --output /tmp/signed_document.pdf
Add ?watermark=true to include the unsigned watermark on in-progress documents.
8. Create a Signing Session Link
Generate a short-lived (default 900s) direct signing URL for a recipient. Replace <document-id> and provide the recipient's email.
Write to /tmp/pandadoc_session.json:
{
"recipient": "jane@example.com",
"lifetime": 900
}
curl -s -X POST "https://api.pandadoc.com/public/v1/documents/<document-id>/session" --header "Authorization: API-Key $PANDADOC_TOKEN" --header "Content-Type: application/json" -d @/tmp/pandadoc_session.json | jq '{id, expires_at}'
Open the signing page at https://app.pandadoc.com/s/<session-id>.
9. Change Document Status
Mark a document as paid, for example. Valid transitions depend on current status.
Write to /tmp/pandadoc_status.json:
{
"status": 2
}
Status codes: 2 = paid (only valid when status is document.completed).
curl -s -X PATCH "https://api.pandadoc.com/public/v1/documents/<document-id>/status" --header "Authorization: API-Key $PANDADOC_TOKEN" --header "Content-Type: application/json" -d @/tmp/pandadoc_status.json
10. Delete Document
curl -s -X DELETE "https://api.pandadoc.com/public/v1/documents/<document-id>" --header "Authorization: API-Key $PANDADOC_TOKEN"
Templates
List Templates
Filter by tag, q (name search), folder_uuid. Paginate with page and count (max 100).
curl -s "https://api.pandadoc.com/public/v1/templates?count=20" --header "Authorization: API-Key $PANDADOC_TOKEN" | jq '.results[] | {id, name, date_created, date_modified}'
Template Details
Replace <template-id>.
curl -s "https://api.pandadoc.com/public/v1/templates/<template-id>/details" --header "Authorization: API-Key $PANDADOC_TOKEN" | jq '{id, name, content_placeholders, fields, pricing, roles, tokens}'
Use roles[].name when constructing recipients[].role in the create-from-template body.
Contacts
List Contacts
curl -s "https://api.pandadoc.com/public/v1/contacts" --header "Authorization: API-Key $PANDADOC_TOKEN" | jq '.[] | {id, email, first_name, last_name, company}'
Create Contact
Write to /tmp/pandadoc_contact.json:
{
"email": "new.contact@example.com",
"first_name": "New",
"last_name": "Contact",
"company": "Acme Corp",
"job_title": "Procurement"
}
curl -s -X POST "https://api.pandadoc.com/public/v1/contacts" --header "Authorization: API-Key $PANDADOC_TOKEN" --header "Content-Type: application/json" -d @/tmp/pandadoc_contact.json | jq '{id, email, first_name, last_name}'
Get / Update / Delete Contact
curl -s "https://api.pandadoc.com/public/v1/contacts/<contact-id>" --header "Authorization: API-Key $PANDADOC_TOKEN"
Update via PATCH — write partial fields to /tmp/pandadoc_contact_patch.json:
{
"job_title": "VP Procurement"
}
curl -s -X PATCH "https://api.pandadoc.com/public/v1/contacts/<contact-id>" --header "Authorization: API-Key $PANDADOC_TOKEN" --header "Content-Type: application/json" -d @/tmp/pandadoc_contact_patch.json
curl -s -X DELETE "https://api.pandadoc.com/public/v1/contacts/<contact-id>" --header "Authorization: API-Key $PANDADOC_TOKEN"
Document Status Values
| Status | Meaning |
|---|---|
document.uploaded |
Document created but still processing (not yet draft) |
document.draft |
Ready to send |
document.sent |
Sent to recipients, awaiting signatures |
document.viewed |
Opened by at least one recipient |
document.waiting_approval |
Pending internal approval |
document.approved |
Internally approved |
document.rejected |
Internally rejected |
document.waiting_pay |
Completed, awaiting payment |
document.paid |
Paid |
document.completed |
All recipients signed |
document.voided |
Voided |
document.declined |
Declined by a recipient |
document.expired |
Past expiration date |
document.external_review |
Sent to external reviewer |
Guidelines
- The
Authorizationheader uses theAPI-Keyprefix, notBearer. Bearer is only for OAuth access tokens. - Document creation is asynchronous — always poll
GET /documents/<id>until status isdocument.draftbefore calling/send. - For Production API keys you need a paid plan with API access; Sandbox keys are free but documents have no legal validity.
- UUIDs (
id,uuid,template_uuid) are dynamic — capture them from responses rather than hard-coding. countis capped at 100 per page on list endpoints; paginate withpage(1-indexed).- Session links (
/documents/<id>/session) expire inlifetimeseconds (default 900). Request a new one when expired. - Rate limits apply per workspace — on
429, back off exponentially and retry.