--- url: /guide/start/introduction.md --- # Introduction ## OpenAPI Specification The Signhost REST API is described using the [OpenAPI Specification](https://www.openapis.org/). This specification is available in YAML format and can be used to generate client libraries, documentation, and other tools. **Download**: openapi.yaml ### Server Address The REST API Base URL is: `https://api.signhost.com` ### Security We require that all API requests are done over HTTPS. See the [Authentication](/guide/features/authentication.md) page for details on API authentication. ### Return codes The REST API uses standard [HTTP status codes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status). In short this means that any status code in the range of: - **2xx** - your request was successful - **4xx** - there was an error in your request. You should not retry the request until you have corrected the error - **5xx** - there was an internal error in Signhost. You may retry this request at a later time. If you implement a retry policy you should use a backoff policy. ### Date Time Formats JSON results which contain a date, time or datetime property are formatted according to [ISO8601](https://www.iso.org/iso/iso8601). A short explanation of the format is available at [w3.org - Date and Time Formats](https://www.w3.org/TR/NOTE-datetime). ## Libraries and demos There are a few client libraries and demos available to make connecting to our API easier. ### Signhost Libraries These libraries are built and maintained by Signhost: - [C# .Net Client Library](https://github.com/Evidos/SignhostClientLibrary) - [C# .Net Client Library (Polly)](https://github.com/Evidos/SignhostClientLibrary.polly) ### Community Libraries These libraries are built and maintained by the community: - [PHP - Laravel library (by NoardCode)](https://github.com/noardcode/laravel-signhost) - [Python Client Library](https://github.com/foarsitter/signhost-api-python-client) ### Postman Workspace Check out our public [Postman](https://www.postman.com/) workspace to make a quick start with testing - [Postman workspace](https://www.postman.com/dark-capsule-755015/signhost-api-public/overview). --- url: /guide/start/quick-start.md --- # Quick Start Get up and running with the Entrust Signhost API in minutes. This guide will walk you through creating your first digital signing transaction. ## Prerequisites Before you begin, you'll need: - An active Signhost account. [Sign up here](https://www.signhost.com/) if you don't have one yet. - A PDF document that you want to have signed ## Step 1: Get Your API Credentials ### 1.1 Generate Application Key 1. Log in to the [Signhost Portal](https://portal.signhost.com) 2. Navigate to the [Application Key](https://portal.signhost.com/v2/developer/application-keys) page 3. Click "Generate New Application Key" 4. Copy and securely store your **Application Key** ### 1.2 Generate User Token 1. Go to the [Settings](https://portal.signhost.com/v2/settings/tokens) page in the portal 2. Click "Generate token" 3. Copy and securely store your **User Token** :::warning **Important**: Both keys are only shown once during generation. Store them securely - if you lose them, you'll need to generate new ones. ::: ## Step 2: Create Your First Transaction ### 2.1 Create a New Transaction Make a `POST` request to create a new transaction: ```bash curl -X POST "https://api.signhost.com/api/transaction" \ -H "Authorization: APIKey ${API_KEY}" \ -H "Application: APPKey ${APP_KEY}" \ -H "Content-Type: application/json" \ -d '{ "Signers": [ { "Email": "signer@example.com", "SignRequestMessage": "Please sign this document", "SendSignRequest": true, "Verifications": [{ "Type": "Consent" }] } ] }' ``` **Response**: You'll receive a transaction object with an `Id` field. This is the `transactionId` that you'll need for the next steps. ### 2.2 Upload Your Document Upload the PDF document to be signed: ```bash curl -X PUT "https://api.signhost.com/api/transaction/${TRANSACTION_ID}/file/document.pdf" \ -H "Authorization: APIKey ${API_KEY}" \ -H "Application: APPKey ${APP_KEY}" \ -H "Content-Type: application/pdf" \ --data-binary @/path/to/your/document.pdf ``` Replace: - `${TRANSACTION_ID}` with the ID from step 2.1 - `document.pdf` with your desired filename - `/path/to/your/document.pdf` with the actual path to your PDF ### 2.3 Start the Transaction Activate the transaction to send signing invitations: ```bash curl -X PUT "https://api.signhost.com/api/transaction/${TRANSACTION_ID}/start" \ -H "Authorization: APIKey ${API_KEY}" \ -H "Application: APPKey ${APP_KEY}" ``` ## Step 3: Monitor Transaction Status ### Recommended Approach: Use Postbacks The best way to monitor transaction status is by configuring [**postbacks**](/guide/features/postbacks.md) (webhooks) that notify your application when status changes occur. The recommended way to set up postbacks is to configure a [Global Postback URL](/guide/features/postbacks.md#global-postback-url-recommended). Global postbacks support security features like digest security and security headers. The example below shows how to set a [Dynamic Postback URL](/guide/features/postbacks.md#dynamic-postback-url) when creating a transaction: ```bash # Include postback URL when creating transaction curl -X POST "https://api.signhost.com/api/transaction" \ -H "Authorization: APIKey ${API_KEY}" \ -H "Application: APPKey ${APP_KEY}" \ -H "Content-Type: application/json" \ -d '{ "Signers": [ { "Email": "signer@example.com", "SignRequestMessage": "Please sign this document", "SendSignRequest": true, "Verifications": [{ "Type": "Consent" }] } ], "PostbackUrl": "https://yourapp.com/webhook/signhost" }' ``` Your webhook endpoint will receive real-time notifications when the transaction status changes. ### Alternative: Check Status Once :::warning **Important**: We recommend **NOT** polling the GET transaction API to monitor status changes. This can impact performance and may result in rate limiting. ::: If you need to check the current status (not for monitoring), you can query it once: ```bash curl -X GET "https://api.signhost.com/api/transaction/${TRANSACTION_ID}" \ -H "Authorization: APIKey ${API_KEY}" \ -H "Application: APPKey ${APP_KEY}" ``` The response will include a `Status` field. See the [Statuses & Activites](/guide/features/status.md#transaction-status) page for possible values. ## Complete Example Here's a complete example using curl to create and start a transaction: ```bash # 1. Create transaction TRANSACTION_RESPONSE=$(curl -s -X POST "https://api.signhost.com/api/transaction" \ -H "Authorization: APIKey ${API_KEY}" \ -H "Application: APPKey ${APP_KEY}" \ -H "Content-Type: application/json" \ -d '{ "Signers": [ { "Email": "signer@example.com", "SignRequestMessage": "Please sign this document", "SendSignRequest": true, "Verifications": [ { "Type": "Consent" } ] } ] }') # Extract transaction ID (requires jq) TRANSACTION_ID=$(echo $TRANSACTION_RESPONSE | jq -r '.Id') # 2. Upload document curl -X PUT "https://api.signhost.com/api/transaction/$TRANSACTION_ID/file/contract.pdf" \ -H "Authorization: APIKey ${API_KEY}" \ -H "Application: APPKey ${APP_KEY}" \ -H "Content-Type: application/pdf" \ --data-binary @contract.pdf # 3. Start transaction curl -X PUT "https://api.signhost.com/api/transaction/$TRANSACTION_ID/start" \ -H "Authorization: APIKey ${API_KEY}" \ -H "Application: APPKey ${APP_KEY}" echo "Transaction created with ID: $TRANSACTION_ID" ``` ## Next Steps Now that you've created your first transaction, explore these advanced features: - **[Multiple Documents](/guide/guides/multidocument-transaction.md)**: Include multiple files in one transaction - **[Form Fields](/guide/guides/fillable-pdf-fields.md)**: Add fillable form fields to your documents ## Need Help? - **API Reference**: Explore the full [API documentation](/openapi/index.md) - **Support**: Contact [support@signhost.com](mailto:support@signhost.com) - **Community**: Try our [Postman workspace](https://www.postman.com/dark-capsule-755015/signhost-api-public/overview) for quick testing --- url: /guide/features/authentication.md --- # Authentication To authenticate your requests, you must add two HTTP headers to every request. | Header Name | Header Value | Description | | --------------- | -------------------------- | --------------- | | `Application` | `APPKey {appkey token}` | Application Key | | `Authorization` | `APIKey {your user token}` | User Token | #### Application Key (APPKey) The application key identifies your application you are integrating from. You can generate your APPKey on the [ApplicationKey](https://portal.signhost.com/v2/developer/application-keys) page of the Signhost portal. #### User Token (APIKey) The User Token identifies the user that signing transactions will originate from. You can generate your User Token on the [Settings](https://portal.signhost.com/v2/settings/tokens) page of the Signhost Portal. :::warning Note that both your User Token and Application Key will only be shown once when you generate them. We cannot retrieve these keys for you, as they are encrypted on our servers. If you lose them, you will need to generate new keys. ::: Below is an HTTP request header example: ``` Authorization: APIKey Al2CERwYbT.aVuHxoOfNAIxTdg7aMfScMjSEWqiA1K4 Application: APPKey RgdrkIBzxz0bd4FG Ez03pWmSjFo5tXjzIaxRUOTyYjIwxoug Content-Type: application/json Accept: application/json Connection: keep-alive ``` --- url: /guide/features/formsets.md --- # Form Sets Form Sets allow you to define multiple collections of form fields that can be assigned to different signers in a transaction. This feature enables you to collect different information from different signers, create reusable field definitions, and build complex multi-signer workflows. ### Overview Form Sets are particularly useful when you need to: - Collect different types of information from different signers - Create reusable field templates for common document types - Build complex multi-signer workflows with role-based field assignments - Maintain consistency across similar transactions ### How Form Sets Work Form Sets are collections of form fields that can be assigned to specific signers. The workflow involves: 1. **Define Form Sets**: Create named collections of fields with specific types and locations 2. **Assign to Signers**: Link Form Sets to specific signers using their signer IDs 3. **Position Fields**: Specify where each field should appear on the PDF document 4. **Collect Data**: Retrieve filled-in data when the transaction is completed ### Key Concepts - **Form Set Names**: Each Form Set must have a unique name within the **entire transaction** (not just per document). This applies across all documents if you have a multi-document transaction. - **Signer Assignment**: Use the signer's ID to assign Form Sets to specific signers - **Field Definitions**: Each field within a Form Set has a type, location, and optional properties - **Reusability**: Form Sets can be reused across multiple transactions with similar requirements ### Supported Field Types | Type | Description | Notes | Use Case | | ------------ | ------------------------------------- | ------------------------------------------ | ------------------------------- | | `Signature` | Create a signature field for a signer | Recommended: Width: 140, Height: 70 | Legal signatures, approvals | | `SingleLine` | Create a single line text input field | | Names, addresses, phone numbers | | `Check` | Create a checkbox | `Value`: The checked value of the checkbox | Agreements, confirmations | | `Seal` | Create a seal signature | _Not yet implemented_ | Official document sealing | #### Field Type Examples **Signature Field:** ```json { "SignatureField": { "Type": "Signature", "Location": { "Search": "{{Signer1}}", "Width": 140, "Height": 70 } } } ``` **Text Input Field:** ```json { "FullName": { "Type": "SingleLine", "Location": { "Search": "Full Name:", "Left": 10, "Width": 200 } } } ``` **Checkbox Example:** ```json { "UserAgrees": { "Type": "Check", "Value": "I agree to the terms and conditions", "Location": { "Search": "user_agree", "Left": 5 } } } ``` ### Field Location Options The `Location` object determines where the field should be placed on the PDF document: | Property | Type | Description | Example | | ------------ | ------- | ------------------------------------------------------------------- | ------------- | | `Search` | String | Text to search in the PDF document to use as the position reference | `{{Signer1}}` | | `Occurrence` | Integer | When using text search, match only this specific occurrence | `1` | | `Top` | Integer | Offset from the top of the search text or page (in pixels) | `10` | | `Right` | Integer | Offset from the right of the search text or page (in pixels) | `20` | | `Bottom` | Integer | Offset from the bottom of the search text or page (in pixels) | `30` | | `Left` | Integer | Offset from the left of the search text or page (in pixels) | `40` | | `Width` | Integer | The width of the field (cannot be used with both Left and Right) | `200` | | `Height` | Integer | The height of the field (cannot be used with both Bottom and Top) | `30` | | `PageNumber` | Integer | The number of the page the field should be placed | `1` | :::tip Coordinate System All measurements are in points (1/72 inch) ::: ### Location Examples #### Search-based positioning: Position a field relative to specific text in the document: ```json { "Location": { "Search": "{{Signer1}}", "Left": 10, "Top": 5 } } ``` #### Absolute positioning: Place a field at a specific location on the page: ```json { "Location": { "PageNumber": 1, "Top": 100, "Left": 50, "Width": 200, "Height": 30 } } ``` ::: info You must specify a page number when not using the `Search` property. ::: #### Simple search positioning: Let the system automatically position the field at the search text: ```json { "Location": { "Search": "Sign here" } } ``` #### Multiple occurrences: Position at the second occurrence of specific text: ```json { "Location": { "Search": "Date:", "Occurrence": 2, "Left": 10 } } ``` ### Complete Form Set Example Here's a comprehensive example showing how to create Form Sets for multiple signers: ```json { "DisplayName": "Employment Contract", "Signers": { "employee-signer-id": { "FormSets": ["EmployeeDetails", "EmployeeSignature"] }, "manager-signer-id": { "FormSets": ["ManagerApproval"] }, "hr-signer-id": { "FormSets": ["HRProcessing"] } }, "FormSets": { "EmployeeDetails": { "FullName": { "Type": "SingleLine", "Location": { "Search": "Employee Name:", "Left": 10, "Width": 200 } }, "StartDate": { "Type": "SingleLine", "Location": { "Search": "Start Date:", "Left": 10, "Width": 100 } }, "EmergencyContact": { "Type": "SingleLine", "Location": { "Search": "Emergency Contact:", "Left": 10, "Width": 200 } } }, "EmployeeSignature": { "EmployeeSign": { "Type": "Signature", "Location": { "Search": "Employee Signature:", "Left": 10, "Width": 140, "Height": 70 } } }, "ManagerApproval": { "ManagerApproves": { "Type": "Check", "Value": "Approved", "Location": { "Search": "Manager Approval:", "Left": 10 } }, "ManagerSign": { "Type": "Signature", "Location": { "Search": "Manager Signature:", "Left": 10, "Width": 140, "Height": 70 } } }, "HRProcessing": { "ProcessingDate": { "Type": "SingleLine", "Location": { "Search": "Processing Date:", "Left": 10, "Width": 100 } }, "HRSign": { "Type": "Signature", "Location": { "Search": "HR Signature:", "Left": 10, "Width": 140, "Height": 70 } } } } } ``` ### Best Practices 1. **Signature Field Dimensions**: Use Width: 140 and Height: 70 for signature fields to ensure adequate space 2. **Signature Fields for All Signers**: If you specify a signature field location for one signer, you must specify signature field locations for **all** signers in the transaction. Omitting signature fields for some signers while defining them for others can result in signatures not being placed correctly. 3. **Unique Form Set Names**: Ensure each Form Set has a unique name within the **entire transaction** (across all documents) to avoid conflicts. Form Set names must be unique at the transaction level, not just per document. 4. **Test Field Positions**: Test your field positioning with sample PDFs before production deployment 5. **Clear Search Text**: Use distinctive text for search-based positioning that won't match unintended locations 6. **Fallback Positioning**: Consider using absolute positioning for critical fields if search text might be unreliable 7. **Logical Grouping**: Group related fields into the same Form Set for better organization 8. **Descriptive Field Names**: Use clear, descriptive names for fields to make data retrieval easier 9. **Consistent Spacing**: Maintain consistent spacing between fields for a professional appearance 10. **Page Boundaries**: Ensure fields don't extend beyond page boundaries 11. **Mobile Compatibility**: Consider how fields will appear on mobile devices during signing ### Common Use Cases #### Contract Signing - **Employee Form Set**: Personal details, emergency contact, signature - **Manager Form Set**: Approval checkbox, signature - **HR Form Set**: Processing notes, signature #### Invoice Approval - **Requester Form Set**: Description, amount, signature - **Approver Form Set**: Approval checkbox, comments, signature - **Finance Form Set**: Account codes, signature #### Multi-Party Agreements - **Party A Form Set**: Contact details, terms acceptance, signature - **Party B Form Set**: Contact details, terms acceptance, signature - **Witness Form Set**: Witness information, signature ### Troubleshooting **Field Not Appearing:** - Check that the search text exists in the PDF - Verify field dimensions fit within the page boundaries - Ensure the Form Set is assigned to the correct signer **Field Positioning Issues:** - Test with absolute positioning first - Check for multiple occurrences of search text - Verify coordinate calculations **Data Not Captured:** - Ensure field names are unique within the Form Set - Check that the signer completed all required fields - Verify the transaction reached completion ### Related Guides - [Form Fields in PDFs](/guide/guides/fillable-pdf-fields.md): Complete guide covering both PDF-embedded and API-generated form fields - [Multi-document Transactions](/guide/guides/multidocument-transaction.md): Working with multiple documents --- url: /guide/features/postbacks.md --- # Postbacks / Webhooks > A webhook is a lightweight, event-driven communication that automatically sends data between applications via HTTP. Triggered by specific events, webhooks automate communication between application programming interfaces (APIs) and can be used to activate workflows, such as in GitOps environments. [Source](https://www.redhat.com/en/topics/automation/what-is-a-webhook) For the purposes of this document, the terms "webhook" and "postback" are used interchangeably. ## Signhost Postbacks The Signhost postback service is meant to provide realtime updates on your transactions. If you cannot implement receiving postback in your application, or if you have any questions about how to implement receiving postbacks, please contact [Support](mailto:support@signhost.com). - Signhost will perform an HTTP POST request to the postback URL that you configure. - Signhost only supports making postback calls to HTTPS URLs on port 443. - Signhost will only issue 1 postback per PostbackURL at a time, one-by-one. - When a postback fails, Signhost will queue all new postbacks and retry them again at a later time. - If the postback succeeds, Signhost will continue issuing the remaining queued postbacks. :::danger Always Return 2xx Response Your postback endpoint **must always** return a `2xx` HTTP status code (such as 200 OK), even if validation fails or errors occur. Failing to do so will cause Signhost to queue all subsequent postbacks, which can lead to significant delays in receiving transaction updates. See the [Recommended Postback Flow](#recommended-postback-flow) section for details. ::: Signhost will send a postback with the most up-to-date data known at the moment when: - There is a status change in the transaction (eg. the transaction went from waiting for signer to all signed). See [Transaction Statuses](/guide/features/status.md#transaction-statuses). - There is a signer activity (eg. an email was sent). See [Signer Activities](/guide/features/status.md#signer-activities). - There is a receiver activity (eg. an email was sent). Receiver activities share the same status codes as [Signer Activities](/guide/features/status.md#signer-activities), but only `Failed` (email bounce) and `SignedDocumentSent` are used for receivers. There are two ways of creating postbacks that will be used to deliver postback messages to: #### Global Postback URL (recommended) You can register a postback URL on the [Postbacks](https://portal.signhost.com/v2/developer/postbacks) page of the Signhost portal. Global Postback URLs will be used for every transaction. :::tip This method supports both digest security and security headers. ::: The Postbacks page allows you to easily manage postbacks, check for any queued requests, and use security features such as the Authorization header and Checksum calculation. For more information, see the [Security](#security) section. When you create a postback URL, we automatically test if your endpoint is available by sending an empty POST request. #### Dynamic Postback URL You can dynamically specify a postback URL for a specific transaction by providing one in the `PostbackUrl` property of the transaction object when creating a transaction. This functionality is meant for separating the postbacks into different 'buckets' that would make sense for your implementation, for example to differentiate between different environments (staging, production, etc.) or different departments (sales, HR, etc.). This functionality is _not_ meant to separate postbacks per transaction as this circumvents the postback queueing system. Your business logic should be able to differentiate between postbacks based on the transaction ID. Signhost reserves the right to block postback URLs if this feature is repeatedly abused. :::warning Dynamic Postback URLs do NOT support digest security or security headers. ::: If you configure both a Global Postback URL and a Dynamic Postback URL for a transaction, postbacks will be delivered to **both** endpoints. Each postback URL acts as a subscriber at different scopes: - **Dynamic Postback URL**: Transaction scope - **Global Postback URL**: Organization scope (applies to all API keys within the organization) This allows you to receive postbacks at multiple endpoints simultaneously if needed for your integration architecture. ## Recommended Postback Flow Signhost recommends the following flow once a postback arrives at your server: 1. Validate the Postback payload. See the [security validation](#security) for more details. - Validate the security header - Validate the body is valid JSON - Validate the JSON has a Checksum property - Validate the Checksum value 2. **Always** return a 200 OK response - Skip rest of the steps if the checksum validation failed. 3. Optional: Persist postback payload to storage 4. Continue business logic :::danger Critical Requirement Your endpoint **must always** return an HTTP `2xx` status code (typically 200 OK), regardless of whether validation succeeds or fails. This is a critical security precaution that prevents information about your validation process from being returned to a potentially malicious sender. **Any response other than `2xx` will cause Signhost to queue all subsequent postbacks**, leading to delays in receiving transaction updates. See [Error Handling](#error-handling) for more details. This is one of the most common issues encountered by API users. Even if your validation fails, return 200 OK and handle the invalid postback internally (e.g., log it, discard it, or alert your team). ::: ## Error Handling If your postback URL returns a non `2xx` HTTP status code, Signhost will [queue any new postbacks](#what-happens-if-your-postback-url-is-down-or-cant-accept-requests). Sighost will retry to deliver the first failed postback with an increasing interval (the first retry is within a few minutes). After 5 successive failed attempts, Signhost will send you an email which will include an attachment with the received response (if any). When Signhost receives a `2xx` HTTP status code, the postback URL will be marked as available and resume sending the queued postbacks. To guarantee performance and uptime, and make sure there is no data loss, it's possible that that may you receive the same postback twice. This can happen because our system consists of multiple instances and there is no deduplication. By checking if the postback from instance one is already sent from instance two, we would re-introduce a single point of failure. Furthermore, it is possible to receive postbacks containing statuses or activities after a signer signed, or after the entire transaction is marked as signed. Your system will have to handle these scenarios. ## Statuses and Activities A postback is sent out when either the Transaction Status changes or a Signer Activity occurs. A postback contains only one transaction status however a signer object will contain the full list of all signer activities which have taken place. This can help you track all of the activities which have taken place for each individual signers in a transaction. ### Transaction Status A transaction has an overall status. This is the status of the entire transaction such as signed or rejected. A full list of all Transaction Statuses can be found at the [Status & Activities](/guide/features/status.md#transaction-statuses) page. A few scenarios around transaction status postbacks: - A transaction is created, and two documents are attached. The transaction will still be in status `5` (waiting for document). You need to start the transaction so Signhost knows you have finished uploading files. - A transaction with two signers is signed by the first signer. The transaction status is still `10` (waiting for signer) because Signer 2 still needs to sign! After end statuses such as **30** (signed), **40** (rejected), **50** (expired), **60** (cancelled), **70** (failed) you can still receive postbacks with signer activities as people might click the invite link again or download signed documents. Transaction status postbacks can be identified via a combination of the Transaction ID, Status code and Checksum. - Transaction ID: The unique ID of the Transaction - Status Code: The current status of the Transaction. See [Status & Activities](/guide/features/status.md#transaction-statuses) - Checksum: The checksum of the postback payload :::tip Multiple postbacks with the same combination of these variables might arrive because you will receive multiple signer activities falling under the same transaction status. ::: ### Signer Activity In addition to the overall transaction status, for each signer that is involved in a transaction, there is a list of activities that the user has performed. This details what an individual person has done in their signing session. A full list of all the Signer Activities can be found at the [Status & Activities](/guide/features/status.md#signer-activities) page. A Signer Activity details what interactions a specific person had with the transaction and the documents within. These activities give real-time insight in the full audit trail of what a signer has done to come to a signed document and can be used in your business logic and dashboarding to provide extra information to your users. A signer who checked a document five times but still hasn't signed, might trigger a signal for you to give them a call. A few scenarios around signer activity postbacks: - In a transaction with two signers, Signer 1 and Signer 2. Signer 1 has signed the document. Signer 2 still has yet to sign the document. Your system receives a postback with signer activity status `203` (signed) for Signer 1. After signing, the Signer 1 goes back to their email, and clicks the invite link again. You will receive a subsequent posback with status `103` (opened) for Signer 1 but that still means Signer 1 has already signed the document. - Due to the queuing processes in both Signhost and your application, you might receive the signer activity postback for 'signed' (`203`) and 'document opened' (`105`) at the same time. That still means that Signer 1 has signed the document. Signer activities can be identified via a combination of Activity ID, Status code and CreatedDateTime. - Activity ID: Unique ID of the Activity - Status Code: The current status of the Activity. See [Status & Activities](/guide/features/status.md#signer-activities) - CreatedDateTime: The date when the activity occurred. Your business logic can use signer activities to trigger subsequent actions. For example, if you are using the [Direct Flow](/guide/features/signing-flows.md#signing-flows) for delivering transactions, and you want to invite Signer 2 after Signer 1 signed, you can rely on the signer activity `203` (signed) for Signer 1 to trigger sending the transaction to Signer 2. Make sure that subsequent signer activities or duplicate postbacks _after_ the first `203` (signed) do not overwrite or retrigger any invitations. :::info Note that the signer activity `203` (signed) only indicates that the **signer** completed the sign flow with the intent of signing the document. The fully signed document is only available once Signhost completes the processing of the document and the **transaction** reaches status `30` (signed). ::: ## Security Signhost offers two methods to secure postbacks: #### Authorization header This can be any string that you enter while registering the postback URL. Postbacks sent from Signhost will include this string in the `Authorization` header of the HTTP POST message. This ensures that the request came from Signhost. #### Checksum calculation This involves calculating a checksum using the transaction ID, transaction status, and a shared secret. You will receive the shared secret only once when registering your Postback URL. Postbacks sent from Signhost will include the checksum in the `Checksum` header of the HTTP POST message. This ensures that the contents of the message have not been altered or tampered with. :::note IP whitelisting and certificate pinning should not be used as security measures because they are not always reliable. All security measures can be found and configured on the [Postbacks](https://portal.signhost.com/v2/developer/postbacks) page in the Signhost portal. ::: The checksum is calculated using the following formula: Checksum = SHA1(transaction id + || + status + | + sharedsecret) > There is a double "pipe" sign between the transaction id and the status. > If you are still using our legacy API - you are seeing a File object in your postback and get responses - you'll have to include the file id at this location. > eg `Checksum = SHA1(transaction id + | + file id + | + status + | + sharedsecret)` The "pipe" sign ( | ) is used as the delimiter between values. You may need to put the delimiters between single quotes (') or double quotes (") depending on the programming language that you will be using. The value returned by the SHA1 function is a string of 40 characters representing a hexadecimal value. How to use the SHA1 algorithm depends on your development platform. Most languages and frameworks (such as PHP, ASP.NET, Python, Java, JavaScript) have built-in implementations of the SHA1 algorithm. For other languages, such as classic ASP, Open Source implementations of the SHA1 algorithm are available online. :::warning Signhost strongly urges you to protect your account by only returning an HTTP 2xx response code, even if an expected security header or checksum does not match. Failing to do so could potentially allow attackers to probe your security measures or cause the formation of queues that could impact the performance of your system. ::: #### Implementation Examples **undefined** ```typescript fold file="./samples/_postback-nodejs.ts" import { createHash, timingSafeEqual } from "node:crypto"; import type { Request, Response } from "express"; // Configuration const SHARED_SECRET = process.env.SIGNHOST_SHARED_SECRET ?? ""; const EXPECTED_AUTH_HEADER = process.env.SIGNHOST_AUTH_HEADER ?? ""; interface PostbackPayload { Id: string; Status: number; Checksum: string; [key: string]: unknown; } interface ValidationResult { valid: boolean; errors: string[]; } function calculateChecksum( transactionId: string, status: number, sharedSecret: string, ): string { // Note: Double pipe (||) between transaction ID and status const checksumString = `${transactionId}||${status}|${sharedSecret}`; return createHash("sha1").update(checksumString).digest("hex"); } function validatePostback( payload: PostbackPayload, authorizationHeader: string | undefined, ): ValidationResult { const errors: string[] = []; // Step 1: Validate Authorization header if (authorizationHeader !== EXPECTED_AUTH_HEADER) { errors.push("Invalid Authorization header"); } // Step 2: Validate JSON structure if (!payload.Id || payload.Status === undefined || !payload.Checksum) { errors.push("Missing required fields"); } // Step 3: Validate checksum using constant-time comparison const expectedChecksum = calculateChecksum( payload.Id, payload.Status, SHARED_SECRET, ); // Use timingSafeEqual for constant-time comparison to prevent timing attacks const expectedBuffer = Buffer.from(expectedChecksum); const receivedBuffer = Buffer.from(payload.Checksum || ""); if ( expectedBuffer.length !== receivedBuffer.length || !timingSafeEqual(expectedBuffer, receivedBuffer) ) { errors.push("Invalid checksum"); } return { valid: errors.length === 0, errors, }; } // Express.js example app.post("/postback", express.json(), (req: Request, res: Response) => { const authHeader = req.headers.authorization; const payload = req.body as PostbackPayload; const validation = validatePostback(payload, authHeader); // CRITICAL: Always return 200 OK if (!validation.valid) { console.error("Invalid postback:", validation.errors); return res.status(200).send("OK"); } // Process valid postback console.log("Valid postback received:", payload.Id); // ... your business logic here ... res.status(200).send("OK"); }); ``` **undefined** ```typescript fold file="./samples/_postback-nodejs.ts" import { createHash, timingSafeEqual } from "node:crypto"; import type { Request, Response } from "express"; // Configuration const SHARED_SECRET = process.env.SIGNHOST_SHARED_SECRET ?? ""; const EXPECTED_AUTH_HEADER = process.env.SIGNHOST_AUTH_HEADER ?? ""; interface PostbackPayload { Id: string; Status: number; Checksum: string; [key: string]: unknown; } interface ValidationResult { valid: boolean; errors: string[]; } function calculateChecksum( transactionId: string, status: number, sharedSecret: string, ): string { // Note: Double pipe (||) between transaction ID and status const checksumString = `${transactionId}||${status}|${sharedSecret}`; return createHash("sha1").update(checksumString).digest("hex"); } function validatePostback( payload: PostbackPayload, authorizationHeader: string | undefined, ): ValidationResult { const errors: string[] = []; // Step 1: Validate Authorization header if (authorizationHeader !== EXPECTED_AUTH_HEADER) { errors.push("Invalid Authorization header"); } // Step 2: Validate JSON structure if (!payload.Id || payload.Status === undefined || !payload.Checksum) { errors.push("Missing required fields"); } // Step 3: Validate checksum using constant-time comparison const expectedChecksum = calculateChecksum( payload.Id, payload.Status, SHARED_SECRET, ); // Use timingSafeEqual for constant-time comparison to prevent timing attacks const expectedBuffer = Buffer.from(expectedChecksum); const receivedBuffer = Buffer.from(payload.Checksum || ""); if ( expectedBuffer.length !== receivedBuffer.length || !timingSafeEqual(expectedBuffer, receivedBuffer) ) { errors.push("Invalid checksum"); } return { valid: errors.length === 0, errors, }; } // Express.js example app.post("/postback", express.json(), (req: Request, res: Response) => { const authHeader = req.headers.authorization; const payload = req.body as PostbackPayload; const validation = validatePostback(payload, authHeader); // CRITICAL: Always return 200 OK if (!validation.valid) { console.error("Invalid postback:", validation.errors); return res.status(200).send("OK"); } // Process valid postback console.log("Valid postback received:", payload.Id); // ... your business logic here ... res.status(200).send("OK"); }); ``` **undefined** ```typescript fold file="./samples/_postback-nodejs.ts" import { createHash, timingSafeEqual } from "node:crypto"; import type { Request, Response } from "express"; // Configuration const SHARED_SECRET = process.env.SIGNHOST_SHARED_SECRET ?? ""; const EXPECTED_AUTH_HEADER = process.env.SIGNHOST_AUTH_HEADER ?? ""; interface PostbackPayload { Id: string; Status: number; Checksum: string; [key: string]: unknown; } interface ValidationResult { valid: boolean; errors: string[]; } function calculateChecksum( transactionId: string, status: number, sharedSecret: string, ): string { // Note: Double pipe (||) between transaction ID and status const checksumString = `${transactionId}||${status}|${sharedSecret}`; return createHash("sha1").update(checksumString).digest("hex"); } function validatePostback( payload: PostbackPayload, authorizationHeader: string | undefined, ): ValidationResult { const errors: string[] = []; // Step 1: Validate Authorization header if (authorizationHeader !== EXPECTED_AUTH_HEADER) { errors.push("Invalid Authorization header"); } // Step 2: Validate JSON structure if (!payload.Id || payload.Status === undefined || !payload.Checksum) { errors.push("Missing required fields"); } // Step 3: Validate checksum using constant-time comparison const expectedChecksum = calculateChecksum( payload.Id, payload.Status, SHARED_SECRET, ); // Use timingSafeEqual for constant-time comparison to prevent timing attacks const expectedBuffer = Buffer.from(expectedChecksum); const receivedBuffer = Buffer.from(payload.Checksum || ""); if ( expectedBuffer.length !== receivedBuffer.length || !timingSafeEqual(expectedBuffer, receivedBuffer) ) { errors.push("Invalid checksum"); } return { valid: errors.length === 0, errors, }; } // Express.js example app.post("/postback", express.json(), (req: Request, res: Response) => { const authHeader = req.headers.authorization; const payload = req.body as PostbackPayload; const validation = validatePostback(payload, authHeader); // CRITICAL: Always return 200 OK if (!validation.valid) { console.error("Invalid postback:", validation.errors); return res.status(200).send("OK"); } // Process valid postback console.log("Valid postback received:", payload.Id); // ... your business logic here ... res.status(200).send("OK"); }); ``` **undefined** ```typescript fold file="./samples/_postback-nodejs.ts" import { createHash, timingSafeEqual } from "node:crypto"; import type { Request, Response } from "express"; // Configuration const SHARED_SECRET = process.env.SIGNHOST_SHARED_SECRET ?? ""; const EXPECTED_AUTH_HEADER = process.env.SIGNHOST_AUTH_HEADER ?? ""; interface PostbackPayload { Id: string; Status: number; Checksum: string; [key: string]: unknown; } interface ValidationResult { valid: boolean; errors: string[]; } function calculateChecksum( transactionId: string, status: number, sharedSecret: string, ): string { // Note: Double pipe (||) between transaction ID and status const checksumString = `${transactionId}||${status}|${sharedSecret}`; return createHash("sha1").update(checksumString).digest("hex"); } function validatePostback( payload: PostbackPayload, authorizationHeader: string | undefined, ): ValidationResult { const errors: string[] = []; // Step 1: Validate Authorization header if (authorizationHeader !== EXPECTED_AUTH_HEADER) { errors.push("Invalid Authorization header"); } // Step 2: Validate JSON structure if (!payload.Id || payload.Status === undefined || !payload.Checksum) { errors.push("Missing required fields"); } // Step 3: Validate checksum using constant-time comparison const expectedChecksum = calculateChecksum( payload.Id, payload.Status, SHARED_SECRET, ); // Use timingSafeEqual for constant-time comparison to prevent timing attacks const expectedBuffer = Buffer.from(expectedChecksum); const receivedBuffer = Buffer.from(payload.Checksum || ""); if ( expectedBuffer.length !== receivedBuffer.length || !timingSafeEqual(expectedBuffer, receivedBuffer) ) { errors.push("Invalid checksum"); } return { valid: errors.length === 0, errors, }; } // Express.js example app.post("/postback", express.json(), (req: Request, res: Response) => { const authHeader = req.headers.authorization; const payload = req.body as PostbackPayload; const validation = validatePostback(payload, authHeader); // CRITICAL: Always return 200 OK if (!validation.valid) { console.error("Invalid postback:", validation.errors); return res.status(200).send("OK"); } // Process valid postback console.log("Valid postback received:", payload.Id); // ... your business logic here ... res.status(200).send("OK"); }); ``` **undefined** ```typescript fold file="./samples/_postback-nodejs.ts" import { createHash, timingSafeEqual } from "node:crypto"; import type { Request, Response } from "express"; // Configuration const SHARED_SECRET = process.env.SIGNHOST_SHARED_SECRET ?? ""; const EXPECTED_AUTH_HEADER = process.env.SIGNHOST_AUTH_HEADER ?? ""; interface PostbackPayload { Id: string; Status: number; Checksum: string; [key: string]: unknown; } interface ValidationResult { valid: boolean; errors: string[]; } function calculateChecksum( transactionId: string, status: number, sharedSecret: string, ): string { // Note: Double pipe (||) between transaction ID and status const checksumString = `${transactionId}||${status}|${sharedSecret}`; return createHash("sha1").update(checksumString).digest("hex"); } function validatePostback( payload: PostbackPayload, authorizationHeader: string | undefined, ): ValidationResult { const errors: string[] = []; // Step 1: Validate Authorization header if (authorizationHeader !== EXPECTED_AUTH_HEADER) { errors.push("Invalid Authorization header"); } // Step 2: Validate JSON structure if (!payload.Id || payload.Status === undefined || !payload.Checksum) { errors.push("Missing required fields"); } // Step 3: Validate checksum using constant-time comparison const expectedChecksum = calculateChecksum( payload.Id, payload.Status, SHARED_SECRET, ); // Use timingSafeEqual for constant-time comparison to prevent timing attacks const expectedBuffer = Buffer.from(expectedChecksum); const receivedBuffer = Buffer.from(payload.Checksum || ""); if ( expectedBuffer.length !== receivedBuffer.length || !timingSafeEqual(expectedBuffer, receivedBuffer) ) { errors.push("Invalid checksum"); } return { valid: errors.length === 0, errors, }; } // Express.js example app.post("/postback", express.json(), (req: Request, res: Response) => { const authHeader = req.headers.authorization; const payload = req.body as PostbackPayload; const validation = validatePostback(payload, authHeader); // CRITICAL: Always return 200 OK if (!validation.valid) { console.error("Invalid postback:", validation.errors); return res.status(200).send("OK"); } // Process valid postback console.log("Valid postback received:", payload.Id); // ... your business logic here ... res.status(200).send("OK"); }); ``` **undefined** ```typescript fold file="./samples/_postback-nodejs.ts" import { createHash, timingSafeEqual } from "node:crypto"; import type { Request, Response } from "express"; // Configuration const SHARED_SECRET = process.env.SIGNHOST_SHARED_SECRET ?? ""; const EXPECTED_AUTH_HEADER = process.env.SIGNHOST_AUTH_HEADER ?? ""; interface PostbackPayload { Id: string; Status: number; Checksum: string; [key: string]: unknown; } interface ValidationResult { valid: boolean; errors: string[]; } function calculateChecksum( transactionId: string, status: number, sharedSecret: string, ): string { // Note: Double pipe (||) between transaction ID and status const checksumString = `${transactionId}||${status}|${sharedSecret}`; return createHash("sha1").update(checksumString).digest("hex"); } function validatePostback( payload: PostbackPayload, authorizationHeader: string | undefined, ): ValidationResult { const errors: string[] = []; // Step 1: Validate Authorization header if (authorizationHeader !== EXPECTED_AUTH_HEADER) { errors.push("Invalid Authorization header"); } // Step 2: Validate JSON structure if (!payload.Id || payload.Status === undefined || !payload.Checksum) { errors.push("Missing required fields"); } // Step 3: Validate checksum using constant-time comparison const expectedChecksum = calculateChecksum( payload.Id, payload.Status, SHARED_SECRET, ); // Use timingSafeEqual for constant-time comparison to prevent timing attacks const expectedBuffer = Buffer.from(expectedChecksum); const receivedBuffer = Buffer.from(payload.Checksum || ""); if ( expectedBuffer.length !== receivedBuffer.length || !timingSafeEqual(expectedBuffer, receivedBuffer) ) { errors.push("Invalid checksum"); } return { valid: errors.length === 0, errors, }; } // Express.js example app.post("/postback", express.json(), (req: Request, res: Response) => { const authHeader = req.headers.authorization; const payload = req.body as PostbackPayload; const validation = validatePostback(payload, authHeader); // CRITICAL: Always return 200 OK if (!validation.valid) { console.error("Invalid postback:", validation.errors); return res.status(200).send("OK"); } // Process valid postback console.log("Valid postback received:", payload.Id); // ... your business logic here ... res.status(200).send("OK"); }); ``` ### What happens if your postback URL is down or can't accept requests? If the webhook URL doesn't return a `2xx` HTTP response code, that POST request will be re-attempted with a random increasing interval. All following postbacks will be put in a postback queue, and will wait there untill the first postback in the queue gets a `2xx` HTTP response code. If a particular POST request is unsuccessful and is being retried, no other POSTs will be attempted until the first one succeeds or is marked as failed. Requests are marked failed and removed from the queue after 48-72 hours of unsuccessful retry attempts. Subsequent postbacks are deferred until the first completes. Once the first postback request completes the deferred requests will be processed sequentially. ### Postback Request Body Example ```json { "Id": "b10ae331-af78-4e79-a39e-5b64693b6b68", "Status": 30, "Files": { "contract.pdf": { "Links": [ { "Rel": "file", "Type": "application/pdf", "Link": "https://api.signhost.com/api/transaction/b10ae331-af78-4e79-a39e-5b64693b6b68/file/contract.pdf" } ], "DisplayName": "contract.pdf" } }, "Seal": false, "Signers": [ { "Id": "fa95495d-6c59-48e0-962a-a4552f8d6b85", "Expires": "2025-11-30T06:00:00+01:00", "Email": "user@example.com", "Authentications": [], "Verifications": [ { "Type": "Scribble", "RequireHandsignature": true, "ScribbleNameFixed": true, "ScribbleName": "John Doe" }, { "Type": "IPAddress", "IPAddress": "203.0.113.42" } ], "Mobile": null, "Iban": null, "BSN": null, "RequireScribbleName": true, "RequireScribble": true, "RequireEmailVerification": true, "RequireSmsVerification": false, "RequireIdealVerification": false, "RequireDigidVerification": false, "RequireSurfnetVerification": false, "SendSignRequest": true, "SendSignConfirmation": true, "SignRequestMessage": "Dear John,\n\nThis automated email is intended to inform you that one or more documents need to be signed.", "DaysToRemind": 7, "Language": "en", "ScribbleName": "John Doe", "ScribbleNameFixed": true, "Reference": "", "IntroText": null, "ReturnUrl": null, "AllowDelegation": false, "Activities": [ { "Id": "a1b2c3d4-e5f6-47a8-b9c0-d1e2f3a4b5c6", "Code": 101, "Activity": "InvitationSent", "CreatedDateTime": "2016-06-15T23:30:00.0000000+02:00" }, { "Id": "b2c3d4e5-f6a7-48b9-c0d1-e2f3a4b5c6d7", "Code": 103, "Activity": "Opened", "CreatedDateTime": "2016-06-15T23:33:04.1965465+02:00" }, { "Id": "c3d4e5f6-a7b8-49c0-d1e2-f3a4b5c6d7e8", "Code": 105, "Activity": "DocumentOpened", "Info": "contract.pdf", "CreatedDateTime": "2016-06-15T23:33:10.0000000+02:00" }, { "Id": "f6a7b8c9-d0e1-42f3-a4b5-c6d7e8f9a0b1", "Code": 203, "Activity": "Signed", "CreatedDateTime": "2016-06-15T23:38:04.1965465+02:00" }, { "Id": "a7b8c9d0-e1f2-43a4-b5c6-d7e8f9a0b1c2", "Code": 301, "Activity": "SignedDocumentSent", "CreatedDateTime": "2016-06-15T23:38:15.0000000+02:00" }, { "Id": "b8c9d0e1-f2a3-44b5-c6d7-e8f9a0b1c2d3", "Code": 401, "Activity": "ReceiptSent", "CreatedDateTime": "2016-06-15T23:38:16.0000000+02:00" }, { "Id": "c9d0e1f2-a3b4-45c6-d7e8-f9a0b1c2d3e4", "Code": 302, "Activity": "SignedDocumentOpened", "CreatedDateTime": "2016-06-15T23:40:30.0000000+02:00" }, { "Id": "d0e1f2a3-b4c5-46d7-e8f9-a0b1c2d3e4f5", "Code": 402, "Activity": "ReceiptOpened", "CreatedDateTime": "2016-06-15T23:40:35.0000000+02:00" }, { "Id": "a3b4c5d6-e7f8-49a0-b1c2-d3e4f5a6b7c8", "Code": 303, "Activity": "SignedDocumentDownloaded", "CreatedDateTime": "2016-06-15T23:42:30.0000000+02:00" } ], "RejectReason": null, "DelegateReason": null, "DelegateSignerEmail": null, "DelegateSignerName": null, "DelegateSignUrl": null, "SignUrl": "https://view.signhost.com/sign/d3c93bd6-f1ce-48e7-8c9c-c2babfdd4034", "SignedDateTime": "2016-06-15T23:38:10+02:00", "RejectDateTime": null, "CreatedDateTime": "2016-06-15T23:30:00.0000000+02:00", "SignerDelegationDateTime": null, "ModifiedDateTime": "2016-06-15T23:38:12.0000000+02:00", "ShowUrl": "https://view.signhost.com/show/document/b10ae331-af78-4e79-a39e-5b64693b6b68?signerId=d3c93bd6-f1ce-48e7-8c9c-c2babfdd4034", "ReceiptUrl": "https://view.signhost.com/show/receipt/b10ae331-af78-4e79-a39e-5b64693b6b68?signerId=d3c93bd6-f1ce-48e7-8c9c-c2babfdd4034", "Context": { "contract.pdf": {} } } ], "Receivers": [], "Reference": "", "PostbackUrl": null, "SignRequestMode": 2, "DaysToExpire": 30, "SendEmailNotifications": true, "Language": "en", "CreatedDateTime": "2016-06-15T23:30:00.0000000+02:00", "ModifiedDateTime": "2016-06-15T23:38:16.0000000+02:00", "CanceledDateTime": null, "Context": null, "Checksum": "b5a99e1de5b9e0e9915df09d3b819be188dae900" } ``` --- url: /guide/features/return-url.md --- # Return URLs When creating a transaction via the REST API, you can specify a `ReturnUrl` for each individual Signer. When the user signs or rejects the transaction, Signhost will redirect the browser to the return URL. The return URL will have the following query parameters appended: - `sh_transactionid` - The unique ID of the transaction - `sh_signerid` - The unique ID of the signer - `sh_signerstatus` - The status determined by the user action The `sh_signerstatus` parameter will have one of the following values: | Action | Example URL | | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `signed` | `https://signhost.com?sh_transactionid=b10ae331-af78-4e79-a39e-5b64693b6b68&sh_signerid=fa95495d-6c59-48e0-962a-a4552f8d6b85&sh_signerstatus=signed` | | `rejected` | `https://signhost.com?sh_transactionid=b10ae331-af78-4e79-a39e-5b64693b6b68&sh_signerid=fa95495d-6c59-48e0-962a-a4552f8d6b85&sh_signerstatus=rejected` | | Other | `https://signhost.com?sh_transactionid=b10ae331-af78-4e79-a39e-5b64693b6b68&sh_signerid=fa95495d-6c59-48e0-962a-a4552f8d6b85&sh_signerstatus=` or `https://signhost.com?sh_transactionid=b10ae331-af78-4e79-a39e-5b64693b6b68&sh_signerid=fa95495d-6c59-48e0-962a-a4552f8d6b85` | The query parameters provide an indication of the transaction, signer, and what the user chose to do. Depending on the `sh_signerstatus` value, you might want to show a different page to the user. :::info For any important processing however, you should not rely on this status, you must validate the actual transaction status using the Postback or API. ::: Once the user signed or rejected the transaction, the server will start processing the input. This means the user will be redirected to the return URL before the transaction status has been updated. Once the server has completed processing, a new postback will be sent with the new transaction status. --- url: /guide/features/sealing.md --- # Sealing and seal-only transactions Signhost supports sealing documents in any signing transaction and also offers seal-only transactions where only your organization applies a seal without involving external signers. ### What is sealing? A seal is an electronic organizational stamp applied by Signhost on behalf of your organization using a dedicated sealing certificate. The seal is embedded in the PDF and recorded in the transaction audit trail, so anyone can verify the integrity and origin of the document afterwards. ### Sealing vs signing A signer represents a natural person who signs a document, while a seal represents your organization as a legal entity. In a regular signing transaction, signers apply signatures and Signhost optionally applies a seal; in a seal-only transaction, no signers are invited and only the seal is applied. ### Seal types #### Advanced sealing (default) By default, sealing uses an **advanced electronic seal** that provides integrity protection and clear evidence that your organization sealed the document. This default seal is applied by Signhost without requiring you to manage certificates or cryptographic keys yourself. #### Qualified sealing (QSeal) With **QSeal** enabled, the seal is created with a qualified certificate linked to your organization as a legal entity, meeting higher assurance and regulatory standards. QSeal is a paid add-on that can be activated per environment or account, after which all sealed documents for that configuration are sealed using the qualified certificate. ### Benefits of using sealing Sealing lets you send the complete PDF to Signhost and receive it back fully sealed, instead of having to compute a hash, build a signature container, and inject timestamps yourself. Signhost handles the full sealing pipeline: hashing, signing with the appropriate (advanced or qualified) certificate, and applying a trusted timestamp, so your integration stays simple and you avoid dealing with low-level cryptography. ### Enabling sealing on a transaction Sealing is controlled via the `Seal` property on the Transaction model. When `Seal` is `true`, Signhost will seal the final document after all signing is completed. The `Seal` flag is visible both when creating a transaction and when retrieving it through `GET /api/transaction/{transactionId}`, so you can confirm that sealing was active. #### Example: Regular signing transaction with sealing ```json POST /api/transaction { "Seal": true, "Signers": [ { "Email": "user@example.com", "SendSignRequest": true, "SignRequestMessage": "Please sign this contract", "DaysToRemind": 7 } ], "SendEmailNotifications": true } ``` In this example, Signhost will apply a seal on behalf of your organization, then the signer will sign the document. ### Creating a seal-only transaction A seal-only transaction is a regular transaction with `Seal` set to `true` and without any signers. Signhost seals the uploaded document as soon as the transaction is started. This pattern is useful for internal archiving, issuing digitally sealed statements, or automatically sealing system-generated PDFs without human interaction. #### Example: Seal-only transaction **Create the transaction** ```json POST /api/transaction { "Seal": true, "Signers": [], "SendEmailNotifications": false, "Reference": "Monthly-report-2026-01" } ``` **Upload the document** ```http PUT /api/transaction/{transactionId}/file/file.pdf Authorization: APIKey {usertoken} Application: APPKey {appkey} Content-Type: application/pdf Digest: SHA-256=... ``` **Start the transaction** ```http PUT /api/transaction/{transactionId}/start Authorization: APIKey {usertoken} Application: APPKey {appkey} ``` Once the transaction is started, Signhost will seal the document and mark the transaction as completed (status `30`). ### Retrieving sealed documents and receipt Use `GET /api/transaction/{transactionId}/file/file.pdf` to download the sealed PDF once the transaction reaches an end status (for example, status `30` for completed). Use `GET /api/file/receipt/{transactionId}` to retrieve the evidential receipt that contains the full audit trail, including the information that a seal was applied to the document. #### Example: Download sealed document ```bash curl \ -H "Authorization: APIKey {usertoken}" \ -H "Application: APPKey {appkey}" \ https://api.signhost.com/api/transaction/{transactionId}/file/Report.pdf \ -o sealed-report.pdf ``` #### Example: Download receipt ```bash curl \ -H "Authorization: APIKey {usertoken}" \ -H "Application: APPKey {appkey}" \ https://api.signhost.com/api/file/receipt/{transactionId} \ -o receipt.pdf ``` ### Use cases #### Internal document archiving Seal official documents, such as board resolutions or policy updates, before storing them in your document management system to ensure their integrity and authenticity. #### Automated document generation Automatically seal system-generated invoices, statements, or reports without requiring manual signing, streamlining your document workflows. #### Multi-party contracts with organizational seal Combine signer signatures with an organizational seal to demonstrate that both individuals and your organization endorse the contract. ### Related resources - [Transaction endpoints](/openapi.md) - [Statuses & Activities](/guide/features/status.md) - [Postback service](/guide/features/postbacks.md) --- url: /guide/features/signature-location.md --- # Signature Location We provide three ways to specify the location for the signatures. ### 1. Signer tags in PDF You can specify the signature location in the PDF document. When you create the PDF that needs to be signed you can include the tag `{{Signer1}}`, `{{Signer2}}`, etc. at the location you want the signature displayed. If you do not want to show a `{{Signer}}` tag in your document you can give the tag the background colour of your document, in general this is the colour white. Although PDF is a document standard we strongly recommend to test the signer location and `{{Signer}}` tag to see if the location is correct. If you do not include the `{{Signer}}` tag, or you do not use another way to specify the location, we will use the default signing location: top right on the first page. :::warning Please be aware that including a lot of `{{Signer}}` tags will have an impact on the performance and signature validation process. For a digital signature we do not recommend to include a signature on every page. ::: ### 2. Upload JSON metadata A second method of specifying the signature location is by uploading a JSON metadata file after creating the transaction. For this method you do not need to prepare the PDF file in any way. The flow of making a transaction is basically the same as before, although this time you need to add file metadata to the transaction before uploading the file(s). For more information about this method please take a further look at: [Form Fields in PDFs - Approach 2: API-Generated Form Fields](/guide/guides/fillable-pdf-fields.md#approach-2-api-generated-form-fields). ### 3. Signature fields The third way of specifying the signature location is to make use of PDF Signature fields. ::: warning This method is not recommended as it requires external PDF tools. ::: To make sure that the signature fields are recognized they have to be named following a specific pattern: `Signature-x_y`. Where the `x` stands for the signer and the `y` for the signature number. For example: `Signature-1_2` will specify the placement for the second signature of the first signer. --- url: /guide/features/signing-flows.md --- # Signing Flows With the Signhost API it is possible to facilitate two different signing flows to the signer. **Invite Flow:** Signhost will send a Sign Request by email to the signer and will facilitate the workflow process by sending reminder emails and emailing the signed results to the signer and the originator. **Direct Flow:** The Signhost API returns a Sign URL that you will be able to use to directly redirect the signer on your portal or send the signer a message with the signing link yourself. :::tip By default the **Direct Flow** will be used when you create a transaction. ::: If you would like to make use of the **Invite flow**, set the Signer `SendSignRequest` parameter to `true` when creating a transaction. ### Reminders If you choose to make use of the **Invite flow** we will send a reminder email after 7 days. You can adjust the amount of days by setting the `DaysToRemind` parameter. A value of -1 disables reminders. ### Signing Artifacts When the transaction is successfully signed (Status=30) you will be able to GET the signed document and receipt with an HTTP GET request to `api/transaction/${TRANSACTION_ID}/file/{fileId}` or `api/file/receipt/${TRANSACTION_ID}`. For legal proof it is important to store both the signed document and the receipt. - The Sender will receive the signed documents and receipt per mail when the **SendEmailNotifications** is set to true. - The Signers will receive the signed documents and receipt per mail when the **SendSignConfirmation** is set to true. - The Receivers will always receive the signed documents per mail. --- url: /guide/features/status.md --- # Statuses & Activities Some of Signhost API endpoints return both the status of the transaction and signer activities. A list of all the transaction statuses as well as a list of all the signer activity statuses can be found below. :::tip Status 20 will be returned whenever there is processing involved by Signhost. ::: ### Transaction Status | Status | Message | | ------ | -------------------- | | 5 | Waiting for document | | 10 | Waiting for signer | | 20 | In progress | | 30 | Signed | | 40 | Rejected | | 50 | Expired | | 60 | Cancelled | | 70 | Failed | ### Signer Activity | Status | Message | | ------ | -------------------------- | | 101 | Invitation sent | | 103 | Opened | | 104 | Reminder sent | | 105 | Document opened | | 202 | Rejected | | 203 | Signed | | 204 | Signer delegated | | 301 | Signed document sent | | 302 | Signed document opened | | 303 | Signed document downloaded | | 401 | Receipt sent | | 402 | Receipt opened | | 403 | Receipt downloaded | --- url: /guide/guides/fillable-pdf-fields.md --- # Form Fields in PDFs This guide explains how to create transactions with form fields that signers can fill in during the signing process. There are two different approaches available, each suited to different use cases. ### Overview Form fields allow signers to provide additional information (such as addresses, phone numbers, or checkboxes) while signing documents. This is useful for: - **Collecting Additional Information**: Gather specific data like addresses, phone numbers, or other personal details - **Interactive Forms**: Create guided forms that lead signers through the information collection process - **Dynamic Content**: Allow signers to enter content that varies from one signer to another - **Signature Placement**: Position signature fields precisely where needed on the document ### Two Approaches Signhost offers two methods for adding form fields to PDFs: #### Approach 1: PDF-Embedded Form Fields Use this when you have a PDF that already contains form fields created in a PDF editor (like Adobe Acrobat). **Best for:** - Pre-designed forms with complex layouts - Reusing existing form templates - When you have control over PDF creation **Requirements:** - PDF must have embedded form fields (text boxes, checkboxes) - Contact [support@signhost.com](mailto:support@signhost.com?subject=PDF%20and%20formfields) for assistance creating PDFs with form fields **Limitations:** - Requires PDF preparation in advance - Limited to text fields and checkboxes #### Approach 2: API-Generated Form Fields Use this when you want to add form fields to any PDF dynamically via the Signhost API. **Best for:** - Adding fields to existing PDFs without modification - Dynamic field placement based on your application logic - Programmatically positioning signatures and form fields - Supporting multiple signers with different field sets **Requirements:** - Any PDF document (no special preparation needed) - Knowledge of where to place fields (coordinates or text-based positioning) **Advantages:** - Works with any PDF - Supports signatures, text fields, and checkboxes - More flexible field positioning options - Can be completely automated ### Comparison Table | Feature | PDF-Embedded Fields | API-Generated Fields | | --------------------- | ---------------------- | ---------------------------------- | | PDF Preparation | Required | Not required | | Supported Field Types | Text boxes, checkboxes | Text boxes, checkboxes, signatures | | Field Positioning | Fixed in PDF | Defined via API coordinates | | Setup Complexity | Requires PDF editor | Requires coordinate calculation | | Flexibility | Limited to PDF design | Highly flexible | | Best Use Case | Pre-designed forms | Dynamic form generation | *** ## Approach 1: PDF-Embedded Form Fields This approach uses form fields that are already embedded in your PDF document. ### Steps for PDF-Embedded Fields 1. **Create a transaction**: This will initialize a new transaction and return a `transactionId`. [`POST /api/transaction/`](/openapi/transactions/createTransaction.md) 2. **Add file metadata**: Upload JSON metadata that specifies which signer can fill which form fields. [`PUT /api/transaction/:transactionId/file/:fileId`](/openapi/files/uploadFile.md) (with JSON content) 3. **Add the PDF file**: Upload the actual PDF document with fillable form fields. [`PUT /api/transaction/:transactionId/file/:fileId`](/openapi/files/uploadFile.md) (with PDF content) 4. **Start the transaction**: Once all files are uploaded, start the transaction to notify the signers. [`PUT /api/transaction/:transactionId/start`](/openapi/transactions/startTransaction.md) #### 1. Create a Transaction First, create a new transaction with the [`POST /api/transaction/`](/openapi/transactions/createTransaction.md) endpoint that will contain your fillable PDF document. This will return a `transactionId` that you will use in subsequent requests. Example: ```bash curl \ -H "Authorization: APIKey ${API_KEY}" \ -H "Application: APPKey ${APP_KEY}" \ -H "Content-Type: application/json" \ -d '{ "Signers": [{ "Email": "john.doe@email.com", "Language": "en-US", "SendSignRequest": true, "SignRequestSubject": "Signature request", "SignRequestMessage": "Hi John, \n \n Please sign the document using the link below. \n \n Kind regards, \n \n Paul", "SendSignConfirmation": true, "DaysToRemind": 14, "Verifications": [ { "Type": "Scribble", "RequireHandsignature": true, "ScribbleName": "John Doe" } ] }], "SendEmailNotifications": true }' \ https://api.signhost.com/api/transaction/ ``` You should receive a response with the transaction details, including the `Id` which is your `transactionId`: ```json { "Id": "b2a9aca4-cd5e-4a21-b7f7-c08a9f2b2d57", "Status": 5, "Signers": [ { "Id": "Signer1", "Email": "john.doe@email.com", "Verifications": [ { "Type": "Scribble", "RequireHandsignature": true, "ScribbleNameFixed": false, "ScribbleName": "John Doe" } ], "SendSignRequest": true, "SendSignConfirmation": true, "SignRequestSubject": "Signature request", "SignRequestMessage": "Hi John, \n \n Please sign the document using the link below. \n \n Kind regards, \n \n Paul", "DaysToRemind": 14, "Language": "en-US", "ScribbleName": "John Doe" } ] } ``` The `Id` field contains your `transactionId` which you'll need for the following steps. #### 2. Add File Metadata Before uploading the actual PDF, you need to specify which signer can fill which form fields by uploading JSON metadata with the [`PUT /api/transaction/:transactionId/file/:fileId`](/openapi/files/uploadFile.md) endpoint. **Parameters:** - `transactionId`: The ID from the transaction you just created - `fileId`: A unique identifier for your document (e.g., "Contract.pdf") - `Content-Type`: Must be `application/json` for metadata **Metadata Structure:** ```json { "DisplayName": "Your personal contract", "Signers": { "Signer1": { "FormSets": ["Contract.pdf"] } } } ``` The `FormSets` array should contain the `fileId` of the document that contains the fillable fields. Example: ```bash curl \ -H "Authorization: APIKey ${API_KEY}" \ -H "Application: APPKey ${APP_KEY}" \ -H "Content-Type: application/json" \ -X PUT \ -d '{ "DisplayName": "Your personal contract", "Signers": { "Signer1": { "FormSets": ["Contract.pdf"] } } }' \ https://api.signhost.com/api/transaction/b2a9aca4-cd5e-4a21-b7f7-c08a9f2b2d57/file/Contract.pdf ``` The response will indicate that the system is now awaiting the PDF document. #### 3. Add the PDF File Now upload the actual PDF document containing the fillable form fields using the [`PUT /api/transaction/:transactionId/file/:fileId`](/openapi/files/uploadFile.md) endpoint. **Parameters:** - `transactionId`: The ID from the transaction - `fileId`: The same identifier used in the metadata step - `Content-Type`: Must be `application/pdf` for PDF files Example: ```bash curl \ -H "Authorization: APIKey ${API_KEY}" \ -H "Application: APPKey ${APP_KEY}" \ -H "Content-Type: application/pdf" \ -X PUT \ -T Contract.pdf \ https://api.signhost.com/api/transaction/b2a9aca4-cd5e-4a21-b7f7-c08a9f2b2d57/file/Contract.pdf ``` This uploads the `Contract.pdf` file to the transaction. The file must contain fillable form fields that match the configuration in your metadata. #### 4. Start the Transaction Once both the metadata and PDF file are uploaded, start the transaction with the [`PUT /api/transaction/:transactionId/start`](/openapi/transactions/startTransaction.md) endpoint to notify the signers that they can now view, fill in the form fields, and sign the document. Example: ```bash curl \ -H "Authorization: APIKey ${API_KEY}" \ -H "Application: APPKey ${APP_KEY}" \ -X PUT \ https://api.signhost.com/api/transaction/b2a9aca4-cd5e-4a21-b7f7-c08a9f2b2d57/start ``` This will start the transaction and send notifications to the signers that they can now view, fill in the form fields, and sign the document. ### Retrieving Form Field Data When a signer completes the form fields and signs the document, you'll receive the filled-in data through postbacks (webhooks) or by retrieving the transaction details. #### Postback The completed form field data will be included in the `Context` property of each signer: ```json { "Id": "b2a9aca4-cd5e-4a21-b7f7-c08a9f2b2d57", "Status": 30, "Signers": [ { "Email": "john.doe@email.com", "RequireScribble": true, "SendSignRequest": true, "SignRequestMessage": "Hello, could you please sign this document and fill in the required fields? Best regards, John Doe", "DaysToRemind": 15, "ScribbleName": "John Doe", "ScribbleNameFixed": false, "Context": { "Contract.pdf": { "addressline1": "123 Main Street", "addressline2": "Apartment 4B", "city": "Amsterdam", "postalcode": "1012 AB", "country": "Netherlands" } } } ] } ``` #### API You can also retrieve the form field data by calling the [Get Transaction](/openapi/transactions/getTransaction.md) endpoint: ```bash curl \ -H "Authorization: APIKey ${API_KEY}" \ -H "Application: APPKey ${APP_KEY}" \ https://api.signhost.com/api/transaction/b2a9aca4-cd5e-4a21-b7f7-c08a9f2b2d57 ``` *** ## Approach 2: API-Generated Form Fields This approach allows you to dynamically create form fields on any PDF document through the API, without requiring the PDF to have pre-existing form fields. ### Steps for API-Generated Fields #### 1. Create a Transaction First, create a new transaction using the [`POST /api/transaction/`](/openapi/transactions/createTransaction.md) endpoint. Example: ```bash curl \ -H "Authorization: APIKey ${API_KEY}" \ -H "Application: APPKey ${APP_KEY}" \ -H "Content-Type: application/json" \ -d '{ "Signers": [{ "Email": "john.doe@email.com", "Language": "en-US", "SendSignRequest": true, "SignRequestSubject": "Signature request", "SignRequestMessage": "Hi John, \n \n Please sign the document using the link below. \n \n Kind regards, \n \n Paul", "SendSignConfirmation": true, "DaysToRemind": 14, "Verifications": [ { "Type": "Scribble", "RequireHandsignature": true, "ScribbleName": "John Doe" } ] }], "SendEmailNotifications": true }' \ https://api.signhost.com/api/transaction/ ``` You should receive a response with the transaction details: ```json { "Id": "b2a9aca4-cd5e-4a21-b7f7-c08a9f2b2d57", "Status": 5, "Signers": [ { "Id": "Signer1", "Email": "john.doe@email.com", ... } ] } ``` Note the following fields from the response: - `b2a9aca4-cd5e-4a21-b7f7-c08a9f2b2d57` This is your `transactionId` which you will use in subsequent requests. - `Signer1` This is your `signerId` which you will use in the metadata step. #### 2. Add File Metadata with Form Sets Upload JSON metadata that defines the form fields and their locations using the [`PUT /api/transaction/:transactionId/file/:fileId`](/openapi/files/uploadFile.md) endpoint. **Parameters:** - `transactionId`: The ID from the transaction you just created - `fileId`: A unique identifier for your document (e.g., "Contract.pdf") - `Content-Type`: Must be `application/json` for metadata **Form Sets Structure:** The `FormSets` object in the metadata defines the fields and their locations. Each Form Set can contain multiple fields with different types: - **Signature**: A field where the signer places their signature - **SingleLine**: A single-line text input field - **Check**: A checkbox field **Field Positioning:** You can position fields using coordinates: - `Right`: Distance from the left edge of the page (in points) - `Top`: Distance from the top edge of the page (in points) - `Width`: Width of the field (in points) - `Height`: Height of the field (in points) - `PageNumber`: The page number where the field should appear (1-based) **Note:** It is recommended to use a width of 140 and a height of 70 for signature fields. Example with multiple field types: ```bash curl \ -H "Authorization: APIKey ${API_KEY}" \ -H "Application: APPKey ${APP_KEY}" \ -H "Content-Type: application/json" \ -X PUT \ -d '{ "DisplayName": "Your personal contract", "Signers": { "Signer1": { "FormSets": ["SampleFormset"] } }, "FormSets": { "SampleFormset": { "SignatureOne": { "Type": "Signature", "Location": { "Right": 10, "Top": 10, "PageNumber": 1, "Width": 140, "Height": 70 } }, "AddressLine1": { "Type": "SingleLine", "Location": { "Right": 50, "Top": 100, "PageNumber": 1, "Width": 200, "Height": 20 } }, "AddressLine2": { "Type": "SingleLine", "Location": { "Right": 50, "Top": 130, "PageNumber": 1, "Width": 200, "Height": 20 } }, "AgreeToTerms": { "Type": "Check", "Location": { "Right": 50, "Top": 160, "PageNumber": 1, "Width": 15, "Height": 15 } } } } }' \ https://api.signhost.com/api/transaction/b2a9aca4-cd5e-4a21-b7f7-c08a9f2b2d57/file/Contract.pdf ``` **Important Notes:** - The `FormSets` array in the `Signers` section should reference one or more keys in the `FormSets` dictionary - A Form Set should be unique within a transaction - If you provide a duplicate Form Set key, the old one will be overwritten with the new one - For more information about Form Sets, see the [Form Sets guide](/guide/features/formsets.md) #### 3. Add the PDF File Upload the actual PDF document using the [`PUT /api/transaction/:transactionId/file/:fileId`](/openapi/files/uploadFile.md) endpoint. **Parameters:** - `transactionId`: The ID from the transaction - `fileId`: The same identifier used in the metadata step - `Content-Type`: Must be `application/pdf` for PDF files Example: ```bash curl \ -H "Authorization: APIKey ${API_KEY}" \ -H "Application: APPKey ${APP_KEY}" \ -H "Content-Type: application/pdf" \ -X PUT \ -T Contract.pdf \ https://api.signhost.com/api/transaction/b2a9aca4-cd5e-4a21-b7f7-c08a9f2b2d57/file/Contract.pdf ``` #### 4. Start the Transaction Start the transaction using the [`PUT /api/transaction/:transactionId/start`](/openapi/transactions/startTransaction.md) endpoint. Example: ```bash curl \ -H "Authorization: APIKey ${API_KEY}" \ -H "Application: APPKey ${APP_KEY}" \ -X PUT \ https://api.signhost.com/api/transaction/b2a9aca4-cd5e-4a21-b7f7-c08a9f2b2d57/start ``` *** ## Retrieving Form Field Data Regardless of which approach you use, retrieving the completed form field data works the same way. ### Via Postback When a signer completes the form fields and signs the document, you'll receive the filled-in data through postbacks (webhooks). The completed form field data will be included in the `Context` property of each signer: ```json { "Id": "b2a9aca4-cd5e-4a21-b7f7-c08a9f2b2d57", "Status": 30, "Signers": [ { "Email": "john.doe@email.com", "RequireScribble": true, "SendSignRequest": true, "SignRequestMessage": "Hello, could you please sign this document and fill in the required fields? Best regards, John Doe", "DaysToRemind": 15, "ScribbleName": "John Doe", "ScribbleNameFixed": false, "Context": { "Contract.pdf": { "addressline1": "123 Main Street", "addressline2": "Apartment 4B", "city": "Amsterdam", "postalcode": "1012 AB", "country": "Netherlands" } } } ] } ``` ### Via API You can also retrieve the form field data by calling the [Get Transaction](/openapi/transactions/getTransaction.md) endpoint: ```bash curl \ -H "Authorization: APIKey ${API_KEY}" \ -H "Application: APPKey ${APP_KEY}" \ https://api.signhost.com/api/transaction/b2a9aca4-cd5e-4a21-b7f7-c08a9f2b2d57 ``` *** ## Prerequisites You will need: - An APP key and User Token to authenticate your requests - For **PDF-Embedded Fields**: A PDF document with embedded form fields (contact [support@signhost.com](mailto:support@signhost.com?subject=PDF%20and%20formfields) for assistance) - For **API-Generated Fields**: Any PDF document and knowledge of where to place fields See the [Authentication guide](/guide/features/authentication.md) for more details on how to obtain authentication tokens. ## Supported Form Field Types Both PDF-Embedded and API-Generated approaches support the following field types: | Field Type | Description | | ------------------------- | ------------------------------------------------------------ | | Text fields (single line) | Allow signers to enter text information | | Checkboxes | Allow signers to select or deselect options | | Signatures | Allow signers to place their signature at specific locations | ## Best Practices 1. **Choose the right approach**: Use PDF-Embedded fields for pre-designed forms with complex layouts, and API-Generated fields for dynamic scenarios or when you need to add fields to existing PDFs. 2. **Test thoroughly**: Test your implementation in a development environment, especially when working with coordinate-based positioning. 3. **Clear field names**: Use descriptive names for your form fields that clearly indicate what information is expected. 4. **Validate data**: Always validate the form field data you receive through postbacks or API calls. 5. **Handle errors gracefully**: Implement proper error handling for cases where form fields are not filled correctly or coordinates are invalid. 6. **User guidance**: Provide clear instructions to signers about what information is required in each field. 7. **Signature field sizing**: When using API-Generated fields, use the recommended dimensions of 140x70 points for signature fields. 8. **Coordinate calculation**: For API-Generated fields, carefully calculate coordinates. Consider using test PDFs to verify field positioning before production use. ## Conclusion You now understand both approaches for creating transactions with form fields using the Signhost API: - **PDF-Embedded Fields**: Best for pre-designed forms with existing field definitions - **API-Generated Fields**: Best for dynamic field placement and working with any PDF Both approaches allow signers to fill in required information during the signing process, and you'll receive the completed data through postbacks or by retrieving transaction details. For more information: - [Postbacks guide](/guide/features/postbacks.md) - Learn about webhooks and status updates - [Form Sets guide](/guide/features/formsets.md) - Detailed information about Form Sets - [API Reference](/openapi/index.md) - Complete API documentation --- url: /guide/guides/multidocument-transaction.md --- # Multi-document Transaction This guide will explain how to create a transaction with multiple documents. ### Overview A transaction can contain multiple documents that need to be signed by one or more signers. Each document can have its own signature locations, and you can specify the order in which the documents are signed. To create a multi-document transaction, you will follow these steps: 1. **Create a transaction**: This will initialize a new transaction and return a `transactionId`. [`POST /api/transaction/`](/openapi/transactions/createTransaction.md) 2. **Add files to the transaction**: For each document you want to include, you will upload the file using the `transactionId` and a unique `fileId`. [`PUT /api/transaction/:transactionId/file/:fileId`](/openapi/files/uploadFile.md) 3. **Start the transaction**: Once all files are uploaded, you will start the transaction to notify the signers. [`PUT /api/transaction/:transactionId/start`](/openapi/transactions/startTransaction.md) ### Prerequisites You will need an APP key and User Token to authenticate your requests. Make sure you have these ready before proceeding. See the [Authentication guide](/guide/features/authentication.md) for more details on how to obtain these tokens. ### Detailed Steps #### 1. Create a Transaction Use the [`POST /api/transaction/`](/openapi/transactions/createTransaction.md) endpoint to create a new transaction. This will return an `id` that you will use in subsequent requests. Example: ```bash curl \ -H "Authorization: APIKey ${API_KEY}" \ -H "Application: APPKey ${APP_KEY}" \ -H "Content-Type: application/json" \ -d '{ "Signers": [ { "Email": "user@example.com", "Verifications": [ { "Type": "Scribble", "ScribbleName": "John Doe", "ScribbleNameFixed": false } ], "SendSignRequest": true, "SignRequestMessage": "Hello, could you please sign this document? Best regards, John Doe", "DaysToRemind": 15 } ], "SendEmailNotifications": true }' https://api.signhost.com/api/transaction/ ``` You should receive a response with the transaction details, including the `Id` which is your `transactionId`. ```json { "Id": "b2a9aca4-cd5e-4a21-b7f7-c08a9f2b2d57", "Status": 5, "Signers": [ { "Email": "user@example.com", "RequireScribble": true, "SendSignRequest": true, "SignRequestMessage": "Hello, could you please sign this document? Best regards, John Doe", "DaysToRemind": 15, "ScribbleName": "John Doe", "ScribbleNameFixed": false } } } ``` The id `b2a9aca4-cd5e-4a21-b7f7-c08a9f2b2d57` is the transactionId and you will need this in the next requests. #### 2. Add File to Transaction Use the [`PUT /api/transaction/:transactionId/file/:fileId`](/openapi/files/uploadFile.md) endpoint to add files to the transaction. Each file must have a unique `fileId`. Example: ```bash curl \ -H "Authorization: APIKey ${API_KEY}" \ -H "Application: APPKey ${APP_KEY}" \ -H "Content-Type: application/pdf" \ -X PUT \ -T Contract1.pdf \ https://api.signhost.com/api/transaction/b2a9aca4-cd5e-4a21-b7f7-c08a9f2b2d57/file/Contract1.pdf curl \ -H "Authorization: APIKey ${API_KEY}" \ -H "Application: APPKey ${APP_KEY}" \ -H "Content-Type: application/pdf" \ -X PUT \ -T Contract2.pdf \ https://api.signhost.com/api/transaction/b2a9aca4-cd5e-4a21-b7f7-c08a9f2b2d57/file/Contract2.pdf ``` This will upload the `Contract1.pdf` and `Contract2.pdf` files to transaction `b2a9aca4-cd5e-4a21-b7f7-c08a9f2b2d57` and set the `fileId` to `Contract1.pdf` and `Contract2.pdf` respectively. #### 3. Start the Transaction Use the [`PUT /api/transaction/:transactionId/start`](/openapi/transactions/startTransaction.md) endpoint to start the transaction. This will notify the signers that they can now view and sign the documents. Example: ```bash curl \ -H "Authorization: APIKey ${API_KEY}" \ -H "Application: APPKey ${APP_KEY}" \ https://api.signhost.com/api/transaction/b2a9aca4-cd5e-4a21-b7f7-c08a9f2b2d57/start ``` This will start the transaction and notify the signers that they can now view and sign the documents. ### Conclusion You have now created a multi-document transaction using the Signhost API. You can repeat the process to add more documents or signers as needed. For more details on each endpoint, refer to the [API Reference](/openapi/index.md) or the specific guides for each feature. --- url: /guide/guides/multisigner-transaction.md --- # --- url: /guide/guides/signer-tag.md --- # --- url: /about.md --- # About Entrust Signhost Entrust Signhost is a secure and flexible electronic/digital signing service that enables users to sign or seal documents digitally. Whether you're signing contracts, onboarding customers and employees, or sealing official documents, Entrust Signhost offers a flexible, high-assurance and trusted solution to support your digital transformation, with multiple features helping to ensure frictionless user experience, and signature types that align with EU eIDAS. [Explore Signhost](https://www.entrust.com/products/electronic-digital-signing/signhost) **Features** - Available via web portal, mobile app and REST API - Support for identity providers / eID schemes, remote ID verification, SMS, and Email OTP for secure electronic signatures on contracts, forms, declarations, and more - Advanced and Qualified Electronic Signatures (AES & QES) in compliance with EU eIDAS regulation - Advanced and Qualified document sealing in compliance with EU eIDAS regulation - Document output in PAdES format - Native integration with Entrust Workflow Studio and Entrust IDaaS **REST API Overview** This document outlines the implementation of the Signhost REST API, which serves as the foundational interface for all official Signhost API methods. It is the most direct and flexible way to interact with the signing features programmatically. This reference is intended for developers building solutions using the Signhost.com REST API. **Support** If you have any questions or suggestions for improving the API, please reach out: Email: [support@signhost.com](mailto:support@signhost.com) Phone: [+31 23 737 0046](tel:+31237370046) Website: [signhost.com](https://www.signhost.com) **Documentation Feedback** Found an error, outdated information, or unclear explanation in our documentation? Please help us improve by [reporting an issue on GitHub](https://github.com/evidos/evidos.github.io/issues). Your feedback helps keep our documentation accurate and helpful for everyone. --- url: /blog/index.md --- # Signhost Developer Blog Check here for the latest articles and release announcements about the Signhost REST API and SDKs. ## [Deprecating Weak Algorithms](/blog/post/2025-04-01-deprecating-weak-algorithms.md) **April 01, 2025** The Signhost REST API is deprecating weak algorithms in the File Upload API. ## [Updated Timezone Handling](/blog/post/2015-12-07-update-timezone-information-on-data-retrieval.md) **December 07, 2015** The REST API has fixed timezone issues with date/time fields in the REST API. --- url: /blog/post/2015-12-07-update-timezone-information-on-data-retrieval.md --- # Updated timezone information on data retrieval _December 7, 2015_ When creating a transaction all datetime properties we return, as always, include timezone information. For example when doing a POST on /api/transaction you would receive the following JSON response (this is an abbreviated example): ```json { "Id": "4e60e355-f875-4e81-8b44-37d7473388e2", "Status": 20, "File": { "Id": "efaba45b-abd2-4a72-82f1-61cefda89b6d", "Name": "contract.pdf" }, "Signers": [ { "Id": "b2b861dc-d0ce-4733-b6d5-e90243213f74", "Email": "user@example.com", "SendSignRequest": true, "SignRequestMessage": "Hello, could you please sign this document? Best regards, John Doe", "CreatedDateTime": "2015-12-07T15:49:30.7415254+01:00", "ModifiedDateTime": "2015-12-07T15:49:30.7415254+01:00" } ], "CreatedDateTime": "2015-12-07T15:49:30.7415254+01:00", "ModifiedDateTime": "2015-12-07T15:49:30.7415254+01:00" } ``` The CreatedDateTime property contains the value "2015-12-07T15:49:30.7415254\*\*+01:00\*\*". As can be seen, there is a "+01:00" timezone information included. This is an absolute point in time, this is correct behaviour. Unfortunately sometimes (particularly on older transactions) you might have noticed that we did not include the timezone information and the "+01:00" got lost. For example, when doing a GET on the previous example you would receive: ```json { "Id": "4e60e355-f875-4e81-8b44-37d7473388e2", "Status": 20, "File": { "Id": "efaba45b-abd2-4a72-82f1-61cefda89b6d", "Name": "contract.pdf" }, "Signers": [ { "Id": "b2b861dc-d0ce-4733-b6d5-e90243213f74", "Email": "user@example.com", "SendSignRequest": true, "SignRequestMessage": "Hello, could you please sign this document? Best regards, John Doe", "CreatedDateTime": "2015-12-07T15:49:30.7415254", "ModifiedDateTime": "2015-12-07T15:49:30.7415254" } ], "CreatedDateTime": "2015-12-07T15:49:30.7415254", "ModifiedDateTime": "2015-12-07T15:49:30.7415254" } ``` Strictly speaking this should be translated to a local time. That would mean that when you are in a different timezone then we are, you might have given it a different timezone offset (if you performed this conversion). On newer requests we include the timezone information on all datetime properties where we have this data available. This means on a GET or webhook postback the result will be the same as in the first example, including the "+01:00" (or a different timezone if applicable). ### What does this mean for you? All datetime properties are still formatted according to [ISO8601](https://www.iso.org/iso/iso8601). When you are using a correct JSON parser this should cause no problems for you. We are not changing the actual datetime values (no conversion is applied) but only including extra information. --- url: /blog/post/2025-04-01-deprecating-weak-algorithms.md --- _April 01, 2025_ # Strengthening Security: Deprecating Weak Hash Algorithms in File Upload API As part of our ongoing commitment to security best practices, we're announcing the deprecation of weak cryptographic hash algorithms in the Signhost REST API file upload endpoint. This change enhances the security of document integrity verification and aligns with modern cryptographic standards. ## What's Changing The `/api/transaction/${TRANSACTION_ID}/file/{fileId}` endpoint will no longer accept MD5 and SHA-1 digest headers. These algorithms have known vulnerabilities and are considered cryptographically weak by current security standards. ### Affected Algorithms (Being Deprecated) - **MD5** - Vulnerable to collision attacks since 2004 - **SHA-1** - Deprecated by NIST and major browsers due to collision vulnerabilities ### Supported Algorithms (Recommended) - **SHA-256** - Industry standard, secure hash algorithm - **SHA-512** - Enhanced security with longer hash length ## Why This Change Matters Cryptographic hash algorithms are essential for verifying file integrity during upload. Weak algorithms like MD5 and SHA-1 can be exploited by attackers to create malicious files that produce the same hash as legitimate documents, potentially compromising document authenticity. By requiring stronger algorithms, we ensure: - **Enhanced Security**: Protection against collision and preimage attacks - **Compliance**: Alignment with industry security standards (NIST, FIPS) - **Future-Proofing**: Preparation for evolving security requirements ## Migration Guide ### Current Implementation (Deprecated) ```bash # ❌ Will be rejected curl -X PUT "https://api.signhost.com/api/transaction/${TRANSACTION_ID}/file/document.pdf" \ -H "Authorization: APIKey ${API_KEY}" \ -H "Application: APPKey ${APP_KEY}" \ -H "Content-Type: application/pdf" \ -H "Digest: MD5=XrY7u+Ae7tCTyyK7j1rNww==" \ --data-binary @document.pdf ``` ### Updated Implementation (Recommended) ```bash # ✅ Use SHA-256 for secure file integrity verification curl -X PUT "https://api.signhost.com/api/transaction/${TRANSACTION_ID}/file/document.pdf" \ -H "Authorization: APIKey ${API_KEY}" \ -H "Application: APPKey ${APP_KEY}" \ -H "Content-Type: application/pdf" \ -H "Digest: SHA-256=HtHRpLOZBEMnTpQS6Zn12veC4uhjtMwamfVAwmPQPmE=" \ --data-binary @document.pdf ``` ### Code Examples #### C# (.NET) ```csharp using System.Security.Cryptography; public static string GenerateSHA256Digest(byte[] fileBytes) { using (var sha256 = SHA256.Create()) { var hash = sha256.ComputeHash(fileBytes); var base64Hash = Convert.ToBase64String(hash); return $"SHA-256={base64Hash}"; } } ``` ## SDK Updates The official .NET SDK has long used SHA-256 by default, ensuring secure file uploads. Other SDKs and libraries should be updated to use SHA-256 or SHA-512 for digest calculations. ## Need Help? If you have questions about this migration: - **Documentation**: [File Upload API Reference](/openapi/files/uploadFile.md) - **Support**: Contact [support@signhost.com](mailto:support@signhost.com) *** This change reinforces our commitment to providing a secure, enterprise-grade digital signing platform. We appreciate your cooperation in maintaining the highest security standards for document integrity verification. _The Signhost Developer Team_ --- url: /index.md --- # Entrust Signhost Developer Hub > Integrate document signing into your application quickly and easily. [Get Started](/guide/start/introduction) | [API Reference](/openapi) | [Try Live API](/live-api) ## Features - [ **Lightning-Fast Integration**](/guide/start/introduction): Get your first digital signature flowing in under 5 minutes with our intuitive REST API and comprehensive documentation. - [ **Multiple SDKs & Languages**](/guide/start/introduction#signhost-libraries): Use the official C# .NET SDK or generate your own using the OpenAPI specification. - [ **Enterprise-Grade Security**](https://www.entrust.com/legal-compliance): Built on Entrust's trusted infrastructure with advanced encryption, audit trails, and compliance with eIDAS, GDPR, and SOC 2. - [ **Smart Identity Verification**](/openapi/models/SignerVerification): From simple email to advanced eID (DigiD, eHerkenning), biometrics, and national identity systems - verify signers your way. - [ **Multi-Document Workflows**](/guide/guides/multidocument-transaction): Handle complex signing workflows with multiple documents, sequential or parallel signing, and automated reminders. - [ **Real-Time Status Updates**](/guide/features/postbacks): Track every step with webhooks, real-time status updates, and detailed activity logs for complete transparency. - [ **Global Reach**](/about): Support for 20+ languages and localized experiences across Europe with country-specific compliance and branding. - [ **Interactive API Explorer**](/live-api): Test endpoints live with our Scalar API explorer, complete with examples, schemas, and instant feedback. - [ **AI-Ready Documentation**](/llms-full.txt): LLM-optimized content with structured llms.txt files following industry standards for seamless AI integration and training. --- url: /live-api.md --- --- url: /openapi/files/downloadFile.md --- # Download a file from a transaction **Method:** `GET` **Path:** `/api/transaction/{transactionId}/file/{fileId}` Retrieves a specific file from a transaction. The file is returned as a PDF document. The user must be the creator of the transaction to access the file. ## Parameters | Name | In | Type | Required | Description | | ----------------- | ---- | ------ | -------- | ------------------------------------------------------------------- | | **transactionId** | path | string | Yes |

