diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index c1215b7..6484bfe 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -40,4 +40,11 @@ jobs:
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
projectName: turbodocx-docs
directory: build
- gitHubToken: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
+ gitHubToken: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Submit URLs to IndexNow
+ if: github.ref == 'refs/heads/main'
+ run: node scripts/submit-to-indexnow.js
+ env:
+ SITE_URL: https://docs.turbodocx.com
+ MAX_URLS: 100
\ No newline at end of file
diff --git a/docs/TurboSign/API Signatures.md b/docs/TurboSign/API Signatures.md
new file mode 100644
index 0000000..142017d
--- /dev/null
+++ b/docs/TurboSign/API Signatures.md
@@ -0,0 +1,571 @@
+---
+title: TurboSign API Integration
+sidebar_position: 3
+description: Complete guide for integrating TurboSign API to upload documents, add recipients, and prepare documents for electronic signatures. Learn the 3-step process with detailed examples and code samples.
+keywords:
+ - turbosign api
+ - document upload api
+ - electronic signature api
+ - recipients api
+ - signature preparation
+ - api integration
+ - document signing workflow
+ - turbodocx api
+ - signature api endpoints
+ - api authentication
+ - document processing api
+ - api tutorial
+ - signature document api
+ - api examples
+ - postman examples
+ - curl examples
+ - javascript api
+ - python api
+ - nodejs api
+ - php api
+ - signature automation
+ - document workflow api
+ - electronic signature integration
+ - api best practices
+ - api troubleshooting
+ - bearer token authentication
+---
+
+import ScriptLoader from '@site/src/components/ScriptLoader';
+
+# TurboSign API Integration
+
+This comprehensive guide walks you through the TurboSign API integration process. Learn how to programmatically upload documents, add recipients, and prepare documents for electronic signatures using our RESTful API.
+
+
+
+## Overview
+
+The TurboSign API follows a simple 3-step process to prepare documents for electronic signatures:
+
+1. **Upload Document** - Upload your PDF document to TurboSign
+2. **Add Recipients** - Configure who needs to sign and their signing order
+3. **Prepare for Signing** - Set up signature fields and send for signing
+
+
+
+### Key Features
+
+- **RESTful API**: Standard HTTP methods with JSON payloads
+- **Bearer Token Authentication**: Secure API access using JWT tokens
+- **Multiple Recipients**: Support for multiple signers with custom signing order
+- **Flexible Field Placement**: Position signature fields using anchors or coordinates
+- **Real-time Status Updates**: Track document status throughout the signing process
+- **Webhook Integration**: Receive notifications when signing is complete
+
+## TLDR; Complete Working Example ๐
+
+Don't want to read all the details? Here's what you need to know:
+
+### Available Field Types
+
+| Type | Description | Use Case |
+| ----------- | -------------------------- | ------------------------------------------------- |
+| `signature` | Electronic signature field | Legal signatures |
+| `initial` | Initial field | Document initials, paragraph acknowledgments |
+| `date` | Date picker field | Signing date, agreement date |
+| `full_name` | Full name field | Automatically fills signer's complete name |
+| `first_name`| First name field | Automatically fills signer's first name |
+| `last_name` | Last name field | Automatically fills signer's last name |
+| `title` | Title/job title field | Professional title or position |
+| `company` | Company name field | Organization or company name |
+| `email` | Email address field | Signer's email address |
+| `text` | Generic text input field | Custom text, notes, or any other text input |
+
+### Complete 3-Step Workflow
+
+
+
+Now that you've seen the whole thing, let's dive into the details...
+
+## Prerequisites
+
+Before you begin, ensure you have:
+
+- **API Access Token**: Bearer token for authentication
+- **Organization ID**: Your organization identifier
+- **PDF Document**: Document ready for signature collection
+
+### Getting Your Credentials
+
+1. **Login to TurboDocx**: Visit [https://www.turbodocx.com](https://www.turbodocx.com)
+2. **Navigate to Settings**: Access your organization settings
+3. **API Keys Section**: Generate or retrieve your API access token
+4. **Organization ID**: Copy your organization ID from the settings
+
+
+
+
+## Authentication
+
+All TurboSign API requests require authentication using a Bearer token in the Authorization header:
+
+```http
+Authorization: Bearer YOUR_API_TOKEN
+```
+
+Additional required headers for all requests:
+
+```http
+x-rapiddocx-org-id: YOUR_ORGANIZATION_ID
+origin: https://www.turbodocx.com
+accept: application/json, text/plain, */*
+```
+
+## Step 1: Upload Document
+
+The first step is to upload your PDF document to TurboSign. This creates a new document record and returns a document ID that you'll use in subsequent steps.
+
+### Endpoint
+
+```http
+POST https://www.turbodocx.com/turbosign/documents/upload
+```
+
+### Headers
+
+```http
+Content-Type: multipart/form-data
+Authorization: Bearer YOUR_API_TOKEN
+x-rapiddocx-org-id: YOUR_ORGANIZATION_ID
+origin: https://www.turbodocx.com
+referer: https://www.turbodocx.com
+accept: application/json, text/plain, */*
+```
+
+### Request Body (Form Data)
+
+```javascript
+{
+ "name": "Contract Agreement",
+ "file": [PDF_FILE_BINARY],
+ // Optional: triggerMeta for advanced configurations
+ // "triggerMeta": "{\"url\": \"callback_url\"}"
+}
+```
+
+### Response
+
+```json
+{
+ "data": {
+ "id": "4a20eca5-7944-430c-97d5-fcce4be24296",
+ "name": "Contract Agreement",
+ "description": "",
+ "status": "draft",
+ "createdOn": "2025-09-17T13:24:57.083Z"
+ }
+}
+```
+
+### Response Fields
+
+| Field | Type | Description |
+| ------------------ | ------ | ----------------------------------------------------- |
+| `data.id` | string | Unique document identifier (save this for next steps) |
+| `data.name` | string | Document name as provided |
+| `data.description` | string | Document description (empty by default) |
+| `data.status` | string | Document status (`draft` after upload) |
+| `data.createdOn` | string | ISO 8601 timestamp of document creation |
+
+
+
+### Code Examples
+
+
+
+## Step 2: Add Recipients
+
+After uploading your document, add the recipients who need to sign. You can specify multiple recipients with their signing order and customize their signature appearance.
+
+### Endpoint
+
+```http
+POST https://www.turbodocx.com/turbosign/documents/{documentId}/update-with-recipients
+```
+
+### Headers
+
+```http
+Content-Type: application/json
+Authorization: Bearer YOUR_API_TOKEN
+x-rapiddocx-org-id: YOUR_ORGANIZATION_ID
+origin: https://www.turbodocx.com
+referer: https://www.turbodocx.com
+accept: application/json, text/plain, */*
+dnt: 1
+accept-language: en-US,en;q=0.9
+priority: u=1, i
+```
+
+### Request Body
+
+```json
+{
+ "document": {
+ "name": "Contract Agreement - Updated",
+ "description": "This document requires electronic signatures from both parties. Please review all content carefully before signing."
+ },
+ "recipients": [
+ {
+ "name": "John Smith",
+ "email": "john.smith@company.com",
+ "signingOrder": 1,
+ "metadata": {
+ "color": "hsl(200, 75%, 50%)",
+ "lightColor": "hsl(200, 75%, 93%)"
+ },
+ "documentId": "4a20eca5-7944-430c-97d5-fcce4be24296"
+ },
+ {
+ "name": "Jane Doe",
+ "email": "jane.doe@partner.com",
+ "signingOrder": 2,
+ "metadata": {
+ "color": "hsl(270, 75%, 50%)",
+ "lightColor": "hsl(270, 75%, 93%)"
+ },
+ "documentId": "4a20eca5-7944-430c-97d5-fcce4be24296"
+ }
+ ]
+}
+```
+
+### Response
+
+```json
+{
+ "data": {
+ "document": {
+ "id": "4a20eca5-7944-430c-97d5-fcce4be24296",
+ "name": "Contract Agreement - Updated",
+ "description": "This document requires electronic signatures from both parties. Please review all content carefully before signing.",
+ "status": "setup_complete",
+ "updatedOn": "2025-09-17T13:26:10.000Z"
+ },
+ "recipients": [
+ {
+ "id": "5f673f37-9912-4e72-85aa-8f3649760f6b",
+ "name": "John Smith",
+ "email": "john.smith@company.com",
+ "signingOrder": 1,
+ "metadata": {
+ "color": "hsl(200, 75%, 50%)",
+ "lightColor": "hsl(200, 75%, 93%)"
+ }
+ },
+ {
+ "id": "a8b9c1d2-3456-7890-abcd-ef1234567890",
+ "name": "Jane Doe",
+ "email": "jane.doe@partner.com",
+ "signingOrder": 2,
+ "metadata": {
+ "color": "hsl(270, 75%, 50%)",
+ "lightColor": "hsl(270, 75%, 93%)"
+ }
+ }
+ ]
+ }
+}
+```
+
+### Request Fields
+
+| Field | Type | Required | Description |
+| ---------------------------------- | ------ | -------- | --------------------------------------------------- |
+| `document.name` | string | Yes | Updated document name |
+| `document.description` | string | No | Document description for recipients |
+| `recipients[].name` | string | Yes | Full name of the signer |
+| `recipients[].email` | string | Yes | Email address for signing notifications |
+| `recipients[].signingOrder` | number | Yes | Order in which recipients should sign (1, 2, 3...) |
+| `recipients[].metadata.color` | string | No | Primary color for this recipient's signature fields |
+| `recipients[].metadata.lightColor` | string | No | Light color variant for highlights |
+| `recipients[].documentId` | string | Yes | Document ID from Step 1 |
+
+
+
+### Code Examples
+
+
+
+## Step 3: Prepare for Signing
+
+The final step configures signature fields and sends the document to recipients for signing. You can position fields using text anchors or absolute coordinates.
+
+### Endpoint
+
+```http
+POST https://www.turbodocx.com/turbosign/documents/{documentId}/prepare-for-signing
+```
+
+### Headers
+
+```http
+Content-Type: application/json
+Authorization: Bearer YOUR_API_TOKEN
+x-rapiddocx-org-id: YOUR_ORGANIZATION_ID
+origin: https://www.turbodocx.com
+referer: https://www.turbodocx.com
+accept: application/json, text/plain, */*
+dnt: 1
+accept-language: en-US,en;q=0.9
+priority: u=1, i
+sec-ch-ua: "Not)A;Brand";v="8", "Chromium";v="138", "Google Chrome";v="138"
+sec-ch-ua-mobile: ?0
+sec-ch-ua-platform: "Windows"
+sec-fetch-dest: empty
+sec-fetch-mode: cors
+sec-fetch-site: same-site
+user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36
+x-device-fingerprint: 280624a233f1fd39ce050a9e9d0a4cc9
+```
+
+### Request Body
+
+```json
+[
+ {
+ "recipientId": "5f673f37-9912-4e72-85aa-8f3649760f6b",
+ "type": "signature",
+ "template": {
+ "anchor": "{Signature1}",
+ "placement": "replace",
+ "size": { "width": 200, "height": 80 },
+ "offset": { "x": 0, "y": 0 },
+ "caseSensitive": true,
+ "useRegex": false
+ },
+ "defaultValue": "",
+ "required": true
+ },
+ {
+ "recipientId": "5f673f37-9912-4e72-85aa-8f3649760f6b",
+ "type": "date",
+ "template": {
+ "anchor": "{Date1}",
+ "placement": "replace",
+ "size": { "width": 150, "height": 30 },
+ "offset": { "x": 0, "y": 0 },
+ "caseSensitive": true,
+ "useRegex": false
+ },
+ "defaultValue": "",
+ "required": true
+ },
+ {
+ "recipientId": "a8b9c1d2-3456-7890-abcd-ef1234567890",
+ "type": "signature",
+ "template": {
+ "anchor": "{Signature2}",
+ "placement": "replace",
+ "size": { "width": 200, "height": 80 },
+ "offset": { "x": 0, "y": 0 },
+ "caseSensitive": true,
+ "useRegex": false
+ },
+ "defaultValue": "",
+ "required": true
+ },
+ {
+ "recipientId": "a8b9c1d2-3456-7890-abcd-ef1234567890",
+ "type": "text",
+ "template": {
+ "anchor": "{Title2}",
+ "placement": "replace",
+ "size": { "width": 200, "height": 30 },
+ "offset": { "x": 0, "y": 0 },
+ "caseSensitive": true,
+ "useRegex": false
+ },
+ "defaultValue": "Business Partner",
+ "required": false
+ }
+]
+```
+
+### Response
+
+```json
+{
+ "success": true
+}
+```
+
+### Template Configuration
+
+| Field | Type | Required | Description |
+| ------------------------ | ------- | -------- | ------------------------------------------------------ |
+| `recipientId` | string | Yes | Recipient ID from Step 2 |
+| `type` | string | Yes | Field type - see Available Field Types in TLDR section |
+| `template.anchor` | string | Yes | Text anchor to find in document (e.g., "{Signature1}") |
+| `template.placement` | string | Yes | How to place field ("replace", "before", "after") |
+| `template.size` | object | Yes | Field dimensions (width, height in pixels) |
+| `template.offset` | object | No | Position offset from anchor (x, y in pixels) |
+| `template.caseSensitive` | boolean | No | Whether anchor search is case-sensitive |
+| `template.useRegex` | boolean | No | Whether to treat anchor as regex pattern |
+| `defaultValue` | string | No | Pre-filled value for the field |
+| `required` | boolean | No | Whether field must be completed |
+
+
+
+### Code Examples
+
+
+
+## Best Practices
+
+### Security
+
+- **Never expose API tokens**: Store tokens securely in environment variables
+- **Use HTTPS only**: All API calls must use HTTPS in production
+- **Validate inputs**: Always validate recipient emails and document names
+- **Implement rate limiting**: Respect API rate limits to avoid throttling
+
+### Error Handling
+
+- **Check HTTP status codes**: Always verify response status before processing
+- **Handle timeouts**: Implement retry logic for network failures
+- **Log API responses**: Keep detailed logs for debugging and monitoring
+- **Validate responses**: Check response structure before accessing data
+
+### Performance
+
+- **Upload optimization**: Compress PDFs when possible to reduce upload time
+- **Batch operations**: Group multiple recipients in single API calls
+- **Async processing**: Use webhooks instead of polling for status updates
+- **Connection pooling**: Reuse HTTP connections for multiple requests
+
+### Document Preparation
+
+- **Use text anchors**: Place anchor text like `{Signature1}` in your PDFs for precise field positioning
+- **Consistent naming**: Use consistent anchor naming conventions across documents
+- **Test coordinates**: Verify field positions with test documents before production
+- **Document validation**: Ensure PDFs are not password-protected or corrupted
+
+## Error Handling & Troubleshooting
+
+### Common HTTP Status Codes
+
+| Status Code | Description | Solution |
+| ----------- | --------------------- | --------------------------------------------- |
+| `200` | Success | Request completed successfully |
+| `400` | Bad Request | Check request body format and required fields |
+| `401` | Unauthorized | Verify API token and headers |
+| `403` | Forbidden | Check organization ID and permissions |
+| `404` | Not Found | Verify document ID and endpoint URLs |
+| `422` | Unprocessable Entity | Validate field values and constraints |
+| `429` | Too Many Requests | Implement rate limiting and retry logic |
+| `500` | Internal Server Error | Contact support if persistent |
+
+### Common Issues
+
+#### Authentication Failures
+
+**Symptoms**: 401 Unauthorized responses
+
+**Solutions**:
+
+- Verify API token is correct and not expired
+- Check that `x-rapiddocx-org-id` header matches your organization
+- Ensure Bearer token format: `Bearer YOUR_TOKEN`
+
+#### Document Upload Failures
+
+**Symptoms**: Upload returns error or times out
+
+**Solutions**:
+
+- Verify PDF file is not corrupted or password-protected
+- Check file size is under the maximum limit (typically 10MB)
+- Ensure file is actually a PDF (check MIME type)
+- Verify network connection and try again
+
+#### Recipient Configuration Issues
+
+**Symptoms**: Recipients not receiving signing invitations
+
+**Solutions**:
+
+- Verify email addresses are valid and correctly formatted
+- Check signing order numbers are sequential (1, 2, 3...)
+- Ensure document ID from Step 1 is used correctly
+- Verify recipient metadata format is correct
+
+#### Field Positioning Problems
+
+**Symptoms**: Signature fields appear in wrong locations
+
+**Solutions**:
+
+- Verify anchor text exists in the PDF document
+- Check anchor text matches exactly (case-sensitive by default)
+- Test with `caseSensitive: false` if having matching issues
+- Use PDF coordinates as fallback if anchors don't work
+
+#### Webhook Integration Issues
+
+**Symptoms**: Not receiving completion notifications
+
+**Solutions**:
+
+- Verify webhook URLs are accessible and return 200 OK
+- Check webhook configuration in organization settings
+- Review webhook delivery history for error details
+- Test webhook endpoints with external tools
+
+### Debugging Tips
+
+1. **Enable request logging**: Log all API requests and responses
+2. **Test step by step**: Isolate issues by testing each step individually
+3. **Use Postman**: Import examples and test manually before coding
+4. **Check network**: Verify connectivity to `turbodocx.com`
+5. **Validate JSON**: Ensure request bodies are valid JSON format
+
+
+
+## Next Steps
+
+### Webhooks - The Next Logical Step
+
+Now that you've integrated the basic signing flow, the next step is setting up webhooks to receive real-time notifications when documents are signed. This eliminates the need for polling and provides instant updates about document status changes.
+
+๐ **[Learn how to configure Webhooks โ](/docs/TurboSign/Webhooks)**
+
+### Related Documentation
+
+- [TurboSign Setup Guide](/docs/TurboSign/Setting-up-TurboSign)
+- [Webhook Configuration](/docs/TurboSign/Webhooks)
+- [API Authentication](/docs/API/turbodocx-api-documentation)
+- [Integration Examples](/docs/Integrations)
+
+## Support
+
+Need help with your integration?
+
+- **Discord Community**: [Join our Discord server](https://discord.gg/NYKwz4BcpX) for real-time support and discussions
+- **Documentation**: [https://docs.turbodocx.com](https://docs.turbodocx.com)
+
+---
+
+Ready to get started? Follow the step-by-step guide above to integrate TurboSign API into your application and start collecting electronic signatures programmatically!
diff --git a/docusaurus-plugin-indexnow.js b/docusaurus-plugin-indexnow.js
new file mode 100644
index 0000000..afbff98
--- /dev/null
+++ b/docusaurus-plugin-indexnow.js
@@ -0,0 +1,111 @@
+/**
+ * Docusaurus IndexNow Plugin
+ * Automatically submits URLs to search engines supporting IndexNow protocol
+ */
+
+const path = require('path');
+const fs = require('fs');
+const https = require('https');
+
+const INDEXNOW_API_KEY = '800eabc35ea5450111e9509e56e568af49305a629982d640818b63cc837c0da1';
+const INDEXNOW_ENDPOINT = 'https://api.indexnow.org/indexnow';
+
+module.exports = function docusaurusPluginIndexNow(context, options) {
+ const { siteConfig } = context;
+ const { url: siteUrl } = siteConfig;
+
+ // Default options
+ const pluginOptions = {
+ enabled: true,
+ submitOnBuild: false, // Disabled by default - use CI/CD script instead
+ maxUrls: 100,
+ ...options
+ };
+
+ return {
+ name: 'docusaurus-plugin-indexnow',
+
+ async postBuild({ siteDir, outDir, routesPaths }) {
+ if (!pluginOptions.enabled || !pluginOptions.submitOnBuild) {
+ return;
+ }
+
+ console.log('๐ก IndexNow: Preparing to submit URLs to search engines...');
+
+ // Convert route paths to full URLs
+ const urls = routesPaths
+ .map(route => `${siteUrl}${route}`)
+ .slice(0, pluginOptions.maxUrls);
+
+ if (urls.length === 0) {
+ console.log('โ ๏ธ IndexNow: No URLs found to submit');
+ return;
+ }
+
+ try {
+ await submitToIndexNow(siteUrl, urls);
+ console.log(`โ
IndexNow: Successfully submitted ${urls.length} URLs`);
+ } catch (error) {
+ console.error('โ IndexNow: Failed to submit URLs:', error.message);
+ }
+ },
+
+ configureWebpack(config, isServer) {
+ return {
+ // No webpack configuration needed
+ };
+ }
+ };
+};
+
+/**
+ * Submit URLs to IndexNow API
+ * @param {string} host - The website host
+ * @param {string[]} urls - Array of URLs to submit
+ */
+async function submitToIndexNow(host, urls) {
+ return new Promise((resolve, reject) => {
+ const hostname = new URL(host).hostname;
+
+ const payload = JSON.stringify({
+ host: hostname,
+ key: INDEXNOW_API_KEY,
+ keyLocation: `${host}/${INDEXNOW_API_KEY}.txt`,
+ urlList: urls
+ });
+
+ const options = {
+ hostname: 'api.indexnow.org',
+ port: 443,
+ path: '/indexnow',
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json; charset=utf-8',
+ 'Content-Length': Buffer.byteLength(payload)
+ }
+ };
+
+ const req = https.request(options, (res) => {
+ let data = '';
+
+ res.on('data', (chunk) => {
+ data += chunk;
+ });
+
+ res.on('end', () => {
+ if (res.statusCode === 200) {
+ resolve({ success: true, data });
+ } else {
+ reject(new Error(`HTTP ${res.statusCode}: ${data}`));
+ }
+ });
+ });
+
+ req.on('error', (error) => {
+ reject(error);
+ });
+
+ req.write(payload);
+ req.end();
+ });
+}
\ No newline at end of file
diff --git a/docusaurus.config.js b/docusaurus.config.js
index 6ccd450..def0481 100644
--- a/docusaurus.config.js
+++ b/docusaurus.config.js
@@ -56,6 +56,14 @@ const config = {
removeDuplicateHeadings: true
}
],
+ [
+ './docusaurus-plugin-indexnow.js',
+ {
+ enabled: true,
+ submitOnBuild: false, // Only submit via CI/CD after deployment
+ maxUrls: 100
+ }
+ ],
[
'docusaurus-plugin-openapi-docs',
{
diff --git a/scripts/submit-to-indexnow.js b/scripts/submit-to-indexnow.js
new file mode 100755
index 0000000..6fa58a1
--- /dev/null
+++ b/scripts/submit-to-indexnow.js
@@ -0,0 +1,187 @@
+#!/usr/bin/env node
+
+/**
+ * IndexNow URL Submission Script for CI/CD
+ *
+ * This script submits URLs to IndexNow after successful deployment to Cloudflare.
+ * Should be run as part of your GitHub Actions workflow after deployment.
+ *
+ * Usage:
+ * node scripts/submit-to-indexnow.js
+ *
+ * Environment Variables:
+ * SITE_URL - The production URL of your site (defaults to https://docs.turbodocx.com)
+ * MAX_URLS - Maximum number of URLs to submit (defaults to 100)
+ * DRY_RUN - If set to 'true', will only show what would be submitted without actually submitting
+ */
+
+const https = require('https');
+const fs = require('fs');
+const path = require('path');
+
+// Configuration
+const INDEXNOW_API_KEY = '800eabc35ea5450111e9509e56e568af49305a629982d640818b63cc837c0da1';
+const INDEXNOW_ENDPOINT = 'https://api.indexnow.org/indexnow';
+const SITE_URL = process.env.SITE_URL || 'https://docs.turbodocx.com';
+const MAX_URLS = parseInt(process.env.MAX_URLS || '100');
+const DRY_RUN = process.env.DRY_RUN === 'true';
+
+async function main() {
+ console.log('๐ IndexNow CI/CD Submission Script');
+ console.log(`๐ Site URL: ${SITE_URL}`);
+ console.log(`๐ข Max URLs: ${MAX_URLS}`);
+ console.log(`๐งช Dry Run: ${DRY_RUN ? 'Yes' : 'No'}`);
+ console.log('');
+
+ try {
+ // Generate sitemap URLs or read from build artifacts
+ const urls = await generateUrlList();
+
+ if (urls.length === 0) {
+ console.log('โ ๏ธ No URLs found to submit');
+ process.exit(0);
+ }
+
+ console.log(`๐ Found ${urls.length} URLs to submit`);
+
+ // Limit URLs
+ const urlsToSubmit = urls.slice(0, MAX_URLS);
+
+ if (DRY_RUN) {
+ console.log('๐งช DRY RUN - Would submit these URLs:');
+ urlsToSubmit.forEach((url, i) => console.log(` ${i + 1}. ${url}`));
+ console.log(`\nโ
Dry run completed. Would submit ${urlsToSubmit.length} URLs to IndexNow`);
+ return;
+ }
+
+ // Submit to IndexNow
+ await submitToIndexNow(SITE_URL, urlsToSubmit);
+ console.log(`โ
Successfully submitted ${urlsToSubmit.length} URLs to IndexNow`);
+
+ } catch (error) {
+ console.error('โ Error:', error.message);
+ process.exit(1);
+ }
+}
+
+/**
+ * Generate list of URLs to submit
+ * This function can be enhanced to read from sitemap.xml or build artifacts
+ */
+async function generateUrlList() {
+ const urls = [];
+
+ // Try to read from sitemap first (if available)
+ const sitemapPath = path.join(__dirname, '..', 'build', 'sitemap.xml');
+ if (fs.existsSync(sitemapPath)) {
+ console.log('๐ Reading URLs from sitemap.xml...');
+ const sitemap = fs.readFileSync(sitemapPath, 'utf8');
+ const urlMatches = sitemap.match(/(.*?)<\/loc>/g);
+ if (urlMatches) {
+ urlMatches.forEach(match => {
+ const url = match.replace(/<\/?loc>/g, '');
+ if (url.startsWith(SITE_URL)) {
+ urls.push(url);
+ }
+ });
+ }
+ }
+
+ // Fallback: Generate common URLs
+ if (urls.length === 0) {
+ console.log('๐ Generating default URL list...');
+ const commonPaths = [
+ '/',
+ '/docs/Welcome%20to%20TurboDocx',
+ '/docs/category/turbodocx-templating',
+ '/docs/category/integrations',
+ '/docs/category/turbosign',
+ '/docs/category/advanced-configuration',
+ '/docs/category/api'
+ ];
+
+ commonPaths.forEach(path => {
+ urls.push(`${SITE_URL}${path}`);
+ });
+
+ // Add API documentation URLs
+ const apiPaths = [
+ '/docs/API/turbodocx-api-documentation',
+ '/docs/API/create-deliverable',
+ '/docs/API/get-templates-and-folders',
+ '/docs/API/upload-template-with-optional-default-values'
+ ];
+
+ apiPaths.forEach(path => {
+ urls.push(`${SITE_URL}${path}`);
+ });
+ }
+
+ return [...new Set(urls)]; // Remove duplicates
+}
+
+/**
+ * Submit URLs to IndexNow API
+ */
+async function submitToIndexNow(siteUrl, urls) {
+ return new Promise((resolve, reject) => {
+ const hostname = new URL(siteUrl).hostname;
+
+ const payload = JSON.stringify({
+ host: hostname,
+ key: INDEXNOW_API_KEY,
+ keyLocation: `${siteUrl}/${INDEXNOW_API_KEY}.txt`,
+ urlList: urls
+ });
+
+ console.log('๐ก Submitting to IndexNow API...');
+ console.log(`๐ Key Location: ${siteUrl}/${INDEXNOW_API_KEY}.txt`);
+ console.log(`๐ Host: ${hostname}`);
+
+ const options = {
+ hostname: 'api.indexnow.org',
+ port: 443,
+ path: '/indexnow',
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json; charset=utf-8',
+ 'Content-Length': Buffer.byteLength(payload),
+ 'User-Agent': 'TurboDocx-Docs-IndexNow/1.0'
+ }
+ };
+
+ const req = https.request(options, (res) => {
+ let data = '';
+
+ res.on('data', (chunk) => {
+ data += chunk;
+ });
+
+ res.on('end', () => {
+ console.log(`๐ Response Status: ${res.statusCode}`);
+ if (res.statusCode === 200 || res.statusCode === 202) {
+ console.log('โ
IndexNow API accepted the submission');
+ resolve({ success: true, statusCode: res.statusCode, data });
+ } else {
+ console.log(`โ IndexNow API Error: ${res.statusCode}`);
+ if (data) console.log(`Response: ${data}`);
+ reject(new Error(`HTTP ${res.statusCode}: ${data}`));
+ }
+ });
+ });
+
+ req.on('error', (error) => {
+ reject(error);
+ });
+
+ req.write(payload);
+ req.end();
+ });
+}
+
+// Run the script
+if (require.main === module) {
+ main();
+}
+
+module.exports = { submitToIndexNow, generateUrlList };
\ No newline at end of file
diff --git a/scripts/webhooks/verification/csharp.cs b/scripts/webhooks/verification/csharp.cs
deleted file mode 100644
index 020810f..0000000
--- a/scripts/webhooks/verification/csharp.cs
+++ /dev/null
@@ -1,204 +0,0 @@
-using System;
-using System.Security.Cryptography;
-using System.Text;
-using System.Text.Json;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Http;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.Logging;
-using System.IO;
-using System.Threading.Tasks;
-
-namespace TurboDocx.Webhooks
-{
- // Functional webhook verification methods
- public static class WebhookVerification
- {
- ///
- /// Verifies a TurboDocx webhook signature
- ///
- /// X-TurboDocx-Signature header value
- /// X-TurboDocx-Timestamp header value
- /// Raw request body
- /// Webhook secret
- /// True if signature is valid
- public static bool VerifySignature(string signature, string timestamp, string body, string secret)
- {
- if (string.IsNullOrEmpty(signature) || string.IsNullOrEmpty(timestamp) || body == null || string.IsNullOrEmpty(secret))
- return false;
-
- // Check timestamp is within 5 minutes
- if (!long.TryParse(timestamp, out long webhookTime))
- return false;
-
- var currentTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
- if (Math.Abs(currentTime - webhookTime) > 300)
- return false;
-
- // Generate expected signature
- var signedString = $"{timestamp}.{body}";
- using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
- var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(signedString));
- var expectedSignature = $"sha256={BitConverter.ToString(hash).Replace("-", "").ToLower()}";
-
- // Timing-safe comparison
- return CryptographicOperations.FixedTimeEquals(
- Encoding.UTF8.GetBytes(signature),
- Encoding.UTF8.GetBytes(expectedSignature)
- );
- }
-
- ///
- /// Process webhook event payload
- ///
- /// The event type
- /// Event data
- /// Optional logger
- public static async Task ProcessEventAsync(string eventType, JsonElement data, ILogger logger = null)
- {
- logger?.LogInformation("Received event: {EventType}", eventType);
-
- switch (eventType)
- {
- case "signature.document.completed":
- await HandleDocumentCompletedAsync(data, logger);
- break;
-
- case "signature.document.voided":
- await HandleDocumentVoidedAsync(data, logger);
- break;
-
- default:
- logger?.LogWarning("Unknown event type: {EventType}", eventType);
- break;
- }
- }
-
- private static async Task HandleDocumentCompletedAsync(JsonElement data, ILogger logger)
- {
- var documentId = data.TryGetProperty("document_id", out var idProp) ? idProp.GetString() : "unknown";
- logger?.LogInformation("Document completed: {DocumentId}", documentId);
-
- // Add your completion logic here
- await Task.CompletedTask;
- }
-
- private static async Task HandleDocumentVoidedAsync(JsonElement data, ILogger logger)
- {
- var documentId = data.TryGetProperty("document_id", out var idProp) ? idProp.GetString() : "unknown";
- logger?.LogInformation("Document voided: {DocumentId}", documentId);
-
- // Add your void logic here
- await Task.CompletedTask;
- }
- }
-
- // Simple webhook payload structure
- public struct WebhookPayload
- {
- public string Event { get; set; }
- public JsonElement Data { get; set; }
- public string Timestamp { get; set; }
- }
-
- // Functional webhook handler for ASP.NET Core
- [ApiController]
- [Route("api/[controller]")]
- public class WebhookController : ControllerBase
- {
- private readonly IConfiguration _configuration;
- private readonly ILogger _logger;
-
- public WebhookController(IConfiguration configuration, ILogger logger)
- {
- _configuration = configuration;
- _logger = logger;
- }
-
- [HttpPost]
- public async Task HandleWebhook()
- {
- try
- {
- var webhookSecret = _configuration["TurboDocx:WebhookSecret"] ??
- _configuration["WEBHOOK_SECRET"] ??
- throw new InvalidOperationException("Webhook secret not configured");
-
- // Get headers
- var signature = Request.Headers["X-TurboDocx-Signature"].FirstOrDefault();
- var timestamp = Request.Headers["X-TurboDocx-Timestamp"].FirstOrDefault();
-
- // Read raw body
- using var reader = new StreamReader(Request.Body);
- var body = await reader.ReadToEndAsync();
-
- // Verify signature
- if (!WebhookVerification.VerifySignature(signature, timestamp, body, webhookSecret))
- {
- _logger.LogWarning("Webhook signature verification failed");
- return Unauthorized();
- }
-
- // Parse and process webhook
- var payload = JsonSerializer.Deserialize(body);
- await WebhookVerification.ProcessEventAsync(payload.Event, payload.Data, _logger);
-
- return Ok();
- }
- catch (JsonException ex)
- {
- _logger.LogError(ex, "Invalid JSON payload received");
- return BadRequest("Invalid payload");
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error processing webhook");
- return StatusCode(500, "Internal server error");
- }
- }
- }
-
- // Minimal setup helper functions
- public static class WebhookSetup
- {
- ///
- /// Simple webhook handler function for minimal APIs or Azure Functions
- ///
- public static async Task HandleWebhookRequest(HttpRequest request, string webhookSecret, ILogger logger = null)
- {
- try
- {
- // Get headers
- var signature = request.Headers["X-TurboDocx-Signature"].FirstOrDefault();
- var timestamp = request.Headers["X-TurboDocx-Timestamp"].FirstOrDefault();
-
- // Read body
- using var reader = new StreamReader(request.Body);
- var body = await reader.ReadToEndAsync();
-
- // Verify signature
- if (!WebhookVerification.VerifySignature(signature, timestamp, body, webhookSecret))
- {
- logger?.LogWarning("Webhook signature verification failed");
- return Results.Unauthorized();
- }
-
- // Parse and process
- var payload = JsonSerializer.Deserialize(body);
- await WebhookVerification.ProcessEventAsync(payload.Event, payload.Data, logger);
-
- return Results.Ok("OK");
- }
- catch (JsonException ex)
- {
- logger?.LogError(ex, "Invalid JSON payload");
- return Results.BadRequest("Invalid payload");
- }
- catch (Exception ex)
- {
- logger?.LogError(ex, "Error processing webhook");
- return Results.Problem("Internal server error");
- }
- }
- }
-}
\ No newline at end of file
diff --git a/scripts/webhooks/verification/csharp/controller.cs b/scripts/webhooks/verification/csharp/controller.cs
deleted file mode 100644
index d341d0e..0000000
--- a/scripts/webhooks/verification/csharp/controller.cs
+++ /dev/null
@@ -1,234 +0,0 @@
-using System;
-using System.Security.Cryptography;
-using System.Text;
-using System.Text.Json;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Http;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.Logging;
-using System.IO;
-using System.Threading.Tasks;
-
-namespace TurboDocx.Webhooks
-{
- public static class WebhookVerification
- {
- ///
- /// Verifies a TurboDocx webhook signature
- ///
- public static bool VerifySignature(string signature, string timestamp, string body, string secret)
- {
- if (string.IsNullOrEmpty(signature) || string.IsNullOrEmpty(timestamp) || body == null || string.IsNullOrEmpty(secret))
- return false;
-
- // Check timestamp is within 5 minutes
- if (!long.TryParse(timestamp, out long webhookTime))
- return false;
-
- var currentTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
- if (Math.Abs(currentTime - webhookTime) > 300)
- return false;
-
- // Generate expected signature
- var signedString = $"{timestamp}.{body}";
- using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
- var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(signedString));
- var expectedSignature = $"sha256={BitConverter.ToString(hash).Replace("-", "").ToLower()}";
-
- // Timing-safe comparison
- return CryptographicOperations.FixedTimeEquals(
- Encoding.UTF8.GetBytes(signature),
- Encoding.UTF8.GetBytes(expectedSignature)
- );
- }
-
- ///
- /// Process webhook event payload
- ///
- public static async Task ProcessEventAsync(string eventType, JsonElement data, ILogger logger = null)
- {
- logger?.LogInformation("Received event: {EventType}", eventType);
-
- switch (eventType)
- {
- case "signature.document.completed":
- await HandleDocumentCompletedAsync(data, logger);
- break;
-
- case "signature.document.voided":
- await HandleDocumentVoidedAsync(data, logger);
- break;
-
- default:
- logger?.LogWarning("Unknown event type: {EventType}", eventType);
- break;
- }
- }
-
- private static async Task HandleDocumentCompletedAsync(JsonElement data, ILogger logger)
- {
- var documentId = data.TryGetProperty("document_id", out var idProp) ? idProp.GetString() : "unknown";
- logger?.LogInformation("Document completed: {DocumentId}", documentId);
-
- // Add your completion logic here
- await Task.CompletedTask;
- }
-
- private static async Task HandleDocumentVoidedAsync(JsonElement data, ILogger logger)
- {
- var documentId = data.TryGetProperty("document_id", out var idProp) ? idProp.GetString() : "unknown";
- logger?.LogInformation("Document voided: {DocumentId}", documentId);
-
- // Add your void logic here
- await Task.CompletedTask;
- }
- }
-
- public struct WebhookPayload
- {
- public string Event { get; set; }
- public JsonElement Data { get; set; }
- public string Timestamp { get; set; }
- }
-
- ///
- /// ASP.NET Core Controller-based webhook handler
- ///
- [ApiController]
- [Route("api/[controller]")]
- public class WebhookController : ControllerBase
- {
- private readonly IConfiguration _configuration;
- private readonly ILogger _logger;
-
- public WebhookController(IConfiguration configuration, ILogger logger)
- {
- _configuration = configuration;
- _logger = logger;
- }
-
- [HttpPost]
- public async Task HandleWebhook()
- {
- try
- {
- var webhookSecret = _configuration["TurboDocx:WebhookSecret"] ??
- _configuration["WEBHOOK_SECRET"] ??
- throw new InvalidOperationException("Webhook secret not configured");
-
- // Get headers
- var signature = Request.Headers["X-TurboDocx-Signature"].FirstOrDefault();
- var timestamp = Request.Headers["X-TurboDocx-Timestamp"].FirstOrDefault();
-
- // Read raw body
- using var reader = new StreamReader(Request.Body);
- var body = await reader.ReadToEndAsync();
-
- // Verify signature
- if (!WebhookVerification.VerifySignature(signature, timestamp, body, webhookSecret))
- {
- _logger.LogWarning("Webhook signature verification failed");
- return Unauthorized();
- }
-
- // Parse and process webhook
- var payload = JsonSerializer.Deserialize(body);
- await WebhookVerification.ProcessEventAsync(payload.Event, payload.Data, _logger);
-
- return Ok();
- }
- catch (JsonException ex)
- {
- _logger.LogError(ex, "Invalid JSON payload received");
- return BadRequest("Invalid payload");
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error processing webhook");
- return StatusCode(500, "Internal server error");
- }
- }
-
- [HttpGet("health")]
- public IActionResult HealthCheck()
- {
- return Ok(new {
- status = "ok",
- timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
- });
- }
- }
-
- ///
- /// Alternative attribute-based approach
- ///
- public class WebhookVerificationAttribute : Attribute, IAsyncActionFilter
- {
- public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
- {
- var configuration = context.HttpContext.RequestServices.GetService();
- var logger = context.HttpContext.RequestServices.GetService>();
-
- var webhookSecret = configuration["TurboDocx:WebhookSecret"] ??
- configuration["WEBHOOK_SECRET"];
-
- if (string.IsNullOrEmpty(webhookSecret))
- {
- context.Result = new BadRequestObjectResult("Webhook secret not configured");
- return;
- }
-
- var signature = context.HttpContext.Request.Headers["X-TurboDocx-Signature"].FirstOrDefault();
- var timestamp = context.HttpContext.Request.Headers["X-TurboDocx-Timestamp"].FirstOrDefault();
-
- context.HttpContext.Request.EnableBuffering();
- using var reader = new StreamReader(context.HttpContext.Request.Body);
- var body = await reader.ReadToEndAsync();
- context.HttpContext.Request.Body.Position = 0;
-
- if (!WebhookVerification.VerifySignature(signature, timestamp, body, webhookSecret))
- {
- logger?.LogWarning("Webhook signature verification failed");
- context.Result = new UnauthorizedResult();
- return;
- }
-
- await next();
- }
- }
-
- ///
- /// Usage example with attribute
- ///
- [ApiController]
- [Route("api/[controller]")]
- public class SecureWebhookController : ControllerBase
- {
- private readonly ILogger _logger;
-
- public SecureWebhookController(ILogger logger)
- {
- _logger = logger;
- }
-
- [HttpPost]
- [WebhookVerification]
- public async Task HandleSecureWebhook()
- {
- try
- {
- using var reader = new StreamReader(Request.Body);
- var body = await reader.ReadToEndAsync();
- var payload = JsonSerializer.Deserialize(body);
-
- await WebhookVerification.ProcessEventAsync(payload.Event, payload.Data, _logger);
- return Ok();
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error processing webhook");
- return StatusCode(500, "Internal server error");
- }
- }
- }
-}
\ No newline at end of file
diff --git a/scripts/webhooks/verification/csharp/minimal.cs b/scripts/webhooks/verification/csharp/minimal.cs
deleted file mode 100644
index e114b31..0000000
--- a/scripts/webhooks/verification/csharp/minimal.cs
+++ /dev/null
@@ -1,181 +0,0 @@
-using System.Security.Cryptography;
-using System.Text;
-using System.Text.Json;
-using Microsoft.AspNetCore.Mvc;
-
-var builder = WebApplication.CreateBuilder(args);
-
-// Add services
-builder.Services.AddLogging();
-
-var app = builder.Build();
-
-// Webhook verification helper
-static bool VerifySignature(string signature, string timestamp, string body, string secret)
-{
- if (string.IsNullOrEmpty(signature) || string.IsNullOrEmpty(timestamp) || body == null || string.IsNullOrEmpty(secret))
- return false;
-
- // Check timestamp is within 5 minutes
- if (!long.TryParse(timestamp, out long webhookTime))
- return false;
-
- var currentTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
- if (Math.Abs(currentTime - webhookTime) > 300)
- return false;
-
- // Generate expected signature
- var signedString = $"{timestamp}.{body}";
- using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
- var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(signedString));
- var expectedSignature = $"sha256={BitConverter.ToString(hash).Replace("-", "").ToLower()}";
-
- // Timing-safe comparison
- return CryptographicOperations.FixedTimeEquals(
- Encoding.UTF8.GetBytes(signature),
- Encoding.UTF8.GetBytes(expectedSignature)
- );
-}
-
-// Process webhook event
-static async Task ProcessEventAsync(string eventType, JsonElement data, ILogger logger)
-{
- logger.LogInformation("Received event: {EventType}", eventType);
-
- switch (eventType)
- {
- case "signature.document.completed":
- await HandleDocumentCompletedAsync(data, logger);
- break;
-
- case "signature.document.voided":
- await HandleDocumentVoidedAsync(data, logger);
- break;
-
- default:
- logger.LogWarning("Unknown event type: {EventType}", eventType);
- break;
- }
-}
-
-static async Task HandleDocumentCompletedAsync(JsonElement data, ILogger logger)
-{
- var documentId = data.TryGetProperty("document_id", out var idProp) ? idProp.GetString() : "unknown";
- logger.LogInformation("Document completed: {DocumentId}", documentId);
-
- // Add your completion logic here
- await Task.CompletedTask;
-}
-
-static async Task HandleDocumentVoidedAsync(JsonElement data, ILogger logger)
-{
- var documentId = data.TryGetProperty("document_id", out var idProp) ? idProp.GetString() : "unknown";
- logger.LogInformation("Document voided: {DocumentId}", documentId);
-
- // Add your void logic here
- await Task.CompletedTask;
-}
-
-// Configure webhook secret
-var webhookSecret = builder.Configuration["TurboDocx:WebhookSecret"] ??
- builder.Configuration["WEBHOOK_SECRET"] ??
- Environment.GetEnvironmentVariable("WEBHOOK_SECRET") ??
- "your-webhook-secret";
-
-// Webhook endpoint
-app.MapPost("/webhook", async (HttpRequest request, ILogger logger) =>
-{
- try
- {
- // Get headers
- var signature = request.Headers["X-TurboDocx-Signature"].FirstOrDefault() ?? "";
- var timestamp = request.Headers["X-TurboDocx-Timestamp"].FirstOrDefault() ?? "";
-
- // Read raw body
- using var reader = new StreamReader(request.Body);
- var body = await reader.ReadToEndAsync();
-
- // Verify signature
- if (!VerifySignature(signature, timestamp, body, webhookSecret))
- {
- logger.LogWarning("Webhook signature verification failed");
- return Results.Unauthorized();
- }
-
- // Parse and process webhook
- var payload = JsonSerializer.Deserialize(body);
- var eventType = payload.RootElement.GetProperty("event").GetString();
- var data = payload.RootElement.GetProperty("data");
-
- await ProcessEventAsync(eventType, data, logger);
-
- return Results.Ok(new { status = "ok" });
- }
- catch (JsonException ex)
- {
- logger.LogError(ex, "Invalid JSON payload received");
- return Results.BadRequest("Invalid payload");
- }
- catch (Exception ex)
- {
- logger.LogError(ex, "Error processing webhook");
- return Results.Problem("Internal server error");
- }
-});
-
-// Health check endpoint
-app.MapGet("/health", () => Results.Ok(new {
- status = "ok",
- timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
-}));
-
-// Webhook endpoint with middleware approach
-app.MapPost("/webhook/middleware", async (HttpRequest request, ILogger logger) =>
-{
- var payload = JsonSerializer.Deserialize(await request.ReadFromJsonAsync());
- var eventType = payload.RootElement.GetProperty("event").GetString();
- var data = payload.RootElement.GetProperty("data");
-
- await ProcessEventAsync(eventType, data, logger);
- return Results.Ok();
-}).AddEndpointFilter(async (context, next) =>
-{
- var request = context.HttpContext.Request;
- var logger = context.HttpContext.RequestServices.GetRequiredService>();
-
- var signature = request.Headers["X-TurboDocx-Signature"].FirstOrDefault() ?? "";
- var timestamp = request.Headers["X-TurboDocx-Timestamp"].FirstOrDefault() ?? "";
-
- // Read body for verification
- request.EnableBuffering();
- using var reader = new StreamReader(request.Body);
- var body = await reader.ReadToEndAsync();
- request.Body.Position = 0;
-
- if (!VerifySignature(signature, timestamp, body, webhookSecret))
- {
- logger.LogWarning("Webhook signature verification failed");
- return Results.Unauthorized();
- }
-
- return await next(context);
-});
-
-// Configure logging
-if (app.Environment.IsDevelopment())
-{
- app.UseRouting();
- app.UseDeveloperExceptionPage();
-}
-
-var port = Environment.GetEnvironmentVariable("PORT") ?? "5000";
-app.Urls.Add($"http://0.0.0.0:{port}");
-
-app.Logger.LogInformation("Starting TurboDocx webhook server on port {Port}", port);
-app.Logger.LogInformation("Webhook secret configured: {SecretLength} characters", webhookSecret.Length);
-
-app.Run();
-
-// Record types for structured data
-public record WebhookPayload(string Event, JsonElement Data, string Timestamp);
-public record WebhookResponse(string Status, DateTime Timestamp);
\ No newline at end of file
diff --git a/scripts/webhooks/verification/go.go b/scripts/webhooks/verification/go.go
deleted file mode 100644
index 953e4c0..0000000
--- a/scripts/webhooks/verification/go.go
+++ /dev/null
@@ -1,177 +0,0 @@
-package main
-
-import (
- "crypto/hmac"
- "crypto/sha256"
- "encoding/hex"
- "encoding/json"
- "fmt"
- "io"
- "log"
- "net/http"
- "os"
- "strconv"
- "time"
-)
-
-// WebhookVerifier handles webhook signature verification
-type WebhookVerifier struct {
- Secret string
-}
-
-// NewWebhookVerifier creates a new webhook verifier
-func NewWebhookVerifier(secret string) *WebhookVerifier {
- return &WebhookVerifier{
- Secret: secret,
- }
-}
-
-// VerifySignature verifies the webhook signature
-func (wv *WebhookVerifier) VerifySignature(signature, timestamp, body string) bool {
- if signature == "" || timestamp == "" {
- return false
- }
-
- // Check timestamp is within 5 minutes
- ts, err := strconv.ParseInt(timestamp, 10, 64)
- if err != nil {
- return false
- }
-
- currentTime := time.Now().Unix()
- if abs(currentTime-ts) > 300 {
- return false
- }
-
- // Generate expected signature
- signedString := timestamp + "." + body
- expectedSignature := "sha256=" + wv.computeHMAC(signedString)
-
- // Timing-safe comparison
- return hmac.Equal([]byte(signature), []byte(expectedSignature))
-}
-
-// computeHMAC computes HMAC-SHA256 for the given data
-func (wv *WebhookVerifier) computeHMAC(data string) string {
- h := hmac.New(sha256.New, []byte(wv.Secret))
- h.Write([]byte(data))
- return hex.EncodeToString(h.Sum(nil))
-}
-
-// WebhookPayload represents the webhook payload structure
-type WebhookPayload struct {
- Event string `json:"event"`
- Data map[string]interface{} `json:"data"`
- Timestamp string `json:"timestamp"`
-}
-
-// ProcessEvent processes the webhook event
-func (wv *WebhookVerifier) ProcessEvent(payload *WebhookPayload) {
- log.Printf("Received event: %s", payload.Event)
-
- switch payload.Event {
- case "signature.document.completed":
- wv.handleDocumentCompleted(payload.Data)
- case "signature.document.voided":
- wv.handleDocumentVoided(payload.Data)
- default:
- log.Printf("Unknown event type: %s", payload.Event)
- }
-}
-
-func (wv *WebhookVerifier) handleDocumentCompleted(data map[string]interface{}) {
- documentID, ok := data["document_id"].(string)
- if !ok {
- documentID = "unknown"
- }
- log.Printf("Document completed: %s", documentID)
-
- // Add your completion logic here
-}
-
-func (wv *WebhookVerifier) handleDocumentVoided(data map[string]interface{}) {
- documentID, ok := data["document_id"].(string)
- if !ok {
- documentID = "unknown"
- }
- log.Printf("Document voided: %s", documentID)
-
- // Add your void logic here
-}
-
-// WebhookHandler handles HTTP requests for webhooks
-type WebhookHandler struct {
- Verifier *WebhookVerifier
-}
-
-// NewWebhookHandler creates a new webhook handler
-func NewWebhookHandler(secret string) *WebhookHandler {
- return &WebhookHandler{
- Verifier: NewWebhookVerifier(secret),
- }
-}
-
-// ServeHTTP handles the webhook HTTP request
-func (wh *WebhookHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- if r.Method != http.MethodPost {
- http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
- return
- }
-
- // Read the body
- body, err := io.ReadAll(r.Body)
- if err != nil {
- http.Error(w, "Failed to read body", http.StatusBadRequest)
- return
- }
- defer r.Body.Close()
-
- // Get headers
- signature := r.Header.Get("X-TurboDocx-Signature")
- timestamp := r.Header.Get("X-TurboDocx-Timestamp")
-
- // Verify signature
- if !wh.Verifier.VerifySignature(signature, timestamp, string(body)) {
- log.Printf("Webhook signature verification failed")
- http.Error(w, "Unauthorized", http.StatusUnauthorized)
- return
- }
-
- // Parse payload
- var payload WebhookPayload
- if err := json.Unmarshal(body, &payload); err != nil {
- log.Printf("Failed to parse JSON payload: %v", err)
- http.Error(w, "Bad Request", http.StatusBadRequest)
- return
- }
-
- // Process the event
- wh.Verifier.ProcessEvent(&payload)
-
- // Return success
- w.WriteHeader(http.StatusOK)
- w.Write([]byte("OK"))
-}
-
-// abs returns the absolute value of x
-func abs(x int64) int64 {
- if x < 0 {
- return -x
- }
- return x
-}
-
-// Example usage
-func main() {
- webhookSecret := os.Getenv("WEBHOOK_SECRET")
- if webhookSecret == "" {
- webhookSecret = "your-webhook-secret"
- }
-
- handler := NewWebhookHandler(webhookSecret)
-
- http.Handle("/webhook", handler)
-
- fmt.Println("Server starting on :8080")
- log.Fatal(http.ListenAndServe(":8080", nil))
-}
\ No newline at end of file
diff --git a/scripts/webhooks/verification/java.java b/scripts/webhooks/verification/java.java
deleted file mode 100644
index 2f9a41f..0000000
--- a/scripts/webhooks/verification/java.java
+++ /dev/null
@@ -1,218 +0,0 @@
-package com.turbodocx.webhooks;
-
-import javax.crypto.Mac;
-import javax.crypto.spec.SecretKeySpec;
-import java.security.MessageDigest;
-import java.nio.charset.StandardCharsets;
-import java.util.Map;
-import java.util.Objects;
-import org.springframework.web.bind.annotation.*;
-import org.springframework.http.ResponseEntity;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.stereotype.Service;
-import org.springframework.stereotype.Component;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.JsonNode;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-@Service
-public class WebhookVerificationService {
-
- private static final Logger logger = LoggerFactory.getLogger(WebhookVerificationService.class);
- private final String webhookSecret;
- private final ObjectMapper objectMapper;
-
- public WebhookVerificationService(@Value("${turbodocx.webhook.secret}") String webhookSecret) {
- this.webhookSecret = Objects.requireNonNull(webhookSecret, "Webhook secret cannot be null");
- this.objectMapper = new ObjectMapper();
- }
-
- /**
- * Verifies a TurboDocx webhook signature
- *
- * @param signature X-TurboDocx-Signature header value
- * @param timestamp X-TurboDocx-Timestamp header value
- * @param body Raw request body
- * @return true if signature is valid
- */
- public boolean verifySignature(String signature, String timestamp, String body) {
- if (signature == null || timestamp == null || body == null) {
- return false;
- }
-
- try {
- // Check timestamp is within 5 minutes
- long currentTime = System.currentTimeMillis() / 1000;
- long webhookTime = Long.parseLong(timestamp);
-
- if (Math.abs(currentTime - webhookTime) > 300) {
- logger.warn("Webhook timestamp too old or too far in future");
- return false;
- }
-
- // Generate expected signature
- String signedString = timestamp + "." + body;
- Mac mac = Mac.getInstance("HmacSHA256");
- SecretKeySpec secretKeySpec = new SecretKeySpec(
- webhookSecret.getBytes(StandardCharsets.UTF_8),
- "HmacSHA256"
- );
- mac.init(secretKeySpec);
-
- byte[] hash = mac.doFinal(signedString.getBytes(StandardCharsets.UTF_8));
- StringBuilder hexString = new StringBuilder();
-
- for (byte b : hash) {
- String hex = Integer.toHexString(0xff & b);
- if (hex.length() == 1) {
- hexString.append('0');
- }
- hexString.append(hex);
- }
-
- String expectedSignature = "sha256=" + hexString.toString();
-
- // Timing-safe comparison
- return MessageDigest.isEqual(
- signature.getBytes(StandardCharsets.UTF_8),
- expectedSignature.getBytes(StandardCharsets.UTF_8)
- );
-
- } catch (Exception e) {
- logger.error("Error verifying webhook signature", e);
- return false;
- }
- }
-
- /**
- * Process webhook event payload
- *
- * @param body Raw request body containing the webhook payload
- */
- public void processEvent(String body) {
- try {
- JsonNode payload = objectMapper.readTree(body);
- String eventType = payload.get("event").asText();
-
- logger.info("Received event: {}", eventType);
-
- switch (eventType) {
- case "signature.document.completed":
- handleDocumentCompleted(payload.get("data"));
- break;
-
- case "signature.document.voided":
- handleDocumentVoided(payload.get("data"));
- break;
-
- default:
- logger.warn("Unknown event type: {}", eventType);
- }
-
- } catch (Exception e) {
- logger.error("Error processing webhook event", e);
- throw new RuntimeException("Failed to process webhook event", e);
- }
- }
-
- private void handleDocumentCompleted(JsonNode data) {
- String documentId = data.get("document_id").asText();
- logger.info("Document completed: {}", documentId);
-
- // Add your completion logic here
- }
-
- private void handleDocumentVoided(JsonNode data) {
- String documentId = data.get("document_id").asText();
- logger.info("Document voided: {}", documentId);
-
- // Add your void logic here
- }
-}
-
-@RestController
-@RequestMapping("/api/webhook")
-public class WebhookController {
-
- private static final Logger logger = LoggerFactory.getLogger(WebhookController.class);
- private final WebhookVerificationService verificationService;
-
- public WebhookController(WebhookVerificationService verificationService) {
- this.verificationService = verificationService;
- }
-
- @PostMapping
- public ResponseEntity handleWebhook(
- @RequestHeader("X-TurboDocx-Signature") String signature,
- @RequestHeader("X-TurboDocx-Timestamp") String timestamp,
- @RequestBody String body) {
-
- try {
- // Verify signature
- if (!verificationService.verifySignature(signature, timestamp, body)) {
- logger.warn("Webhook signature verification failed");
- return ResponseEntity.status(401).body("Unauthorized");
- }
-
- // Process the event
- verificationService.processEvent(body);
-
- return ResponseEntity.ok("OK");
-
- } catch (Exception e) {
- logger.error("Error handling webhook", e);
- return ResponseEntity.badRequest().body("Error processing webhook");
- }
- }
-}
-
-// Configuration class for Spring Boot applications
-@Component
-public class WebhookConfig {
-
- @Value("${turbodocx.webhook.secret:#{null}}")
- private String webhookSecret;
-
- public void validateConfiguration() {
- if (webhookSecret == null || webhookSecret.trim().isEmpty()) {
- throw new IllegalStateException(
- "Webhook secret must be configured via 'turbodocx.webhook.secret' property"
- );
- }
- }
-}
-
-// Data classes for webhook payloads
-public class WebhookPayload {
- private String event;
- private JsonNode data;
- private String timestamp;
-
- // Constructors
- public WebhookPayload() {}
-
- public WebhookPayload(String event, JsonNode data, String timestamp) {
- this.event = event;
- this.data = data;
- this.timestamp = timestamp;
- }
-
- // Getters and setters
- public String getEvent() { return event; }
- public void setEvent(String event) { this.event = event; }
-
- public JsonNode getData() { return data; }
- public void setData(JsonNode data) { this.data = data; }
-
- public String getTimestamp() { return timestamp; }
- public void setTimestamp(String timestamp) { this.timestamp = timestamp; }
-
- @Override
- public String toString() {
- return "WebhookPayload{" +
- "event='" + event + '\'' +
- ", timestamp='" + timestamp + '\'' +
- '}';
- }
-}
\ No newline at end of file
diff --git a/scripts/webhooks/verification/nodejs.js b/scripts/webhooks/verification/nodejs.js
deleted file mode 100644
index a4babd1..0000000
--- a/scripts/webhooks/verification/nodejs.js
+++ /dev/null
@@ -1,96 +0,0 @@
-const crypto = require("crypto");
-
-/**
- * Verifies a TurboDocx webhook signature
- * @param {Object} request - The request object
- * @param {Object} request.headers - Request headers
- * @param {string} request.rawBody - Raw request body as string
- * @param {string} secret - Your webhook secret
- * @returns {boolean} - True if signature is valid
- */
-function verifyWebhookSignature(request, secret) {
- const signature = request.headers["x-turbodocx-signature"];
- const timestamp = request.headers["x-turbodocx-timestamp"];
- const body = request.rawBody;
-
- // Check timestamp is within 5 minutes
- const currentTime = Math.floor(Date.now() / 1000);
- if (Math.abs(currentTime - parseInt(timestamp)) > 300) {
- return false; // Timestamp too old or too far in future
- }
-
- // Generate expected signature
- const signedString = `${timestamp}.${body}`;
- const expectedSignature =
- "sha256=" +
- crypto
- .createHmac("sha256", secret)
- .update(signedString, "utf8")
- .digest("hex");
-
- // Use timing-safe comparison
- return crypto.timingSafeEqual(
- Buffer.from(signature),
- Buffer.from(expectedSignature)
- );
-}
-
-/**
- * Express.js middleware for webhook verification
- * @param {string} secret - Your webhook secret
- * @returns {Function} Express middleware
- */
-function createWebhookMiddleware(secret) {
- return (req, res, next) => {
- if (!verifyWebhookSignature(req, secret)) {
- return res.status(401).send("Unauthorized");
- }
- next();
- };
-}
-
-/**
- * Complete Express.js webhook handler example
- */
-function setupWebhookHandler(app, secret) {
- app.post(
- "/webhook",
- express.raw({ type: "application/json" }),
- (req, res) => {
- if (!verifyWebhookSignature(req, secret)) {
- return res.status(401).send("Unauthorized");
- }
-
- // Process the webhook
- const payload = JSON.parse(req.body);
- console.log("Received event:", payload.event);
-
- // Always return 200 OK quickly
- res.status(200).send("OK");
-
- // Process the event asynchronously
- processWebhookEvent(payload);
- }
- );
-}
-
-async function processWebhookEvent(payload) {
- // Add your webhook processing logic here
- switch (payload.event) {
- case "signature.document.completed":
- console.log("Document completed:", payload.data.document_id);
- break;
- case "signature.document.voided":
- console.log("Document voided:", payload.data.document_id);
- break;
- default:
- console.log("Unknown event type:", payload.event);
- }
-}
-
-module.exports = {
- verifyWebhookSignature,
- createWebhookMiddleware,
- setupWebhookHandler,
- processWebhookEvent,
-};
diff --git a/scripts/webhooks/verification/nodejs/express.js b/scripts/webhooks/verification/nodejs/express.js
deleted file mode 100644
index ef21816..0000000
--- a/scripts/webhooks/verification/nodejs/express.js
+++ /dev/null
@@ -1,127 +0,0 @@
-const express = require('express');
-const crypto = require('crypto');
-
-/**
- * Verifies a TurboDocx webhook signature
- * @param {Object} request - The Express request object
- * @param {string} secret - Your webhook secret
- * @returns {boolean} - True if signature is valid
- */
-function verifyWebhookSignature(request, secret) {
- const signature = request.headers['x-turbodocx-signature'];
- const timestamp = request.headers['x-turbodocx-timestamp'];
- const body = request.rawBody || request.body;
-
- if (!signature || !timestamp || !body || !secret) {
- return false;
- }
-
- // Check timestamp is within 5 minutes
- const currentTime = Math.floor(Date.now() / 1000);
- if (Math.abs(currentTime - parseInt(timestamp)) > 300) {
- return false; // Timestamp too old or too far in future
- }
-
- // Generate expected signature
- const signedString = `${timestamp}.${body}`;
- const expectedSignature = 'sha256=' +
- crypto.createHmac('sha256', secret)
- .update(signedString, 'utf8')
- .digest('hex');
-
- // Use timing-safe comparison
- return crypto.timingSafeEqual(
- Buffer.from(signature),
- Buffer.from(expectedSignature)
- );
-}
-
-/**
- * Process webhook event payload
- * @param {Object} payload - The webhook payload
- */
-function processWebhookEvent(payload) {
- const eventType = payload.event;
- console.log('Received event:', eventType);
-
- switch (eventType) {
- case 'signature.document.completed':
- handleDocumentCompleted(payload.data);
- break;
- case 'signature.document.voided':
- handleDocumentVoided(payload.data);
- break;
- default:
- console.log('Unknown event type:', eventType);
- }
-}
-
-function handleDocumentCompleted(data) {
- const documentId = data.document_id || 'unknown';
- console.log('Document completed:', documentId);
- // Add your completion logic here
-}
-
-function handleDocumentVoided(data) {
- const documentId = data.document_id || 'unknown';
- console.log('Document voided:', documentId);
- // Add your void logic here
-}
-
-/**
- * Express.js middleware for webhook verification
- * @param {string} secret - Your webhook secret
- * @returns {Function} Express middleware
- */
-function createWebhookMiddleware(secret) {
- return (req, res, next) => {
- if (!verifyWebhookSignature(req, secret)) {
- return res.status(401).send('Unauthorized');
- }
- next();
- };
-}
-
-// Express app setup
-const app = express();
-const webhookSecret = process.env.WEBHOOK_SECRET || 'your-webhook-secret';
-
-// Middleware to capture raw body for signature verification
-app.use('/webhook', express.raw({type: 'application/json'}), (req, res, next) => {
- req.rawBody = req.body.toString();
- next();
-});
-
-// Apply webhook verification middleware
-app.use('/webhook', createWebhookMiddleware(webhookSecret));
-
-// Webhook endpoint
-app.post('/webhook', (req, res) => {
- try {
- const payload = JSON.parse(req.rawBody);
- processWebhookEvent(payload);
- res.status(200).send('OK');
- } catch (error) {
- console.error('Error processing webhook:', error);
- res.status(400).send('Bad Request');
- }
-});
-
-// Health check endpoint
-app.get('/health', (req, res) => {
- res.json({ status: 'ok', timestamp: new Date().toISOString() });
-});
-
-// Start server
-const PORT = process.env.PORT || 3000;
-app.listen(PORT, () => {
- console.log(`Webhook server listening on port ${PORT}`);
- console.log(`Webhook secret configured: ${webhookSecret.length} characters`);
-});
-
-module.exports = {
- verifyWebhookSignature,
- processWebhookEvent,
- createWebhookMiddleware,
- app
-};
\ No newline at end of file
diff --git a/scripts/webhooks/verification/nodejs/fastify.js b/scripts/webhooks/verification/nodejs/fastify.js
deleted file mode 100644
index 9c714dc..0000000
--- a/scripts/webhooks/verification/nodejs/fastify.js
+++ /dev/null
@@ -1,143 +0,0 @@
-const fastify = require('fastify')({ logger: true });
-const crypto = require('crypto');
-
-/**
- * Verifies a TurboDocx webhook signature
- * @param {Object} request - The Fastify request object
- * @param {string} secret - Your webhook secret
- * @returns {boolean} - True if signature is valid
- */
-function verifyWebhookSignature(request, secret) {
- const signature = request.headers['x-turbodocx-signature'];
- const timestamp = request.headers['x-turbodocx-timestamp'];
- const body = request.rawBody || JSON.stringify(request.body);
-
- if (!signature || !timestamp || !body || !secret) {
- return false;
- }
-
- // Check timestamp is within 5 minutes
- const currentTime = Math.floor(Date.now() / 1000);
- if (Math.abs(currentTime - parseInt(timestamp)) > 300) {
- return false; // Timestamp too old or too far in future
- }
-
- // Generate expected signature
- const signedString = `${timestamp}.${body}`;
- const expectedSignature = 'sha256=' +
- crypto.createHmac('sha256', secret)
- .update(signedString, 'utf8')
- .digest('hex');
-
- // Use timing-safe comparison
- return crypto.timingSafeEqual(
- Buffer.from(signature),
- Buffer.from(expectedSignature)
- );
-}
-
-/**
- * Process webhook event payload
- * @param {Object} payload - The webhook payload
- */
-function processWebhookEvent(payload) {
- const eventType = payload.event;
- fastify.log.info('Received event:', eventType);
-
- switch (eventType) {
- case 'signature.document.completed':
- handleDocumentCompleted(payload.data);
- break;
- case 'signature.document.voided':
- handleDocumentVoided(payload.data);
- break;
- default:
- fastify.log.warn('Unknown event type:', eventType);
- }
-}
-
-function handleDocumentCompleted(data) {
- const documentId = data.document_id || 'unknown';
- fastify.log.info('Document completed:', documentId);
- // Add your completion logic here
-}
-
-function handleDocumentVoided(data) {
- const documentId = data.document_id || 'unknown';
- fastify.log.info('Document voided:', documentId);
- // Add your void logic here
-}
-
-const webhookSecret = process.env.WEBHOOK_SECRET || 'your-webhook-secret';
-
-// Add content parsing with raw body capture
-fastify.addContentTypeParser('application/json', { parseAs: 'buffer' }, (req, body, done) => {
- req.rawBody = body.toString();
- try {
- const json = JSON.parse(body);
- done(null, json);
- } catch (err) {
- done(err);
- }
-});
-
-// Webhook verification hook
-fastify.addHook('preHandler', async (request, reply) => {
- if (request.url === '/webhook' && request.method === 'POST') {
- if (!verifyWebhookSignature(request, webhookSecret)) {
- reply.code(401).send('Unauthorized');
- return;
- }
- }
-});
-
-// Webhook endpoint
-fastify.post('/webhook', {
- schema: {
- body: {
- type: 'object',
- properties: {
- event: { type: 'string' },
- data: { type: 'object' },
- timestamp: { type: 'string' }
- },
- required: ['event']
- }
- }
-}, async (request, reply) => {
- try {
- processWebhookEvent(request.body);
- reply.code(200).send('OK');
- } catch (error) {
- fastify.log.error('Error processing webhook:', error);
- reply.code(500).send('Internal Server Error');
- }
-});
-
-// Health check endpoint
-fastify.get('/health', async (request, reply) => {
- return { status: 'ok', timestamp: new Date().toISOString() };
-});
-
-// Start server
-const start = async () => {
- try {
- const PORT = process.env.PORT || 3000;
- await fastify.listen({ port: PORT, host: '0.0.0.0' });
- fastify.log.info(`Webhook server listening on port ${PORT}`);
- fastify.log.info(`Webhook secret configured: ${webhookSecret.length} characters`);
- } catch (err) {
- fastify.log.error(err);
- process.exit(1);
- }
-};
-
-if (require.main === module) {
- start();
-}
-
-module.exports = {
- verifyWebhookSignature,
- processWebhookEvent,
- fastify
-};
\ No newline at end of file
diff --git a/scripts/webhooks/verification/php.php b/scripts/webhooks/verification/php.php
deleted file mode 100644
index d7118fb..0000000
--- a/scripts/webhooks/verification/php.php
+++ /dev/null
@@ -1,202 +0,0 @@
- 300) {
- return false;
- }
-
- // Generate expected signature
- $signedString = $timestamp . '.' . $body;
- $expectedSignature = 'sha256=' . hash_hmac('sha256', $signedString, $secret);
-
- // Use timing-safe comparison
- return hash_equals($signature, $expectedSignature);
-}
-
-/**
- * Process webhook event payload
- *
- * @param array $payload Webhook payload
- */
-function processTurboDocxWebhookEvent(array $payload): void
-{
- $eventType = $payload['event'] ?? '';
-
- error_log('Received event: ' . $eventType);
-
- switch ($eventType) {
- case 'signature.document.completed':
- handleDocumentCompleted($payload['data'] ?? []);
- break;
-
- case 'signature.document.voided':
- handleDocumentVoided($payload['data'] ?? []);
- break;
-
- default:
- error_log("Unknown event type: $eventType");
- }
-}
-
-/**
- * Handle document completion event
- *
- * @param array $data Event data
- */
-function handleDocumentCompleted(array $data): void
-{
- $documentId = $data['document_id'] ?? 'unknown';
- error_log("Document completed: $documentId");
-
- // Add your completion logic here
- // Example: Send email notification, update database, etc.
-}
-
-/**
- * Handle document voided event
- *
- * @param array $data Event data
- */
-function handleDocumentVoided(array $data): void
-{
- $documentId = $data['document_id'] ?? 'unknown';
- error_log("Document voided: $documentId");
-
- // Add your void logic here
- // Example: Update status, send notifications, etc.
-}
-
-/**
- * Complete webhook handler function
- *
- * @param string $secret Your webhook secret
- * @param array|null $headers Optional headers (will read from request if null)
- * @param string|null $body Optional body (will read from php://input if null)
- * @return array Response with status code and body
- */
-function handleTurboDocxWebhook(string $secret, ?array $headers = null, ?string $body = null): array
-{
- // Get headers and body if not provided
- $headers = $headers ?? getallheaders();
- $body = $body ?? file_get_contents('php://input');
-
- // Verify signature
- if (!verifyTurboDocxWebhookSignature($headers, $body, $secret)) {
- error_log('Webhook signature verification failed');
- return ['status' => 401, 'body' => 'Unauthorized'];
- }
-
- // Process the webhook
- try {
- $payload = json_decode($body, true, 512, JSON_THROW_ON_ERROR);
-
- if (!is_array($payload)) {
- throw new JsonException('Payload is not a valid array');
- }
-
- processTurboDocxWebhookEvent($payload);
-
- return ['status' => 200, 'body' => 'OK'];
-
- } catch (JsonException $e) {
- error_log('Invalid JSON payload: ' . $e->getMessage());
- return ['status' => 400, 'body' => 'Bad Request'];
- } catch (Exception $e) {
- error_log('Error processing webhook: ' . $e->getMessage());
- return ['status' => 500, 'body' => 'Internal Server Error'];
- }
-}
-
-/**
- * Express-style middleware for webhook verification
- *
- * @param string $secret Your webhook secret
- * @return callable Middleware function
- */
-function createTurboDocxWebhookMiddleware(string $secret): callable
-{
- return function() use ($secret) {
- $result = handleTurboDocxWebhook($secret);
-
- http_response_code($result['status']);
- echo $result['body'];
-
- if ($result['status'] !== 200) {
- exit();
- }
- };
-}
-
-/**
- * Test webhook verification (useful for development)
- *
- * @param string $secret Test secret
- * @return bool Test result
- */
-function testWebhookVerification(string $secret = 'test-secret'): bool
-{
- $timestamp = (string) time();
- $body = json_encode([
- 'event' => 'signature.document.completed',
- 'data' => ['document_id' => 'test123'],
- 'timestamp' => $timestamp
- ]);
-
- // Generate test signature
- $signedString = $timestamp . '.' . $body;
- $signature = 'sha256=' . hash_hmac('sha256', $signedString, $secret);
-
- $headers = [
- 'X-TurboDocx-Signature' => $signature,
- 'X-TurboDocx-Timestamp' => $timestamp
- ];
-
- // Test verification
- $result = verifyTurboDocxWebhookSignature($headers, $body, $secret);
- echo "Test verification result: " . ($result ? 'PASS' : 'FAIL') . "\n";
-
- if ($result) {
- $payload = json_decode($body, true);
- processTurboDocxWebhookEvent($payload);
- }
-
- return $result;
-}
-
-// Example usage when script is run directly
-if (__FILE__ === $_SERVER['SCRIPT_FILENAME']) {
- $webhookSecret = $_ENV['WEBHOOK_SECRET'] ?? 'your-webhook-secret';
-
- // If running from command line, run test
- if (php_sapi_name() === 'cli') {
- echo "Running webhook verification test...\n";
- testWebhookVerification($webhookSecret);
- } else {
- // Handle webhook request
- $result = handleTurboDocxWebhook($webhookSecret);
- http_response_code($result['status']);
- echo $result['body'];
- }
-}
-
-?>
\ No newline at end of file
diff --git a/scripts/webhooks/verification/powershell.ps1 b/scripts/webhooks/verification/powershell.ps1
deleted file mode 100644
index f3e4ceb..0000000
--- a/scripts/webhooks/verification/powershell.ps1
+++ /dev/null
@@ -1,297 +0,0 @@
-# TurboDocx Webhook Verification PowerShell Script - Functional Approach
-
-# Verifies the webhook signature
-function Test-TurboDocxWebhookSignature {
- param(
- [Parameter(Mandatory=$true)]
- [string]$Signature,
-
- [Parameter(Mandatory=$true)]
- [string]$Timestamp,
-
- [Parameter(Mandatory=$true)]
- [string]$Body,
-
- [Parameter(Mandatory=$true)]
- [string]$Secret
- )
-
- if ([string]::IsNullOrEmpty($Signature) -or [string]::IsNullOrEmpty($Timestamp)) {
- return $false
- }
-
- # Check timestamp is within 5 minutes
- try {
- $currentTime = [DateTimeOffset]::UtcNow.ToUnixTimeSeconds()
- $webhookTime = [int64]$Timestamp
-
- if ([Math]::Abs($currentTime - $webhookTime) -gt 300) {
- Write-Warning "Webhook timestamp too old or too far in future"
- return $false
- }
- }
- catch {
- Write-Error "Invalid timestamp format: $_"
- return $false
- }
-
- # Generate expected signature
- $signedString = "$Timestamp.$Body"
- $expectedSignature = "sha256=" + (Get-TurboDocxHMAC -Data $signedString -Secret $Secret)
-
- # Timing-safe comparison
- return Compare-SecureString -StringA $Signature -StringB $expectedSignature
-}
-
-# Computes HMAC-SHA256 for the given data
-function Get-TurboDocxHMAC {
- param(
- [Parameter(Mandatory=$true)]
- [string]$Data,
-
- [Parameter(Mandatory=$true)]
- [string]$Secret
- )
-
- $hmacSha256 = New-Object System.Security.Cryptography.HMACSHA256
- $hmacSha256.Key = [System.Text.Encoding]::UTF8.GetBytes($Secret)
-
- $dataBytes = [System.Text.Encoding]::UTF8.GetBytes($Data)
- $hashBytes = $hmacSha256.ComputeHash($dataBytes)
-
- $hmacSha256.Dispose()
-
- return [System.BitConverter]::ToString($hashBytes).Replace("-", "").ToLower()
-}
-
-# Timing-safe string comparison (basic implementation)
-function Compare-SecureString {
- param(
- [Parameter(Mandatory=$true)]
- [string]$StringA,
-
- [Parameter(Mandatory=$true)]
- [string]$StringB
- )
-
- if ($StringA.Length -ne $StringB.Length) {
- return $false
- }
-
- $result = 0
- for ($i = 0; $i -lt $StringA.Length; $i++) {
- $result = $result -bor ($StringA[$i] -bxor $StringB[$i])
- }
-
- return $result -eq 0
-}
-
-# Process webhook event payload
-function Invoke-TurboDocxWebhookEvent {
- param(
- [Parameter(Mandatory=$true)]
- [hashtable]$Payload
- )
-
- $eventType = $Payload.event
- Write-Host "Received event: $eventType" -ForegroundColor Green
-
- switch ($eventType) {
- "signature.document.completed" {
- Invoke-DocumentCompleted -Data $Payload.data
- }
- "signature.document.voided" {
- Invoke-DocumentVoided -Data $Payload.data
- }
- default {
- Write-Warning "Unknown event type: $eventType"
- }
- }
-}
-
-# Handle document completion event
-function Invoke-DocumentCompleted {
- param(
- [Parameter(Mandatory=$true)]
- [hashtable]$Data
- )
-
- $documentId = if ($Data.document_id) { $Data.document_id } else { "unknown" }
- Write-Host "Document completed: $documentId" -ForegroundColor Cyan
-
- # Add your completion logic here
- # Example: Send email, update database, trigger workflow
-}
-
-# Handle document voided event
-function Invoke-DocumentVoided {
- param(
- [Parameter(Mandatory=$true)]
- [hashtable]$Data
- )
-
- $documentId = if ($Data.document_id) { $Data.document_id } else { "unknown" }
- Write-Host "Document voided: $documentId" -ForegroundColor Yellow
-
- # Add your void logic here
- # Example: Update status, send notifications
-}
-
-# Function to handle webhook request (for use with PowerShell Universal or other web frameworks)
-function Invoke-WebhookHandler {
- param(
- [Parameter(Mandatory=$true)]
- [hashtable]$Headers,
-
- [Parameter(Mandatory=$true)]
- [string]$Body,
-
- [Parameter(Mandatory=$true)]
- [string]$WebhookSecret
- )
-
- try {
-
- # Get signature and timestamp from headers
- $signature = $Headers['X-TurboDocx-Signature']
- $timestamp = $Headers['X-TurboDocx-Timestamp']
-
- # Verify signature
- if (-not (Test-TurboDocxWebhookSignature -Signature $signature -Timestamp $timestamp -Body $Body -Secret $WebhookSecret)) {
- Write-Warning "Webhook signature verification failed"
- return @{ StatusCode = 401; Body = "Unauthorized" }
- }
-
- # Parse JSON payload
- try {
- $payload = $Body | ConvertFrom-Json -AsHashtable
- }
- catch {
- Write-Error "Failed to parse JSON payload: $_"
- return @{ StatusCode = 400; Body = "Bad Request" }
- }
-
- # Process the event
- Invoke-TurboDocxWebhookEvent -Payload $payload
-
- # Return success
- return @{ StatusCode = 200; Body = "OK" }
- }
- catch {
- Write-Error "Error processing webhook: $_"
- return @{ StatusCode = 500; Body = "Internal Server Error" }
- }
-}
-
-# Function for Azure Functions or similar serverless environments
-function Invoke-TurboDocxWebhook {
- param(
- [Parameter(Mandatory=$true)]
- $Request,
-
- [Parameter(Mandatory=$true)]
- [string]$WebhookSecret
- )
-
- # Extract headers and body from the request object
- $headers = @{}
- foreach ($header in $Request.Headers.GetEnumerator()) {
- $headers[$header.Key] = $header.Value
- }
-
- # Get the raw body
- $body = $Request.RawBody
-
- # Process the webhook
- return Invoke-WebhookHandler -Headers $headers -Body $body -WebhookSecret $WebhookSecret
-}
-
-# Example usage and testing functions
-function Test-WebhookVerification {
- param(
- [string]$WebhookSecret = "test-secret"
- )
-
- Write-Host "Testing webhook verification..." -ForegroundColor Magenta
-
- # Create test data
- $timestamp = [DateTimeOffset]::UtcNow.ToUnixTimeSeconds().ToString()
- $body = '{"event":"signature.document.completed","data":{"document_id":"doc123"}}'
-
- # Generate signature for testing
- $signedString = "$timestamp.$body"
- $signature = "sha256=" + (Get-TurboDocxHMAC -Data $signedString -Secret $WebhookSecret)
-
- # Test verification
- $result = Test-TurboDocxWebhookSignature -Signature $signature -Timestamp $timestamp -Body $body -Secret $WebhookSecret
- Write-Host "Verification result: $result" -ForegroundColor $(if ($result) { "Green" } else { "Red" })
-
- if ($result) {
- # Test event processing
- $payload = $body | ConvertFrom-Json -AsHashtable
- Invoke-TurboDocxWebhookEvent -Payload $payload
- }
-
- return $result
-}
-
-function Start-WebhookListener {
- param(
- [int]$Port = 8080,
- [string]$WebhookSecret = $env:WEBHOOK_SECRET
- )
-
- if ([string]::IsNullOrEmpty($WebhookSecret)) {
- $WebhookSecret = "your-webhook-secret"
- Write-Warning "No WEBHOOK_SECRET environment variable found, using default"
- }
-
- Write-Host "Starting webhook listener on port $Port..." -ForegroundColor Green
- Write-Host "Webhook secret configured: $($WebhookSecret.Length) characters" -ForegroundColor Yellow
-
- # Note: This is a simple example. For production use, consider PowerShell Universal,
- # Azure Functions, or other proper web hosting solutions.
-
- Write-Host @"
-To test this webhook handler, you can use the following curl command:
-
-curl -X POST http://localhost:$Port/webhook \
- -H "Content-Type: application/json" \
- -H "X-TurboDocx-Signature: sha256=" \
- -H "X-TurboDocx-Timestamp: " \
- -d '{"event":"signature.document.completed","data":{"document_id":"test123"}}'
-
-Or run the test function:
-Test-WebhookVerification -WebhookSecret '$WebhookSecret'
-"@
-}
-
-# Export functions for module use
-Export-ModuleMember -Function Invoke-WebhookHandler, Invoke-TurboDocxWebhook, Test-WebhookVerification, Start-WebhookListener
-
-# Example usage when script is run directly
-if ($MyInvocation.InvocationName -ne '.') {
- # Run test if no parameters provided
- if ($args.Count -eq 0) {
- Test-WebhookVerification
- Start-WebhookListener
- }
- else {
- # Handle command line arguments
- switch ($args[0]) {
- "test" {
- $secret = if ($args[1]) { $args[1] } else { "test-secret" }
- Test-WebhookVerification -WebhookSecret $secret
- }
- "listen" {
- $port = if ($args[1]) { [int]$args[1] } else { 8080 }
- Start-WebhookListener -Port $port
- }
- default {
- Write-Host "Usage: powershell.ps1 [test|listen] [parameters]"
- Write-Host " test [secret] - Run verification test"
- Write-Host " listen [port] - Start webhook listener"
- }
- }
- }
-}
\ No newline at end of file
diff --git a/scripts/webhooks/verification/python.py b/scripts/webhooks/verification/python.py
deleted file mode 100644
index 576d85a..0000000
--- a/scripts/webhooks/verification/python.py
+++ /dev/null
@@ -1,128 +0,0 @@
-import hmac
-import hashlib
-import time
-import json
-import os
-from typing import Dict, Any, Optional
-from flask import Flask, request, abort
-
-def verify_webhook_signature(headers: Dict[str, str], body: str, secret: str) -> bool:
- """
- Verifies a TurboDocx webhook signature
-
- Args:
- headers: Request headers dictionary
- body: Raw request body as string
- secret: Your webhook secret
-
- Returns:
- bool: True if signature is valid
- """
- signature = headers.get('X-TurboDocx-Signature', '')
- timestamp = headers.get('X-TurboDocx-Timestamp', '')
-
- if not signature or not timestamp:
- return False
-
- # Check timestamp is within 5 minutes
- current_time = int(time.time())
- try:
- webhook_time = int(timestamp)
- except ValueError:
- return False
-
- if abs(current_time - webhook_time) > 300:
- return False
-
- # Generate expected signature
- signed_string = f"{timestamp}.{body}"
- expected_signature = 'sha256=' + hmac.new(
- secret.encode('utf-8'),
- signed_string.encode('utf-8'),
- hashlib.sha256
- ).hexdigest()
-
- # Use timing-safe comparison
- return hmac.compare_digest(signature, expected_signature)
-
-def create_webhook_decorator(secret: str):
- """
- Creates a Flask decorator for webhook verification
-
- Args:
- secret: Your webhook secret
-
- Returns:
- Decorator function
- """
- def webhook_required(f):
- def decorated_function(*args, **kwargs):
- headers = dict(request.headers)
- body = request.get_data(as_text=True)
-
- if not verify_webhook_signature(headers, body, secret):
- abort(401)
- return f(*args, **kwargs)
- decorated_function.__name__ = f.__name__
- return decorated_function
- return webhook_required
-
-def setup_webhook_handler(app: Flask, secret: str):
- """
- Sets up a complete Flask webhook handler
-
- Args:
- app: Flask application instance
- secret: Your webhook secret
- """
- @app.route('/webhook', methods=['POST'])
- def handle_webhook():
- headers = dict(request.headers)
- body = request.get_data(as_text=True)
-
- if not verify_webhook_signature(headers, body, secret):
- abort(401)
-
- # Process the webhook
- try:
- payload = request.get_json()
- print(f"Received event: {payload['event']}")
-
- # Process the event
- process_webhook_event(payload)
-
- # Always return 200 OK quickly
- return 'OK', 200
-
- except Exception as e:
- print(f"Error processing webhook: {e}")
- return 'Bad Request', 400
-
-def process_webhook_event(payload: Dict[str, Any]) -> None:
- """
- Process webhook event payload
-
- Args:
- payload: The webhook payload
- """
- event_type = payload.get('event')
-
- if event_type == 'signature.document.completed':
- print(f"Document completed: {payload['data']['document_id']}")
- # Add your completion logic here
-
- elif event_type == 'signature.document.voided':
- print(f"Document voided: {payload['data']['document_id']}")
- # Add your void logic here
-
- else:
- print(f"Unknown event type: {event_type}")
-
-# Example usage
-if __name__ == '__main__':
- app = Flask(__name__)
- webhook_secret = os.environ.get('WEBHOOK_SECRET', 'your-webhook-secret')
-
- setup_webhook_handler(app, webhook_secret)
-
- app.run(host='0.0.0.0', port=5000, debug=True)
\ No newline at end of file
diff --git a/scripts/webhooks/verification/python/fastapi.py b/scripts/webhooks/verification/python/fastapi.py
deleted file mode 100644
index 6f52c3a..0000000
--- a/scripts/webhooks/verification/python/fastapi.py
+++ /dev/null
@@ -1,138 +0,0 @@
-from fastapi import FastAPI, Request, HTTPException, Depends
-import hashlib
-import hmac
-import time
-import json
-import os
-from typing import Dict, Any
-
-app = FastAPI(title="TurboDocx Webhook Handler")
-
-def verify_webhook_signature(signature: str, timestamp: str, body: bytes, secret: str) -> bool:
- """
- Verifies a TurboDocx webhook signature
-
- Args:
- signature: X-TurboDocx-Signature header value
- timestamp: X-TurboDocx-Timestamp header value
- body: Raw request body bytes
- secret: Webhook secret
-
- Returns:
- bool: True if signature is valid
- """
- if not signature or not timestamp or not body or not secret:
- return False
-
- # Check timestamp is within 5 minutes
- current_time = int(time.time())
- webhook_time = int(timestamp)
-
- if abs(current_time - webhook_time) > 300:
- return False
-
- # Generate expected signature
- signed_string = f"{timestamp}.{body.decode('utf-8')}"
- expected_signature = "sha256=" + hmac.new(
- secret.encode('utf-8'),
- signed_string.encode('utf-8'),
- hashlib.sha256
- ).hexdigest()
-
- # Timing-safe comparison
- return hmac.compare_digest(signature, expected_signature)
-
-def process_webhook_event(payload: Dict[str, Any]) -> None:
- """Process webhook event payload"""
- event_type = payload.get('event', '')
- print(f"Received event: {event_type}")
-
- if event_type == 'signature.document.completed':
- handle_document_completed(payload.get('data', {}))
- elif event_type == 'signature.document.voided':
- handle_document_voided(payload.get('data', {}))
- else:
- print(f"Unknown event type: {event_type}")
-
-def handle_document_completed(data: Dict[str, Any]) -> None:
- """Handle document completion event"""
- document_id = data.get('document_id', 'unknown')
- print(f"Document completed: {document_id}")
- # Add your completion logic here
-
-def handle_document_voided(data: Dict[str, Any]) -> None:
- """Handle document voided event"""
- document_id = data.get('document_id', 'unknown')
- print(f"Document voided: {document_id}")
- # Add your void logic here
-
-# Dependency to get webhook secret
-def get_webhook_secret():
- return os.getenv('WEBHOOK_SECRET', 'your-webhook-secret')
-
-async def verify_signature(request: Request, secret: str = Depends(get_webhook_secret)):
- """FastAPI dependency for webhook signature verification"""
- signature = request.headers.get('x-turbodocx-signature', '')
- timestamp = request.headers.get('x-turbodocx-timestamp', '')
- body = await request.body()
-
- if not verify_webhook_signature(signature, timestamp, body, secret):
- raise HTTPException(status_code=401, detail="Unauthorized")
-
- return json.loads(body)
-
-@app.post("/webhook")
-async def webhook_handler(payload: Dict[str, Any] = Depends(verify_signature)):
- """
- Webhook endpoint with automatic signature verification
- """
- try:
- process_webhook_event(payload)
- return {"status": "ok"}
- except Exception as e:
- print(f"Error processing webhook: {e}")
- raise HTTPException(status_code=500, detail="Internal Server Error")
-
-@app.get("/health")
-async def health_check():
- """Health check endpoint"""
- return {
- "status": "ok",
- "timestamp": time.time()
- }
-
-# Alternative manual verification approach
-@app.post("/webhook/manual")
-async def webhook_handler_manual(request: Request):
- """
- Webhook endpoint with manual signature verification
- """
- secret = os.getenv('WEBHOOK_SECRET', 'your-webhook-secret')
- signature = request.headers.get('x-turbodocx-signature', '')
- timestamp = request.headers.get('x-turbodocx-timestamp', '')
- body = await request.body()
-
- # Verify signature
- if not verify_webhook_signature(signature, timestamp, body, secret):
- raise HTTPException(status_code=401, detail="Unauthorized")
-
- try:
- payload = json.loads(body)
- process_webhook_event(payload)
- return {"status": "ok"}
- except json.JSONDecodeError:
- raise HTTPException(status_code=400, detail="Invalid JSON payload")
- except Exception as e:
- print(f"Error processing webhook: {e}")
- raise HTTPException(status_code=500, detail="Internal Server Error")
-
-if __name__ == "__main__":
- import uvicorn
-
- port = int(os.getenv('PORT', 8000))
- secret = os.getenv('WEBHOOK_SECRET', 'your-webhook-secret')
-
- print(f"Starting webhook server on port {port}")
- print(f"Webhook secret configured: {len(secret)} characters")
-
- uvicorn.run(app, host="0.0.0.0", port=port)
\ No newline at end of file
diff --git a/scripts/webhooks/verification/python/flask.py b/scripts/webhooks/verification/python/flask.py
deleted file mode 100644
index 2937f84..0000000
--- a/scripts/webhooks/verification/python/flask.py
+++ /dev/null
@@ -1,181 +0,0 @@
-from flask import Flask, request, jsonify
-import hashlib
-import hmac
-import time
-import json
-import os
-
-app = Flask(__name__)
-
-def verify_webhook_signature(signature: str, timestamp: str, body: bytes, secret: str) -> bool:
- """
- Verifies a TurboDocx webhook signature
-
- Args:
- signature: X-TurboDocx-Signature header value
- timestamp: X-TurboDocx-Timestamp header value
- body: Raw request body bytes
- secret: Webhook secret
-
- Returns:
- bool: True if signature is valid
- """
- if not signature or not timestamp or not body or not secret:
- return False
-
- # Check timestamp is within 5 minutes
- current_time = int(time.time())
- try:
- webhook_time = int(timestamp)
- except (ValueError, TypeError):
- return False
-
- if abs(current_time - webhook_time) > 300:
- return False
-
- # Generate expected signature
- signed_string = f"{timestamp}.{body.decode('utf-8')}"
- expected_signature = "sha256=" + hmac.new(
- secret.encode('utf-8'),
- signed_string.encode('utf-8'),
- hashlib.sha256
- ).hexdigest()
-
- # Timing-safe comparison
- return hmac.compare_digest(signature, expected_signature)
-
-def process_webhook_event(payload: dict) -> None:
- """Process webhook event payload"""
- event_type = payload.get('event', '')
- app.logger.info(f"Received event: {event_type}")
-
- if event_type == 'signature.document.completed':
- handle_document_completed(payload.get('data', {}))
- elif event_type == 'signature.document.voided':
- handle_document_voided(payload.get('data', {}))
- else:
- app.logger.warning(f"Unknown event type: {event_type}")
-
-def handle_document_completed(data: dict) -> None:
- """Handle document completion event"""
- document_id = data.get('document_id', 'unknown')
- app.logger.info(f"Document completed: {document_id}")
- # Add your completion logic here
-
-def handle_document_voided(data: dict) -> None:
- """Handle document voided event"""
- document_id = data.get('document_id', 'unknown')
- app.logger.info(f"Document voided: {document_id}")
- # Add your void logic here
-
-@app.route('/webhook', methods=['POST'])
-def webhook_handler():
- """
- Main webhook endpoint
- """
- webhook_secret = os.getenv('WEBHOOK_SECRET', 'your-webhook-secret')
-
- # Get headers and body
- signature = request.headers.get('X-TurboDocx-Signature', '')
- timestamp = request.headers.get('X-TurboDocx-Timestamp', '')
- body = request.get_data()
-
- # Verify signature
- if not verify_webhook_signature(signature, timestamp, body, webhook_secret):
- app.logger.warning('Webhook signature verification failed')
- return 'Unauthorized', 401
-
- try:
- # Parse JSON payload
- payload = json.loads(body)
-
- # Process the event
- process_webhook_event(payload)
-
- return 'OK', 200
-
- except json.JSONDecodeError as e:
- app.logger.error(f'Invalid JSON payload: {e}')
- return 'Bad Request', 400
- except Exception as e:
- app.logger.error(f'Error processing webhook: {e}')
- return 'Internal Server Error', 500
-
-@app.route('/health', methods=['GET'])
-def health_check():
- """Health check endpoint"""
- return jsonify({
- 'status': 'ok',
- 'timestamp': time.time()
- })
-
-# Decorator approach for webhook verification
-def verify_webhook(f):
- """Decorator for webhook signature verification"""
- def decorated_function(*args, **kwargs):
- webhook_secret = os.getenv('WEBHOOK_SECRET', 'your-webhook-secret')
- signature = request.headers.get('X-TurboDocx-Signature', '')
- timestamp = request.headers.get('X-TurboDocx-Timestamp', '')
- body = request.get_data()
-
- if not verify_webhook_signature(signature, timestamp, body, webhook_secret):
- app.logger.warning('Webhook signature verification failed')
- return 'Unauthorized', 401
-
- return f(*args, **kwargs)
- return decorated_function
-
-@app.route('/webhook/decorated', methods=['POST'])
-@verify_webhook
-def webhook_handler_decorated():
- """
- Alternative webhook endpoint using decorator approach
- """
- try:
- payload = request.get_json()
- process_webhook_event(payload)
- return 'OK', 200
- except Exception as e:
- app.logger.error(f'Error processing webhook: {e}')
- return 'Internal Server Error', 500
-
-# Test function for development
-def test_webhook_verification():
- """Test webhook verification functionality"""
- secret = 'test-secret'
- timestamp = str(int(time.time()))
- body = json.dumps({
- 'event': 'signature.document.completed',
- 'data': {'document_id': 'test123'}
- }).encode('utf-8')
-
- # Generate test signature
- signed_string = f"{timestamp}.{body.decode('utf-8')}"
- signature = "sha256=" + hmac.new(
- secret.encode('utf-8'),
- signed_string.encode('utf-8'),
- hashlib.sha256
- ).hexdigest()
-
- # Test verification
- result = verify_webhook_signature(signature, timestamp, body, secret)
- print(f"Test verification result: {'PASS' if result else 'FAIL'}")
-
- if result:
- payload = json.loads(body)
- process_webhook_event(payload)
-
- return result
-
-if __name__ == '__main__':
- port = int(os.getenv('PORT', 5000))
- secret = os.getenv('WEBHOOK_SECRET', 'your-webhook-secret')
-
- print(f"Starting Flask webhook server on port {port}")
- print(f"Webhook secret configured: {len(secret)} characters")
-
- # Run test in development mode
- if os.getenv('FLASK_ENV') == 'development':
- test_webhook_verification()
-
- app.run(host='0.0.0.0', port=port, debug=os.getenv('FLASK_ENV') == 'development')
\ No newline at end of file
diff --git a/scripts/webhooks/verification/ruby.rb b/scripts/webhooks/verification/ruby.rb
deleted file mode 100644
index cce0c61..0000000
--- a/scripts/webhooks/verification/ruby.rb
+++ /dev/null
@@ -1,194 +0,0 @@
-require 'sinatra'
-require 'openssl'
-require 'json'
-require 'logger'
-
-# WebhookVerifier handles TurboDocx webhook verification
-class WebhookVerifier
- def initialize(secret)
- @secret = secret
- @logger = Logger.new(STDOUT)
- end
-
- # Verifies the webhook signature
- # @param signature [String] X-TurboDocx-Signature header value
- # @param timestamp [String] X-TurboDocx-Timestamp header value
- # @param body [String] Raw request body
- # @return [Boolean] true if signature is valid
- def verify_signature(signature, timestamp, body)
- return false if signature.nil? || signature.empty? || timestamp.nil? || timestamp.empty?
-
- # Check timestamp is within 5 minutes
- current_time = Time.now.to_i
- webhook_time = timestamp.to_i
-
- return false if (current_time - webhook_time).abs > 300
-
- # Generate expected signature
- signed_string = "#{timestamp}.#{body}"
- expected_signature = "sha256=#{compute_hmac(signed_string)}"
-
- # Timing-safe comparison
- secure_compare(signature, expected_signature)
- end
-
- # Process webhook event payload
- # @param payload [Hash] The webhook payload
- def process_event(payload)
- event_type = payload['event']
- @logger.info "Received event: #{event_type}"
-
- case event_type
- when 'signature.document.completed'
- handle_document_completed(payload['data'])
- when 'signature.document.voided'
- handle_document_voided(payload['data'])
- else
- @logger.warn "Unknown event type: #{event_type}"
- end
- end
-
- private
-
- # Computes HMAC-SHA256 for the given data
- # @param data [String] The data to sign
- # @return [String] Hex-encoded HMAC
- def compute_hmac(data)
- OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), @secret, data)
- end
-
- # Timing-safe string comparison
- # @param a [String] First string
- # @param b [String] Second string
- # @return [Boolean] true if strings are equal
- def secure_compare(a, b)
- return false if a.bytesize != b.bytesize
-
- result = 0
- a.bytes.zip(b.bytes) { |x, y| result |= x ^ y }
- result == 0
- end
-
- def handle_document_completed(data)
- document_id = data['document_id'] || 'unknown'
- @logger.info "Document completed: #{document_id}"
-
- # Add your completion logic here
- end
-
- def handle_document_voided(data)
- document_id = data['document_id'] || 'unknown'
- @logger.info "Document voided: #{document_id}"
-
- # Add your void logic here
- end
-end
-
-# Sinatra application setup
-class WebhookApp < Sinatra::Base
- def initialize
- super
- @webhook_secret = ENV['WEBHOOK_SECRET'] || 'your-webhook-secret'
- @verifier = WebhookVerifier.new(@webhook_secret)
- @logger = Logger.new(STDOUT)
- end
-
- # Webhook endpoint
- post '/webhook' do
- # Read raw body
- request.body.rewind
- body = request.body.read
-
- # Get headers
- signature = request.env['HTTP_X_TURBODOCX_SIGNATURE']
- timestamp = request.env['HTTP_X_TURBODOCX_TIMESTAMP']
-
- # Verify signature
- unless @verifier.verify_signature(signature, timestamp, body)
- @logger.warn 'Webhook signature verification failed'
- halt 401, 'Unauthorized'
- end
-
- # Parse JSON payload
- begin
- payload = JSON.parse(body)
- rescue JSON::ParserError => e
- @logger.error "Failed to parse JSON payload: #{e.message}"
- halt 400, 'Bad Request'
- end
-
- # Process the event
- @verifier.process_event(payload)
-
- # Return success
- content_type :text
- status 200
- 'OK'
- end
-
- # Health check endpoint
- get '/health' do
- content_type :json
- { status: 'ok', timestamp: Time.now.iso8601 }.to_json
- end
-end
-
-# Example standalone usage (without Sinatra)
-module WebhookProcessor
- class << self
- # Process a webhook request manually
- # @param headers [Hash] Request headers
- # @param body [String] Raw request body
- # @param secret [String] Webhook secret
- # @return [Boolean] true if processed successfully
- def process_request(headers, body, secret)
- verifier = WebhookVerifier.new(secret)
-
- signature = headers['X-TurboDocx-Signature'] || headers['HTTP_X_TURBODOCX_SIGNATURE']
- timestamp = headers['X-TurboDocx-Timestamp'] || headers['HTTP_X_TURBODOCX_TIMESTAMP']
-
- # Verify signature
- return false unless verifier.verify_signature(signature, timestamp, body)
-
- # Parse and process payload
- begin
- payload = JSON.parse(body)
- verifier.process_event(payload)
- true
- rescue JSON::ParserError => e
- puts "Failed to parse JSON payload: #{e.message}"
- false
- end
- end
-
- # Example method for testing
- def test_webhook_verification
- secret = 'test-secret'
- verifier = WebhookVerifier.new(secret)
-
- timestamp = Time.now.to_i.to_s
- body = '{"event":"signature.document.completed","data":{"document_id":"doc123"}}'
-
- # Generate signature for testing
- signed_string = "#{timestamp}.#{body}"
- signature = "sha256=#{OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), secret, signed_string)}"
-
- # Verify
- result = verifier.verify_signature(signature, timestamp, body)
- puts "Test verification result: #{result}"
-
- # Process event if verification passed
- if result
- payload = JSON.parse(body)
- verifier.process_event(payload)
- end
-
- result
- end
- end
-end
-
-# Run the app if this file is executed directly
-if __FILE__ == $0
- WebhookApp.run! host: '0.0.0.0', port: 4567
-end
\ No newline at end of file
diff --git a/static/800eabc35ea5450111e9509e56e568af49305a629982d640818b63cc837c0da1.txt b/static/800eabc35ea5450111e9509e56e568af49305a629982d640818b63cc837c0da1.txt
new file mode 100644
index 0000000..d659b23
--- /dev/null
+++ b/static/800eabc35ea5450111e9509e56e568af49305a629982d640818b63cc837c0da1.txt
@@ -0,0 +1 @@
+800eabc35ea5450111e9509e56e568af49305a629982d640818b63cc837c0da1
\ No newline at end of file
diff --git a/static/img/turbosign/api/api-illustration.png b/static/img/turbosign/api/api-illustration.png
new file mode 100644
index 0000000..6b6c216
Binary files /dev/null and b/static/img/turbosign/api/api-illustration.png differ
diff --git a/static/img/turbosign/api/api-key.png b/static/img/turbosign/api/api-key.png
new file mode 100644
index 0000000..c506b4d
Binary files /dev/null and b/static/img/turbosign/api/api-key.png differ
diff --git a/static/img/turbosign/api/org-id.png b/static/img/turbosign/api/org-id.png
new file mode 100644
index 0000000..2a3628d
Binary files /dev/null and b/static/img/turbosign/api/org-id.png differ
diff --git a/static/img/turbosign/api/steps.png b/static/img/turbosign/api/steps.png
new file mode 100644
index 0000000..cc3f16e
Binary files /dev/null and b/static/img/turbosign/api/steps.png differ
diff --git a/static/scripts/turbosign/api/complete-workflow/csharp/controller.cs b/static/scripts/turbosign/api/complete-workflow/csharp/controller.cs
new file mode 100644
index 0000000..6c927b9
--- /dev/null
+++ b/static/scripts/turbosign/api/complete-workflow/csharp/controller.cs
@@ -0,0 +1,138 @@
+using System;
+using System.IO;
+using System.Net.Http;
+using System.Text;
+using System.Threading.Tasks;
+using System.Text.Json;
+
+class Program
+{
+ static async Task Main(string[] args)
+ {
+ // Complete Workflow: Upload โ Recipients โ Prepare
+ using var client = new HttpClient();
+
+ // Step 1: Upload Document
+ using var uploadContent = new MultipartFormDataContent();
+ uploadContent.Add(new StringContent("Contract Agreement"), "name");
+
+ var fileContent = new ByteArrayContent(File.ReadAllBytes("./document.pdf"));
+ fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
+ uploadContent.Add(fileContent, "file", "document.pdf");
+
+ client.DefaultRequestHeaders.Add("Authorization", "Bearer YOUR_API_TOKEN");
+ client.DefaultRequestHeaders.Add("x-rapiddocx-org-id", "YOUR_ORGANIZATION_ID");
+ client.DefaultRequestHeaders.Add("origin", "https://www.turbodocx.com");
+ client.DefaultRequestHeaders.Add("referer", "https://www.turbodocx.com");
+ client.DefaultRequestHeaders.Add("accept", "application/json, text/plain, */*");
+
+ var uploadResponse = await client.PostAsync("https://www.turbodocx.com/turbosign/documents/upload", uploadContent);
+ var uploadResult = await uploadResponse.Content.ReadAsStringAsync();
+
+ var uploadDoc = JsonDocument.Parse(uploadResult);
+ var documentId = uploadDoc.RootElement.GetProperty("data").GetProperty("id").GetString();
+
+ // Step 2: Add Recipients
+ var recipientPayload = @"{
+ ""document"": {
+ ""name"": ""Contract Agreement - Updated"",
+ ""description"": ""This document requires electronic signatures from both parties. Please review all content carefully before signing.""
+ },
+ ""recipients"": [
+ {
+ ""name"": ""John Smith"",
+ ""email"": ""john.smith@company.com"",
+ ""signingOrder"": 1,
+ ""metadata"": {
+ ""color"": ""hsl(200, 75%, 50%)"",
+ ""lightColor"": ""hsl(200, 75%, 93%)""
+ },
+ ""documentId"": """ + documentId + @"""
+ },
+ {
+ ""name"": ""Jane Doe"",
+ ""email"": ""jane.doe@partner.com"",
+ ""signingOrder"": 2,
+ ""metadata"": {
+ ""color"": ""hsl(270, 75%, 50%)"",
+ ""lightColor"": ""hsl(270, 75%, 93%)""
+ },
+ ""documentId"": """ + documentId + @"""
+ }
+ ]
+ }";
+
+ var recipientContent = new StringContent(recipientPayload, Encoding.UTF8, "application/json");
+ var recipientResponse = await client.PostAsync($"https://www.turbodocx.com/turbosign/documents/{documentId}/update-with-recipients", recipientContent);
+ var recipientResult = await recipientResponse.Content.ReadAsStringAsync();
+
+ var recipientDoc = JsonDocument.Parse(recipientResult);
+ var recipients = recipientDoc.RootElement.GetProperty("data").GetProperty("recipients");
+
+ // Step 3: Prepare for Signing
+ var signaturePayload = $@"[
+ {{
+ ""recipientId"": ""{recipients[0].GetProperty("id").GetString()}"",
+ ""type"": ""signature"",
+ ""template"": {{
+ ""anchor"": ""{{Signature1}}"",
+ ""placement"": ""replace"",
+ ""size"": {{ ""width"": 200, ""height"": 80 }},
+ ""offset"": {{ ""x"": 0, ""y"": 0 }},
+ ""caseSensitive"": true,
+ ""useRegex"": false
+ }},
+ ""defaultValue"": """",
+ ""required"": true
+ }},
+ {{
+ ""recipientId"": ""{recipients[0].GetProperty("id").GetString()}"",
+ ""type"": ""date"",
+ ""template"": {{
+ ""anchor"": ""{{Date1}}"",
+ ""placement"": ""replace"",
+ ""size"": {{ ""width"": 150, ""height"": 30 }},
+ ""offset"": {{ ""x"": 0, ""y"": 0 }},
+ ""caseSensitive"": true,
+ ""useRegex"": false
+ }},
+ ""defaultValue"": """",
+ ""required"": true
+ }},
+ {{
+ ""recipientId"": ""{recipients[1].GetProperty("id").GetString()}"",
+ ""type"": ""signature"",
+ ""template"": {{
+ ""anchor"": ""{{Signature2}}"",
+ ""placement"": ""replace"",
+ ""size"": {{ ""width"": 200, ""height"": 80 }},
+ ""offset"": {{ ""x"": 0, ""y"": 0 }},
+ ""caseSensitive"": true,
+ ""useRegex"": false
+ }},
+ ""defaultValue"": """",
+ ""required"": true
+ }},
+ {{
+ ""recipientId"": ""{recipients[1].GetProperty("id").GetString()}"",
+ ""type"": ""text"",
+ ""template"": {{
+ ""anchor"": ""{{Title2}}"",
+ ""placement"": ""replace"",
+ ""size"": {{ ""width"": 200, ""height"": 30 }},
+ ""offset"": {{ ""x"": 0, ""y"": 0 }},
+ ""caseSensitive"": true,
+ ""useRegex"": false
+ }},
+ ""defaultValue"": ""Business Partner"",
+ ""required"": false
+ }}
+ ]";
+
+ var prepareContent = new StringContent(signaturePayload, Encoding.UTF8, "application/json");
+ var prepareResponse = await client.PostAsync($"https://www.turbodocx.com/turbosign/documents/{documentId}/prepare-for-signing", prepareContent);
+ var finalResult = await prepareResponse.Content.ReadAsStringAsync();
+
+ Console.WriteLine(finalResult);
+ }
+}
\ No newline at end of file
diff --git a/static/scripts/turbosign/api/complete-workflow/csharp/minimal.cs b/static/scripts/turbosign/api/complete-workflow/csharp/minimal.cs
new file mode 100644
index 0000000..6c927b9
--- /dev/null
+++ b/static/scripts/turbosign/api/complete-workflow/csharp/minimal.cs
@@ -0,0 +1,138 @@
+using System;
+using System.IO;
+using System.Net.Http;
+using System.Text;
+using System.Threading.Tasks;
+using System.Text.Json;
+
+class Program
+{
+ static async Task Main(string[] args)
+ {
+ // Complete Workflow: Upload โ Recipients โ Prepare
+ using var client = new HttpClient();
+
+ // Step 1: Upload Document
+ using var uploadContent = new MultipartFormDataContent();
+ uploadContent.Add(new StringContent("Contract Agreement"), "name");
+
+ var fileContent = new ByteArrayContent(File.ReadAllBytes("./document.pdf"));
+ fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
+ uploadContent.Add(fileContent, "file", "document.pdf");
+
+ client.DefaultRequestHeaders.Add("Authorization", "Bearer YOUR_API_TOKEN");
+ client.DefaultRequestHeaders.Add("x-rapiddocx-org-id", "YOUR_ORGANIZATION_ID");
+ client.DefaultRequestHeaders.Add("origin", "https://www.turbodocx.com");
+ client.DefaultRequestHeaders.Add("referer", "https://www.turbodocx.com");
+ client.DefaultRequestHeaders.Add("accept", "application/json, text/plain, */*");
+
+ var uploadResponse = await client.PostAsync("https://www.turbodocx.com/turbosign/documents/upload", uploadContent);
+ var uploadResult = await uploadResponse.Content.ReadAsStringAsync();
+
+ var uploadDoc = JsonDocument.Parse(uploadResult);
+ var documentId = uploadDoc.RootElement.GetProperty("data").GetProperty("id").GetString();
+
+ // Step 2: Add Recipients
+ var recipientPayload = @"{
+ ""document"": {
+ ""name"": ""Contract Agreement - Updated"",
+ ""description"": ""This document requires electronic signatures from both parties. Please review all content carefully before signing.""
+ },
+ ""recipients"": [
+ {
+ ""name"": ""John Smith"",
+ ""email"": ""john.smith@company.com"",
+ ""signingOrder"": 1,
+ ""metadata"": {
+ ""color"": ""hsl(200, 75%, 50%)"",
+ ""lightColor"": ""hsl(200, 75%, 93%)""
+ },
+ ""documentId"": """ + documentId + @"""
+ },
+ {
+ ""name"": ""Jane Doe"",
+ ""email"": ""jane.doe@partner.com"",
+ ""signingOrder"": 2,
+ ""metadata"": {
+ ""color"": ""hsl(270, 75%, 50%)"",
+ ""lightColor"": ""hsl(270, 75%, 93%)""
+ },
+ ""documentId"": """ + documentId + @"""
+ }
+ ]
+ }";
+
+ var recipientContent = new StringContent(recipientPayload, Encoding.UTF8, "application/json");
+ var recipientResponse = await client.PostAsync($"https://www.turbodocx.com/turbosign/documents/{documentId}/update-with-recipients", recipientContent);
+ var recipientResult = await recipientResponse.Content.ReadAsStringAsync();
+
+ var recipientDoc = JsonDocument.Parse(recipientResult);
+ var recipients = recipientDoc.RootElement.GetProperty("data").GetProperty("recipients");
+
+ // Step 3: Prepare for Signing
+ var signaturePayload = $@"[
+ {{
+ ""recipientId"": ""{recipients[0].GetProperty("id").GetString()}"",
+ ""type"": ""signature"",
+ ""template"": {{
+ ""anchor"": ""{{Signature1}}"",
+ ""placement"": ""replace"",
+ ""size"": {{ ""width"": 200, ""height"": 80 }},
+ ""offset"": {{ ""x"": 0, ""y"": 0 }},
+ ""caseSensitive"": true,
+ ""useRegex"": false
+ }},
+ ""defaultValue"": """",
+ ""required"": true
+ }},
+ {{
+ ""recipientId"": ""{recipients[0].GetProperty("id").GetString()}"",
+ ""type"": ""date"",
+ ""template"": {{
+ ""anchor"": ""{{Date1}}"",
+ ""placement"": ""replace"",
+ ""size"": {{ ""width"": 150, ""height"": 30 }},
+ ""offset"": {{ ""x"": 0, ""y"": 0 }},
+ ""caseSensitive"": true,
+ ""useRegex"": false
+ }},
+ ""defaultValue"": """",
+ ""required"": true
+ }},
+ {{
+ ""recipientId"": ""{recipients[1].GetProperty("id").GetString()}"",
+ ""type"": ""signature"",
+ ""template"": {{
+ ""anchor"": ""{{Signature2}}"",
+ ""placement"": ""replace"",
+ ""size"": {{ ""width"": 200, ""height"": 80 }},
+ ""offset"": {{ ""x"": 0, ""y"": 0 }},
+ ""caseSensitive"": true,
+ ""useRegex"": false
+ }},
+ ""defaultValue"": """",
+ ""required"": true
+ }},
+ {{
+ ""recipientId"": ""{recipients[1].GetProperty("id").GetString()}"",
+ ""type"": ""text"",
+ ""template"": {{
+ ""anchor"": ""{{Title2}}"",
+ ""placement"": ""replace"",
+ ""size"": {{ ""width"": 200, ""height"": 30 }},
+ ""offset"": {{ ""x"": 0, ""y"": 0 }},
+ ""caseSensitive"": true,
+ ""useRegex"": false
+ }},
+ ""defaultValue"": ""Business Partner"",
+ ""required"": false
+ }}
+ ]";
+
+ var prepareContent = new StringContent(signaturePayload, Encoding.UTF8, "application/json");
+ var prepareResponse = await client.PostAsync($"https://www.turbodocx.com/turbosign/documents/{documentId}/prepare-for-signing", prepareContent);
+ var finalResult = await prepareResponse.Content.ReadAsStringAsync();
+
+ Console.WriteLine(finalResult);
+ }
+}
\ No newline at end of file
diff --git a/static/scripts/turbosign/api/complete-workflow/go.go b/static/scripts/turbosign/api/complete-workflow/go.go
new file mode 100644
index 0000000..3496fe6
--- /dev/null
+++ b/static/scripts/turbosign/api/complete-workflow/go.go
@@ -0,0 +1,178 @@
+package main
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "io"
+ "mime/multipart"
+ "net/http"
+ "os"
+)
+
+type UploadResponse struct {
+ Data struct {
+ ID string `json:"id"`
+ } `json:"data"`
+}
+
+type RecipientResponse struct {
+ Data struct {
+ Recipients []struct {
+ ID string `json:"id"`
+ } `json:"recipients"`
+ } `json:"data"`
+}
+
+func main() {
+ // Complete Workflow: Upload โ Recipients โ Prepare
+
+ // Step 1: Upload Document
+ var buf bytes.Buffer
+ writer := multipart.NewWriter(&buf)
+
+ writer.WriteField("name", "Contract Agreement")
+
+ file, _ := os.Open("./document.pdf")
+ defer file.Close()
+ part, _ := writer.CreateFormFile("file", "document.pdf")
+ io.Copy(part, file)
+ writer.Close()
+
+ req, _ := http.NewRequest("POST", "https://www.turbodocx.com/turbosign/documents/upload", &buf)
+ req.Header.Set("Authorization", "Bearer YOUR_API_TOKEN")
+ req.Header.Set("x-rapiddocx-org-id", "YOUR_ORGANIZATION_ID")
+ req.Header.Set("origin", "https://www.turbodocx.com")
+ req.Header.Set("referer", "https://www.turbodocx.com")
+ req.Header.Set("accept", "application/json, text/plain, */*")
+ req.Header.Set("Content-Type", writer.FormDataContentType())
+
+ client := &http.Client{}
+ resp, _ := client.Do(req)
+ defer resp.Body.Close()
+
+ uploadBody, _ := io.ReadAll(resp.Body)
+ var uploadResult UploadResponse
+ json.Unmarshal(uploadBody, &uploadResult)
+ documentID := uploadResult.Data.ID
+
+ // Step 2: Add Recipients
+ recipientPayload := fmt.Sprintf(`{
+ "document": {
+ "name": "Contract Agreement - Updated",
+ "description": "This document requires electronic signatures from both parties. Please review all content carefully before signing."
+ },
+ "recipients": [
+ {
+ "name": "John Smith",
+ "email": "john.smith@company.com",
+ "signingOrder": 1,
+ "metadata": {
+ "color": "hsl(200, 75%%, 50%%)",
+ "lightColor": "hsl(200, 75%%, 93%%)"
+ },
+ "documentId": "%s"
+ },
+ {
+ "name": "Jane Doe",
+ "email": "jane.doe@partner.com",
+ "signingOrder": 2,
+ "metadata": {
+ "color": "hsl(270, 75%%, 50%%)",
+ "lightColor": "hsl(270, 75%%, 93%%)"
+ },
+ "documentId": "%s"
+ }
+ ]
+ }`, documentID, documentID)
+
+ req2, _ := http.NewRequest("POST", fmt.Sprintf("https://www.turbodocx.com/turbosign/documents/%s/update-with-recipients", documentID), bytes.NewBufferString(recipientPayload))
+ req2.Header.Set("Content-Type", "application/json")
+ req2.Header.Set("Authorization", "Bearer YOUR_API_TOKEN")
+ req2.Header.Set("x-rapiddocx-org-id", "YOUR_ORGANIZATION_ID")
+ req2.Header.Set("origin", "https://www.turbodocx.com")
+ req2.Header.Set("referer", "https://www.turbodocx.com")
+ req2.Header.Set("accept", "application/json, text/plain, */*")
+
+ resp2, _ := client.Do(req2)
+ defer resp2.Body.Close()
+
+ recipientBody, _ := io.ReadAll(resp2.Body)
+ var recipientResult RecipientResponse
+ json.Unmarshal(recipientBody, &recipientResult)
+ recipients := recipientResult.Data.Recipients
+
+ // Step 3: Prepare for Signing
+ signaturePayload := fmt.Sprintf(`[
+ {
+ "recipientId": "%s",
+ "type": "signature",
+ "template": {
+ "anchor": "{Signature1}",
+ "placement": "replace",
+ "size": { "width": 200, "height": 80 },
+ "offset": { "x": 0, "y": 0 },
+ "caseSensitive": true,
+ "useRegex": false
+ },
+ "defaultValue": "",
+ "required": true
+ },
+ {
+ "recipientId": "%s",
+ "type": "date",
+ "template": {
+ "anchor": "{Date1}",
+ "placement": "replace",
+ "size": { "width": 150, "height": 30 },
+ "offset": { "x": 0, "y": 0 },
+ "caseSensitive": true,
+ "useRegex": false
+ },
+ "defaultValue": "",
+ "required": true
+ },
+ {
+ "recipientId": "%s",
+ "type": "signature",
+ "template": {
+ "anchor": "{Signature2}",
+ "placement": "replace",
+ "size": { "width": 200, "height": 80 },
+ "offset": { "x": 0, "y": 0 },
+ "caseSensitive": true,
+ "useRegex": false
+ },
+ "defaultValue": "",
+ "required": true
+ },
+ {
+ "recipientId": "%s",
+ "type": "text",
+ "template": {
+ "anchor": "{Title2}",
+ "placement": "replace",
+ "size": { "width": 200, "height": 30 },
+ "offset": { "x": 0, "y": 0 },
+ "caseSensitive": true,
+ "useRegex": false
+ },
+ "defaultValue": "Business Partner",
+ "required": false
+ }
+ ]`, recipients[0].ID, recipients[0].ID, recipients[1].ID, recipients[1].ID)
+
+ req3, _ := http.NewRequest("POST", fmt.Sprintf("https://www.turbodocx.com/turbosign/documents/%s/prepare-for-signing", documentID), bytes.NewBufferString(signaturePayload))
+ req3.Header.Set("Content-Type", "application/json")
+ req3.Header.Set("Authorization", "Bearer YOUR_API_TOKEN")
+ req3.Header.Set("x-rapiddocx-org-id", "YOUR_ORGANIZATION_ID")
+ req3.Header.Set("origin", "https://www.turbodocx.com")
+ req3.Header.Set("referer", "https://www.turbodocx.com")
+ req3.Header.Set("accept", "application/json, text/plain, */*")
+
+ resp3, _ := client.Do(req3)
+ defer resp3.Body.Close()
+
+ finalBody, _ := io.ReadAll(resp3.Body)
+ fmt.Println(string(finalBody))
+}
\ No newline at end of file
diff --git a/static/scripts/turbosign/api/complete-workflow/java.java b/static/scripts/turbosign/api/complete-workflow/java.java
new file mode 100644
index 0000000..728e161
--- /dev/null
+++ b/static/scripts/turbosign/api/complete-workflow/java.java
@@ -0,0 +1,174 @@
+import java.io.*;
+import java.net.http.*;
+import java.net.URI;
+import java.nio.file.*;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.JsonNode;
+
+public class TurboSignWorkflow {
+ public static void main(String[] args) throws Exception {
+ // Complete Workflow: Upload โ Recipients โ Prepare
+ HttpClient client = HttpClient.newHttpClient();
+ ObjectMapper mapper = new ObjectMapper();
+
+ // Step 1: Upload Document
+ String boundary = "----JavaBoundary" + System.currentTimeMillis();
+ String fileName = "./document.pdf";
+ byte[] fileBytes = Files.readAllBytes(Paths.get(fileName));
+
+ StringBuilder formData = new StringBuilder();
+ formData.append("--").append(boundary).append("\r\n");
+ formData.append("Content-Disposition: form-data; name=\"name\"\r\n\r\n");
+ formData.append("Contract Agreement\r\n");
+ formData.append("--").append(boundary).append("\r\n");
+ formData.append("Content-Disposition: form-data; name=\"file\"; filename=\"document.pdf\"\r\n");
+ formData.append("Content-Type: application/pdf\r\n\r\n");
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ baos.write(formData.toString().getBytes());
+ baos.write(fileBytes);
+ baos.write(("\r\n--" + boundary + "--\r\n").getBytes());
+
+ HttpRequest uploadRequest = HttpRequest.newBuilder()
+ .uri(URI.create("https://www.turbodocx.com/turbosign/documents/upload"))
+ .header("Authorization", "Bearer YOUR_API_TOKEN")
+ .header("x-rapiddocx-org-id", "YOUR_ORGANIZATION_ID")
+ .header("origin", "https://www.turbodocx.com")
+ .header("referer", "https://www.turbodocx.com")
+ .header("accept", "application/json, text/plain, */*")
+ .header("Content-Type", "multipart/form-data; boundary=" + boundary)
+ .POST(HttpRequest.BodyPublishers.ofByteArray(baos.toByteArray()))
+ .build();
+
+ HttpResponse uploadResponse = client.send(uploadRequest, HttpResponse.BodyHandlers.ofString());
+ JsonNode uploadResult = mapper.readTree(uploadResponse.body());
+ String documentId = uploadResult.get("data").get("id").asText();
+
+ // Step 2: Add Recipients
+ String recipientPayload = String.format("""
+ {
+ "document": {
+ "name": "Contract Agreement - Updated",
+ "description": "This document requires electronic signatures from both parties. Please review all content carefully before signing."
+ },
+ "recipients": [
+ {
+ "name": "John Smith",
+ "email": "john.smith@company.com",
+ "signingOrder": 1,
+ "metadata": {
+ "color": "hsl(200, 75%%, 50%%)",
+ "lightColor": "hsl(200, 75%%, 93%%)"
+ },
+ "documentId": "%s"
+ },
+ {
+ "name": "Jane Doe",
+ "email": "jane.doe@partner.com",
+ "signingOrder": 2,
+ "metadata": {
+ "color": "hsl(270, 75%%, 50%%)",
+ "lightColor": "hsl(270, 75%%, 93%%)"
+ },
+ "documentId": "%s"
+ }
+ ]
+ }
+ """, documentId, documentId);
+
+ HttpRequest recipientRequest = HttpRequest.newBuilder()
+ .uri(URI.create("https://www.turbodocx.com/turbosign/documents/" + documentId + "/update-with-recipients"))
+ .header("Content-Type", "application/json")
+ .header("Authorization", "Bearer YOUR_API_TOKEN")
+ .header("x-rapiddocx-org-id", "YOUR_ORGANIZATION_ID")
+ .header("origin", "https://www.turbodocx.com")
+ .header("referer", "https://www.turbodocx.com")
+ .header("accept", "application/json, text/plain, */*")
+ .POST(HttpRequest.BodyPublishers.ofString(recipientPayload))
+ .build();
+
+ HttpResponse recipientResponse = client.send(recipientRequest, HttpResponse.BodyHandlers.ofString());
+ JsonNode recipientResult = mapper.readTree(recipientResponse.body());
+ JsonNode recipients = recipientResult.get("data").get("recipients");
+
+ // Step 3: Prepare for Signing
+ String signaturePayload = String.format("""
+ [
+ {
+ "recipientId": "%s",
+ "type": "signature",
+ "template": {
+ "anchor": "{Signature1}",
+ "placement": "replace",
+ "size": { "width": 200, "height": 80 },
+ "offset": { "x": 0, "y": 0 },
+ "caseSensitive": true,
+ "useRegex": false
+ },
+ "defaultValue": "",
+ "required": true
+ },
+ {
+ "recipientId": "%s",
+ "type": "date",
+ "template": {
+ "anchor": "{Date1}",
+ "placement": "replace",
+ "size": { "width": 150, "height": 30 },
+ "offset": { "x": 0, "y": 0 },
+ "caseSensitive": true,
+ "useRegex": false
+ },
+ "defaultValue": "",
+ "required": true
+ },
+ {
+ "recipientId": "%s",
+ "type": "signature",
+ "template": {
+ "anchor": "{Signature2}",
+ "placement": "replace",
+ "size": { "width": 200, "height": 80 },
+ "offset": { "x": 0, "y": 0 },
+ "caseSensitive": true,
+ "useRegex": false
+ },
+ "defaultValue": "",
+ "required": true
+ },
+ {
+ "recipientId": "%s",
+ "type": "text",
+ "template": {
+ "anchor": "{Title2}",
+ "placement": "replace",
+ "size": { "width": 200, "height": 30 },
+ "offset": { "x": 0, "y": 0 },
+ "caseSensitive": true,
+ "useRegex": false
+ },
+ "defaultValue": "Business Partner",
+ "required": false
+ }
+ ]
+ """,
+ recipients.get(0).get("id").asText(),
+ recipients.get(0).get("id").asText(),
+ recipients.get(1).get("id").asText(),
+ recipients.get(1).get("id").asText());
+
+ HttpRequest prepareRequest = HttpRequest.newBuilder()
+ .uri(URI.create("https://www.turbodocx.com/turbosign/documents/" + documentId + "/prepare-for-signing"))
+ .header("Content-Type", "application/json")
+ .header("Authorization", "Bearer YOUR_API_TOKEN")
+ .header("x-rapiddocx-org-id", "YOUR_ORGANIZATION_ID")
+ .header("origin", "https://www.turbodocx.com")
+ .header("referer", "https://www.turbodocx.com")
+ .header("accept", "application/json, text/plain, */*")
+ .POST(HttpRequest.BodyPublishers.ofString(signaturePayload))
+ .build();
+
+ HttpResponse prepareResponse = client.send(prepareRequest, HttpResponse.BodyHandlers.ofString());
+ System.out.println(prepareResponse.body());
+ }
+}
\ No newline at end of file
diff --git a/static/scripts/turbosign/api/complete-workflow/nodejs/express.js b/static/scripts/turbosign/api/complete-workflow/nodejs/express.js
new file mode 100644
index 0000000..aaf8985
--- /dev/null
+++ b/static/scripts/turbosign/api/complete-workflow/nodejs/express.js
@@ -0,0 +1,148 @@
+const FormData = require('form-data');
+const fs = require('fs');
+const fetch = require('node-fetch');
+
+// Complete Workflow: Upload โ Recipients โ Prepare
+
+// Step 1: Upload Document
+const formData = new FormData();
+formData.append('name', 'Contract Agreement');
+formData.append('file', fs.createReadStream('./document.pdf'));
+
+const uploadResponse = await fetch('https://www.turbodocx.com/turbosign/documents/upload', {
+ method: 'POST',
+ headers: {
+ 'Authorization': 'Bearer YOUR_API_TOKEN',
+ 'x-rapiddocx-org-id': 'YOUR_ORGANIZATION_ID',
+ 'origin': 'https://www.turbodocx.com',
+ 'referer': 'https://www.turbodocx.com',
+ 'accept': 'application/json, text/plain, */*',
+ ...formData.getHeaders()
+ },
+ body: formData
+});
+
+const uploadResult = await uploadResponse.json();
+const documentId = uploadResult.data.id;
+
+// Step 2: Add Recipients
+const recipientPayload = {
+ "document": {
+ "name": "Contract Agreement - Updated",
+ "description": "This document requires electronic signatures from both parties. Please review all content carefully before signing."
+ },
+ "recipients": [
+ {
+ "name": "John Smith",
+ "email": "john.smith@company.com",
+ "signingOrder": 1,
+ "metadata": {
+ "color": "hsl(200, 75%, 50%)",
+ "lightColor": "hsl(200, 75%, 93%)"
+ },
+ "documentId": documentId
+ },
+ {
+ "name": "Jane Doe",
+ "email": "jane.doe@partner.com",
+ "signingOrder": 2,
+ "metadata": {
+ "color": "hsl(270, 75%, 50%)",
+ "lightColor": "hsl(270, 75%, 93%)"
+ },
+ "documentId": documentId
+ }
+ ]
+};
+
+const recipientResponse = await fetch(`https://www.turbodocx.com/turbosign/documents/${documentId}/update-with-recipients`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': 'Bearer YOUR_API_TOKEN',
+ 'x-rapiddocx-org-id': 'YOUR_ORGANIZATION_ID',
+ 'origin': 'https://www.turbodocx.com',
+ 'referer': 'https://www.turbodocx.com',
+ 'accept': 'application/json, text/plain, */*'
+ },
+ body: JSON.stringify(recipientPayload)
+});
+
+const recipientResult = await recipientResponse.json();
+const recipients = recipientResult.data.recipients;
+
+// Step 3: Prepare for Signing
+const signatureFields = [
+ {
+ "recipientId": recipients[0].id,
+ "type": "signature",
+ "template": {
+ "anchor": "{Signature1}",
+ "placement": "replace",
+ "size": { "width": 200, "height": 80 },
+ "offset": { "x": 0, "y": 0 },
+ "caseSensitive": true,
+ "useRegex": false
+ },
+ "defaultValue": "",
+ "required": true
+ },
+ {
+ "recipientId": recipients[0].id,
+ "type": "date",
+ "template": {
+ "anchor": "{Date1}",
+ "placement": "replace",
+ "size": { "width": 150, "height": 30 },
+ "offset": { "x": 0, "y": 0 },
+ "caseSensitive": true,
+ "useRegex": false
+ },
+ "defaultValue": "",
+ "required": true
+ },
+ {
+ "recipientId": recipients[1].id,
+ "type": "signature",
+ "template": {
+ "anchor": "{Signature2}",
+ "placement": "replace",
+ "size": { "width": 200, "height": 80 },
+ "offset": { "x": 0, "y": 0 },
+ "caseSensitive": true,
+ "useRegex": false
+ },
+ "defaultValue": "",
+ "required": true
+ },
+ {
+ "recipientId": recipients[1].id,
+ "type": "text",
+ "template": {
+ "anchor": "{Title2}",
+ "placement": "replace",
+ "size": { "width": 200, "height": 30 },
+ "offset": { "x": 0, "y": 0 },
+ "caseSensitive": true,
+ "useRegex": false
+ },
+ "defaultValue": "Business Partner",
+ "required": false
+ }
+];
+
+const prepareResponse = await fetch(`https://www.turbodocx.com/turbosign/documents/${documentId}/prepare-for-signing`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': 'Bearer YOUR_API_TOKEN',
+ 'x-rapiddocx-org-id': 'YOUR_ORGANIZATION_ID',
+ 'origin': 'https://www.turbodocx.com',
+ 'referer': 'https://www.turbodocx.com',
+ 'accept': 'application/json, text/plain, */*'
+ },
+ body: JSON.stringify(signatureFields)
+});
+
+const finalResult = await prepareResponse.json();
+console.log(finalResult);
\ No newline at end of file
diff --git a/static/scripts/turbosign/api/complete-workflow/nodejs/fastify.js b/static/scripts/turbosign/api/complete-workflow/nodejs/fastify.js
new file mode 100644
index 0000000..aaf8985
--- /dev/null
+++ b/static/scripts/turbosign/api/complete-workflow/nodejs/fastify.js
@@ -0,0 +1,148 @@
+const FormData = require('form-data');
+const fs = require('fs');
+const fetch = require('node-fetch');
+
+// Complete Workflow: Upload โ Recipients โ Prepare
+
+// Step 1: Upload Document
+const formData = new FormData();
+formData.append('name', 'Contract Agreement');
+formData.append('file', fs.createReadStream('./document.pdf'));
+
+const uploadResponse = await fetch('https://www.turbodocx.com/turbosign/documents/upload', {
+ method: 'POST',
+ headers: {
+ 'Authorization': 'Bearer YOUR_API_TOKEN',
+ 'x-rapiddocx-org-id': 'YOUR_ORGANIZATION_ID',
+ 'origin': 'https://www.turbodocx.com',
+ 'referer': 'https://www.turbodocx.com',
+ 'accept': 'application/json, text/plain, */*',
+ ...formData.getHeaders()
+ },
+ body: formData
+});
+
+const uploadResult = await uploadResponse.json();
+const documentId = uploadResult.data.id;
+
+// Step 2: Add Recipients
+const recipientPayload = {
+ "document": {
+ "name": "Contract Agreement - Updated",
+ "description": "This document requires electronic signatures from both parties. Please review all content carefully before signing."
+ },
+ "recipients": [
+ {
+ "name": "John Smith",
+ "email": "john.smith@company.com",
+ "signingOrder": 1,
+ "metadata": {
+ "color": "hsl(200, 75%, 50%)",
+ "lightColor": "hsl(200, 75%, 93%)"
+ },
+ "documentId": documentId
+ },
+ {
+ "name": "Jane Doe",
+ "email": "jane.doe@partner.com",
+ "signingOrder": 2,
+ "metadata": {
+ "color": "hsl(270, 75%, 50%)",
+ "lightColor": "hsl(270, 75%, 93%)"
+ },
+ "documentId": documentId
+ }
+ ]
+};
+
+const recipientResponse = await fetch(`https://www.turbodocx.com/turbosign/documents/${documentId}/update-with-recipients`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': 'Bearer YOUR_API_TOKEN',
+ 'x-rapiddocx-org-id': 'YOUR_ORGANIZATION_ID',
+ 'origin': 'https://www.turbodocx.com',
+ 'referer': 'https://www.turbodocx.com',
+ 'accept': 'application/json, text/plain, */*'
+ },
+ body: JSON.stringify(recipientPayload)
+});
+
+const recipientResult = await recipientResponse.json();
+const recipients = recipientResult.data.recipients;
+
+// Step 3: Prepare for Signing
+const signatureFields = [
+ {
+ "recipientId": recipients[0].id,
+ "type": "signature",
+ "template": {
+ "anchor": "{Signature1}",
+ "placement": "replace",
+ "size": { "width": 200, "height": 80 },
+ "offset": { "x": 0, "y": 0 },
+ "caseSensitive": true,
+ "useRegex": false
+ },
+ "defaultValue": "",
+ "required": true
+ },
+ {
+ "recipientId": recipients[0].id,
+ "type": "date",
+ "template": {
+ "anchor": "{Date1}",
+ "placement": "replace",
+ "size": { "width": 150, "height": 30 },
+ "offset": { "x": 0, "y": 0 },
+ "caseSensitive": true,
+ "useRegex": false
+ },
+ "defaultValue": "",
+ "required": true
+ },
+ {
+ "recipientId": recipients[1].id,
+ "type": "signature",
+ "template": {
+ "anchor": "{Signature2}",
+ "placement": "replace",
+ "size": { "width": 200, "height": 80 },
+ "offset": { "x": 0, "y": 0 },
+ "caseSensitive": true,
+ "useRegex": false
+ },
+ "defaultValue": "",
+ "required": true
+ },
+ {
+ "recipientId": recipients[1].id,
+ "type": "text",
+ "template": {
+ "anchor": "{Title2}",
+ "placement": "replace",
+ "size": { "width": 200, "height": 30 },
+ "offset": { "x": 0, "y": 0 },
+ "caseSensitive": true,
+ "useRegex": false
+ },
+ "defaultValue": "Business Partner",
+ "required": false
+ }
+];
+
+const prepareResponse = await fetch(`https://www.turbodocx.com/turbosign/documents/${documentId}/prepare-for-signing`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': 'Bearer YOUR_API_TOKEN',
+ 'x-rapiddocx-org-id': 'YOUR_ORGANIZATION_ID',
+ 'origin': 'https://www.turbodocx.com',
+ 'referer': 'https://www.turbodocx.com',
+ 'accept': 'application/json, text/plain, */*'
+ },
+ body: JSON.stringify(signatureFields)
+});
+
+const finalResult = await prepareResponse.json();
+console.log(finalResult);
\ No newline at end of file
diff --git a/static/scripts/turbosign/api/complete-workflow/php.php b/static/scripts/turbosign/api/complete-workflow/php.php
new file mode 100644
index 0000000..5817da1
--- /dev/null
+++ b/static/scripts/turbosign/api/complete-workflow/php.php
@@ -0,0 +1,173 @@
+ 'Contract Agreement',
+ 'file' => new CURLFile($file_path, 'application/pdf', 'document.pdf')
+];
+
+$upload_headers = [
+ 'Authorization: Bearer YOUR_API_TOKEN',
+ 'x-rapiddocx-org-id: YOUR_ORGANIZATION_ID',
+ 'origin: https://www.turbodocx.com',
+ 'referer: https://www.turbodocx.com',
+ 'accept: application/json, text/plain, */*'
+];
+
+$ch = curl_init();
+curl_setopt_array($ch, [
+ CURLOPT_URL => $upload_url,
+ CURLOPT_POST => true,
+ CURLOPT_POSTFIELDS => $upload_data,
+ CURLOPT_HTTPHEADER => $upload_headers,
+ CURLOPT_RETURNTRANSFER => true
+]);
+
+$upload_response = curl_exec($ch);
+curl_close($ch);
+
+$upload_result = json_decode($upload_response, true);
+$document_id = $upload_result['data']['id'];
+
+// Step 2: Add Recipients
+$recipient_payload = [
+ 'document' => [
+ 'name' => 'Contract Agreement - Updated',
+ 'description' => 'This document requires electronic signatures from both parties. Please review all content carefully before signing.'
+ ],
+ 'recipients' => [
+ [
+ 'name' => 'John Smith',
+ 'email' => 'john.smith@company.com',
+ 'signingOrder' => 1,
+ 'metadata' => [
+ 'color' => 'hsl(200, 75%, 50%)',
+ 'lightColor' => 'hsl(200, 75%, 93%)'
+ ],
+ 'documentId' => $document_id
+ ],
+ [
+ 'name' => 'Jane Doe',
+ 'email' => 'jane.doe@partner.com',
+ 'signingOrder' => 2,
+ 'metadata' => [
+ 'color' => 'hsl(270, 75%, 50%)',
+ 'lightColor' => 'hsl(270, 75%, 93%)'
+ ],
+ 'documentId' => $document_id
+ ]
+ ]
+];
+
+$recipient_headers = [
+ 'Content-Type: application/json',
+ 'Authorization: Bearer YOUR_API_TOKEN',
+ 'x-rapiddocx-org-id: YOUR_ORGANIZATION_ID',
+ 'origin: https://www.turbodocx.com',
+ 'referer: https://www.turbodocx.com',
+ 'accept: application/json, text/plain, */*'
+];
+
+$ch = curl_init();
+curl_setopt_array($ch, [
+ CURLOPT_URL => "https://www.turbodocx.com/turbosign/documents/$document_id/update-with-recipients",
+ CURLOPT_POST => true,
+ CURLOPT_POSTFIELDS => json_encode($recipient_payload),
+ CURLOPT_HTTPHEADER => $recipient_headers,
+ CURLOPT_RETURNTRANSFER => true
+]);
+
+$recipient_response = curl_exec($ch);
+curl_close($ch);
+
+$recipient_result = json_decode($recipient_response, true);
+$recipients = $recipient_result['data']['recipients'];
+
+// Step 3: Prepare for Signing
+$signature_fields = [
+ [
+ 'recipientId' => $recipients[0]['id'],
+ 'type' => 'signature',
+ 'template' => [
+ 'anchor' => '{Signature1}',
+ 'placement' => 'replace',
+ 'size' => ['width' => 200, 'height' => 80],
+ 'offset' => ['x' => 0, 'y' => 0],
+ 'caseSensitive' => true,
+ 'useRegex' => false
+ ],
+ 'defaultValue' => '',
+ 'required' => true
+ ],
+ [
+ 'recipientId' => $recipients[0]['id'],
+ 'type' => 'date',
+ 'template' => [
+ 'anchor' => '{Date1}',
+ 'placement' => 'replace',
+ 'size' => ['width' => 150, 'height' => 30],
+ 'offset' => ['x' => 0, 'y' => 0],
+ 'caseSensitive' => true,
+ 'useRegex' => false
+ ],
+ 'defaultValue' => '',
+ 'required' => true
+ ],
+ [
+ 'recipientId' => $recipients[1]['id'],
+ 'type' => 'signature',
+ 'template' => [
+ 'anchor' => '{Signature2}',
+ 'placement' => 'replace',
+ 'size' => ['width' => 200, 'height' => 80],
+ 'offset' => ['x' => 0, 'y' => 0],
+ 'caseSensitive' => true,
+ 'useRegex' => false
+ ],
+ 'defaultValue' => '',
+ 'required' => true
+ ],
+ [
+ 'recipientId' => $recipients[1]['id'],
+ 'type' => 'text',
+ 'template' => [
+ 'anchor' => '{Title2}',
+ 'placement' => 'replace',
+ 'size' => ['width' => 200, 'height' => 30],
+ 'offset' => ['x' => 0, 'y' => 0],
+ 'caseSensitive' => true,
+ 'useRegex' => false
+ ],
+ 'defaultValue' => 'Business Partner',
+ 'required' => false
+ ]
+];
+
+$prepare_headers = [
+ 'Content-Type: application/json',
+ 'Authorization: Bearer YOUR_API_TOKEN',
+ 'x-rapiddocx-org-id: YOUR_ORGANIZATION_ID',
+ 'origin: https://www.turbodocx.com',
+ 'referer: https://www.turbodocx.com',
+ 'accept: application/json, text/plain, */*'
+];
+
+$ch = curl_init();
+curl_setopt_array($ch, [
+ CURLOPT_URL => "https://www.turbodocx.com/turbosign/documents/$document_id/prepare-for-signing",
+ CURLOPT_POST => true,
+ CURLOPT_POSTFIELDS => json_encode($signature_fields),
+ CURLOPT_HTTPHEADER => $prepare_headers,
+ CURLOPT_RETURNTRANSFER => true
+]);
+
+$prepare_response = curl_exec($ch);
+curl_close($ch);
+
+$final_result = json_decode($prepare_response, true);
+print_r($final_result);
+?>
\ No newline at end of file
diff --git a/static/scripts/turbosign/api/complete-workflow/powershell.ps1 b/static/scripts/turbosign/api/complete-workflow/powershell.ps1
new file mode 100644
index 0000000..52dd95b
--- /dev/null
+++ b/static/scripts/turbosign/api/complete-workflow/powershell.ps1
@@ -0,0 +1,151 @@
+# Complete Workflow: Upload โ Recipients โ Prepare
+
+# Step 1: Upload Document
+$boundary = "----PowerShellBoundary$([System.Guid]::NewGuid())"
+$filePath = "./document.pdf"
+$fileBytes = [System.IO.File]::ReadAllBytes($filePath)
+
+$formData = @"
+--$boundary
+Content-Disposition: form-data; name="name"
+
+Contract Agreement
+--$boundary
+Content-Disposition: form-data; name="file"; filename="document.pdf"
+Content-Type: application/pdf
+
+"@
+
+$formDataBytes = [System.Text.Encoding]::UTF8.GetBytes($formData)
+$endBoundary = [System.Text.Encoding]::UTF8.GetBytes("`r`n--$boundary--`r`n")
+
+$uploadBody = New-Object byte[] ($formDataBytes.Length + $fileBytes.Length + $endBoundary.Length)
+[Array]::Copy($formDataBytes, 0, $uploadBody, 0, $formDataBytes.Length)
+[Array]::Copy($fileBytes, 0, $uploadBody, $formDataBytes.Length, $fileBytes.Length)
+[Array]::Copy($endBoundary, 0, $uploadBody, $formDataBytes.Length + $fileBytes.Length, $endBoundary.Length)
+
+$uploadHeaders = @{
+ 'Authorization' = 'Bearer YOUR_API_TOKEN'
+ 'x-rapiddocx-org-id' = 'YOUR_ORGANIZATION_ID'
+ 'origin' = 'https://www.turbodocx.com'
+ 'referer' = 'https://www.turbodocx.com'
+ 'accept' = 'application/json, text/plain, */*'
+ 'Content-Type' = "multipart/form-data; boundary=$boundary"
+}
+
+$uploadResponse = Invoke-RestMethod -Uri "https://www.turbodocx.com/turbosign/documents/upload" -Method Post -Body $uploadBody -Headers $uploadHeaders
+$documentId = $uploadResponse.data.id
+
+# Step 2: Add Recipients
+$recipientPayload = @{
+ document = @{
+ name = "Contract Agreement - Updated"
+ description = "This document requires electronic signatures from both parties. Please review all content carefully before signing."
+ }
+ recipients = @(
+ @{
+ name = "John Smith"
+ email = "john.smith@company.com"
+ signingOrder = 1
+ metadata = @{
+ color = "hsl(200, 75%, 50%)"
+ lightColor = "hsl(200, 75%, 93%)"
+ }
+ documentId = $documentId
+ },
+ @{
+ name = "Jane Doe"
+ email = "jane.doe@partner.com"
+ signingOrder = 2
+ metadata = @{
+ color = "hsl(270, 75%, 50%)"
+ lightColor = "hsl(270, 75%, 93%)"
+ }
+ documentId = $documentId
+ }
+ )
+} | ConvertTo-Json -Depth 10
+
+$recipientHeaders = @{
+ 'Content-Type' = 'application/json'
+ 'Authorization' = 'Bearer YOUR_API_TOKEN'
+ 'x-rapiddocx-org-id' = 'YOUR_ORGANIZATION_ID'
+ 'origin' = 'https://www.turbodocx.com'
+ 'referer' = 'https://www.turbodocx.com'
+ 'accept' = 'application/json, text/plain, */*'
+}
+
+$recipientResponse = Invoke-RestMethod -Uri "https://www.turbodocx.com/turbosign/documents/$documentId/update-with-recipients" -Method Post -Body $recipientPayload -Headers $recipientHeaders -ContentType 'application/json'
+$recipients = $recipientResponse.data.recipients
+
+# Step 3: Prepare for Signing
+$signatureFields = @(
+ @{
+ recipientId = $recipients[0].id
+ type = "signature"
+ template = @{
+ anchor = "{Signature1}"
+ placement = "replace"
+ size = @{ width = 200; height = 80 }
+ offset = @{ x = 0; y = 0 }
+ caseSensitive = $true
+ useRegex = $false
+ }
+ defaultValue = ""
+ required = $true
+ },
+ @{
+ recipientId = $recipients[0].id
+ type = "date"
+ template = @{
+ anchor = "{Date1}"
+ placement = "replace"
+ size = @{ width = 150; height = 30 }
+ offset = @{ x = 0; y = 0 }
+ caseSensitive = $true
+ useRegex = $false
+ }
+ defaultValue = ""
+ required = $true
+ },
+ @{
+ recipientId = $recipients[1].id
+ type = "signature"
+ template = @{
+ anchor = "{Signature2}"
+ placement = "replace"
+ size = @{ width = 200; height = 80 }
+ offset = @{ x = 0; y = 0 }
+ caseSensitive = $true
+ useRegex = $false
+ }
+ defaultValue = ""
+ required = $true
+ },
+ @{
+ recipientId = $recipients[1].id
+ type = "text"
+ template = @{
+ anchor = "{Title2}"
+ placement = "replace"
+ size = @{ width = 200; height = 30 }
+ offset = @{ x = 0; y = 0 }
+ caseSensitive = $true
+ useRegex = $false
+ }
+ defaultValue = "Business Partner"
+ required = $false
+ }
+) | ConvertTo-Json -Depth 10
+
+$prepareHeaders = @{
+ 'Content-Type' = 'application/json'
+ 'Authorization' = 'Bearer YOUR_API_TOKEN'
+ 'x-rapiddocx-org-id' = 'YOUR_ORGANIZATION_ID'
+ 'origin' = 'https://www.turbodocx.com'
+ 'referer' = 'https://www.turbodocx.com'
+ 'accept' = 'application/json, text/plain, */*'
+}
+
+$finalResponse = Invoke-RestMethod -Uri "https://www.turbodocx.com/turbosign/documents/$documentId/prepare-for-signing" -Method Post -Body $signatureFields -Headers $prepareHeaders -ContentType 'application/json'
+$finalResponse | ConvertTo-Json
\ No newline at end of file
diff --git a/static/scripts/turbosign/api/complete-workflow/python/fastapi.py b/static/scripts/turbosign/api/complete-workflow/python/fastapi.py
new file mode 100644
index 0000000..f738cb8
--- /dev/null
+++ b/static/scripts/turbosign/api/complete-workflow/python/fastapi.py
@@ -0,0 +1,146 @@
+import requests
+
+# Complete Workflow: Upload โ Recipients โ Prepare
+
+# Step 1: Upload Document
+files = {
+ 'name': (None, 'Contract Agreement'),
+ 'file': ('document.pdf', open('document.pdf', 'rb'), 'application/pdf')
+}
+
+upload_response = requests.post(
+ 'https://www.turbodocx.com/turbosign/documents/upload',
+ headers={
+ 'Authorization': 'Bearer YOUR_API_TOKEN',
+ 'x-rapiddocx-org-id': 'YOUR_ORGANIZATION_ID',
+ 'origin': 'https://www.turbodocx.com',
+ 'referer': 'https://www.turbodocx.com',
+ 'accept': 'application/json, text/plain, */*'
+ },
+ files=files
+)
+
+upload_result = upload_response.json()
+document_id = upload_result['data']['id']
+
+# Step 2: Add Recipients
+recipient_payload = {
+ "document": {
+ "name": "Contract Agreement - Updated",
+ "description": "This document requires electronic signatures from both parties. Please review all content carefully before signing."
+ },
+ "recipients": [
+ {
+ "name": "John Smith",
+ "email": "john.smith@company.com",
+ "signingOrder": 1,
+ "metadata": {
+ "color": "hsl(200, 75%, 50%)",
+ "lightColor": "hsl(200, 75%, 93%)"
+ },
+ "documentId": document_id
+ },
+ {
+ "name": "Jane Doe",
+ "email": "jane.doe@partner.com",
+ "signingOrder": 2,
+ "metadata": {
+ "color": "hsl(270, 75%, 50%)",
+ "lightColor": "hsl(270, 75%, 93%)"
+ },
+ "documentId": document_id
+ }
+ ]
+}
+
+recipient_response = requests.post(
+ f'https://www.turbodocx.com/turbosign/documents/{document_id}/update-with-recipients',
+ headers={
+ 'Content-Type': 'application/json',
+ 'Authorization': 'Bearer YOUR_API_TOKEN',
+ 'x-rapiddocx-org-id': 'YOUR_ORGANIZATION_ID',
+ 'origin': 'https://www.turbodocx.com',
+ 'referer': 'https://www.turbodocx.com',
+ 'accept': 'application/json, text/plain, */*'
+ },
+ json=recipient_payload
+)
+
+recipient_result = recipient_response.json()
+recipients = recipient_result['data']['recipients']
+
+# Step 3: Prepare for Signing
+signature_fields = [
+ {
+ "recipientId": recipients[0]['id'],
+ "type": "signature",
+ "template": {
+ "anchor": "{Signature1}",
+ "placement": "replace",
+ "size": {"width": 200, "height": 80},
+ "offset": {"x": 0, "y": 0},
+ "caseSensitive": True,
+ "useRegex": False
+ },
+ "defaultValue": "",
+ "required": True
+ },
+ {
+ "recipientId": recipients[0]['id'],
+ "type": "date",
+ "template": {
+ "anchor": "{Date1}",
+ "placement": "replace",
+ "size": {"width": 150, "height": 30},
+ "offset": {"x": 0, "y": 0},
+ "caseSensitive": True,
+ "useRegex": False
+ },
+ "defaultValue": "",
+ "required": True
+ },
+ {
+ "recipientId": recipients[1]['id'],
+ "type": "signature",
+ "template": {
+ "anchor": "{Signature2}",
+ "placement": "replace",
+ "size": {"width": 200, "height": 80},
+ "offset": {"x": 0, "y": 0},
+ "caseSensitive": True,
+ "useRegex": False
+ },
+ "defaultValue": "",
+ "required": True
+ },
+ {
+ "recipientId": recipients[1]['id'],
+ "type": "text",
+ "template": {
+ "anchor": "{Title2}",
+ "placement": "replace",
+ "size": {"width": 200, "height": 30},
+ "offset": {"x": 0, "y": 0},
+ "caseSensitive": True,
+ "useRegex": False
+ },
+ "defaultValue": "Business Partner",
+ "required": False
+ }
+]
+
+prepare_response = requests.post(
+ f'https://www.turbodocx.com/turbosign/documents/{document_id}/prepare-for-signing',
+ headers={
+ 'Content-Type': 'application/json',
+ 'Authorization': 'Bearer YOUR_API_TOKEN',
+ 'x-rapiddocx-org-id': 'YOUR_ORGANIZATION_ID',
+ 'origin': 'https://www.turbodocx.com',
+ 'referer': 'https://www.turbodocx.com',
+ 'accept': 'application/json, text/plain, */*'
+ },
+ json=signature_fields
+)
+
+final_result = prepare_response.json()
+print(final_result)
\ No newline at end of file
diff --git a/static/scripts/turbosign/api/complete-workflow/python/flask.py b/static/scripts/turbosign/api/complete-workflow/python/flask.py
new file mode 100644
index 0000000..f738cb8
--- /dev/null
+++ b/static/scripts/turbosign/api/complete-workflow/python/flask.py
@@ -0,0 +1,146 @@
+import requests
+
+# Complete Workflow: Upload โ Recipients โ Prepare
+
+# Step 1: Upload Document
+files = {
+ 'name': (None, 'Contract Agreement'),
+ 'file': ('document.pdf', open('document.pdf', 'rb'), 'application/pdf')
+}
+
+upload_response = requests.post(
+ 'https://www.turbodocx.com/turbosign/documents/upload',
+ headers={
+ 'Authorization': 'Bearer YOUR_API_TOKEN',
+ 'x-rapiddocx-org-id': 'YOUR_ORGANIZATION_ID',
+ 'origin': 'https://www.turbodocx.com',
+ 'referer': 'https://www.turbodocx.com',
+ 'accept': 'application/json, text/plain, */*'
+ },
+ files=files
+)
+
+upload_result = upload_response.json()
+document_id = upload_result['data']['id']
+
+# Step 2: Add Recipients
+recipient_payload = {
+ "document": {
+ "name": "Contract Agreement - Updated",
+ "description": "This document requires electronic signatures from both parties. Please review all content carefully before signing."
+ },
+ "recipients": [
+ {
+ "name": "John Smith",
+ "email": "john.smith@company.com",
+ "signingOrder": 1,
+ "metadata": {
+ "color": "hsl(200, 75%, 50%)",
+ "lightColor": "hsl(200, 75%, 93%)"
+ },
+ "documentId": document_id
+ },
+ {
+ "name": "Jane Doe",
+ "email": "jane.doe@partner.com",
+ "signingOrder": 2,
+ "metadata": {
+ "color": "hsl(270, 75%, 50%)",
+ "lightColor": "hsl(270, 75%, 93%)"
+ },
+ "documentId": document_id
+ }
+ ]
+}
+
+recipient_response = requests.post(
+ f'https://www.turbodocx.com/turbosign/documents/{document_id}/update-with-recipients',
+ headers={
+ 'Content-Type': 'application/json',
+ 'Authorization': 'Bearer YOUR_API_TOKEN',
+ 'x-rapiddocx-org-id': 'YOUR_ORGANIZATION_ID',
+ 'origin': 'https://www.turbodocx.com',
+ 'referer': 'https://www.turbodocx.com',
+ 'accept': 'application/json, text/plain, */*'
+ },
+ json=recipient_payload
+)
+
+recipient_result = recipient_response.json()
+recipients = recipient_result['data']['recipients']
+
+# Step 3: Prepare for Signing
+signature_fields = [
+ {
+ "recipientId": recipients[0]['id'],
+ "type": "signature",
+ "template": {
+ "anchor": "{Signature1}",
+ "placement": "replace",
+ "size": {"width": 200, "height": 80},
+ "offset": {"x": 0, "y": 0},
+ "caseSensitive": True,
+ "useRegex": False
+ },
+ "defaultValue": "",
+ "required": True
+ },
+ {
+ "recipientId": recipients[0]['id'],
+ "type": "date",
+ "template": {
+ "anchor": "{Date1}",
+ "placement": "replace",
+ "size": {"width": 150, "height": 30},
+ "offset": {"x": 0, "y": 0},
+ "caseSensitive": True,
+ "useRegex": False
+ },
+ "defaultValue": "",
+ "required": True
+ },
+ {
+ "recipientId": recipients[1]['id'],
+ "type": "signature",
+ "template": {
+ "anchor": "{Signature2}",
+ "placement": "replace",
+ "size": {"width": 200, "height": 80},
+ "offset": {"x": 0, "y": 0},
+ "caseSensitive": True,
+ "useRegex": False
+ },
+ "defaultValue": "",
+ "required": True
+ },
+ {
+ "recipientId": recipients[1]['id'],
+ "type": "text",
+ "template": {
+ "anchor": "{Title2}",
+ "placement": "replace",
+ "size": {"width": 200, "height": 30},
+ "offset": {"x": 0, "y": 0},
+ "caseSensitive": True,
+ "useRegex": False
+ },
+ "defaultValue": "Business Partner",
+ "required": False
+ }
+]
+
+prepare_response = requests.post(
+ f'https://www.turbodocx.com/turbosign/documents/{document_id}/prepare-for-signing',
+ headers={
+ 'Content-Type': 'application/json',
+ 'Authorization': 'Bearer YOUR_API_TOKEN',
+ 'x-rapiddocx-org-id': 'YOUR_ORGANIZATION_ID',
+ 'origin': 'https://www.turbodocx.com',
+ 'referer': 'https://www.turbodocx.com',
+ 'accept': 'application/json, text/plain, */*'
+ },
+ json=signature_fields
+)
+
+final_result = prepare_response.json()
+print(final_result)
\ No newline at end of file
diff --git a/static/scripts/turbosign/api/complete-workflow/ruby.rb b/static/scripts/turbosign/api/complete-workflow/ruby.rb
new file mode 100644
index 0000000..f1a641c
--- /dev/null
+++ b/static/scripts/turbosign/api/complete-workflow/ruby.rb
@@ -0,0 +1,154 @@
+require 'net/http'
+require 'uri'
+require 'json'
+
+# Complete Workflow: Upload โ Recipients โ Prepare
+
+# Step 1: Upload Document
+uri = URI('https://www.turbodocx.com/turbosign/documents/upload')
+boundary = "----RubyBoundary#{rand(1000000)}"
+
+form_data = ""
+form_data << "--#{boundary}\r\n"
+form_data << "Content-Disposition: form-data; name=\"name\"\r\n\r\n"
+form_data << "Contract Agreement\r\n"
+form_data << "--#{boundary}\r\n"
+form_data << "Content-Disposition: form-data; name=\"file\"; filename=\"document.pdf\"\r\n"
+form_data << "Content-Type: application/pdf\r\n\r\n"
+form_data << File.read('./document.pdf')
+form_data << "\r\n--#{boundary}--\r\n"
+
+http = Net::HTTP.new(uri.host, uri.port)
+http.use_ssl = true
+
+request = Net::HTTP::Post.new(uri)
+request['Authorization'] = 'Bearer YOUR_API_TOKEN'
+request['x-rapiddocx-org-id'] = 'YOUR_ORGANIZATION_ID'
+request['origin'] = 'https://www.turbodocx.com'
+request['referer'] = 'https://www.turbodocx.com'
+request['accept'] = 'application/json, text/plain, */*'
+request['Content-Type'] = "multipart/form-data; boundary=#{boundary}"
+request.body = form_data
+
+upload_response = http.request(request)
+upload_result = JSON.parse(upload_response.body)
+document_id = upload_result['data']['id']
+
+# Step 2: Add Recipients
+recipient_payload = {
+ "document" => {
+ "name" => "Contract Agreement - Updated",
+ "description" => "This document requires electronic signatures from both parties. Please review all content carefully before signing."
+ },
+ "recipients" => [
+ {
+ "name" => "John Smith",
+ "email" => "john.smith@company.com",
+ "signingOrder" => 1,
+ "metadata" => {
+ "color" => "hsl(200, 75%, 50%)",
+ "lightColor" => "hsl(200, 75%, 93%)"
+ },
+ "documentId" => document_id
+ },
+ {
+ "name" => "Jane Doe",
+ "email" => "jane.doe@partner.com",
+ "signingOrder" => 2,
+ "metadata" => {
+ "color" => "hsl(270, 75%, 50%)",
+ "lightColor" => "hsl(270, 75%, 93%)"
+ },
+ "documentId" => document_id
+ }
+ ]
+}
+
+uri2 = URI("https://www.turbodocx.com/turbosign/documents/#{document_id}/update-with-recipients")
+
+request2 = Net::HTTP::Post.new(uri2)
+request2['Content-Type'] = 'application/json'
+request2['Authorization'] = 'Bearer YOUR_API_TOKEN'
+request2['x-rapiddocx-org-id'] = 'YOUR_ORGANIZATION_ID'
+request2['origin'] = 'https://www.turbodocx.com'
+request2['referer'] = 'https://www.turbodocx.com'
+request2['accept'] = 'application/json, text/plain, */*'
+request2.body = recipient_payload.to_json
+
+recipient_response = http.request(request2)
+recipient_result = JSON.parse(recipient_response.body)
+recipients = recipient_result['data']['recipients']
+
+# Step 3: Prepare for Signing
+signature_fields = [
+ {
+ "recipientId" => recipients[0]['id'],
+ "type" => "signature",
+ "template" => {
+ "anchor" => "{Signature1}",
+ "placement" => "replace",
+ "size" => { "width" => 200, "height" => 80 },
+ "offset" => { "x" => 0, "y" => 0 },
+ "caseSensitive" => true,
+ "useRegex" => false
+ },
+ "defaultValue" => "",
+ "required" => true
+ },
+ {
+ "recipientId" => recipients[0]['id'],
+ "type" => "date",
+ "template" => {
+ "anchor" => "{Date1}",
+ "placement" => "replace",
+ "size" => { "width" => 150, "height" => 30 },
+ "offset" => { "x" => 0, "y" => 0 },
+ "caseSensitive" => true,
+ "useRegex" => false
+ },
+ "defaultValue" => "",
+ "required" => true
+ },
+ {
+ "recipientId" => recipients[1]['id'],
+ "type" => "signature",
+ "template" => {
+ "anchor" => "{Signature2}",
+ "placement" => "replace",
+ "size" => { "width" => 200, "height" => 80 },
+ "offset" => { "x" => 0, "y" => 0 },
+ "caseSensitive" => true,
+ "useRegex" => false
+ },
+ "defaultValue" => "",
+ "required" => true
+ },
+ {
+ "recipientId" => recipients[1]['id'],
+ "type" => "text",
+ "template" => {
+ "anchor" => "{Title2}",
+ "placement" => "replace",
+ "size" => { "width" => 200, "height" => 30 },
+ "offset" => { "x" => 0, "y" => 0 },
+ "caseSensitive" => true,
+ "useRegex" => false
+ },
+ "defaultValue" => "Business Partner",
+ "required" => false
+ }
+]
+
+uri3 = URI("https://www.turbodocx.com/turbosign/documents/#{document_id}/prepare-for-signing")
+
+request3 = Net::HTTP::Post.new(uri3)
+request3['Content-Type'] = 'application/json'
+request3['Authorization'] = 'Bearer YOUR_API_TOKEN'
+request3['x-rapiddocx-org-id'] = 'YOUR_ORGANIZATION_ID'
+request3['origin'] = 'https://www.turbodocx.com'
+request3['referer'] = 'https://www.turbodocx.com'
+request3['accept'] = 'application/json, text/plain, */*'
+request3.body = signature_fields.to_json
+
+prepare_response = http.request(request3)
+puts prepare_response.body
\ No newline at end of file
diff --git a/static/scripts/turbosign/api/step1-upload/csharp/controller.cs b/static/scripts/turbosign/api/step1-upload/csharp/controller.cs
new file mode 100644
index 0000000..db734f2
--- /dev/null
+++ b/static/scripts/turbosign/api/step1-upload/csharp/controller.cs
@@ -0,0 +1,31 @@
+using System;
+using System.IO;
+using System.Net.Http;
+using System.Threading.Tasks;
+
+class Program
+{
+ static async Task Main(string[] args)
+ {
+ // Step 1: Upload Document
+ using var client = new HttpClient();
+
+ client.DefaultRequestHeaders.Add("Authorization", "Bearer YOUR_API_TOKEN");
+ client.DefaultRequestHeaders.Add("x-rapiddocx-org-id", "YOUR_ORGANIZATION_ID");
+ client.DefaultRequestHeaders.Add("origin", "https://www.turbodocx.com");
+ client.DefaultRequestHeaders.Add("referer", "https://www.turbodocx.com");
+ client.DefaultRequestHeaders.Add("accept", "application/json, text/plain, */*");
+
+ using var content = new MultipartFormDataContent();
+ content.Add(new StringContent("Contract Agreement"), "name");
+
+ var fileContent = new ByteArrayContent(File.ReadAllBytes("./document.pdf"));
+ fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
+ content.Add(fileContent, "file", "document.pdf");
+
+ var response = await client.PostAsync("https://www.turbodocx.com/turbosign/documents/upload", content);
+ var result = await response.Content.ReadAsStringAsync();
+
+ Console.WriteLine(result);
+ }
+}
\ No newline at end of file
diff --git a/static/scripts/turbosign/api/step1-upload/csharp/minimal.cs b/static/scripts/turbosign/api/step1-upload/csharp/minimal.cs
new file mode 100644
index 0000000..db734f2
--- /dev/null
+++ b/static/scripts/turbosign/api/step1-upload/csharp/minimal.cs
@@ -0,0 +1,31 @@
+using System;
+using System.IO;
+using System.Net.Http;
+using System.Threading.Tasks;
+
+class Program
+{
+ static async Task Main(string[] args)
+ {
+ // Step 1: Upload Document
+ using var client = new HttpClient();
+
+ client.DefaultRequestHeaders.Add("Authorization", "Bearer YOUR_API_TOKEN");
+ client.DefaultRequestHeaders.Add("x-rapiddocx-org-id", "YOUR_ORGANIZATION_ID");
+ client.DefaultRequestHeaders.Add("origin", "https://www.turbodocx.com");
+ client.DefaultRequestHeaders.Add("referer", "https://www.turbodocx.com");
+ client.DefaultRequestHeaders.Add("accept", "application/json, text/plain, */*");
+
+ using var content = new MultipartFormDataContent();
+ content.Add(new StringContent("Contract Agreement"), "name");
+
+ var fileContent = new ByteArrayContent(File.ReadAllBytes("./document.pdf"));
+ fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
+ content.Add(fileContent, "file", "document.pdf");
+
+ var response = await client.PostAsync("https://www.turbodocx.com/turbosign/documents/upload", content);
+ var result = await response.Content.ReadAsStringAsync();
+
+ Console.WriteLine(result);
+ }
+}
\ No newline at end of file
diff --git a/static/scripts/turbosign/api/step1-upload/go.go b/static/scripts/turbosign/api/step1-upload/go.go
new file mode 100644
index 0000000..57530ad
--- /dev/null
+++ b/static/scripts/turbosign/api/step1-upload/go.go
@@ -0,0 +1,38 @@
+package main
+
+import (
+ "bytes"
+ "io"
+ "mime/multipart"
+ "net/http"
+ "os"
+)
+
+func main() {
+ // Step 1: Upload Document
+ var buf bytes.Buffer
+ writer := multipart.NewWriter(&buf)
+
+ writer.WriteField("name", "Contract Agreement")
+
+ file, _ := os.Open("./document.pdf")
+ defer file.Close()
+ part, _ := writer.CreateFormFile("file", "document.pdf")
+ io.Copy(part, file)
+ writer.Close()
+
+ req, _ := http.NewRequest("POST", "https://www.turbodocx.com/turbosign/documents/upload", &buf)
+ req.Header.Set("Authorization", "Bearer YOUR_API_TOKEN")
+ req.Header.Set("x-rapiddocx-org-id", "YOUR_ORGANIZATION_ID")
+ req.Header.Set("origin", "https://www.turbodocx.com")
+ req.Header.Set("referer", "https://www.turbodocx.com")
+ req.Header.Set("accept", "application/json, text/plain, */*")
+ req.Header.Set("Content-Type", writer.FormDataContentType())
+
+ client := &http.Client{}
+ resp, _ := client.Do(req)
+ defer resp.Body.Close()
+
+ body, _ := io.ReadAll(resp.Body)
+ println(string(body))
+}
\ No newline at end of file
diff --git a/static/scripts/turbosign/api/step1-upload/java.java b/static/scripts/turbosign/api/step1-upload/java.java
new file mode 100644
index 0000000..5cfa23b
--- /dev/null
+++ b/static/scripts/turbosign/api/step1-upload/java.java
@@ -0,0 +1,41 @@
+import java.io.*;
+import java.net.http.*;
+import java.nio.file.*;
+
+public class TurboSignUpload {
+ public static void main(String[] args) throws Exception {
+ // Step 1: Upload Document
+ HttpClient client = HttpClient.newHttpClient();
+
+ String boundary = "----JavaBoundary" + System.currentTimeMillis();
+ String fileName = "./document.pdf";
+ byte[] fileBytes = Files.readAllBytes(Paths.get(fileName));
+
+ StringBuilder formData = new StringBuilder();
+ formData.append("--").append(boundary).append("\r\n");
+ formData.append("Content-Disposition: form-data; name=\"name\"\r\n\r\n");
+ formData.append("Contract Agreement\r\n");
+ formData.append("--").append(boundary).append("\r\n");
+ formData.append("Content-Disposition: form-data; name=\"file\"; filename=\"document.pdf\"\r\n");
+ formData.append("Content-Type: application/pdf\r\n\r\n");
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ baos.write(formData.toString().getBytes());
+ baos.write(fileBytes);
+ baos.write(("\r\n--" + boundary + "--\r\n").getBytes());
+
+ HttpRequest request = HttpRequest.newBuilder()
+ .uri(URI.create("https://www.turbodocx.com/turbosign/documents/upload"))
+ .header("Authorization", "Bearer YOUR_API_TOKEN")
+ .header("x-rapiddocx-org-id", "YOUR_ORGANIZATION_ID")
+ .header("origin", "https://www.turbodocx.com")
+ .header("referer", "https://www.turbodocx.com")
+ .header("accept", "application/json, text/plain, */*")
+ .header("Content-Type", "multipart/form-data; boundary=" + boundary)
+ .POST(HttpRequest.BodyPublishers.ofByteArray(baos.toByteArray()))
+ .build();
+
+ HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
+ System.out.println(response.body());
+ }
+}
\ No newline at end of file
diff --git a/static/scripts/turbosign/api/step1-upload/nodejs/express.js b/static/scripts/turbosign/api/step1-upload/nodejs/express.js
new file mode 100644
index 0000000..a48f169
--- /dev/null
+++ b/static/scripts/turbosign/api/step1-upload/nodejs/express.js
@@ -0,0 +1,24 @@
+const FormData = require('form-data');
+const fs = require('fs');
+const fetch = require('node-fetch');
+
+// Step 1: Upload Document
+const formData = new FormData();
+formData.append('name', 'Contract Agreement');
+formData.append('file', fs.createReadStream('./document.pdf'));
+
+const response = await fetch('https://www.turbodocx.com/turbosign/documents/upload', {
+ method: 'POST',
+ headers: {
+ 'Authorization': 'Bearer YOUR_API_TOKEN',
+ 'x-rapiddocx-org-id': 'YOUR_ORGANIZATION_ID',
+ 'origin': 'https://www.turbodocx.com',
+ 'referer': 'https://www.turbodocx.com',
+ 'accept': 'application/json, text/plain, */*',
+ ...formData.getHeaders()
+ },
+ body: formData
+});
+
+const result = await response.json();
+console.log(result);
\ No newline at end of file
diff --git a/static/scripts/turbosign/api/step1-upload/nodejs/fastify.js b/static/scripts/turbosign/api/step1-upload/nodejs/fastify.js
new file mode 100644
index 0000000..a48f169
--- /dev/null
+++ b/static/scripts/turbosign/api/step1-upload/nodejs/fastify.js
@@ -0,0 +1,24 @@
+const FormData = require('form-data');
+const fs = require('fs');
+const fetch = require('node-fetch');
+
+// Step 1: Upload Document
+const formData = new FormData();
+formData.append('name', 'Contract Agreement');
+formData.append('file', fs.createReadStream('./document.pdf'));
+
+const response = await fetch('https://www.turbodocx.com/turbosign/documents/upload', {
+ method: 'POST',
+ headers: {
+ 'Authorization': 'Bearer YOUR_API_TOKEN',
+ 'x-rapiddocx-org-id': 'YOUR_ORGANIZATION_ID',
+ 'origin': 'https://www.turbodocx.com',
+ 'referer': 'https://www.turbodocx.com',
+ 'accept': 'application/json, text/plain, */*',
+ ...formData.getHeaders()
+ },
+ body: formData
+});
+
+const result = await response.json();
+console.log(result);
\ No newline at end of file
diff --git a/static/scripts/turbosign/api/step1-upload/php.php b/static/scripts/turbosign/api/step1-upload/php.php
new file mode 100644
index 0000000..c145cba
--- /dev/null
+++ b/static/scripts/turbosign/api/step1-upload/php.php
@@ -0,0 +1,32 @@
+ 'Contract Agreement',
+ 'file' => new CURLFile('./document.pdf', 'application/pdf', 'document.pdf')
+];
+
+$ch = curl_init();
+curl_setopt_array($ch, [
+ CURLOPT_URL => $url,
+ CURLOPT_POST => true,
+ CURLOPT_POSTFIELDS => $postFields,
+ CURLOPT_HTTPHEADER => $headers,
+ CURLOPT_RETURNTRANSFER => true
+]);
+
+$response = curl_exec($ch);
+curl_close($ch);
+
+$result = json_decode($response, true);
+print_r($result);
+?>
\ No newline at end of file
diff --git a/static/scripts/turbosign/api/step1-upload/powershell.ps1 b/static/scripts/turbosign/api/step1-upload/powershell.ps1
new file mode 100644
index 0000000..0983e2a
--- /dev/null
+++ b/static/scripts/turbosign/api/step1-upload/powershell.ps1
@@ -0,0 +1,35 @@
+# Step 1: Upload Document
+$boundary = "----PowerShellBoundary$([System.Guid]::NewGuid())"
+$filePath = "./document.pdf"
+$fileBytes = [System.IO.File]::ReadAllBytes($filePath)
+
+$formData = @"
+--$boundary
+Content-Disposition: form-data; name="name"
+
+Contract Agreement
+--$boundary
+Content-Disposition: form-data; name="file"; filename="document.pdf"
+Content-Type: application/pdf
+
+"@
+
+$formDataBytes = [System.Text.Encoding]::UTF8.GetBytes($formData)
+$endBoundary = [System.Text.Encoding]::UTF8.GetBytes("`r`n--$boundary--`r`n")
+
+$body = New-Object byte[] ($formDataBytes.Length + $fileBytes.Length + $endBoundary.Length)
+[Array]::Copy($formDataBytes, 0, $body, 0, $formDataBytes.Length)
+[Array]::Copy($fileBytes, 0, $body, $formDataBytes.Length, $fileBytes.Length)
+[Array]::Copy($endBoundary, 0, $body, $formDataBytes.Length + $fileBytes.Length, $endBoundary.Length)
+
+$headers = @{
+ 'Authorization' = 'Bearer YOUR_API_TOKEN'
+ 'x-rapiddocx-org-id' = 'YOUR_ORGANIZATION_ID'
+ 'origin' = 'https://www.turbodocx.com'
+ 'referer' = 'https://www.turbodocx.com'
+ 'accept' = 'application/json, text/plain, */*'
+ 'Content-Type' = "multipart/form-data; boundary=$boundary"
+}
+
+$response = Invoke-RestMethod -Uri "https://www.turbodocx.com/turbosign/documents/upload" -Method Post -Body $body -Headers $headers
+$response | ConvertTo-Json
\ No newline at end of file
diff --git a/static/scripts/turbosign/api/step1-upload/python/fastapi.py b/static/scripts/turbosign/api/step1-upload/python/fastapi.py
new file mode 100644
index 0000000..10122dd
--- /dev/null
+++ b/static/scripts/turbosign/api/step1-upload/python/fastapi.py
@@ -0,0 +1,22 @@
+import requests
+
+# Step 1: Upload Document
+files = {
+ 'name': (None, 'Contract Agreement'),
+ 'file': ('document.pdf', open('document.pdf', 'rb'), 'application/pdf')
+}
+
+response = requests.post(
+ 'https://www.turbodocx.com/turbosign/documents/upload',
+ headers={
+ 'Authorization': 'Bearer YOUR_API_TOKEN',
+ 'x-rapiddocx-org-id': 'YOUR_ORGANIZATION_ID',
+ 'origin': 'https://www.turbodocx.com',
+ 'referer': 'https://www.turbodocx.com',
+ 'accept': 'application/json, text/plain, */*'
+ },
+ files=files
+)
+
+result = response.json()
+print(result)
\ No newline at end of file
diff --git a/static/scripts/turbosign/api/step1-upload/python/flask.py b/static/scripts/turbosign/api/step1-upload/python/flask.py
new file mode 100644
index 0000000..10122dd
--- /dev/null
+++ b/static/scripts/turbosign/api/step1-upload/python/flask.py
@@ -0,0 +1,22 @@
+import requests
+
+# Step 1: Upload Document
+files = {
+ 'name': (None, 'Contract Agreement'),
+ 'file': ('document.pdf', open('document.pdf', 'rb'), 'application/pdf')
+}
+
+response = requests.post(
+ 'https://www.turbodocx.com/turbosign/documents/upload',
+ headers={
+ 'Authorization': 'Bearer YOUR_API_TOKEN',
+ 'x-rapiddocx-org-id': 'YOUR_ORGANIZATION_ID',
+ 'origin': 'https://www.turbodocx.com',
+ 'referer': 'https://www.turbodocx.com',
+ 'accept': 'application/json, text/plain, */*'
+ },
+ files=files
+)
+
+result = response.json()
+print(result)
\ No newline at end of file
diff --git a/static/scripts/turbosign/api/step1-upload/ruby.rb b/static/scripts/turbosign/api/step1-upload/ruby.rb
new file mode 100644
index 0000000..07072f9
--- /dev/null
+++ b/static/scripts/turbosign/api/step1-upload/ruby.rb
@@ -0,0 +1,31 @@
+require 'net/http'
+require 'uri'
+
+# Step 1: Upload Document
+uri = URI('https://www.turbodocx.com/turbosign/documents/upload')
+boundary = "----RubyBoundary#{rand(1000000)}"
+
+form_data = ""
+form_data << "--#{boundary}\r\n"
+form_data << "Content-Disposition: form-data; name=\"name\"\r\n\r\n"
+form_data << "Contract Agreement\r\n"
+form_data << "--#{boundary}\r\n"
+form_data << "Content-Disposition: form-data; name=\"file\"; filename=\"document.pdf\"\r\n"
+form_data << "Content-Type: application/pdf\r\n\r\n"
+form_data << File.read('./document.pdf')
+form_data << "\r\n--#{boundary}--\r\n"
+
+http = Net::HTTP.new(uri.host, uri.port)
+http.use_ssl = true
+
+request = Net::HTTP::Post.new(uri)
+request['Authorization'] = 'Bearer YOUR_API_TOKEN'
+request['x-rapiddocx-org-id'] = 'YOUR_ORGANIZATION_ID'
+request['origin'] = 'https://www.turbodocx.com'
+request['referer'] = 'https://www.turbodocx.com'
+request['accept'] = 'application/json, text/plain, */*'
+request['Content-Type'] = "multipart/form-data; boundary=#{boundary}"
+request.body = form_data
+
+response = http.request(request)
+puts response.body
\ No newline at end of file
diff --git a/static/scripts/turbosign/api/step2-recipients/csharp/controller.cs b/static/scripts/turbosign/api/step2-recipients/csharp/controller.cs
new file mode 100644
index 0000000..07b63f0
--- /dev/null
+++ b/static/scripts/turbosign/api/step2-recipients/csharp/controller.cs
@@ -0,0 +1,56 @@
+using System;
+using System.Net.Http;
+using System.Text;
+using System.Threading.Tasks;
+
+class Program
+{
+ static async Task Main(string[] args)
+ {
+ // Step 2: Add Recipients
+ var documentId = "4a20eca5-7944-430c-97d5-fcce4be24296";
+
+ using var client = new HttpClient();
+
+ client.DefaultRequestHeaders.Add("Authorization", "Bearer YOUR_API_TOKEN");
+ client.DefaultRequestHeaders.Add("x-rapiddocx-org-id", "YOUR_ORGANIZATION_ID");
+ client.DefaultRequestHeaders.Add("origin", "https://www.turbodocx.com");
+ client.DefaultRequestHeaders.Add("referer", "https://www.turbodocx.com");
+ client.DefaultRequestHeaders.Add("accept", "application/json, text/plain, */*");
+
+ var payload = @"{
+ ""document"": {
+ ""name"": ""Contract Agreement - Updated"",
+ ""description"": ""This document requires electronic signatures from both parties. Please review all content carefully before signing.""
+ },
+ ""recipients"": [
+ {
+ ""name"": ""John Smith"",
+ ""email"": ""john.smith@company.com"",
+ ""signingOrder"": 1,
+ ""metadata"": {
+ ""color"": ""hsl(200, 75%, 50%)"",
+ ""lightColor"": ""hsl(200, 75%, 93%)""
+ },
+ ""documentId"": """ + documentId + @"""
+ },
+ {
+ ""name"": ""Jane Doe"",
+ ""email"": ""jane.doe@partner.com"",
+ ""signingOrder"": 2,
+ ""metadata"": {
+ ""color"": ""hsl(270, 75%, 50%)"",
+ ""lightColor"": ""hsl(270, 75%, 93%)""
+ },
+ ""documentId"": """ + documentId + @"""
+ }
+ ]
+ }";
+
+ var content = new StringContent(payload, Encoding.UTF8, "application/json");
+ var response = await client.PostAsync($"https://www.turbodocx.com/turbosign/documents/{documentId}/update-with-recipients", content);
+ var result = await response.Content.ReadAsStringAsync();
+
+ Console.WriteLine(result);
+ }
+}
\ No newline at end of file
diff --git a/static/scripts/turbosign/api/step2-recipients/csharp/minimal.cs b/static/scripts/turbosign/api/step2-recipients/csharp/minimal.cs
new file mode 100644
index 0000000..07b63f0
--- /dev/null
+++ b/static/scripts/turbosign/api/step2-recipients/csharp/minimal.cs
@@ -0,0 +1,56 @@
+using System;
+using System.Net.Http;
+using System.Text;
+using System.Threading.Tasks;
+
+class Program
+{
+ static async Task Main(string[] args)
+ {
+ // Step 2: Add Recipients
+ var documentId = "4a20eca5-7944-430c-97d5-fcce4be24296";
+
+ using var client = new HttpClient();
+
+ client.DefaultRequestHeaders.Add("Authorization", "Bearer YOUR_API_TOKEN");
+ client.DefaultRequestHeaders.Add("x-rapiddocx-org-id", "YOUR_ORGANIZATION_ID");
+ client.DefaultRequestHeaders.Add("origin", "https://www.turbodocx.com");
+ client.DefaultRequestHeaders.Add("referer", "https://www.turbodocx.com");
+ client.DefaultRequestHeaders.Add("accept", "application/json, text/plain, */*");
+
+ var payload = @"{
+ ""document"": {
+ ""name"": ""Contract Agreement - Updated"",
+ ""description"": ""This document requires electronic signatures from both parties. Please review all content carefully before signing.""
+ },
+ ""recipients"": [
+ {
+ ""name"": ""John Smith"",
+ ""email"": ""john.smith@company.com"",
+ ""signingOrder"": 1,
+ ""metadata"": {
+ ""color"": ""hsl(200, 75%, 50%)"",
+ ""lightColor"": ""hsl(200, 75%, 93%)""
+ },
+ ""documentId"": """ + documentId + @"""
+ },
+ {
+ ""name"": ""Jane Doe"",
+ ""email"": ""jane.doe@partner.com"",
+ ""signingOrder"": 2,
+ ""metadata"": {
+ ""color"": ""hsl(270, 75%, 50%)"",
+ ""lightColor"": ""hsl(270, 75%, 93%)""
+ },
+ ""documentId"": """ + documentId + @"""
+ }
+ ]
+ }";
+
+ var content = new StringContent(payload, Encoding.UTF8, "application/json");
+ var response = await client.PostAsync($"https://www.turbodocx.com/turbosign/documents/{documentId}/update-with-recipients", content);
+ var result = await response.Content.ReadAsStringAsync();
+
+ Console.WriteLine(result);
+ }
+}
\ No newline at end of file
diff --git a/static/scripts/turbosign/api/step2-recipients/go.go b/static/scripts/turbosign/api/step2-recipients/go.go
new file mode 100644
index 0000000..752097c
--- /dev/null
+++ b/static/scripts/turbosign/api/step2-recipients/go.go
@@ -0,0 +1,57 @@
+package main
+
+import (
+ "bytes"
+ "fmt"
+ "net/http"
+)
+
+func main() {
+ // Step 2: Add Recipients
+ documentID := "4a20eca5-7944-430c-97d5-fcce4be24296"
+
+ payload := `{
+ "document": {
+ "name": "Contract Agreement - Updated",
+ "description": "This document requires electronic signatures from both parties. Please review all content carefully before signing."
+ },
+ "recipients": [
+ {
+ "name": "John Smith",
+ "email": "john.smith@company.com",
+ "signingOrder": 1,
+ "metadata": {
+ "color": "hsl(200, 75%, 50%)",
+ "lightColor": "hsl(200, 75%, 93%)"
+ },
+ "documentId": "` + documentID + `"
+ },
+ {
+ "name": "Jane Doe",
+ "email": "jane.doe@partner.com",
+ "signingOrder": 2,
+ "metadata": {
+ "color": "hsl(270, 75%, 50%)",
+ "lightColor": "hsl(270, 75%, 93%)"
+ },
+ "documentId": "` + documentID + `"
+ }
+ ]
+ }`
+
+ req, _ := http.NewRequest("POST", fmt.Sprintf("https://www.turbodocx.com/turbosign/documents/%s/update-with-recipients", documentID), bytes.NewBufferString(payload))
+ req.Header.Set("Content-Type", "application/json")
+ req.Header.Set("Authorization", "Bearer YOUR_API_TOKEN")
+ req.Header.Set("x-rapiddocx-org-id", "YOUR_ORGANIZATION_ID")
+ req.Header.Set("origin", "https://www.turbodocx.com")
+ req.Header.Set("referer", "https://www.turbodocx.com")
+ req.Header.Set("accept", "application/json, text/plain, */*")
+
+ client := &http.Client{}
+ resp, _ := client.Do(req)
+ defer resp.Body.Close()
+
+ body := make([]byte, 1024)
+ resp.Body.Read(body)
+ fmt.Println(string(body))
+}
\ No newline at end of file
diff --git a/static/scripts/turbosign/api/step2-recipients/java.java b/static/scripts/turbosign/api/step2-recipients/java.java
new file mode 100644
index 0000000..f61fac0
--- /dev/null
+++ b/static/scripts/turbosign/api/step2-recipients/java.java
@@ -0,0 +1,56 @@
+import java.net.http.*;
+import java.net.URI;
+
+public class TurboSignRecipients {
+ public static void main(String[] args) throws Exception {
+ // Step 2: Add Recipients
+ String documentId = "4a20eca5-7944-430c-97d5-fcce4be24296";
+
+ HttpClient client = HttpClient.newHttpClient();
+
+ String payload = """
+ {
+ "document": {
+ "name": "Contract Agreement - Updated",
+ "description": "This document requires electronic signatures from both parties. Please review all content carefully before signing."
+ },
+ "recipients": [
+ {
+ "name": "John Smith",
+ "email": "john.smith@company.com",
+ "signingOrder": 1,
+ "metadata": {
+ "color": "hsl(200, 75%, 50%)",
+ "lightColor": "hsl(200, 75%, 93%)"
+ },
+ "documentId": "%s"
+ },
+ {
+ "name": "Jane Doe",
+ "email": "jane.doe@partner.com",
+ "signingOrder": 2,
+ "metadata": {
+ "color": "hsl(270, 75%, 50%)",
+ "lightColor": "hsl(270, 75%, 93%)"
+ },
+ "documentId": "%s"
+ }
+ ]
+ }
+ """.formatted(documentId, documentId);
+
+ HttpRequest request = HttpRequest.newBuilder()
+ .uri(URI.create("https://www.turbodocx.com/turbosign/documents/" + documentId + "/update-with-recipients"))
+ .header("Content-Type", "application/json")
+ .header("Authorization", "Bearer YOUR_API_TOKEN")
+ .header("x-rapiddocx-org-id", "YOUR_ORGANIZATION_ID")
+ .header("origin", "https://www.turbodocx.com")
+ .header("referer", "https://www.turbodocx.com")
+ .header("accept", "application/json, text/plain, */*")
+ .POST(HttpRequest.BodyPublishers.ofString(payload))
+ .build();
+
+ HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
+ System.out.println(response.body());
+ }
+}
\ No newline at end of file
diff --git a/static/scripts/turbosign/api/step2-recipients/nodejs/express.js b/static/scripts/turbosign/api/step2-recipients/nodejs/express.js
new file mode 100644
index 0000000..607f949
--- /dev/null
+++ b/static/scripts/turbosign/api/step2-recipients/nodejs/express.js
@@ -0,0 +1,52 @@
+const fetch = require('node-fetch');
+
+// Step 2: Add Recipients
+const documentId = "4a20eca5-7944-430c-97d5-fcce4be24296";
+
+const payload = {
+ "document": {
+ "name": "Contract Agreement - Updated",
+ "description": "This document requires electronic signatures from both parties. Please review all content carefully before signing."
+ },
+ "recipients": [
+ {
+ "name": "John Smith",
+ "email": "john.smith@company.com",
+ "signingOrder": 1,
+ "metadata": {
+ "color": "hsl(200, 75%, 50%)",
+ "lightColor": "hsl(200, 75%, 93%)"
+ },
+ "documentId": documentId
+ },
+ {
+ "name": "Jane Doe",
+ "email": "jane.doe@partner.com",
+ "signingOrder": 2,
+ "metadata": {
+ "color": "hsl(270, 75%, 50%)",
+ "lightColor": "hsl(270, 75%, 93%)"
+ },
+ "documentId": documentId
+ }
+ ]
+};
+
+const response = await fetch(`https://www.turbodocx.com/turbosign/documents/${documentId}/update-with-recipients`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': 'Bearer YOUR_API_TOKEN',
+ 'x-rapiddocx-org-id': 'YOUR_ORGANIZATION_ID',
+ 'origin': 'https://www.turbodocx.com',
+ 'referer': 'https://www.turbodocx.com',
+ 'accept': 'application/json, text/plain, */*',
+ 'dnt': '1',
+ 'accept-language': 'en-US,en;q=0.9',
+ 'priority': 'u=1, i'
+ },
+ body: JSON.stringify(payload)
+});
+
+const result = await response.json();
+console.log(result);
\ No newline at end of file
diff --git a/static/scripts/turbosign/api/step2-recipients/nodejs/fastify.js b/static/scripts/turbosign/api/step2-recipients/nodejs/fastify.js
new file mode 100644
index 0000000..607f949
--- /dev/null
+++ b/static/scripts/turbosign/api/step2-recipients/nodejs/fastify.js
@@ -0,0 +1,52 @@
+const fetch = require('node-fetch');
+
+// Step 2: Add Recipients
+const documentId = "4a20eca5-7944-430c-97d5-fcce4be24296";
+
+const payload = {
+ "document": {
+ "name": "Contract Agreement - Updated",
+ "description": "This document requires electronic signatures from both parties. Please review all content carefully before signing."
+ },
+ "recipients": [
+ {
+ "name": "John Smith",
+ "email": "john.smith@company.com",
+ "signingOrder": 1,
+ "metadata": {
+ "color": "hsl(200, 75%, 50%)",
+ "lightColor": "hsl(200, 75%, 93%)"
+ },
+ "documentId": documentId
+ },
+ {
+ "name": "Jane Doe",
+ "email": "jane.doe@partner.com",
+ "signingOrder": 2,
+ "metadata": {
+ "color": "hsl(270, 75%, 50%)",
+ "lightColor": "hsl(270, 75%, 93%)"
+ },
+ "documentId": documentId
+ }
+ ]
+};
+
+const response = await fetch(`https://www.turbodocx.com/turbosign/documents/${documentId}/update-with-recipients`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': 'Bearer YOUR_API_TOKEN',
+ 'x-rapiddocx-org-id': 'YOUR_ORGANIZATION_ID',
+ 'origin': 'https://www.turbodocx.com',
+ 'referer': 'https://www.turbodocx.com',
+ 'accept': 'application/json, text/plain, */*',
+ 'dnt': '1',
+ 'accept-language': 'en-US,en;q=0.9',
+ 'priority': 'u=1, i'
+ },
+ body: JSON.stringify(payload)
+});
+
+const result = await response.json();
+console.log(result);
\ No newline at end of file
diff --git a/static/scripts/turbosign/api/step2-recipients/php.php b/static/scripts/turbosign/api/step2-recipients/php.php
new file mode 100644
index 0000000..8e36d07
--- /dev/null
+++ b/static/scripts/turbosign/api/step2-recipients/php.php
@@ -0,0 +1,62 @@
+ [
+ 'name' => 'Contract Agreement - Updated',
+ 'description' => 'This document requires electronic signatures from both parties. Please review all content carefully before signing.'
+ ],
+ 'recipients' => [
+ [
+ 'name' => 'John Smith',
+ 'email' => 'john.smith@company.com',
+ 'signingOrder' => 1,
+ 'metadata' => [
+ 'color' => 'hsl(200, 75%, 50%)',
+ 'lightColor' => 'hsl(200, 75%, 93%)'
+ ],
+ 'documentId' => $document_id
+ ],
+ [
+ 'name' => 'Jane Doe',
+ 'email' => 'jane.doe@partner.com',
+ 'signingOrder' => 2,
+ 'metadata' => [
+ 'color' => 'hsl(270, 75%, 50%)',
+ 'lightColor' => 'hsl(270, 75%, 93%)'
+ ],
+ 'documentId' => $document_id
+ ]
+ ]
+];
+
+$ch = curl_init();
+curl_setopt_array($ch, [
+ CURLOPT_URL => $url,
+ CURLOPT_POST => true,
+ CURLOPT_POSTFIELDS => json_encode($payload),
+ CURLOPT_HTTPHEADER => $headers,
+ CURLOPT_RETURNTRANSFER => true
+]);
+
+$response = curl_exec($ch);
+curl_close($ch);
+
+$result = json_decode($response, true);
+print_r($result);
+?>
\ No newline at end of file
diff --git a/static/scripts/turbosign/api/step2-recipients/powershell.ps1 b/static/scripts/turbosign/api/step2-recipients/powershell.ps1
new file mode 100644
index 0000000..1baf5d8
--- /dev/null
+++ b/static/scripts/turbosign/api/step2-recipients/powershell.ps1
@@ -0,0 +1,43 @@
+# Step 2: Add Recipients
+$documentId = "4a20eca5-7944-430c-97d5-fcce4be24296"
+
+$payload = @{
+ document = @{
+ name = "Contract Agreement - Updated"
+ description = "This document requires electronic signatures from both parties. Please review all content carefully before signing."
+ }
+ recipients = @(
+ @{
+ name = "John Smith"
+ email = "john.smith@company.com"
+ signingOrder = 1
+ metadata = @{
+ color = "hsl(200, 75%, 50%)"
+ lightColor = "hsl(200, 75%, 93%)"
+ }
+ documentId = $documentId
+ },
+ @{
+ name = "Jane Doe"
+ email = "jane.doe@partner.com"
+ signingOrder = 2
+ metadata = @{
+ color = "hsl(270, 75%, 50%)"
+ lightColor = "hsl(270, 75%, 93%)"
+ }
+ documentId = $documentId
+ }
+ )
+} | ConvertTo-Json -Depth 10
+
+$headers = @{
+ 'Content-Type' = 'application/json'
+ 'Authorization' = 'Bearer YOUR_API_TOKEN'
+ 'x-rapiddocx-org-id' = 'YOUR_ORGANIZATION_ID'
+ 'origin' = 'https://www.turbodocx.com'
+ 'referer' = 'https://www.turbodocx.com'
+ 'accept' = 'application/json, text/plain, */*'
+}
+
+$response = Invoke-RestMethod -Uri "https://www.turbodocx.com/turbosign/documents/$documentId/update-with-recipients" -Method Post -Body $payload -Headers $headers -ContentType 'application/json'
+$response | ConvertTo-Json
\ No newline at end of file
diff --git a/static/scripts/turbosign/api/step2-recipients/python/fastapi.py b/static/scripts/turbosign/api/step2-recipients/python/fastapi.py
new file mode 100644
index 0000000..acaccfb
--- /dev/null
+++ b/static/scripts/turbosign/api/step2-recipients/python/fastapi.py
@@ -0,0 +1,52 @@
+import requests
+import json
+
+# Step 2: Add Recipients
+document_id = "4a20eca5-7944-430c-97d5-fcce4be24296"
+
+url = f"https://www.turbodocx.com/turbosign/documents/{document_id}/update-with-recipients"
+
+headers = {
+ "Content-Type": "application/json",
+ "Authorization": "Bearer YOUR_API_TOKEN",
+ "x-rapiddocx-org-id": "YOUR_ORGANIZATION_ID",
+ "origin": "https://www.turbodocx.com",
+ "referer": "https://www.turbodocx.com",
+ "accept": "application/json, text/plain, */*",
+ "dnt": "1",
+ "accept-language": "en-US,en;q=0.9",
+ "priority": "u=1, i"
+}
+
+payload = {
+ "document": {
+ "name": "Contract Agreement - Updated",
+ "description": "This document requires electronic signatures from both parties. Please review all content carefully before signing."
+ },
+ "recipients": [
+ {
+ "name": "John Smith",
+ "email": "john.smith@company.com",
+ "signingOrder": 1,
+ "metadata": {
+ "color": "hsl(200, 75%, 50%)",
+ "lightColor": "hsl(200, 75%, 93%)"
+ },
+ "documentId": document_id
+ },
+ {
+ "name": "Jane Doe",
+ "email": "jane.doe@partner.com",
+ "signingOrder": 2,
+ "metadata": {
+ "color": "hsl(270, 75%, 50%)",
+ "lightColor": "hsl(270, 75%, 93%)"
+ },
+ "documentId": document_id
+ }
+ ]
+}
+
+response = requests.post(url, headers=headers, json=payload)
+result = response.json()
+print(result)
\ No newline at end of file
diff --git a/static/scripts/turbosign/api/step2-recipients/python/flask.py b/static/scripts/turbosign/api/step2-recipients/python/flask.py
new file mode 100644
index 0000000..acaccfb
--- /dev/null
+++ b/static/scripts/turbosign/api/step2-recipients/python/flask.py
@@ -0,0 +1,52 @@
+import requests
+import json
+
+# Step 2: Add Recipients
+document_id = "4a20eca5-7944-430c-97d5-fcce4be24296"
+
+url = f"https://www.turbodocx.com/turbosign/documents/{document_id}/update-with-recipients"
+
+headers = {
+ "Content-Type": "application/json",
+ "Authorization": "Bearer YOUR_API_TOKEN",
+ "x-rapiddocx-org-id": "YOUR_ORGANIZATION_ID",
+ "origin": "https://www.turbodocx.com",
+ "referer": "https://www.turbodocx.com",
+ "accept": "application/json, text/plain, */*",
+ "dnt": "1",
+ "accept-language": "en-US,en;q=0.9",
+ "priority": "u=1, i"
+}
+
+payload = {
+ "document": {
+ "name": "Contract Agreement - Updated",
+ "description": "This document requires electronic signatures from both parties. Please review all content carefully before signing."
+ },
+ "recipients": [
+ {
+ "name": "John Smith",
+ "email": "john.smith@company.com",
+ "signingOrder": 1,
+ "metadata": {
+ "color": "hsl(200, 75%, 50%)",
+ "lightColor": "hsl(200, 75%, 93%)"
+ },
+ "documentId": document_id
+ },
+ {
+ "name": "Jane Doe",
+ "email": "jane.doe@partner.com",
+ "signingOrder": 2,
+ "metadata": {
+ "color": "hsl(270, 75%, 50%)",
+ "lightColor": "hsl(270, 75%, 93%)"
+ },
+ "documentId": document_id
+ }
+ ]
+}
+
+response = requests.post(url, headers=headers, json=payload)
+result = response.json()
+print(result)
\ No newline at end of file
diff --git a/static/scripts/turbosign/api/step2-recipients/ruby.rb b/static/scripts/turbosign/api/step2-recipients/ruby.rb
new file mode 100644
index 0000000..51e44fb
--- /dev/null
+++ b/static/scripts/turbosign/api/step2-recipients/ruby.rb
@@ -0,0 +1,52 @@
+require 'net/http'
+require 'uri'
+require 'json'
+
+# Step 2: Add Recipients
+document_id = "4a20eca5-7944-430c-97d5-fcce4be24296"
+
+payload = {
+ "document" => {
+ "name" => "Contract Agreement - Updated",
+ "description" => "This document requires electronic signatures from both parties. Please review all content carefully before signing."
+ },
+ "recipients" => [
+ {
+ "name" => "John Smith",
+ "email" => "john.smith@company.com",
+ "signingOrder" => 1,
+ "metadata" => {
+ "color" => "hsl(200, 75%, 50%)",
+ "lightColor" => "hsl(200, 75%, 93%)"
+ },
+ "documentId" => document_id
+ },
+ {
+ "name" => "Jane Doe",
+ "email" => "jane.doe@partner.com",
+ "signingOrder" => 2,
+ "metadata" => {
+ "color" => "hsl(270, 75%, 50%)",
+ "lightColor" => "hsl(270, 75%, 93%)"
+ },
+ "documentId" => document_id
+ }
+ ]
+}
+
+uri = URI("https://www.turbodocx.com/turbosign/documents/#{document_id}/update-with-recipients")
+
+http = Net::HTTP.new(uri.host, uri.port)
+http.use_ssl = true
+
+request = Net::HTTP::Post.new(uri)
+request['Content-Type'] = 'application/json'
+request['Authorization'] = 'Bearer YOUR_API_TOKEN'
+request['x-rapiddocx-org-id'] = 'YOUR_ORGANIZATION_ID'
+request['origin'] = 'https://www.turbodocx.com'
+request['referer'] = 'https://www.turbodocx.com'
+request['accept'] = 'application/json, text/plain, */*'
+request.body = payload.to_json
+
+response = http.request(request)
+puts response.body
\ No newline at end of file
diff --git a/static/scripts/turbosign/api/step3-prepare/csharp/controller.cs b/static/scripts/turbosign/api/step3-prepare/csharp/controller.cs
new file mode 100644
index 0000000..2f8d6aa
--- /dev/null
+++ b/static/scripts/turbosign/api/step3-prepare/csharp/controller.cs
@@ -0,0 +1,86 @@
+using System;
+using System.Net.Http;
+using System.Text;
+using System.Threading.Tasks;
+
+class Program
+{
+ static async Task Main(string[] args)
+ {
+ // Step 3: Prepare for Signing
+ var documentId = "4a20eca5-7944-430c-97d5-fcce4be24296";
+
+ using var client = new HttpClient();
+
+ client.DefaultRequestHeaders.Add("Authorization", "Bearer YOUR_API_TOKEN");
+ client.DefaultRequestHeaders.Add("x-rapiddocx-org-id", "YOUR_ORGANIZATION_ID");
+ client.DefaultRequestHeaders.Add("origin", "https://www.turbodocx.com");
+ client.DefaultRequestHeaders.Add("referer", "https://www.turbodocx.com");
+ client.DefaultRequestHeaders.Add("accept", "application/json, text/plain, */*");
+
+ var payload = @"[
+ {
+ ""recipientId"": ""5f673f37-9912-4e72-85aa-8f3649760f6b"",
+ ""type"": ""signature"",
+ ""template"": {
+ ""anchor"": ""{Signature1}"",
+ ""placement"": ""replace"",
+ ""size"": { ""width"": 200, ""height"": 80 },
+ ""offset"": { ""x"": 0, ""y"": 0 },
+ ""caseSensitive"": true,
+ ""useRegex"": false
+ },
+ ""defaultValue"": """",
+ ""required"": true
+ },
+ {
+ ""recipientId"": ""5f673f37-9912-4e72-85aa-8f3649760f6b"",
+ ""type"": ""date"",
+ ""template"": {
+ ""anchor"": ""{Date1}"",
+ ""placement"": ""replace"",
+ ""size"": { ""width"": 150, ""height"": 30 },
+ ""offset"": { ""x"": 0, ""y"": 0 },
+ ""caseSensitive"": true,
+ ""useRegex"": false
+ },
+ ""defaultValue"": """",
+ ""required"": true
+ },
+ {
+ ""recipientId"": ""a8b9c1d2-3456-7890-abcd-ef1234567890"",
+ ""type"": ""signature"",
+ ""template"": {
+ ""anchor"": ""{Signature2}"",
+ ""placement"": ""replace"",
+ ""size"": { ""width"": 200, ""height"": 80 },
+ ""offset"": { ""x"": 0, ""y"": 0 },
+ ""caseSensitive"": true,
+ ""useRegex"": false
+ },
+ ""defaultValue"": """",
+ ""required"": true
+ },
+ {
+ ""recipientId"": ""a8b9c1d2-3456-7890-abcd-ef1234567890"",
+ ""type"": ""text"",
+ ""template"": {
+ ""anchor"": ""{Title2}"",
+ ""placement"": ""replace"",
+ ""size"": { ""width"": 200, ""height"": 30 },
+ ""offset"": { ""x"": 0, ""y"": 0 },
+ ""caseSensitive"": true,
+ ""useRegex"": false
+ },
+ ""defaultValue"": ""Business Partner"",
+ ""required"": false
+ }
+ ]";
+
+ var content = new StringContent(payload, Encoding.UTF8, "application/json");
+ var response = await client.PostAsync($"https://www.turbodocx.com/turbosign/documents/{documentId}/prepare-for-signing", content);
+ var result = await response.Content.ReadAsStringAsync();
+
+ Console.WriteLine(result);
+ }
+}
\ No newline at end of file
diff --git a/static/scripts/turbosign/api/step3-prepare/csharp/minimal.cs b/static/scripts/turbosign/api/step3-prepare/csharp/minimal.cs
new file mode 100644
index 0000000..2f8d6aa
--- /dev/null
+++ b/static/scripts/turbosign/api/step3-prepare/csharp/minimal.cs
@@ -0,0 +1,86 @@
+using System;
+using System.Net.Http;
+using System.Text;
+using System.Threading.Tasks;
+
+class Program
+{
+ static async Task Main(string[] args)
+ {
+ // Step 3: Prepare for Signing
+ var documentId = "4a20eca5-7944-430c-97d5-fcce4be24296";
+
+ using var client = new HttpClient();
+
+ client.DefaultRequestHeaders.Add("Authorization", "Bearer YOUR_API_TOKEN");
+ client.DefaultRequestHeaders.Add("x-rapiddocx-org-id", "YOUR_ORGANIZATION_ID");
+ client.DefaultRequestHeaders.Add("origin", "https://www.turbodocx.com");
+ client.DefaultRequestHeaders.Add("referer", "https://www.turbodocx.com");
+ client.DefaultRequestHeaders.Add("accept", "application/json, text/plain, */*");
+
+ var payload = @"[
+ {
+ ""recipientId"": ""5f673f37-9912-4e72-85aa-8f3649760f6b"",
+ ""type"": ""signature"",
+ ""template"": {
+ ""anchor"": ""{Signature1}"",
+ ""placement"": ""replace"",
+ ""size"": { ""width"": 200, ""height"": 80 },
+ ""offset"": { ""x"": 0, ""y"": 0 },
+ ""caseSensitive"": true,
+ ""useRegex"": false
+ },
+ ""defaultValue"": """",
+ ""required"": true
+ },
+ {
+ ""recipientId"": ""5f673f37-9912-4e72-85aa-8f3649760f6b"",
+ ""type"": ""date"",
+ ""template"": {
+ ""anchor"": ""{Date1}"",
+ ""placement"": ""replace"",
+ ""size"": { ""width"": 150, ""height"": 30 },
+ ""offset"": { ""x"": 0, ""y"": 0 },
+ ""caseSensitive"": true,
+ ""useRegex"": false
+ },
+ ""defaultValue"": """",
+ ""required"": true
+ },
+ {
+ ""recipientId"": ""a8b9c1d2-3456-7890-abcd-ef1234567890"",
+ ""type"": ""signature"",
+ ""template"": {
+ ""anchor"": ""{Signature2}"",
+ ""placement"": ""replace"",
+ ""size"": { ""width"": 200, ""height"": 80 },
+ ""offset"": { ""x"": 0, ""y"": 0 },
+ ""caseSensitive"": true,
+ ""useRegex"": false
+ },
+ ""defaultValue"": """",
+ ""required"": true
+ },
+ {
+ ""recipientId"": ""a8b9c1d2-3456-7890-abcd-ef1234567890"",
+ ""type"": ""text"",
+ ""template"": {
+ ""anchor"": ""{Title2}"",
+ ""placement"": ""replace"",
+ ""size"": { ""width"": 200, ""height"": 30 },
+ ""offset"": { ""x"": 0, ""y"": 0 },
+ ""caseSensitive"": true,
+ ""useRegex"": false
+ },
+ ""defaultValue"": ""Business Partner"",
+ ""required"": false
+ }
+ ]";
+
+ var content = new StringContent(payload, Encoding.UTF8, "application/json");
+ var response = await client.PostAsync($"https://www.turbodocx.com/turbosign/documents/{documentId}/prepare-for-signing", content);
+ var result = await response.Content.ReadAsStringAsync();
+
+ Console.WriteLine(result);
+ }
+}
\ No newline at end of file
diff --git a/static/scripts/turbosign/api/step3-prepare/go.go b/static/scripts/turbosign/api/step3-prepare/go.go
new file mode 100644
index 0000000..d13632d
--- /dev/null
+++ b/static/scripts/turbosign/api/step3-prepare/go.go
@@ -0,0 +1,87 @@
+package main
+
+import (
+ "bytes"
+ "fmt"
+ "net/http"
+)
+
+func main() {
+ // Step 3: Prepare for Signing
+ documentID := "4a20eca5-7944-430c-97d5-fcce4be24296"
+
+ payload := `[
+ {
+ "recipientId": "5f673f37-9912-4e72-85aa-8f3649760f6b",
+ "type": "signature",
+ "template": {
+ "anchor": "{Signature1}",
+ "placement": "replace",
+ "size": { "width": 200, "height": 80 },
+ "offset": { "x": 0, "y": 0 },
+ "caseSensitive": true,
+ "useRegex": false
+ },
+ "defaultValue": "",
+ "required": true
+ },
+ {
+ "recipientId": "5f673f37-9912-4e72-85aa-8f3649760f6b",
+ "type": "date",
+ "template": {
+ "anchor": "{Date1}",
+ "placement": "replace",
+ "size": { "width": 150, "height": 30 },
+ "offset": { "x": 0, "y": 0 },
+ "caseSensitive": true,
+ "useRegex": false
+ },
+ "defaultValue": "",
+ "required": true
+ },
+ {
+ "recipientId": "a8b9c1d2-3456-7890-abcd-ef1234567890",
+ "type": "signature",
+ "template": {
+ "anchor": "{Signature2}",
+ "placement": "replace",
+ "size": { "width": 200, "height": 80 },
+ "offset": { "x": 0, "y": 0 },
+ "caseSensitive": true,
+ "useRegex": false
+ },
+ "defaultValue": "",
+ "required": true
+ },
+ {
+ "recipientId": "a8b9c1d2-3456-7890-abcd-ef1234567890",
+ "type": "text",
+ "template": {
+ "anchor": "{Title2}",
+ "placement": "replace",
+ "size": { "width": 200, "height": 30 },
+ "offset": { "x": 0, "y": 0 },
+ "caseSensitive": true,
+ "useRegex": false
+ },
+ "defaultValue": "Business Partner",
+ "required": false
+ }
+ ]`
+
+ req, _ := http.NewRequest("POST", fmt.Sprintf("https://www.turbodocx.com/turbosign/documents/%s/prepare-for-signing", documentID), bytes.NewBufferString(payload))
+ req.Header.Set("Content-Type", "application/json")
+ req.Header.Set("Authorization", "Bearer YOUR_API_TOKEN")
+ req.Header.Set("x-rapiddocx-org-id", "YOUR_ORGANIZATION_ID")
+ req.Header.Set("origin", "https://www.turbodocx.com")
+ req.Header.Set("referer", "https://www.turbodocx.com")
+ req.Header.Set("accept", "application/json, text/plain, */*")
+
+ client := &http.Client{}
+ resp, _ := client.Do(req)
+ defer resp.Body.Close()
+
+ body := make([]byte, 1024)
+ resp.Body.Read(body)
+ fmt.Println(string(body))
+}
\ No newline at end of file
diff --git a/static/scripts/turbosign/api/step3-prepare/java.java b/static/scripts/turbosign/api/step3-prepare/java.java
new file mode 100644
index 0000000..20bfe8e
--- /dev/null
+++ b/static/scripts/turbosign/api/step3-prepare/java.java
@@ -0,0 +1,86 @@
+import java.net.http.*;
+import java.net.URI;
+
+public class TurboSignPrepare {
+ public static void main(String[] args) throws Exception {
+ // Step 3: Prepare for Signing
+ String documentId = "4a20eca5-7944-430c-97d5-fcce4be24296";
+
+ HttpClient client = HttpClient.newHttpClient();
+
+ String payload = """
+ [
+ {
+ "recipientId": "5f673f37-9912-4e72-85aa-8f3649760f6b",
+ "type": "signature",
+ "template": {
+ "anchor": "{Signature1}",
+ "placement": "replace",
+ "size": { "width": 200, "height": 80 },
+ "offset": { "x": 0, "y": 0 },
+ "caseSensitive": true,
+ "useRegex": false
+ },
+ "defaultValue": "",
+ "required": true
+ },
+ {
+ "recipientId": "5f673f37-9912-4e72-85aa-8f3649760f6b",
+ "type": "date",
+ "template": {
+ "anchor": "{Date1}",
+ "placement": "replace",
+ "size": { "width": 150, "height": 30 },
+ "offset": { "x": 0, "y": 0 },
+ "caseSensitive": true,
+ "useRegex": false
+ },
+ "defaultValue": "",
+ "required": true
+ },
+ {
+ "recipientId": "a8b9c1d2-3456-7890-abcd-ef1234567890",
+ "type": "signature",
+ "template": {
+ "anchor": "{Signature2}",
+ "placement": "replace",
+ "size": { "width": 200, "height": 80 },
+ "offset": { "x": 0, "y": 0 },
+ "caseSensitive": true,
+ "useRegex": false
+ },
+ "defaultValue": "",
+ "required": true
+ },
+ {
+ "recipientId": "a8b9c1d2-3456-7890-abcd-ef1234567890",
+ "type": "text",
+ "template": {
+ "anchor": "{Title2}",
+ "placement": "replace",
+ "size": { "width": 200, "height": 30 },
+ "offset": { "x": 0, "y": 0 },
+ "caseSensitive": true,
+ "useRegex": false
+ },
+ "defaultValue": "Business Partner",
+ "required": false
+ }
+ ]
+ """;
+
+ HttpRequest request = HttpRequest.newBuilder()
+ .uri(URI.create("https://www.turbodocx.com/turbosign/documents/" + documentId + "/prepare-for-signing"))
+ .header("Content-Type", "application/json")
+ .header("Authorization", "Bearer YOUR_API_TOKEN")
+ .header("x-rapiddocx-org-id", "YOUR_ORGANIZATION_ID")
+ .header("origin", "https://www.turbodocx.com")
+ .header("referer", "https://www.turbodocx.com")
+ .header("accept", "application/json, text/plain, */*")
+ .POST(HttpRequest.BodyPublishers.ofString(payload))
+ .build();
+
+ HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
+ System.out.println(response.body());
+ }
+}
\ No newline at end of file
diff --git a/static/scripts/turbosign/api/step3-prepare/nodejs/express.js b/static/scripts/turbosign/api/step3-prepare/nodejs/express.js
new file mode 100644
index 0000000..b32da5f
--- /dev/null
+++ b/static/scripts/turbosign/api/step3-prepare/nodejs/express.js
@@ -0,0 +1,90 @@
+const fetch = require('node-fetch');
+
+// Step 3: Prepare for Signing
+const documentId = "4a20eca5-7944-430c-97d5-fcce4be24296";
+
+const signatureFields = [
+ {
+ "recipientId": "5f673f37-9912-4e72-85aa-8f3649760f6b",
+ "type": "signature",
+ "template": {
+ "anchor": "{Signature1}",
+ "placement": "replace",
+ "size": { "width": 200, "height": 80 },
+ "offset": { "x": 0, "y": 0 },
+ "caseSensitive": true,
+ "useRegex": false
+ },
+ "defaultValue": "",
+ "required": true
+ },
+ {
+ "recipientId": "5f673f37-9912-4e72-85aa-8f3649760f6b",
+ "type": "date",
+ "template": {
+ "anchor": "{Date1}",
+ "placement": "replace",
+ "size": { "width": 150, "height": 30 },
+ "offset": { "x": 0, "y": 0 },
+ "caseSensitive": true,
+ "useRegex": false
+ },
+ "defaultValue": "",
+ "required": true
+ },
+ {
+ "recipientId": "a8b9c1d2-3456-7890-abcd-ef1234567890",
+ "type": "signature",
+ "template": {
+ "anchor": "{Signature2}",
+ "placement": "replace",
+ "size": { "width": 200, "height": 80 },
+ "offset": { "x": 0, "y": 0 },
+ "caseSensitive": true,
+ "useRegex": false
+ },
+ "defaultValue": "",
+ "required": true
+ },
+ {
+ "recipientId": "a8b9c1d2-3456-7890-abcd-ef1234567890",
+ "type": "text",
+ "template": {
+ "anchor": "{Title2}",
+ "placement": "replace",
+ "size": { "width": 200, "height": 30 },
+ "offset": { "x": 0, "y": 0 },
+ "caseSensitive": true,
+ "useRegex": false
+ },
+ "defaultValue": "Business Partner",
+ "required": false
+ }
+];
+
+const response = await fetch(`https://www.turbodocx.com/turbosign/documents/${documentId}/prepare-for-signing`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': 'Bearer YOUR_API_TOKEN',
+ 'x-rapiddocx-org-id': 'YOUR_ORGANIZATION_ID',
+ 'origin': 'https://www.turbodocx.com',
+ 'referer': 'https://www.turbodocx.com',
+ 'accept': 'application/json, text/plain, */*',
+ 'dnt': '1',
+ 'accept-language': 'en-US,en;q=0.9',
+ 'priority': 'u=1, i',
+ 'sec-ch-ua': '"Not)A;Brand";v="8", "Chromium";v="138", "Google Chrome";v="138"',
+ 'sec-ch-ua-mobile': '?0',
+ 'sec-ch-ua-platform': '"Windows"',
+ 'sec-fetch-dest': 'empty',
+ 'sec-fetch-mode': 'cors',
+ 'sec-fetch-site': 'same-site',
+ 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36',
+ 'x-device-fingerprint': '280624a233f1fd39ce050a9e9d0a4cc9'
+ },
+ body: JSON.stringify(signatureFields)
+});
+
+const result = await response.json();
+console.log(result);
\ No newline at end of file
diff --git a/static/scripts/turbosign/api/step3-prepare/nodejs/fastify.js b/static/scripts/turbosign/api/step3-prepare/nodejs/fastify.js
new file mode 100644
index 0000000..b32da5f
--- /dev/null
+++ b/static/scripts/turbosign/api/step3-prepare/nodejs/fastify.js
@@ -0,0 +1,90 @@
+const fetch = require('node-fetch');
+
+// Step 3: Prepare for Signing
+const documentId = "4a20eca5-7944-430c-97d5-fcce4be24296";
+
+const signatureFields = [
+ {
+ "recipientId": "5f673f37-9912-4e72-85aa-8f3649760f6b",
+ "type": "signature",
+ "template": {
+ "anchor": "{Signature1}",
+ "placement": "replace",
+ "size": { "width": 200, "height": 80 },
+ "offset": { "x": 0, "y": 0 },
+ "caseSensitive": true,
+ "useRegex": false
+ },
+ "defaultValue": "",
+ "required": true
+ },
+ {
+ "recipientId": "5f673f37-9912-4e72-85aa-8f3649760f6b",
+ "type": "date",
+ "template": {
+ "anchor": "{Date1}",
+ "placement": "replace",
+ "size": { "width": 150, "height": 30 },
+ "offset": { "x": 0, "y": 0 },
+ "caseSensitive": true,
+ "useRegex": false
+ },
+ "defaultValue": "",
+ "required": true
+ },
+ {
+ "recipientId": "a8b9c1d2-3456-7890-abcd-ef1234567890",
+ "type": "signature",
+ "template": {
+ "anchor": "{Signature2}",
+ "placement": "replace",
+ "size": { "width": 200, "height": 80 },
+ "offset": { "x": 0, "y": 0 },
+ "caseSensitive": true,
+ "useRegex": false
+ },
+ "defaultValue": "",
+ "required": true
+ },
+ {
+ "recipientId": "a8b9c1d2-3456-7890-abcd-ef1234567890",
+ "type": "text",
+ "template": {
+ "anchor": "{Title2}",
+ "placement": "replace",
+ "size": { "width": 200, "height": 30 },
+ "offset": { "x": 0, "y": 0 },
+ "caseSensitive": true,
+ "useRegex": false
+ },
+ "defaultValue": "Business Partner",
+ "required": false
+ }
+];
+
+const response = await fetch(`https://www.turbodocx.com/turbosign/documents/${documentId}/prepare-for-signing`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': 'Bearer YOUR_API_TOKEN',
+ 'x-rapiddocx-org-id': 'YOUR_ORGANIZATION_ID',
+ 'origin': 'https://www.turbodocx.com',
+ 'referer': 'https://www.turbodocx.com',
+ 'accept': 'application/json, text/plain, */*',
+ 'dnt': '1',
+ 'accept-language': 'en-US,en;q=0.9',
+ 'priority': 'u=1, i',
+ 'sec-ch-ua': '"Not)A;Brand";v="8", "Chromium";v="138", "Google Chrome";v="138"',
+ 'sec-ch-ua-mobile': '?0',
+ 'sec-ch-ua-platform': '"Windows"',
+ 'sec-fetch-dest': 'empty',
+ 'sec-fetch-mode': 'cors',
+ 'sec-fetch-site': 'same-site',
+ 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36',
+ 'x-device-fingerprint': '280624a233f1fd39ce050a9e9d0a4cc9'
+ },
+ body: JSON.stringify(signatureFields)
+});
+
+const result = await response.json();
+console.log(result);
\ No newline at end of file
diff --git a/static/scripts/turbosign/api/step3-prepare/php.php b/static/scripts/turbosign/api/step3-prepare/php.php
new file mode 100644
index 0000000..4ff7b0f
--- /dev/null
+++ b/static/scripts/turbosign/api/step3-prepare/php.php
@@ -0,0 +1,95 @@
+ "5f673f37-9912-4e72-85aa-8f3649760f6b",
+ "type" => "signature",
+ "template" => [
+ "anchor" => "{Signature1}",
+ "placement" => "replace",
+ "size" => ["width" => 200, "height" => 80],
+ "offset" => ["x" => 0, "y" => 0],
+ "caseSensitive" => true,
+ "useRegex" => false
+ ],
+ "defaultValue" => "",
+ "required" => true
+ ],
+ [
+ "recipientId" => "5f673f37-9912-4e72-85aa-8f3649760f6b",
+ "type" => "date",
+ "template" => [
+ "anchor" => "{Date1}",
+ "placement" => "replace",
+ "size" => ["width" => 150, "height" => 30],
+ "offset" => ["x" => 0, "y" => 0],
+ "caseSensitive" => true,
+ "useRegex" => false
+ ],
+ "defaultValue" => "",
+ "required" => true
+ ],
+ [
+ "recipientId" => "a8b9c1d2-3456-7890-abcd-ef1234567890",
+ "type" => "signature",
+ "template" => [
+ "anchor" => "{Signature2}",
+ "placement" => "replace",
+ "size" => ["width" => 200, "height" => 80],
+ "offset" => ["x" => 0, "y" => 0],
+ "caseSensitive" => true,
+ "useRegex" => false
+ ],
+ "defaultValue" => "",
+ "required" => true
+ ],
+ [
+ "recipientId" => "a8b9c1d2-3456-7890-abcd-ef1234567890",
+ "type" => "text",
+ "template" => [
+ "anchor" => "{Title2}",
+ "placement" => "replace",
+ "size" => ["width" => 200, "height" => 30],
+ "offset" => ["x" => 0, "y" => 0],
+ "caseSensitive" => true,
+ "useRegex" => false
+ ],
+ "defaultValue" => "Business Partner",
+ "required" => false
+ ]
+];
+
+$ch = curl_init();
+curl_setopt_array($ch, [
+ CURLOPT_URL => "https://www.turbodocx.com/turbosign/documents/$document_id/prepare-for-signing",
+ CURLOPT_POST => true,
+ CURLOPT_POSTFIELDS => json_encode($signature_fields),
+ CURLOPT_HTTPHEADER => [
+ 'Content-Type: application/json',
+ 'Authorization: Bearer YOUR_API_TOKEN',
+ 'x-rapiddocx-org-id: YOUR_ORGANIZATION_ID',
+ 'origin: https://www.turbodocx.com',
+ 'referer: https://www.turbodocx.com',
+ 'accept: application/json, text/plain, */*',
+ 'dnt: 1',
+ 'accept-language: en-US,en;q=0.9',
+ 'priority: u=1, i',
+ 'sec-ch-ua: "Not)A;Brand";v="8", "Chromium";v="138", "Google Chrome";v="138"',
+ 'sec-ch-ua-mobile: ?0',
+ 'sec-ch-ua-platform: "Windows"',
+ 'sec-fetch-dest: empty',
+ 'sec-fetch-mode: cors',
+ 'sec-fetch-site: same-site',
+ 'user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36',
+ 'x-device-fingerprint: 280624a233f1fd39ce050a9e9d0a4cc9'
+ ],
+ CURLOPT_RETURNTRANSFER => true
+]);
+
+$response = curl_exec($ch);
+curl_close($ch);
+
+$result = json_decode($response, true);
+print_r($result);
+?>
\ No newline at end of file
diff --git a/static/scripts/turbosign/api/step3-prepare/powershell.ps1 b/static/scripts/turbosign/api/step3-prepare/powershell.ps1
new file mode 100644
index 0000000..d5059f8
--- /dev/null
+++ b/static/scripts/turbosign/api/step3-prepare/powershell.ps1
@@ -0,0 +1,73 @@
+# Step 3: Prepare for Signing
+$documentId = "4a20eca5-7944-430c-97d5-fcce4be24296"
+
+$signatureFields = @(
+ @{
+ recipientId = "5f673f37-9912-4e72-85aa-8f3649760f6b"
+ type = "signature"
+ template = @{
+ anchor = "{Signature1}"
+ placement = "replace"
+ size = @{ width = 200; height = 80 }
+ offset = @{ x = 0; y = 0 }
+ caseSensitive = $true
+ useRegex = $false
+ }
+ defaultValue = ""
+ required = $true
+ },
+ @{
+ recipientId = "5f673f37-9912-4e72-85aa-8f3649760f6b"
+ type = "date"
+ template = @{
+ anchor = "{Date1}"
+ placement = "replace"
+ size = @{ width = 150; height = 30 }
+ offset = @{ x = 0; y = 0 }
+ caseSensitive = $true
+ useRegex = $false
+ }
+ defaultValue = ""
+ required = $true
+ },
+ @{
+ recipientId = "a8b9c1d2-3456-7890-abcd-ef1234567890"
+ type = "signature"
+ template = @{
+ anchor = "{Signature2}"
+ placement = "replace"
+ size = @{ width = 200; height = 80 }
+ offset = @{ x = 0; y = 0 }
+ caseSensitive = $true
+ useRegex = $false
+ }
+ defaultValue = ""
+ required = $true
+ },
+ @{
+ recipientId = "a8b9c1d2-3456-7890-abcd-ef1234567890"
+ type = "text"
+ template = @{
+ anchor = "{Title2}"
+ placement = "replace"
+ size = @{ width = 200; height = 30 }
+ offset = @{ x = 0; y = 0 }
+ caseSensitive = $true
+ useRegex = $false
+ }
+ defaultValue = "Business Partner"
+ required = $false
+ }
+) | ConvertTo-Json -Depth 10
+
+$headers = @{
+ 'Content-Type' = 'application/json'
+ 'Authorization' = 'Bearer YOUR_API_TOKEN'
+ 'x-rapiddocx-org-id' = 'YOUR_ORGANIZATION_ID'
+ 'origin' = 'https://www.turbodocx.com'
+ 'referer' = 'https://www.turbodocx.com'
+ 'accept' = 'application/json, text/plain, */*'
+}
+
+$response = Invoke-RestMethod -Uri "https://www.turbodocx.com/turbosign/documents/$documentId/prepare-for-signing" -Method Post -Body $signatureFields -Headers $headers -ContentType 'application/json'
+$response | ConvertTo-Json
\ No newline at end of file
diff --git a/static/scripts/turbosign/api/step3-prepare/python/fastapi.py b/static/scripts/turbosign/api/step3-prepare/python/fastapi.py
new file mode 100644
index 0000000..471f345
--- /dev/null
+++ b/static/scripts/turbosign/api/step3-prepare/python/fastapi.py
@@ -0,0 +1,90 @@
+import requests
+import json
+
+# Step 3: Prepare for Signing
+document_id = "4a20eca5-7944-430c-97d5-fcce4be24296"
+
+url = f"https://www.turbodocx.com/turbosign/documents/{document_id}/prepare-for-signing"
+
+headers = {
+ "Content-Type": "application/json",
+ "Authorization": "Bearer YOUR_API_TOKEN",
+ "x-rapiddocx-org-id": "YOUR_ORGANIZATION_ID",
+ "origin": "https://www.turbodocx.com",
+ "referer": "https://www.turbodocx.com",
+ "accept": "application/json, text/plain, */*",
+ "dnt": "1",
+ "accept-language": "en-US,en;q=0.9",
+ "priority": "u=1, i",
+ "sec-ch-ua": "\"Not)A;Brand\";v=\"8\", \"Chromium\";v=\"138\", \"Google Chrome\";v=\"138\"",
+ "sec-ch-ua-mobile": "?0",
+ "sec-ch-ua-platform": "\"Windows\"",
+ "sec-fetch-dest": "empty",
+ "sec-fetch-mode": "cors",
+ "sec-fetch-site": "same-site",
+ "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36",
+ "x-device-fingerprint": "280624a233f1fd39ce050a9e9d0a4cc9"
+}
+
+signature_fields = [
+ {
+ "recipientId": "5f673f37-9912-4e72-85aa-8f3649760f6b",
+ "type": "signature",
+ "template": {
+ "anchor": "{Signature1}",
+ "placement": "replace",
+ "size": {"width": 200, "height": 80},
+ "offset": {"x": 0, "y": 0},
+ "caseSensitive": True,
+ "useRegex": False
+ },
+ "defaultValue": "",
+ "required": True
+ },
+ {
+ "recipientId": "5f673f37-9912-4e72-85aa-8f3649760f6b",
+ "type": "date",
+ "template": {
+ "anchor": "{Date1}",
+ "placement": "replace",
+ "size": {"width": 150, "height": 30},
+ "offset": {"x": 0, "y": 0},
+ "caseSensitive": True,
+ "useRegex": False
+ },
+ "defaultValue": "",
+ "required": True
+ },
+ {
+ "recipientId": "a8b9c1d2-3456-7890-abcd-ef1234567890",
+ "type": "signature",
+ "template": {
+ "anchor": "{Signature2}",
+ "placement": "replace",
+ "size": {"width": 200, "height": 80},
+ "offset": {"x": 0, "y": 0},
+ "caseSensitive": True,
+ "useRegex": False
+ },
+ "defaultValue": "",
+ "required": True
+ },
+ {
+ "recipientId": "a8b9c1d2-3456-7890-abcd-ef1234567890",
+ "type": "text",
+ "template": {
+ "anchor": "{Title2}",
+ "placement": "replace",
+ "size": {"width": 200, "height": 30},
+ "offset": {"x": 0, "y": 0},
+ "caseSensitive": True,
+ "useRegex": False
+ },
+ "defaultValue": "Business Partner",
+ "required": False
+ }
+]
+
+response = requests.post(url, headers=headers, json=signature_fields)
+result = response.json()
+print(result)
\ No newline at end of file
diff --git a/static/scripts/turbosign/api/step3-prepare/python/flask.py b/static/scripts/turbosign/api/step3-prepare/python/flask.py
new file mode 100644
index 0000000..471f345
--- /dev/null
+++ b/static/scripts/turbosign/api/step3-prepare/python/flask.py
@@ -0,0 +1,90 @@
+import requests
+import json
+
+# Step 3: Prepare for Signing
+document_id = "4a20eca5-7944-430c-97d5-fcce4be24296"
+
+url = f"https://www.turbodocx.com/turbosign/documents/{document_id}/prepare-for-signing"
+
+headers = {
+ "Content-Type": "application/json",
+ "Authorization": "Bearer YOUR_API_TOKEN",
+ "x-rapiddocx-org-id": "YOUR_ORGANIZATION_ID",
+ "origin": "https://www.turbodocx.com",
+ "referer": "https://www.turbodocx.com",
+ "accept": "application/json, text/plain, */*",
+ "dnt": "1",
+ "accept-language": "en-US,en;q=0.9",
+ "priority": "u=1, i",
+ "sec-ch-ua": "\"Not)A;Brand\";v=\"8\", \"Chromium\";v=\"138\", \"Google Chrome\";v=\"138\"",
+ "sec-ch-ua-mobile": "?0",
+ "sec-ch-ua-platform": "\"Windows\"",
+ "sec-fetch-dest": "empty",
+ "sec-fetch-mode": "cors",
+ "sec-fetch-site": "same-site",
+ "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36",
+ "x-device-fingerprint": "280624a233f1fd39ce050a9e9d0a4cc9"
+}
+
+signature_fields = [
+ {
+ "recipientId": "5f673f37-9912-4e72-85aa-8f3649760f6b",
+ "type": "signature",
+ "template": {
+ "anchor": "{Signature1}",
+ "placement": "replace",
+ "size": {"width": 200, "height": 80},
+ "offset": {"x": 0, "y": 0},
+ "caseSensitive": True,
+ "useRegex": False
+ },
+ "defaultValue": "",
+ "required": True
+ },
+ {
+ "recipientId": "5f673f37-9912-4e72-85aa-8f3649760f6b",
+ "type": "date",
+ "template": {
+ "anchor": "{Date1}",
+ "placement": "replace",
+ "size": {"width": 150, "height": 30},
+ "offset": {"x": 0, "y": 0},
+ "caseSensitive": True,
+ "useRegex": False
+ },
+ "defaultValue": "",
+ "required": True
+ },
+ {
+ "recipientId": "a8b9c1d2-3456-7890-abcd-ef1234567890",
+ "type": "signature",
+ "template": {
+ "anchor": "{Signature2}",
+ "placement": "replace",
+ "size": {"width": 200, "height": 80},
+ "offset": {"x": 0, "y": 0},
+ "caseSensitive": True,
+ "useRegex": False
+ },
+ "defaultValue": "",
+ "required": True
+ },
+ {
+ "recipientId": "a8b9c1d2-3456-7890-abcd-ef1234567890",
+ "type": "text",
+ "template": {
+ "anchor": "{Title2}",
+ "placement": "replace",
+ "size": {"width": 200, "height": 30},
+ "offset": {"x": 0, "y": 0},
+ "caseSensitive": True,
+ "useRegex": False
+ },
+ "defaultValue": "Business Partner",
+ "required": False
+ }
+]
+
+response = requests.post(url, headers=headers, json=signature_fields)
+result = response.json()
+print(result)
\ No newline at end of file
diff --git a/static/scripts/turbosign/api/step3-prepare/ruby.rb b/static/scripts/turbosign/api/step3-prepare/ruby.rb
new file mode 100644
index 0000000..048f24a
--- /dev/null
+++ b/static/scripts/turbosign/api/step3-prepare/ruby.rb
@@ -0,0 +1,82 @@
+require 'net/http'
+require 'uri'
+require 'json'
+
+# Step 3: Prepare for Signing
+document_id = "4a20eca5-7944-430c-97d5-fcce4be24296"
+
+signature_fields = [
+ {
+ "recipientId" => "5f673f37-9912-4e72-85aa-8f3649760f6b",
+ "type" => "signature",
+ "template" => {
+ "anchor" => "{Signature1}",
+ "placement" => "replace",
+ "size" => { "width" => 200, "height" => 80 },
+ "offset" => { "x" => 0, "y" => 0 },
+ "caseSensitive" => true,
+ "useRegex" => false
+ },
+ "defaultValue" => "",
+ "required" => true
+ },
+ {
+ "recipientId" => "5f673f37-9912-4e72-85aa-8f3649760f6b",
+ "type" => "date",
+ "template" => {
+ "anchor" => "{Date1}",
+ "placement" => "replace",
+ "size" => { "width" => 150, "height" => 30 },
+ "offset" => { "x" => 0, "y" => 0 },
+ "caseSensitive" => true,
+ "useRegex" => false
+ },
+ "defaultValue" => "",
+ "required" => true
+ },
+ {
+ "recipientId" => "a8b9c1d2-3456-7890-abcd-ef1234567890",
+ "type" => "signature",
+ "template" => {
+ "anchor" => "{Signature2}",
+ "placement" => "replace",
+ "size" => { "width" => 200, "height" => 80 },
+ "offset" => { "x" => 0, "y" => 0 },
+ "caseSensitive" => true,
+ "useRegex" => false
+ },
+ "defaultValue" => "",
+ "required" => true
+ },
+ {
+ "recipientId" => "a8b9c1d2-3456-7890-abcd-ef1234567890",
+ "type" => "text",
+ "template" => {
+ "anchor" => "{Title2}",
+ "placement" => "replace",
+ "size" => { "width" => 200, "height" => 30 },
+ "offset" => { "x" => 0, "y" => 0 },
+ "caseSensitive" => true,
+ "useRegex" => false
+ },
+ "defaultValue" => "Business Partner",
+ "required" => false
+ }
+]
+
+uri = URI("https://www.turbodocx.com/turbosign/documents/#{document_id}/prepare-for-signing")
+
+http = Net::HTTP.new(uri.host, uri.port)
+http.use_ssl = true
+
+request = Net::HTTP::Post.new(uri)
+request['Content-Type'] = 'application/json'
+request['Authorization'] = 'Bearer YOUR_API_TOKEN'
+request['x-rapiddocx-org-id'] = 'YOUR_ORGANIZATION_ID'
+request['origin'] = 'https://www.turbodocx.com'
+request['referer'] = 'https://www.turbodocx.com'
+request['accept'] = 'application/json, text/plain, */*'
+request.body = signature_fields.to_json
+
+response = http.request(request)
+puts response.body
\ No newline at end of file