Digital Signature API for Dynamics 365 Invoice Signing: The Complete Integration Guide
Step-by-step guide to automating invoice signing in Microsoft Dynamics 365 with the SignLift digital signature API: auth, request anatomy, Power Automate, Azure Functions, X++, LTV timestamping, and bulk signing.
By Anirudha Narkar

Table of contents▾
- Why invoice signing is the hidden bottleneck in Dynamics 365
- What you will build
- Before you start
- How the SignLift API works
- Authentication
- The three endpoints
- Anatomy of a /sign-pdf request
- Document source and output
- Choosing the signature type (and why invoices need LTV)
- Placing the signature: anchor to text, do not hardcode pixels
- Signature appearance
- The response
- Pick your integration pattern
- Pattern A — Power Automate (recommended starting point)
- Pattern B — Azure Function (C#) for bulk signing
- Pattern C — X++ directly from Finance & Operations
- Make it production-grade
- Error handling, retries, and idempotency
- Security
- Is it legally valid in India?
- Test before you go live
- Conclusion
On this page
- Why invoice signing is the hidden bottleneck in Dynamics 365
- What you will build
- Before you start
- How the SignLift API works
- Authentication
- The three endpoints
- Anatomy of a /sign-pdf request
- Document source and output
- Choosing the signature type (and why invoices need LTV)
- Placing the signature: anchor to text, do not hardcode pixels
- Signature appearance
- The response
- Pick your integration pattern
- Pattern A — Power Automate (recommended starting point)
- Pattern B — Azure Function (C#) for bulk signing
- Pattern C — X++ directly from Finance & Operations
- Make it production-grade
- Error handling, retries, and idempotency
- Security
- Is it legally valid in India?
- Test before you go live
- Conclusion
Why invoice signing is the hidden bottleneck in Dynamics 365
Every finance team running Microsoft Dynamics 365 hits the same wall at month-end. Invoices are generated cleanly inside the ERP — and then someone exports each one to PDF, opens it, applies a signature, saves it, and re-uploads it before it can reach the customer. For a batch of 500 invoices, that is an afternoon gone, and an afternoon of error-prone manual work standing between a posted invoice and the cash it represents.
This guide shows you how to remove that step entirely. We will wire Dynamics 365 to the SignLift digital signature API so that the moment an invoice PDF exists, it comes back digitally signed — in seconds, with a tamper-evident PKI signature, an audit-ready timestamp, and zero human clicks.
This is a hands-on integration guide, not a brochure. You get the exact request anatomy, three integration patterns (no-code and code-first), invoice-specific signature placement, and the compliance details that matter for statutory retention in India.
What you will build
The end-to-end flow is simple to reason about:
1Dynamics 365 ──(1) invoice PDF generated──▶2 │3 │ (2) PDF (base64) + signing instructions4 ▼5SignLift API ──(3) POST /sign-pdf (X-API-Token)──▶6 PKI signing + timestamp / LTV7 │8 │ (4) signed PDF (base64) returned9 ▼10Store back in D365 / SharePoint + email to customer
The signing certificate lives server-side on SignLift — your Dynamics environment never has to hold a private key. You send a PDF plus a small JSON instruction set; you get a signed PDF back.
Before you start
You will need:
- A SignLift API token — the
X-API-Tokenvalue provisioned by SignSecure. Treat it as a secret. - A Dynamics 365 environment — Finance & Operations, Business Central, or Customer Engagement / Sales.
- A way to obtain the invoice PDF — SSRS / Print Management archive (F&O), report-to-PDF (Business Central), or a Dataverse note / attachment (CE).
- A place to call HTTP from — Power Automate, an Azure Function, a Logic App, or X++ (pick one; see the patterns below).
You do not need to generate, store, or manage the signing certificate yourself — SignLift holds the PKCS#12 (.pfx) certificate and signs on your behalf.
How the SignLift API works
Authentication
Every authenticated request carries a single header:
X-API-Token(required) — the API token provisioned by SignSecure.
Auth failures are explicit, which makes them easy to handle in a flow:
- Missing header →
401with{"status": false, "error": {"message": "Missing API token"}} - Invalid / expired token →
401with{"status": false, "error": {"message": "Invalid token"}} - Expired license →
403with{"status": false, "error": {"message": "License expired"}}
The three endpoints
GET /version— health check / version info. No auth.GET /check-token— verify the token and read its expiry. Auth required.POST /sign-pdf— sign a PDF document. Auth required.
Run /check-token from your monitoring before you ever push production traffic. It returns the token expiry so you can alert weeks before it lapses instead of discovering it during a month-end run:
1curl https://api.signsecure.in/check-token \2 -H "X-API-Token: $SIGNLIFT_TOKEN"
1{2 "status": true,3 "timestamp": "2026-03-21T15:17:16.623Z[UTC]",4 "tokenExpiry": "21-03-2027"5}
Anatomy of a /sign-pdf request
This is the heart of the integration. The request has two top-level objects: document (what to sign and where the result goes) and signature (how to sign it).
1{2 "document": {3 "source": { "type": "base64", "base64": "<invoice-pdf-base64>" },4 "output": { "type": "base64" }5 },6 "signature": {7 "type": "sign-ltv",8 "tsaUrl": "http://timestamp.digicert.com",9 "reason": "Invoice approved for issue",10 "location": "Mumbai, IN",11 "lockAfterSigning": true,12 "appearance": {13 "renderMode": "image-and-description",14 "imageBase64": "<signature-png-base64>",15 "signerName": "Acme Corp Pvt Ltd",16 "signerNameDisplay": "signed-by-with-name",17 "showTimestamp": true,18 "additionalText": "GSTIN: 27AAACA1234A1Z5"19 },20 "placements": [21 {22 "type": "text",23 "searchText": "Authorised Signatory",24 "position": "above",25 "gap": 6,26 "width": 180,27 "height": 6028 }29 ]30 }31}
Document source and output
The source can be inline base64 or an S3 object — and the output mirrors it:
base64— send the PDF in the request body. Simplest; ideal from Power Automate.s3— point at an object in a bucket via{ "bucket": "...", "key": "..." }. Ideal for large or high-volume batches so you are not shipping megabytes through your flow.- Output default is
{ "type": "base64" }, which returns the signed PDF in the response. Set it to S3 to have SignLift write the signed file straight to a bucket and return the path instead.
Choosing the signature type (and why invoices need LTV)
"sign"— a standard PKI signature. Verifiable as long as the certificate revocation info is reachable."sign-ltv"— Long-Term Validation. SignLift embeds the validation data (DSS/LTV) into the PDF so the signature stays verifiable for years, even after the signing certificate expires.
For invoices this is not optional fine print. Indian statute requires you to retain tax invoices for years (8 years under the Companies Act; GST records similarly long). A plain signature can become "validity unknown" once the certificate lifecycle moves on. Use sign-ltv. Add a tsaUrl (an RFC 3161 timestamp authority) and SignLift also embeds a trusted timestamp proving when the invoice was signed — exactly what an auditor wants to see.
Placing the signature: anchor to text, do not hardcode pixels
This is the single most useful feature for invoice automation, and where most naive integrations go wrong. You can place a signature at fixed coordinates:
1{ "type": "coordinates", "pages": [1], "x": 380, "y": 90, "width": 180, "height": 60 }
But invoices vary in length — a 1-page invoice and a 3-page invoice put the signature block in different places, so hardcoded coordinates drift. Instead, anchor to text that already prints on your template. Almost every invoice layout has an "Authorised Signatory" or "For <Company Name>" line. Tell SignLift to find it and place the signature relative to it:
1{2 "type": "text",3 "searchText": "Authorised Signatory",4 "position": "above",5 "gap": 6,6 "width": 180,7 "height": 608}
The text-placement fields:
searchText— text to find in the PDF (e.g. your signatory line).position— "above" or "below" the found text.gap— points of spacing between the text and the signature box.offsetX— horizontal nudge in points.pages— restrict the search to specific pages (defaults to all).
Now the signature lands in the right spot on every invoice regardless of how many line items it has — no template-specific math.
Signature appearance
renderMode controls the visual:
"description"— text only ("Digitally signed by … / Date … / Reason …")."image"— your signature or logo PNG only."image-and-description"— image beside the text. Recommended for invoices."background-image-and-description"— text drawn over a faded image (use a transparent PNG)."name-and-description"— name plus the standard description block.
Pair it with additionalText to surface compliance metadata right in the stamp — GSTIN, an internal approval ID, "Original for Recipient", etc. showTimestamp: true prints the signing time; lockAfterSigning: true flattens the document so no further edits are possible after signing.
The response
1{2 "status": true,3 "timestamp": "2026-03-21T15:17:16.623Z[UTC]",4 "document": { "content": "<base64-encoded signed PDF>" },5 "tokenExpiry": "21-03-2027"6}
Decode document.content and you have the signed invoice. (With S3 output, content is the s3://bucket/key path instead.) On failure you get status: false and a human-readable error.message — always branch on status, never just the HTTP code.
Pick your integration pattern
There is no single right way to call SignLift from Dynamics — it depends on volume and where your team is comfortable building.
- A. Power Automate — best for most teams and low-to-moderate volume. Lowest effort, native D365 connectors, fastest to ship.
- B. Azure Function / Logic App — best for high volume, event-driven signing with retries. Cleanest for bulk and easy Key Vault secrets.
- C. X++ (F&O in-process) — best when signing must be tightly coupled to posting logic. Highest effort; keep it asynchronous.
Pattern A — Power Automate (recommended starting point)
No servers, native Dynamics triggers. This is the path most finance teams should take first.
- Trigger. Use the F&O connector "When a business event occurs" on a customer-invoice event, or a Dataverse "When a row is added" trigger for D365 Sales. A scheduled trigger over recently posted invoices works for nightly catch-up.
- Get the invoice PDF. Fetch the generated PDF — from the F&O document archive, a SharePoint library, or the Dataverse attachment — into a "Get file content" step.
- Build the request body. Add an HTTP action and compose the JSON, encoding the PDF with the base64() expression (shown below).
- Parse and decode. Add "Parse JSON" on the response, check status, then convert the signed PDF back with base64ToBinary(body('HTTP')?['document']?['content']).
- Store and send. Write the signed PDF back to the invoice record / SharePoint and optionally email it to the customer.
1{2 "document": {3 "source": { "type": "base64", "base64": "@{base64(body('Get_file_content'))}" },4 "output": { "type": "base64" }5 },6 "signature": {7 "type": "sign-ltv",8 "tsaUrl": "http://timestamp.digicert.com",9 "reason": "Invoice approved for issue",10 "location": "@{variables('LegalEntityCity')}",11 "lockAfterSigning": true,12 "appearance": {13 "renderMode": "image-and-description",14 "signerName": "@{variables('CompanyName')}",15 "showTimestamp": true,16 "additionalText": "@{concat('GSTIN: ', variables('GSTIN'))}"17 },18 "placements": [19 { "type": "text", "searchText": "Authorised Signatory",20 "position": "above", "gap": 6, "width": 180, "height": 60 }21 ]22 }23}
HTTP action settings:
- Method — POST
- URI — https://api.signsecure.in/sign-pdf
- Headers — X-API-Token (from a secure environment variable / Key Vault) and Content-Type: application/json
That is the entire loop — and it now runs every time an invoice is posted, with no one touching a mouse.
Pattern B — Azure Function (C#) for bulk signing
When you are signing thousands of invoices, or you want robust retries and centralized secrets, put a thin Azure Function between Dynamics and SignLift. D365 raises a business event (or drops a message on Service Bus), the function signs, and writes the result back.
1public class SignLiftClient2{3 private readonly HttpClient _http;45 public SignLiftClient(HttpClient http, string apiToken)6 {7 _http = http;8 _http.BaseAddress = new Uri("https://api.signsecure.in/");9 _http.DefaultRequestHeaders.Add("X-API-Token", apiToken);10 }1112 public async Task<byte[]> SignInvoiceAsync(byte[] pdfBytes, string gstin, string city)13 {14 var request = new15 {16 document = new17 {18 source = new { type = "base64", base64 = Convert.ToBase64String(pdfBytes) },19 output = new { type = "base64" }20 },21 signature = new22 {23 type = "sign-ltv",24 tsaUrl = "http://timestamp.digicert.com",25 reason = "Invoice approved for issue",26 location = city,27 lockAfterSigning = true,28 appearance = new29 {30 renderMode = "image-and-description",31 signerNameDisplay = "signed-by-with-name",32 showTimestamp = true,33 additionalText = "GSTIN: " + gstin34 },35 placements = new[]36 {37 new { type = "text", searchText = "Authorised Signatory",38 position = "above", gap = 6.0, width = 180.0, height = 60.0 }39 }40 }41 };4243 using var resp = await _http.PostAsJsonAsync("sign-pdf", request);44 var result = await resp.Content.ReadFromJsonAsync<SignLiftResponse>();4546 if (result is null || !result.Status)47 throw new InvalidOperationException(result?.Error?.Message ?? "Signing failed");4849 return Convert.FromBase64String(result.Document.Content);50 }51}5253public record SignLiftResponse(bool Status, DocumentPayload Document, ErrorPayload Error);54public record DocumentPayload(string Content);55public record ErrorPayload(string Message);
Store apiToken in Azure Key Vault and inject it via configuration — never in code or plain-text app settings. For a batch, fan out with bounded concurrency rather than a serial loop, and for very large PDFs switch source/output to type: "s3" so you pass pointers instead of megabytes of base64:
1var throttler = new SemaphoreSlim(8); // tune to your throughput limits2var tasks = invoices.Select(async inv =>3{4 await throttler.WaitAsync();5 try { return await client.SignInvoiceAsync(inv.Pdf, inv.Gstin, inv.City); }6 finally { throttler.Release(); }7});8var signed = await Task.WhenAll(tasks);
This is where "1–2 hours becomes 1–2 minutes" actually comes from: parallel calls to a stateless signing endpoint, with no human in the loop.
Pattern C — X++ directly from Finance & Operations
If you want signing to happen in-process right after the invoice posts, call the API from X++ via the .NET HttpClient. Keep it asynchronous (a batch job or business-event handler) so signing never blocks the posting transaction:
1public str signInvoicePdf(System.Byte[] _pdfBytes)2{3 str token = SysGlobalCache::get('SignLift', 'token'); // from a secure store4 str endpoint = 'https://api.signsecure.in/sign-pdf';5 str base64Pdf = System.Convert::ToBase64String(_pdfBytes);67 str body = strFmt(@'{"document":{"source":{"type":"base64","base64":"%1"},'8 + '"output":{"type":"base64"}},'9 + '"signature":{"type":"sign-ltv","reason":"Invoice approved for issue",'10 + '"lockAfterSigning":true,'11 + '"placements":[{"type":"text","searchText":"Authorised Signatory",'12 + '"position":"above","gap":6,"width":180,"height":60}]}}', base64Pdf);1314 System.Net.Http.HttpClient client = new System.Net.Http.HttpClient();15 client.DefaultRequestHeaders.Add('X-API-Token', token);1617 var content = new System.Net.Http.StringContent(18 body, System.Text.Encoding::get_UTF8(), 'application/json');19 var response = client.PostAsync(endpoint, content).get_Result();20 str json = response.Content.ReadAsStringAsync().get_Result();2122 // Parse json, read document.content, Convert.FromBase64String back to bytes,23 // then attach to the invoice via DocuRef / Print Management archive.24 return json;25}
For most shops, Pattern A or B is cleaner than embedding HTTP calls in X++ — but this option exists when you need signing tightly coupled to posting.
Make it production-grade
Error handling, retries, and idempotency
- Always branch on status, not just the HTTP status code. SignLift returns a structured error.message you can log and surface.
- Retry transient failures (network timeouts, 5xx) with exponential backoff. Signing is safe to retry — you just get a freshly signed copy.
- Guard against double-signing. Track a "signed" flag on the invoice record so a re-run of a batch does not sign the same document twice. Idempotency is the integrator's job.
- Pre-flight the token. Have monitoring hit /check-token daily and alert when tokenExpiry is within ~30 days.
Security
- Keep the X-API-Token in a secret store — Azure Key Vault, Dataverse secure environment variables, or F&O secure configuration. Never commit it.
- Restrict who can edit the flow / function. Anyone who can change it can change what gets signed.
- Use HTTPS only and pin to your provisioned endpoint.
- Log the audit trail — store the returned timestamp, the signing reason, and the invoice ID for every signed document.
Is it legally valid in India?
Signatures applied by SignLift are PKI digital signatures backed by an X.509 certificate (held server-side as a PKCS#12 .pfx). Under the Information Technology Act, 2000, digital signatures created with a valid certificate carry legal recognition, and a signed PDF validates against its trust chain in standard readers like Adobe Acrobat — the signature is tamper-evident, so any change to the invoice after signing invalidates it visibly. With sign-ltv plus a timestamp authority, the signed invoice remains independently verifiable for the full statutory retention period, without depending on live certificate or revocation servers years down the line. That is the difference between "we signed it" and "we can prove we signed it, and when."
Test before you go live
- Health check — GET /version (no auth) confirms reachability.
- Token check — GET /check-token confirms your token and shows its expiry.
- Round-trip one invoice — send a real (non-production) invoice through /sign-pdf, open the returned PDF in Adobe Reader, and confirm the signature panel shows a valid signature anchored in the right place.
A Postman collection ships with the SignLift docs so you can exercise every endpoint before writing a line of integration code.
Conclusion
Manual invoice signing is a self-inflicted bottleneck. With the SignLift digital signature API, Dynamics 365 can hand off every posted invoice and get back a signed, timestamped, audit-ready PDF in seconds — whether you wire it up with a no-code Power Automate flow, an Azure Function for bulk runs, or X++ inside Finance & Operations. Start with Pattern A to prove the loop end-to-end, then graduate to Pattern B when volume demands it.
Ready to automate your invoice signing? Talk to SignSecure to get a SignLift API token and start signing in an afternoon.
Related articles

Integrating Online PDF Signer Solutions with Your ERP or CRM System
Integrate your ERP/CRM with a PDF signer to automate contracts, invoices, and forms. Secure, efficient, and easy to implement.

Use Cases of Digital Signature APIs Across Industries
Explore digital signature API use cases in banking, healthcare, and more. Automate workflows, improve security, and go paperless.

Digital Signature API vs. Traditional E-Signature Platforms: What Is The Difference?
There is a lot of buzz going on about the similarity between ‘electronic signatures’ and ‘digital signatures.

What Is E Sign And How E Sign API Works?
Learn what is E Sign & How E Sign API can work to streamline document signing workflow by effortlessly integrating with any existing system.