The unique identifier of the transaction containing the file

| | **fileId** | path | string | Yes |

The unique identifier of the file within the transaction

| ## Responses ### 200 File retrieved successfully Content-Type: `application/pdf` **Type:** string ### 401 Unauthorized - user not creator of transaction Content-Type: `application/json` **Schema:** [ErrorResponse](/openapi/models/ErrorResponse.md) #### Properties | Name | Type | Description | | ------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **type** | String |

A URI reference identifying the problem type. This property is always present in RFC 7807 responses.

| | **title** | String |

A short, human-readable summary of the problem

| | **detail** | String |

A human-readable explanation specific to this occurrence of the problem

| | **status** | Integer |

The HTTP status code

| | **instance** | String |

A URI reference that identifies the specific occurrence of the problem

| | **Message** | String |

Deprecated (Legacy Format Only): Contains the error message in the old response format. This property appears alone in legacy error responses and is mutually exclusive with the RFC 7807 properties above. Will be phased out as we complete our transition to RFC 7807.

| ### 403 Forbidden - user is not the creator of the transaction Content-Type: `application/json` #### Properties | Name | Type | Description | | ----------- | ------ | ----------- | | **Message** | String | | ### 404 File not found or not yet processed Content-Type: `application/json` **Schema:** [ErrorResponse](/openapi/models/ErrorResponse.md) #### Properties | Name | Type | Description | | ------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **type** | String |

