A developer's guide to eSignature API integration
Most signing integrations follow the same shortcut, redirecting the user to a hosted signing page, then polling an endpoint until the status flips to "completed." That works well enough for simple workflows, but breaks down when enterprise customers expect to stay inside your product, when a healthcare deployment requires a HIPAA-compliant audit trail your team actually controls, and when eIDAS 2.0 compliance across the EU means you can't treat conformance as an afterthought. This guide covers the full integration path for a production-grade eSignature API, from OAuth2 token acquisition and PDF field placement to embedded signing sessions and webhook-driven completion handling. You'll come away with working patterns for every layer of the stack, all verified against the live Foxit eSign API. Prerequisites Everything here runs against real endpoints, so set up a workspace before the first request: - Python 3.8+ with pip, plus a virtual environment so the dependencies stay isolated. - The requests library for the API calls and Flask for the webhook receiver. - A free Foxit eSign developer account, created with no credit card. Activate the API tab in your account settings to get a client_id andclient_secret . - A tagged sample PDF, so you don't have to author one. This guide uses agreement-signable.pdf, which already carries Text Tags for a single signer. - A code editor. VS Code with the Python extension is a good default; any editor works. Scaffold the workspace in one shot, then store your credentials as environment variables so they never land in source control: mkdir foxit-esign && cd foxit-esign python3 -m venv .venv && source .venv/bin/activate pip install requests flask export ESIGN_CLIENT_ID="your_client_id" export ESIGN_CLIENT_SECRET="your_client_secret" API concepts and compliance baseline The Foxit eSign API organizes signing workflows around folders. A folder holds one or more documents, a list of parties (signers, approvers, carbon-copy recipients), and the metadata that governs how those parties interact with those documents. Fields inside a document (signature boxes, text inputs, date stamps) are each assigned to a specific party. You can define that assignment in two ways, either by embedding Text Tags directly in the PDF to bake field definitions into the file itself, or by specifying field ownership in the API call. When both the API request and the PDF tags supply recipient or party information, the API call values take precedence. Signing order is controlled by three workflow modes, all driven by the signInSequence parameter: - Sequential: parties sign one after another in a defined order, and the next signer receives access only when the previous one completes. - Parallel: all parties receive signing access simultaneously ( signInSequence set tofalse ). - Hybrid: a mix of sequential stages, each of which may contain multiple parallel signers. Before you write a line of code, establish your compliance scope. HIPAA, eIDAS Advanced Electronic Signatures (AES) and Qualified Electronic Signatures (QES), ESIGN, and UETA are supported out of the box. For healthcare deployments, confirm HIPAA configurations with your account team before go-live. For EU deployments, choose the eu1 regional endpoint to keep data residency inside the EU and satisfy eIDAS 2.0 requirements. Make these architecture decisions at the start, because waiting until a customer's legal team raises them costs you a re-architecture. Authentication Foxit eSign uses the OAuth 2.0 client-credentials grant. You exchange a client_id and client_secret , available under the API tab in your Foxit eSign account settings, for a short-lived Bearer token that authorizes all subsequent calls. Get these two things right before you hit the endpoint: - The request body must be form-encoded ( application/x-www-form-urlencoded ). Sending a JSON body returns HTTP 415. Userequests.post(url, data={...}) , notjson={...} . - Choose your regional host at token time: na1.foxitesign.foxit.com for US deployments,eu1.foxitesign.foxit.com for EU. Both return the same error shape for bad credentials. import os import requests # Regional endpoint (swap na1 for eu1 for EU data residency) TOKEN_URL = "https://na1.foxitesign.foxit.com/api/oauth2/access_token" # Body must be form-encoded. A JSON body returns HTTP 415. response = requests.post( TOKEN_URL, data={ "grant_type": "client_credentials", "client_id": os.environ["ESIGN_CLIENT_ID"], "client_secret": os.environ["ESIGN_CLIENT_SECRET"], "scope": "read-write", }, ) token_data = response.json() # Response fields: access_token, token_type, expires_in, instance_url access_token = token_data["access_token"] instance_url = token_data["instance_url"].rstrip("/") # base URL for all calls # Attach the Bearer token to every downstream API request headers = { "Authorization": f"Bearer {access_token}", "Content-Type": "application/json", } The code above reads your credentials from the environment, posts them as a form-encoded body to the regional token endpoint, and unpacks the response. The instance_url it returns is already a full URL, so use it directly as the base for every downstream call rather than prepending a scheme yourself. Cache the token and schedule a refresh before expires_in seconds elapse, because re-acquiring on every request adds unnecessary overhead. For account activation steps, the Foxit eSign developer quickstart covers those without repetition here. Preparing and sending a document Create the document You can supply a PDF two ways. Pass a publicly accessible HTTPS URL in fileUrls and Foxit fetches the file, or send the bytes inline by setting inputType to "base64" and passing the encoded string in a base64FileString array when the PDF lives behind authentication or hasn't been published externally. Define recipients and field ownership Define each signer as a party with a first name, last name, email address (emailId ), a sequence number, and a permission such as FILL_FIELDS_AND_SIGN . Assign field ownership either via Text Tags embedded in the PDF or by specifying field coordinates in the API call. A Text Tag follows the syntax ${fieldtype:party_number:required:field_name:width} , so a required signature for the first party looks like ${signfield:1:y:____} , where y marks the field required, the party number maps to a signer's sequence , and width is expressed as underscores. If the API call and the PDF tags both specify party information, the API call wins. Send modes Two request parameters control how a document goes out. Choose based on whether you need a human review step or an in-app signing experience. Draft creates the document but holds it for review. Set sendNow to false and no invitation email goes out, which suits flows where a user confirms the recipient list before the envelope is dispatched. { "folderName": "Service Agreement - Acme Corp", "sendNow": false, "fileUrls": ["https://your-storage.example.com/agreement.pdf"], "fileNames": ["agreement.pdf"], "parties": [ { "firstName": "Jane", "lastName": "Smith", "emailId": "j***@acme.com", "permission": "FILL_FIELDS_AND_SIGN", "sequence": 1 } ] } Direct send dispatches immediately with no intermediate review step. Flip sendNow to true and Foxit emails the signers right away. { "folderName": "NDA - Standard", "sendNow": true, "fileUrls": ["https://your-storage.example.com/nda.pdf"], "fileNames": ["nda.pdf"], "parties": [ { "firstName": "Alex", "lastName": "Rivera", "emailId": "a***@partner.com", "permission": "FILL_FIELDS_AND_SIGN", "sequence": 1 } ] } Embedded signing adds createEmbeddedSigningSession and a list of embeddedSignersEmailIds , and the response returns a session URL you load inside your application, keeping the signer in your UI from start to finish. { "folderName": "Onboarding - User #4421", "sendNow": false, "createEmbeddedSigningSession": true, "embeddedSignersEmailIds": ["s**@yourapp.com"], "signSuccessUrl": "https://yourapp.example.com/signed", "fileUrls": ["https://your-storage.example.com/onboarding.pdf"], "fileNames": ["onboarding.pdf"], "parties": [ { "firstName": "Sam", "lastName": "Lee", "emailId": "s**@yourapp.com", "permission": "FILL_FIELDS_AND_SIGN", "sequence": 1 } ] } One nuance to expect here. sendNow: false on its own produces a DRAFT folder, but pairing it with createEmbeddedSigningSession returns a folderStatus of SHARED , since the folder has to be live for the session URL to open. No email goes out either way. The response carries an embeddedSigningSessions array, and each entry holds emailIdOfSigner , embeddedToken , and the embeddedSessionURL you render in the next step. Omitting embeddedSignersEmailIds returns email id of embedded signer(s) not submitted , so always list your embedded signers explicitly. Embedded signing and custom branding With an embedded signing session, the signer never leaves your application. Load the embeddedSessionURL in an iframe or a dedicated view, with no redirect, no hosted page from another domain, and no disorienting context switch mid-workflow. An embedded session loaded in-app. The signer completes every field without leaving your product. The session exposes configurable UI options that give you control over the signing surface: - Custom logo and colors: pass branding parameters at session creation to match your product's visual identity. - Hidden controls: suppress UI elements like the "Add Parties" button when operating in draft or template mode, preventing signers from modifying the recipient list. - Self-sign via API: trigger a signing action programmatically without user interaction, which is useful for automated counter-signature workflows where your system is one of the parties. For a document where every recipient signs inside your app, createEmbeddedSigningSessionForAllParties set to true covers all of them at once rather than naming each email individually. sequenceDiagram participant App as Your Application participant API as Foxit eSign API participant
Comments
No comments yet. Start the discussion.