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. + +![TurboSign API Integration Overview](/img/turbosign/api/api-illustration.png) + +## 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 + +![TurboSign API Integration Overview](/img/turbosign/api/steps.png) + +### 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 + +![TurboSign API Integration Overview](/img/turbosign/api/api-key.png) +![TurboSign API Integration Overview](/img/turbosign/api/org-id.png) + +## 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