A URI reference identifying the problem type. This property is always present in RFC 7807 responses.

| | **title** | String |

A short, human-readable summary of the problem

| | **detail** | String |

A human-readable explanation specific to this occurrence of the problem

| | **status** | Integer |

The HTTP status code

| | **instance** | String |

A URI reference that identifies the specific occurrence of the problem

| | **Message** | String |

Deprecated (Legacy Format Only): Contains the error message in the old response format. This property appears alone in legacy error responses and is mutually exclusive with the RFC 7807 properties above. Will be phased out as we complete our transition to RFC 7807.

| **Example:** ```json { "Message": "Either the file or transaction does not exist or the file has not yet been processed." } ``` ### 410 Gone - the document is no longer available (archived or deleted) Content-Type: `application/json` **Schema:** [ErrorResponse](/openapi/models/ErrorResponse.md) #### Properties | Name | Type | Description | | ------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **type** | String |

A URI reference identifying the problem type. This property is always present in RFC 7807 responses.

| | **title** | String |

A short, human-readable summary of the problem

| | **detail** | String |

A human-readable explanation specific to this occurrence of the problem

| | **status** | Integer |

The HTTP status code

| | **instance** | String |

A URI reference that identifies the specific occurrence of the problem

| | **Message** | String |

Deprecated (Legacy Format Only): Contains the error message in the old response format. This property appears alone in legacy error responses and is mutually exclusive with the RFC 7807 properties above. Will be phased out as we complete our transition to RFC 7807.

