Learn how to build production-grade unattended bots that populate PDF AcroForm fields using PDFtk and iTextSharp, apply certificate-based digital signatures, and route completed documents to structured network share locations — all without human intervention and with robust per-record error handling.

Picture this: your accounts payable team processes 400 vendor contracts a month. Each one requires pulling data from your ERP, populating a standardized PDF agreement, getting a digital signature applied, and routing the completed file to the appropriate network folder — organized by vendor category and fiscal month. Right now, a coordinator spends two days on this every billing cycle, manually copy-pasting fields into Adobe Acrobat, saving files one by one, and dragging them into the right folder. It is tedious, error-prone, and frankly an embarrassing use of a skilled person's time.
Power Automate Desktop can handle this entire workflow unattended — pulling data from a source system, driving a PDF application to populate every field, triggering a certificate-based digital signature, saving the completed document, and routing it to the right network location — all while the coordinator is doing something more valuable. But making it production-ready requires understanding how PDF applications expose their UI, how digital signature workflows differ depending on whether you're using Acrobat, a web-based signing tool, or a scripted approach via PDFtk or iTextSharp, and how to build recovery logic that prevents a single bad record from corrupting an entire batch run.
By the end of this lesson, you will have the architecture, the concrete action sequences, and the error-handling patterns to deploy this workflow as a fully unattended bot on a locked-down Windows server.
What you'll learn:
This lesson assumes you are already comfortable with the Power Automate Desktop environment. You should have experience building multi-step flows, understand how UI elements and selectors work in Power Automate Desktop, and have deployed or at minimum configured an unattended run machine. You should also know the fundamentals of attended vs. unattended RPA run modes and understand why session isolation matters in unattended contexts.
Familiarity with PowerShell basics will help significantly for the scripting sections, since we will be using PowerShell to drive PDFtk and handle file operations that are more reliable than pure UI clicks.
On the tooling side, you will need one of the following installed on your automation machine: Adobe Acrobat Standard or Pro (not Reader — Reader cannot save filled forms), or a command-line PDF tool such as PDFtk Server or iTextSharp via a .NET script. We will cover both paths.
Before you write a single action, you need to make a decision that will shape your entire workflow architecture: are you going to automate a PDF application's UI, use a scripting engine to manipulate PDFs programmatically, or combine both approaches?
Each option has meaningful trade-offs.
UI Automation via Adobe Acrobat is the most accessible approach. Acrobat exposes its form fields as proper UI elements that Power Automate Desktop can interact with using its PDF-specific actions or standard UI automation. The advantage is that you do not need programming knowledge and the workflow is visible and debuggable. The disadvantage is that Acrobat's UI is notoriously fragile — field selectors break between minor version updates, popup dialogs appear unpredictably, and in unattended sessions where no display adapter is present, rendering anomalies can cause the UI to not behave as expected.
Scripted PDF manipulation via PDFtk or PowerShell is more robust for unattended runs. PDFtk Server is a free, command-line tool that can populate AcroForm fields using FDF (Forms Data Format) files and flatten/stamp the result. Because it does not require a GUI, it works perfectly in headless server sessions. The downside is that it cannot apply certificate-based digital signatures — it can only populate fields and generate output PDFs.
Hybrid approach — script the field population, then open the completed PDF in Acrobat only to apply the signature — is often the best production architecture. You get the reliability of scripted field filling with the full PKI signature capability of Acrobat.
Key insight
In unattended runs on Windows Server machines, Acrobat sometimes fails to render its UI correctly because no physical monitor is attached. Always test your Acrobat UI automation on a machine configured identically to your production bot machine, including whether it runs in a remote desktop session or a virtual desktop. The attended vs. unattended RPA article explains session types in detail — this matters significantly for PDF UI automation.
A well-structured PDF automation flow has three clear layers: data retrieval, document processing, and file routing. Conflating these into a single monolithic sequence makes debugging painful.
Start by designing your main flow with subflows. Create a subflow called GetPendingContracts, a subflow called FillAndSignDocument, and a subflow called RouteCompletedFile. The main flow calls them in sequence inside a loop. This separation means if the signature step fails, you can test it in isolation without re-running data retrieval.
For this lesson, imagine your data source is a CSV file that the ERP system exports nightly to a shared network path. Each row contains: VendorID, VendorName, ContractDate, Amount, Category, SignatoryName, and OutputFolder.
Read this file at the start of your main flow:
# Action: Read text from file
File path: \\fileserver\rpa-inbox\pending_contracts.csv
Store content in: %CSVContent%
# Action: Convert CSV text to data table
CSV text: %CSVContent%
Has header row: True
Store result in: %ContractTable%
If you need a deeper reference on parsing CSVs in PAD, the CSV and text file handling guide covers delimiter edge cases and encoding issues you will encounter with vendor data.
Now structure your main loop:
For each CurrentRow in %ContractTable%
Set variable %ProcessingStatus% to 'Pending'
On block error (label: RecordError)
Set variable %ProcessingStatus% to 'Failed'
# Log error, continue to next record
Run subflow: LogFailure
End on block error
Run subflow: FillAndSignDocument
Run subflow: RouteCompletedFile
Set variable %ProcessingStatus% to 'Completed'
End for each
Warning
Never let an unhandled exception in one iteration abort the entire loop. Wrapping each record in an On block error block at the iteration level is mandatory for batch processing. The error handling patterns article covers how to structure these correctly, including how to capture error details to a log file without losing your loop position.
PDFtk Server is the workhorse for programmatic AcroForm filling. Download and install it on your bot machine at C:\Program Files (x86)\PDFtk Server\bin\pdftk.exe.
The workflow is: generate an FDF file containing the field values, then use PDFtk to merge it with your template PDF and produce a filled output.
An FDF file is a simple text structure. Here is what a minimal one looks like for a vendor contract:
%FDF-1.2
1 0 obj
<< /FDF << /Fields [
<< /T (VendorName) /V (Acme Industrial Supply) >>
<< /T (ContractDate) /V (June 15, 2025) >>
<< /T (ContractAmount) /V ($47,500.00) >>
<< /T (SignatoryName) /V (Margaret Chen) >>
<< /T (VendorID) /V (VND-00842) >>
<< /T (Category) /V (Raw Materials) >>
] >> >>
endobj
trailer << /Root 1 0 R >>
%%EOF
You will generate this file dynamically in your PAD flow using a PowerShell script action. Here is how to build that script inside the PowerShell scripting action:
# PowerShell script - called from "Run PowerShell script" action
# Input variables passed from PAD:
# %VendorName%, %ContractDate%, %Amount%, %SignatoryName%, %VendorID%, %Category%
# %TempFolder%, %TemplatePath%, %OutputPath%
param(
[string]$VendorName,
[string]$ContractDate,
[string]$Amount,
[string]$SignatoryName,
[string]$VendorID,
[string]$Category,
[string]$TempFolder,
[string]$TemplatePath,
[string]$OutputPath
)
# Build FDF content
$FDFContent = @"
%FDF-1.2
1 0 obj
<< /FDF << /Fields [
<< /T (VendorName) /V ($VendorName) >>
<< /T (ContractDate) /V ($ContractDate) >>
<< /T (ContractAmount) /V ($Amount) >>
<< /T (SignatoryName) /V ($SignatoryName) >>
<< /T (VendorID) /V ($VendorID) >>
<< /T (Category) /V ($Category) >>
] >> >>
endobj
trailer << /Root 1 0 R >>
%%EOF
"@
# Write FDF to temp file
$FDFPath = Join-Path $TempFolder "temp_$VendorID.fdf"
[System.IO.File]::WriteAllText($FDFPath, $FDFContent, [System.Text.Encoding]::Latin1)
# Run PDFtk to fill the form
$PDFtkPath = "C:\Program Files (x86)\PDFtk Server\bin\pdftk.exe"
$args = "`"$TemplatePath`" fill_form `"$FDFPath`" output `"$OutputPath`" flatten"
$result = Start-Process -FilePath $PDFtkPath -ArgumentList $args -Wait -PassThru -NoNewWindow
# Clean up FDF
Remove-Item $FDFPath -ErrorAction SilentlyContinue
# Return exit code
exit $result.ExitCode
In your PAD flow, call this script with the Run PowerShell script action and pass each field value from %CurrentRow%:
Run PowerShell script
Script: [the script above stored in a file or inline]
Script parameters:
-VendorName "%CurrentRow['VendorName']%"
-ContractDate "%CurrentRow['ContractDate']%"
-Amount "%CurrentRow['Amount']%"
-SignatoryName "%CurrentRow['SignatoryName']%"
-VendorID "%CurrentRow['VendorID']%"
-Category "%CurrentRow['Category']%"
-TempFolder "C:\RPA\Temp"
-TemplatePath "\\fileserver\templates\vendor_contract_template.pdf"
-OutputPath "C:\RPA\Temp\%CurrentRow['VendorID']%_filled.pdf"
PowerShell output stored in: %PSOutput%
Script exit code stored in: %PSExitCode%
Then check the exit code immediately:
If %PSExitCode% <> 0 Then
Throw error: 'PDFtk failed for VendorID %CurrentRow['VendorID']% with exit code %PSExitCode%'
End If
Tip
The flatten argument in the PDFtk command converts all form fields to static text content after filling, which produces a cleaner output and prevents downstream recipients from accidentally editing the values. However, if you need the signature step to recognize a specific signature field in the PDF, do not flatten before signing — flatten only after the signature is applied. Use the output command without flatten for the intermediate document, then flatten the final signed version.
One of the most common early failures in this workflow is mismatched field names. Your PowerShell FDF must use the exact internal field names from the PDF template — not the label text printed next to the field on the form.
PDFtk can dump a PDF's field structure for you. Run this in PowerShell or a command prompt:
pdftk "\\fileserver\templates\vendor_contract_template.pdf" dump_data_fields output fields.txt
The output file will contain blocks like this:
---
FieldType: Text
FieldName: VendorName
FieldFlags: 0
FieldValue:
FieldJustification: Left
---
FieldType: Text
FieldName: ContractDate
FieldFlags: 0
FieldValue:
FieldJustification: Left
---
FieldType: Signature
FieldName: AuthorizedSignature
FieldFlags: 0
FieldValue:
---
Note the FieldType: Signature entry. That is your digital signature field. PDFtk cannot fill it — that requires Acrobat. Write down its exact name (AuthorizedSignature in this example) because you will need it when you automate Acrobat for the signing step.
Note
If dump_data_fields shows no fields at all, your PDF may not be an AcroForm — it might be a scanned or "flat" PDF with no underlying field structure. In that case, you cannot use FDF-based population. You would need to either obtain a properly constructed AcroForm template from the document owner, use OCR to locate field positions and type into them as if they were text boxes, or investigate Acrobat's fillForm JavaScript API. The OCR extraction article is relevant for the identification step even if you are not using OCR for the filling.
Certificate-based digital signatures require Acrobat Pro (Standard works for basic certificates). The signing process in Acrobat involves opening the document, navigating to the signature field, selecting a certificate from the Windows certificate store, and confirming. This is inherently UI-driven for PKI certificates.
First, open the filled (but not yet flattened) PDF in Acrobat:
# Action: Open application
Application path: C:\Program Files\Adobe\Acrobat DC\Acrobat\Acrobat.exe
Window style: Normal
Store process into: %AcrobatProcess%
# Wait for Acrobat to fully load
Wait 3 seconds
# Action: Open file in Acrobat via menu
# Use keyboard shortcut Ctrl+O
Send keys to window: [Title: Adobe Acrobat] Ctrl+O
# Handle file picker dialog
# Action: Populate dialog field
# (The Windows Open dialog appears)
Set text in field [Name: File name] to: C:\RPA\Temp\%CurrentRow['VendorID']%_filled.pdf
Click button [Name: Open]
# Wait for document to open and render
Wait 2 seconds
Warning
Acrobat's startup behavior is affected by first-run dialogs, update notifications, and subscription prompts that may appear unexpectedly in unattended sessions. Before deploying to production, log in interactively to the bot machine under the service account, launch Acrobat, dismiss all one-time dialogs, and disable automatic updates via Edit > Preferences > Updater. Failing to do this will cause the bot to encounter unexpected popups during unattended runs. This is a common cause of signature step failures that only appear in production, never in testing.
Now navigate to the signature field. Acrobat exposes form fields as clickable UI elements. Use the Click UI element action targeting the signature field by its accessibility name:
# Click the signature field
Click UI element: [Process: Acrobat.exe] [Name: AuthorizedSignature] [Role: signature]
If Acrobat's accessibility tree does not expose the signature field reliably (which happens with older PDF structures), fall back to the Fill & Sign approach via the menu:
# Navigate via menu: Tools > Certificates > Digitally Sign
Click UI element: [Process: Acrobat.exe] [Name: Tools] [Role: menu item]
Click UI element: [Process: Acrobat.exe] [Name: Certificates] [Role: menu item]
Click UI element: [Process: Acrobat.exe] [Name: Digitally Sign] [Role: menu item]
# Acrobat will prompt you to draw a signature rectangle or select a field
# If a dialog appears asking to select the signature field:
Click UI element: [Process: Acrobat.exe] [Name: OK] [Role: button]
When Acrobat opens the digital signature dialog, you need to select the certificate and confirm. In the Sign with a Digital ID dialog:
# Select the certificate from the list
# The certificate list is a UI tree/list - select by name
Click UI element: [Process: Acrobat.exe] [Name: CN=RPA Bot Signing, OU=Finance Ops] [Role: list item]
Click UI element: [Process: Acrobat.exe] [Name: Continue] [Role: button]
# Sign and Save dialog appears
# Set the output path
Set text in field [Name: File Name]: C:\RPA\Temp\%CurrentRow['VendorID']%_signed.pdf
Click UI element: [Process: Acrobat.exe] [Name: Save] [Role: button]
The certificate (CN=RPA Bot Signing, OU=Finance Ops) needs to be installed in the Windows certificate store of the service account under which the unattended bot runs. This is a one-time setup step performed by your IT or PKI team. See handling credentials securely in desktop flows for the broader context of credential and certificate management in bot accounts.
After saving, close Acrobat before processing the next record to prevent memory buildup across hundreds of iterations:
# Close Acrobat
Close process: %AcrobatProcess%
Wait 1 second
If you are signing hundreds of documents per run, the UI-driven Acrobat approach will be slow and brittle. A more scalable approach uses Acrobat's JavaScript-based batch processing via command-line invocation, or iTextSharp for programmatic signing with a PFX certificate.
Here is how to apply a certificate-based signature programmatically using iTextSharp (iText 5, which has a free LGPL license for open source use — confirm licensing for your commercial context):
# PowerShell script using iTextSharp for digital signature
param(
[string]$InputPDF,
[string]$OutputPDF,
[string]$CertPFXPath,
[string]$CertPassword,
[string]$SignatureFieldName,
[string]$SignatoryName,
[string]$Reason,
[string]$Location
)
Add-Type -Path "C:\RPA\Libs\itextsharp.dll"
try {
$reader = New-Object iTextSharp.text.pdf.PdfReader($InputPDF)
$stamper = [iTextSharp.text.pdf.PdfStamper]::CreateSignature(
$reader,
[System.IO.File]::OpenWrite($OutputPDF),
[char]0
)
# Load certificate
$certBytes = [System.IO.File]::ReadAllBytes($CertPFXPath)
$store = New-Object Org.BouncyCastle.Pkcs.Pkcs12Store(
[System.IO.MemoryStream]::new($certBytes),
$CertPassword.ToCharArray()
)
$alias = ($store.Aliases | Select-Object -First 1)
$privateKey = $store.GetKey($alias).Key
$certChain = $store.GetCertificateChain($alias) | ForEach-Object { $_.Certificate }
# Configure signature appearance
$appearance = $stamper.SignatureAppearance
$appearance.SignDate = [System.DateTime]::Now
$appearance.SetVisibleSignature($SignatureFieldName)
$appearance.SignatureCreator = "Power Automate Desktop RPA Bot"
$appearance.Contact = $SignatoryName
$appearance.Reason = $Reason
$appearance.Location = $Location
$appearance.Acro6Layers = $true
# Sign
$signature = New-Object iTextSharp.text.pdf.security.MakeSignature
[iTextSharp.text.pdf.security.MakeSignature]::SignDetached(
$appearance,
[iTextSharp.text.pdf.security.BouncyCastleDigest]::new(),
@($privateKey, $certChain),
$null, $null, $null, 0,
[iTextSharp.text.pdf.security.CryptoStandard]::CMS
)
$stamper.Close()
$reader.Close()
exit 0
}
catch {
Write-Error $_.Exception.Message
exit 1
}
Key insight
The PFX certificate file path and password used in the script above should never be hardcoded or stored in plain text in your flow. Store the PFX path as an environment variable and retrieve the password from Azure Key Vault or the PAD credential store. The secure credential handling guide explains how to retrieve Key Vault secrets inside a desktop flow using the built-in connector actions.
This scripted approach eliminates all the Acrobat UI fragility and runs in approximately 1–3 seconds per document, compared to 8–15 seconds for the UI-driven path. For a 400-document batch, that is a difference of roughly 1 hour versus nearly 2.5 hours of wall-clock time.
After you have a completed, signed PDF, routing it correctly is where many bots cut corners and create file management nightmares. Good routing logic does three things: creates the correct destination folder structure if it does not exist, moves (not copies) the file to the destination, and writes a routing log entry.
The destination path in our scenario is constructed from the vendor category and fiscal month:
\\fileserver\contracts\completed\{Category}\{FiscalYear}\{FiscalMonth}\{VendorID}_{VendorName}_{ContractDate}.pdf
Build this path in PAD using variable concatenation:
# Parse fiscal period from ContractDate
Run PowerShell script:
$date = [DateTime]::Parse("%CurrentRow['ContractDate']%")
Write-Output $date.Year
Store output in: %FiscalYear%
Run PowerShell script:
$date = [DateTime]::Parse("%CurrentRow['ContractDate']%")
Write-Output $date.ToString("MM-MMMM")
Store output in: %FiscalMonth%
# Sanitize vendor name for use in filename (remove special chars)
Run PowerShell script:
$name = "%CurrentRow['VendorName']%"
$safe = $name -replace '[\\/:*?"<>|]', '_'
Write-Output $safe
Store output in: %SafeVendorName%
# Build destination folder path
Set variable %DestFolder% to:
\\fileserver\contracts\completed\%CurrentRow['Category']%\%FiscalYear%\%FiscalMonth%
# Build destination file name
Set variable %DestFileName% to:
%CurrentRow['VendorID']%_%SafeVendorName%_%CurrentRow['ContractDate']%.pdf
# Create destination folder if it doesn't exist
# Action: Create folder
Folder to create: %DestFolder%
The Create folder action in PAD is idempotent — it does not throw an error if the folder already exists, which makes it safe to call on every iteration.
Now move the signed file:
# Action: Move file
File to move: C:\RPA\Temp\%CurrentRow['VendorID']%_signed.pdf
Destination folder: %DestFolder%
If file exists: Overwrite
New file name: %DestFileName%
After the move, write a log entry. The file and folder operations article covers the full set of file manipulation actions available in PAD, but for logging, a simple append-to-CSV approach works well:
# Build log line
Set variable %LogLine% to:
%CurrentRow['VendorID']%,%SafeVendorName%,%CurrentRow['ContractDate']%,%DestFolder%\%DestFileName%,%DateTime.Now%,Completed
# Append to log file
# Action: Write text to file (append mode)
File path: C:\RPA\Logs\contract_processing_%Date%.csv
Text to write: %LogLine%
Append newline: True
Append to existing content: True
Tip
Always use a date-stamped log filename rather than a static one like processing_log.csv. In unattended production environments, a single log file accumulates indefinitely and becomes difficult to analyze. Date-stamped files let you archive logs daily and keep your monitoring queries fast. A filename like contract_processing_2025-06-15.csv is immediately scannable in Windows Explorer and easy to query by date range.
Unattended bots run under a service account in a session that may be locked or headless. Several PDF-specific issues arise in these conditions that do not appear during attended testing.
Font rendering issues in Acrobat: In sessions without a physical display adapter or with a remote desktop display set to 8-bit color, Acrobat may render form fields incorrectly or fail to display at all. Use a virtual display driver (such as the Microsoft Basic Display Adapter configured through Device Manager) or ensure your RDS/VM environment provides adequate display capability. Check your machine configuration in the machine group management article for best practice session configurations.
Acrobat activation dialogs: Acrobat's Creative Cloud licensing may prompt for reactivation if the service account has not had an interactive session recently. Suppress this by ensuring the service account logs in interactively at least once per 30 days (or per your Adobe Enterprise licensing terms), or by deploying an Adobe enterprise serialized package that does not require per-user activation.
File locking: If your flow crashes mid-run, a partially written PDF may be left in the temp folder with a file lock. Add a pre-run cleanup step:
# At the start of each iteration, clean up any prior temp files for this VendorID
# Action: Get files in folder
Folder: C:\RPA\Temp
File filter: %CurrentRow['VendorID']%*.pdf
Store in: %TempFiles%
For each TempFile in %TempFiles%
Delete file: %TempFile%
End for each
Network path availability: Before the routing step, verify the network share is accessible. A network blip should not fail a completed document that is already signed:
Run PowerShell script:
Test-Path "\\fileserver\contracts\completed"
Store output in: %NetworkAvailable%
If %NetworkAvailable% = 'False' Then
# Retry with exponential backoff
Wait 30 seconds
Run PowerShell script: Test-Path "\\fileserver\contracts\completed"
Store output in: %NetworkAvailable%
If %NetworkAvailable% = 'False' Then
# Copy to local fallback, log for manual routing
Copy file: C:\RPA\Temp\%CurrentRow['VendorID']%_signed.pdf
Destination: C:\RPA\NetworkFallback\
Append to log: "NETWORK_UNAVAILABLE - manual routing required"
End If
End If
For enterprise deployments, you will not trigger this desktop flow manually — it will be called from a scheduled cloud flow that passes the input parameters and receives status back. The cloud flow triggering article covers the full connection setup, but here is the specific pattern for this PDF workflow.
Define your desktop flow's input variables in the flow designer:
Input variables:
- SourceCSVPath (Text) - path to the data file for this run
- TemplatePDFPath (Text) - which PDF template to use
- OutputBasePath (Text) - root network path for completed files
- RunID (Text) - unique identifier for this batch run (for logging)
Output variables:
- TotalProcessed (Number)
- TotalFailed (Number)
- RunLogPath (Text)
This design means you can run the same desktop flow against different contract templates by just changing which cloud flow triggers it and what template path it passes. A vendor onboarding template and a service agreement template both use the same bot logic.
In your PAD flow, increment counters in the main loop:
Set variable %TotalProcessed% to 0
Set variable %TotalFailed% to 0
For each CurrentRow in %ContractTable%
On block error
Increment %TotalFailed% by 1
End on block error
Run subflow: FillAndSignDocument
Run subflow: RouteCompletedFile
Increment %TotalProcessed% by 1
End for each
At the end of the flow, the cloud flow picks up these output variables and can write them to a SharePoint list, send a summary Teams message, or trigger a conditional alert if the failure count exceeds a threshold. This gives your operations team visibility without having to log into the bot machine.
Not every organization uses certificate-based PDF signatures. Many use DocuSign, Adobe Sign, or similar SaaS platforms. If your process sends documents through DocuSign for external signatories before final filing, your desktop flow handles the pre-signing population and then triggers the DocuSign envelope via API from within PAD.
The cleanest approach is to call the DocuSign API from a PowerShell script action within PAD, then poll for completion:
# PowerShell to create a DocuSign envelope and get the envelope ID
param(
[string]$AccessToken,
[string]$AccountID,
[string]$FilePath,
[string]$SignatoryEmail,
[string]$SignatoryName,
[string]$SignatureFieldName
)
$BaseURL = "https://na3.docusign.net/restapi/v2.1/accounts/$AccountID"
$Headers = @{
"Authorization" = "Bearer $AccessToken"
"Content-Type" = "application/json"
}
# Read file and base64 encode it
$FileBytes = [System.IO.File]::ReadAllBytes($FilePath)
$FileBase64 = [Convert]::ToBase64String($FileBytes)
$FileName = [System.IO.Path]::GetFileName($FilePath)
$Body = @{
emailSubject = "Contract Signature Required"
documents = @(
@{
documentBase64 = $FileBase64
name = $FileName
fileExtension = "pdf"
documentId = "1"
}
)
recipients = @{
signers = @(
@{
email = $SignatoryEmail
name = $SignatoryName
recipientId = "1"
tabs = @{
signHereTabs = @(
@{
documentId = "1"
pageNumber = "1"
xPosition = "200"
yPosition = "600"
}
)
}
}
)
}
status = "sent"
} | ConvertTo-Json -Depth 10
$Response = Invoke-RestMethod -Uri "$BaseURL/envelopes" -Method POST -Headers $Headers -Body $Body
Write-Output $Response.envelopeId
The envelope ID returned by this script becomes a PAD variable. You can then build a polling loop that checks envelope status every 5 minutes until it reaches completed, after which the desktop flow downloads the signed document directly from DocuSign's API and routes it to the network location.
Note
DocuSign API access tokens expire after 8 hours. If your batch process runs longer than that (possible with very large document sets), implement token refresh logic in your PowerShell scripts. Store the refresh token securely — again, use the credential store or Azure Key Vault, not a plaintext variable. For the sake of keeping this lesson focused on PAD patterns rather than OAuth flows, treat the access token as an input variable passed from your cloud flow, where token management is much more straightforward using certified connectors.
Build this workflow against a real PDF template and test it end-to-end. Here are the steps to complete:
Preparation:
CompanyName, InvoiceDate, InvoiceAmount, ApproverName, DepartmentCode.dump_data_fields to confirm the exact field names in your template.Flow construction:
4. Build the main flow architecture with subflows as described in Steps 1 and the data loop.
5. Implement the PDFtk-based field population using the PowerShell script pattern from Step 2.
6. Add the file routing logic from Step 6, routing to a local folder structure (C:\RPA\Exercise\Output\{DepartmentCode}\{Year}\{Month}\) rather than a network share.
7. Test the flow in attended mode first, watching each step execute.
Verification:
8. Open each output PDF and confirm all fields are populated correctly.
9. Open the log CSV and confirm every row has a Completed status.
10. Deliberately corrupt one row in your CSV (blank out the InvoiceDate field) and confirm the error handler catches it, logs Failed, and continues processing the remaining rows.
Stretch goal:
11. Add a PowerShell-based iTextSharp signature step using a self-signed certificate generated with New-SelfSignedCertificate in PowerShell. Export it to a PFX file and use it to sign the output PDFs. Verify the signature appears in Acrobat Reader's signature panel (it will show as "not trusted" since it is self-signed, which is expected).
Mistake: Field names are case-sensitive and must match exactly. FDF field names are case-sensitive. VendorName and vendorname are different fields. If your filled PDF comes out blank for some fields, run dump_data_fields again and compare character-by-character.
Mistake: Not waiting for Acrobat to fully render before sending UI actions. Acrobat loads asynchronously — the window appears before the document is interactive. Insert Wait for UI element to appear targeting the document's title in Acrobat's toolbar rather than using a fixed Wait delay. Fixed delays break under load.
Mistake: Using Acrobat's UI to fill fields instead of PDFtk in production. Acrobat's UI field interaction is fragile across versions. One Acrobat update in October can silently break a flow that worked perfectly in September. Always prefer the scripted path for field population and use Acrobat UI automation only for what scripting cannot do — applying existing certificate-based signatures.
Mistake: Not handling the flatten step correctly. If you forget to flatten the final signed document, recipients can edit the form fields and then re-save — potentially obscuring the fact that the underlying form data was changed after signature. Always flatten signed documents before distribution.
Troubleshooting: PDFtk produces output with garbled characters. This is almost always an encoding issue with the FDF file. FDF uses Latin-1 (ISO-8859-1) encoding by default. If your data contains UTF-8 characters (accented letters, Chinese characters, etc.), you need to either encode them in Unicode FDF format or pre-convert them. The [System.IO.File]::WriteAllText($path, $content, [System.Text.Encoding]::Latin1) call in the script handles ASCII-range Latin characters. For full Unicode support, switch to XFDF format (the XML variant of FDF), which PDFtk also supports.
Troubleshooting: Acrobat prompts for a password or shows a protected document warning. Some PDF templates are secured against filling or modification. Use PDFtk's input_pw option to unlock them if you have the owner password, or request an unlocked template from the document owner. Never circumvent security on documents you do not own.
Troubleshooting: Unattended run fails at the Acrobat launch step but works attended. Check whether the service account has Acrobat licensed and activated. Acrobat's per-user licensing model means each Windows user profile must have its own active license. The service account used by the unattended bot is a separate user profile and needs its own activation. This is frequently overlooked in initial deployment.
Troubleshooting: Network share move fails intermittently. This is typically a timing issue with distributed file system replication or firewall reconnection after session idle. Wrap the move action in a retry loop with a 5-second delay between attempts, up to 3 retries. If all retries fail, copy to the local fallback folder and log for manual intervention.
You now have a production-grade architecture for automated PDF form filling and digital signature workflows in Power Automate Desktop. The key design decisions you have made in this lesson are:
For your next area of study, consider how to scale this pattern across multiple bot machines simultaneously. When you have 2,000 contracts to process at month-end, a single bot taking 3 seconds per document means nearly 2 hours of processing time. A machine group with four bots running concurrently drops that to under 30 minutes. The enterprise-scale unattended deployment guide covers how to partition work queues so multiple bots pick up records without collision, which is the natural next layer on top of what you have built here.
Also consider adding post-processing validation: after every document is signed and routed, a second lightweight PAD flow can reopen each output PDF and programmatically verify that the expected fields contain non-empty values and that the signature panel shows a valid signature. This kind of in-process quality gate catches issues like a field that populated silently with an empty string because a data source row had a null value — a class of bug that is otherwise invisible until an auditor picks it up months later.
Power Automate Desktop & RPA