| **Example:** ```json { "Message": "The document is no longer available" } ``` ### 500 Internal server error Content-Type: `application/json` **Schema:** [ErrorResponse](/openapi/models/ErrorResponse.md) #### Properties | Name | Type | Description | | ------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **type** | String |

A URI reference identifying the problem type. This property is always present in RFC 7807 responses.

| | **title** | String |

A short, human-readable summary of the problem

| | **detail** | String |

A human-readable explanation specific to this occurrence of the problem

| | **status** | Integer |

The HTTP status code

| | **instance** | String |

A URI reference that identifies the specific occurrence of the problem

| | **Message** | String |

Deprecated (Legacy Format Only): Contains the error message in the old response format. This property appears alone in legacy error responses and is mutually exclusive with the RFC 7807 properties above. Will be phased out as we complete our transition to RFC 7807.

| --- url: /openapi/files/getReceipt.md --- # Download receipt for a transaction **Method:** `GET` **Path:** `/api/file/receipt/{transactionId}` Downloads the receipt for a completed transaction (Status=30). The receipt contains information about the signing process and serves as proof of the completed transaction. ## Parameters | Name | In | Type | Required | Description | | ----------------- | ---- | ------ | -------- | ----------------------------------------------------------- | | **transactionId** | path | string | Yes |

The transaction ID for which to download the receipt

| ## Responses ### 200 Receipt file successfully retrieved Content-Type: `application/pdf` **Type:** string ### 401 Authorization denied - user not authorized to access this transaction Content-Type: `application/json` **Schema:** [ErrorResponse](/openapi/models/ErrorResponse.md) #### Properties | Name | Type | Description | | ------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **type** | String |

A URI reference identifying the problem type. This property is always present in RFC 7807 responses.

| | **title** | String |

A short, human-readable summary of the problem

| | **detail** | String |

A human-readable explanation specific to this occurrence of the problem

| | **status** | Integer |

The HTTP status code

| | **instance** | String |

A URI reference that identifies the specific occurrence of the problem

| | **Message** | String |

Deprecated (Legacy Format Only): Contains the error message in the old response format. This property appears alone in legacy error responses and is mutually exclusive with the RFC 7807 properties above. Will be phased out as we complete our transition to RFC 7807.

| **Example:** ```json { "Message": "Authorization has been denied for this request." } ``` ### 404 Receipt not found Content-Type: `application/json` **Schema:** [ErrorResponse](/openapi/models/ErrorResponse.md) #### Properties | Name | Type | Description | | ------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **type** | String |

A URI reference identifying the problem type. This property is always present in RFC 7807 responses.

| | **title** | String |

A short, human-readable summary of the problem

| | **detail** | String |

A human-readable explanation specific to this occurrence of the problem

| | **status** | Integer |

The HTTP status code

| | **instance** | String |

A URI reference that identifies the specific occurrence of the problem

| | **Message** | String |

Deprecated (Legacy Format Only): Contains the error message in the old response format. This property appears alone in legacy error responses and is mutually exclusive with the RFC 7807 properties above. Will be phased out as we complete our transition to RFC 7807.

| **Example:** ```json { "Message": "receipt not found." } ``` Content-Type: `text/plain` **Type:** string **Example:** ``` receipt not found. ``` ### 406 Not Acceptable - unsupported Accept header Content-Type: `application/json` **Schema:** [ErrorResponse](/openapi/models/ErrorResponse.md) #### Properties | Name | Type | Description | | ------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **type** | String |

A URI reference identifying the problem type. This property is always present in RFC 7807 responses.

| | **title** | String |

A short, human-readable summary of the problem

| | **detail** | String |

A human-readable explanation specific to this occurrence of the problem

| | **status** | Integer |

The HTTP status code

| | **instance** | String |

A URI reference that identifies the specific occurrence of the problem

| | **Message** | String |

Deprecated (Legacy Format Only): Contains the error message in the old response format. This property appears alone in legacy error responses and is mutually exclusive with the RFC 7807 properties above. Will be phased out as we complete our transition to RFC 7807.

| **Example:** ```json { "Message": "The requested content type is not supported." } ``` ### 410 Gone - receipt is no longer available (archived or deleted) Content-Type: `application/json` **Schema:** [ErrorResponse](/openapi/models/ErrorResponse.md) #### Properties | Name | Type | Description | | ------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **type** | String |

A URI reference identifying the problem type. This property is always present in RFC 7807 responses.

| | **title** | String |

A short, human-readable summary of the problem

| | **detail** | String |

A human-readable explanation specific to this occurrence of the problem

| | **status** | Integer |

The HTTP status code

| | **instance** | String |

A URI reference that identifies the specific occurrence of the problem

| | **Message** | String |

Deprecated (Legacy Format Only): Contains the error message in the old response format. This property appears alone in legacy error responses and is mutually exclusive with the RFC 7807 properties above. Will be phased out as we complete our transition to RFC 7807.

| **Example:** ```json { "Message": "The receipt is no longer available" } ``` ### 500 Internal server error Content-Type: `application/json` **Schema:** [ErrorResponse](/openapi/models/ErrorResponse.md) #### Properties | Name | Type | Description | | ------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **type** | String |

A URI reference identifying the problem type. This property is always present in RFC 7807 responses.

| | **title** | String |

A short, human-readable summary of the problem

| | **detail** | String |

A human-readable explanation specific to this occurrence of the problem

| | **status** | Integer |

The HTTP status code

| | **instance** | String |

A URI reference that identifies the specific occurrence of the problem

| | **Message** | String |

Deprecated (Legacy Format Only): Contains the error message in the old response format. This property appears alone in legacy error responses and is mutually exclusive with the RFC 7807 properties above. Will be phased out as we complete our transition to RFC 7807.

| **Example:** ```json { "Message": "An internal error occurred while processing the request." } ``` --- url: /openapi/files/uploadFile.md --- # Upload PDF file or file metadata **Method:** `PUT` **Path:** `/api/transaction/{transactionId}/file/{fileId}` Upload either a PDF file or file metadata to a transaction. The Content-Type header determines which endpoint is used: - `application/pdf`: Upload PDF file content - `application/json`: Upload file metadata Add a file to the transaction or overwrite an existing file with the same \`fileId\`\`. The file parameter can either be the PDF document or JSON metadata. If your file requires metadata, the JSON metadata MUST be supplied first. #### File digest header When uploading a file, it is possible to send a digest header along with the HTTP request. This header should contain a base64-encoded SHA checksum of the uploaded file. For more information on digest headers, refer to [RFC 3230](https://www.ietf.org/rfc/rfc3230.txt) for format instructions and [RFC 5843](https://www.ietf.org/rfc/rfc5843.txt) for the supported algorithms. #### Supported Algorithms Currently, only SHA-256 and SHA-512 are accepted as hash algorithms. While RFC 3230 originally included MD5 and SHA-1, these weaker algorithms are no longer supported for security reasons. Using SHA-256 or SHA-512 ensures compliance with modern security standards. For example: `Digest: SHA-256=HtHRpLOZBEMnTpQS6Zn12veC4uhjtMwamfVAwmPQPmE=` ## Parameters | Name | In | Type | Required | Description | | | ----------------- | ------ | ------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | | **transactionId** | path | string | Yes |

The transaction to add the files to

| | | **fileId** | path | string | Yes |

A unique identifier for the file within the transaction. Must not exceed 255 characters and cannot contain control characters or the following special characters: :, \*, ?, \, /, ", \<, >, | , or null characters.

| | **Content-Type** | header | string | Yes |

Content type - either application/pdf for file upload or application/json for metadata

| | | **Digest** | header | string | No |

SHA-256 or SHA-512 checksum of the PDF file for integrity verification (only for PDF uploads)

| | ## Request Body (required) Either PDF file content or file metadata JSON #### application/json Content-Type: `application/json` **Schema:** [FileMetadata](/openapi/models/FileMetadata.md) #### Properties | Name | Type | Description | | | ---------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **DisplayName** | String | Null |

Human-readable display name for the file that will be shown to users in the signing interface. If not provided, the fileId will be used as the display name.

| | **DisplayOrder** | Integer | Null |

Numeric value determining the order in which files are displayed and processed when multiple files are present in a transaction. Lower numbers appear first. If not specified, a timestamp-based order is used.

| | **Description** | String | Null |

Detailed description of the file's purpose and contents. This helps users understand what they are signing and may be displayed in the signing interface or audit logs.

| | **SetParaph** | Boolean | Null |

Indicates whether a paraph (initial) should be set on each page of the document in addition to the main signature. When true, signers will be required to initial every page. Defaults to false if not specified.

| | **Signers** | Object |

Dictionary mapping signer identifiers to their specific configuration for this file. Each signer can have different form sets and signing requirements for the same document. The key is the signer identifier.

| | | **FormSets** | Object |

Nested dictionary structure defining form fields and signing areas within the document. The outer key represents the form set name, and the inner key represents individual field identifiers within that set. Form sets allow grouping related fields together for organization and signer assignment.

The following characters are allowed as a key / field name: a-z A-Z 0-9 \_.

Map of pdf field definitions.

| | #### application/pdf Content-Type: `application/pdf` **Type:** string ## Responses ### 200 File was updated successfully (PDF upload) Content-Type: `application/json` #### Properties | Name | Type | Description | | ----------- | ------ | ----------- | | **Message** | String | | ### 201 New file was created successfully (PDF upload) Content-Type: `application/json` #### Properties | Name | Type | Description | | ----------- | ------ | ----------- | | **Message** | String | | ### 202 Metadata accepted, waiting for actual file (metadata upload) Content-Type: `application/json` #### Properties | Name | Type | Description | | ----------- | ------ | ----------- | | **Message** | String | | ### 400 Bad request - validation error, digest mismatch, or invalid JSON Content-Type: `application/json` **Schema:** [ErrorResponse](/openapi/models/ErrorResponse.md) #### Properties | Name | Type | Description | | ------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **type** | String |

A URI reference identifying the problem type. This property is always present in RFC 7807 responses.

| | **title** | String |

A short, human-readable summary of the problem

| | **detail** | String |

A human-readable explanation specific to this occurrence of the problem

| | **status** | Integer |

The HTTP status code

| | **instance** | String |

A URI reference that identifies the specific occurrence of the problem

| | **Message** | String |

Deprecated (Legacy Format Only): Contains the error message in the old response format. This property appears alone in legacy error responses and is mutually exclusive with the RFC 7807 properties above. Will be phased out as we complete our transition to RFC 7807.

| **Examples:** _digestMismatch:_ ```json { "Message": "SHA-256 digest mismatch" } ``` _unsupportedAlgorithm:_ ```json { "Message": "Hash algorithm [MD5] is not supported." } ``` _invalidFileId:_ ```json { "Message": "FileId is invalid" } ``` _signerNotFound:_ ```json { "Message": "Signer 'signer1' not found." } ``` _missingContentType:_ ```json { "Message": "Unexpected or missing Content-Type header, did you specify 'application/pdf'?" } ``` _invalidJson:_ ```json { "Message": "Invalid json structure provided." } ``` ### 401 Unauthorized - user not creator of transaction Content-Type: `application/json` **Schema:** [ErrorResponse](/openapi/models/ErrorResponse.md) #### Properties | Name | Type | Description | | ------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **type** | String |

A URI reference identifying the problem type. This property is always present in RFC 7807 responses.

| | **title** | String |

A short, human-readable summary of the problem

| | **detail** | String |

A human-readable explanation specific to this occurrence of the problem

| | **status** | Integer |

The HTTP status code

| | **instance** | String |

A URI reference that identifies the specific occurrence of the problem

| | **Message** | String |

Deprecated (Legacy Format Only): Contains the error message in the old response format. This property appears alone in legacy error responses and is mutually exclusive with the RFC 7807 properties above. Will be phased out as we complete our transition to RFC 7807.

| **Example:** ```json { "Message": "Unauthorized access to this resource." } ``` ### 403 Forbidden - authorization policy violation Content-Type: `application/json` **Schema:** [ErrorResponse](/openapi/models/ErrorResponse.md) #### Properties | Name | Type | Description | | ------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **type** | String |

A URI reference identifying the problem type. This property is always present in RFC 7807 responses.

| | **title** | String |

A short, human-readable summary of the problem

| | **detail** | String |

A human-readable explanation specific to this occurrence of the problem

| | **status** | Integer |

The HTTP status code

| | **instance** | String |

A URI reference that identifies the specific occurrence of the problem

| | **Message** | String |

Deprecated (Legacy Format Only): Contains the error message in the old response format. This property appears alone in legacy error responses and is mutually exclusive with the RFC 7807 properties above. Will be phased out as we complete our transition to RFC 7807.

| **Example:** ```json { "Message": "Access denied by authorization policy." } ``` ### 413 Payload too large - file exceeds 50MB limit Content-Type: `application/json` **Schema:** [ErrorResponse](/openapi/models/ErrorResponse.md) #### Properties | Name | Type | Description | | ------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **type** | String |

A URI reference identifying the problem type. This property is always present in RFC 7807 responses.

| | **title** | String |

A short, human-readable summary of the problem

| | **detail** | String |

A human-readable explanation specific to this occurrence of the problem

| | **status** | Integer |

The HTTP status code

| | **instance** | String |

A URI reference that identifies the specific occurrence of the problem

| | **Message** | String |

Deprecated (Legacy Format Only): Contains the error message in the old response format. This property appears alone in legacy error responses and is mutually exclusive with the RFC 7807 properties above. Will be phased out as we complete our transition to RFC 7807.

| **Example:** ```json { "Message": "Request entity too large." } ``` ### 500 Internal server error Content-Type: `application/json` **Schema:** [ErrorResponse](/openapi/models/ErrorResponse.md) #### Properties | Name | Type | Description | | ------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **type** | String |

A URI reference identifying the problem type. This property is always present in RFC 7807 responses.

| | **title** | String |

A short, human-readable summary of the problem

| | **detail** | String |

A human-readable explanation specific to this occurrence of the problem

| | **status** | Integer |

The HTTP status code

| | **instance** | String |

A URI reference that identifies the specific occurrence of the problem

| | **Message** | String |

Deprecated (Legacy Format Only): Contains the error message in the old response format. This property appears alone in legacy error responses and is mutually exclusive with the RFC 7807 properties above. Will be phased out as we complete our transition to RFC 7807.

| **Example:** ```json { "Message": "An internal server error occurred." } ``` --- url: /openapi/index.md --- # Entrust Signhost REST API A comprehensive REST API for creating, managing, and processing digital document signing transactions. This API enables developers to integrate secure electronic signature workflows into their applications, supporting multiple authentication methods, verification types, and document management features. **Key Features** - Create and configure signing transactions with multiple signers and receivers - Support for various authentication methods (DigiD, SMS, itsme, etc.) - Multiple verification options including digital certificates and biometric verification - File upload and download with metadata support - Real-time transaction status tracking and webhook notifications - Multi-language support for international use cases - Comprehensive audit trails and receipt generation **Important Notes** - All input strings should be UTF-8 encoded - Optional JSON properties should be omitted when not used - Do not provide null values - Do not perform active polling for transactions status updates. Use the postback service instead. For detailed integration guides and examples, visit the [Signhost Developer Documentation](https://evidos.github.io/). ### Contact | Name | Email | URL | | ---------------- | --------------------------------------------------- | ------------------------------------------------------------------------ | | Signhost Support | [support@signhost.com](mailto:support@signhost.com) | [https://intercom.help/signhost/en/](https://intercom.help/signhost/en/) | ### Servers | URL | | -------------------------- | | `https://api.signhost.com` | ### Authentication | HTTP Header Name | Description | | ---------------- | ----------------------------------------------------------------- | | `Authorization` | The User Token. Header value must be prefixed with `APIKey`. | | `Application` | The Application Key. Header value must be prefixed with `APPKey`. | ### API Operations | Operation | Method | Path | Description | | --------------------------------------------------------------- | -------- | ------------------------------------------------ | ---------------------------------- | | [createTransaction](/openapi/transactions/createTransaction.md) | `POST` | `/api/transaction` | Create Transaction | | [startTransaction](/openapi/transactions/startTransaction.md) | `PUT` | `/api/transaction/{transactionId}/start` | Start a transaction | | [getTransaction](/openapi/transactions/getTransaction.md) | `GET` | `/api/transaction/{transactionId}` | Get a transaction by ID | | [deleteTransaction](/openapi/transactions/deleteTransaction.md) | `DELETE` | `/api/transaction/{transactionId}` | Delete / Cancel a Transaction | | [getReceipt](/openapi/files/getReceipt.md) | `GET` | `/api/file/receipt/{transactionId}` | Download receipt for a transaction | | [downloadFile](/openapi/files/downloadFile.md) | `GET` | `/api/transaction/{transactionId}/file/{fileId}` | Download a file from a transaction | | [uploadFile](/openapi/files/uploadFile.md) | `PUT` | `/api/transaction/{transactionId}/file/{fileId}` | Upload PDF file or file metadata | ### API Models | Model | Description | | ------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [CreateTransactionRequest](/openapi/models/CreateTransactionRequest.md) | Transaction creation data including signers, receivers, files, and configuration. This is the request body for creating a new transaction. | | [Transaction](/openapi/models/Transaction.md) | Complete transaction data returned when retrieving or creating a transaction. Includes all configuration, current status, and file information. | | [CreateSignerRequest](/openapi/models/CreateSignerRequest.md) | Request object for creating a new signer in a transaction. Defines the signer's identity, authentication/verification requirements, notification preferences, and signing behavior. **Key Requirements:** - Email address is mandatory - Either Authentications or Verifications must be provided (or both) - Signers with AllowDelegation enabled cannot have Authentications - SendSignRequest determines if sign request emails are sent automatically | | [Signer](/openapi/models/Signer.md) | Signer information with current status (based on SignerGetDto which extends SignerBaseDto) | | [CreateReceiverRequest](/openapi/models/CreateReceiverRequest.md) | Receiver configuration for getting copies of signed documents | | [Receiver](/openapi/models/Receiver.md) | Receiver information (based on ReceiverGetDto which extends ReceiverBaseDto) | | [SignerAuthentication](/openapi/models/SignerAuthentication.md) | Authentication methods used to verify signer identity before document access. These methods must be completed before the signer can view documents. **Important:** The "Type" property is case-sensitive and must be capitalized. | | [SignerVerification](/openapi/models/SignerVerification.md) | Verification methods used to confirm signer identity before document signing. These methods must be completed before the signer can sign documents. **Important:** The "Type" property is case-sensitive and must be capitalized. **Critical Requirement:** You **must** use one of the following verification methods as the **last** verification in your verifications list: - Consent - PhoneNumber - Scribble - CSC Qualified\* **Rules for CSC Qualified:** - This method **must always be the absolute final verification** - nothing can come after it **Rules for Consent, PhoneNumber, and Scribble:** - These three can be used as the final verification method - They are commonly used when you want additional verification steps before the final consent/signature **Common Mistakes:** - Using only authentication methods (e.g., iDIN) without a final verification method - Placing verifications after CSC Qualified **Valid Examples:** - `[Consent]` ✓ - `[iDIN, Consent]` ✓ - `[iDIN, PhoneNumber]` ✓ - `[iDIN, CSC Qualified]` ✓ - `[CSC Qualified, Consent]` ✗ (Nothing can come after CSC Qualified) - `[iDIN]` ✗ (Missing required final verification) | | [SignerScribbleVerification](/openapi/models/SignerScribbleVerification.md) | Handwritten signature verification | | [SignerPhoneNumberIdentification](/openapi/models/SignerPhoneNumberIdentification.md) | SMS phone number verification | | [SignerDigidIdentification](/openapi/models/SignerDigidIdentification.md) | Dutch DigiD verification | | [SignerIDealVerification](/openapi/models/SignerIDealVerification.md) | iDEAL bank verification | | [SignerSurfnetVerification](/openapi/models/SignerSurfnetVerification.md) | SURFnet academic verification | | [SignerIDINVerification](/openapi/models/SignerIDINVerification.md) | iDIN bank identification verification | | [SignerIPAddressVerification](/openapi/models/SignerIPAddressVerification.md) | IP address verification | | [SignerEHerkenningVerification](/openapi/models/SignerEHerkenningVerification.md) | eHerkenning business identity verification | | [SignerItsmeIdentificationVerification](/openapi/models/SignerItsmeIdentificationVerification.md) | itsme identification verification | | [SignerConsentVerification](/openapi/models/SignerConsentVerification.md) | Consent-based verification | | [SignerCscVerification](/openapi/models/SignerCscVerification.md) | Cloud Signature Consortium (CSC) verification | | [SignerOidcIdentification](/openapi/models/SignerOidcIdentification.md) | OpenID Connect identification | | [SignerOnfidoIdentification](/openapi/models/SignerOnfidoIdentification.md) | Onfido identity verification | | [FileEntry](/openapi/models/FileEntry.md) | File information and metadata associated with a transaction. Contains details about uploaded documents including display properties, download links, and current processing status. Files are referenced by their unique identifier within the transaction and can include PDFs, receipts, and other document types. | | [Link](/openapi/models/Link.md) | URI link with relationship and type information | | [Activity](/openapi/models/Activity.md) | Activity/event information for a signer (based on ActivityGetDto) | | [FileMetadata](/openapi/models/FileMetadata.md) | Comprehensive metadata information for a file in a transaction. This metadata defines how the file should be displayed, processed, and what form fields and signing areas it contains. The metadata can be uploaded before or after the actual PDF file. | | [FileSignerData](/openapi/models/FileSignerData.md) | Configuration data specific to a signer for a particular file. This determines which form sets the signer needs to complete when signing the document. | | [FileField](/openapi/models/FileField.md) | Represents an individual form field or signing area within a document. Fields define where and how users interact with the document during the signing process. | | [FileFieldType](/openapi/models/FileFieldType.md) | Specifies the type of interaction or data entry required for this field. Different types have different behaviors and validation rules in the signing interface. | | [FileFieldLocation](/openapi/models/FileFieldLocation.md) | Defines the precise positioning and sizing of a field within the PDF document. Fields can be positioned using absolute coordinates or by searching for specific text anchors within the document. | | [TransactionDeleteOptions](/openapi/models/TransactionDeleteOptions.md) | Options for cancelling a transaction | | [ErrorResponse](/openapi/models/ErrorResponse.md) | Error response object based on RFC 7807 Problem Details for HTTP APIs. See [https://datatracker.ietf.org/doc/html/rfc7807](https://datatracker.ietf.org/doc/html/rfc7807) for details. **Response Format:** A response is either in the new RFC 7807 Problem Details format OR the legacy Message format, but never both. - **New Format (RFC 7807):** Contains the `type` property and standard problem detail properties (`title`, `detail`, `status`). May also include additional extension members. - **Legacy Format:** Contains only the `Message` property. This format is used for some existing error responses during our transition to full RFC 7807 compliance. **Usage:** Check if the `type` property is present. If it exists, the response is in RFC 7807 format. If `type` is absent, fall back to reading the `Message` property. | --- url: /openapi/models/Activity.md --- # Activity Activity/event information for a signer (based on ActivityGetDto) #### Properties | Name | Type | Description | | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Id** | String |

Unique activity identifier

| | **Code** | enum: |

Activity code indicating the type of activity. Possible values include:

| | **Activity** | String |

Activity description

| | **Info** | String |

Additional activity information

| | **CreatedDateTime** | String |

When the activity occurred

| --- url: /openapi/models/CreateReceiverRequest.md --- # CreateReceiverRequest Receiver configuration for getting copies of signed documents #### Properties | Name | Type | Description | | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Name** | String |

Receiver's name

| | **Email** | String | **Required**.

Receiver's email address

| | **Language** | enum: |

Language for receiver communications

| | **Subject** | String |

Custom subject for notification email. Maximum of 64 characters allowed. Omitting this parameter will enable the default subject.

| | **Message** | String | **Required**.

Custom message for notification email. Newlines can be created by including a \n in the message. HTML is not allowed.

| | **Reference** | String |

Custom reference for this receiver

| | **Context** | Object |

Custom receiver data (JSON object only)

| --- url: /openapi/models/CreateSignerRequest.md --- # CreateSignerRequest Request object for creating a new signer in a transaction. Defines the signer's identity, authentication/verification requirements, notification preferences, and signing behavior. **Key Requirements:** - Email address is mandatory - Either Authentications or Verifications must be provided (or both) - Signers with AllowDelegation enabled cannot have Authentications - SendSignRequest determines if sign request emails are sent automatically #### Properties | Name | Type | Description | | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Id** | String |

Signer identifier (can be provided or generated)

| | **Expires** | String |

When the signer's access expires

| | **Email** | String | **Required**.

Signer's email address

| | **Authentications** | Array\<[SignerAuthentication](/openapi/models/SignerAuthentication.md)> |

List of authentications that the signer has to authenticate with. The order in which the authentications are provided determine in which order the signer will have to perform the specified method.

Authentications must be performed before the document(s) can be viewed.

When the authentication SecureDownload is configured, the download url for a signer is authenticated with the same method as the signing url.

The download url is returned in the response, and (optionally) emailed to the signer.

You must explicitly specify the API-version when using this feature. This is done with the header: 'Accept: application/vnd.signhost.v1+json'.

| | **Verifications** | Array\<[SignerVerification](/openapi/models/SignerVerification.md)> |

List of verifications that the signer has to verify with.

The order in which the verifications are provided determine in which order the signer will have to perform the specified method.

Verifications must be performed before the document(s) can be signed.

Critical Requirement: You must use one of the following verification methods as the last verification in the list:

Important Notes:

Common mistake: Providing only authentication methods (e.g., iDIN) without including one of these required final verification methods.

| | **SendSignRequest** | Boolean |

Whether to send sign request to this signer's email address

| | **SendSignConfirmation** | Boolean |

Whether to send a confirmation email to the signer after signing. Default value is the value of SendSignRequest

| | **SignRequestSubject** | String |

The subject of the sign request email in plain text. Maximum of 64 characters allowed.

If omitted, the default subject will be used.

| | **SignRequestMessage** | String |

The message of the sign request email in plain text. HTML is not allowed. Newlines can be created by including a \n. Required if SendSignRequest is true.

| | **DaysToRemind** | Integer |

Number of days between automatic reminder emails sent to this signer.

Note: Reminders are only sent if SendSignRequest is true and the signer hasn't completed signing yet.

| | **Language** | enum: |

Language for signer interface and emails

| | **Reference** | String |

Custom reference for this signer

| | **IntroText** | String |

Custom introduction text shown to the signer during the signing proces. This will be shown on the first screen to the signer and supports limited markdown markup. The following markup is supported:

| | **ReturnUrl** | String |

URL to redirect signer after signing

| | **AllowDelegation** | Boolean |

Whether this signer can delegate signing to another person. Cannot be used together with Authentications.

| | **Context** | Object |

Custom signer data (dynamic JSON object)

| --- url: /openapi/models/CreateTransactionRequest.md --- # CreateTransactionRequest Transaction creation data including signers, receivers, files, and configuration. This is the request body for creating a new transaction. #### Properties | Name | Type | Description | | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Seal** | Boolean | **Required**.

Whether to seal the transaction (no signers required). When true, the transaction is automatically completed without requiring signatures.

| | **Reference** | String |

Custom reference identifier for the transaction

| | **PostbackUrl** | String |

URL to receive status notifications about the transaction

| | **DaysToExpire** | Integer |

Number of days until the transaction expires. If 0, uses organization default. Maximum value depends on organization settings.

| | **SendEmailNotifications** | Boolean |

Whether to send email notifications to signers and receivers

| | **SignRequestMode** | enum: |

Mode for sign request delivery:

| | **Language** | enum: |

Language code for transaction interface and emails

| | **Context** | Object |

Custom JSON object for additional transaction data. Only JSON objects are allowed (no arrays or primitives).

| | **Signers** | Array\<[CreateSignerRequest](/openapi/models/CreateSignerRequest.md)> |

List of signers for the transaction

| | **Receivers** | Array\<[CreateReceiverRequest](/openapi/models/CreateReceiverRequest.md)> |

List of receivers who get copies of completed documents

| **Example:** ```json { "Seal": false, "Reference": "CONTRACT-2024-001", "PostbackUrl": "https://example.com/webhook/signhost", "DaysToExpire": 30, "SendEmailNotifications": true, "SignRequestMode": 2, "Language": "en-US", "Context": { "department": "HR", "contract_type": "employment" }, "Signers": [ { "Email": "john.doe@example.com", "SendSignRequest": true, "SignRequestMessage": "Please review and sign the attached employment contract.", "Language": "en-US" } ], "Receivers": [ { "Email": "hr@example.com", "Message": "The employment contract has been signed and is attached.", "Language": "en-US" } ] } ``` --- url: /openapi/models/ErrorResponse.md --- # ErrorResponse Error response object based on RFC 7807 Problem Details for HTTP APIs. See [https://datatracker.ietf.org/doc/html/rfc7807](https://datatracker.ietf.org/doc/html/rfc7807) for details. **Response Format:** A response is either in the new RFC 7807 Problem Details format OR the legacy Message format, but never both. - **New Format (RFC 7807):** Contains the `type` property and standard problem detail properties (`title`, `detail`, `status`). May also include additional extension members. - **Legacy Format:** Contains only the `Message` property. This format is used for some existing error responses during our transition to full RFC 7807 compliance. **Usage:** Check if the `type` property is present. If it exists, the response is in RFC 7807 format. If `type` is absent, fall back to reading the `Message` property. #### Properties | Name | Type | Description | | ------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **type** | String |

A URI reference identifying the problem type. This property is always present in RFC 7807 responses.

| | **title** | String |

A short, human-readable summary of the problem

| | **detail** | String |

A human-readable explanation specific to this occurrence of the problem

| | **status** | Integer |

The HTTP status code

| | **instance** | String |

A URI reference that identifies the specific occurrence of the problem

| | **Message** | String |

Deprecated (Legacy Format Only): Contains the error message in the old response format. This property appears alone in legacy error responses and is mutually exclusive with the RFC 7807 properties above. Will be phased out as we complete our transition to RFC 7807.

| **Examples:** _Modern RFC 7807 Problem Details format_ ```json { "type": "https://api.signhost.com/errors/validation-failed", "title": "Validation Failed", "detail": "One or more validation errors occurred", "status": 400, "errors": { "$": [ "Some validation error" ] } } ``` _Legacy error response format_ ```json { "Message": "An error occurred while processing the request." } ``` --- url: /openapi/models/FileEntry.md --- # FileEntry File information and metadata associated with a transaction. Contains details about uploaded documents including display properties, download links, and current processing status. Files are referenced by their unique identifier within the transaction and can include PDFs, receipts, and other document types. #### Properties | Name | Type | Description | | --------------- | --------------------------------------- | -------------------------------------------------- | | **Links** | Array\<[Link](/openapi/models/Link.md)> |

Related links for the file (download, etc.)

| | **DisplayName** | String |

Display name for the file

| --- url: /openapi/models/FileField.md --- # FileField Represents an individual form field or signing area within a document. Fields define where and how users interact with the document during the signing process. #### Properties | Name | Type | Description | | | | | ------------ | --------------------------------------------------------- | ------------- | ------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Type** | [FileFieldType](/openapi/models/FileFieldType.md) | **Required**. | | | | | **Location** | [FileFieldLocation](/openapi/models/FileFieldLocation.md) | **Required**. | | | | | **Value** | string | number | boolean | object |

The value content for the field. The type and format depend on the field type:

| --- url: /openapi/models/FileFieldLocation.md --- # FileFieldLocation Defines the precise positioning and sizing of a field within the PDF document. Fields can be positioned using absolute coordinates or by searching for specific text anchors within the document. #### Properties | Name | Type | Description | | | -------------- | ------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **PageNumber** | Integer | Null |

The page number where this field should be placed (1-based indexing). If not specified, the field may be positioned based on search criteria across all pages.

| | **Search** | String | Null |

Text string to search for in the document to anchor the field position. When specified, the field will be positioned relative to this text rather than using absolute coordinates.

| | **Occurence** | Integer | Null |

When using search text positioning, specifies which occurrence of the search text to use if the text appears multiple times on the page or document (1-based indexing).

| | **Top** | Integer | Null |

Vertical position from the top of the page in pixels or points. Used for absolute positioning or as an offset when combined with search positioning.

| | **Right** | Integer | Null |

Horizontal position from the right edge of the page in pixels or points. Typically used for right-aligned field positioning.

| | **Bottom** | Integer | Null |

Vertical position from the bottom of the page in pixels or points. Can be used instead of or in combination with the top property for precise vertical positioning.

| | **Left** | Integer | Null |

Horizontal position from the left edge of the page in pixels or points. The most common way to specify horizontal field positioning.

| | **Width** | Integer | Null |

Width of the field in pixels or points. Determines how much horizontal space the field occupies and affects text wrapping for text fields.

For signature and seal fields we suggest a width of 140.

| | **Height** | Integer | Null |

Height of the field in pixels or points. Determines how much vertical space the field occupies and affects text size and line spacing for text fields.

For signature and seal fields we suggest a height of 70.

| --- url: /openapi/models/FileFieldType.md --- # FileFieldType Specifies the type of interaction or data entry required for this field. Different types have different behaviors and validation rules in the signing interface. **Possible values:** `Seal`, `Signature`, `Check`, `Radio`, `SingleLine`, `Number`, `Date` **Example:** ```json "Signature" ``` --- url: /openapi/models/FileMetadata.md --- # FileMetadata Comprehensive metadata information for a file in a transaction. This metadata defines how the file should be displayed, processed, and what form fields and signing areas it contains. The metadata can be uploaded before or after the actual PDF file. #### Properties | Name | Type | Description | | | ---------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **DisplayName** | String | Null |

Human-readable display name for the file that will be shown to users in the signing interface. If not provided, the fileId will be used as the display name.

| | **DisplayOrder** | Integer | Null |

Numeric value determining the order in which files are displayed and processed when multiple files are present in a transaction. Lower numbers appear first. If not specified, a timestamp-based order is used.

| | **Description** | String | Null |

Detailed description of the file's purpose and contents. This helps users understand what they are signing and may be displayed in the signing interface or audit logs.

| | **SetParaph** | Boolean | Null |

Indicates whether a paraph (initial) should be set on each page of the document in addition to the main signature. When true, signers will be required to initial every page. Defaults to false if not specified.

| | **Signers** | Object |

Dictionary mapping signer identifiers to their specific configuration for this file. Each signer can have different form sets and signing requirements for the same document. The key is the signer identifier.

| | | **FormSets** | Object |

Nested dictionary structure defining form fields and signing areas within the document. The outer key represents the form set name, and the inner key represents individual field identifiers within that set. Form sets allow grouping related fields together for organization and signer assignment.

The following characters are allowed as a key / field name: a-z A-Z 0-9 \_.

Map of pdf field definitions.

| | --- url: /openapi/models/FileSignerData.md --- # FileSignerData Configuration data specific to a signer for a particular file. This determines which form sets the signer needs to complete when signing the document. #### Properties | Name | Type | Description | | ------------ | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **FormSets** | Array\ |

Array of form set identifiers that this signer is responsible for completing. Each form set contains related fields that the signer must fill out or sign. Multiple signers can be assigned to the same form set, or each signer can have unique form sets.

| --- url: /openapi/models/Link.md --- # Link URI link with relationship and type information #### Properties | Name | Type | Description | | -------- | ------ | --------------------------------------------------------------------------------------------------------------------------------- | | **Rel** | String |

Relationship type of the link

| | **Type** | String |

MIME type or content type of the linked resource. Include this in the Accept header when requesting the file.

| | **Link** | String |

The actual URI/URL of the linked resource

| --- url: /openapi/models/Receiver.md --- # Receiver Receiver information (based on ReceiverGetDto which extends ReceiverBaseDto) #### Properties | Name | Type | Description | | -------------------- | ----------------------------------------------- | ------------------------------------------------- | | **Name** | String |

Receiver's name

| | **Email** | String |

Receiver's email address

| | **Language** | String |

Language for receiver communications

| | **Subject** | String |

Custom subject for notification email

| | **Message** | String |

Custom message for notification email

| | **Reference** | String |

Custom reference for this receiver

| | **Context** | Object |

Custom receiver data (dynamic JSON object)

| | **Id** | String |

Unique receiver identifier

| | **Activities** | Array\<[Activity](/openapi/models/Activity.md)> |

Activities/events for this receiver

| | **CreatedDateTime** | String |

When the receiver was added

| | **ModifiedDateTime** | String |

When the receiver was last modified

| --- url: /openapi/models/Signer.md --- # Signer Signer information with current status (based on SignerGetDto which extends SignerBaseDto) #### Properties | Name | Type | Description | | ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Id** | String |

Unique signer identifier

| | **Expires** | String |

When the signer's access expires

| | **Email** | String |

Signer's email address

| | **Authentications** | Array\<[SignerAuthentication](/openapi/models/SignerAuthentication.md)> |

Authentication methods for this signer

| | **Verifications** | Array\<[SignerVerification](/openapi/models/SignerVerification.md)> |

Verification methods for this signer

| | **SendSignRequest** | Boolean |

Whether to send sign request to this signer

| | **SendSignConfirmation** | Boolean |

Whether to send a confirmation email after signing

| | **SignRequestSubject** | String |

Custom subject for sign request email

| | **SignRequestMessage** | String |

Custom message for sign request email

| | **DaysToRemind** | Integer |

Number of days between automatic reminder emails sent to this signer.

Note: Reminders are only sent if SendSignRequest is true and the signer hasn't completed signing yet.

| | **Language** | enum: |

Language for signer interface and emails

| | **Reference** | String |

Custom reference for this signer

| | **IntroText** | String |

Custom introduction text shown to the signer

| | **ReturnUrl** | String |

URL to redirect signer after signing

| | **AllowDelegation** | Boolean |

Whether this signer can delegate signing to another person

| | **Context** | Object |

Custom signer data (dynamic JSON object)

| | **Activities** | Array\<[Activity](/openapi/models/Activity.md)> |

Activities/events for this signer

| | **RejectReason** | String |

Reason provided if the signer rejected the transaction

| | **DelegateReason** | String |

Reason provided for delegation

| | **DelegateSignerEmail** | String |

Email of the delegate signer

| | **DelegateSignerName** | String |

Name of the delegate signer

| | **DelegateSignUrl** | String |

URL for delegate to sign

| | **SignUrl** | String |

A unique URL for this signer to access the signing interface. This URL is generated after the transaction is created and can be used to direct the signer to sign the document(s).

| | **SignedDateTime** | String |

When the signer completed signing

| | **RejectDateTime** | String |

When the signer rejected the transaction

| | **CreatedDateTime** | String |

When the signer was added

| | **SignerDelegationDateTime** | String |

When the signer delegated to another person

| | **ModifiedDateTime** | String |

When the signer was last modified

| | **ShowUrl** | String |

A unique URL per signer that provides the signed document(s) download flow for the signer. Documents can only be retrieved from this URL once the transaction has reached status 30 (completed).

| | **ReceiptUrl** | String |

A unique URL per signer that provides the receipt download flow for the signer. The receipt can only be retrieved from this URL once the transaction has reached status 30 (completed).

| --- url: /openapi/models/SignerAuthentication.md --- # SignerAuthentication Authentication methods used to verify signer identity before document access. These methods must be completed before the signer can view documents. **Important:** The "Type" property is case-sensitive and must be capitalized. #### Discriminator This is a polymorphic schema that uses the `Type` property to determine the specific type. #### One Of Schemas The following schemas can be used: - [SignerPhoneNumberIdentification](/openapi/models/SignerPhoneNumberIdentification.md) - [SignerDigidIdentification](/openapi/models/SignerDigidIdentification.md) --- url: /openapi/models/SignerConsentVerification.md --- # SignerConsentVerification Consent-based verification #### Parent Type This model is a variant of the following polymorphic type: - [SignerVerification](/openapi/models/SignerVerification.md) #### Properties | Name | Type | Description | | -------- | ---------------------------------------------- | ----------- | | **Type** | enum: | | --- url: /openapi/models/SignerCscVerification.md --- # SignerCscVerification Cloud Signature Consortium (CSC) verification #### Parent Type This model is a variant of the following polymorphic type: - [SignerVerification](/openapi/models/SignerVerification.md) #### Properties | Name | Type | Description | | ---------------------- | ---------------------------------------------------- | -------------------------------------------- | | **Type** | enum: | | | **Provider** | String | **Read-Only**.

Provider identifier

| | **Issuer** | String | **Read-Only**.

Certificate issuer

| | **Subject** | String | **Read-Only**.

Certificate subject

| | **Thumbprint** | String | **Read-Only**.

Certificate thumbprint

| | **AdditionalUserData** | Object | **Read-Only**.

Additional user data

| --- url: /openapi/models/SignerDigidIdentification.md --- # SignerDigidIdentification Dutch DigiD verification #### Parent Types This model is a variant of the following polymorphic types: - [SignerAuthentication](/openapi/models/SignerAuthentication.md) - [SignerVerification](/openapi/models/SignerVerification.md) #### Properties | Name | Type | Description | | -------------------------- | -------------------------------------------- | -------------------------------------------------------------- | | **Type** | enum: | | | **Bsn** | String |

Dutch social security number

| | **Betrouwbaarheidsniveau** | String | **Read-Only**.

DigiD trust level (reliability level)

| | **SecureDownload** | Boolean |

Whether this verification enables secure file downloads

| --- url: /openapi/models/SignerEHerkenningVerification.md --- # SignerEHerkenningVerification eHerkenning business identity verification #### Parent Type This model is a variant of the following polymorphic type: - [SignerVerification](/openapi/models/SignerVerification.md) #### Properties | Name | Type | Description | | ------------------------ | -------------------------------------------------- | ------------------------------------------------- | | **Type** | enum: | | | **Uid** | String | **Read-Only**.

eHerkenning user identifier

| | **EntityConcernIdKvkNr** | String |

KvK (Chamber of Commerce) number

| --- url: /openapi/models/SignerIDINVerification.md --- # SignerIDINVerification iDIN bank identification verification #### Parent Type This model is a variant of the following polymorphic type: - [SignerVerification](/openapi/models/SignerVerification.md) #### Properties | Name | Type | Description | | ---------------------------- | ------------------------------------------- | ----------------------------------------------------------------------- | | **Type** | enum: | | | **AccountHolderName** | String | **Read-Only**.

Account holder's name

| | **AccountHolderAddress1** | String | **Read-Only**.

Account holder's primary address

| | **AccountHolderAddress2** | String | **Read-Only**.

Account holder's secondary address

| | **AccountHolderDateOfBirth** | String | **Read-Only**.

Account holder's date of birth

| | **Attributes** | Object | **Read-Only**.

Raw iDIN attributes (availability not guaranteed)

| --- url: /openapi/models/SignerIDealVerification.md --- # SignerIDealVerification iDEAL bank verification #### Parent Type This model is a variant of the following polymorphic type: - [SignerVerification](/openapi/models/SignerVerification.md) #### Properties | Name | Type | Description | | -------- | -------------------------------------------- | ---------------------------- | | **Type** | enum: | | | **Iban** | String |

IBAN for verification

| --- url: /openapi/models/SignerIPAddressVerification.md --- # SignerIPAddressVerification IP address verification #### Parent Type This model is a variant of the following polymorphic type: - [SignerVerification](/openapi/models/SignerVerification.md) #### Properties | Name | Type | Description | | ------------- | ------------------------------------------------ | ---------------------------------- | | **Type** | enum: | | | **IPAddress** | String |

IP address for verification

| --- url: /openapi/models/SignerItsmeIdentificationVerification.md --- # SignerItsmeIdentificationVerification itsme identification verification #### Parent Type This model is a variant of the following polymorphic type: - [SignerVerification](/openapi/models/SignerVerification.md) #### Properties | Name | Type | Description | | --------------- | ----------------------------------------------------------- | ------------------------------------------------------------------------ | | **Type** | enum: | | | **PhoneNumber** | String |

Phone number for itsme verification

| | **Attributes** | Object | **Read-Only**.

Raw itsme attributes (availability not guaranteed)

| --- url: /openapi/models/SignerOidcIdentification.md --- # SignerOidcIdentification OpenID Connect identification #### Parent Type This model is a variant of the following polymorphic type: - [SignerVerification](/openapi/models/SignerVerification.md) #### Properties | Name | Type | Description | | ---------------- | ------------------------------------------------------- | ------------------------- | | **Type** | enum: | | | **ProviderName** | String |

OIDC provider name

| --- url: /openapi/models/SignerOnfidoIdentification.md --- # SignerOnfidoIdentification Onfido identity verification #### Parent Type This model is a variant of the following polymorphic type: - [SignerVerification](/openapi/models/SignerVerification.md) #### Properties | Name | Type | Description | | ----------------- | --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Type** | enum: | **Required**. | | **WorkflowId** | String |

Onfido workflow identifier. Provide this when linking to a custom Entrust (formary Onfido) Studio environment. Omit this to use Signhost's standard Studio configuration.

| | **WorkflowRunId** | String | **Read-Only**.

Onfido workflow run identifier

| | **Version** | Integer | **Read-Only**.

Onfido API version

| | **Attributes** | Object | **Read-Only**.

Raw Onfido attributes (availability not guaranteed)

| --- url: /openapi/models/SignerPhoneNumberIdentification.md --- # SignerPhoneNumberIdentification SMS phone number verification #### Parent Types This model is a variant of the following polymorphic types: - [SignerAuthentication](/openapi/models/SignerAuthentication.md) - [SignerVerification](/openapi/models/SignerVerification.md) #### Properties | Name | Type | Description | | ------------------ | -------------------------------------------------- | -------------------------------------------------------------- | | **Type** | enum: | | | **Number** | String |

Phone number for SMS verification

| | **SecureDownload** | Boolean |

Whether this verification enables secure file downloads

| --- url: /openapi/models/SignerScribbleVerification.md --- # SignerScribbleVerification Handwritten signature verification #### Parent Type This model is a variant of the following polymorphic type: - [SignerVerification](/openapi/models/SignerVerification.md) #### Properties | Name | Type | Description | | ------------------------ | ----------------------------------------------- | ------------------------------------------------- | | **Type** | enum: | | | **RequireHandsignature** | Boolean |

Whether a hand-drawn signature is required

| | **ScribbleName** | String |

Name to appear in signature

| | **ScribbleNameFixed** | Boolean |

Whether the name cannot be changed

| --- url: /openapi/models/SignerSurfnetVerification.md --- # SignerSurfnetVerification SURFnet academic verification #### Parent Type This model is a variant of the following polymorphic type: - [SignerVerification](/openapi/models/SignerVerification.md) #### Properties | Name | Type | Description | | -------------- | ---------------------------------------------- | --------------------------------------------------- | | **Type** | enum: | | | **Uid** | String | **Read-Only**.

SURFnet user identifier

| | **Attributes** | Object | **Read-Only**.

Additional SURFnet attributes

| --- url: /openapi/models/SignerVerification.md --- # SignerVerification Verification methods used to confirm signer identity before document signing. These methods must be completed before the signer can sign documents. **Important:** The "Type" property is case-sensitive and must be capitalized. **Critical Requirement:** You **must** use one of the following verification methods as the **last** verification in your verifications list: - Consent - PhoneNumber - Scribble - CSC Qualified\* **Rules for CSC Qualified:** - This method **must always be the absolute final verification** - nothing can come after it **Rules for Consent, PhoneNumber, and Scribble:** - These three can be used as the final verification method - They are commonly used when you want additional verification steps before the final consent/signature **Common Mistakes:** - Using only authentication methods (e.g., iDIN) without a final verification method - Placing verifications after CSC Qualified **Valid Examples:** - `[Consent]` ✓ - `[iDIN, Consent]` ✓ - `[iDIN, PhoneNumber]` ✓ - `[iDIN, CSC Qualified]` ✓ - `[CSC Qualified, Consent]` ✗ (Nothing can come after CSC Qualified) - `[iDIN]` ✗ (Missing required final verification) #### Discriminator This is a polymorphic schema that uses the `Type` property to determine the specific type. #### One Of Schemas The following schemas can be used: - [SignerScribbleVerification](/openapi/models/SignerScribbleVerification.md) - [SignerPhoneNumberIdentification](/openapi/models/SignerPhoneNumberIdentification.md) - [SignerDigidIdentification](/openapi/models/SignerDigidIdentification.md) - [SignerIDealVerification](/openapi/models/SignerIDealVerification.md) - [SignerSurfnetVerification](/openapi/models/SignerSurfnetVerification.md) - [SignerIDINVerification](/openapi/models/SignerIDINVerification.md) - [SignerIPAddressVerification](/openapi/models/SignerIPAddressVerification.md) - [SignerEHerkenningVerification](/openapi/models/SignerEHerkenningVerification.md) - [SignerItsmeIdentificationVerification](/openapi/models/SignerItsmeIdentificationVerification.md) - [SignerConsentVerification](/openapi/models/SignerConsentVerification.md) - [SignerCscVerification](/openapi/models/SignerCscVerification.md) - [SignerOidcIdentification](/openapi/models/SignerOidcIdentification.md) - [SignerOnfidoIdentification](/openapi/models/SignerOnfidoIdentification.md) --- url: /openapi/models/Transaction.md --- # Transaction Complete transaction data returned when retrieving or creating a transaction. Includes all configuration, current status, and file information. #### Properties | Name | Type | Description | | -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **Id** | String |

Unique transaction identifier

| | **Seal** | Boolean |

Whether the transaction is sealed (no signers required)

| | **Reference** | String |

Custom reference identifier

| | **PostbackUrl** | String |

Webhook URL for status notifications

| | **DaysToExpire** | Integer |

Days until the transaction expires.

| | **SendEmailNotifications** | Boolean |

Send e-mail notifications to the sender.

| | **SignRequestMode** | enum: |

Sign request delivery mode

| | **Language** | enum: |

Language code for the Sender email notifications, and transaction receipt.

| | **Status** | enum: |

Current transaction status:

| | **Context** | Object |

Custom JSON object for additional transaction metadata that was provided during creation.

| | **CreatedDateTime** | String |

When the transaction was created

| | **ModifiedDateTime** | String |

When the transaction was last modified

| | **CanceledDateTime** | String |

When the transaction was cancelled, if applicable

| | **Files** | Object |

Files attached to the transaction as a key-value mapping. Key is the file ID, value is the file information.

| | **Signers** | Array\<[Signer](/openapi/models/Signer.md)> |

List of signers attached to this transaction, including their configuration, current status, verification methods, and signing progress. Each signer represents an individual who needs to sign the document or has already completed the signing process.

| | **Receivers** | Array\<[Receiver](/openapi/models/Receiver.md)> |

List of receivers who will receive copies of the completed signed documents. Receivers are notified via email once the transaction is successfully completed and all required signatures have been obtained.

| | **CancelationReason** | String |

The reason for the cancellation of the transaction, if applicable. This is provided when the transaction is cancelled via the DELETE endpoint.

| --- url: /openapi/models/TransactionDeleteOptions.md --- # TransactionDeleteOptions Options for cancelling a transaction #### Properties | Name | Type | Description | | --------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Reason** | String |

Reason for cancelling the transaction

| | **SendNotifications** | Boolean |

Whether to notify signers about the cancellation. Note that this only applies when SendSignRequest has been set to true during the transaction creation.

| --- url: /openapi/transactions/createTransaction.md --- # Create Transaction **Method:** `POST` **Path:** `/api/transaction` Create a new transaction for document signing. A transaction contains signers, receivers, and configuration for the signing process. After creating a transaction, you can upload files and start the signing process. **Important Notes:** - If `Seal` is false, at least one signer must be provided - Providing no signers with `Seal` true allows for automatic sealing. - Signers with `AllowDelegation` enabled cannot have authentications - If any signer has `SendSignRequest` enabled, `SignRequestMode` defaults to 2 - Authentication array requires explicit versioning header (Accept: application/json;version=1) - The `Type` property in authentication/verification objects is case-sensitive and must be capitalized ## Request Body (required) The request body for creating a transaction Content-Type: `application/json` **Schema:** [CreateTransactionRequest](/openapi/models/CreateTransactionRequest.md) #### Properties | Name | Type | Description | | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Seal** | Boolean | **Required**.

Whether to seal the transaction (no signers required). When true, the transaction is automatically completed without requiring signatures.

| | **Reference** | String |

Custom reference identifier for the transaction

| | **PostbackUrl** | String |

URL to receive status notifications about the transaction

| | **DaysToExpire** | Integer |

Number of days until the transaction expires. If 0, uses organization default. Maximum value depends on organization settings.

| | **SendEmailNotifications** | Boolean |

Whether to send email notifications to signers and receivers

| | **SignRequestMode** | enum: |

Mode for sign request delivery:

| | **Language** | enum: |

Language code for transaction interface and emails

| | **Context** | Object |

Custom JSON object for additional transaction data. Only JSON objects are allowed (no arrays or primitives).

| | **Signers** | Array\<[CreateSignerRequest](/openapi/models/CreateSignerRequest.md)> |

List of signers for the transaction

| | **Receivers** | Array\<[CreateReceiverRequest](/openapi/models/CreateReceiverRequest.md)> |

List of receivers who get copies of completed documents

| **Example:** ```json { "Seal": false, "Reference": "CONTRACT-2024-001", "PostbackUrl": "https://example.com/webhook/signhost", "DaysToExpire": 30, "SendEmailNotifications": true, "SignRequestMode": 2, "Language": "en-US", "Context": { "department": "HR", "contract_type": "employment" }, "Signers": [ { "Email": "john.doe@example.com", "SendSignRequest": true, "SignRequestMessage": "Please review and sign the attached employment contract.", "Language": "en-US", "Verifications": [ { "Type": "Scribble", "RequireHandsignature": true } ] } ], "Receivers": [ { "Email": "hr@example.com", "Message": "The employment contract has been signed and is attached.", "Language": "en-US" } ] } ``` ## Responses ### 200 Transaction created successfully Content-Type: `application/json` **Schema:** [Transaction](/openapi/models/Transaction.md) #### Properties | Name | Type | Description | | -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **Id** | String |

Unique transaction identifier

| | **Seal** | Boolean |

Whether the transaction is sealed (no signers required)

| | **Reference** | String |

Custom reference identifier

| | **PostbackUrl** | String |

Webhook URL for status notifications

| | **DaysToExpire** | Integer |

Days until the transaction expires.

| | **SendEmailNotifications** | Boolean |

Send e-mail notifications to the sender.

| | **SignRequestMode** | enum: |

Sign request delivery mode

| | **Language** | enum: |

Language code for the Sender email notifications, and transaction receipt.

| | **Status** | enum: |

Current transaction status:

| | **Context** | Object |

Custom JSON object for additional transaction metadata that was provided during creation.

| | **CreatedDateTime** | String |

When the transaction was created

| | **ModifiedDateTime** | String |

When the transaction was last modified

| | **CanceledDateTime** | String |

When the transaction was cancelled, if applicable

| | **Files** | Object |

Files attached to the transaction as a key-value mapping. Key is the file ID, value is the file information.

| | **Signers** | Array\<[Signer](/openapi/models/Signer.md)> |

List of signers attached to this transaction, including their configuration, current status, verification methods, and signing progress. Each signer represents an individual who needs to sign the document or has already completed the signing process.

| | **Receivers** | Array\<[Receiver](/openapi/models/Receiver.md)> |

List of receivers who will receive copies of the completed signed documents. Receivers are notified via email once the transaction is successfully completed and all required signatures have been obtained.

| | **CancelationReason** | String |

The reason for the cancellation of the transaction, if applicable. This is provided when the transaction is cancelled via the DELETE endpoint.

| ### 400 Bad request - validation error Content-Type: `application/json` **Schema:** [ErrorResponse](/openapi/models/ErrorResponse.md) #### Properties | Name | Type | Description | | ------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **type** | String |

A URI reference identifying the problem type. This property is always present in RFC 7807 responses.

| | **title** | String |

A short, human-readable summary of the problem

| | **detail** | String |

A human-readable explanation specific to this occurrence of the problem

| | **status** | Integer |

The HTTP status code

| | **instance** | String |

A URI reference that identifies the specific occurrence of the problem

| | **Message** | String |

Deprecated (Legacy Format Only): Contains the error message in the old response format. This property appears alone in legacy error responses and is mutually exclusive with the RFC 7807 properties above. Will be phased out as we complete our transition to RFC 7807.

| **Example:** ```json { "Message": "Invalid request data provided." } ``` ### 401 Unauthorized Content-Type: `application/json` **Schema:** [ErrorResponse](/openapi/models/ErrorResponse.md) #### Properties | Name | Type | Description | | ------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **type** | String |

A URI reference identifying the problem type. This property is always present in RFC 7807 responses.

| | **title** | String |

A short, human-readable summary of the problem

| | **detail** | String |

A human-readable explanation specific to this occurrence of the problem

| | **status** | Integer |

The HTTP status code

| | **instance** | String |

A URI reference that identifies the specific occurrence of the problem

| | **Message** | String |

Deprecated (Legacy Format Only): Contains the error message in the old response format. This property appears alone in legacy error responses and is mutually exclusive with the RFC 7807 properties above. Will be phased out as we complete our transition to RFC 7807.

| ### 500 Internal server error Content-Type: `application/json` **Schema:** [ErrorResponse](/openapi/models/ErrorResponse.md) #### Properties | Name | Type | Description | | ------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **type** | String |

A URI reference identifying the problem type. This property is always present in RFC 7807 responses.

| | **title** | String |

A short, human-readable summary of the problem

| | **detail** | String |

A human-readable explanation specific to this occurrence of the problem

| | **status** | Integer |

The HTTP status code

| | **instance** | String |

A URI reference that identifies the specific occurrence of the problem

| | **Message** | String |

Deprecated (Legacy Format Only): Contains the error message in the old response format. This property appears alone in legacy error responses and is mutually exclusive with the RFC 7807 properties above. Will be phased out as we complete our transition to RFC 7807.

| --- url: /openapi/transactions/deleteTransaction.md --- # Delete / Cancel a Transaction **Method:** `DELETE` **Path:** `/api/transaction/{transactionId}` ### Cancelling a Transaction When a transaction is not in an end-state (eg. Status = 5, 10, or 20) the transaction will be cancelled and deleted. When cancelling a transaction, you can choose to send an email notification to any awaiting signers informing them that the transaction has been cancelled. The status of the transaction will be set to cancelled. ### Deleting a Transaction When a transaction is in an end-state (eg. Status = 30, 40, 50, or 60) the transaction will be deleted. When deleting a transaction, you can choose to send an email notification to any awaiting signers informing them that the transaction has been deleted. The status of the transaction will remain the same but any uploaded documents and sensitive data will be deleted as soon as possible. ## Parameters | Name | In | Type | Required | Description | | ----------------- | ---- | ------ | -------- | --------------------------------------------- | | **transactionId** | path | string | Yes |

The transaction ID to cancel or delete

| ## Request Body Options for cancelling or deleting the transaction Content-Type: `application/json` **Schema:** [TransactionDeleteOptions](/openapi/models/TransactionDeleteOptions.md) #### Properties | Name | Type | Description | | --------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Reason** | String |

Reason for cancelling the transaction

| | **SendNotifications** | Boolean |

Whether to notify signers about the cancellation. Note that this only applies when SendSignRequest has been set to true during the transaction creation.

| ## Responses ### 200 Transaction cancelled successfully Content-Type: `application/json` **Schema:** [Transaction](/openapi/models/Transaction.md) #### Properties | Name | Type | Description | | -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **Id** | String |

Unique transaction identifier

| | **Seal** | Boolean |

Whether the transaction is sealed (no signers required)

| | **Reference** | String |

Custom reference identifier

| | **PostbackUrl** | String |

Webhook URL for status notifications

| | **DaysToExpire** | Integer |

Days until the transaction expires.

| | **SendEmailNotifications** | Boolean |

Send e-mail notifications to the sender.

| | **SignRequestMode** | enum: |

Sign request delivery mode

| | **Language** | enum: |

Language code for the Sender email notifications, and transaction receipt.

| | **Status** | enum: |

Current transaction status:

| | **Context** | Object |

Custom JSON object for additional transaction metadata that was provided during creation.

| | **CreatedDateTime** | String |

When the transaction was created

| | **ModifiedDateTime** | String |

When the transaction was last modified

| | **CanceledDateTime** | String |

When the transaction was cancelled, if applicable

| | **Files** | Object |

Files attached to the transaction as a key-value mapping. Key is the file ID, value is the file information.

| | **Signers** | Array\<[Signer](/openapi/models/Signer.md)> |

List of signers attached to this transaction, including their configuration, current status, verification methods, and signing progress. Each signer represents an individual who needs to sign the document or has already completed the signing process.

| | **Receivers** | Array\<[Receiver](/openapi/models/Receiver.md)> |

List of receivers who will receive copies of the completed signed documents. Receivers are notified via email once the transaction is successfully completed and all required signatures have been obtained.

| | **CancelationReason** | String |

The reason for the cancellation of the transaction, if applicable. This is provided when the transaction is cancelled via the DELETE endpoint.

| ### 401 Unauthorized - user not creator of transaction Content-Type: `application/json` **Schema:** [ErrorResponse](/openapi/models/ErrorResponse.md) #### Properties | Name | Type | Description | | ------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **type** | String |

A URI reference identifying the problem type. This property is always present in RFC 7807 responses.

| | **title** | String |

A short, human-readable summary of the problem

| | **detail** | String |

A human-readable explanation specific to this occurrence of the problem

| | **status** | Integer |

The HTTP status code

| | **instance** | String |

A URI reference that identifies the specific occurrence of the problem

| | **Message** | String |

Deprecated (Legacy Format Only): Contains the error message in the old response format. This property appears alone in legacy error responses and is mutually exclusive with the RFC 7807 properties above. Will be phased out as we complete our transition to RFC 7807.

| ### 403 Forbidden - authorization policy violation Content-Type: `application/json` **Schema:** [ErrorResponse](/openapi/models/ErrorResponse.md) #### Properties | Name | Type | Description | | ------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **type** | String |

A URI reference identifying the problem type. This property is always present in RFC 7807 responses.

| | **title** | String |

A short, human-readable summary of the problem

| | **detail** | String |

A human-readable explanation specific to this occurrence of the problem

| | **status** | Integer |

The HTTP status code

| | **instance** | String |

A URI reference that identifies the specific occurrence of the problem

| | **Message** | String |

Deprecated (Legacy Format Only): Contains the error message in the old response format. This property appears alone in legacy error responses and is mutually exclusive with the RFC 7807 properties above. Will be phased out as we complete our transition to RFC 7807.

| ### 500 Internal server error Content-Type: `application/json` **Schema:** [ErrorResponse](/openapi/models/ErrorResponse.md) #### Properties | Name | Type | Description | | ------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **type** | String |

A URI reference identifying the problem type. This property is always present in RFC 7807 responses.

| | **title** | String |

A short, human-readable summary of the problem

| | **detail** | String |

A human-readable explanation specific to this occurrence of the problem

| | **status** | Integer |

The HTTP status code

| | **instance** | String |

A URI reference that identifies the specific occurrence of the problem

| | **Message** | String |

Deprecated (Legacy Format Only): Contains the error message in the old response format. This property appears alone in legacy error responses and is mutually exclusive with the RFC 7807 properties above. Will be phased out as we complete our transition to RFC 7807.

| --- url: /openapi/transactions/getTransaction.md --- # Get a transaction by ID **Method:** `GET` **Path:** `/api/transaction/{transactionId}` Retrieve transaction details including signers, receivers, files, and current status. Do not use this method for active polling. Use the postback service instead. ## Parameters | Name | In | Type | Required | Description | | ----------------- | ---- | ------ | -------- | ------------------------------------- | | **transactionId** | path | string | Yes |

The transaction ID to retrieve

| ## Responses ### 200 Transaction retrieved successfully Content-Type: `application/json` **Schema:** [Transaction](/openapi/models/Transaction.md) #### Properties | Name | Type | Description | | -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **Id** | String |

Unique transaction identifier

| | **Seal** | Boolean |

Whether the transaction is sealed (no signers required)

| | **Reference** | String |

Custom reference identifier

| | **PostbackUrl** | String |

Webhook URL for status notifications

| | **DaysToExpire** | Integer |

Days until the transaction expires.

| | **SendEmailNotifications** | Boolean |

Send e-mail notifications to the sender.

| | **SignRequestMode** | enum: |

Sign request delivery mode

| | **Language** | enum: |

Language code for the Sender email notifications, and transaction receipt.

| | **Status** | enum: |

Current transaction status:

| | **Context** | Object |

Custom JSON object for additional transaction metadata that was provided during creation.

| | **CreatedDateTime** | String |

When the transaction was created

| | **ModifiedDateTime** | String |

When the transaction was last modified

| | **CanceledDateTime** | String |

When the transaction was cancelled, if applicable

| | **Files** | Object |

Files attached to the transaction as a key-value mapping. Key is the file ID, value is the file information.

| | **Signers** | Array\<[Signer](/openapi/models/Signer.md)> |

List of signers attached to this transaction, including their configuration, current status, verification methods, and signing progress. Each signer represents an individual who needs to sign the document or has already completed the signing process.

| | **Receivers** | Array\<[Receiver](/openapi/models/Receiver.md)> |

List of receivers who will receive copies of the completed signed documents. Receivers are notified via email once the transaction is successfully completed and all required signatures have been obtained.

| | **CancelationReason** | String |

The reason for the cancellation of the transaction, if applicable. This is provided when the transaction is cancelled via the DELETE endpoint.

| ### 401 Unauthorized - user not owner of transaction Content-Type: `application/json` **Schema:** [ErrorResponse](/openapi/models/ErrorResponse.md) #### Properties | Name | Type | Description | | ------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **type** | String |

A URI reference identifying the problem type. This property is always present in RFC 7807 responses.

| | **title** | String |

A short, human-readable summary of the problem

| | **detail** | String |

A human-readable explanation specific to this occurrence of the problem

| | **status** | Integer |

The HTTP status code

| | **instance** | String |

A URI reference that identifies the specific occurrence of the problem

| | **Message** | String |

Deprecated (Legacy Format Only): Contains the error message in the old response format. This property appears alone in legacy error responses and is mutually exclusive with the RFC 7807 properties above. Will be phased out as we complete our transition to RFC 7807.

| **Example:** ```json { "Message": "Authorization has been denied for this request." } ``` ### 410 The transaction details are no longer available and as a result the returned JSON only contains partial historical data. This happens when the system cleaned the transaction for example due to reaching an end status (any status above 30) or the transaction has expired. Content-Type: `application/json` **Schema:** [Transaction](/openapi/models/Transaction.md) #### Properties | Name | Type | Description | | -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **Id** | String |

Unique transaction identifier

| | **Seal** | Boolean |

Whether the transaction is sealed (no signers required)

| | **Reference** | String |

Custom reference identifier

| | **PostbackUrl** | String |

Webhook URL for status notifications

| | **DaysToExpire** | Integer |

Days until the transaction expires.

| | **SendEmailNotifications** | Boolean |

Send e-mail notifications to the sender.

| | **SignRequestMode** | enum: |

Sign request delivery mode

| | **Language** | enum: |

Language code for the Sender email notifications, and transaction receipt.

| | **Status** | enum: |

Current transaction status:

| | **Context** | Object |

Custom JSON object for additional transaction metadata that was provided during creation.

| | **CreatedDateTime** | String |

When the transaction was created

| | **ModifiedDateTime** | String |

When the transaction was last modified

| | **CanceledDateTime** | String |

When the transaction was cancelled, if applicable

| | **Files** | Object |

Files attached to the transaction as a key-value mapping. Key is the file ID, value is the file information.

| | **Signers** | Array\<[Signer](/openapi/models/Signer.md)> |

List of signers attached to this transaction, including their configuration, current status, verification methods, and signing progress. Each signer represents an individual who needs to sign the document or has already completed the signing process.

| | **Receivers** | Array\<[Receiver](/openapi/models/Receiver.md)> |

List of receivers who will receive copies of the completed signed documents. Receivers are notified via email once the transaction is successfully completed and all required signatures have been obtained.

| | **CancelationReason** | String |

The reason for the cancellation of the transaction, if applicable. This is provided when the transaction is cancelled via the DELETE endpoint.

| ### 500 Internal server error Content-Type: `application/json` **Schema:** [ErrorResponse](/openapi/models/ErrorResponse.md) #### Properties | Name | Type | Description | | ------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **type** | String |

A URI reference identifying the problem type. This property is always present in RFC 7807 responses.

| | **title** | String |

A short, human-readable summary of the problem

| | **detail** | String |

A human-readable explanation specific to this occurrence of the problem

| | **status** | Integer |

The HTTP status code

| | **instance** | String |

A URI reference that identifies the specific occurrence of the problem

| | **Message** | String |

Deprecated (Legacy Format Only): Contains the error message in the old response format. This property appears alone in legacy error responses and is mutually exclusive with the RFC 7807 properties above. Will be phased out as we complete our transition to RFC 7807.

| --- url: /openapi/transactions/startTransaction.md --- # Start a transaction **Method:** `PUT` **Path:** `/api/transaction/{transactionId}/start` Start the signing process for a transaction. All required files and signers must be configured before starting. Once started, the transaction cannot be modified and signers will be notified to begin signing. ## Parameters | Name | In | Type | Required | Description | | ----------------- | ---- | ------ | -------- | ---------------------------------- | | **transactionId** | path | string | Yes |

The transaction ID to start

| ## Responses ### 204 Transaction started successfully ### 400 The transaction could not be started. This typically occurs if the transaction contains errors or is in a Failed status. Content-Type: `application/json` **Schema:** [ErrorResponse](/openapi/models/ErrorResponse.md) #### Properties | Name | Type | Description | | ------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **type** | String |

A URI reference identifying the problem type. This property is always present in RFC 7807 responses.

| | **title** | String |

A short, human-readable summary of the problem

| | **detail** | String |

A human-readable explanation specific to this occurrence of the problem

| | **status** | Integer |

The HTTP status code

| | **instance** | String |

A URI reference that identifies the specific occurrence of the problem

| | **Message** | String |

Deprecated (Legacy Format Only): Contains the error message in the old response format. This property appears alone in legacy error responses and is mutually exclusive with the RFC 7807 properties above. Will be phased out as we complete our transition to RFC 7807.

| ### 401 Unauthorized - user not creator of transaction Content-Type: `application/json` **Schema:** [ErrorResponse](/openapi/models/ErrorResponse.md) #### Properties | Name | Type | Description | | ------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **type** | String |

A URI reference identifying the problem type. This property is always present in RFC 7807 responses.

| | **title** | String |

A short, human-readable summary of the problem

| | **detail** | String |

A human-readable explanation specific to this occurrence of the problem

| | **status** | Integer |

The HTTP status code

| | **instance** | String |

A URI reference that identifies the specific occurrence of the problem

| | **Message** | String |

Deprecated (Legacy Format Only): Contains the error message in the old response format. This property appears alone in legacy error responses and is mutually exclusive with the RFC 7807 properties above. Will be phased out as we complete our transition to RFC 7807.

| ### 403 Forbidden - authorization policy violation Content-Type: `application/json` **Schema:** [ErrorResponse](/openapi/models/ErrorResponse.md) #### Properties | Name | Type | Description | | ------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **type** | String |

A URI reference identifying the problem type. This property is always present in RFC 7807 responses.

| | **title** | String |

A short, human-readable summary of the problem

| | **detail** | String |

A human-readable explanation specific to this occurrence of the problem

| | **status** | Integer |

The HTTP status code

| | **instance** | String |

A URI reference that identifies the specific occurrence of the problem

| | **Message** | String |

Deprecated (Legacy Format Only): Contains the error message in the old response format. This property appears alone in legacy error responses and is mutually exclusive with the RFC 7807 properties above. Will be phased out as we complete our transition to RFC 7807.

| ### 500 Internal server error Content-Type: `application/json` **Schema:** [ErrorResponse](/openapi/models/ErrorResponse.md) #### Properties | Name | Type | Description | | ------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **type** | String |

A URI reference identifying the problem type. This property is always present in RFC 7807 responses.

| | **title** | String |

A short, human-readable summary of the problem

| | **detail** | String |

A human-readable explanation specific to this occurrence of the problem

| | **status** | Integer |

The HTTP status code

| | **instance** | String |

A URI reference that identifies the specific occurrence of the problem

| | **Message** | String |

Deprecated (Legacy Format Only): Contains the error message in the old response format. This property appears alone in legacy error responses and is mutually exclusive with the RFC 7807 properties above. Will be phased out as we complete our transition to RFC 7807.

|