From 044a6666aac355a75eaaca5bb9b9f99db67101f0 Mon Sep 17 00:00:00 2001 From: Nicolas Fry Date: Mon, 24 Nov 2025 06:59:04 -0500 Subject: [PATCH 1/6] docs: add comprehensive SDK documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Create new SDKs section with overview hub page - Add detailed documentation for all 6 SDKs: - JavaScript/TypeScript with Express, Next.js, NestJS examples - Python with FastAPI, Django, Flask examples - Go with HTTP handlers and Gin examples - .NET with ASP.NET Core minimal API and controller examples - Java with Spring Boot examples - Ruby with Rails and Sidekiq examples - Update sidebars.js to include SDKs category - Add SDK reference callout to TurboSign API Signatures page - Add multi-language SDK webhook verification examples to Webhooks page 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- docs/SDKs/dotnet.md | 555 +++++++++++++++++++++++++++++++ docs/SDKs/go.md | 544 ++++++++++++++++++++++++++++++ docs/SDKs/index.md | 472 ++++++++++++++++++++++++++ docs/SDKs/java.md | 555 +++++++++++++++++++++++++++++++ docs/SDKs/javascript.md | 478 ++++++++++++++++++++++++++ docs/SDKs/python.md | 486 +++++++++++++++++++++++++++ docs/SDKs/ruby.md | 514 ++++++++++++++++++++++++++++ docs/TurboSign/API Signatures.md | 15 + docs/TurboSign/Webhooks.md | 147 +++++++- sidebars.js | 41 +++ 10 files changed, 3805 insertions(+), 2 deletions(-) create mode 100644 docs/SDKs/dotnet.md create mode 100644 docs/SDKs/go.md create mode 100644 docs/SDKs/index.md create mode 100644 docs/SDKs/java.md create mode 100644 docs/SDKs/javascript.md create mode 100644 docs/SDKs/python.md create mode 100644 docs/SDKs/ruby.md diff --git a/docs/SDKs/dotnet.md b/docs/SDKs/dotnet.md new file mode 100644 index 0000000..5d47229 --- /dev/null +++ b/docs/SDKs/dotnet.md @@ -0,0 +1,555 @@ +--- +title: .NET SDK +sidebar_position: 5 +sidebar_label: .NET / C# +description: Official TurboDocx .NET SDK. Full async/await support with dependency injection for ASP.NET Core applications. +keywords: + - turbodocx dotnet + - turbodocx csharp + - turbosign dotnet + - nuget turbodocx + - asp.net core sdk + - document api dotnet + - esignature csharp +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# .NET SDK + +The official TurboDocx SDK for .NET applications. Full async/await support with ASP.NET Core dependency injection. + +[![NuGet version](https://img.shields.io/nuget/v/TurboDocx.Sdk?logo=nuget&logoColor=white)](https://www.nuget.org/packages/TurboDocx.Sdk) +[![GitHub](https://img.shields.io/github/stars/turbodocx/sdk?style=social)](https://github.com/TurboDocx/SDK) + +## Installation + + + + +```bash +dotnet add package TurboDocx.Sdk +``` + + + + +```powershell +Install-Package TurboDocx.Sdk +``` + + + + +```xml + +``` + + + + +## Requirements + +- .NET 6.0+ or .NET Standard 2.1+ + +--- + +## Configuration + +### Basic Configuration + +```csharp +using TurboDocx.Sdk; + +// Create a client directly +var turboSign = new TurboSignClient(Environment.GetEnvironmentVariable("TURBODOCX_API_KEY")); + +// Or with options +var turboSign = new TurboSignClient(new TurboSignOptions +{ + ApiKey = Environment.GetEnvironmentVariable("TURBODOCX_API_KEY"), + BaseUrl = "https://api.turbodocx.com", + Timeout = TimeSpan.FromSeconds(30) +}); +``` + +### ASP.NET Core Dependency Injection + +```csharp +// Program.cs or Startup.cs +builder.Services.AddTurboDocx(options => +{ + options.ApiKey = builder.Configuration["TurboDocx:ApiKey"]; +}); + +// Then inject ITurboSignClient in your services +public class ContractService +{ + private readonly ITurboSignClient _turboSign; + + public ContractService(ITurboSignClient turboSign) + { + _turboSign = turboSign; + } +} +``` + +### Configuration File + +```json +// appsettings.json +{ + "TurboDocx": { + "ApiKey": "your_api_key_here" + } +} +``` + +--- + +## Quick Start + +### Send a Document for Signature + +```csharp +using TurboDocx.Sdk; + +var turboSign = new TurboSignClient(Environment.GetEnvironmentVariable("TURBODOCX_API_KEY")); + +var result = await turboSign.PrepareForSigningSingleAsync(new SigningRequest +{ + FileLink = "https://example.com/contract.pdf", + DocumentName = "Service Agreement", + SenderName = "Acme Corp", + SenderEmail = "contracts@acme.com", + Recipients = new[] + { + new Recipient { Name = "Alice Smith", Email = "alice@example.com", Order = 1 }, + new Recipient { Name = "Bob Johnson", Email = "bob@example.com", Order = 2 } + }, + Fields = new[] + { + // Alice's signature + new Field { Type = FieldType.Signature, Page = 1, X = 100, Y = 650, Width = 200, Height = 50, RecipientOrder = 1 }, + new Field { Type = FieldType.Date, Page = 1, X = 320, Y = 650, Width = 100, Height = 30, RecipientOrder = 1 }, + // Bob's signature + new Field { Type = FieldType.Signature, Page = 1, X = 100, Y = 720, Width = 200, Height = 50, RecipientOrder = 2 }, + new Field { Type = FieldType.Date, Page = 1, X = 320, Y = 720, Width = 100, Height = 30, RecipientOrder = 2 } + } +}); + +Console.WriteLine($"Document ID: {result.DocumentId}"); +foreach (var recipient in result.Recipients) +{ + Console.WriteLine($"{recipient.Name}: {recipient.SignUrl}"); +} +``` + +### Using Template-Based Fields + +```csharp +var result = await turboSign.PrepareForSigningSingleAsync(new SigningRequest +{ + FileLink = "https://example.com/contract-with-placeholders.pdf", + Recipients = new[] + { + new Recipient { Name = "Alice Smith", Email = "alice@example.com", Order = 1 } + }, + Fields = new[] + { + new Field { Type = FieldType.Signature, Anchor = "{SIGNATURE_ALICE}", Width = 200, Height = 50, RecipientOrder = 1 }, + new Field { Type = FieldType.Date, Anchor = "{DATE_ALICE}", Width = 100, Height = 30, RecipientOrder = 1 } + } +}); +``` + +--- + +## API Reference + +### PrepareForReviewAsync() + +Upload a document for preview without sending emails. + +```csharp +var result = await turboSign.PrepareForReviewAsync(new ReviewRequest +{ + FileLink = "https://example.com/document.pdf", + // Or upload directly: + // File = await File.ReadAllBytesAsync("document.pdf"), + DocumentName = "Contract Draft", + Recipients = new[] + { + new Recipient { Name = "John Doe", Email = "john@example.com", Order = 1 } + }, + Fields = new[] + { + new Field { Type = FieldType.Signature, Page = 1, X = 100, Y = 500, Width = 200, Height = 50, RecipientOrder = 1 } + } +}); + +Console.WriteLine(result.DocumentId); +Console.WriteLine(result.PreviewUrl); +``` + +### PrepareForSigningSingleAsync() + +Upload a document and immediately send signature requests. + +```csharp +var result = await turboSign.PrepareForSigningSingleAsync(new SigningRequest +{ + FileLink = "https://example.com/document.pdf", + DocumentName = "Service Agreement", + SenderName = "Your Company", + SenderEmail = "sender@company.com", + Recipients = new[] + { + new Recipient { Name = "Recipient Name", Email = "recipient@example.com", Order = 1 } + }, + Fields = new[] + { + new Field { Type = FieldType.Signature, Page = 1, X = 100, Y = 500, Width = 200, Height = 50, RecipientOrder = 1 } + } +}); +``` + +### GetStatusAsync() + +Check the status of a document. + +```csharp +var status = await turboSign.GetStatusAsync("document-uuid"); + +Console.WriteLine(status.Status); // "pending", "completed", or "voided" +Console.WriteLine(status.CompletedAt); + +foreach (var recipient in status.Recipients) +{ + Console.WriteLine($"{recipient.Name}: {recipient.Status}"); + Console.WriteLine($"Signed at: {recipient.SignedAt}"); +} +``` + +### DownloadAsync() + +Download the completed signed document. + +```csharp +var pdfBytes = await turboSign.DownloadAsync("document-uuid"); + +// Save to file +await File.WriteAllBytesAsync("signed-contract.pdf", pdfBytes); + +// Or upload to Azure Blob Storage +await blobClient.UploadAsync(new MemoryStream(pdfBytes)); +``` + +### VoidAsync() + +Cancel/void a signature request. + +```csharp +await turboSign.VoidAsync("document-uuid", "Contract terms changed"); +``` + +### ResendAsync() + +Resend signature request emails. + +```csharp +// Resend to all pending recipients +await turboSign.ResendAsync("document-uuid"); + +// Resend to specific recipients +await turboSign.ResendAsync("document-uuid", new[] { "recipient-uuid-1", "recipient-uuid-2" }); +``` + +--- + +## ASP.NET Core Examples + +### Minimal API + +```csharp +using TurboDocx.Sdk; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddTurboDocx(options => +{ + options.ApiKey = builder.Configuration["TurboDocx:ApiKey"]; +}); + +var app = builder.Build(); + +app.MapPost("/api/send-contract", async ( + SendContractRequest request, + ITurboSignClient turboSign) => +{ + var result = await turboSign.PrepareForSigningSingleAsync(new SigningRequest + { + FileLink = request.ContractUrl, + Recipients = new[] + { + new Recipient { Name = request.RecipientName, Email = request.RecipientEmail, Order = 1 } + }, + Fields = new[] + { + new Field { Type = FieldType.Signature, Page = 1, X = 100, Y = 650, Width = 200, Height = 50, RecipientOrder = 1 } + } + }); + + return Results.Ok(new { result.DocumentId, SignUrl = result.Recipients[0].SignUrl }); +}); + +app.MapGet("/api/document/{id}/status", async (string id, ITurboSignClient turboSign) => +{ + var status = await turboSign.GetStatusAsync(id); + return Results.Ok(status); +}); + +app.Run(); + +record SendContractRequest(string RecipientName, string RecipientEmail, string ContractUrl); +``` + +### Controller-Based API + +```csharp +using Microsoft.AspNetCore.Mvc; +using TurboDocx.Sdk; + +[ApiController] +[Route("api/[controller]")] +public class ContractsController : ControllerBase +{ + private readonly ITurboSignClient _turboSign; + + public ContractsController(ITurboSignClient turboSign) + { + _turboSign = turboSign; + } + + [HttpPost("send")] + public async Task SendContract([FromBody] SendContractRequest request) + { + try + { + var result = await _turboSign.PrepareForSigningSingleAsync(new SigningRequest + { + FileLink = request.ContractUrl, + Recipients = new[] + { + new Recipient { Name = request.RecipientName, Email = request.RecipientEmail, Order = 1 } + }, + Fields = new[] + { + new Field { Type = FieldType.Signature, Page = 1, X = 100, Y = 650, Width = 200, Height = 50, RecipientOrder = 1 } + } + }); + + return Ok(new { result.DocumentId, SignUrl = result.Recipients[0].SignUrl }); + } + catch (TurboDocxException ex) + { + return StatusCode(500, new { Error = ex.Message }); + } + } + + [HttpGet("{id}/status")] + public async Task GetStatus(string id) + { + try + { + var status = await _turboSign.GetStatusAsync(id); + return Ok(status); + } + catch (TurboDocxException ex) when (ex.Code == ErrorCode.NotFound) + { + return NotFound(); + } + } + + [HttpGet("{id}/download")] + public async Task Download(string id) + { + var pdfBytes = await _turboSign.DownloadAsync(id); + return File(pdfBytes, "application/pdf", "signed-document.pdf"); + } +} + +public record SendContractRequest(string RecipientName, string RecipientEmail, string ContractUrl); +``` + +--- + +## Error Handling + +```csharp +using TurboDocx.Sdk; +using TurboDocx.Sdk.Exceptions; + +try +{ + var result = await turboSign.PrepareForSigningSingleAsync(request); +} +catch (UnauthorizedException) +{ + Console.WriteLine("Invalid API key"); +} +catch (InvalidDocumentException ex) +{ + Console.WriteLine($"Could not process document: {ex.Message}"); +} +catch (RateLimitedException ex) +{ + Console.WriteLine($"Rate limited, retry after: {ex.RetryAfter} seconds"); +} +catch (NotFoundException) +{ + Console.WriteLine("Document not found"); +} +catch (TurboDocxException ex) +{ + Console.WriteLine($"Error {ex.Code}: {ex.Message}"); +} +``` + +--- + +## Types + +### Field Types + +```csharp +public enum FieldType +{ + Signature, + Initials, + Text, + Date, + Checkbox, + FullName, + Email, + Title, + Company +} +``` + +### Models + +```csharp +public class Recipient +{ + public string Name { get; set; } + public string Email { get; set; } + public int Order { get; set; } + // Response properties + public string? Id { get; set; } + public string? Status { get; set; } + public string? SignUrl { get; set; } + public DateTime? SignedAt { get; set; } +} + +public class Field +{ + public FieldType Type { get; set; } + public int? Page { get; set; } + public int? X { get; set; } + public int? Y { get; set; } + public int Width { get; set; } + public int Height { get; set; } + public int RecipientOrder { get; set; } + public string? Anchor { get; set; } // For template-based fields +} + +public class SigningRequest +{ + public string? FileLink { get; set; } + public byte[]? File { get; set; } + public string? DocumentName { get; set; } + public string? SenderName { get; set; } + public string? SenderEmail { get; set; } + public Recipient[] Recipients { get; set; } + public Field[] Fields { get; set; } +} + +public class SigningResult +{ + public string DocumentId { get; set; } + public Recipient[] Recipients { get; set; } +} + +public class DocumentStatus +{ + public string Status { get; set; } // "pending", "completed", "voided" + public DateTime? CompletedAt { get; set; } + public Recipient[] Recipients { get; set; } +} +``` + +--- + +## Webhook Signature Verification + +Verify that webhooks are genuinely from TurboDocx: + +```csharp +using Microsoft.AspNetCore.Mvc; +using TurboDocx.Sdk; + +[ApiController] +[Route("webhook")] +public class WebhookController : ControllerBase +{ + private readonly IConfiguration _configuration; + + public WebhookController(IConfiguration configuration) + { + _configuration = configuration; + } + + [HttpPost] + public async Task HandleWebhook() + { + using var reader = new StreamReader(Request.Body); + var body = await reader.ReadToEndAsync(); + + var signature = Request.Headers["X-TurboDocx-Signature"].FirstOrDefault(); + var timestamp = Request.Headers["X-TurboDocx-Timestamp"].FirstOrDefault(); + var secret = _configuration["TurboDocx:WebhookSecret"]; + + var isValid = WebhookVerifier.VerifySignature(signature, timestamp, body, secret); + + if (!isValid) + { + return Unauthorized("Invalid signature"); + } + + var webhookEvent = JsonSerializer.Deserialize(body); + + switch (webhookEvent.Event) + { + case "signature.document.completed": + Console.WriteLine($"Document completed: {webhookEvent.Data.DocumentId}"); + break; + case "signature.document.voided": + Console.WriteLine($"Document voided: {webhookEvent.Data.DocumentId}"); + break; + } + + return Ok(new { Received = true }); + } +} +``` + +--- + +## Resources + +- [GitHub Repository](https://github.com/TurboDocx/SDK/tree/main/packages/dotnet-sdk) +- [NuGet Package](https://www.nuget.org/packages/TurboDocx.Sdk) +- [API Reference](/docs/TurboSign/API-Signatures) +- [Webhook Configuration](/docs/TurboSign/Webhooks) diff --git a/docs/SDKs/go.md b/docs/SDKs/go.md new file mode 100644 index 0000000..40235af --- /dev/null +++ b/docs/SDKs/go.md @@ -0,0 +1,544 @@ +--- +title: Go SDK +sidebar_position: 4 +sidebar_label: Go +description: Official TurboDocx Go SDK. Idiomatic Go with context support for document generation and digital signatures. +keywords: + - turbodocx go + - turbosign go + - golang sdk + - go module + - document api go + - esignature golang +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Go SDK + +The official TurboDocx SDK for Go applications. Idiomatic Go with full context support. + +[![Go Reference](https://pkg.go.dev/badge/github.com/turbodocx/sdk.svg)](https://pkg.go.dev/github.com/turbodocx/sdk) +[![GitHub](https://img.shields.io/github/stars/turbodocx/sdk?style=social)](https://github.com/TurboDocx/SDK) + +## Installation + +```bash +go get github.com/turbodocx/sdk +``` + +## Requirements + +- Go 1.21+ + +--- + +## Configuration + +```go +package main + +import ( + "os" + + "github.com/turbodocx/sdk" +) + +func main() { + // Create a new client + client := sdk.NewTurboSign(os.Getenv("TURBODOCX_API_KEY")) + + // Or with options + client := sdk.NewTurboSign( + os.Getenv("TURBODOCX_API_KEY"), + sdk.WithBaseURL("https://api.turbodocx.com"), + sdk.WithTimeout(30 * time.Second), + ) +} +``` + +### Environment Variables + +```bash +export TURBODOCX_API_KEY=your_api_key_here +``` + +--- + +## Quick Start + +### Send a Document for Signature + +```go +package main + +import ( + "context" + "fmt" + "log" + "os" + + "github.com/turbodocx/sdk" +) + +func main() { + client := sdk.NewTurboSign(os.Getenv("TURBODOCX_API_KEY")) + + ctx := context.Background() + + result, err := client.PrepareForSigningSingle(ctx, &sdk.SigningRequest{ + FileLink: "https://example.com/contract.pdf", + DocumentName: "Service Agreement", + SenderName: "Acme Corp", + SenderEmail: "contracts@acme.com", + Recipients: []sdk.Recipient{ + {Name: "Alice Smith", Email: "alice@example.com", Order: 1}, + {Name: "Bob Johnson", Email: "bob@example.com", Order: 2}, + }, + Fields: []sdk.Field{ + // Alice's signature + {Type: sdk.FieldTypeSignature, Page: 1, X: 100, Y: 650, Width: 200, Height: 50, RecipientOrder: 1}, + {Type: sdk.FieldTypeDate, Page: 1, X: 320, Y: 650, Width: 100, Height: 30, RecipientOrder: 1}, + // Bob's signature + {Type: sdk.FieldTypeSignature, Page: 1, X: 100, Y: 720, Width: 200, Height: 50, RecipientOrder: 2}, + {Type: sdk.FieldTypeDate, Page: 1, X: 320, Y: 720, Width: 100, Height: 30, RecipientOrder: 2}, + }, + }) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("Document ID: %s\n", result.DocumentID) + for _, recipient := range result.Recipients { + fmt.Printf("%s: %s\n", recipient.Name, recipient.SignURL) + } +} +``` + +### Using Template-Based Fields + +```go +result, err := client.PrepareForSigningSingle(ctx, &sdk.SigningRequest{ + FileLink: "https://example.com/contract-with-placeholders.pdf", + Recipients: []sdk.Recipient{ + {Name: "Alice Smith", Email: "alice@example.com", Order: 1}, + }, + Fields: []sdk.Field{ + {Type: sdk.FieldTypeSignature, Anchor: "{SIGNATURE_ALICE}", Width: 200, Height: 50, RecipientOrder: 1}, + {Type: sdk.FieldTypeDate, Anchor: "{DATE_ALICE}", Width: 100, Height: 30, RecipientOrder: 1}, + }, +}) +``` + +--- + +## API Reference + +### NewTurboSign() + +Create a new TurboSign client. + +```go +func NewTurboSign(apiKey string, opts ...Option) *TurboSign + +// Available options +sdk.WithBaseURL(url string) +sdk.WithTimeout(timeout time.Duration) +sdk.WithHTTPClient(client *http.Client) +``` + +### PrepareForReview() + +Upload a document for preview without sending emails. + +```go +result, err := client.PrepareForReview(ctx, &sdk.ReviewRequest{ + FileLink: "https://example.com/document.pdf", + DocumentName: "Contract Draft", + Recipients: []sdk.Recipient{ + {Name: "John Doe", Email: "john@example.com", Order: 1}, + }, + Fields: []sdk.Field{ + {Type: sdk.FieldTypeSignature, Page: 1, X: 100, Y: 500, Width: 200, Height: 50, RecipientOrder: 1}, + }, +}) + +fmt.Println(result.DocumentID) +fmt.Println(result.PreviewURL) +``` + +### PrepareForSigningSingle() + +Upload a document and immediately send signature requests. + +```go +result, err := client.PrepareForSigningSingle(ctx, &sdk.SigningRequest{ + FileLink: "https://example.com/document.pdf", + DocumentName: "Service Agreement", + SenderName: "Your Company", + SenderEmail: "sender@company.com", + Recipients: []sdk.Recipient{ + {Name: "Recipient Name", Email: "recipient@example.com", Order: 1}, + }, + Fields: []sdk.Field{ + {Type: sdk.FieldTypeSignature, Page: 1, X: 100, Y: 500, Width: 200, Height: 50, RecipientOrder: 1}, + }, +}) +``` + +### GetStatus() + +Check the status of a document. + +```go +status, err := client.GetStatus(ctx, "document-uuid") +if err != nil { + log.Fatal(err) +} + +fmt.Println(status.Status) // "pending", "completed", or "voided" +fmt.Println(status.CompletedAt) + +for _, recipient := range status.Recipients { + fmt.Printf("%s: %s\n", recipient.Name, recipient.Status) +} +``` + +### Download() + +Download the completed signed document. + +```go +pdfData, err := client.Download(ctx, "document-uuid") +if err != nil { + log.Fatal(err) +} + +// Save to file +err = os.WriteFile("signed-contract.pdf", pdfData, 0644) +if err != nil { + log.Fatal(err) +} + +// Or upload to S3 +_, err = s3Client.PutObject(ctx, &s3.PutObjectInput{ + Bucket: aws.String("my-bucket"), + Key: aws.String("signed-contract.pdf"), + Body: bytes.NewReader(pdfData), +}) +``` + +### Void() + +Cancel/void a signature request. + +```go +err := client.Void(ctx, "document-uuid", "Contract terms changed") +``` + +### Resend() + +Resend signature request emails. + +```go +// Resend to all pending recipients +err := client.Resend(ctx, "document-uuid", nil) + +// Resend to specific recipients +err := client.Resend(ctx, "document-uuid", []string{"recipient-uuid-1", "recipient-uuid-2"}) +``` + +--- + +## HTTP Handler Examples + +### Basic HTTP Handler + +```go +package main + +import ( + "encoding/json" + "net/http" + "os" + + "github.com/turbodocx/sdk" +) + +var client = sdk.NewTurboSign(os.Getenv("TURBODOCX_API_KEY")) + +type SendContractRequest struct { + RecipientName string `json:"recipient_name"` + RecipientEmail string `json:"recipient_email"` + ContractURL string `json:"contract_url"` +} + +type SendContractResponse struct { + DocumentID string `json:"document_id"` + SignURL string `json:"sign_url"` +} + +func sendContractHandler(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + return + } + + var req SendContractRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + result, err := client.PrepareForSigningSingle(r.Context(), &sdk.SigningRequest{ + FileLink: req.ContractURL, + Recipients: []sdk.Recipient{ + {Name: req.RecipientName, Email: req.RecipientEmail, Order: 1}, + }, + Fields: []sdk.Field{ + {Type: sdk.FieldTypeSignature, Page: 1, X: 100, Y: 650, Width: 200, Height: 50, RecipientOrder: 1}, + }, + }) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + json.NewEncoder(w).Encode(SendContractResponse{ + DocumentID: result.DocumentID, + SignURL: result.Recipients[0].SignURL, + }) +} + +func main() { + http.HandleFunc("/api/send-contract", sendContractHandler) + http.ListenAndServe(":8080", nil) +} +``` + +### Gin Framework + +```go +package main + +import ( + "net/http" + "os" + + "github.com/gin-gonic/gin" + "github.com/turbodocx/sdk" +) + +var client = sdk.NewTurboSign(os.Getenv("TURBODOCX_API_KEY")) + +func main() { + r := gin.Default() + + r.POST("/api/send-contract", func(c *gin.Context) { + var req struct { + RecipientName string `json:"recipient_name" binding:"required"` + RecipientEmail string `json:"recipient_email" binding:"required"` + ContractURL string `json:"contract_url" binding:"required"` + } + + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + result, err := client.PrepareForSigningSingle(c.Request.Context(), &sdk.SigningRequest{ + FileLink: req.ContractURL, + Recipients: []sdk.Recipient{ + {Name: req.RecipientName, Email: req.RecipientEmail, Order: 1}, + }, + Fields: []sdk.Field{ + {Type: sdk.FieldTypeSignature, Page: 1, X: 100, Y: 650, Width: 200, Height: 50, RecipientOrder: 1}, + }, + }) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, gin.H{ + "document_id": result.DocumentID, + "sign_url": result.Recipients[0].SignURL, + }) + }) + + r.GET("/api/document/:id/status", func(c *gin.Context) { + status, err := client.GetStatus(c.Request.Context(), c.Param("id")) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, status) + }) + + r.Run(":8080") +} +``` + +--- + +## Error Handling + +```go +import ( + "errors" + + "github.com/turbodocx/sdk" +) + +result, err := client.PrepareForSigningSingle(ctx, request) +if err != nil { + var turboErr *sdk.TurboDocxError + if errors.As(err, &turboErr) { + switch turboErr.Code { + case sdk.ErrCodeUnauthorized: + log.Println("Invalid API key") + case sdk.ErrCodeInvalidDocument: + log.Printf("Could not process document: %s", turboErr.Message) + case sdk.ErrCodeRateLimited: + log.Printf("Rate limited, retry after: %d seconds", turboErr.RetryAfter) + case sdk.ErrCodeNotFound: + log.Println("Document not found") + default: + log.Printf("Error %s: %s", turboErr.Code, turboErr.Message) + } + } else { + // Network or unexpected error + log.Fatal(err) + } +} +``` + +--- + +## Types + +### Field Types + +```go +const ( + FieldTypeSignature sdk.FieldType = "signature" + FieldTypeInitials sdk.FieldType = "initials" + FieldTypeText sdk.FieldType = "text" + FieldTypeDate sdk.FieldType = "date" + FieldTypeCheckbox sdk.FieldType = "checkbox" + FieldTypeFullName sdk.FieldType = "full_name" + FieldTypeEmail sdk.FieldType = "email" + FieldTypeTitle sdk.FieldType = "title" + FieldTypeCompany sdk.FieldType = "company" +) +``` + +### Structs + +```go +type Recipient struct { + Name string `json:"name"` + Email string `json:"email"` + Order int `json:"order"` + // Response fields + ID string `json:"id,omitempty"` + Status string `json:"status,omitempty"` + SignURL string `json:"sign_url,omitempty"` +} + +type Field struct { + Type FieldType `json:"type"` + Page int `json:"page,omitempty"` + X int `json:"x,omitempty"` + Y int `json:"y,omitempty"` + Width int `json:"width"` + Height int `json:"height"` + RecipientOrder int `json:"recipient_order"` + Anchor string `json:"anchor,omitempty"` // For template-based fields +} + +type SigningRequest struct { + FileLink string `json:"file_link,omitempty"` + File []byte `json:"-"` // For direct upload + DocumentName string `json:"document_name,omitempty"` + SenderName string `json:"sender_name,omitempty"` + SenderEmail string `json:"sender_email,omitempty"` + Recipients []Recipient `json:"recipients"` + Fields []Field `json:"fields"` +} + +type SigningResult struct { + DocumentID string `json:"document_id"` + Recipients []Recipient `json:"recipients"` +} + +type DocumentStatus struct { + Status string `json:"status"` // "pending", "completed", "voided" + CompletedAt *time.Time `json:"completed_at,omitempty"` + Recipients []Recipient `json:"recipients"` +} +``` + +--- + +## Webhook Signature Verification + +Verify that webhooks are genuinely from TurboDocx: + +```go +package main + +import ( + "encoding/json" + "io" + "net/http" + "os" + + "github.com/turbodocx/sdk" +) + +func webhookHandler(w http.ResponseWriter, r *http.Request) { + body, err := io.ReadAll(r.Body) + if err != nil { + http.Error(w, "Failed to read body", http.StatusBadRequest) + return + } + + signature := r.Header.Get("X-TurboDocx-Signature") + timestamp := r.Header.Get("X-TurboDocx-Timestamp") + secret := os.Getenv("TURBODOCX_WEBHOOK_SECRET") + + isValid := sdk.VerifyWebhookSignature(signature, timestamp, body, secret) + if !isValid { + http.Error(w, "Invalid signature", http.StatusUnauthorized) + return + } + + var event sdk.WebhookEvent + if err := json.Unmarshal(body, &event); err != nil { + http.Error(w, "Failed to parse event", http.StatusBadRequest) + return + } + + switch event.Event { + case "signature.document.completed": + log.Printf("Document completed: %s", event.Data.DocumentID) + case "signature.document.voided": + log.Printf("Document voided: %s", event.Data.DocumentID) + } + + w.WriteHeader(http.StatusOK) + json.NewEncoder(w).Encode(map[string]bool{"received": true}) +} +``` + +--- + +## Resources + +- [GitHub Repository](https://github.com/TurboDocx/SDK/tree/main/packages/go-sdk) +- [Go Package Documentation](https://pkg.go.dev/github.com/turbodocx/sdk) +- [API Reference](/docs/TurboSign/API-Signatures) +- [Webhook Configuration](/docs/TurboSign/Webhooks) diff --git a/docs/SDKs/index.md b/docs/SDKs/index.md new file mode 100644 index 0000000..b03b414 --- /dev/null +++ b/docs/SDKs/index.md @@ -0,0 +1,472 @@ +--- +title: SDKs Overview +sidebar_position: 1 +sidebar_label: Overview +description: Official TurboDocx SDKs for JavaScript, Python, Go, .NET, Java, and Ruby. Get started with document generation and digital signatures in minutes. +keywords: + - turbodocx sdk + - turbosign sdk + - javascript sdk + - python sdk + - go sdk + - dotnet sdk + - java sdk + - ruby sdk + - document api + - esignature sdk + - api client library +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# TurboDocx SDKs + +Official client libraries for the TurboDocx API. Build document generation and digital signature workflows in your language of choice. + +## Available SDKs + +| Language | Package | Install Command | Links | +|:---------|:--------|:----------------|:------| +| **JavaScript/TypeScript** | `@turbodocx/sdk` | `npm install @turbodocx/sdk` | [Docs](/docs/SDKs/javascript) · [GitHub](https://github.com/TurboDocx/SDK/tree/main/packages/js-sdk) | +| **Python** | `turbodocx-sdk` | `pip install turbodocx-sdk` | [Docs](/docs/SDKs/python) · [GitHub](https://github.com/TurboDocx/SDK/tree/main/packages/py-sdk) | +| **Go** | `github.com/turbodocx/sdk` | `go get github.com/turbodocx/sdk` | [Docs](/docs/SDKs/go) · [GitHub](https://github.com/TurboDocx/SDK/tree/main/packages/go-sdk) | +| **C# / .NET** | `TurboDocx.Sdk` | `dotnet add package TurboDocx.Sdk` | [Docs](/docs/SDKs/dotnet) · [GitHub](https://github.com/TurboDocx/SDK/tree/main/packages/dotnet-sdk) | +| **Java** | `com.turbodocx:sdk` | [Maven Central](https://search.maven.org/artifact/com.turbodocx/sdk) | [Docs](/docs/SDKs/java) · [GitHub](https://github.com/TurboDocx/SDK/tree/main/packages/java-sdk) | +| **Ruby** | `turbodocx-sdk` | `gem install turbodocx-sdk` | [Docs](/docs/SDKs/ruby) · [GitHub](https://github.com/TurboDocx/SDK/tree/main/packages/ruby-sdk) | + +:::tip Low-code or No-code? +Check out our [n8n community node](https://www.npmjs.com/package/@turbodocx/n8n-nodes-turbodocx) for workflow automation, or get [TurboDocx Writer](https://appsource.microsoft.com/en-us/product/office/WA200007397) for Microsoft Word. +::: + +--- + +## Quick Start + +Get up and running in under 2 minutes. + +### 1. Get Your API Key + +Sign up at [turbodocx.com](https://www.turbodocx.com) and grab your API key from the dashboard under **Settings → API Keys**. + +### 2. Install the SDK + + + + +```bash +npm install @turbodocx/sdk +# or +yarn add @turbodocx/sdk +``` + + + + +```bash +pip install turbodocx-sdk +# or +poetry add turbodocx-sdk +``` + + + + +```bash +go get github.com/turbodocx/sdk +``` + + + + +```bash +dotnet add package TurboDocx.Sdk +``` + + + + +```xml + + com.turbodocx + sdk + 1.0.0 + +``` + + + + +```bash +gem install turbodocx-sdk +# or add to Gemfile: +gem 'turbodocx-sdk' +``` + + + + +### 3. Send Your First Document for Signature + + + + +```typescript +import { TurboSign } from '@turbodocx/sdk'; + +// Configure with your API key +TurboSign.configure({ apiKey: process.env.TURBODOCX_API_KEY }); + +// Send a document for signature +const { documentId, recipients } = await TurboSign.prepareForSigningSingle({ + fileLink: 'https://example.com/contract.pdf', + recipients: [ + { name: 'John Doe', email: 'john@example.com', order: 1 } + ], + fields: [ + { type: 'signature', page: 1, x: 100, y: 500, width: 200, height: 50, recipientOrder: 1 } + ] +}); + +console.log(`Document sent! Sign URL: ${recipients[0].signUrl}`); +``` + + + + +```python +from turbodocx import TurboSign +import os + +# Configure with your API key +TurboSign.configure(api_key=os.environ["TURBODOCX_API_KEY"]) + +# Send a document for signature +result = await TurboSign.prepare_for_signing_single( + file_link="https://example.com/contract.pdf", + recipients=[ + {"name": "John Doe", "email": "john@example.com", "order": 1} + ], + fields=[ + {"type": "signature", "page": 1, "x": 100, "y": 500, "width": 200, "height": 50, "recipient_order": 1} + ] +) + +print(f"Document sent! Sign URL: {result.recipients[0].sign_url}") +``` + + + + +```go +package main + +import ( + "context" + "fmt" + "os" + + "github.com/turbodocx/sdk" +) + +func main() { + // Configure with your API key + client := sdk.NewTurboSign(os.Getenv("TURBODOCX_API_KEY")) + + // Send a document for signature + result, err := client.PrepareForSigningSingle(context.Background(), &sdk.SigningRequest{ + FileLink: "https://example.com/contract.pdf", + Recipients: []sdk.Recipient{ + {Name: "John Doe", Email: "john@example.com", Order: 1}, + }, + Fields: []sdk.Field{ + {Type: "signature", Page: 1, X: 100, Y: 500, Width: 200, Height: 50, RecipientOrder: 1}, + }, + }) + if err != nil { + panic(err) + } + + fmt.Printf("Document sent! Sign URL: %s\n", result.Recipients[0].SignUrl) +} +``` + + + + +```csharp +using TurboDocx.Sdk; + +// Configure with your API key +var turboSign = new TurboSignClient(Environment.GetEnvironmentVariable("TURBODOCX_API_KEY")); + +// Send a document for signature +var result = await turboSign.PrepareForSigningSingleAsync(new SigningRequest +{ + FileLink = "https://example.com/contract.pdf", + Recipients = new[] + { + new Recipient { Name = "John Doe", Email = "john@example.com", Order = 1 } + }, + Fields = new[] + { + new Field { Type = "signature", Page = 1, X = 100, Y = 500, Width = 200, Height = 50, RecipientOrder = 1 } + } +}); + +Console.WriteLine($"Document sent! Sign URL: {result.Recipients[0].SignUrl}"); +``` + + + + +```java +import com.turbodocx.sdk.TurboSign; +import com.turbodocx.sdk.models.*; + +public class Main { + public static void main(String[] args) { + // Configure with your API key + TurboSign turboSign = new TurboSign(System.getenv("TURBODOCX_API_KEY")); + + // Send a document for signature + SigningResult result = turboSign.prepareForSigningSingle( + SigningRequest.builder() + .fileLink("https://example.com/contract.pdf") + .recipient(Recipient.builder() + .name("John Doe") + .email("john@example.com") + .order(1) + .build()) + .field(Field.builder() + .type("signature") + .page(1).x(100).y(500).width(200).height(50) + .recipientOrder(1) + .build()) + .build() + ); + + System.out.println("Document sent! Sign URL: " + result.getRecipients().get(0).getSignUrl()); + } +} +``` + + + + +```ruby +require 'turbodocx' + +# Configure with your API key +TurboDocx.configure do |config| + config.api_key = ENV['TURBODOCX_API_KEY'] +end + +# Send a document for signature +result = TurboDocx::TurboSign.prepare_for_signing_single( + file_link: 'https://example.com/contract.pdf', + recipients: [ + { name: 'John Doe', email: 'john@example.com', order: 1 } + ], + fields: [ + { type: 'signature', page: 1, x: 100, y: 500, width: 200, height: 50, recipient_order: 1 } + ] +) + +puts "Document sent! Sign URL: #{result.recipients.first.sign_url}" +``` + + + + +--- + +## Core Features + +All TurboDocx SDKs provide access to: + +### TurboSign — Digital Signatures + +Send documents for legally-binding eSignatures with full audit trails. + +| Method | Description | +|:-------|:------------| +| `prepareForReview()` | Upload document for preview without sending emails | +| `prepareForSigningSingle()` | Upload and immediately send signature requests | +| `getStatus()` | Check document and recipient signing status | +| `download()` | Download the completed signed document | +| `void()` | Cancel/void a signature request | +| `resend()` | Resend signature request emails | + +[Learn more about TurboSign →](/docs/TurboSign/Setting-up-TurboSign) + +### TurboDocx — Document Generation *(Coming Soon)* + +Generate documents from templates with dynamic data. + +--- + +## Field Positioning + +TurboSign supports two methods for placing signature fields: + +### Coordinate-Based (Pixel Positioning) + +Specify exact positions using page coordinates: + +```javascript +{ + type: 'signature', + page: 1, + x: 100, // pixels from left + y: 500, // pixels from top + width: 200, + height: 50, + recipientOrder: 1 +} +``` + +### Template-Based (Text Anchors) + +Use text markers in your PDF to position fields automatically: + +```javascript +{ + type: 'signature', + anchor: '{SIGNATURE_1}', // text to find in PDF + width: 200, + height: 50, + recipientOrder: 1 +} +``` + +:::tip When to use each method +- **Coordinate-based**: Best for PDFs with fixed layouts where you know exact positions +- **Template-based**: Best for templates where content may shift, using anchor text like `{SIGNATURE_1}` +::: + +--- + +## Error Handling + +All SDKs provide structured error handling with detailed error codes: + + + + +```typescript +import { TurboSign, TurboDocxError } from '@turbodocx/sdk'; + +try { + const result = await TurboSign.prepareForSigningSingle({ /* ... */ }); +} catch (error) { + if (error instanceof TurboDocxError) { + console.error(`Error ${error.code}: ${error.message}`); + // Handle specific error codes + if (error.code === 'INVALID_DOCUMENT') { + // Handle invalid document error + } + } +} +``` + + + + +```python +from turbodocx import TurboSign +from turbodocx.exceptions import TurboDocxError + +try: + result = await TurboSign.prepare_for_signing_single(...) +except TurboDocxError as e: + print(f"Error {e.code}: {e.message}") + if e.code == "INVALID_DOCUMENT": + # Handle invalid document error + pass +``` + + + + +```go +result, err := client.PrepareForSigningSingle(ctx, request) +if err != nil { + var turboErr *sdk.TurboDocxError + if errors.As(err, &turboErr) { + fmt.Printf("Error %s: %s\n", turboErr.Code, turboErr.Message) + if turboErr.Code == "INVALID_DOCUMENT" { + // Handle invalid document error + } + } +} +``` + + + + +### Common Error Codes + +| Code | Description | +|:-----|:------------| +| `UNAUTHORIZED` | Invalid or expired API key | +| `INVALID_DOCUMENT` | Document could not be processed | +| `INVALID_RECIPIENT` | Invalid recipient email or data | +| `RATE_LIMITED` | Too many requests, slow down | +| `NOT_FOUND` | Document or resource not found | + +--- + +## Next Steps + +
+
+
+
+

API Signatures

+
+
+

Complete guide to TurboSign API integration with detailed examples.

+
+ +
+
+
+
+
+

Webhooks

+
+
+

Receive real-time notifications when documents are signed.

+
+ +
+
+
+
+
+

GitHub Repository

+
+
+

View source code, report issues, and contribute to the SDKs.

+
+ +
+
+
+ +--- + +## Support + +Need help with the SDKs? + +- **Discord Community**: [Join our Discord](https://discord.gg/NYKwz4BcpX) for real-time support +- **GitHub Issues**: [Report bugs or request features](https://github.com/TurboDocx/SDK/issues) +- **Documentation**: Browse the language-specific guides in the sidebar diff --git a/docs/SDKs/java.md b/docs/SDKs/java.md new file mode 100644 index 0000000..7cfb4e0 --- /dev/null +++ b/docs/SDKs/java.md @@ -0,0 +1,555 @@ +--- +title: Java SDK +sidebar_position: 6 +sidebar_label: Java +description: Official TurboDocx Java SDK. Builder pattern with Spring Boot integration for document generation and digital signatures. +keywords: + - turbodocx java + - turbosign java + - maven turbodocx + - gradle turbodocx + - spring boot sdk + - document api java + - esignature java +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Java SDK + +The official TurboDocx SDK for Java applications. Builder pattern API with Spring Boot integration. + +[![Maven Central](https://img.shields.io/maven-central/v/com.turbodocx/sdk?logo=java&logoColor=white)](https://search.maven.org/artifact/com.turbodocx/sdk) +[![GitHub](https://img.shields.io/github/stars/turbodocx/sdk?style=social)](https://github.com/TurboDocx/SDK) + +## Installation + + + + +```xml + + com.turbodocx + sdk + 1.0.0 + +``` + + + + +```kotlin +implementation("com.turbodocx:sdk:1.0.0") +``` + + + + +```groovy +implementation 'com.turbodocx:sdk:1.0.0' +``` + + + + +## Requirements + +- Java 11+ +- Jackson for JSON serialization (included) + +--- + +## Configuration + +### Basic Configuration + +```java +import com.turbodocx.sdk.TurboSign; + +// Create a client +TurboSign turboSign = new TurboSign(System.getenv("TURBODOCX_API_KEY")); + +// Or with builder +TurboSign turboSign = TurboSign.builder() + .apiKey(System.getenv("TURBODOCX_API_KEY")) + .baseUrl("https://api.turbodocx.com") + .timeout(Duration.ofSeconds(30)) + .build(); +``` + +### Spring Boot Configuration + +```java +// application.yml +turbodocx: + api-key: ${TURBODOCX_API_KEY} + +// TurboDocxConfig.java +@Configuration +public class TurboDocxConfig { + + @Bean + public TurboSign turboSign(@Value("${turbodocx.api-key}") String apiKey) { + return new TurboSign(apiKey); + } +} +``` + +--- + +## Quick Start + +### Send a Document for Signature + +```java +import com.turbodocx.sdk.TurboSign; +import com.turbodocx.sdk.models.*; + +public class Main { + public static void main(String[] args) { + TurboSign turboSign = new TurboSign(System.getenv("TURBODOCX_API_KEY")); + + SigningResult result = turboSign.prepareForSigningSingle( + SigningRequest.builder() + .fileLink("https://example.com/contract.pdf") + .documentName("Service Agreement") + .senderName("Acme Corp") + .senderEmail("contracts@acme.com") + .recipient(Recipient.builder() + .name("Alice Smith") + .email("alice@example.com") + .order(1) + .build()) + .recipient(Recipient.builder() + .name("Bob Johnson") + .email("bob@example.com") + .order(2) + .build()) + // Alice's signature + .field(Field.builder() + .type(FieldType.SIGNATURE) + .page(1).x(100).y(650).width(200).height(50) + .recipientOrder(1) + .build()) + .field(Field.builder() + .type(FieldType.DATE) + .page(1).x(320).y(650).width(100).height(30) + .recipientOrder(1) + .build()) + // Bob's signature + .field(Field.builder() + .type(FieldType.SIGNATURE) + .page(1).x(100).y(720).width(200).height(50) + .recipientOrder(2) + .build()) + .field(Field.builder() + .type(FieldType.DATE) + .page(1).x(320).y(720).width(100).height(30) + .recipientOrder(2) + .build()) + .build() + ); + + System.out.println("Document ID: " + result.getDocumentId()); + for (Recipient recipient : result.getRecipients()) { + System.out.println(recipient.getName() + ": " + recipient.getSignUrl()); + } + } +} +``` + +### Using Template-Based Fields + +```java +SigningResult result = turboSign.prepareForSigningSingle( + SigningRequest.builder() + .fileLink("https://example.com/contract-with-placeholders.pdf") + .recipient(Recipient.builder() + .name("Alice Smith") + .email("alice@example.com") + .order(1) + .build()) + .field(Field.builder() + .type(FieldType.SIGNATURE) + .anchor("{SIGNATURE_ALICE}") + .width(200).height(50) + .recipientOrder(1) + .build()) + .field(Field.builder() + .type(FieldType.DATE) + .anchor("{DATE_ALICE}") + .width(100).height(30) + .recipientOrder(1) + .build()) + .build() +); +``` + +--- + +## API Reference + +### prepareForReview() + +Upload a document for preview without sending emails. + +```java +ReviewResult result = turboSign.prepareForReview( + ReviewRequest.builder() + .fileLink("https://example.com/document.pdf") + // Or upload directly: + // .file(Files.readAllBytes(Path.of("document.pdf"))) + .documentName("Contract Draft") + .recipient(Recipient.builder() + .name("John Doe") + .email("john@example.com") + .order(1) + .build()) + .field(Field.builder() + .type(FieldType.SIGNATURE) + .page(1).x(100).y(500).width(200).height(50) + .recipientOrder(1) + .build()) + .build() +); + +System.out.println(result.getDocumentId()); +System.out.println(result.getPreviewUrl()); +``` + +### prepareForSigningSingle() + +Upload a document and immediately send signature requests. + +```java +SigningResult result = turboSign.prepareForSigningSingle( + SigningRequest.builder() + .fileLink("https://example.com/document.pdf") + .documentName("Service Agreement") + .senderName("Your Company") + .senderEmail("sender@company.com") + .recipient(Recipient.builder() + .name("Recipient Name") + .email("recipient@example.com") + .order(1) + .build()) + .field(Field.builder() + .type(FieldType.SIGNATURE) + .page(1).x(100).y(500).width(200).height(50) + .recipientOrder(1) + .build()) + .build() +); +``` + +### getStatus() + +Check the status of a document. + +```java +DocumentStatus status = turboSign.getStatus("document-uuid"); + +System.out.println(status.getStatus()); // "pending", "completed", or "voided" +System.out.println(status.getCompletedAt()); + +for (Recipient recipient : status.getRecipients()) { + System.out.println(recipient.getName() + ": " + recipient.getStatus()); + System.out.println("Signed at: " + recipient.getSignedAt()); +} +``` + +### download() + +Download the completed signed document. + +```java +byte[] pdfBytes = turboSign.download("document-uuid"); + +// Save to file +Files.write(Path.of("signed-contract.pdf"), pdfBytes); + +// Or upload to S3 +s3Client.putObject(PutObjectRequest.builder() + .bucket("my-bucket") + .key("signed-contract.pdf") + .build(), + RequestBody.fromBytes(pdfBytes) +); +``` + +### void() + +Cancel/void a signature request. + +```java +turboSign.voidDocument("document-uuid", "Contract terms changed"); +``` + +### resend() + +Resend signature request emails. + +```java +// Resend to all pending recipients +turboSign.resend("document-uuid"); + +// Resend to specific recipients +turboSign.resend("document-uuid", List.of("recipient-uuid-1", "recipient-uuid-2")); +``` + +--- + +## Spring Boot Examples + +### REST Controller + +```java +import com.turbodocx.sdk.TurboSign; +import com.turbodocx.sdk.models.*; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/api/contracts") +public class ContractController { + + private final TurboSign turboSign; + + public ContractController(TurboSign turboSign) { + this.turboSign = turboSign; + } + + @PostMapping("/send") + public SendContractResponse sendContract(@RequestBody SendContractRequest request) { + SigningResult result = turboSign.prepareForSigningSingle( + SigningRequest.builder() + .fileLink(request.getContractUrl()) + .recipient(Recipient.builder() + .name(request.getRecipientName()) + .email(request.getRecipientEmail()) + .order(1) + .build()) + .field(Field.builder() + .type(FieldType.SIGNATURE) + .page(1).x(100).y(650).width(200).height(50) + .recipientOrder(1) + .build()) + .build() + ); + + return new SendContractResponse( + result.getDocumentId(), + result.getRecipients().get(0).getSignUrl() + ); + } + + @GetMapping("/{id}/status") + public DocumentStatus getStatus(@PathVariable String id) { + return turboSign.getStatus(id); + } + + @GetMapping("/{id}/download") + public ResponseEntity download(@PathVariable String id) { + byte[] pdfBytes = turboSign.download(id); + return ResponseEntity.ok() + .contentType(MediaType.APPLICATION_PDF) + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"signed-document.pdf\"") + .body(pdfBytes); + } +} + +record SendContractRequest(String recipientName, String recipientEmail, String contractUrl) {} +record SendContractResponse(String documentId, String signUrl) {} +``` + +### Service Layer + +```java +import com.turbodocx.sdk.TurboSign; +import com.turbodocx.sdk.models.*; +import org.springframework.stereotype.Service; + +@Service +public class ContractService { + + private final TurboSign turboSign; + + public ContractService(TurboSign turboSign) { + this.turboSign = turboSign; + } + + public SigningResult sendForSignature(String documentUrl, String recipientName, String recipientEmail) { + return turboSign.prepareForSigningSingle( + SigningRequest.builder() + .fileLink(documentUrl) + .recipient(Recipient.builder() + .name(recipientName) + .email(recipientEmail) + .order(1) + .build()) + .field(Field.builder() + .type(FieldType.SIGNATURE) + .page(1).x(100).y(650).width(200).height(50) + .recipientOrder(1) + .build()) + .build() + ); + } + + public DocumentStatus checkStatus(String documentId) { + return turboSign.getStatus(documentId); + } + + public byte[] downloadSigned(String documentId) { + return turboSign.download(documentId); + } +} +``` + +--- + +## Error Handling + +```java +import com.turbodocx.sdk.TurboSign; +import com.turbodocx.sdk.exceptions.*; + +try { + SigningResult result = turboSign.prepareForSigningSingle(request); +} catch (UnauthorizedException e) { + System.out.println("Invalid API key"); +} catch (InvalidDocumentException e) { + System.out.println("Could not process document: " + e.getMessage()); +} catch (RateLimitedException e) { + System.out.println("Rate limited, retry after: " + e.getRetryAfter() + " seconds"); +} catch (NotFoundException e) { + System.out.println("Document not found"); +} catch (TurboDocxException e) { + System.out.println("Error " + e.getCode() + ": " + e.getMessage()); +} +``` + +--- + +## Types + +### Field Types + +```java +public enum FieldType { + SIGNATURE, + INITIALS, + TEXT, + DATE, + CHECKBOX, + FULL_NAME, + EMAIL, + TITLE, + COMPANY +} +``` + +### Models + +```java +@Builder +public class Recipient { + private String name; + private String email; + private int order; + // Response fields + private String id; + private String status; + private String signUrl; + private Instant signedAt; +} + +@Builder +public class Field { + private FieldType type; + private Integer page; + private Integer x; + private Integer y; + private int width; + private int height; + private int recipientOrder; + private String anchor; // For template-based fields +} + +@Builder +public class SigningRequest { + private String fileLink; + private byte[] file; + private String documentName; + private String senderName; + private String senderEmail; + @Singular private List recipients; + @Singular private List fields; +} + +public class SigningResult { + private String documentId; + private List recipients; +} + +public class DocumentStatus { + private String status; // "pending", "completed", "voided" + private Instant completedAt; + private List recipients; +} +``` + +--- + +## Webhook Signature Verification + +Verify that webhooks are genuinely from TurboDocx: + +```java +import com.turbodocx.sdk.WebhookVerifier; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/webhook") +public class WebhookController { + + @Value("${turbodocx.webhook-secret}") + private String webhookSecret; + + @PostMapping + public ResponseEntity handleWebhook( + @RequestBody String body, + @RequestHeader("X-TurboDocx-Signature") String signature, + @RequestHeader("X-TurboDocx-Timestamp") String timestamp) { + + boolean isValid = WebhookVerifier.verifySignature(signature, timestamp, body, webhookSecret); + + if (!isValid) { + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid signature"); + } + + ObjectMapper mapper = new ObjectMapper(); + WebhookEvent event = mapper.readValue(body, WebhookEvent.class); + + switch (event.getEvent()) { + case "signature.document.completed": + System.out.println("Document completed: " + event.getData().getDocumentId()); + break; + case "signature.document.voided": + System.out.println("Document voided: " + event.getData().getDocumentId()); + break; + } + + return ResponseEntity.ok(Map.of("received", true)); + } +} +``` + +--- + +## Resources + +- [GitHub Repository](https://github.com/TurboDocx/SDK/tree/main/packages/java-sdk) +- [Maven Central](https://search.maven.org/artifact/com.turbodocx/sdk) +- [API Reference](/docs/TurboSign/API-Signatures) +- [Webhook Configuration](/docs/TurboSign/Webhooks) diff --git a/docs/SDKs/javascript.md b/docs/SDKs/javascript.md new file mode 100644 index 0000000..5f7c697 --- /dev/null +++ b/docs/SDKs/javascript.md @@ -0,0 +1,478 @@ +--- +title: JavaScript / TypeScript SDK +sidebar_position: 2 +sidebar_label: JavaScript / TypeScript +description: Official TurboDocx JavaScript and TypeScript SDK. Full TypeScript support with async/await patterns for document generation and digital signatures. +keywords: + - turbodocx javascript + - turbodocx typescript + - turbosign javascript + - node.js sdk + - typescript sdk + - npm turbodocx + - document api javascript + - esignature javascript +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# JavaScript / TypeScript SDK + +The official TurboDocx SDK for JavaScript and TypeScript applications. + +[![npm version](https://img.shields.io/npm/v/@turbodocx/sdk?logo=npm&logoColor=white)](https://www.npmjs.com/package/@turbodocx/sdk) +[![GitHub](https://img.shields.io/github/stars/turbodocx/sdk?style=social)](https://github.com/TurboDocx/SDK) + +## Installation + + + + +```bash +npm install @turbodocx/sdk +``` + + + + +```bash +yarn add @turbodocx/sdk +``` + + + + +```bash +pnpm add @turbodocx/sdk +``` + + + + +## Requirements + +- Node.js 18+ or modern browser +- TypeScript 4.7+ (optional, for type checking) + +--- + +## Configuration + +```typescript +import { TurboSign } from '@turbodocx/sdk'; + +// Configure globally (recommended for server-side) +TurboSign.configure({ + apiKey: process.env.TURBODOCX_API_KEY, + // Optional: override base URL for testing + // baseUrl: 'https://api.turbodocx.com' +}); +``` + +### Environment Variables + +```bash +# .env +TURBODOCX_API_KEY=your_api_key_here +``` + +--- + +## Quick Start + +### Send a Document for Signature + +```typescript +import { TurboSign } from '@turbodocx/sdk'; + +TurboSign.configure({ apiKey: process.env.TURBODOCX_API_KEY }); + +// Send document with coordinate-based fields +const result = await TurboSign.prepareForSigningSingle({ + fileLink: 'https://example.com/contract.pdf', + documentName: 'Service Agreement', + senderName: 'Acme Corp', + senderEmail: 'contracts@acme.com', + recipients: [ + { name: 'Alice Smith', email: 'alice@example.com', order: 1 }, + { name: 'Bob Johnson', email: 'bob@example.com', order: 2 } + ], + fields: [ + // Alice's signature + { type: 'signature', page: 1, x: 100, y: 650, width: 200, height: 50, recipientOrder: 1 }, + { type: 'date', page: 1, x: 320, y: 650, width: 100, height: 30, recipientOrder: 1 }, + // Bob's signature + { type: 'signature', page: 1, x: 100, y: 720, width: 200, height: 50, recipientOrder: 2 }, + { type: 'date', page: 1, x: 320, y: 720, width: 100, height: 30, recipientOrder: 2 } + ] +}); + +console.log(`Document ID: ${result.documentId}`); +for (const recipient of result.recipients) { + console.log(`${recipient.name}: ${recipient.signUrl}`); +} +``` + +### Using Template-Based Fields + +```typescript +// Use text anchors instead of coordinates +const result = await TurboSign.prepareForSigningSingle({ + fileLink: 'https://example.com/contract-with-placeholders.pdf', + recipients: [ + { name: 'Alice Smith', email: 'alice@example.com', order: 1 } + ], + fields: [ + { type: 'signature', anchor: '{SIGNATURE_ALICE}', width: 200, height: 50, recipientOrder: 1 }, + { type: 'date', anchor: '{DATE_ALICE}', width: 100, height: 30, recipientOrder: 1 } + ] +}); +``` + +--- + +## API Reference + +### TurboSign.configure(options) + +Configure the SDK with your API credentials. + +```typescript +TurboSign.configure({ + apiKey: string; // Required: Your TurboDocx API key + baseUrl?: string; // Optional: API base URL + timeout?: number; // Optional: Request timeout in ms (default: 30000) +}); +``` + +### TurboSign.prepareForReview(options) + +Upload a document for preview without sending signature request emails. + +```typescript +const { documentId, previewUrl } = await TurboSign.prepareForReview({ + fileLink: 'https://example.com/document.pdf', + // or upload directly: + // file: Buffer | Blob, + documentName: 'Contract Draft', + recipients: [ + { name: 'John Doe', email: 'john@example.com', order: 1 } + ], + fields: [ + { type: 'signature', page: 1, x: 100, y: 500, width: 200, height: 50, recipientOrder: 1 } + ] +}); +``` + +### TurboSign.prepareForSigningSingle(options) + +Upload a document and immediately send signature requests to all recipients. + +```typescript +const { documentId, recipients } = await TurboSign.prepareForSigningSingle({ + fileLink: 'https://example.com/document.pdf', + documentName: 'Service Agreement', + senderName: 'Your Company', + senderEmail: 'sender@company.com', + recipients: [ + { name: 'Recipient Name', email: 'recipient@example.com', order: 1 } + ], + fields: [ + { type: 'signature', page: 1, x: 100, y: 500, width: 200, height: 50, recipientOrder: 1 } + ] +}); +``` + +### TurboSign.getStatus(documentId) + +Check the status of a document and its recipients. + +```typescript +const status = await TurboSign.getStatus('document-uuid'); + +console.log(status.status); // 'pending' | 'completed' | 'voided' +console.log(status.completedAt); // Date when all signatures completed + +for (const recipient of status.recipients) { + console.log(`${recipient.name}: ${recipient.status}`); // 'pending' | 'signed' + console.log(`Signed at: ${recipient.signedAt}`); +} +``` + +### TurboSign.download(documentId) + +Download the completed signed document. + +```typescript +const pdfBuffer = await TurboSign.download('document-uuid'); + +// Save to file (Node.js) +import { writeFileSync } from 'fs'; +writeFileSync('signed-contract.pdf', pdfBuffer); + +// Or upload to cloud storage +await s3.upload({ Body: pdfBuffer, Key: 'signed-contract.pdf' }); +``` + +### TurboSign.void(documentId, reason?) + +Cancel/void a signature request. + +```typescript +await TurboSign.void('document-uuid', 'Contract terms changed'); +``` + +### TurboSign.resend(documentId, recipientIds?) + +Resend signature request emails to specific recipients. + +```typescript +// Resend to all pending recipients +await TurboSign.resend('document-uuid'); + +// Resend to specific recipients +await TurboSign.resend('document-uuid', ['recipient-uuid-1', 'recipient-uuid-2']); +``` + +--- + +## Framework Examples + +### Express.js + +```typescript +import express from 'express'; +import { TurboSign } from '@turbodocx/sdk'; + +const app = express(); +app.use(express.json()); + +TurboSign.configure({ apiKey: process.env.TURBODOCX_API_KEY }); + +app.post('/api/send-contract', async (req, res) => { + try { + const { recipientName, recipientEmail, contractUrl } = req.body; + + const result = await TurboSign.prepareForSigningSingle({ + fileLink: contractUrl, + recipients: [{ name: recipientName, email: recipientEmail, order: 1 }], + fields: [ + { type: 'signature', page: 1, x: 100, y: 650, width: 200, height: 50, recipientOrder: 1 } + ] + }); + + res.json({ documentId: result.documentId, signUrl: result.recipients[0].signUrl }); + } catch (error) { + res.status(500).json({ error: error.message }); + } +}); + +app.get('/api/document/:id/status', async (req, res) => { + try { + const status = await TurboSign.getStatus(req.params.id); + res.json(status); + } catch (error) { + res.status(500).json({ error: error.message }); + } +}); + +app.listen(3000); +``` + +### Next.js API Routes + +```typescript +// pages/api/send-for-signature.ts +import type { NextApiRequest, NextApiResponse } from 'next'; +import { TurboSign } from '@turbodocx/sdk'; + +TurboSign.configure({ apiKey: process.env.TURBODOCX_API_KEY }); + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + if (req.method !== 'POST') { + return res.status(405).json({ error: 'Method not allowed' }); + } + + const { recipients, documentUrl, fields } = req.body; + + try { + const result = await TurboSign.prepareForSigningSingle({ + fileLink: documentUrl, + recipients, + fields + }); + + res.status(200).json(result); + } catch (error) { + res.status(500).json({ error: error.message }); + } +} +``` + +### NestJS Service + +```typescript +import { Injectable } from '@nestjs/common'; +import { TurboSign } from '@turbodocx/sdk'; + +@Injectable() +export class SignatureService { + constructor() { + TurboSign.configure({ apiKey: process.env.TURBODOCX_API_KEY }); + } + + async sendForSignature(documentUrl: string, recipientEmail: string, recipientName: string) { + return TurboSign.prepareForSigningSingle({ + fileLink: documentUrl, + recipients: [{ name: recipientName, email: recipientEmail, order: 1 }], + fields: [ + { type: 'signature', page: 1, x: 100, y: 650, width: 200, height: 50, recipientOrder: 1 } + ] + }); + } + + async getDocumentStatus(documentId: string) { + return TurboSign.getStatus(documentId); + } + + async downloadSignedDocument(documentId: string) { + return TurboSign.download(documentId); + } +} +``` + +--- + +## Error Handling + +```typescript +import { TurboSign, TurboDocxError, TurboDocxErrorCode } from '@turbodocx/sdk'; + +try { + const result = await TurboSign.prepareForSigningSingle({ /* ... */ }); +} catch (error) { + if (error instanceof TurboDocxError) { + switch (error.code) { + case TurboDocxErrorCode.UNAUTHORIZED: + console.error('Invalid API key'); + break; + case TurboDocxErrorCode.INVALID_DOCUMENT: + console.error('Could not process document:', error.message); + break; + case TurboDocxErrorCode.RATE_LIMITED: + console.error('Rate limited, retry after:', error.retryAfter); + break; + default: + console.error(`Error ${error.code}: ${error.message}`); + } + } else { + // Network or unexpected error + throw error; + } +} +``` + +--- + +## TypeScript Types + +The SDK exports all types for full TypeScript support: + +```typescript +import type { + SigningRequest, + SigningResult, + Recipient, + Field, + DocumentStatus, + TurboDocxError +} from '@turbodocx/sdk'; + +// Type-safe field creation +const field: Field = { + type: 'signature', + page: 1, + x: 100, + y: 500, + width: 200, + height: 50, + recipientOrder: 1 +}; + +// Type-safe recipient +const recipient: Recipient = { + name: 'John Doe', + email: 'john@example.com', + order: 1 +}; +``` + +--- + +## Field Types + +| Type | Description | Required by Signer | +|:-----|:------------|:-------------------| +| `signature` | Electronic signature drawing/typing | Yes | +| `initials` | Initials field | Yes | +| `text` | Free-form text input | Configurable | +| `date` | Date stamp (auto-filled on signing) | Auto | +| `checkbox` | Checkbox for agreements | Configurable | +| `full_name` | Signer's full name | Auto | +| `email` | Signer's email address | Auto | +| `title` | Job title field | Optional | +| `company` | Company name field | Optional | + +--- + +## Webhook Signature Verification + +Verify that webhooks are genuinely from TurboDocx: + +```typescript +import { verifyWebhookSignature } from '@turbodocx/sdk'; +import express from 'express'; + +const app = express(); + +// Use raw body for signature verification +app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => { + const signature = req.headers['x-turbodocx-signature'] as string; + const timestamp = req.headers['x-turbodocx-timestamp'] as string; + const body = req.body; + + const isValid = verifyWebhookSignature({ + signature, + timestamp, + body, + secret: process.env.TURBODOCX_WEBHOOK_SECRET + }); + + if (!isValid) { + return res.status(401).json({ error: 'Invalid signature' }); + } + + const event = JSON.parse(body.toString()); + + switch (event.event) { + case 'signature.document.completed': + console.log('Document completed:', event.data.document_id); + break; + case 'signature.document.voided': + console.log('Document voided:', event.data.document_id); + break; + } + + res.status(200).json({ received: true }); +}); +``` + +--- + +## Resources + +- [GitHub Repository](https://github.com/TurboDocx/SDK/tree/main/packages/js-sdk) +- [npm Package](https://www.npmjs.com/package/@turbodocx/sdk) +- [API Reference](/docs/TurboSign/API-Signatures) +- [Webhook Configuration](/docs/TurboSign/Webhooks) diff --git a/docs/SDKs/python.md b/docs/SDKs/python.md new file mode 100644 index 0000000..59f62cf --- /dev/null +++ b/docs/SDKs/python.md @@ -0,0 +1,486 @@ +--- +title: Python SDK +sidebar_position: 3 +sidebar_label: Python +description: Official TurboDocx Python SDK. Async-first design with sync wrappers for document generation and digital signatures. +keywords: + - turbodocx python + - turbosign python + - python sdk + - pip turbodocx + - asyncio sdk + - fastapi turbodocx + - django turbodocx + - document api python +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Python SDK + +The official TurboDocx SDK for Python applications. Async-first design with synchronous wrappers for flexibility. + +[![PyPI version](https://img.shields.io/pypi/v/turbodocx-sdk?logo=python&logoColor=white)](https://pypi.org/project/turbodocx-sdk/) +[![GitHub](https://img.shields.io/github/stars/turbodocx/sdk?style=social)](https://github.com/TurboDocx/SDK) + +## Installation + + + + +```bash +pip install turbodocx-sdk +``` + + + + +```bash +poetry add turbodocx-sdk +``` + + + + +```bash +pipenv install turbodocx-sdk +``` + + + + +## Requirements + +- Python 3.8+ +- `httpx` (installed automatically) +- `pydantic` (installed automatically) + +--- + +## Configuration + +```python +from turbodocx import TurboSign +import os + +# Configure globally +TurboSign.configure(api_key=os.environ["TURBODOCX_API_KEY"]) + +# Or create an instance with configuration +client = TurboSign(api_key=os.environ["TURBODOCX_API_KEY"]) +``` + +### Environment Variables + +```bash +# .env +TURBODOCX_API_KEY=your_api_key_here +``` + +--- + +## Quick Start + +### Async Usage (Recommended) + +```python +import asyncio +from turbodocx import TurboSign +import os + +TurboSign.configure(api_key=os.environ["TURBODOCX_API_KEY"]) + +async def send_contract(): + result = await TurboSign.prepare_for_signing_single( + file_link="https://example.com/contract.pdf", + document_name="Service Agreement", + sender_name="Acme Corp", + sender_email="contracts@acme.com", + recipients=[ + {"name": "Alice Smith", "email": "alice@example.com", "order": 1}, + {"name": "Bob Johnson", "email": "bob@example.com", "order": 2} + ], + fields=[ + # Alice's signature + {"type": "signature", "page": 1, "x": 100, "y": 650, "width": 200, "height": 50, "recipient_order": 1}, + {"type": "date", "page": 1, "x": 320, "y": 650, "width": 100, "height": 30, "recipient_order": 1}, + # Bob's signature + {"type": "signature", "page": 1, "x": 100, "y": 720, "width": 200, "height": 50, "recipient_order": 2}, + {"type": "date", "page": 1, "x": 320, "y": 720, "width": 100, "height": 30, "recipient_order": 2} + ] + ) + + print(f"Document ID: {result.document_id}") + for recipient in result.recipients: + print(f"{recipient.name}: {recipient.sign_url}") + +asyncio.run(send_contract()) +``` + +### Synchronous Usage + +```python +from turbodocx import TurboSign +import os + +TurboSign.configure(api_key=os.environ["TURBODOCX_API_KEY"]) + +# Use sync_ prefix for synchronous methods +result = TurboSign.sync_prepare_for_signing_single( + file_link="https://example.com/contract.pdf", + recipients=[{"name": "John Doe", "email": "john@example.com", "order": 1}], + fields=[ + {"type": "signature", "page": 1, "x": 100, "y": 500, "width": 200, "height": 50, "recipient_order": 1} + ] +) + +print(f"Sign URL: {result.recipients[0].sign_url}") +``` + +### Using Template-Based Fields + +```python +result = await TurboSign.prepare_for_signing_single( + file_link="https://example.com/contract-with-placeholders.pdf", + recipients=[{"name": "Alice Smith", "email": "alice@example.com", "order": 1}], + fields=[ + {"type": "signature", "anchor": "{SIGNATURE_ALICE}", "width": 200, "height": 50, "recipient_order": 1}, + {"type": "date", "anchor": "{DATE_ALICE}", "width": 100, "height": 30, "recipient_order": 1} + ] +) +``` + +--- + +## API Reference + +### TurboSign.configure() + +Configure the SDK with your API credentials. + +```python +TurboSign.configure( + api_key: str, # Required: Your TurboDocx API key + base_url: str = None, # Optional: API base URL + timeout: int = 30 # Optional: Request timeout in seconds +) +``` + +### prepare_for_review() + +Upload a document for preview without sending signature request emails. + +```python +result = await TurboSign.prepare_for_review( + file_link="https://example.com/document.pdf", + # or upload directly: + # file=open("document.pdf", "rb"), + document_name="Contract Draft", + recipients=[{"name": "John Doe", "email": "john@example.com", "order": 1}], + fields=[{"type": "signature", "page": 1, "x": 100, "y": 500, "width": 200, "height": 50, "recipient_order": 1}] +) + +print(result.document_id) +print(result.preview_url) +``` + +### prepare_for_signing_single() + +Upload a document and immediately send signature requests. + +```python +result = await TurboSign.prepare_for_signing_single( + file_link="https://example.com/document.pdf", + document_name="Service Agreement", + sender_name="Your Company", + sender_email="sender@company.com", + recipients=[{"name": "Recipient Name", "email": "recipient@example.com", "order": 1}], + fields=[{"type": "signature", "page": 1, "x": 100, "y": 500, "width": 200, "height": 50, "recipient_order": 1}] +) +``` + +### get_status() + +Check the status of a document and its recipients. + +```python +status = await TurboSign.get_status("document-uuid") + +print(status.status) # 'pending', 'completed', or 'voided' +print(status.completed_at) + +for recipient in status.recipients: + print(f"{recipient.name}: {recipient.status}") + print(f"Signed at: {recipient.signed_at}") +``` + +### download() + +Download the completed signed document. + +```python +pdf_bytes = await TurboSign.download("document-uuid") + +# Save to file +with open("signed-contract.pdf", "wb") as f: + f.write(pdf_bytes) + +# Or upload to S3 +import boto3 +s3 = boto3.client("s3") +s3.put_object(Bucket="my-bucket", Key="signed-contract.pdf", Body=pdf_bytes) +``` + +### void() + +Cancel/void a signature request. + +```python +await TurboSign.void("document-uuid", reason="Contract terms changed") +``` + +### resend() + +Resend signature request emails. + +```python +# Resend to all pending recipients +await TurboSign.resend("document-uuid") + +# Resend to specific recipients +await TurboSign.resend("document-uuid", recipient_ids=["recipient-uuid-1", "recipient-uuid-2"]) +``` + +--- + +## Framework Examples + +### FastAPI + +```python +from fastapi import FastAPI, HTTPException +from pydantic import BaseModel +from turbodocx import TurboSign +import os + +app = FastAPI() +TurboSign.configure(api_key=os.environ["TURBODOCX_API_KEY"]) + +class SendContractRequest(BaseModel): + recipient_name: str + recipient_email: str + contract_url: str + +class SendContractResponse(BaseModel): + document_id: str + sign_url: str + +@app.post("/api/send-contract", response_model=SendContractResponse) +async def send_contract(request: SendContractRequest): + try: + result = await TurboSign.prepare_for_signing_single( + file_link=request.contract_url, + recipients=[{"name": request.recipient_name, "email": request.recipient_email, "order": 1}], + fields=[{"type": "signature", "page": 1, "x": 100, "y": 650, "width": 200, "height": 50, "recipient_order": 1}] + ) + return SendContractResponse( + document_id=result.document_id, + sign_url=result.recipients[0].sign_url + ) + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) + +@app.get("/api/document/{document_id}/status") +async def get_document_status(document_id: str): + try: + return await TurboSign.get_status(document_id) + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) +``` + +### Django + +```python +# views.py +from django.http import JsonResponse +from django.views.decorators.http import require_http_methods +from django.views.decorators.csrf import csrf_exempt +from turbodocx import TurboSign +import json +import os + +TurboSign.configure(api_key=os.environ["TURBODOCX_API_KEY"]) + +@csrf_exempt +@require_http_methods(["POST"]) +def send_contract(request): + data = json.loads(request.body) + + # Use sync version in Django (unless using async views) + result = TurboSign.sync_prepare_for_signing_single( + file_link=data["contract_url"], + recipients=[{"name": data["recipient_name"], "email": data["recipient_email"], "order": 1}], + fields=[{"type": "signature", "page": 1, "x": 100, "y": 650, "width": 200, "height": 50, "recipient_order": 1}] + ) + + return JsonResponse({ + "document_id": result.document_id, + "sign_url": result.recipients[0].sign_url + }) + +@require_http_methods(["GET"]) +def document_status(request, document_id): + status = TurboSign.sync_get_status(document_id) + return JsonResponse({ + "status": status.status, + "recipients": [ + {"name": r.name, "status": r.status} + for r in status.recipients + ] + }) +``` + +### Flask + +```python +from flask import Flask, request, jsonify +from turbodocx import TurboSign +import os + +app = Flask(__name__) +TurboSign.configure(api_key=os.environ["TURBODOCX_API_KEY"]) + +@app.route("/api/send-contract", methods=["POST"]) +def send_contract(): + data = request.json + + result = TurboSign.sync_prepare_for_signing_single( + file_link=data["contract_url"], + recipients=[{"name": data["recipient_name"], "email": data["recipient_email"], "order": 1}], + fields=[{"type": "signature", "page": 1, "x": 100, "y": 650, "width": 200, "height": 50, "recipient_order": 1}] + ) + + return jsonify({ + "document_id": result.document_id, + "sign_url": result.recipients[0].sign_url + }) + +@app.route("/api/document//status") +def document_status(document_id): + status = TurboSign.sync_get_status(document_id) + return jsonify(status.dict()) + +if __name__ == "__main__": + app.run(debug=True) +``` + +--- + +## Error Handling + +```python +from turbodocx import TurboSign +from turbodocx.exceptions import ( + TurboDocxError, + UnauthorizedError, + InvalidDocumentError, + RateLimitedError, + NotFoundError +) + +try: + result = await TurboSign.prepare_for_signing_single(...) +except UnauthorizedError: + print("Invalid API key") +except InvalidDocumentError as e: + print(f"Could not process document: {e.message}") +except RateLimitedError as e: + print(f"Rate limited, retry after: {e.retry_after} seconds") +except NotFoundError: + print("Document not found") +except TurboDocxError as e: + print(f"Error {e.code}: {e.message}") +``` + +--- + +## Type Hints + +The SDK includes full type annotations: + +```python +from turbodocx.types import ( + SigningRequest, + SigningResult, + Recipient, + Field, + DocumentStatus, + FieldType +) + +# Type-safe field creation +field: Field = { + "type": FieldType.SIGNATURE, + "page": 1, + "x": 100, + "y": 500, + "width": 200, + "height": 50, + "recipient_order": 1 +} + +# Type-safe recipient +recipient: Recipient = { + "name": "John Doe", + "email": "john@example.com", + "order": 1 +} +``` + +--- + +## Webhook Signature Verification + +Verify that webhooks are genuinely from TurboDocx: + +```python +from turbodocx import verify_webhook_signature +from fastapi import FastAPI, Request, HTTPException + +app = FastAPI() + +@app.post("/webhook") +async def handle_webhook(request: Request): + body = await request.body() + signature = request.headers.get("x-turbodocx-signature") + timestamp = request.headers.get("x-turbodocx-timestamp") + + is_valid = verify_webhook_signature( + signature=signature, + timestamp=timestamp, + body=body, + secret=os.environ["TURBODOCX_WEBHOOK_SECRET"] + ) + + if not is_valid: + raise HTTPException(status_code=401, detail="Invalid signature") + + event = json.loads(body) + + if event["event"] == "signature.document.completed": + print(f"Document completed: {event['data']['document_id']}") + elif event["event"] == "signature.document.voided": + print(f"Document voided: {event['data']['document_id']}") + + return {"received": True} +``` + +--- + +## Resources + +- [GitHub Repository](https://github.com/TurboDocx/SDK/tree/main/packages/py-sdk) +- [PyPI Package](https://pypi.org/project/turbodocx-sdk/) +- [API Reference](/docs/TurboSign/API-Signatures) +- [Webhook Configuration](/docs/TurboSign/Webhooks) diff --git a/docs/SDKs/ruby.md b/docs/SDKs/ruby.md new file mode 100644 index 0000000..b101452 --- /dev/null +++ b/docs/SDKs/ruby.md @@ -0,0 +1,514 @@ +--- +title: Ruby SDK +sidebar_position: 7 +sidebar_label: Ruby +description: Official TurboDocx Ruby SDK. Idiomatic Ruby with Rails and Sidekiq integration for document generation and digital signatures. +keywords: + - turbodocx ruby + - turbosign ruby + - rubygems turbodocx + - rails sdk + - sidekiq integration + - document api ruby + - esignature ruby +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Ruby SDK + +The official TurboDocx SDK for Ruby applications. Idiomatic Ruby with Rails and background job integration. + +[![Gem Version](https://img.shields.io/gem/v/turbodocx-sdk?logo=ruby&logoColor=white)](https://rubygems.org/gems/turbodocx-sdk) +[![GitHub](https://img.shields.io/github/stars/turbodocx/sdk?style=social)](https://github.com/TurboDocx/SDK) + +## Installation + + + + +```bash +gem install turbodocx-sdk +``` + + + + +```ruby +# Gemfile +gem 'turbodocx-sdk' +``` + +Then run: + +```bash +bundle install +``` + + + + +## Requirements + +- Ruby 3.0+ +- `faraday` (installed automatically) + +--- + +## Configuration + +### Basic Configuration + +```ruby +require 'turbodocx' + +# Global configuration +TurboDocx.configure do |config| + config.api_key = ENV['TURBODOCX_API_KEY'] + # Optional + config.base_url = 'https://api.turbodocx.com' + config.timeout = 30 +end + +# Or create a client instance +client = TurboDocx::TurboSign.new(api_key: ENV['TURBODOCX_API_KEY']) +``` + +### Rails Configuration + +```ruby +# config/initializers/turbodocx.rb +TurboDocx.configure do |config| + config.api_key = Rails.application.credentials.turbodocx[:api_key] +end +``` + +--- + +## Quick Start + +### Send a Document for Signature + +```ruby +require 'turbodocx' + +TurboDocx.configure do |config| + config.api_key = ENV['TURBODOCX_API_KEY'] +end + +result = TurboDocx::TurboSign.prepare_for_signing_single( + file_link: 'https://example.com/contract.pdf', + document_name: 'Service Agreement', + sender_name: 'Acme Corp', + sender_email: 'contracts@acme.com', + recipients: [ + { name: 'Alice Smith', email: 'alice@example.com', order: 1 }, + { name: 'Bob Johnson', email: 'bob@example.com', order: 2 } + ], + fields: [ + # Alice's signature + { type: 'signature', page: 1, x: 100, y: 650, width: 200, height: 50, recipient_order: 1 }, + { type: 'date', page: 1, x: 320, y: 650, width: 100, height: 30, recipient_order: 1 }, + # Bob's signature + { type: 'signature', page: 1, x: 100, y: 720, width: 200, height: 50, recipient_order: 2 }, + { type: 'date', page: 1, x: 320, y: 720, width: 100, height: 30, recipient_order: 2 } + ] +) + +puts "Document ID: #{result.document_id}" +result.recipients.each do |recipient| + puts "#{recipient.name}: #{recipient.sign_url}" +end +``` + +### Using Template-Based Fields + +```ruby +result = TurboDocx::TurboSign.prepare_for_signing_single( + file_link: 'https://example.com/contract-with-placeholders.pdf', + recipients: [ + { name: 'Alice Smith', email: 'alice@example.com', order: 1 } + ], + fields: [ + { type: 'signature', anchor: '{SIGNATURE_ALICE}', width: 200, height: 50, recipient_order: 1 }, + { type: 'date', anchor: '{DATE_ALICE}', width: 100, height: 30, recipient_order: 1 } + ] +) +``` + +--- + +## API Reference + +### prepare_for_review + +Upload a document for preview without sending emails. + +```ruby +result = TurboDocx::TurboSign.prepare_for_review( + file_link: 'https://example.com/document.pdf', + # Or upload directly: + # file: File.read('document.pdf'), + document_name: 'Contract Draft', + recipients: [ + { name: 'John Doe', email: 'john@example.com', order: 1 } + ], + fields: [ + { type: 'signature', page: 1, x: 100, y: 500, width: 200, height: 50, recipient_order: 1 } + ] +) + +puts result.document_id +puts result.preview_url +``` + +### prepare_for_signing_single + +Upload a document and immediately send signature requests. + +```ruby +result = TurboDocx::TurboSign.prepare_for_signing_single( + file_link: 'https://example.com/document.pdf', + document_name: 'Service Agreement', + sender_name: 'Your Company', + sender_email: 'sender@company.com', + recipients: [ + { name: 'Recipient Name', email: 'recipient@example.com', order: 1 } + ], + fields: [ + { type: 'signature', page: 1, x: 100, y: 500, width: 200, height: 50, recipient_order: 1 } + ] +) +``` + +### get_status + +Check the status of a document. + +```ruby +status = TurboDocx::TurboSign.get_status('document-uuid') + +puts status.status # 'pending', 'completed', or 'voided' +puts status.completed_at + +status.recipients.each do |recipient| + puts "#{recipient.name}: #{recipient.status}" + puts "Signed at: #{recipient.signed_at}" +end +``` + +### download + +Download the completed signed document. + +```ruby +pdf_data = TurboDocx::TurboSign.download('document-uuid') + +# Save to file +File.binwrite('signed-contract.pdf', pdf_data) + +# Or upload to S3 +s3_client.put_object( + bucket: 'my-bucket', + key: 'signed-contract.pdf', + body: pdf_data +) +``` + +### void + +Cancel/void a signature request. + +```ruby +TurboDocx::TurboSign.void('document-uuid', reason: 'Contract terms changed') +``` + +### resend + +Resend signature request emails. + +```ruby +# Resend to all pending recipients +TurboDocx::TurboSign.resend('document-uuid') + +# Resend to specific recipients +TurboDocx::TurboSign.resend('document-uuid', recipient_ids: ['recipient-uuid-1', 'recipient-uuid-2']) +``` + +--- + +## Rails Examples + +### Controller + +```ruby +# app/controllers/api/contracts_controller.rb +class Api::ContractsController < ApplicationController + def send_contract + result = TurboDocx::TurboSign.prepare_for_signing_single( + file_link: params[:contract_url], + recipients: [ + { name: params[:recipient_name], email: params[:recipient_email], order: 1 } + ], + fields: [ + { type: 'signature', page: 1, x: 100, y: 650, width: 200, height: 50, recipient_order: 1 } + ] + ) + + render json: { + document_id: result.document_id, + sign_url: result.recipients.first.sign_url + } + rescue TurboDocx::Error => e + render json: { error: e.message }, status: :unprocessable_entity + end + + def status + status = TurboDocx::TurboSign.get_status(params[:id]) + render json: status + rescue TurboDocx::NotFoundError + render json: { error: 'Document not found' }, status: :not_found + end + + def download + pdf_data = TurboDocx::TurboSign.download(params[:id]) + send_data pdf_data, + type: 'application/pdf', + filename: 'signed-document.pdf', + disposition: 'attachment' + end +end +``` + +### Routes + +```ruby +# config/routes.rb +Rails.application.routes.draw do + namespace :api do + post 'contracts/send', to: 'contracts#send_contract' + get 'contracts/:id/status', to: 'contracts#status' + get 'contracts/:id/download', to: 'contracts#download' + end +end +``` + +### Service Object + +```ruby +# app/services/contract_signing_service.rb +class ContractSigningService + def initialize(document_url:, recipient_name:, recipient_email:) + @document_url = document_url + @recipient_name = recipient_name + @recipient_email = recipient_email + end + + def call + TurboDocx::TurboSign.prepare_for_signing_single( + file_link: @document_url, + recipients: [ + { name: @recipient_name, email: @recipient_email, order: 1 } + ], + fields: signature_fields + ) + end + + private + + def signature_fields + [ + { type: 'signature', page: 1, x: 100, y: 650, width: 200, height: 50, recipient_order: 1 }, + { type: 'date', page: 1, x: 320, y: 650, width: 100, height: 30, recipient_order: 1 } + ] + end +end + +# Usage +result = ContractSigningService.new( + document_url: 'https://example.com/contract.pdf', + recipient_name: 'John Doe', + recipient_email: 'john@example.com' +).call +``` + +--- + +## Background Jobs + +### Sidekiq + +```ruby +# app/jobs/send_contract_for_signature_job.rb +class SendContractForSignatureJob + include Sidekiq::Job + + def perform(contract_id, recipient_email, recipient_name) + contract = Contract.find(contract_id) + + result = TurboDocx::TurboSign.prepare_for_signing_single( + file_link: contract.document_url, + document_name: contract.name, + recipients: [ + { name: recipient_name, email: recipient_email, order: 1 } + ], + fields: [ + { type: 'signature', page: 1, x: 100, y: 650, width: 200, height: 50, recipient_order: 1 } + ] + ) + + contract.update!( + turbosign_document_id: result.document_id, + sign_url: result.recipients.first.sign_url, + status: 'pending_signature' + ) + rescue TurboDocx::Error => e + contract.update!(status: 'signature_failed', error_message: e.message) + raise + end +end + +# Usage +SendContractForSignatureJob.perform_async(contract.id, 'john@example.com', 'John Doe') +``` + +### ActiveJob + +```ruby +# app/jobs/check_signature_status_job.rb +class CheckSignatureStatusJob < ApplicationJob + queue_as :default + + def perform(contract_id) + contract = Contract.find(contract_id) + return unless contract.turbosign_document_id.present? + + status = TurboDocx::TurboSign.get_status(contract.turbosign_document_id) + + case status.status + when 'completed' + contract.update!(status: 'signed', signed_at: status.completed_at) + ContractSignedMailer.notify(contract).deliver_later + when 'voided' + contract.update!(status: 'voided') + else + # Recheck later + CheckSignatureStatusJob.set(wait: 1.hour).perform_later(contract_id) + end + end +end +``` + +--- + +## Error Handling + +```ruby +begin + result = TurboDocx::TurboSign.prepare_for_signing_single(...) +rescue TurboDocx::UnauthorizedError + puts 'Invalid API key' +rescue TurboDocx::InvalidDocumentError => e + puts "Could not process document: #{e.message}" +rescue TurboDocx::RateLimitedError => e + puts "Rate limited, retry after: #{e.retry_after} seconds" +rescue TurboDocx::NotFoundError + puts 'Document not found' +rescue TurboDocx::Error => e + puts "Error #{e.code}: #{e.message}" +end +``` + +--- + +## Field Types + +```ruby +# Available field types +'signature' # Electronic signature drawing/typing +'initials' # Initials field +'text' # Free-form text input +'date' # Date stamp (auto-filled on signing) +'checkbox' # Checkbox for agreements +'full_name' # Signer's full name (auto-filled) +'email' # Signer's email address (auto-filled) +'title' # Job title field +'company' # Company name field +``` + +--- + +## Webhook Signature Verification + +Verify that webhooks are genuinely from TurboDocx: + +```ruby +# app/controllers/webhooks_controller.rb +class WebhooksController < ApplicationController + skip_before_action :verify_authenticity_token + + def turbodocx + signature = request.headers['X-TurboDocx-Signature'] + timestamp = request.headers['X-TurboDocx-Timestamp'] + body = request.raw_post + + is_valid = TurboDocx::WebhookVerifier.verify( + signature: signature, + timestamp: timestamp, + body: body, + secret: ENV['TURBODOCX_WEBHOOK_SECRET'] + ) + + unless is_valid + return render json: { error: 'Invalid signature' }, status: :unauthorized + end + + event = JSON.parse(body) + + case event['event'] + when 'signature.document.completed' + handle_document_completed(event['data']) + when 'signature.document.voided' + handle_document_voided(event['data']) + end + + render json: { received: true } + end + + private + + def handle_document_completed(data) + contract = Contract.find_by(turbosign_document_id: data['document_id']) + return unless contract + + contract.update!(status: 'signed', signed_at: Time.parse(data['completed_at'])) + DownloadSignedDocumentJob.perform_later(contract.id) + end + + def handle_document_voided(data) + contract = Contract.find_by(turbosign_document_id: data['document_id']) + return unless contract + + contract.update!(status: 'voided', void_reason: data['void_reason']) + end +end +``` + +### Routes + +```ruby +# config/routes.rb +Rails.application.routes.draw do + post 'webhooks/turbodocx', to: 'webhooks#turbodocx' +end +``` + +--- + +## Resources + +- [GitHub Repository](https://github.com/TurboDocx/SDK/tree/main/packages/ruby-sdk) +- [RubyGems Package](https://rubygems.org/gems/turbodocx-sdk) +- [API Reference](/docs/TurboSign/API-Signatures) +- [Webhook Configuration](/docs/TurboSign/Webhooks) diff --git a/docs/TurboSign/API Signatures.md b/docs/TurboSign/API Signatures.md index b73de02..bc8594c 100644 --- a/docs/TurboSign/API Signatures.md +++ b/docs/TurboSign/API Signatures.md @@ -66,6 +66,21 @@ TurboSign offers two single-step endpoints to fit different workflows: - **Real-time Status Updates**: Track document status throughout the signing process - **Webhook Integration**: Receive notifications when signing is complete +:::tip Prefer using an SDK? +We offer official SDKs that handle authentication, error handling, and type safety for you: + +| Language | Package | Quick Install | +|:---------|:--------|:--------------| +| **JavaScript/TypeScript** | [@turbodocx/sdk](/docs/SDKs/javascript) | `npm install @turbodocx/sdk` | +| **Python** | [turbodocx-sdk](/docs/SDKs/python) | `pip install turbodocx-sdk` | +| **Go** | [turbodocx-sdk](/docs/SDKs/go) | `go get github.com/turbodocx/sdk` | +| **C# / .NET** | [TurboDocx.Sdk](/docs/SDKs/dotnet) | `dotnet add package TurboDocx.Sdk` | +| **Java** | [com.turbodocx:sdk](/docs/SDKs/java) | [Maven Central](https://search.maven.org/artifact/com.turbodocx/sdk) | +| **Ruby** | [turbodocx-sdk](/docs/SDKs/ruby) | `gem install turbodocx-sdk` | + +[View all SDKs →](/docs/SDKs) +::: + ## TLDR; Complete Working Example 🚀 Don't want to read all the details? Here's what you need to know: diff --git a/docs/TurboSign/Webhooks.md b/docs/TurboSign/Webhooks.md index ff46520..27a58cd 100644 --- a/docs/TurboSign/Webhooks.md +++ b/docs/TurboSign/Webhooks.md @@ -210,12 +210,155 @@ Each webhook request includes these headers: ### Try it Now - +### SDK Verification Examples + +Our SDKs include built-in webhook verification. Here are examples for each language: + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + + + + +```typescript +import { verifyWebhookSignature } from '@turbodocx/sdk'; +import express from 'express'; + +const app = express(); + +app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => { + const isValid = verifyWebhookSignature({ + signature: req.headers['x-turbodocx-signature'], + timestamp: req.headers['x-turbodocx-timestamp'], + body: req.body, + secret: process.env.TURBODOCX_WEBHOOK_SECRET + }); + + if (!isValid) { + return res.status(401).json({ error: 'Invalid signature' }); + } + + const event = JSON.parse(req.body.toString()); + // Process event... + res.status(200).json({ received: true }); +}); +``` + + + + +```python +from turbodocx import verify_webhook_signature +from fastapi import FastAPI, Request, HTTPException + +app = FastAPI() + +@app.post("/webhook") +async def handle_webhook(request: Request): + body = await request.body() + + is_valid = verify_webhook_signature( + signature=request.headers.get("x-turbodocx-signature"), + timestamp=request.headers.get("x-turbodocx-timestamp"), + body=body, + secret=os.environ["TURBODOCX_WEBHOOK_SECRET"] + ) + + if not is_valid: + raise HTTPException(status_code=401, detail="Invalid signature") + + event = json.loads(body) + # Process event... + return {"received": True} +``` + + + + +```go +func webhookHandler(w http.ResponseWriter, r *http.Request) { + body, _ := io.ReadAll(r.Body) + + isValid := sdk.VerifyWebhookSignature( + r.Header.Get("X-TurboDocx-Signature"), + r.Header.Get("X-TurboDocx-Timestamp"), + body, + os.Getenv("TURBODOCX_WEBHOOK_SECRET"), + ) + + if !isValid { + http.Error(w, "Invalid signature", http.StatusUnauthorized) + return + } + + var event sdk.WebhookEvent + json.Unmarshal(body, &event) + // Process event... + w.WriteHeader(http.StatusOK) +} +``` + + + + +```csharp +[HttpPost] +public async Task HandleWebhook() +{ + using var reader = new StreamReader(Request.Body); + var body = await reader.ReadToEndAsync(); + + var isValid = WebhookVerifier.VerifySignature( + Request.Headers["X-TurboDocx-Signature"], + Request.Headers["X-TurboDocx-Timestamp"], + body, + _configuration["TurboDocx:WebhookSecret"] + ); + + if (!isValid) + return Unauthorized("Invalid signature"); + + var webhookEvent = JsonSerializer.Deserialize(body); + // Process event... + return Ok(new { Received = true }); +} +``` + + + + +```ruby +def turbodocx_webhook + body = request.raw_post + + is_valid = TurboDocx::WebhookVerifier.verify( + signature: request.headers['X-TurboDocx-Signature'], + timestamp: request.headers['X-TurboDocx-Timestamp'], + body: body, + secret: ENV['TURBODOCX_WEBHOOK_SECRET'] + ) + + unless is_valid + return render json: { error: 'Invalid signature' }, status: :unauthorized + end + + event = JSON.parse(body) + # Process event... + render json: { received: true } +end +``` + + + + +[View full SDK documentation →](/docs/SDKs) + ### Security Best Practices 1. **Always verify signatures**: Never process webhooks without verifying the signature diff --git a/sidebars.js b/sidebars.js index 02fe794..fec8b30 100644 --- a/sidebars.js +++ b/sidebars.js @@ -17,6 +17,47 @@ const sidebars = { mySidebar: [ 'Welcome to TurboDocx', + { + type: 'category', + label: 'SDKs', + collapsed: false, + link: { + type: 'doc', + id: 'SDKs/index', + }, + items: [ + { + type: 'doc', + id: 'SDKs/javascript', + label: 'JavaScript / TypeScript', + }, + { + type: 'doc', + id: 'SDKs/python', + label: 'Python', + }, + { + type: 'doc', + id: 'SDKs/go', + label: 'Go', + }, + { + type: 'doc', + id: 'SDKs/dotnet', + label: '.NET / C#', + }, + { + type: 'doc', + id: 'SDKs/java', + label: 'Java', + }, + { + type: 'doc', + id: 'SDKs/ruby', + label: 'Ruby', + }, + ], + }, { type: 'category', label: 'TurboDocx Templating', From 86768e6cf003c8f7d568be208efc9d2a4018eb2d Mon Sep 17 00:00:00 2001 From: Amit Sharma Date: Wed, 10 Dec 2025 13:30:10 +0000 Subject: [PATCH 2/6] feat: added js/ts and python docs for SDKs --- docs/SDKs/dotnet.md | 555 -------------- docs/SDKs/go.md | 64 +- docs/SDKs/index.md | 523 ++++++++++---- docs/SDKs/java.md | 42 +- docs/SDKs/javascript.md | 1166 +++++++++++++++++++++++------- docs/SDKs/python.md | 611 +++++++++------- docs/SDKs/ruby.md | 514 ------------- docs/TurboSign/API Signatures.md | 13 +- docs/TurboSign/Webhooks.md | 2 +- sidebars.js | 10 - 10 files changed, 1704 insertions(+), 1796 deletions(-) delete mode 100644 docs/SDKs/dotnet.md delete mode 100644 docs/SDKs/ruby.md diff --git a/docs/SDKs/dotnet.md b/docs/SDKs/dotnet.md deleted file mode 100644 index 5d47229..0000000 --- a/docs/SDKs/dotnet.md +++ /dev/null @@ -1,555 +0,0 @@ ---- -title: .NET SDK -sidebar_position: 5 -sidebar_label: .NET / C# -description: Official TurboDocx .NET SDK. Full async/await support with dependency injection for ASP.NET Core applications. -keywords: - - turbodocx dotnet - - turbodocx csharp - - turbosign dotnet - - nuget turbodocx - - asp.net core sdk - - document api dotnet - - esignature csharp ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -# .NET SDK - -The official TurboDocx SDK for .NET applications. Full async/await support with ASP.NET Core dependency injection. - -[![NuGet version](https://img.shields.io/nuget/v/TurboDocx.Sdk?logo=nuget&logoColor=white)](https://www.nuget.org/packages/TurboDocx.Sdk) -[![GitHub](https://img.shields.io/github/stars/turbodocx/sdk?style=social)](https://github.com/TurboDocx/SDK) - -## Installation - - - - -```bash -dotnet add package TurboDocx.Sdk -``` - - - - -```powershell -Install-Package TurboDocx.Sdk -``` - - - - -```xml - -``` - - - - -## Requirements - -- .NET 6.0+ or .NET Standard 2.1+ - ---- - -## Configuration - -### Basic Configuration - -```csharp -using TurboDocx.Sdk; - -// Create a client directly -var turboSign = new TurboSignClient(Environment.GetEnvironmentVariable("TURBODOCX_API_KEY")); - -// Or with options -var turboSign = new TurboSignClient(new TurboSignOptions -{ - ApiKey = Environment.GetEnvironmentVariable("TURBODOCX_API_KEY"), - BaseUrl = "https://api.turbodocx.com", - Timeout = TimeSpan.FromSeconds(30) -}); -``` - -### ASP.NET Core Dependency Injection - -```csharp -// Program.cs or Startup.cs -builder.Services.AddTurboDocx(options => -{ - options.ApiKey = builder.Configuration["TurboDocx:ApiKey"]; -}); - -// Then inject ITurboSignClient in your services -public class ContractService -{ - private readonly ITurboSignClient _turboSign; - - public ContractService(ITurboSignClient turboSign) - { - _turboSign = turboSign; - } -} -``` - -### Configuration File - -```json -// appsettings.json -{ - "TurboDocx": { - "ApiKey": "your_api_key_here" - } -} -``` - ---- - -## Quick Start - -### Send a Document for Signature - -```csharp -using TurboDocx.Sdk; - -var turboSign = new TurboSignClient(Environment.GetEnvironmentVariable("TURBODOCX_API_KEY")); - -var result = await turboSign.PrepareForSigningSingleAsync(new SigningRequest -{ - FileLink = "https://example.com/contract.pdf", - DocumentName = "Service Agreement", - SenderName = "Acme Corp", - SenderEmail = "contracts@acme.com", - Recipients = new[] - { - new Recipient { Name = "Alice Smith", Email = "alice@example.com", Order = 1 }, - new Recipient { Name = "Bob Johnson", Email = "bob@example.com", Order = 2 } - }, - Fields = new[] - { - // Alice's signature - new Field { Type = FieldType.Signature, Page = 1, X = 100, Y = 650, Width = 200, Height = 50, RecipientOrder = 1 }, - new Field { Type = FieldType.Date, Page = 1, X = 320, Y = 650, Width = 100, Height = 30, RecipientOrder = 1 }, - // Bob's signature - new Field { Type = FieldType.Signature, Page = 1, X = 100, Y = 720, Width = 200, Height = 50, RecipientOrder = 2 }, - new Field { Type = FieldType.Date, Page = 1, X = 320, Y = 720, Width = 100, Height = 30, RecipientOrder = 2 } - } -}); - -Console.WriteLine($"Document ID: {result.DocumentId}"); -foreach (var recipient in result.Recipients) -{ - Console.WriteLine($"{recipient.Name}: {recipient.SignUrl}"); -} -``` - -### Using Template-Based Fields - -```csharp -var result = await turboSign.PrepareForSigningSingleAsync(new SigningRequest -{ - FileLink = "https://example.com/contract-with-placeholders.pdf", - Recipients = new[] - { - new Recipient { Name = "Alice Smith", Email = "alice@example.com", Order = 1 } - }, - Fields = new[] - { - new Field { Type = FieldType.Signature, Anchor = "{SIGNATURE_ALICE}", Width = 200, Height = 50, RecipientOrder = 1 }, - new Field { Type = FieldType.Date, Anchor = "{DATE_ALICE}", Width = 100, Height = 30, RecipientOrder = 1 } - } -}); -``` - ---- - -## API Reference - -### PrepareForReviewAsync() - -Upload a document for preview without sending emails. - -```csharp -var result = await turboSign.PrepareForReviewAsync(new ReviewRequest -{ - FileLink = "https://example.com/document.pdf", - // Or upload directly: - // File = await File.ReadAllBytesAsync("document.pdf"), - DocumentName = "Contract Draft", - Recipients = new[] - { - new Recipient { Name = "John Doe", Email = "john@example.com", Order = 1 } - }, - Fields = new[] - { - new Field { Type = FieldType.Signature, Page = 1, X = 100, Y = 500, Width = 200, Height = 50, RecipientOrder = 1 } - } -}); - -Console.WriteLine(result.DocumentId); -Console.WriteLine(result.PreviewUrl); -``` - -### PrepareForSigningSingleAsync() - -Upload a document and immediately send signature requests. - -```csharp -var result = await turboSign.PrepareForSigningSingleAsync(new SigningRequest -{ - FileLink = "https://example.com/document.pdf", - DocumentName = "Service Agreement", - SenderName = "Your Company", - SenderEmail = "sender@company.com", - Recipients = new[] - { - new Recipient { Name = "Recipient Name", Email = "recipient@example.com", Order = 1 } - }, - Fields = new[] - { - new Field { Type = FieldType.Signature, Page = 1, X = 100, Y = 500, Width = 200, Height = 50, RecipientOrder = 1 } - } -}); -``` - -### GetStatusAsync() - -Check the status of a document. - -```csharp -var status = await turboSign.GetStatusAsync("document-uuid"); - -Console.WriteLine(status.Status); // "pending", "completed", or "voided" -Console.WriteLine(status.CompletedAt); - -foreach (var recipient in status.Recipients) -{ - Console.WriteLine($"{recipient.Name}: {recipient.Status}"); - Console.WriteLine($"Signed at: {recipient.SignedAt}"); -} -``` - -### DownloadAsync() - -Download the completed signed document. - -```csharp -var pdfBytes = await turboSign.DownloadAsync("document-uuid"); - -// Save to file -await File.WriteAllBytesAsync("signed-contract.pdf", pdfBytes); - -// Or upload to Azure Blob Storage -await blobClient.UploadAsync(new MemoryStream(pdfBytes)); -``` - -### VoidAsync() - -Cancel/void a signature request. - -```csharp -await turboSign.VoidAsync("document-uuid", "Contract terms changed"); -``` - -### ResendAsync() - -Resend signature request emails. - -```csharp -// Resend to all pending recipients -await turboSign.ResendAsync("document-uuid"); - -// Resend to specific recipients -await turboSign.ResendAsync("document-uuid", new[] { "recipient-uuid-1", "recipient-uuid-2" }); -``` - ---- - -## ASP.NET Core Examples - -### Minimal API - -```csharp -using TurboDocx.Sdk; - -var builder = WebApplication.CreateBuilder(args); - -builder.Services.AddTurboDocx(options => -{ - options.ApiKey = builder.Configuration["TurboDocx:ApiKey"]; -}); - -var app = builder.Build(); - -app.MapPost("/api/send-contract", async ( - SendContractRequest request, - ITurboSignClient turboSign) => -{ - var result = await turboSign.PrepareForSigningSingleAsync(new SigningRequest - { - FileLink = request.ContractUrl, - Recipients = new[] - { - new Recipient { Name = request.RecipientName, Email = request.RecipientEmail, Order = 1 } - }, - Fields = new[] - { - new Field { Type = FieldType.Signature, Page = 1, X = 100, Y = 650, Width = 200, Height = 50, RecipientOrder = 1 } - } - }); - - return Results.Ok(new { result.DocumentId, SignUrl = result.Recipients[0].SignUrl }); -}); - -app.MapGet("/api/document/{id}/status", async (string id, ITurboSignClient turboSign) => -{ - var status = await turboSign.GetStatusAsync(id); - return Results.Ok(status); -}); - -app.Run(); - -record SendContractRequest(string RecipientName, string RecipientEmail, string ContractUrl); -``` - -### Controller-Based API - -```csharp -using Microsoft.AspNetCore.Mvc; -using TurboDocx.Sdk; - -[ApiController] -[Route("api/[controller]")] -public class ContractsController : ControllerBase -{ - private readonly ITurboSignClient _turboSign; - - public ContractsController(ITurboSignClient turboSign) - { - _turboSign = turboSign; - } - - [HttpPost("send")] - public async Task SendContract([FromBody] SendContractRequest request) - { - try - { - var result = await _turboSign.PrepareForSigningSingleAsync(new SigningRequest - { - FileLink = request.ContractUrl, - Recipients = new[] - { - new Recipient { Name = request.RecipientName, Email = request.RecipientEmail, Order = 1 } - }, - Fields = new[] - { - new Field { Type = FieldType.Signature, Page = 1, X = 100, Y = 650, Width = 200, Height = 50, RecipientOrder = 1 } - } - }); - - return Ok(new { result.DocumentId, SignUrl = result.Recipients[0].SignUrl }); - } - catch (TurboDocxException ex) - { - return StatusCode(500, new { Error = ex.Message }); - } - } - - [HttpGet("{id}/status")] - public async Task GetStatus(string id) - { - try - { - var status = await _turboSign.GetStatusAsync(id); - return Ok(status); - } - catch (TurboDocxException ex) when (ex.Code == ErrorCode.NotFound) - { - return NotFound(); - } - } - - [HttpGet("{id}/download")] - public async Task Download(string id) - { - var pdfBytes = await _turboSign.DownloadAsync(id); - return File(pdfBytes, "application/pdf", "signed-document.pdf"); - } -} - -public record SendContractRequest(string RecipientName, string RecipientEmail, string ContractUrl); -``` - ---- - -## Error Handling - -```csharp -using TurboDocx.Sdk; -using TurboDocx.Sdk.Exceptions; - -try -{ - var result = await turboSign.PrepareForSigningSingleAsync(request); -} -catch (UnauthorizedException) -{ - Console.WriteLine("Invalid API key"); -} -catch (InvalidDocumentException ex) -{ - Console.WriteLine($"Could not process document: {ex.Message}"); -} -catch (RateLimitedException ex) -{ - Console.WriteLine($"Rate limited, retry after: {ex.RetryAfter} seconds"); -} -catch (NotFoundException) -{ - Console.WriteLine("Document not found"); -} -catch (TurboDocxException ex) -{ - Console.WriteLine($"Error {ex.Code}: {ex.Message}"); -} -``` - ---- - -## Types - -### Field Types - -```csharp -public enum FieldType -{ - Signature, - Initials, - Text, - Date, - Checkbox, - FullName, - Email, - Title, - Company -} -``` - -### Models - -```csharp -public class Recipient -{ - public string Name { get; set; } - public string Email { get; set; } - public int Order { get; set; } - // Response properties - public string? Id { get; set; } - public string? Status { get; set; } - public string? SignUrl { get; set; } - public DateTime? SignedAt { get; set; } -} - -public class Field -{ - public FieldType Type { get; set; } - public int? Page { get; set; } - public int? X { get; set; } - public int? Y { get; set; } - public int Width { get; set; } - public int Height { get; set; } - public int RecipientOrder { get; set; } - public string? Anchor { get; set; } // For template-based fields -} - -public class SigningRequest -{ - public string? FileLink { get; set; } - public byte[]? File { get; set; } - public string? DocumentName { get; set; } - public string? SenderName { get; set; } - public string? SenderEmail { get; set; } - public Recipient[] Recipients { get; set; } - public Field[] Fields { get; set; } -} - -public class SigningResult -{ - public string DocumentId { get; set; } - public Recipient[] Recipients { get; set; } -} - -public class DocumentStatus -{ - public string Status { get; set; } // "pending", "completed", "voided" - public DateTime? CompletedAt { get; set; } - public Recipient[] Recipients { get; set; } -} -``` - ---- - -## Webhook Signature Verification - -Verify that webhooks are genuinely from TurboDocx: - -```csharp -using Microsoft.AspNetCore.Mvc; -using TurboDocx.Sdk; - -[ApiController] -[Route("webhook")] -public class WebhookController : ControllerBase -{ - private readonly IConfiguration _configuration; - - public WebhookController(IConfiguration configuration) - { - _configuration = configuration; - } - - [HttpPost] - public async Task HandleWebhook() - { - using var reader = new StreamReader(Request.Body); - var body = await reader.ReadToEndAsync(); - - var signature = Request.Headers["X-TurboDocx-Signature"].FirstOrDefault(); - var timestamp = Request.Headers["X-TurboDocx-Timestamp"].FirstOrDefault(); - var secret = _configuration["TurboDocx:WebhookSecret"]; - - var isValid = WebhookVerifier.VerifySignature(signature, timestamp, body, secret); - - if (!isValid) - { - return Unauthorized("Invalid signature"); - } - - var webhookEvent = JsonSerializer.Deserialize(body); - - switch (webhookEvent.Event) - { - case "signature.document.completed": - Console.WriteLine($"Document completed: {webhookEvent.Data.DocumentId}"); - break; - case "signature.document.voided": - Console.WriteLine($"Document voided: {webhookEvent.Data.DocumentId}"); - break; - } - - return Ok(new { Received = true }); - } -} -``` - ---- - -## Resources - -- [GitHub Repository](https://github.com/TurboDocx/SDK/tree/main/packages/dotnet-sdk) -- [NuGet Package](https://www.nuget.org/packages/TurboDocx.Sdk) -- [API Reference](/docs/TurboSign/API-Signatures) -- [Webhook Configuration](/docs/TurboSign/Webhooks) diff --git a/docs/SDKs/go.md b/docs/SDKs/go.md index 40235af..fd46ee9 100644 --- a/docs/SDKs/go.md +++ b/docs/SDKs/go.md @@ -14,6 +14,7 @@ keywords: import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; +import ScriptLoader from '@site/src/components/ScriptLoader'; # Go SDK @@ -47,11 +48,15 @@ import ( func main() { // Create a new client - client := sdk.NewTurboSign(os.Getenv("TURBODOCX_API_KEY")) + client := sdk.NewTurboSign( + os.Getenv("TURBODOCX_API_KEY"), + os.Getenv("TURBODOCX_ORG_ID"), + ) // Or with options client := sdk.NewTurboSign( os.Getenv("TURBODOCX_API_KEY"), + os.Getenv("TURBODOCX_ORG_ID"), sdk.WithBaseURL("https://api.turbodocx.com"), sdk.WithTimeout(30 * time.Second), ) @@ -83,7 +88,10 @@ import ( ) func main() { - client := sdk.NewTurboSign(os.Getenv("TURBODOCX_API_KEY")) + client := sdk.NewTurboSign( + os.Getenv("TURBODOCX_API_KEY"), + os.Getenv("TURBODOCX_ORG_ID"), + ) ctx := context.Background() @@ -93,16 +101,16 @@ func main() { SenderName: "Acme Corp", SenderEmail: "contracts@acme.com", Recipients: []sdk.Recipient{ - {Name: "Alice Smith", Email: "alice@example.com", Order: 1}, - {Name: "Bob Johnson", Email: "bob@example.com", Order: 2}, + {Name: "Alice Smith", Email: "alice@example.com", SigningOrder: 1}, + {Name: "Bob Johnson", Email: "bob@example.com", SigningOrder: 2}, }, Fields: []sdk.Field{ // Alice's signature - {Type: sdk.FieldTypeSignature, Page: 1, X: 100, Y: 650, Width: 200, Height: 50, RecipientOrder: 1}, - {Type: sdk.FieldTypeDate, Page: 1, X: 320, Y: 650, Width: 100, Height: 30, RecipientOrder: 1}, + {Type: sdk.FieldTypeSignature, Page: 1, X: 100, Y: 650, Width: 200, Height: 50, RecipientEmail: "alice@example.com"}, + {Type: sdk.FieldTypeDate, Page: 1, X: 320, Y: 650, Width: 100, Height: 30, RecipientEmail: "alice@example.com"}, // Bob's signature - {Type: sdk.FieldTypeSignature, Page: 1, X: 100, Y: 720, Width: 200, Height: 50, RecipientOrder: 2}, - {Type: sdk.FieldTypeDate, Page: 1, X: 320, Y: 720, Width: 100, Height: 30, RecipientOrder: 2}, + {Type: sdk.FieldTypeSignature, Page: 1, X: 100, Y: 720, Width: 200, Height: 50, RecipientEmail: "bob@example.com"}, + {Type: sdk.FieldTypeDate, Page: 1, X: 320, Y: 720, Width: 100, Height: 30, RecipientEmail: "bob@example.com"}, }, }) if err != nil { @@ -122,11 +130,11 @@ func main() { result, err := client.PrepareForSigningSingle(ctx, &sdk.SigningRequest{ FileLink: "https://example.com/contract-with-placeholders.pdf", Recipients: []sdk.Recipient{ - {Name: "Alice Smith", Email: "alice@example.com", Order: 1}, + {Name: "Alice Smith", Email: "alice@example.com", SigningOrder: 1}, }, Fields: []sdk.Field{ - {Type: sdk.FieldTypeSignature, Anchor: "{SIGNATURE_ALICE}", Width: 200, Height: 50, RecipientOrder: 1}, - {Type: sdk.FieldTypeDate, Anchor: "{DATE_ALICE}", Width: 100, Height: 30, RecipientOrder: 1}, + {Type: sdk.FieldTypeSignature, Anchor: "{SIGNATURE_ALICE}", Width: 200, Height: 50, RecipientEmail: "alice@example.com"}, + {Type: sdk.FieldTypeDate, Anchor: "{DATE_ALICE}", Width: 100, Height: 30, RecipientEmail: "alice@example.com"}, }, }) ``` @@ -157,10 +165,10 @@ result, err := client.PrepareForReview(ctx, &sdk.ReviewRequest{ FileLink: "https://example.com/document.pdf", DocumentName: "Contract Draft", Recipients: []sdk.Recipient{ - {Name: "John Doe", Email: "john@example.com", Order: 1}, + {Name: "John Doe", Email: "john@example.com", SigningOrder: 1}, }, Fields: []sdk.Field{ - {Type: sdk.FieldTypeSignature, Page: 1, X: 100, Y: 500, Width: 200, Height: 50, RecipientOrder: 1}, + {Type: sdk.FieldTypeSignature, Page: 1, X: 100, Y: 500, Width: 200, Height: 50, RecipientEmail: "john@example.com"}, }, }) @@ -179,10 +187,10 @@ result, err := client.PrepareForSigningSingle(ctx, &sdk.SigningRequest{ SenderName: "Your Company", SenderEmail: "sender@company.com", Recipients: []sdk.Recipient{ - {Name: "Recipient Name", Email: "recipient@example.com", Order: 1}, + {Name: "Recipient Name", Email: "recipient@example.com", SigningOrder: 1}, }, Fields: []sdk.Field{ - {Type: sdk.FieldTypeSignature, Page: 1, X: 100, Y: 500, Width: 200, Height: 50, RecipientOrder: 1}, + {Type: sdk.FieldTypeSignature, Page: 1, X: 100, Y: 500, Width: 200, Height: 50, RecipientEmail: "recipient@example.com"}, }, }) ``` @@ -266,7 +274,10 @@ import ( "github.com/turbodocx/sdk" ) -var client = sdk.NewTurboSign(os.Getenv("TURBODOCX_API_KEY")) +var client = sdk.NewTurboSign( + os.Getenv("TURBODOCX_API_KEY"), + os.Getenv("TURBODOCX_ORG_ID"), +) type SendContractRequest struct { RecipientName string `json:"recipient_name"` @@ -294,10 +305,10 @@ func sendContractHandler(w http.ResponseWriter, r *http.Request) { result, err := client.PrepareForSigningSingle(r.Context(), &sdk.SigningRequest{ FileLink: req.ContractURL, Recipients: []sdk.Recipient{ - {Name: req.RecipientName, Email: req.RecipientEmail, Order: 1}, + {Name: req.RecipientName, Email: req.RecipientEmail, SigningOrder: 1}, }, Fields: []sdk.Field{ - {Type: sdk.FieldTypeSignature, Page: 1, X: 100, Y: 650, Width: 200, Height: 50, RecipientOrder: 1}, + {Type: sdk.FieldTypeSignature, Page: 1, X: 100, Y: 650, Width: 200, Height: 50, RecipientEmail: req.RecipientEmail}, }, }) if err != nil { @@ -330,7 +341,10 @@ import ( "github.com/turbodocx/sdk" ) -var client = sdk.NewTurboSign(os.Getenv("TURBODOCX_API_KEY")) +var client = sdk.NewTurboSign( + os.Getenv("TURBODOCX_API_KEY"), + os.Getenv("TURBODOCX_ORG_ID"), +) func main() { r := gin.Default() @@ -350,10 +364,10 @@ func main() { result, err := client.PrepareForSigningSingle(c.Request.Context(), &sdk.SigningRequest{ FileLink: req.ContractURL, Recipients: []sdk.Recipient{ - {Name: req.RecipientName, Email: req.RecipientEmail, Order: 1}, + {Name: req.RecipientName, Email: req.RecipientEmail, SigningOrder: 1}, }, Fields: []sdk.Field{ - {Type: sdk.FieldTypeSignature, Page: 1, X: 100, Y: 650, Width: 200, Height: 50, RecipientOrder: 1}, + {Type: sdk.FieldTypeSignature, Page: 1, X: 100, Y: 650, Width: 200, Height: 50, RecipientEmail: req.RecipientEmail}, }, }) if err != nil { @@ -439,9 +453,9 @@ const ( ```go type Recipient struct { - Name string `json:"name"` - Email string `json:"email"` - Order int `json:"order"` + Name string `json:"name"` + Email string `json:"email"` + SigningOrder int `json:"signingOrder"` // Response fields ID string `json:"id,omitempty"` Status string `json:"status,omitempty"` @@ -455,7 +469,7 @@ type Field struct { Y int `json:"y,omitempty"` Width int `json:"width"` Height int `json:"height"` - RecipientOrder int `json:"recipient_order"` + RecipientEmail string `json:"recipientEmail"` Anchor string `json:"anchor,omitempty"` // For template-based fields } diff --git a/docs/SDKs/index.md b/docs/SDKs/index.md index b03b414..4e876ec 100644 --- a/docs/SDKs/index.md +++ b/docs/SDKs/index.md @@ -2,16 +2,14 @@ title: SDKs Overview sidebar_position: 1 sidebar_label: Overview -description: Official TurboDocx SDKs for JavaScript, Python, Go, .NET, Java, and Ruby. Get started with document generation and digital signatures in minutes. +description: Official TurboDocx SDKs for JavaScript, Python, Go, and Java. Get started with document generation and digital signatures in minutes. keywords: - turbodocx sdk - turbosign sdk - javascript sdk - python sdk - go sdk - - dotnet sdk - java sdk - - ruby sdk - document api - esignature sdk - api client library @@ -26,14 +24,12 @@ Official client libraries for the TurboDocx API. Build document generation and d ## Available SDKs -| Language | Package | Install Command | Links | -|:---------|:--------|:----------------|:------| -| **JavaScript/TypeScript** | `@turbodocx/sdk` | `npm install @turbodocx/sdk` | [Docs](/docs/SDKs/javascript) · [GitHub](https://github.com/TurboDocx/SDK/tree/main/packages/js-sdk) | -| **Python** | `turbodocx-sdk` | `pip install turbodocx-sdk` | [Docs](/docs/SDKs/python) · [GitHub](https://github.com/TurboDocx/SDK/tree/main/packages/py-sdk) | -| **Go** | `github.com/turbodocx/sdk` | `go get github.com/turbodocx/sdk` | [Docs](/docs/SDKs/go) · [GitHub](https://github.com/TurboDocx/SDK/tree/main/packages/go-sdk) | -| **C# / .NET** | `TurboDocx.Sdk` | `dotnet add package TurboDocx.Sdk` | [Docs](/docs/SDKs/dotnet) · [GitHub](https://github.com/TurboDocx/SDK/tree/main/packages/dotnet-sdk) | -| **Java** | `com.turbodocx:sdk` | [Maven Central](https://search.maven.org/artifact/com.turbodocx/sdk) | [Docs](/docs/SDKs/java) · [GitHub](https://github.com/TurboDocx/SDK/tree/main/packages/java-sdk) | -| **Ruby** | `turbodocx-sdk` | `gem install turbodocx-sdk` | [Docs](/docs/SDKs/ruby) · [GitHub](https://github.com/TurboDocx/SDK/tree/main/packages/ruby-sdk) | +| Language | Package | Install Command | Links | +| :------------------------ | :------------------------- | :------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------- | +| **JavaScript/TypeScript** | `@turbodocx/sdk` | `npm install @turbodocx/sdk` | [Docs](/docs/SDKs/javascript) [GitHub](https://github.com/TurboDocx/SDK/tree/main/packages/js-sdk) | +| **Python** | `turbodocx-sdk` | `pip install turbodocx-sdk` | [Docs](/docs/SDKs/python) [GitHub](https://github.com/TurboDocx/SDK/tree/main/packages/py-sdk) | +| **Go** | `github.com/turbodocx/sdk` | `go get github.com/turbodocx/sdk` | [Docs](/docs/SDKs/go) [GitHub](https://github.com/TurboDocx/SDK/tree/main/packages/go-sdk) | +| **Java** | `com.turbodocx:sdk` | [Maven Central](https://search.maven.org/artifact/com.turbodocx/sdk) | [Docs](/docs/SDKs/java) [GitHub](https://github.com/TurboDocx/SDK/tree/main/packages/java-sdk) | :::tip Low-code or No-code? Check out our [n8n community node](https://www.npmjs.com/package/@turbodocx/n8n-nodes-turbodocx) for workflow automation, or get [TurboDocx Writer](https://appsource.microsoft.com/en-us/product/office/WA200007397) for Microsoft Word. @@ -45,9 +41,29 @@ Check out our [n8n community node](https://www.npmjs.com/package/@turbodocx/n8n- Get up and running in under 2 minutes. -### 1. Get Your API Key +### 1. Get Your Credentials -Sign up at [turbodocx.com](https://www.turbodocx.com) and grab your API key from the dashboard under **Settings → API Keys**. +Before you begin, you'll need two things from your TurboDocx account: + +- **API Access Token**: Your authentication key +- **Organization ID**: Your unique organization identifier + +#### How to Get 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 copy your API access token +4. **Organization ID**: Copy your organization ID from the same settings page + +![TurboSign API Key](/img/turbosign/api/api-key.png) +![TurboSign Organization ID](/img/turbosign/api/org-id.png) + +:::tip Keep Your Credentials Secure + +- Store your API key and Organization ID as environment variables +- Never commit credentials to version control +- Rotate your API keys regularly for security +::: ### 2. Install the SDK @@ -58,29 +74,37 @@ Sign up at [turbodocx.com](https://www.turbodocx.com) and grab your API key from npm install @turbodocx/sdk # or yarn add @turbodocx/sdk +# or +pnpm add @turbodocx/sdk ``` - + ```bash -pip install turbodocx-sdk +npm install @turbodocx/sdk # or -poetry add turbodocx-sdk +yarn add @turbodocx/sdk +# or +pnpm add @turbodocx/sdk + +# TypeScript types are included in the package ``` - + ```bash -go get github.com/turbodocx/sdk +pip install turbodocx-sdk +# or +poetry add turbodocx-sdk ``` - + ```bash -dotnet add package TurboDocx.Sdk +go get github.com/turbodocx/sdk ``` @@ -94,15 +118,6 @@ dotnet add package TurboDocx.Sdk ``` - - - -```bash -gem install turbodocx-sdk -# or add to Gemfile: -gem 'turbodocx-sdk' -``` - @@ -111,24 +126,71 @@ gem 'turbodocx-sdk' +```javascript +const { TurboSign } = require("@turbodocx/sdk"); +// or with ES modules: +// import { TurboSign } from '@turbodocx/sdk'; + +// Configure with your API key +TurboSign.configure({ + apiKey: process.env.TURBODOCX_API_KEY, + orgId: process.env.TURBODOCX_ORG_ID, +}); + +// Send a document for signature +const result = await TurboSign.prepareForSigningSingle({ + fileLink: "https://example.com/contract.pdf", + recipients: [ + { name: "John Doe", email: "john@example.com", signingOrder: 1 }, + ], + fields: [ + { + type: "signature", + page: 1, + x: 100, + y: 500, + width: 200, + height: 50, + recipientEmail: "john@example.com", + }, + ], +}); + +console.log(`Document sent! ID: ${result.documentId}`); +``` + + + + ```typescript -import { TurboSign } from '@turbodocx/sdk'; +import { TurboSign } from "@turbodocx/sdk"; // Configure with your API key -TurboSign.configure({ apiKey: process.env.TURBODOCX_API_KEY }); +TurboSign.configure({ + apiKey: process.env.TURBODOCX_API_KEY || "", + orgId: process.env.TURBODOCX_ORG_ID || "", +}); // Send a document for signature -const { documentId, recipients } = await TurboSign.prepareForSigningSingle({ - fileLink: 'https://example.com/contract.pdf', +const result = await TurboSign.prepareForSigningSingle({ + fileLink: "https://example.com/contract.pdf", recipients: [ - { name: 'John Doe', email: 'john@example.com', order: 1 } + { name: "John Doe", email: "john@example.com", signingOrder: 1 }, ], fields: [ - { type: 'signature', page: 1, x: 100, y: 500, width: 200, height: 50, recipientOrder: 1 } - ] + { + type: "signature", + page: 1, + x: 100, + y: 500, + width: 200, + height: 50, + recipientEmail: "john@example.com", + }, + ], }); -console.log(`Document sent! Sign URL: ${recipients[0].signUrl}`); +console.log(`Document sent! ID: ${result.documentId}`); ``` @@ -139,20 +201,23 @@ from turbodocx import TurboSign import os # Configure with your API key -TurboSign.configure(api_key=os.environ["TURBODOCX_API_KEY"]) +TurboSign.configure( + api_key=os.environ["TURBODOCX_API_KEY"], + org_id=os.environ["TURBODOCX_ORG_ID"] +) # Send a document for signature -result = await TurboSign.prepare_for_signing_single( +result = TurboSign.prepare_for_signing_single( file_link="https://example.com/contract.pdf", recipients=[ - {"name": "John Doe", "email": "john@example.com", "order": 1} + {"name": "John Doe", "email": "john@example.com", "signingOrder": 1} ], fields=[ - {"type": "signature", "page": 1, "x": 100, "y": 500, "width": 200, "height": 50, "recipient_order": 1} + {"type": "signature", "page": 1, "x": 100, "y": 500, "width": 200, "height": 50, "recipientEmail": "john@example.com"} ] ) -print(f"Document sent! Sign URL: {result.recipients[0].sign_url}") +print(f"Document sent! ID: {result.documentId}") ``` @@ -171,52 +236,29 @@ import ( func main() { // Configure with your API key - client := sdk.NewTurboSign(os.Getenv("TURBODOCX_API_KEY")) + client := sdk.NewTurboSign( + os.Getenv("TURBODOCX_API_KEY"), + os.Getenv("TURBODOCX_ORG_ID"), + ) // Send a document for signature result, err := client.PrepareForSigningSingle(context.Background(), &sdk.SigningRequest{ FileLink: "https://example.com/contract.pdf", Recipients: []sdk.Recipient{ - {Name: "John Doe", Email: "john@example.com", Order: 1}, + {Name: "John Doe", Email: "john@example.com", SigningOrder: 1}, }, Fields: []sdk.Field{ - {Type: "signature", Page: 1, X: 100, Y: 500, Width: 200, Height: 50, RecipientOrder: 1}, + {Type: "signature", Page: 1, X: 100, Y: 500, Width: 200, Height: 50, RecipientEmail: "john@example.com"}, }, }) if err != nil { panic(err) } - fmt.Printf("Document sent! Sign URL: %s\n", result.Recipients[0].SignUrl) + fmt.Printf("Document sent! ID: %s\n", result.documentId) } ``` - - - -```csharp -using TurboDocx.Sdk; - -// Configure with your API key -var turboSign = new TurboSignClient(Environment.GetEnvironmentVariable("TURBODOCX_API_KEY")); - -// Send a document for signature -var result = await turboSign.PrepareForSigningSingleAsync(new SigningRequest -{ - FileLink = "https://example.com/contract.pdf", - Recipients = new[] - { - new Recipient { Name = "John Doe", Email = "john@example.com", Order = 1 } - }, - Fields = new[] - { - new Field { Type = "signature", Page = 1, X = 100, Y = 500, Width = 200, Height = 50, RecipientOrder = 1 } - } -}); - -Console.WriteLine($"Document sent! Sign URL: {result.Recipients[0].SignUrl}"); -``` - @@ -227,7 +269,10 @@ import com.turbodocx.sdk.models.*; public class Main { public static void main(String[] args) { // Configure with your API key - TurboSign turboSign = new TurboSign(System.getenv("TURBODOCX_API_KEY")); + TurboSign turboSign = new TurboSign( + System.getenv("TURBODOCX_API_KEY"), + System.getenv("TURBODOCX_ORG_ID") + ); // Send a document for signature SigningResult result = turboSign.prepareForSigningSingle( @@ -236,46 +281,21 @@ public class Main { .recipient(Recipient.builder() .name("John Doe") .email("john@example.com") - .order(1) + .signingOrder(1) .build()) .field(Field.builder() .type("signature") .page(1).x(100).y(500).width(200).height(50) - .recipientOrder(1) + .recipientEmail("john@example.com") .build()) .build() ); - System.out.println("Document sent! Sign URL: " + result.getRecipients().get(0).getSignUrl()); + System.out.println("Document sent! ID: " + result.documentId); } } ``` - - - -```ruby -require 'turbodocx' - -# Configure with your API key -TurboDocx.configure do |config| - config.api_key = ENV['TURBODOCX_API_KEY'] -end - -# Send a document for signature -result = TurboDocx::TurboSign.prepare_for_signing_single( - file_link: 'https://example.com/contract.pdf', - recipients: [ - { name: 'John Doe', email: 'john@example.com', order: 1 } - ], - fields: [ - { type: 'signature', page: 1, x: 100, y: 500, width: 200, height: 50, recipient_order: 1 } - ] -) - -puts "Document sent! Sign URL: #{result.recipients.first.sign_url}" -``` - @@ -289,18 +309,19 @@ All TurboDocx SDKs provide access to: Send documents for legally-binding eSignatures with full audit trails. -| Method | Description | -|:-------|:------------| -| `prepareForReview()` | Upload document for preview without sending emails | -| `prepareForSigningSingle()` | Upload and immediately send signature requests | -| `getStatus()` | Check document and recipient signing status | -| `download()` | Download the completed signed document | -| `void()` | Cancel/void a signature request | -| `resend()` | Resend signature request emails | +| Method | Description | +| :-------------------------- | :------------------------------------------------------ | +| `prepareForReview()` | Upload document for preview without sending emails | +| `prepareForSigningSingle()` | Upload and immediately send signature requests | +| `getStatus()` | Check document and recipient signing status | +| `download()` | Download the completed signed document | +| `void()` | Cancel/void a signature request | +| `resend()` | Resend signature request emails | +| `getAuditTrail()` | Get complete audit trail with all events and timestamps | -[Learn more about TurboSign →](/docs/TurboSign/Setting-up-TurboSign) +[Learn more about TurboSign →](/docs/TurboSign/Setting%20up%20TurboSign) -### TurboDocx — Document Generation *(Coming Soon)* +### TurboDocx — Document Generation _(Coming Soon)_ Generate documents from templates with dynamic data. @@ -322,7 +343,7 @@ Specify exact positions using page coordinates: y: 500, // pixels from top width: 200, height: 50, - recipientOrder: 1 + recipientEmail: 'user@example.com' } ``` @@ -336,13 +357,18 @@ Use text markers in your PDF to position fields automatically: anchor: '{SIGNATURE_1}', // text to find in PDF width: 200, height: 50, - recipientOrder: 1 + recipientEmail: 'user@example.com' } ``` :::tip When to use each method + - **Coordinate-based**: Best for PDFs with fixed layouts where you know exact positions - **Template-based**: Best for templates where content may shift, using anchor text like `{SIGNATURE_1}` + ::: + +:::info Complete Field Types Reference +For a comprehensive list of all available field types (signature, initials, text, date, checkbox, full_name, email, title, company) and their detailed usage, see the [Field Types section in the API Signatures guide](/docs/TurboSign/API%20Signatures#field-types-reference) ::: --- @@ -354,17 +380,40 @@ All SDKs provide structured error handling with detailed error codes: +```javascript +const { TurboSign, TurboDocxError } = require("@turbodocx/sdk"); + +try { + const result = await TurboSign.prepareForSigningSingle({ + /* ... */ + }); +} catch (error) { + if (error instanceof TurboDocxError) { + console.error(`Error ${error.code}: ${error.message}`); + // Handle specific error codes + if (error.code === "VALIDATION_ERROR") { + // Handle validation error + } + } +} +``` + + + + ```typescript -import { TurboSign, TurboDocxError } from '@turbodocx/sdk'; +import { TurboSign, TurboDocxError } from "@turbodocx/sdk"; try { - const result = await TurboSign.prepareForSigningSingle({ /* ... */ }); + const result = await TurboSign.prepareForSigningSingle({ + /* ... */ + }); } catch (error) { if (error instanceof TurboDocxError) { console.error(`Error ${error.code}: ${error.message}`); // Handle specific error codes - if (error.code === 'INVALID_DOCUMENT') { - // Handle invalid document error + if (error.code === "VALIDATION_ERROR") { + // Handle validation error } } } @@ -381,8 +430,8 @@ try: result = await TurboSign.prepare_for_signing_single(...) except TurboDocxError as e: print(f"Error {e.code}: {e.message}") - if e.code == "INVALID_DOCUMENT": - # Handle invalid document error + if e.code == "VALIDATION_ERROR": + # Handle validation error pass ``` @@ -395,25 +444,48 @@ if err != nil { var turboErr *sdk.TurboDocxError if errors.As(err, &turboErr) { fmt.Printf("Error %s: %s\n", turboErr.Code, turboErr.Message) - if turboErr.Code == "INVALID_DOCUMENT" { - // Handle invalid document error + if turboErr.Code == "VALIDATION_ERROR" { + // Handle validation error } } } ``` + + + +```java +import com.turbodocx.sdk.TurboSign; +import com.turbodocx.sdk.TurboDocxException; +import com.turbodocx.sdk.TurboDocxException.*; + +try { + SigningResult result = turboSign.prepareForSigningSingle(/* ... */); +} catch (AuthenticationException e) { + System.err.println("Invalid API key: " + e.getMessage()); +} catch (ValidationException e) { + System.err.println("Validation error: " + e.getMessage()); + System.err.println("Error code: " + e.getCode()); +} catch (RateLimitException e) { + System.err.println("Rate limited: " + e.getMessage()); +} catch (TurboDocxException e) { + System.err.println("Error " + e.getCode() + ": " + e.getMessage()); + System.err.println("Status code: " + e.getStatusCode()); +} +``` + ### Common Error Codes -| Code | Description | -|:-----|:------------| -| `UNAUTHORIZED` | Invalid or expired API key | -| `INVALID_DOCUMENT` | Document could not be processed | -| `INVALID_RECIPIENT` | Invalid recipient email or data | -| `RATE_LIMITED` | Too many requests, slow down | -| `NOT_FOUND` | Document or resource not found | +| Code | HTTP Status | Description | +| :--------------------- | :---------- | :------------------------------------ | +| `AUTHENTICATION_ERROR` | 401 | Invalid or expired API key | +| `VALIDATION_ERROR` | 400 | Invalid request data or parameters | +| `NOT_FOUND` | 404 | Document or resource not found | +| `RATE_LIMIT_EXCEEDED` | 429 | Too many requests, retry with backoff | +| `NETWORK_ERROR` | N/A | Network connection or timeout error | --- @@ -421,41 +493,194 @@ if err != nil {
-
-
-

API Signatures

+
{ + e.currentTarget.style.transform = 'translateY(-8px)'; + e.currentTarget.style.boxShadow = '0 12px 24px rgba(0, 0, 0, 0.2), 0 6px 12px rgba(0, 0, 0, 0.15)'; + }} + onMouseOut={(e) => { + e.currentTarget.style.transform = 'translateY(0)'; + e.currentTarget.style.boxShadow = '0 4px 6px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.06)'; + }} + > +
+

API Signatures

-
-
-
-

Webhooks

+
{ + e.currentTarget.style.transform = 'translateY(-8px)'; + e.currentTarget.style.boxShadow = '0 12px 24px rgba(0, 0, 0, 0.2), 0 6px 12px rgba(0, 0, 0, 0.15)'; + }} + onMouseOut={(e) => { + e.currentTarget.style.transform = 'translateY(0)'; + e.currentTarget.style.boxShadow = '0 4px 6px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.06)'; + }} + > +
+

Webhooks

-
-
-
-

GitHub Repository

+
{ + e.currentTarget.style.transform = 'translateY(-8px)'; + e.currentTarget.style.boxShadow = '0 12px 24px rgba(0, 0, 0, 0.2), 0 6px 12px rgba(0, 0, 0, 0.15)'; + }} + onMouseOut={(e) => { + e.currentTarget.style.transform = 'translateY(0)'; + e.currentTarget.style.boxShadow = '0 4px 6px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.06)'; + }} + > +
+

GitHub Repository

- diff --git a/docs/SDKs/java.md b/docs/SDKs/java.md index 7cfb4e0..cce8c22 100644 --- a/docs/SDKs/java.md +++ b/docs/SDKs/java.md @@ -15,6 +15,7 @@ keywords: import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; +import ScriptLoader from '@site/src/components/ScriptLoader'; # Java SDK @@ -68,11 +69,15 @@ implementation 'com.turbodocx:sdk:1.0.0' import com.turbodocx.sdk.TurboSign; // Create a client -TurboSign turboSign = new TurboSign(System.getenv("TURBODOCX_API_KEY")); +TurboSign turboSign = new TurboSign( + System.getenv("TURBODOCX_API_KEY"), + System.getenv("TURBODOCX_ORG_ID") +); // Or with builder TurboSign turboSign = TurboSign.builder() .apiKey(System.getenv("TURBODOCX_API_KEY")) + .orgId(System.getenv("TURBODOCX_ORG_ID")) .baseUrl("https://api.turbodocx.com") .timeout(Duration.ofSeconds(30)) .build(); @@ -90,8 +95,10 @@ turbodocx: public class TurboDocxConfig { @Bean - public TurboSign turboSign(@Value("${turbodocx.api-key}") String apiKey) { - return new TurboSign(apiKey); + public TurboSign turboSign( + @Value("${turbodocx.api-key}") String apiKey, + @Value("${turbodocx.org-id}") String orgId) { + return new TurboSign(apiKey, orgId); } } ``` @@ -108,7 +115,10 @@ import com.turbodocx.sdk.models.*; public class Main { public static void main(String[] args) { - TurboSign turboSign = new TurboSign(System.getenv("TURBODOCX_API_KEY")); + TurboSign turboSign = new TurboSign( + System.getenv("TURBODOCX_API_KEY"), + System.getenv("TURBODOCX_ORG_ID") + ); SigningResult result = turboSign.prepareForSigningSingle( SigningRequest.builder() @@ -119,34 +129,34 @@ public class Main { .recipient(Recipient.builder() .name("Alice Smith") .email("alice@example.com") - .order(1) + .signingOrder(1) .build()) .recipient(Recipient.builder() .name("Bob Johnson") .email("bob@example.com") - .order(2) + .signingOrder(2) .build()) // Alice's signature .field(Field.builder() .type(FieldType.SIGNATURE) .page(1).x(100).y(650).width(200).height(50) - .recipientOrder(1) + .recipientEmail("alice@example.com") .build()) .field(Field.builder() .type(FieldType.DATE) .page(1).x(320).y(650).width(100).height(30) - .recipientOrder(1) + .recipientEmail("alice@example.com") .build()) // Bob's signature .field(Field.builder() .type(FieldType.SIGNATURE) .page(1).x(100).y(720).width(200).height(50) - .recipientOrder(2) + .recipientEmail("bob@example.com") .build()) .field(Field.builder() .type(FieldType.DATE) .page(1).x(320).y(720).width(100).height(30) - .recipientOrder(2) + .recipientEmail("bob@example.com") .build()) .build() ); @@ -327,12 +337,12 @@ public class ContractController { .recipient(Recipient.builder() .name(request.getRecipientName()) .email(request.getRecipientEmail()) - .order(1) + .signingOrder(1) .build()) .field(Field.builder() .type(FieldType.SIGNATURE) .page(1).x(100).y(650).width(200).height(50) - .recipientOrder(1) + .recipientEmail("alice@example.com") .build()) .build() ); @@ -385,12 +395,12 @@ public class ContractService { .recipient(Recipient.builder() .name(recipientName) .email(recipientEmail) - .order(1) + .signingOrder(1) .build()) .field(Field.builder() .type(FieldType.SIGNATURE) .page(1).x(100).y(650).width(200).height(50) - .recipientOrder(1) + .recipientEmail("alice@example.com") .build()) .build() ); @@ -456,7 +466,7 @@ public enum FieldType { public class Recipient { private String name; private String email; - private int order; + private int signingOrder; // Response fields private String id; private String status; @@ -472,7 +482,7 @@ public class Field { private Integer y; private int width; private int height; - private int recipientOrder; + private String recipientEmail; private String anchor; // For template-based fields } diff --git a/docs/SDKs/javascript.md b/docs/SDKs/javascript.md index 5f7c697..71119c5 100644 --- a/docs/SDKs/javascript.md +++ b/docs/SDKs/javascript.md @@ -19,10 +19,7 @@ import TabItem from '@theme/TabItem'; # JavaScript / TypeScript SDK -The official TurboDocx SDK for JavaScript and TypeScript applications. - -[![npm version](https://img.shields.io/npm/v/@turbodocx/sdk?logo=npm&logoColor=white)](https://www.npmjs.com/package/@turbodocx/sdk) -[![GitHub](https://img.shields.io/github/stars/turbodocx/sdk?style=social)](https://github.com/TurboDocx/SDK) +The official TurboDocx SDK for JavaScript and TypeScript applications. Build document generation and digital signature workflows with full TypeScript support, async/await patterns, and comprehensive error handling. Available on npm as `@turbodocx/sdk`. ## Installation @@ -59,22 +56,49 @@ pnpm add @turbodocx/sdk ## Configuration + + + +```javascript +const { TurboSign } = require("@turbodocx/sdk"); + +// Configure globally (recommended for server-side) +TurboSign.configure({ + apiKey: process.env.TURBODOCX_API_KEY, // Required : Your TurboDocx API key + orgId: process.env.TURBODOCX_ORG_ID, // Required: Your organization ID + // Optional: override base URL for testing + // baseUrl: 'https://api.turbodocx.com' +}); +``` + + + + ```typescript -import { TurboSign } from '@turbodocx/sdk'; +import { TurboSign } from "@turbodocx/sdk"; // Configure globally (recommended for server-side) TurboSign.configure({ - apiKey: process.env.TURBODOCX_API_KEY, + apiKey: process.env.TURBODOCX_API_KEY || "", // Required : Your TurboDocx API key + orgId: process.env.TURBODOCX_ORG_ID || "", // Required: Your organization ID // Optional: override base URL for testing // baseUrl: 'https://api.turbodocx.com' }); ``` + + + +:::tip Authentication +Authenticate using `apiKey`. API keys are recommended for server-side applications. +::: + ### Environment Variables ```bash # .env TURBODOCX_API_KEY=your_api_key_here +TURBODOCX_ORG_ID=your_org_id_here ``` --- @@ -83,390 +107,1024 @@ TURBODOCX_API_KEY=your_api_key_here ### Send a Document for Signature + + + +```javascript +const { TurboSign } = require("@turbodocx/sdk"); + +TurboSign.configure({ + apiKey: process.env.TURBODOCX_API_KEY, + orgId: process.env.TURBODOCX_ORG_ID, +}); + +// Send document with coordinate-based fields +const result = await TurboSign.prepareForSigningSingle({ + fileLink: "https://example.com/contract.pdf", + documentName: "Service Agreement", + senderName: "Acme Corp", + senderEmail: "contracts@acme.com", + recipients: [ + { name: "Alice Smith", email: "alice@example.com", signingOrder: 1 }, + { name: "Bob Johnson", email: "bob@example.com", signingOrder: 2 }, + ], + fields: [ + // Alice's signature + { + type: "signature", + page: 1, + x: 100, + y: 650, + width: 200, + height: 50, + recipientEmail: "alice@example.com", + }, + { + type: "date", + page: 1, + x: 320, + y: 650, + width: 100, + height: 30, + recipientEmail: "alice@example.com", + }, + // Bob's signature + { + type: "signature", + page: 1, + x: 100, + y: 720, + width: 200, + height: 50, + recipientEmail: "bob@example.com", + }, + { + type: "date", + page: 1, + x: 320, + y: 720, + width: 100, + height: 30, + recipientEmail: "bob@example.com", + }, + ], +}); + +console.log(JSON.stringify(result, null, 2)); +``` + + + + ```typescript -import { TurboSign } from '@turbodocx/sdk'; +import { TurboSign } from "@turbodocx/sdk"; -TurboSign.configure({ apiKey: process.env.TURBODOCX_API_KEY }); +TurboSign.configure({ + apiKey: process.env.TURBODOCX_API_KEY || "", + orgId: process.env.TURBODOCX_ORG_ID || "", +}); // Send document with coordinate-based fields const result = await TurboSign.prepareForSigningSingle({ - fileLink: 'https://example.com/contract.pdf', - documentName: 'Service Agreement', - senderName: 'Acme Corp', - senderEmail: 'contracts@acme.com', + fileLink: "https://example.com/contract.pdf", + documentName: "Service Agreement", + senderName: "Acme Corp", + senderEmail: "contracts@acme.com", recipients: [ - { name: 'Alice Smith', email: 'alice@example.com', order: 1 }, - { name: 'Bob Johnson', email: 'bob@example.com', order: 2 } + { name: "Alice Smith", email: "alice@example.com", signingOrder: 1 }, + { name: "Bob Johnson", email: "bob@example.com", signingOrder: 2 }, ], fields: [ // Alice's signature - { type: 'signature', page: 1, x: 100, y: 650, width: 200, height: 50, recipientOrder: 1 }, - { type: 'date', page: 1, x: 320, y: 650, width: 100, height: 30, recipientOrder: 1 }, + { + type: "signature", + page: 1, + x: 100, + y: 650, + width: 200, + height: 50, + recipientEmail: "alice@example.com", + }, + { + type: "date", + page: 1, + x: 320, + y: 650, + width: 100, + height: 30, + recipientEmail: "alice@example.com", + }, // Bob's signature - { type: 'signature', page: 1, x: 100, y: 720, width: 200, height: 50, recipientOrder: 2 }, - { type: 'date', page: 1, x: 320, y: 720, width: 100, height: 30, recipientOrder: 2 } - ] + { + type: "signature", + page: 1, + x: 100, + y: 720, + width: 200, + height: 50, + recipientEmail: "bob@example.com", + }, + { + type: "date", + page: 1, + x: 320, + y: 720, + width: 100, + height: 30, + recipientEmail: "bob@example.com", + }, + ], }); -console.log(`Document ID: ${result.documentId}`); -for (const recipient of result.recipients) { - console.log(`${recipient.name}: ${recipient.signUrl}`); -} +console.log(JSON.stringify(result, null, 2)); ``` + + + ### Using Template-Based Fields + + + +```javascript +// Use text anchors instead of coordinates +const result = await TurboSign.prepareForSigningSingle({ + fileLink: "https://example.com/contract-with-placeholders.pdf", + recipients: [ + { name: "Alice Smith", email: "alice@example.com", signingOrder: 1 }, + ], + fields: [ + { + type: "signature", + template: { anchor: "{SIGNATURE_ALICE}" }, + width: 200, + height: 50, + recipientEmail: "alice@example.com", + }, + { + type: "date", + template: { anchor: "{DATE_ALICE}" }, + width: 100, + height: 30, + recipientEmail: "alice@example.com", + }, + ], +}); + +console.log(JSON.stringify(result, null, 2)); +``` + + + + ```typescript // Use text anchors instead of coordinates const result = await TurboSign.prepareForSigningSingle({ - fileLink: 'https://example.com/contract-with-placeholders.pdf', + fileLink: "https://example.com/contract-with-placeholders.pdf", + recipients: [ + { name: "Alice Smith", email: "alice@example.com", signingOrder: 1 }, + ], + fields: [ + { + type: "signature", + template: { anchor: "{SIGNATURE_ALICE}" }, + width: 200, + height: 50, + recipientEmail: "alice@example.com", + }, + { + type: "date", + template: { anchor: "{DATE_ALICE}" }, + width: 100, + height: 30, + recipientEmail: "alice@example.com", + }, + ], +}); + +console.log(JSON.stringify(result, null, 2)); +``` + + + + +:::info Template Anchors Required +**Important:** The document file must contain the anchor text (e.g., `{SIGNATURE_ALICE}`, `{DATE_ALICE}`) that you reference in your fields. If the anchors don't exist in the document, the API will return an error. + +**Alternative:** Use a TurboDocx template with pre-configured anchors: + +```typescript +const result = await TurboSign.prepareForSigningSingle({ + templateId: "template-uuid-from-turbodocx", // Template already contains anchors + recipients: [ + { name: "Alice Smith", email: "alice@example.com", signingOrder: 1 }, + ], + fields: [ + { + type: "signature", + template: { anchor: "{SIGNATURE_ALICE}" }, + width: 200, + height: 50, + recipientEmail: "alice@example.com", + }, + ], +}); +``` + +::: + +--- + +## File Input Methods + +TurboSign supports four different ways to provide document files: + +### 1. File Upload (Buffer/Blob) + + + + +```javascript +const { readFileSync } = require("fs"); +const { TurboSign } = require("@turbodocx/sdk"); + +const fileBuffer = readFileSync("./contract.pdf"); + +const result = await TurboSign.prepareForSigningSingle({ + file: fileBuffer, + recipients: [ + { name: "John Doe", email: "john@example.com", signingOrder: 1 }, + ], + fields: [ + { + type: "signature", + page: 1, + x: 100, + y: 650, + width: 200, + height: 50, + recipientEmail: "john@example.com", + }, + ], +}); +``` + + + + +```typescript +import { readFileSync } from "fs"; +import { TurboSign } from "@turbodocx/sdk"; + +const fileBuffer = readFileSync("./contract.pdf"); + +const result = await TurboSign.prepareForSigningSingle({ + file: fileBuffer, + recipients: [ + { name: "John Doe", email: "john@example.com", signingOrder: 1 }, + ], + fields: [ + { + type: "signature", + page: 1, + x: 100, + y: 650, + width: 200, + height: 50, + recipientEmail: "john@example.com", + }, + ], +}); +``` + + + + +### 2. File URL (fileLink) + + + + +```javascript +const result = await TurboSign.prepareForSigningSingle({ + fileLink: "https://storage.example.com/contracts/agreement.pdf", + recipients: [ + { name: "John Doe", email: "john@example.com", signingOrder: 1 }, + ], + fields: [ + { + type: "signature", + page: 1, + x: 100, + y: 650, + width: 200, + height: 50, + recipientEmail: "john@example.com", + }, + ], +}); +``` + + + + +```typescript +const result = await TurboSign.prepareForSigningSingle({ + fileLink: "https://storage.example.com/contracts/agreement.pdf", + recipients: [ + { name: "John Doe", email: "john@example.com", signingOrder: 1 }, + ], + fields: [ + { + type: "signature", + page: 1, + x: 100, + y: 650, + width: 200, + height: 50, + recipientEmail: "john@example.com", + }, + ], +}); +``` + + + + +:::tip When to use fileLink +Use `fileLink` when your documents are already hosted on cloud storage (S3, Google Cloud Storage, etc.). This is more efficient than downloading and re-uploading files. +::: + +### 3. TurboDocx Deliverable ID + + + + +```javascript +// Use a previously generated TurboDocx document +const result = await TurboSign.prepareForSigningSingle({ + deliverableId: "deliverable-uuid-from-turbodocx", + recipients: [ + { name: "John Doe", email: "john@example.com", signingOrder: 1 }, + ], + fields: [ + { + type: "signature", + page: 1, + x: 100, + y: 650, + width: 200, + height: 50, + recipientEmail: "john@example.com", + }, + ], +}); +``` + + + + +```typescript +// Use a previously generated TurboDocx document +const result = await TurboSign.prepareForSigningSingle({ + deliverableId: "deliverable-uuid-from-turbodocx", + recipients: [ + { name: "John Doe", email: "john@example.com", signingOrder: 1 }, + ], + fields: [ + { + type: "signature", + page: 1, + x: 100, + y: 650, + width: 200, + height: 50, + recipientEmail: "john@example.com", + }, + ], +}); +``` + + + + +:::info Integration with TurboDocx +`deliverableId` references documents generated using TurboDocx's document generation API. This creates a seamless workflow: generate → sign. +::: + +### 4. TurboDocx Template ID + + + + +```javascript +// Use a pre-configured TurboSign template +const result = await TurboSign.prepareForSigningSingle({ + templateId: "template-uuid-from-turbodocx", // Template already contains anchors + recipients: [ + { name: "Alice Smith", email: "alice@example.com", signingOrder: 1 }, + ], + fields: [ + { + type: "signature", + template: { anchor: "{SIGNATURE_ALICE}" }, + width: 200, + height: 50, + recipientEmail: "alice@example.com", + }, + ], +}); +``` + + + + +```typescript +// Use a pre-configured TurboSign template +const result = await TurboSign.prepareForSigningSingle({ + templateId: "template-uuid-from-turbodocx", // Template already contains anchors recipients: [ - { name: 'Alice Smith', email: 'alice@example.com', order: 1 } + { name: "Alice Smith", email: "alice@example.com", signingOrder: 1 }, ], fields: [ - { type: 'signature', anchor: '{SIGNATURE_ALICE}', width: 200, height: 50, recipientOrder: 1 }, - { type: 'date', anchor: '{DATE_ALICE}', width: 100, height: 30, recipientOrder: 1 } - ] + { + type: "signature", + template: { anchor: "{SIGNATURE_ALICE}" }, + width: 200, + height: 50, + recipientEmail: "alice@example.com", + }, + ], }); ``` + + + +:::info Integration with TurboDocx +`templateId` references pre-configured TurboSign templates created in the TurboDocx dashboard. These templates come with built-in anchors and field positioning, making it easy to reuse signature workflows across multiple documents. +::: + --- ## API Reference -### TurboSign.configure(options) +### Configure + +Configure the SDK with your API credentials and organization settings. + +```typescript +TurboSign.configure({ + apiKey: string; // Required : Your TurboDocx API key + orgId: string; // Required: Your organization ID + baseUrl?: string; // Optional: API base URL (default: 'https://api.turbodocx.com') +}); +``` + +**Example:** + + + -Configure the SDK with your API credentials. +```javascript +const { TurboSign } = require("@turbodocx/sdk"); + +TurboSign.configure({ + apiKey: process.env.TURBODOCX_API_KEY, + orgId: process.env.TURBODOCX_ORG_ID, + // Optional: override for testing + // baseUrl: 'https://api.turbodocx.com' +}); +``` + + + ```typescript +import { TurboSign } from "@turbodocx/sdk"; + TurboSign.configure({ - apiKey: string; // Required: Your TurboDocx API key - baseUrl?: string; // Optional: API base URL - timeout?: number; // Optional: Request timeout in ms (default: 30000) + apiKey: process.env.TURBODOCX_API_KEY || "", + orgId: process.env.TURBODOCX_ORG_ID || "", + // Optional: override for testing + // baseUrl: 'https://api.turbodocx.com' }); ``` -### TurboSign.prepareForReview(options) + + + +:::warning API Credentials Required +Both `apiKey` and `orgId` parameters are **required** for all API requests. To get your credentials, follow the **[Get Your Credentials](/docs/SDKs#1-get-your-credentials)** steps from the SDKs main page. +::: + +### Prepare for review Upload a document for preview without sending signature request emails. + + + +```javascript +const { documentId, previewUrl } = await TurboSign.prepareForReview({ + fileLink: "https://example.com/document.pdf", + documentName: "Contract Draft", + recipients: [ + { name: "John Doe", email: "john@example.com", signingOrder: 1 }, + ], + fields: [ + { + type: "signature", + page: 1, + x: 100, + y: 500, + width: 200, + height: 50, + recipientEmail: "john@example.com", + }, + ], +}); +``` + + + + ```typescript const { documentId, previewUrl } = await TurboSign.prepareForReview({ - fileLink: 'https://example.com/document.pdf', - // or upload directly: - // file: Buffer | Blob, - documentName: 'Contract Draft', + fileLink: "https://example.com/document.pdf", + documentName: "Contract Draft", recipients: [ - { name: 'John Doe', email: 'john@example.com', order: 1 } + { name: "John Doe", email: "john@example.com", signingOrder: 1 }, ], fields: [ - { type: 'signature', page: 1, x: 100, y: 500, width: 200, height: 50, recipientOrder: 1 } - ] + { + type: "signature", + page: 1, + x: 100, + y: 500, + width: 200, + height: 50, + recipientEmail: "john@example.com", + }, + ], }); ``` -### TurboSign.prepareForSigningSingle(options) + + + +### Prepare for signing Upload a document and immediately send signature requests to all recipients. + + + +```javascript +const { documentId } = await TurboSign.prepareForSigningSingle({ + fileLink: "https://example.com/document.pdf", + documentName: "Service Agreement", + senderName: "Your Company", + senderEmail: "sender@company.com", + recipients: [ + { name: "Recipient Name", email: "recipient@example.com", signingOrder: 1 }, + ], + fields: [ + { + type: "signature", + page: 1, + x: 100, + y: 500, + width: 200, + height: 50, + recipientEmail: "recipient@example.com", + }, + ], +}); +``` + + + + ```typescript -const { documentId, recipients } = await TurboSign.prepareForSigningSingle({ - fileLink: 'https://example.com/document.pdf', - documentName: 'Service Agreement', - senderName: 'Your Company', - senderEmail: 'sender@company.com', +const { documentId } = await TurboSign.prepareForSigningSingle({ + fileLink: "https://example.com/document.pdf", + documentName: "Service Agreement", + senderName: "Your Company", + senderEmail: "sender@company.com", recipients: [ - { name: 'Recipient Name', email: 'recipient@example.com', order: 1 } + { name: "Recipient Name", email: "recipient@example.com", signingOrder: 1 }, ], fields: [ - { type: 'signature', page: 1, x: 100, y: 500, width: 200, height: 50, recipientOrder: 1 } - ] + { + type: "signature", + page: 1, + x: 100, + y: 500, + width: 200, + height: 50, + recipientEmail: "recipient@example.com", + }, + ], }); ``` -### TurboSign.getStatus(documentId) + + -Check the status of a document and its recipients. +### Get status -```typescript -const status = await TurboSign.getStatus('document-uuid'); +Retrieve the current status of a document. -console.log(status.status); // 'pending' | 'completed' | 'voided' -console.log(status.completedAt); // Date when all signatures completed + + -for (const recipient of status.recipients) { - console.log(`${recipient.name}: ${recipient.status}`); // 'pending' | 'signed' - console.log(`Signed at: ${recipient.signedAt}`); -} -``` +```javascript +const result = await TurboSign.getStatus("document-uuid"); -### TurboSign.download(documentId) +console.log(JSON.stringify(result, null, 2)); +``` -Download the completed signed document. + + ```typescript -const pdfBuffer = await TurboSign.download('document-uuid'); +const result = await TurboSign.getStatus("document-uuid"); -// Save to file (Node.js) -import { writeFileSync } from 'fs'; -writeFileSync('signed-contract.pdf', pdfBuffer); +console.log(JSON.stringify(result, null, 2)); +``` -// Or upload to cloud storage -await s3.upload({ Body: pdfBuffer, Key: 'signed-contract.pdf' }); + + + +### Download document + +Download the completed signed document as a PDF Blob. + + + + +```javascript +const result = await TurboSign.download("document-uuid"); + +// Node.js: Save to file +const { writeFileSync } = require("fs"); +const buffer = Buffer.from(await result.arrayBuffer()); +writeFileSync("signed-contract.pdf", buffer); +``` + + + + +```typescript +const result = await TurboSign.download("document-uuid"); + +// Node.js: Save to file +import { writeFileSync } from "fs"; +const buffer = Buffer.from(await result.arrayBuffer()); +writeFileSync("signed-contract.pdf", buffer); ``` -### TurboSign.void(documentId, reason?) + + + +### Void Cancel/void a signature request. + + + +```javascript +await TurboSign.void("document-uuid", "Contract terms changed"); +``` + + + + ```typescript -await TurboSign.void('document-uuid', 'Contract terms changed'); +await TurboSign.void("document-uuid", "Contract terms changed"); ``` -### TurboSign.resend(documentId, recipientIds?) + + + +### Resend Resend signature request emails to specific recipients. -```typescript + + + +```javascript // Resend to all pending recipients -await TurboSign.resend('document-uuid'); +await TurboSign.resend("document-uuid"); // Resend to specific recipients -await TurboSign.resend('document-uuid', ['recipient-uuid-1', 'recipient-uuid-2']); +await TurboSign.resend("document-uuid", [ + "recipient-uuid-1", + "recipient-uuid-2", +]); ``` ---- - -## Framework Examples - -### Express.js + + ```typescript -import express from 'express'; -import { TurboSign } from '@turbodocx/sdk'; +// Resend to all pending recipients +await TurboSign.resend("document-uuid"); -const app = express(); -app.use(express.json()); +// Resend to specific recipients +await TurboSign.resend("document-uuid", [ + "recipient-uuid-1", + "recipient-uuid-2", +]); +``` -TurboSign.configure({ apiKey: process.env.TURBODOCX_API_KEY }); + + -app.post('/api/send-contract', async (req, res) => { - try { - const { recipientName, recipientEmail, contractUrl } = req.body; +### Get audit trail - const result = await TurboSign.prepareForSigningSingle({ - fileLink: contractUrl, - recipients: [{ name: recipientName, email: recipientEmail, order: 1 }], - fields: [ - { type: 'signature', page: 1, x: 100, y: 650, width: 200, height: 50, recipientOrder: 1 } - ] - }); +Retrieve the complete audit trail for a document, including all events and actions. - res.json({ documentId: result.documentId, signUrl: result.recipients[0].signUrl }); - } catch (error) { - res.status(500).json({ error: error.message }); - } -}); + + -app.get('/api/document/:id/status', async (req, res) => { - try { - const status = await TurboSign.getStatus(req.params.id); - res.json(status); - } catch (error) { - res.status(500).json({ error: error.message }); - } -}); +```javascript +const result = await TurboSign.getAuditTrail("document-uuid"); -app.listen(3000); +console.log(JSON.stringify(result, null, 2)); ``` -### Next.js API Routes + + ```typescript -// pages/api/send-for-signature.ts -import type { NextApiRequest, NextApiResponse } from 'next'; -import { TurboSign } from '@turbodocx/sdk'; +const result = await TurboSign.getAuditTrail("document-uuid"); -TurboSign.configure({ apiKey: process.env.TURBODOCX_API_KEY }); +console.log(JSON.stringify(result, null, 2)); +``` -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - if (req.method !== 'POST') { - return res.status(405).json({ error: 'Method not allowed' }); - } + + - const { recipients, documentUrl, fields } = req.body; +--- - try { - const result = await TurboSign.prepareForSigningSingle({ - fileLink: documentUrl, - recipients, - fields - }); +## Error Handling - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - } -} -``` +The SDK provides typed error classes for different failure scenarios. All errors extend the base `TurboDocxError` class. -### NestJS Service +### Error Classes -```typescript -import { Injectable } from '@nestjs/common'; -import { TurboSign } from '@turbodocx/sdk'; +| Error Class | Status Code | Code | Description | +|-------------|-------------|------|-------------| +| `TurboDocxError` | varies | varies | Base error class for all SDK errors | +| `AuthenticationError` | 401 | `AUTHENTICATION_ERROR` | Invalid or missing API credentials | +| `ValidationError` | 400 | `VALIDATION_ERROR` | Invalid request parameters | +| `NotFoundError` | 404 | `NOT_FOUND` | Document or resource not found | +| `RateLimitError` | 429 | `RATE_LIMIT_EXCEEDED` | Too many requests | +| `NetworkError` | - | `NETWORK_ERROR` | Network connectivity issues | -@Injectable() -export class SignatureService { - constructor() { - TurboSign.configure({ apiKey: process.env.TURBODOCX_API_KEY }); - } +### Handling Errors - async sendForSignature(documentUrl: string, recipientEmail: string, recipientName: string) { - return TurboSign.prepareForSigningSingle({ - fileLink: documentUrl, - recipients: [{ name: recipientName, email: recipientEmail, order: 1 }], - fields: [ - { type: 'signature', page: 1, x: 100, y: 650, width: 200, height: 50, recipientOrder: 1 } - ] - }); - } + + - async getDocumentStatus(documentId: string) { - return TurboSign.getStatus(documentId); - } +```javascript +const { + TurboSign, + TurboDocxError, + AuthenticationError, + ValidationError, + NotFoundError, + RateLimitError, + NetworkError, +} = require("@turbodocx/sdk"); - async downloadSignedDocument(documentId: string) { - return TurboSign.download(documentId); +try { + const result = await TurboSign.prepareForSigningSingle({ + fileLink: "https://example.com/contract.pdf", + recipients: [ + { name: "John Doe", email: "john@example.com", signingOrder: 1 }, + ], + fields: [ + { + type: "signature", + page: 1, + x: 100, + y: 650, + width: 200, + height: 50, + recipientEmail: "john@example.com", + }, + ], + }); +} catch (error) { + if (error instanceof AuthenticationError) { + console.error("Authentication failed:", error.message); + // Check your API key and org ID + } else if (error instanceof ValidationError) { + console.error("Validation error:", error.message); + // Check request parameters + } else if (error instanceof NotFoundError) { + console.error("Resource not found:", error.message); + // Document or recipient doesn't exist + } else if (error instanceof RateLimitError) { + console.error("Rate limited:", error.message); + // Wait and retry + } else if (error instanceof NetworkError) { + console.error("Network error:", error.message); + // Check connectivity + } else if (error instanceof TurboDocxError) { + console.error("SDK error:", error.message, error.statusCode, error.code); } } ``` ---- - -## Error Handling + + ```typescript -import { TurboSign, TurboDocxError, TurboDocxErrorCode } from '@turbodocx/sdk'; +import { + TurboSign, + TurboDocxError, + AuthenticationError, + ValidationError, + NotFoundError, + RateLimitError, + NetworkError, +} from "@turbodocx/sdk"; try { - const result = await TurboSign.prepareForSigningSingle({ /* ... */ }); + const result = await TurboSign.prepareForSigningSingle({ + fileLink: "https://example.com/contract.pdf", + recipients: [ + { name: "John Doe", email: "john@example.com", signingOrder: 1 }, + ], + fields: [ + { + type: "signature", + page: 1, + x: 100, + y: 650, + width: 200, + height: 50, + recipientEmail: "john@example.com", + }, + ], + }); } catch (error) { - if (error instanceof TurboDocxError) { - switch (error.code) { - case TurboDocxErrorCode.UNAUTHORIZED: - console.error('Invalid API key'); - break; - case TurboDocxErrorCode.INVALID_DOCUMENT: - console.error('Could not process document:', error.message); - break; - case TurboDocxErrorCode.RATE_LIMITED: - console.error('Rate limited, retry after:', error.retryAfter); - break; - default: - console.error(`Error ${error.code}: ${error.message}`); - } - } else { - // Network or unexpected error - throw error; + if (error instanceof AuthenticationError) { + console.error("Authentication failed:", error.message); + // Check your API key and org ID + } else if (error instanceof ValidationError) { + console.error("Validation error:", error.message); + // Check request parameters + } else if (error instanceof NotFoundError) { + console.error("Resource not found:", error.message); + // Document or recipient doesn't exist + } else if (error instanceof RateLimitError) { + console.error("Rate limited:", error.message); + // Wait and retry + } else if (error instanceof NetworkError) { + console.error("Network error:", error.message); + // Check connectivity + } else if (error instanceof TurboDocxError) { + console.error("SDK error:", error.message, error.statusCode, error.code); } } ``` + + + +### Error Properties + +All errors include these properties: + +| Property | Type | Description | +|----------|------|-------------| +| `message` | `string` | Human-readable error description | +| `statusCode` | `number \| undefined` | HTTP status code (if applicable) | +| `code` | `string \| undefined` | Machine-readable error code | + --- ## TypeScript Types -The SDK exports all types for full TypeScript support: +The SDK exports TypeScript types for full type safety. Import them directly from the package. + +### Importing Types ```typescript import type { - SigningRequest, - SigningResult, - Recipient, - Field, - DocumentStatus, - TurboDocxError -} from '@turbodocx/sdk'; - -// Type-safe field creation -const field: Field = { - type: 'signature', - page: 1, - x: 100, - y: 500, - width: 200, - height: 50, - recipientOrder: 1 -}; - -// Type-safe recipient -const recipient: Recipient = { - name: 'John Doe', - email: 'john@example.com', - order: 1 -}; + // Field types + SignatureFieldType, + N8nField, + N8nRecipient, + // Request types + PrepareForReviewRequest, + PrepareForSigningSingleRequest, +} from "@turbodocx/sdk"; ``` ---- - -## Field Types +### SignatureFieldType -| Type | Description | Required by Signer | -|:-----|:------------|:-------------------| -| `signature` | Electronic signature drawing/typing | Yes | -| `initials` | Initials field | Yes | -| `text` | Free-form text input | Configurable | -| `date` | Date stamp (auto-filled on signing) | Auto | -| `checkbox` | Checkbox for agreements | Configurable | -| `full_name` | Signer's full name | Auto | -| `email` | Signer's email address | Auto | -| `title` | Job title field | Optional | -| `company` | Company name field | Optional | - ---- +Union type for all available field types: -## Webhook Signature Verification +```typescript +type SignatureFieldType = + | "signature" + | "initial" + | "date" + | "text" + | "full_name" + | "title" + | "company" + | "first_name" + | "last_name" + | "email" + | "checkbox"; +``` -Verify that webhooks are genuinely from TurboDocx: +### N8nRecipient + +Recipient configuration for signature requests: + +| Property | Type | Required | Description | +|----------|------|----------|-------------| +| `name` | `string` | Yes | Recipient's full name | +| `email` | `string` | Yes | Recipient's email address | +| `signingOrder` | `number` | Yes | Signing order (1-indexed) | + +### N8nField + +Field configuration supporting both coordinate-based and template-based positioning: + +| Property | Type | Required | Description | +|----------|------|----------|-------------| +| `type` | `SignatureFieldType` | Yes | Field type | +| `recipientEmail` | `string` | Yes | Which recipient fills this field | +| `page` | `number` | Yes | Page number (1-indexed) | +| `x` | `number` | Yes | X coordinate position | +| `y` | `number` | Yes | Y coordinate position | +| `width` | `number` | Yes | Field width in pixels | +| `height` | `number` | Yes | Field height in pixels | +| `defaultValue` | `string` | No | Default value (for checkbox: `"true"` or `"false"`) | +| `isMultiline` | `boolean` | No | Enable multiline text | +| `isReadonly` | `boolean` | No | Make field read-only (pre-filled) | +| `required` | `boolean` | No | Whether field is required | +| `backgroundColor` | `string` | No | Background color (hex, rgb, or named) | +| `template` | `object` | No | Template anchor configuration | + +**Template Configuration:** + +| Property | Type | Description | +|----------|------|-------------| +| `anchor` | `string` | Text anchor pattern like `{TagName}` | +| `searchText` | `string` | Alternative: search for any text | +| `placement` | `string` | `"replace"` \| `"before"` \| `"after"` \| `"above"` \| `"below"` | +| `size` | `object` | `{ width: number; height: number }` | +| `offset` | `object` | `{ x: number; y: number }` | +| `caseSensitive` | `boolean` | Case sensitive search (default: false) | +| `useRegex` | `boolean` | Use regex for anchor/searchText (default: false) | + +### PrepareForReviewRequest / PrepareForSigningSingleRequest + +Request configuration for `prepareForReview` and `prepareForSigningSingle` methods: + +| Property | Type | Required | Description | +|----------|------|----------|-------------| +| `file` | `string \| File \| Buffer` | Conditional | PDF file as file path, Buffer, or browser File | +| `fileLink` | `string` | Conditional | URL to document file | +| `deliverableId` | `string` | Conditional | TurboDocx deliverable ID | +| `templateId` | `string` | Conditional | TurboDocx template ID | +| `recipients` | `N8nRecipient[]` | Yes | Recipients who will sign | +| `fields` | `N8nField[]` | Yes | Signature fields configuration | +| `documentName` | `string` | No | Document name | +| `documentDescription` | `string` | No | Document description | +| `senderName` | `string` | No | Sender name | +| `senderEmail` | `string` | No | Sender email | +| `ccEmails` | `string[]` | No | Array of CC email addresses | + +:::info File Source (Conditional) +Exactly one file source is required: `file`, `fileLink`, `deliverableId`, or `templateId`. +::: -```typescript -import { verifyWebhookSignature } from '@turbodocx/sdk'; -import express from 'express'; - -const app = express(); - -// Use raw body for signature verification -app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => { - const signature = req.headers['x-turbodocx-signature'] as string; - const timestamp = req.headers['x-turbodocx-timestamp'] as string; - const body = req.body; - - const isValid = verifyWebhookSignature({ - signature, - timestamp, - body, - secret: process.env.TURBODOCX_WEBHOOK_SECRET - }); +--- - if (!isValid) { - return res.status(401).json({ error: 'Invalid signature' }); - } +## Additional Documentation - const event = JSON.parse(body.toString()); +For detailed information about advanced configuration and API concepts, see: - switch (event.event) { - case 'signature.document.completed': - console.log('Document completed:', event.data.document_id); - break; - case 'signature.document.voided': - console.log('Document voided:', event.data.document_id); - break; - } +### Core API References - res.status(200).json({ received: true }); -}); -``` +- **[Request Body Reference](/docs/TurboSign/API%20Signatures#request-body-multipartform-data)** - Complete request body parameters, file sources, and multipart/form-data structure +- **[Recipients Reference](/docs/TurboSign/API%20Signatures#recipients-reference)** - Recipient properties, signing order, metadata, and configuration options +- **[Field Types Reference](/docs/TurboSign/API%20Signatures#field-types-reference)** - All available field types (signature, date, text, checkbox, etc.) with properties and behaviors +- **[Field Positioning Methods](/docs/TurboSign/API%20Signatures#field-positioning-methods)** - Template-based vs coordinate-based positioning, anchor configuration, and best practices --- @@ -474,5 +1132,5 @@ app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => { - [GitHub Repository](https://github.com/TurboDocx/SDK/tree/main/packages/js-sdk) - [npm Package](https://www.npmjs.com/package/@turbodocx/sdk) -- [API Reference](/docs/TurboSign/API-Signatures) +- [API Reference](/docs/TurboSign/API%20Signatures) - [Webhook Configuration](/docs/TurboSign/Webhooks) diff --git a/docs/SDKs/python.md b/docs/SDKs/python.md index 59f62cf..8fb7ccf 100644 --- a/docs/SDKs/python.md +++ b/docs/SDKs/python.md @@ -16,13 +16,11 @@ keywords: import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; +import ScriptLoader from '@site/src/components/ScriptLoader'; # Python SDK -The official TurboDocx SDK for Python applications. Async-first design with synchronous wrappers for flexibility. - -[![PyPI version](https://img.shields.io/pypi/v/turbodocx-sdk?logo=python&logoColor=white)](https://pypi.org/project/turbodocx-sdk/) -[![GitHub](https://img.shields.io/github/stars/turbodocx/sdk?style=social)](https://github.com/TurboDocx/SDK) +The official TurboDocx SDK for Python applications. Build document generation and digital signature workflows with async/await patterns and comprehensive error handling. Available on PyPI as `turbodocx-sdk`. ## Installation @@ -61,163 +59,264 @@ pipenv install turbodocx-sdk ## Configuration ```python -from turbodocx import TurboSign +from turbodocx_sdk import TurboSign import os -# Configure globally -TurboSign.configure(api_key=os.environ["TURBODOCX_API_KEY"]) - -# Or create an instance with configuration -client = TurboSign(api_key=os.environ["TURBODOCX_API_KEY"]) +# Configure globally (recommended) +TurboSign.configure( + api_key=os.environ["TURBODOCX_API_KEY"], # Required: Your TurboDocx API key + org_id=os.environ["TURBODOCX_ORG_ID"], # Required: Your organization ID + # base_url="https://api.turbodocx.com" # Optional: Override base URL +) ``` +:::tip Authentication +Authenticate using `api_key`. API keys are recommended for server-side applications. +::: + ### Environment Variables ```bash # .env TURBODOCX_API_KEY=your_api_key_here +TURBODOCX_ORG_ID=your_org_id_here ``` +:::warning API Credentials Required +Both `api_key` and `org_id` parameters are **required** for all API requests. To get your credentials, follow the **[Get Your Credentials](/docs/SDKs#1-get-your-credentials)** steps from the SDKs main page. +::: + --- ## Quick Start -### Async Usage (Recommended) +### Send a Document for Signature ```python import asyncio -from turbodocx import TurboSign +from turbodocx_sdk import TurboSign import os -TurboSign.configure(api_key=os.environ["TURBODOCX_API_KEY"]) +TurboSign.configure( + api_key=os.environ["TURBODOCX_API_KEY"], + org_id=os.environ["TURBODOCX_ORG_ID"] +) async def send_contract(): result = await TurboSign.prepare_for_signing_single( - file_link="https://example.com/contract.pdf", - document_name="Service Agreement", - sender_name="Acme Corp", - sender_email="contracts@acme.com", recipients=[ - {"name": "Alice Smith", "email": "alice@example.com", "order": 1}, - {"name": "Bob Johnson", "email": "bob@example.com", "order": 2} + {"name": "Alice Smith", "email": "alice@example.com", "signingOrder": 1}, + {"name": "Bob Johnson", "email": "bob@example.com", "signingOrder": 2} ], fields=[ # Alice's signature - {"type": "signature", "page": 1, "x": 100, "y": 650, "width": 200, "height": 50, "recipient_order": 1}, - {"type": "date", "page": 1, "x": 320, "y": 650, "width": 100, "height": 30, "recipient_order": 1}, + {"type": "signature", "page": 1, "x": 100, "y": 650, "width": 200, "height": 50, "recipientEmail": "alice@example.com"}, + {"type": "date", "page": 1, "x": 320, "y": 650, "width": 100, "height": 30, "recipientEmail": "alice@example.com"}, # Bob's signature - {"type": "signature", "page": 1, "x": 100, "y": 720, "width": 200, "height": 50, "recipient_order": 2}, - {"type": "date", "page": 1, "x": 320, "y": 720, "width": 100, "height": 30, "recipient_order": 2} - ] + {"type": "signature", "page": 1, "x": 100, "y": 720, "width": 200, "height": 50, "recipientEmail": "bob@example.com"}, + {"type": "date", "page": 1, "x": 320, "y": 720, "width": 100, "height": 30, "recipientEmail": "bob@example.com"} + ], + file_link="https://example.com/contract.pdf", + document_name="Service Agreement", + sender_name="Acme Corp", + sender_email="contracts@acme.com", ) - print(f"Document ID: {result.document_id}") - for recipient in result.recipients: - print(f"{recipient.name}: {recipient.sign_url}") + print("Result:", json.dumps(result, indent=2)) asyncio.run(send_contract()) ``` -### Synchronous Usage +### Using Template-Based Fields ```python -from turbodocx import TurboSign -import os +result = await TurboSign.prepare_for_signing_single( + recipients=[{"name": "Alice Smith", "email": "alice@example.com", "signingOrder": 1}], + fields=[ + {"type": "signature", "template": {"anchor": "{SIGNATURE_ALICE}"}, "width": 200, "height": 50, "recipientEmail": "alice@example.com"}, + {"type": "date", "template": {"anchor": "{DATE_ALICE}"}, "width": 100, "height": 30, "recipientEmail": "alice@example.com"} + ], + file_link="https://example.com/contract-with-placeholders.pdf", +) + +print("Result:", json.dumps(result, indent=2)) +``` + +:::info Template Anchors Required +**Important:** The document file must contain the anchor text (e.g., `{SIGNATURE_ALICE}`, `{DATE_ALICE}`) that you reference in your fields. If the anchors don't exist in the document, the API will return an error. +::: -TurboSign.configure(api_key=os.environ["TURBODOCX_API_KEY"]) +--- -# Use sync_ prefix for synchronous methods -result = TurboSign.sync_prepare_for_signing_single( - file_link="https://example.com/contract.pdf", - recipients=[{"name": "John Doe", "email": "john@example.com", "order": 1}], +## File Input Methods + +TurboSign supports four different ways to provide document files: + +### 1. File Upload (bytes) + +```python +with open("./contract.pdf", "rb") as f: + pdf_buffer = f.read() + +result = await TurboSign.prepare_for_signing_single( + file=pdf_buffer, + recipients=[ + {"name": "John Doe", "email": "john@example.com", "signingOrder": 1}, + ], fields=[ - {"type": "signature", "page": 1, "x": 100, "y": 500, "width": 200, "height": 50, "recipient_order": 1} - ] + { + "type": "signature", + "page": 1, + "x": 100, + "y": 650, + "width": 200, + "height": 50, + "recipientEmail": "john@example.com", + }, + ], ) +``` -print(f"Sign URL: {result.recipients[0].sign_url}") +### 2. File URL (file_link) + +```python +result = await TurboSign.prepare_for_signing_single( + file_link="https://storage.example.com/contracts/agreement.pdf", + recipients=[ + {"name": "John Doe", "email": "john@example.com", "signingOrder": 1}, + ], + fields=[ + { + "type": "signature", + "page": 1, + "x": 100, + "y": 650, + "width": 200, + "height": 50, + "recipientEmail": "john@example.com", + }, + ], +) ``` -### Using Template-Based Fields +:::tip When to use file_link +Use `file_link` when your documents are already hosted on cloud storage (S3, Google Cloud Storage, etc.). This is more efficient than downloading and re-uploading files. +::: + +### 3. TurboDocx Deliverable ID ```python +# Use a previously generated TurboDocx document result = await TurboSign.prepare_for_signing_single( - file_link="https://example.com/contract-with-placeholders.pdf", - recipients=[{"name": "Alice Smith", "email": "alice@example.com", "order": 1}], + deliverable_id="deliverable-uuid-from-turbodocx", + recipients=[ + {"name": "John Doe", "email": "john@example.com", "signingOrder": 1}, + ], fields=[ - {"type": "signature", "anchor": "{SIGNATURE_ALICE}", "width": 200, "height": 50, "recipient_order": 1}, - {"type": "date", "anchor": "{DATE_ALICE}", "width": 100, "height": 30, "recipient_order": 1} - ] + { + "type": "signature", + "page": 1, + "x": 100, + "y": 650, + "width": 200, + "height": 50, + "recipientEmail": "john@example.com", + }, + ], ) ``` +:::info Integration with TurboDocx +`deliverable_id` references documents generated using TurboDocx's document generation API. This creates a seamless workflow: generate → sign. +::: + +### 4. TurboDocx Template ID + +```python +# Use a pre-configured TurboSign template +result = await TurboSign.prepare_for_signing_single( + template_id="template-uuid-from-turbodocx", # Template already contains anchors + recipients=[ + {"name": "Alice Smith", "email": "alice@example.com", "signingOrder": 1}, + ], + fields=[ + { + "type": "signature", + "template": {"anchor": "{SIGNATURE_ALICE}"}, + "width": 200, + "height": 50, + "recipientEmail": "alice@example.com", + }, + ], +) +``` + +:::info Integration with TurboDocx +`template_id` references pre-configured TurboSign templates created in the TurboDocx dashboard. These templates come with built-in anchors and field positioning, making it easy to reuse signature workflows across multiple documents. +::: + --- ## API Reference -### TurboSign.configure() +### Configure -Configure the SDK with your API credentials. +Configure the SDK with your API credentials and organization settings. ```python TurboSign.configure( - api_key: str, # Required: Your TurboDocx API key - base_url: str = None, # Optional: API base URL - timeout: int = 30 # Optional: Request timeout in seconds + api_key: str, # Required: Your TurboDocx API key + org_id: str, # Required: Your organization ID + base_url: str = "https://api.turbodocx.com" # Optional: API base URL ) ``` -### prepare_for_review() +### Prepare for review Upload a document for preview without sending signature request emails. ```python result = await TurboSign.prepare_for_review( + recipients=[{"name": "John Doe", "email": "john@example.com", "signingOrder": 1}], + fields=[{"type": "signature", "page": 1, "x": 100, "y": 500, "width": 200, "height": 50, "recipientEmail": "john@example.com"}], file_link="https://example.com/document.pdf", - # or upload directly: - # file=open("document.pdf", "rb"), document_name="Contract Draft", - recipients=[{"name": "John Doe", "email": "john@example.com", "order": 1}], - fields=[{"type": "signature", "page": 1, "x": 100, "y": 500, "width": 200, "height": 50, "recipient_order": 1}] ) -print(result.document_id) -print(result.preview_url) +print(result["documentId"]) +print(result["previewUrl"]) ``` -### prepare_for_signing_single() +### Prepare for signing -Upload a document and immediately send signature requests. +Upload a document and immediately send signature requests to all recipients. ```python result = await TurboSign.prepare_for_signing_single( + recipients=[{"name": "Recipient Name", "email": "recipient@example.com", "signingOrder": 1}], + fields=[{"type": "signature", "page": 1, "x": 100, "y": 500, "width": 200, "height": 50, "recipientEmail": "recipient@example.com"}], file_link="https://example.com/document.pdf", document_name="Service Agreement", sender_name="Your Company", sender_email="sender@company.com", - recipients=[{"name": "Recipient Name", "email": "recipient@example.com", "order": 1}], - fields=[{"type": "signature", "page": 1, "x": 100, "y": 500, "width": 200, "height": 50, "recipient_order": 1}] ) + +print(result["documentId"]) ``` -### get_status() +### Get status -Check the status of a document and its recipients. +Retrieve the current status of a document. ```python -status = await TurboSign.get_status("document-uuid") - -print(status.status) # 'pending', 'completed', or 'voided' -print(status.completed_at) +result = await TurboSign.get_status("document-uuid") -for recipient in status.recipients: - print(f"{recipient.name}: {recipient.status}") - print(f"Signed at: {recipient.signed_at}") +print("Result:", json.dumps(result, indent=2)) ``` -### download() +### Download document -Download the completed signed document. +Download the completed signed document as PDF bytes. ```python pdf_bytes = await TurboSign.download("document-uuid") @@ -225,256 +324,246 @@ pdf_bytes = await TurboSign.download("document-uuid") # Save to file with open("signed-contract.pdf", "wb") as f: f.write(pdf_bytes) - -# Or upload to S3 -import boto3 -s3 = boto3.client("s3") -s3.put_object(Bucket="my-bucket", Key="signed-contract.pdf", Body=pdf_bytes) ``` -### void() +### Void Cancel/void a signature request. ```python -await TurboSign.void("document-uuid", reason="Contract terms changed") +result = await TurboSign.void_document("document-uuid", reason="Contract terms changed") + +print("Result:", json.dumps(result, indent=2)) ``` -### resend() +### Resend -Resend signature request emails. +Resend signature request emails to specific recipients. ```python -# Resend to all pending recipients -await TurboSign.resend("document-uuid") +result = await TurboSign.resend_email("document-uuid", recipient_ids=["recipient-uuid-1", "recipient-uuid-2"]) -# Resend to specific recipients -await TurboSign.resend("document-uuid", recipient_ids=["recipient-uuid-1", "recipient-uuid-2"]) +print("Result:", json.dumps(result, indent=2)) ``` ---- - -## Framework Examples +### Get audit trail -### FastAPI +Retrieve the complete audit trail for a document, including all events and actions. ```python -from fastapi import FastAPI, HTTPException -from pydantic import BaseModel -from turbodocx import TurboSign -import os +result = await TurboSign.get_audit_trail("document-uuid") -app = FastAPI() -TurboSign.configure(api_key=os.environ["TURBODOCX_API_KEY"]) - -class SendContractRequest(BaseModel): - recipient_name: str - recipient_email: str - contract_url: str - -class SendContractResponse(BaseModel): - document_id: str - sign_url: str - -@app.post("/api/send-contract", response_model=SendContractResponse) -async def send_contract(request: SendContractRequest): - try: - result = await TurboSign.prepare_for_signing_single( - file_link=request.contract_url, - recipients=[{"name": request.recipient_name, "email": request.recipient_email, "order": 1}], - fields=[{"type": "signature", "page": 1, "x": 100, "y": 650, "width": 200, "height": 50, "recipient_order": 1}] - ) - return SendContractResponse( - document_id=result.document_id, - sign_url=result.recipients[0].sign_url - ) - except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) - -@app.get("/api/document/{document_id}/status") -async def get_document_status(document_id: str): - try: - return await TurboSign.get_status(document_id) - except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) +print("Result:", json.dumps(result, indent=2)) ``` -### Django +--- -```python -# views.py -from django.http import JsonResponse -from django.views.decorators.http import require_http_methods -from django.views.decorators.csrf import csrf_exempt -from turbodocx import TurboSign -import json -import os +## Error Handling -TurboSign.configure(api_key=os.environ["TURBODOCX_API_KEY"]) +The SDK provides typed error classes for different failure scenarios. All errors extend the base `TurboDocxError` class. -@csrf_exempt -@require_http_methods(["POST"]) -def send_contract(request): - data = json.loads(request.body) +### Error Classes - # Use sync version in Django (unless using async views) - result = TurboSign.sync_prepare_for_signing_single( - file_link=data["contract_url"], - recipients=[{"name": data["recipient_name"], "email": data["recipient_email"], "order": 1}], - fields=[{"type": "signature", "page": 1, "x": 100, "y": 650, "width": 200, "height": 50, "recipient_order": 1}] - ) +| Error Class | Status Code | Description | +| --------------------- | ----------- | ----------------------------------- | +| `TurboDocxError` | varies | Base error class for all SDK errors | +| `AuthenticationError` | 401 | Invalid or missing API credentials | +| `ValidationError` | 400 | Invalid request parameters | +| `NotFoundError` | 404 | Document or resource not found | +| `RateLimitError` | 429 | Too many requests | +| `NetworkError` | - | Network connectivity issues | + +### Handling Errors + +```python +from turbodocx_sdk import ( + TurboSign, + TurboDocxError, + AuthenticationError, + ValidationError, + NotFoundError, + RateLimitError, + NetworkError, +) - return JsonResponse({ - "document_id": result.document_id, - "sign_url": result.recipients[0].sign_url - }) - -@require_http_methods(["GET"]) -def document_status(request, document_id): - status = TurboSign.sync_get_status(document_id) - return JsonResponse({ - "status": status.status, - "recipients": [ - {"name": r.name, "status": r.status} - for r in status.recipients - ] - }) +try: + result = await TurboSign.prepare_for_signing_single( + recipients=[{"name": "John Doe", "email": "john@example.com", "signingOrder": 1}], + fields=[{ + "type": "signature", + "page": 1, + "x": 100, + "y": 650, + "width": 200, + "height": 50, + "recipientEmail": "john@example.com", + }], + file_link="https://example.com/contract.pdf", + ) +except AuthenticationError as e: + print(f"Authentication failed: {e}") + # Check your API key and org ID +except ValidationError as e: + print(f"Validation error: {e}") + # Check request parameters +except NotFoundError as e: + print(f"Resource not found: {e}") + # Document or recipient doesn't exist +except RateLimitError as e: + print(f"Rate limited: {e}") + # Wait and retry +except NetworkError as e: + print(f"Network error: {e}") + # Check connectivity +except TurboDocxError as e: + print(f"SDK error: {e}, status_code={e.status_code}, code={e.code}") ``` -### Flask +### Error Properties -```python -from flask import Flask, request, jsonify -from turbodocx import TurboSign -import os +All errors include these properties: -app = Flask(__name__) -TurboSign.configure(api_key=os.environ["TURBODOCX_API_KEY"]) +| Property | Type | Description | +| ------------- | ------------- | --------------------------------------------------- | +| `message` | `str` | Human-readable error description (via `str(error)`) | +| `status_code` | `int \| None` | HTTP status code (if applicable) | +| `code` | `str \| None` | Machine-readable error code | -@app.route("/api/send-contract", methods=["POST"]) -def send_contract(): - data = request.json +--- - result = TurboSign.sync_prepare_for_signing_single( - file_link=data["contract_url"], - recipients=[{"name": data["recipient_name"], "email": data["recipient_email"], "order": 1}], - fields=[{"type": "signature", "page": 1, "x": 100, "y": 650, "width": 200, "height": 50, "recipient_order": 1}] - ) +## Python Types - return jsonify({ - "document_id": result.document_id, - "sign_url": result.recipients[0].sign_url - }) +The SDK uses Python type hints with `Dict[str, Any]` for flexible JSON-like structures. -@app.route("/api/document//status") -def document_status(document_id): - status = TurboSign.sync_get_status(document_id) - return jsonify(status.dict()) +### Importing Types -if __name__ == "__main__": - app.run(debug=True) +```python +from typing import Dict, List, Any, Optional ``` ---- +### SignatureFieldType -## Error Handling +String literal values for field types: ```python -from turbodocx import TurboSign -from turbodocx.exceptions import ( - TurboDocxError, - UnauthorizedError, - InvalidDocumentError, - RateLimitedError, - NotFoundError -) - -try: - result = await TurboSign.prepare_for_signing_single(...) -except UnauthorizedError: - print("Invalid API key") -except InvalidDocumentError as e: - print(f"Could not process document: {e.message}") -except RateLimitedError as e: - print(f"Rate limited, retry after: {e.retry_after} seconds") -except NotFoundError: - print("Document not found") -except TurboDocxError as e: - print(f"Error {e.code}: {e.message}") +# Available field type values +field_types = [ + "signature", + "initial", + "date", + "text", + "full_name", + "title", + "company", + "first_name", + "last_name", + "email", + "checkbox", +] ``` ---- +### Recipient -## Type Hints +Recipient configuration for signature requests: -The SDK includes full type annotations: +  + +| Property | Type | Required | Description | +| -------------- | ----- | -------- | ------------------------- | +| `name` | `str` | Yes | Recipient's full name | +| `email` | `str` | Yes | Recipient's email address | +| `signingOrder` | `int` | Yes | Signing order (1-indexed) | ```python -from turbodocx.types import ( - SigningRequest, - SigningResult, - Recipient, - Field, - DocumentStatus, - FieldType -) +recipient: Dict[str, Any] = { + "name": "John Doe", + "email": "john@example.com", + "signingOrder": 1 +} +``` -# Type-safe field creation -field: Field = { - "type": FieldType.SIGNATURE, +### Field + +Field configuration supporting both coordinate-based and template-based positioning: + +  + +| Property | Type | Required | Description | +| ----------------- | ------ | -------- | --------------------------------------------------- | +| `type` | `str` | Yes | Field type (see SignatureFieldType) | +| `recipientEmail` | `str` | Yes | Which recipient fills this field | +| `page` | `int` | Yes | Page number (1-indexed) | +| `x` | `int` | Yes | X coordinate position | +| `y` | `int` | Yes | Y coordinate position | +| `width` | `int` | Yes | Field width in pixels | +| `height` | `int` | Yes | Field height in pixels | +| `defaultValue` | `str` | No | Default value (for checkbox: `"true"` or `"false"`) | +| `isMultiline` | `bool` | No | Enable multiline text | +| `isReadonly` | `bool` | No | Make field read-only (pre-filled) | +| `required` | `bool` | No | Whether field is required | +| `backgroundColor` | `str` | No | Background color (hex, rgb, or named) | +| `template` | `Dict` | No | Template anchor configuration | + +**Template Configuration:** + +| Property | Type | Description | +| --------------- | ------ | ---------------------------------------------------------------- | +| `anchor` | `str` | Text anchor pattern like `{TagName}` | +| `searchText` | `str` | Alternative: search for any text | +| `placement` | `str` | `"replace"` \| `"before"` \| `"after"` \| `"above"` \| `"below"` | +| `size` | `Dict` | `{ "width": int, "height": int }` | +| `offset` | `Dict` | `{ "x": int, "y": int }` | +| `caseSensitive` | `bool` | Case sensitive search (default: False) | +| `useRegex` | `bool` | Use regex for anchor/searchText (default: False) | + +```python +field: Dict[str, Any] = { + "type": "signature", "page": 1, "x": 100, "y": 500, "width": 200, "height": 50, - "recipient_order": 1 -} - -# Type-safe recipient -recipient: Recipient = { - "name": "John Doe", - "email": "john@example.com", - "order": 1 + "recipientEmail": "john@example.com" } ``` ---- +### Request Parameters -## Webhook Signature Verification +Request configuration for `prepare_for_review` and `prepare_for_signing_single` methods: -Verify that webhooks are genuinely from TurboDocx: +  -```python -from turbodocx import verify_webhook_signature -from fastapi import FastAPI, Request, HTTPException - -app = FastAPI() - -@app.post("/webhook") -async def handle_webhook(request: Request): - body = await request.body() - signature = request.headers.get("x-turbodocx-signature") - timestamp = request.headers.get("x-turbodocx-timestamp") - - is_valid = verify_webhook_signature( - signature=signature, - timestamp=timestamp, - body=body, - secret=os.environ["TURBODOCX_WEBHOOK_SECRET"] - ) +| Parameter | Type | Required | Description | +| ---------------------- | ------------ | ----------- | ------------------------------ | +| `recipients` | `List[Dict]` | Yes | Recipients who will sign | +| `fields` | `List[Dict]` | Yes | Signature fields configuration | +| `file` | `bytes` | Conditional | PDF file content as bytes | +| `file_link` | `str` | Conditional | URL to document file | +| `deliverable_id` | `str` | Conditional | TurboDocx deliverable ID | +| `template_id` | `str` | Conditional | TurboDocx template ID | +| `document_name` | `str` | No | Document name | +| `document_description` | `str` | No | Document description | +| `sender_name` | `str` | No | Sender name | +| `sender_email` | `str` | No | Sender email | +| `cc_emails` | `List[str]` | No | Array of CC email addresses | + +:::info File Source (Conditional) +Exactly one file source is required: `file`, `file_link`, `deliverable_id`, or `template_id`. +::: - if not is_valid: - raise HTTPException(status_code=401, detail="Invalid signature") +--- - event = json.loads(body) +## Additional Documentation - if event["event"] == "signature.document.completed": - print(f"Document completed: {event['data']['document_id']}") - elif event["event"] == "signature.document.voided": - print(f"Document voided: {event['data']['document_id']}") +For detailed information about advanced configuration and API concepts, see: - return {"received": True} -``` +### Core API References + +- **[Request Body Reference](/docs/TurboSign/API%20Signatures#request-body-multipartform-data)** - Complete request body parameters, file sources, and multipart/form-data structure +- **[Recipients Reference](/docs/TurboSign/API%20Signatures#recipients-reference)** - Recipient properties, signing order, metadata, and configuration options +- **[Field Types Reference](/docs/TurboSign/API%20Signatures#field-types-reference)** - All available field types (signature, date, text, checkbox, etc.) with properties and behaviors +- **[Field Positioning Methods](/docs/TurboSign/API%20Signatures#field-positioning-methods)** - Template-based vs coordinate-based positioning, anchor configuration, and best practices --- @@ -482,5 +571,5 @@ async def handle_webhook(request: Request): - [GitHub Repository](https://github.com/TurboDocx/SDK/tree/main/packages/py-sdk) - [PyPI Package](https://pypi.org/project/turbodocx-sdk/) -- [API Reference](/docs/TurboSign/API-Signatures) +- [API Reference](/docs/TurboSign/API%20Signatures) - [Webhook Configuration](/docs/TurboSign/Webhooks) diff --git a/docs/SDKs/ruby.md b/docs/SDKs/ruby.md deleted file mode 100644 index b101452..0000000 --- a/docs/SDKs/ruby.md +++ /dev/null @@ -1,514 +0,0 @@ ---- -title: Ruby SDK -sidebar_position: 7 -sidebar_label: Ruby -description: Official TurboDocx Ruby SDK. Idiomatic Ruby with Rails and Sidekiq integration for document generation and digital signatures. -keywords: - - turbodocx ruby - - turbosign ruby - - rubygems turbodocx - - rails sdk - - sidekiq integration - - document api ruby - - esignature ruby ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -# Ruby SDK - -The official TurboDocx SDK for Ruby applications. Idiomatic Ruby with Rails and background job integration. - -[![Gem Version](https://img.shields.io/gem/v/turbodocx-sdk?logo=ruby&logoColor=white)](https://rubygems.org/gems/turbodocx-sdk) -[![GitHub](https://img.shields.io/github/stars/turbodocx/sdk?style=social)](https://github.com/TurboDocx/SDK) - -## Installation - - - - -```bash -gem install turbodocx-sdk -``` - - - - -```ruby -# Gemfile -gem 'turbodocx-sdk' -``` - -Then run: - -```bash -bundle install -``` - - - - -## Requirements - -- Ruby 3.0+ -- `faraday` (installed automatically) - ---- - -## Configuration - -### Basic Configuration - -```ruby -require 'turbodocx' - -# Global configuration -TurboDocx.configure do |config| - config.api_key = ENV['TURBODOCX_API_KEY'] - # Optional - config.base_url = 'https://api.turbodocx.com' - config.timeout = 30 -end - -# Or create a client instance -client = TurboDocx::TurboSign.new(api_key: ENV['TURBODOCX_API_KEY']) -``` - -### Rails Configuration - -```ruby -# config/initializers/turbodocx.rb -TurboDocx.configure do |config| - config.api_key = Rails.application.credentials.turbodocx[:api_key] -end -``` - ---- - -## Quick Start - -### Send a Document for Signature - -```ruby -require 'turbodocx' - -TurboDocx.configure do |config| - config.api_key = ENV['TURBODOCX_API_KEY'] -end - -result = TurboDocx::TurboSign.prepare_for_signing_single( - file_link: 'https://example.com/contract.pdf', - document_name: 'Service Agreement', - sender_name: 'Acme Corp', - sender_email: 'contracts@acme.com', - recipients: [ - { name: 'Alice Smith', email: 'alice@example.com', order: 1 }, - { name: 'Bob Johnson', email: 'bob@example.com', order: 2 } - ], - fields: [ - # Alice's signature - { type: 'signature', page: 1, x: 100, y: 650, width: 200, height: 50, recipient_order: 1 }, - { type: 'date', page: 1, x: 320, y: 650, width: 100, height: 30, recipient_order: 1 }, - # Bob's signature - { type: 'signature', page: 1, x: 100, y: 720, width: 200, height: 50, recipient_order: 2 }, - { type: 'date', page: 1, x: 320, y: 720, width: 100, height: 30, recipient_order: 2 } - ] -) - -puts "Document ID: #{result.document_id}" -result.recipients.each do |recipient| - puts "#{recipient.name}: #{recipient.sign_url}" -end -``` - -### Using Template-Based Fields - -```ruby -result = TurboDocx::TurboSign.prepare_for_signing_single( - file_link: 'https://example.com/contract-with-placeholders.pdf', - recipients: [ - { name: 'Alice Smith', email: 'alice@example.com', order: 1 } - ], - fields: [ - { type: 'signature', anchor: '{SIGNATURE_ALICE}', width: 200, height: 50, recipient_order: 1 }, - { type: 'date', anchor: '{DATE_ALICE}', width: 100, height: 30, recipient_order: 1 } - ] -) -``` - ---- - -## API Reference - -### prepare_for_review - -Upload a document for preview without sending emails. - -```ruby -result = TurboDocx::TurboSign.prepare_for_review( - file_link: 'https://example.com/document.pdf', - # Or upload directly: - # file: File.read('document.pdf'), - document_name: 'Contract Draft', - recipients: [ - { name: 'John Doe', email: 'john@example.com', order: 1 } - ], - fields: [ - { type: 'signature', page: 1, x: 100, y: 500, width: 200, height: 50, recipient_order: 1 } - ] -) - -puts result.document_id -puts result.preview_url -``` - -### prepare_for_signing_single - -Upload a document and immediately send signature requests. - -```ruby -result = TurboDocx::TurboSign.prepare_for_signing_single( - file_link: 'https://example.com/document.pdf', - document_name: 'Service Agreement', - sender_name: 'Your Company', - sender_email: 'sender@company.com', - recipients: [ - { name: 'Recipient Name', email: 'recipient@example.com', order: 1 } - ], - fields: [ - { type: 'signature', page: 1, x: 100, y: 500, width: 200, height: 50, recipient_order: 1 } - ] -) -``` - -### get_status - -Check the status of a document. - -```ruby -status = TurboDocx::TurboSign.get_status('document-uuid') - -puts status.status # 'pending', 'completed', or 'voided' -puts status.completed_at - -status.recipients.each do |recipient| - puts "#{recipient.name}: #{recipient.status}" - puts "Signed at: #{recipient.signed_at}" -end -``` - -### download - -Download the completed signed document. - -```ruby -pdf_data = TurboDocx::TurboSign.download('document-uuid') - -# Save to file -File.binwrite('signed-contract.pdf', pdf_data) - -# Or upload to S3 -s3_client.put_object( - bucket: 'my-bucket', - key: 'signed-contract.pdf', - body: pdf_data -) -``` - -### void - -Cancel/void a signature request. - -```ruby -TurboDocx::TurboSign.void('document-uuid', reason: 'Contract terms changed') -``` - -### resend - -Resend signature request emails. - -```ruby -# Resend to all pending recipients -TurboDocx::TurboSign.resend('document-uuid') - -# Resend to specific recipients -TurboDocx::TurboSign.resend('document-uuid', recipient_ids: ['recipient-uuid-1', 'recipient-uuid-2']) -``` - ---- - -## Rails Examples - -### Controller - -```ruby -# app/controllers/api/contracts_controller.rb -class Api::ContractsController < ApplicationController - def send_contract - result = TurboDocx::TurboSign.prepare_for_signing_single( - file_link: params[:contract_url], - recipients: [ - { name: params[:recipient_name], email: params[:recipient_email], order: 1 } - ], - fields: [ - { type: 'signature', page: 1, x: 100, y: 650, width: 200, height: 50, recipient_order: 1 } - ] - ) - - render json: { - document_id: result.document_id, - sign_url: result.recipients.first.sign_url - } - rescue TurboDocx::Error => e - render json: { error: e.message }, status: :unprocessable_entity - end - - def status - status = TurboDocx::TurboSign.get_status(params[:id]) - render json: status - rescue TurboDocx::NotFoundError - render json: { error: 'Document not found' }, status: :not_found - end - - def download - pdf_data = TurboDocx::TurboSign.download(params[:id]) - send_data pdf_data, - type: 'application/pdf', - filename: 'signed-document.pdf', - disposition: 'attachment' - end -end -``` - -### Routes - -```ruby -# config/routes.rb -Rails.application.routes.draw do - namespace :api do - post 'contracts/send', to: 'contracts#send_contract' - get 'contracts/:id/status', to: 'contracts#status' - get 'contracts/:id/download', to: 'contracts#download' - end -end -``` - -### Service Object - -```ruby -# app/services/contract_signing_service.rb -class ContractSigningService - def initialize(document_url:, recipient_name:, recipient_email:) - @document_url = document_url - @recipient_name = recipient_name - @recipient_email = recipient_email - end - - def call - TurboDocx::TurboSign.prepare_for_signing_single( - file_link: @document_url, - recipients: [ - { name: @recipient_name, email: @recipient_email, order: 1 } - ], - fields: signature_fields - ) - end - - private - - def signature_fields - [ - { type: 'signature', page: 1, x: 100, y: 650, width: 200, height: 50, recipient_order: 1 }, - { type: 'date', page: 1, x: 320, y: 650, width: 100, height: 30, recipient_order: 1 } - ] - end -end - -# Usage -result = ContractSigningService.new( - document_url: 'https://example.com/contract.pdf', - recipient_name: 'John Doe', - recipient_email: 'john@example.com' -).call -``` - ---- - -## Background Jobs - -### Sidekiq - -```ruby -# app/jobs/send_contract_for_signature_job.rb -class SendContractForSignatureJob - include Sidekiq::Job - - def perform(contract_id, recipient_email, recipient_name) - contract = Contract.find(contract_id) - - result = TurboDocx::TurboSign.prepare_for_signing_single( - file_link: contract.document_url, - document_name: contract.name, - recipients: [ - { name: recipient_name, email: recipient_email, order: 1 } - ], - fields: [ - { type: 'signature', page: 1, x: 100, y: 650, width: 200, height: 50, recipient_order: 1 } - ] - ) - - contract.update!( - turbosign_document_id: result.document_id, - sign_url: result.recipients.first.sign_url, - status: 'pending_signature' - ) - rescue TurboDocx::Error => e - contract.update!(status: 'signature_failed', error_message: e.message) - raise - end -end - -# Usage -SendContractForSignatureJob.perform_async(contract.id, 'john@example.com', 'John Doe') -``` - -### ActiveJob - -```ruby -# app/jobs/check_signature_status_job.rb -class CheckSignatureStatusJob < ApplicationJob - queue_as :default - - def perform(contract_id) - contract = Contract.find(contract_id) - return unless contract.turbosign_document_id.present? - - status = TurboDocx::TurboSign.get_status(contract.turbosign_document_id) - - case status.status - when 'completed' - contract.update!(status: 'signed', signed_at: status.completed_at) - ContractSignedMailer.notify(contract).deliver_later - when 'voided' - contract.update!(status: 'voided') - else - # Recheck later - CheckSignatureStatusJob.set(wait: 1.hour).perform_later(contract_id) - end - end -end -``` - ---- - -## Error Handling - -```ruby -begin - result = TurboDocx::TurboSign.prepare_for_signing_single(...) -rescue TurboDocx::UnauthorizedError - puts 'Invalid API key' -rescue TurboDocx::InvalidDocumentError => e - puts "Could not process document: #{e.message}" -rescue TurboDocx::RateLimitedError => e - puts "Rate limited, retry after: #{e.retry_after} seconds" -rescue TurboDocx::NotFoundError - puts 'Document not found' -rescue TurboDocx::Error => e - puts "Error #{e.code}: #{e.message}" -end -``` - ---- - -## Field Types - -```ruby -# Available field types -'signature' # Electronic signature drawing/typing -'initials' # Initials field -'text' # Free-form text input -'date' # Date stamp (auto-filled on signing) -'checkbox' # Checkbox for agreements -'full_name' # Signer's full name (auto-filled) -'email' # Signer's email address (auto-filled) -'title' # Job title field -'company' # Company name field -``` - ---- - -## Webhook Signature Verification - -Verify that webhooks are genuinely from TurboDocx: - -```ruby -# app/controllers/webhooks_controller.rb -class WebhooksController < ApplicationController - skip_before_action :verify_authenticity_token - - def turbodocx - signature = request.headers['X-TurboDocx-Signature'] - timestamp = request.headers['X-TurboDocx-Timestamp'] - body = request.raw_post - - is_valid = TurboDocx::WebhookVerifier.verify( - signature: signature, - timestamp: timestamp, - body: body, - secret: ENV['TURBODOCX_WEBHOOK_SECRET'] - ) - - unless is_valid - return render json: { error: 'Invalid signature' }, status: :unauthorized - end - - event = JSON.parse(body) - - case event['event'] - when 'signature.document.completed' - handle_document_completed(event['data']) - when 'signature.document.voided' - handle_document_voided(event['data']) - end - - render json: { received: true } - end - - private - - def handle_document_completed(data) - contract = Contract.find_by(turbosign_document_id: data['document_id']) - return unless contract - - contract.update!(status: 'signed', signed_at: Time.parse(data['completed_at'])) - DownloadSignedDocumentJob.perform_later(contract.id) - end - - def handle_document_voided(data) - contract = Contract.find_by(turbosign_document_id: data['document_id']) - return unless contract - - contract.update!(status: 'voided', void_reason: data['void_reason']) - end -end -``` - -### Routes - -```ruby -# config/routes.rb -Rails.application.routes.draw do - post 'webhooks/turbodocx', to: 'webhooks#turbodocx' -end -``` - ---- - -## Resources - -- [GitHub Repository](https://github.com/TurboDocx/SDK/tree/main/packages/ruby-sdk) -- [RubyGems Package](https://rubygems.org/gems/turbodocx-sdk) -- [API Reference](/docs/TurboSign/API-Signatures) -- [Webhook Configuration](/docs/TurboSign/Webhooks) diff --git a/docs/TurboSign/API Signatures.md b/docs/TurboSign/API Signatures.md index bc8594c..454eb63 100644 --- a/docs/TurboSign/API Signatures.md +++ b/docs/TurboSign/API Signatures.md @@ -67,16 +67,7 @@ TurboSign offers two single-step endpoints to fit different workflows: - **Webhook Integration**: Receive notifications when signing is complete :::tip Prefer using an SDK? -We offer official SDKs that handle authentication, error handling, and type safety for you: - -| Language | Package | Quick Install | -|:---------|:--------|:--------------| -| **JavaScript/TypeScript** | [@turbodocx/sdk](/docs/SDKs/javascript) | `npm install @turbodocx/sdk` | -| **Python** | [turbodocx-sdk](/docs/SDKs/python) | `pip install turbodocx-sdk` | -| **Go** | [turbodocx-sdk](/docs/SDKs/go) | `go get github.com/turbodocx/sdk` | -| **C# / .NET** | [TurboDocx.Sdk](/docs/SDKs/dotnet) | `dotnet add package TurboDocx.Sdk` | -| **Java** | [com.turbodocx:sdk](/docs/SDKs/java) | [Maven Central](https://search.maven.org/artifact/com.turbodocx/sdk) | -| **Ruby** | [turbodocx-sdk](/docs/SDKs/ruby) | `gem install turbodocx-sdk` | +We offer official SDKs that handle authentication, error handling, and type safety for you. [View all SDKs →](/docs/SDKs) ::: @@ -1019,7 +1010,7 @@ Now that you've integrated the single-step signing flow, the next step is settin ### Related Documentation -- [TurboSign Setup Guide](/docs/TurboSign/Setting-up-TurboSign) +- [TurboSign Setup Guide](/docs/TurboSign/Setting%20up%20TurboSign) - [Webhook Configuration](/docs/TurboSign/Webhooks) - [API Authentication](/docs/API/turbodocx-api-documentation) - [Integration Examples](/docs/Integrations) diff --git a/docs/TurboSign/Webhooks.md b/docs/TurboSign/Webhooks.md index 27a58cd..f1101ea 100644 --- a/docs/TurboSign/Webhooks.md +++ b/docs/TurboSign/Webhooks.md @@ -533,6 +533,6 @@ If you encounter issues not covered here: ## Next Steps -- [Learn about TurboSign](/docs/TurboSign/Setting-up-TurboSign) +- [Learn about TurboSign](/docs/TurboSign/Setting%20up%20TurboSign) - [Explore API Documentation](/docs/API/turbodocx-api-documentation) - [View Integration Guides](/docs/Integrations) diff --git a/sidebars.js b/sidebars.js index fec8b30..b8de214 100644 --- a/sidebars.js +++ b/sidebars.js @@ -41,21 +41,11 @@ const sidebars = { id: 'SDKs/go', label: 'Go', }, - { - type: 'doc', - id: 'SDKs/dotnet', - label: '.NET / C#', - }, { type: 'doc', id: 'SDKs/java', label: 'Java', }, - { - type: 'doc', - id: 'SDKs/ruby', - label: 'Ruby', - }, ], }, { From b6fcd1b6dbfb24ae2fe8266650c1806d9c43cede Mon Sep 17 00:00:00 2001 From: Amit Sharma Date: Wed, 10 Dec 2025 16:33:50 +0000 Subject: [PATCH 3/6] feat: added go and java docs for SDKs --- docs/SDKs/go.md | 655 +++++++++++++++++------------------ docs/SDKs/index.md | 45 +-- docs/SDKs/java.md | 743 ++++++++++++++++++++-------------------- docs/SDKs/javascript.md | 77 +++-- docs/SDKs/python.md | 46 ++- 5 files changed, 767 insertions(+), 799 deletions(-) diff --git a/docs/SDKs/go.md b/docs/SDKs/go.md index fd46ee9..edda646 100644 --- a/docs/SDKs/go.md +++ b/docs/SDKs/go.md @@ -14,19 +14,15 @@ keywords: import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -import ScriptLoader from '@site/src/components/ScriptLoader'; # Go SDK -The official TurboDocx SDK for Go applications. Idiomatic Go with full context support. - -[![Go Reference](https://pkg.go.dev/badge/github.com/turbodocx/sdk.svg)](https://pkg.go.dev/github.com/turbodocx/sdk) -[![GitHub](https://img.shields.io/github/stars/turbodocx/sdk?style=social)](https://github.com/TurboDocx/SDK) +The official TurboDocx SDK for Go applications. Build document generation and digital signature workflows with idiomatic Go patterns, context support, and comprehensive error handling. Available as `github.com/TurboDocx/SDK/packages/go-sdk`. ## Installation ```bash -go get github.com/turbodocx/sdk +go get github.com/TurboDocx/SDK/packages/go-sdk ``` ## Requirements @@ -43,23 +39,25 @@ package main import ( "os" - "github.com/turbodocx/sdk" + turbodocx "github.com/TurboDocx/SDK/packages/go-sdk" ) func main() { // Create a new client - client := sdk.NewTurboSign( + client, err := turbodocx.NewClient( os.Getenv("TURBODOCX_API_KEY"), os.Getenv("TURBODOCX_ORG_ID"), ) + if err != nil { + log.Fatal(err) + } - // Or with options - client := sdk.NewTurboSign( - os.Getenv("TURBODOCX_API_KEY"), - os.Getenv("TURBODOCX_ORG_ID"), - sdk.WithBaseURL("https://api.turbodocx.com"), - sdk.WithTimeout(30 * time.Second), - ) + // Or with custom configuration + client, err := turbodocx.NewClientWithConfig(turbodocx.ClientConfig{ + APIKey: os.Getenv("TURBODOCX_API_KEY"), + OrgID: os.Getenv("TURBODOCX_ORG_ID"), + BaseURL: "https://api.turbodocx.com", // Optional custom base URL + }) } ``` @@ -67,6 +65,7 @@ func main() { ```bash export TURBODOCX_API_KEY=your_api_key_here +export TURBODOCX_ORG_ID=your_org_id_here ``` --- @@ -80,145 +79,254 @@ package main import ( "context" + "encoding/json" "fmt" "log" "os" - "github.com/turbodocx/sdk" + turbodocx "github.com/TurboDocx/SDK/packages/go-sdk" ) func main() { - client := sdk.NewTurboSign( + client, err := turbodocx.NewClient( os.Getenv("TURBODOCX_API_KEY"), os.Getenv("TURBODOCX_ORG_ID"), ) + if err != nil { + log.Fatal(err) + } ctx := context.Background() - result, err := client.PrepareForSigningSingle(ctx, &sdk.SigningRequest{ + result, err := client.TurboSign.PrepareForSigningSingle(ctx, &turbodocx.PrepareForSigningRequest{ FileLink: "https://example.com/contract.pdf", DocumentName: "Service Agreement", SenderName: "Acme Corp", SenderEmail: "contracts@acme.com", - Recipients: []sdk.Recipient{ + Recipients: []turbodocx.Recipient{ {Name: "Alice Smith", Email: "alice@example.com", SigningOrder: 1}, {Name: "Bob Johnson", Email: "bob@example.com", SigningOrder: 2}, }, - Fields: []sdk.Field{ + Fields: []turbodocx.Field{ // Alice's signature - {Type: sdk.FieldTypeSignature, Page: 1, X: 100, Y: 650, Width: 200, Height: 50, RecipientEmail: "alice@example.com"}, - {Type: sdk.FieldTypeDate, Page: 1, X: 320, Y: 650, Width: 100, Height: 30, RecipientEmail: "alice@example.com"}, + {Type: "signature", Page: 1, X: 100, Y: 650, Width: 200, Height: 50, RecipientEmail: "alice@example.com"}, + {Type: "date", Page: 1, X: 320, Y: 650, Width: 100, Height: 30, RecipientEmail: "alice@example.com"}, // Bob's signature - {Type: sdk.FieldTypeSignature, Page: 1, X: 100, Y: 720, Width: 200, Height: 50, RecipientEmail: "bob@example.com"}, - {Type: sdk.FieldTypeDate, Page: 1, X: 320, Y: 720, Width: 100, Height: 30, RecipientEmail: "bob@example.com"}, + {Type: "signature", Page: 1, X: 100, Y: 720, Width: 200, Height: 50, RecipientEmail: "bob@example.com"}, + {Type: "date", Page: 1, X: 320, Y: 720, Width: 100, Height: 30, RecipientEmail: "bob@example.com"}, }, }) if err != nil { log.Fatal(err) } - fmt.Printf("Document ID: %s\n", result.DocumentID) - for _, recipient := range result.Recipients { - fmt.Printf("%s: %s\n", recipient.Name, recipient.SignURL) - } + b, _ := json.MarshalIndent(result, "", " "); fmt.Println("Result:", string(b)) } ``` ### Using Template-Based Fields ```go -result, err := client.PrepareForSigningSingle(ctx, &sdk.SigningRequest{ +result, err := client.TurboSign.PrepareForSigningSingle(ctx, &turbodocx.PrepareForSigningRequest{ FileLink: "https://example.com/contract-with-placeholders.pdf", - Recipients: []sdk.Recipient{ + Recipients: []turbodocx.Recipient{ {Name: "Alice Smith", Email: "alice@example.com", SigningOrder: 1}, }, - Fields: []sdk.Field{ - {Type: sdk.FieldTypeSignature, Anchor: "{SIGNATURE_ALICE}", Width: 200, Height: 50, RecipientEmail: "alice@example.com"}, - {Type: sdk.FieldTypeDate, Anchor: "{DATE_ALICE}", Width: 100, Height: 30, RecipientEmail: "alice@example.com"}, + Fields: []turbodocx.Field{ + { + Type: "signature", + RecipientEmail: "alice@example.com", + Template: &turbodocx.TemplateAnchor{ + Anchor: "{SIGNATURE_ALICE}", + Placement: "replace", + Size: &turbodocx.Size{Width: 200, Height: 50}, + }, + }, + { + Type: "date", + RecipientEmail: "alice@example.com", + Template: &turbodocx.TemplateAnchor{ + Anchor: "{DATE_ALICE}", + Placement: "replace", + Size: &turbodocx.Size{Width: 100, Height: 30}, + }, + }, }, }) ``` +:::info Template Anchors Required +**Important:** The document file must contain the anchor text (e.g., `{SIGNATURE_ALICE}`, `{DATE_ALICE}`) that you reference in your fields. If the anchors don't exist in the document, the API will return an error. +::: + --- -## API Reference +## File Input Methods + +The SDK supports multiple ways to provide your document: + +### 1. File Upload ([]byte) + +Upload a document directly from file bytes: + +```go +pdfBytes, err := os.ReadFile("/path/to/document.pdf") +if err != nil { + log.Fatal(err) +} + +result, err := client.TurboSign.PrepareForSigningSingle(ctx, &turbodocx.PrepareForSigningRequest{ + File: pdfBytes, + Recipients: []turbodocx.Recipient{ + {Name: "John Doe", Email: "john@example.com", SigningOrder: 1}, + }, + Fields: []turbodocx.Field{ + {Type: "signature", Page: 1, X: 100, Y: 500, Width: 200, Height: 50, RecipientEmail: "john@example.com"}, + }, +}) +``` + +### 2. File URL + +Provide a publicly accessible URL to your document: + +```go +result, err := client.TurboSign.PrepareForSigningSingle(ctx, &turbodocx.PrepareForSigningRequest{ + FileLink: "https://example.com/documents/contract.pdf", + Recipients: []turbodocx.Recipient{ + {Name: "John Doe", Email: "john@example.com", SigningOrder: 1}, + }, + Fields: []turbodocx.Field{ + {Type: "signature", Page: 1, X: 100, Y: 500, Width: 200, Height: 50, RecipientEmail: "john@example.com"}, + }, +}) +``` + +:::tip When to use FileLink +Use `FileLink` when your documents are already hosted on cloud storage (S3, Google Cloud Storage, etc.). This is more efficient than downloading and re-uploading files. +::: + +### 3. TurboDocx Deliverable ID + +Use a document generated by TurboDocx document generation: -### NewTurboSign() +```go +result, err := client.TurboSign.PrepareForSigningSingle(ctx, &turbodocx.PrepareForSigningRequest{ + DeliverableID: "deliverable-uuid-from-turbodocx", + Recipients: []turbodocx.Recipient{ + {Name: "John Doe", Email: "john@example.com", SigningOrder: 1}, + }, + Fields: []turbodocx.Field{ + {Type: "signature", Page: 1, X: 100, Y: 500, Width: 200, Height: 50, RecipientEmail: "john@example.com"}, + }, +}) +``` -Create a new TurboSign client. +:::info Integration with TurboDocx +`DeliverableID` references documents generated using TurboDocx's document generation API. This creates a seamless workflow: generate → sign. +::: + +### 4. TurboDocx Template ID + +Use a pre-configured TurboDocx template: ```go -func NewTurboSign(apiKey string, opts ...Option) *TurboSign +result, err := client.TurboSign.PrepareForSigningSingle(ctx, &turbodocx.PrepareForSigningRequest{ + TemplateID: "template-uuid-from-turbodocx", + Recipients: []turbodocx.Recipient{ + {Name: "John Doe", Email: "john@example.com", SigningOrder: 1}, + }, + Fields: []turbodocx.Field{ + {Type: "signature", Page: 1, X: 100, Y: 500, Width: 200, Height: 50, RecipientEmail: "john@example.com"}, + }, +}) +``` + +:::info Integration with TurboDocx +`TemplateID` references pre-configured TurboSign templates created in the TurboDocx dashboard. These templates come with built-in anchors and field positioning, making it easy to reuse signature workflows across multiple documents. +::: + +--- + +## API Reference -// Available options -sdk.WithBaseURL(url string) -sdk.WithTimeout(timeout time.Duration) -sdk.WithHTTPClient(client *http.Client) +### Configure + +Create a new TurboDocx client. + +```go +// Simple initialization +client, err := turbodocx.NewClient(apiKey, orgID string) + +// With custom configuration +client, err := turbodocx.NewClientWithConfig(turbodocx.ClientConfig{ + APIKey: "your-api-key", + OrgID: "your-org-id", + BaseURL: "https://api.turbodocx.com", // Optional +}) ``` -### PrepareForReview() +:::warning API Credentials Required +Both `APIKey` and `OrgID` parameters are **required** for all API requests. To get your credentials, follow the **[Get Your Credentials](/docs/SDKs#1-get-your-credentials)** steps from the SDKs main page. +::: + +### Prepare for review Upload a document for preview without sending emails. ```go -result, err := client.PrepareForReview(ctx, &sdk.ReviewRequest{ +result, err := client.TurboSign.PrepareForReview(ctx, &turbodocx.PrepareForReviewRequest{ FileLink: "https://example.com/document.pdf", DocumentName: "Contract Draft", - Recipients: []sdk.Recipient{ + Recipients: []turbodocx.Recipient{ {Name: "John Doe", Email: "john@example.com", SigningOrder: 1}, }, - Fields: []sdk.Field{ - {Type: sdk.FieldTypeSignature, Page: 1, X: 100, Y: 500, Width: 200, Height: 50, RecipientEmail: "john@example.com"}, + Fields: []turbodocx.Field{ + {Type: "signature", Page: 1, X: 100, Y: 500, Width: 200, Height: 50, RecipientEmail: "john@example.com"}, }, }) -fmt.Println(result.DocumentID) -fmt.Println(result.PreviewURL) +b, _ := json.MarshalIndent(result, "", " "); fmt.Println("Result:", string(b)) ``` -### PrepareForSigningSingle() +### Prepare for signing Upload a document and immediately send signature requests. ```go -result, err := client.PrepareForSigningSingle(ctx, &sdk.SigningRequest{ +result, err := client.TurboSign.PrepareForSigningSingle(ctx, &turbodocx.PrepareForSigningRequest{ FileLink: "https://example.com/document.pdf", DocumentName: "Service Agreement", SenderName: "Your Company", SenderEmail: "sender@company.com", - Recipients: []sdk.Recipient{ + Recipients: []turbodocx.Recipient{ {Name: "Recipient Name", Email: "recipient@example.com", SigningOrder: 1}, }, - Fields: []sdk.Field{ - {Type: sdk.FieldTypeSignature, Page: 1, X: 100, Y: 500, Width: 200, Height: 50, RecipientEmail: "recipient@example.com"}, + Fields: []turbodocx.Field{ + {Type: "signature", Page: 1, X: 100, Y: 500, Width: 200, Height: 50, RecipientEmail: "recipient@example.com"}, }, }) ``` -### GetStatus() +### Get status Check the status of a document. ```go -status, err := client.GetStatus(ctx, "document-uuid") +status, err := client.TurboSign.GetStatus(ctx, "document-uuid") if err != nil { log.Fatal(err) } -fmt.Println(status.Status) // "pending", "completed", or "voided" -fmt.Println(status.CompletedAt) - -for _, recipient := range status.Recipients { - fmt.Printf("%s: %s\n", recipient.Name, recipient.Status) -} +b, _ := json.MarshalIndent(status, "", " "); fmt.Println("Result:", string(b)) ``` -### Download() +### Download document Download the completed signed document. ```go -pdfData, err := client.Download(ctx, "document-uuid") +pdfData, err := client.TurboSign.Download(ctx, "document-uuid") if err != nil { log.Fatal(err) } @@ -228,203 +336,100 @@ err = os.WriteFile("signed-contract.pdf", pdfData, 0644) if err != nil { log.Fatal(err) } - -// Or upload to S3 -_, err = s3Client.PutObject(ctx, &s3.PutObjectInput{ - Bucket: aws.String("my-bucket"), - Key: aws.String("signed-contract.pdf"), - Body: bytes.NewReader(pdfData), -}) ``` -### Void() +### Get audit trail -Cancel/void a signature request. +Retrieve the audit trail for a document. ```go -err := client.Void(ctx, "document-uuid", "Contract terms changed") +auditTrail, err := client.TurboSign.GetAuditTrail(ctx, "document-uuid") +if err != nil { + log.Fatal(err) +} + +b, _ := json.MarshalIndent(auditTrail, "", " "); fmt.Println("Result:", string(b)) ``` -### Resend() +### Void -Resend signature request emails. +Cancel/void a signature request. ```go -// Resend to all pending recipients -err := client.Resend(ctx, "document-uuid", nil) - -// Resend to specific recipients -err := client.Resend(ctx, "document-uuid", []string{"recipient-uuid-1", "recipient-uuid-2"}) +result, err := client.TurboSign.VoidDocument(ctx, "document-uuid", "Contract terms changed") ``` ---- - -## HTTP Handler Examples +### Resend -### Basic HTTP Handler +Resend signature request emails. ```go -package main - -import ( - "encoding/json" - "net/http" - "os" - - "github.com/turbodocx/sdk" -) - -var client = sdk.NewTurboSign( - os.Getenv("TURBODOCX_API_KEY"), - os.Getenv("TURBODOCX_ORG_ID"), -) - -type SendContractRequest struct { - RecipientName string `json:"recipient_name"` - RecipientEmail string `json:"recipient_email"` - ContractURL string `json:"contract_url"` -} - -type SendContractResponse struct { - DocumentID string `json:"document_id"` - SignURL string `json:"sign_url"` -} - -func sendContractHandler(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodPost { - http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) - return - } - - var req SendContractRequest - if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - result, err := client.PrepareForSigningSingle(r.Context(), &sdk.SigningRequest{ - FileLink: req.ContractURL, - Recipients: []sdk.Recipient{ - {Name: req.RecipientName, Email: req.RecipientEmail, SigningOrder: 1}, - }, - Fields: []sdk.Field{ - {Type: sdk.FieldTypeSignature, Page: 1, X: 100, Y: 650, Width: 200, Height: 50, RecipientEmail: req.RecipientEmail}, - }, - }) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - json.NewEncoder(w).Encode(SendContractResponse{ - DocumentID: result.DocumentID, - SignURL: result.Recipients[0].SignURL, - }) -} - -func main() { - http.HandleFunc("/api/send-contract", sendContractHandler) - http.ListenAndServe(":8080", nil) -} +// Resend to specific recipients +result, err := client.TurboSign.ResendEmail(ctx, "document-uuid", []string{"recipient-uuid-1", "recipient-uuid-2"}) ``` -### Gin Framework - -```go -package main - -import ( - "net/http" - "os" - - "github.com/gin-gonic/gin" - "github.com/turbodocx/sdk" -) - -var client = sdk.NewTurboSign( - os.Getenv("TURBODOCX_API_KEY"), - os.Getenv("TURBODOCX_ORG_ID"), -) - -func main() { - r := gin.Default() - - r.POST("/api/send-contract", func(c *gin.Context) { - var req struct { - RecipientName string `json:"recipient_name" binding:"required"` - RecipientEmail string `json:"recipient_email" binding:"required"` - ContractURL string `json:"contract_url" binding:"required"` - } - - if err := c.ShouldBindJSON(&req); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - return - } +--- - result, err := client.PrepareForSigningSingle(c.Request.Context(), &sdk.SigningRequest{ - FileLink: req.ContractURL, - Recipients: []sdk.Recipient{ - {Name: req.RecipientName, Email: req.RecipientEmail, SigningOrder: 1}, - }, - Fields: []sdk.Field{ - {Type: sdk.FieldTypeSignature, Page: 1, X: 100, Y: 650, Width: 200, Height: 50, RecipientEmail: req.RecipientEmail}, - }, - }) - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - return - } +## Error Handling - c.JSON(http.StatusOK, gin.H{ - "document_id": result.DocumentID, - "sign_url": result.Recipients[0].SignURL, - }) - }) +The SDK provides typed errors for different error scenarios: - r.GET("/api/document/:id/status", func(c *gin.Context) { - status, err := client.GetStatus(c.Request.Context(), c.Param("id")) - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - return - } +### Error Types - c.JSON(http.StatusOK, status) - }) +| Error Type | Status Code | Description | +| --------------------- | ----------- | ---------------------------------- | +| `TurboDocxError` | varies | Base error type for all API errors | +| `AuthenticationError` | 401 | Invalid or missing API key | +| `ValidationError` | 400 | Invalid request parameters | +| `NotFoundError` | 404 | Resource not found | +| `RateLimitError` | 429 | Too many requests | +| `NetworkError` | - | Network connectivity issues | - r.Run(":8080") -} -``` +### Error Properties ---- +| Property | Type | Description | +| ------------ | -------- | ---------------------------- | +| `Message` | `string` | Human-readable error message | +| `StatusCode` | `int` | HTTP status code | +| `Code` | `string` | Error code (if available) | -## Error Handling +### Example ```go import ( "errors" - "github.com/turbodocx/sdk" + turbodocx "github.com/TurboDocx/SDK/packages/go-sdk" ) -result, err := client.PrepareForSigningSingle(ctx, request) +result, err := client.TurboSign.PrepareForSigningSingle(ctx, request) if err != nil { - var turboErr *sdk.TurboDocxError - if errors.As(err, &turboErr) { - switch turboErr.Code { - case sdk.ErrCodeUnauthorized: - log.Println("Invalid API key") - case sdk.ErrCodeInvalidDocument: - log.Printf("Could not process document: %s", turboErr.Message) - case sdk.ErrCodeRateLimited: - log.Printf("Rate limited, retry after: %d seconds", turboErr.RetryAfter) - case sdk.ErrCodeNotFound: - log.Println("Document not found") - default: - log.Printf("Error %s: %s", turboErr.Code, turboErr.Message) + // Check for specific error types + var authErr *turbodocx.AuthenticationError + var validationErr *turbodocx.ValidationError + var notFoundErr *turbodocx.NotFoundError + var rateLimitErr *turbodocx.RateLimitError + var networkErr *turbodocx.NetworkError + + switch { + case errors.As(err, &authErr): + log.Printf("Authentication failed: %s", authErr.Message) + case errors.As(err, &validationErr): + log.Printf("Validation error: %s", validationErr.Message) + case errors.As(err, ¬FoundErr): + log.Printf("Not found: %s", notFoundErr.Message) + case errors.As(err, &rateLimitErr): + log.Printf("Rate limited: %s", rateLimitErr.Message) + case errors.As(err, &networkErr): + log.Printf("Network error: %s", networkErr.Message) + default: + // Base TurboDocxError or unexpected error + var turboErr *turbodocx.TurboDocxError + if errors.As(err, &turboErr) { + log.Printf("API error [%d]: %s", turboErr.StatusCode, turboErr.Message) + } else { + log.Fatal(err) } - } else { - // Network or unexpected error - log.Fatal(err) } } ``` @@ -433,126 +438,104 @@ if err != nil { ## Types -### Field Types - -```go -const ( - FieldTypeSignature sdk.FieldType = "signature" - FieldTypeInitials sdk.FieldType = "initials" - FieldTypeText sdk.FieldType = "text" - FieldTypeDate sdk.FieldType = "date" - FieldTypeCheckbox sdk.FieldType = "checkbox" - FieldTypeFullName sdk.FieldType = "full_name" - FieldTypeEmail sdk.FieldType = "email" - FieldTypeTitle sdk.FieldType = "title" - FieldTypeCompany sdk.FieldType = "company" -) -``` - -### Structs - -```go -type Recipient struct { - Name string `json:"name"` - Email string `json:"email"` - SigningOrder int `json:"signingOrder"` - // Response fields - ID string `json:"id,omitempty"` - Status string `json:"status,omitempty"` - SignURL string `json:"sign_url,omitempty"` -} - -type Field struct { - Type FieldType `json:"type"` - Page int `json:"page,omitempty"` - X int `json:"x,omitempty"` - Y int `json:"y,omitempty"` - Width int `json:"width"` - Height int `json:"height"` - RecipientEmail string `json:"recipientEmail"` - Anchor string `json:"anchor,omitempty"` // For template-based fields -} - -type SigningRequest struct { - FileLink string `json:"file_link,omitempty"` - File []byte `json:"-"` // For direct upload - DocumentName string `json:"document_name,omitempty"` - SenderName string `json:"sender_name,omitempty"` - SenderEmail string `json:"sender_email,omitempty"` - Recipients []Recipient `json:"recipients"` - Fields []Field `json:"fields"` -} - -type SigningResult struct { - DocumentID string `json:"document_id"` - Recipients []Recipient `json:"recipients"` -} - -type DocumentStatus struct { - Status string `json:"status"` // "pending", "completed", "voided" - CompletedAt *time.Time `json:"completed_at,omitempty"` - Recipients []Recipient `json:"recipients"` -} -``` +### Signature Field Types + +The `Type` field accepts the following string values: + +| Type | Description | +| -------------- | ---------------- | +| `"signature"` | Signature field | +| `"initials"` | Initials field | +| `"text"` | Text input field | +| `"date"` | Date field | +| `"checkbox"` | Checkbox field | +| `"full_name"` | Full name field | +| `"first_name"` | First name field | +| `"last_name"` | Last name field | +| `"email"` | Email field | +| `"title"` | Title field | +| `"company"` | Company field | + +### Recipient + +| Property | Type | Required | Description | +| -------------- | -------- | -------- | ------------------------------------------------- | +| `Name` | `string` | Yes | Recipient's full name | +| `Email` | `string` | Yes | Recipient's email address | +| `SigningOrder` | `int` | Yes | Order in which recipient should sign (1, 2, 3...) | + +### Field + +| Property | Type | Required | Description | +| ----------------- | ----------------- | -------- | ------------------------------------------- | +| `Type` | `string` | Yes | Field type (see table above) | +| `RecipientEmail` | `string` | Yes | Email of the recipient who fills this field | +| `Page` | `int` | No\* | Page number (1-indexed) | +| `X` | `int` | No\* | X coordinate in pixels | +| `Y` | `int` | No\* | Y coordinate in pixels | +| `Width` | `int` | No* | Field width in pixels | +| `Height` | `int` | No* | Field height in pixels | +| `DefaultValue` | `string` | No | Pre-filled value | +| `IsMultiline` | `bool` | No | Enable multiline for text fields | +| `IsReadonly` | `bool` | No | Make field read-only | +| `Required` | `bool` | No | Make field required | +| `BackgroundColor` | `string` | No | Background color | +| `Template` | `*TemplateAnchor` | No | Template anchor configuration | + +\*Required when not using template anchors + +#### Template Configuration + +When using `Template` instead of coordinates: + +| Property | Type | Required | Description | +| --------------- | -------- | -------- | ------------------------------------------------------------------------------------- | +| `Anchor` | `string` | Yes | Text to find in document (e.g., `"{SIGNATURE}"`) | +| `Placement` | `string` | Yes | Position relative to anchor: `"replace"`, `"before"`, `"after"`, `"above"`, `"below"` | +| `Size` | `*Size` | Yes | Size with `Width` and `Height` | +| `Offset` | `*Point` | No | Offset with `X` and `Y` | +| `CaseSensitive` | `bool` | No | Case-sensitive anchor search | +| `UseRegex` | `bool` | No | Use regex for anchor search | + +### Request Parameters + +Both `PrepareForReviewRequest` and `PrepareForSigningRequest` accept: + +| Property | Type | Required | Description | +| --------------------- | ------------- | ----------- | ------------------------ | +| `File` | `[]byte` | Conditional | File content as bytes | +| `FileLink` | `string` | Conditional | URL to document | +| `DeliverableID` | `string` | Conditional | TurboDocx deliverable ID | +| `TemplateID` | `string` | Conditional | TurboDocx template ID | +| `Recipients` | `[]Recipient` | Yes | List of recipients | +| `Fields` | `[]Field` | Yes | List of fields | +| `DocumentName` | `string` | No | Document display name | +| `DocumentDescription` | `string` | No | Document description | +| `SenderName` | `string` | No | Sender's name | +| `SenderEmail` | `string` | No | Sender's email | +| `CCEmails` | `[]string` | No | CC email addresses | + +:::info File Source (Conditional) +Exactly one file source is required: `File`, `FileLink`, `DeliverableID`, or `TemplateID`. +::: --- -## Webhook Signature Verification - -Verify that webhooks are genuinely from TurboDocx: - -```go -package main - -import ( - "encoding/json" - "io" - "net/http" - "os" - - "github.com/turbodocx/sdk" -) - -func webhookHandler(w http.ResponseWriter, r *http.Request) { - body, err := io.ReadAll(r.Body) - if err != nil { - http.Error(w, "Failed to read body", http.StatusBadRequest) - return - } +## Additional Documentation - signature := r.Header.Get("X-TurboDocx-Signature") - timestamp := r.Header.Get("X-TurboDocx-Timestamp") - secret := os.Getenv("TURBODOCX_WEBHOOK_SECRET") +For detailed information about advanced configuration and API concepts, see: - isValid := sdk.VerifyWebhookSignature(signature, timestamp, body, secret) - if !isValid { - http.Error(w, "Invalid signature", http.StatusUnauthorized) - return - } +### Core API References - var event sdk.WebhookEvent - if err := json.Unmarshal(body, &event); err != nil { - http.Error(w, "Failed to parse event", http.StatusBadRequest) - return - } - - switch event.Event { - case "signature.document.completed": - log.Printf("Document completed: %s", event.Data.DocumentID) - case "signature.document.voided": - log.Printf("Document voided: %s", event.Data.DocumentID) - } - - w.WriteHeader(http.StatusOK) - json.NewEncoder(w).Encode(map[string]bool{"received": true}) -} -``` +- **[Request Body Reference](/docs/TurboSign/API%20Signatures#request-body-multipartform-data)** - Complete request body parameters, file sources, and multipart/form-data structure +- **[Recipients Reference](/docs/TurboSign/API%20Signatures#recipients-reference)** - Recipient properties, signing order, metadata, and configuration options +- **[Field Types Reference](/docs/TurboSign/API%20Signatures#field-types-reference)** - All available field types (signature, date, text, checkbox, etc.) with properties and behaviors +- **[Field Positioning Methods](/docs/TurboSign/API%20Signatures#field-positioning-methods)** - Template-based vs coordinate-based positioning, anchor configuration, and best practices --- ## Resources - [GitHub Repository](https://github.com/TurboDocx/SDK/tree/main/packages/go-sdk) -- [Go Package Documentation](https://pkg.go.dev/github.com/turbodocx/sdk) - [API Reference](/docs/TurboSign/API-Signatures) - [Webhook Configuration](/docs/TurboSign/Webhooks) diff --git a/docs/SDKs/index.md b/docs/SDKs/index.md index 4e876ec..045977a 100644 --- a/docs/SDKs/index.md +++ b/docs/SDKs/index.md @@ -329,46 +329,19 @@ Generate documents from templates with dynamic data. ## Field Positioning -TurboSign supports two methods for placing signature fields: +TurboSign supports two methods for placing signature fields on your documents: -### Coordinate-Based (Pixel Positioning) +| Method | Best For | +| :------------------- | :----------------------------------------------------------------------- | +| **Coordinate-Based** | PDFs with fixed layouts where you know exact pixel positions | +| **Template-Based** | Documents where content may shift, using text anchors like `{SIGNATURE}` | -Specify exact positions using page coordinates: - -```javascript -{ - type: 'signature', - page: 1, - x: 100, // pixels from left - y: 500, // pixels from top - width: 200, - height: 50, - recipientEmail: 'user@example.com' -} -``` - -### Template-Based (Text Anchors) - -Use text markers in your PDF to position fields automatically: - -```javascript -{ - type: 'signature', - anchor: '{SIGNATURE_1}', // text to find in PDF - width: 200, - height: 50, - recipientEmail: 'user@example.com' -} -``` - -:::tip When to use each method - -- **Coordinate-based**: Best for PDFs with fixed layouts where you know exact positions -- **Template-based**: Best for templates where content may shift, using anchor text like `{SIGNATURE_1}` - ::: +:::info Field Positioning Reference +For detailed information about both positioning methods, including anchor configuration, placement options, and best practices, see the **[Field Positioning Methods](/docs/TurboSign/API%20Signatures#field-positioning-methods)** guide. +::: :::info Complete Field Types Reference -For a comprehensive list of all available field types (signature, initials, text, date, checkbox, full_name, email, title, company) and their detailed usage, see the [Field Types section in the API Signatures guide](/docs/TurboSign/API%20Signatures#field-types-reference) +For a comprehensive list of all available field types (signature, initials, text, date, checkbox, full_name, email, title, company) and their detailed usage, see the [Field Types section in the API Signatures guide](/docs/TurboSign/API%20Signatures#field-types-reference). ::: --- diff --git a/docs/SDKs/java.md b/docs/SDKs/java.md index cce8c22..fa032b6 100644 --- a/docs/SDKs/java.md +++ b/docs/SDKs/java.md @@ -2,27 +2,23 @@ title: Java SDK sidebar_position: 6 sidebar_label: Java -description: Official TurboDocx Java SDK. Builder pattern with Spring Boot integration for document generation and digital signatures. +description: Official TurboDocx Java SDK. Builder pattern API with comprehensive error handling for document generation and digital signatures. keywords: - turbodocx java - turbosign java - maven turbodocx - gradle turbodocx - - spring boot sdk + - java sdk - document api java - esignature java --- import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -import ScriptLoader from '@site/src/components/ScriptLoader'; # Java SDK -The official TurboDocx SDK for Java applications. Builder pattern API with Spring Boot integration. - -[![Maven Central](https://img.shields.io/maven-central/v/com.turbodocx/sdk?logo=java&logoColor=white)](https://search.maven.org/artifact/com.turbodocx/sdk) -[![GitHub](https://img.shields.io/github/stars/turbodocx/sdk?style=social)](https://github.com/TurboDocx/SDK) +The official TurboDocx SDK for Java applications. Build document generation and digital signature workflows with the Builder pattern, comprehensive error handling, and type-safe APIs. Available on Maven Central as `com.turbodocx:sdk`. ## Installation @@ -57,52 +53,45 @@ implementation 'com.turbodocx:sdk:1.0.0' ## Requirements - Java 11+ -- Jackson for JSON serialization (included) +- OkHttp 4.x (included) +- Gson 2.x (included) --- ## Configuration -### Basic Configuration - ```java -import com.turbodocx.sdk.TurboSign; +import com.turbodocx.TurboDocxClient; -// Create a client -TurboSign turboSign = new TurboSign( - System.getenv("TURBODOCX_API_KEY"), - System.getenv("TURBODOCX_ORG_ID") -); - -// Or with builder -TurboSign turboSign = TurboSign.builder() - .apiKey(System.getenv("TURBODOCX_API_KEY")) - .orgId(System.getenv("TURBODOCX_ORG_ID")) - .baseUrl("https://api.turbodocx.com") - .timeout(Duration.ofSeconds(30)) - .build(); +public class Main { + public static void main(String[] args) { + // Create client with Builder pattern + TurboDocxClient client = new TurboDocxClient.Builder() + .apiKey(System.getenv("TURBODOCX_API_KEY")) + .orgId(System.getenv("TURBODOCX_ORG_ID")) + .build(); + + // Or with custom base URL + TurboDocxClient client = new TurboDocxClient.Builder() + .apiKey(System.getenv("TURBODOCX_API_KEY")) + .orgId(System.getenv("TURBODOCX_ORG_ID")) + .baseUrl("https://api.turbodocx.com") + .build(); + } +} ``` -### Spring Boot Configuration +### Environment Variables -```java -// application.yml -turbodocx: - api-key: ${TURBODOCX_API_KEY} - -// TurboDocxConfig.java -@Configuration -public class TurboDocxConfig { - - @Bean - public TurboSign turboSign( - @Value("${turbodocx.api-key}") String apiKey, - @Value("${turbodocx.org-id}") String orgId) { - return new TurboSign(apiKey, orgId); - } -} +```bash +export TURBODOCX_API_KEY=your_api_key_here +export TURBODOCX_ORG_ID=your_org_id_here ``` +:::warning API Credentials Required +Both `apiKey` and `orgId` parameters are **required** for all API requests. To get your credentials, follow the **[Get Your Credentials](/docs/SDKs#1-get-your-credentials)** steps from the SDKs main page. +::: + --- ## Quick Start @@ -110,61 +99,44 @@ public class TurboDocxConfig { ### Send a Document for Signature ```java -import com.turbodocx.sdk.TurboSign; -import com.turbodocx.sdk.models.*; +import com.turbodocx.TurboDocxClient; +import com.turbodocx.models.*; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import java.util.Arrays; public class Main { - public static void main(String[] args) { - TurboSign turboSign = new TurboSign( - System.getenv("TURBODOCX_API_KEY"), - System.getenv("TURBODOCX_ORG_ID") - ); + public static void main(String[] args) throws Exception { + TurboDocxClient client = new TurboDocxClient.Builder() + .apiKey(System.getenv("TURBODOCX_API_KEY")) + .orgId(System.getenv("TURBODOCX_ORG_ID")) + .build(); + + Gson gson = new GsonBuilder().setPrettyPrinting().create(); - SigningResult result = turboSign.prepareForSigningSingle( - SigningRequest.builder() + PrepareForSigningResponse result = client.turboSign().prepareForSigningSingle( + new PrepareForSigningRequest.Builder() .fileLink("https://example.com/contract.pdf") .documentName("Service Agreement") .senderName("Acme Corp") .senderEmail("contracts@acme.com") - .recipient(Recipient.builder() - .name("Alice Smith") - .email("alice@example.com") - .signingOrder(1) - .build()) - .recipient(Recipient.builder() - .name("Bob Johnson") - .email("bob@example.com") - .signingOrder(2) - .build()) - // Alice's signature - .field(Field.builder() - .type(FieldType.SIGNATURE) - .page(1).x(100).y(650).width(200).height(50) - .recipientEmail("alice@example.com") - .build()) - .field(Field.builder() - .type(FieldType.DATE) - .page(1).x(320).y(650).width(100).height(30) - .recipientEmail("alice@example.com") - .build()) - // Bob's signature - .field(Field.builder() - .type(FieldType.SIGNATURE) - .page(1).x(100).y(720).width(200).height(50) - .recipientEmail("bob@example.com") - .build()) - .field(Field.builder() - .type(FieldType.DATE) - .page(1).x(320).y(720).width(100).height(30) - .recipientEmail("bob@example.com") - .build()) + .recipients(Arrays.asList( + new Recipient("Alice Smith", "alice@example.com", 1), + new Recipient("Bob Johnson", "bob@example.com", 2) + )) + .fields(Arrays.asList( + // Alice's signature + new Field("signature", 1, 100, 650, 200, 50, "alice@example.com"), + new Field("date", 1, 320, 650, 100, 30, "alice@example.com"), + // Bob's signature + new Field("signature", 1, 100, 720, 200, 50, "bob@example.com"), + new Field("date", 1, 320, 720, 100, 30, "bob@example.com") + )) .build() ); - System.out.println("Document ID: " + result.getDocumentId()); - for (Recipient recipient : result.getRecipients()) { - System.out.println(recipient.getName() + ": " + recipient.getSignUrl()); - } + System.out.println("Result: " + gson.toJson(result)); } } ``` @@ -172,388 +144,405 @@ public class Main { ### Using Template-Based Fields ```java -SigningResult result = turboSign.prepareForSigningSingle( - SigningRequest.builder() +// Template-based field using anchor text +Field.TemplateAnchor templateAnchor = new Field.TemplateAnchor( + "{SIGNATURE_ALICE}", // anchor text to find + null, // searchText (alternative to anchor) + "replace", // placement: replace/before/after/above/below + new Field.Size(200, 50), // size + null, // offset + false, // caseSensitive + false // useRegex +); + +// Field with template anchor (no page/x/y coordinates needed) +Field templateField = new Field( + "signature", // type + null, // page (null for template-based) + null, // x (null for template-based) + null, // y (null for template-based) + null, // width (null, using template size) + null, // height (null, using template size) + "alice@example.com", // recipientEmail + null, // defaultValue + null, // isMultiline + null, // isReadonly + null, // required + null, // backgroundColor + templateAnchor // template anchor config +); + +PrepareForSigningResponse result = client.turboSign().prepareForSigningSingle( + new PrepareForSigningRequest.Builder() .fileLink("https://example.com/contract-with-placeholders.pdf") - .recipient(Recipient.builder() - .name("Alice Smith") - .email("alice@example.com") - .order(1) - .build()) - .field(Field.builder() - .type(FieldType.SIGNATURE) - .anchor("{SIGNATURE_ALICE}") - .width(200).height(50) - .recipientOrder(1) - .build()) - .field(Field.builder() - .type(FieldType.DATE) - .anchor("{DATE_ALICE}") - .width(100).height(30) - .recipientOrder(1) - .build()) + .recipients(Arrays.asList( + new Recipient("Alice Smith", "alice@example.com", 1) + )) + .fields(Arrays.asList(templateField)) .build() ); ``` +:::info Template Anchors Required +**Important:** The document file must contain the anchor text (e.g., `{SIGNATURE_ALICE}`, `{DATE_ALICE}`) that you reference in your fields. If the anchors don't exist in the document, the API will return an error. +::: + --- -## API Reference +## File Input Methods -### prepareForReview() +The SDK supports multiple ways to provide your document: -Upload a document for preview without sending emails. +### 1. File Upload (byte[]) + +Upload a document directly from file bytes: ```java -ReviewResult result = turboSign.prepareForReview( - ReviewRequest.builder() - .fileLink("https://example.com/document.pdf") - // Or upload directly: - // .file(Files.readAllBytes(Path.of("document.pdf"))) - .documentName("Contract Draft") - .recipient(Recipient.builder() - .name("John Doe") - .email("john@example.com") - .order(1) - .build()) - .field(Field.builder() - .type(FieldType.SIGNATURE) - .page(1).x(100).y(500).width(200).height(50) - .recipientOrder(1) - .build()) +import java.nio.file.Files; +import java.nio.file.Paths; + +byte[] pdfBytes = Files.readAllBytes(Paths.get("/path/to/document.pdf")); + +PrepareForSigningResponse result = client.turboSign().prepareForSigningSingle( + new PrepareForSigningRequest.Builder() + .file(pdfBytes) + .recipients(Arrays.asList( + new Recipient("John Doe", "john@example.com", 1) + )) + .fields(Arrays.asList( + new Field("signature", 1, 100, 500, 200, 50, "john@example.com") + )) .build() ); - -System.out.println(result.getDocumentId()); -System.out.println(result.getPreviewUrl()); ``` -### prepareForSigningSingle() +### 2. File URL -Upload a document and immediately send signature requests. +Provide a publicly accessible URL to your document: ```java -SigningResult result = turboSign.prepareForSigningSingle( - SigningRequest.builder() - .fileLink("https://example.com/document.pdf") - .documentName("Service Agreement") - .senderName("Your Company") - .senderEmail("sender@company.com") - .recipient(Recipient.builder() - .name("Recipient Name") - .email("recipient@example.com") - .order(1) - .build()) - .field(Field.builder() - .type(FieldType.SIGNATURE) - .page(1).x(100).y(500).width(200).height(50) - .recipientOrder(1) - .build()) +PrepareForSigningResponse result = client.turboSign().prepareForSigningSingle( + new PrepareForSigningRequest.Builder() + .fileLink("https://example.com/documents/contract.pdf") + .recipients(Arrays.asList( + new Recipient("John Doe", "john@example.com", 1) + )) + .fields(Arrays.asList( + new Field("signature", 1, 100, 500, 200, 50, "john@example.com") + )) .build() ); ``` -### getStatus() +:::tip When to use fileLink +Use `fileLink` when your documents are already hosted on cloud storage (S3, Google Cloud Storage, etc.). This is more efficient than downloading and re-uploading files. +::: -Check the status of a document. - -```java -DocumentStatus status = turboSign.getStatus("document-uuid"); +### 3. TurboDocx Deliverable ID -System.out.println(status.getStatus()); // "pending", "completed", or "voided" -System.out.println(status.getCompletedAt()); +Use a document generated by TurboDocx document generation: -for (Recipient recipient : status.getRecipients()) { - System.out.println(recipient.getName() + ": " + recipient.getStatus()); - System.out.println("Signed at: " + recipient.getSignedAt()); -} +```java +PrepareForSigningResponse result = client.turboSign().prepareForSigningSingle( + new PrepareForSigningRequest.Builder() + .deliverableId("deliverable-uuid-from-turbodocx") + .recipients(Arrays.asList( + new Recipient("John Doe", "john@example.com", 1) + )) + .fields(Arrays.asList( + new Field("signature", 1, 100, 500, 200, 50, "john@example.com") + )) + .build() +); ``` -### download() +:::info Integration with TurboDocx +`deliverableId` references documents generated using TurboDocx's document generation API. This creates a seamless workflow: generate → sign. +::: -Download the completed signed document. +### 4. TurboDocx Template ID -```java -byte[] pdfBytes = turboSign.download("document-uuid"); +Use a pre-configured TurboDocx template: -// Save to file -Files.write(Path.of("signed-contract.pdf"), pdfBytes); - -// Or upload to S3 -s3Client.putObject(PutObjectRequest.builder() - .bucket("my-bucket") - .key("signed-contract.pdf") - .build(), - RequestBody.fromBytes(pdfBytes) +```java +PrepareForSigningResponse result = client.turboSign().prepareForSigningSingle( + new PrepareForSigningRequest.Builder() + .templateId("template-uuid-from-turbodocx") + .recipients(Arrays.asList( + new Recipient("John Doe", "john@example.com", 1) + )) + .fields(Arrays.asList( + new Field("signature", 1, 100, 500, 200, 50, "john@example.com") + )) + .build() ); ``` -### void() +:::info Integration with TurboDocx +`templateId` references pre-configured TurboSign templates created in the TurboDocx dashboard. These templates come with built-in anchors and field positioning, making it easy to reuse signature workflows across multiple documents. +::: -Cancel/void a signature request. +--- + +## API Reference + +### Configure + +Create a new TurboDocx client using the Builder pattern. ```java -turboSign.voidDocument("document-uuid", "Contract terms changed"); +TurboDocxClient client = new TurboDocxClient.Builder() + .apiKey("your-api-key") // Required + .orgId("your-org-id") // Required + .baseUrl("https://api.turbodocx.com") // Optional + .build(); ``` -### resend() +### Prepare for review -Resend signature request emails. +Upload a document for preview without sending emails. ```java -// Resend to all pending recipients -turboSign.resend("document-uuid"); +PrepareForReviewResponse result = client.turboSign().prepareForReview( + new PrepareForReviewRequest.Builder() + .fileLink("https://example.com/document.pdf") + .documentName("Contract Draft") + .recipients(Arrays.asList( + new Recipient("John Doe", "john@example.com", 1) + )) + .fields(Arrays.asList( + new Field("signature", 1, 100, 500, 200, 50, "john@example.com") + )) + .build() +); -// Resend to specific recipients -turboSign.resend("document-uuid", List.of("recipient-uuid-1", "recipient-uuid-2")); +System.out.println("Result: " + gson.toJson(result)); ``` ---- - -## Spring Boot Examples +### Prepare for signing -### REST Controller +Upload a document and immediately send signature requests. ```java -import com.turbodocx.sdk.TurboSign; -import com.turbodocx.sdk.models.*; -import org.springframework.web.bind.annotation.*; +PrepareForSigningResponse result = client.turboSign().prepareForSigningSingle( + new PrepareForSigningRequest.Builder() + .fileLink("https://example.com/document.pdf") + .documentName("Service Agreement") + .senderName("Your Company") + .senderEmail("sender@company.com") + .recipients(Arrays.asList( + new Recipient("Recipient Name", "recipient@example.com", 1) + )) + .fields(Arrays.asList( + new Field("signature", 1, 100, 500, 200, 50, "recipient@example.com") + )) + .build() +); -@RestController -@RequestMapping("/api/contracts") -public class ContractController { +System.out.println("Result: " + gson.toJson(result)); +``` - private final TurboSign turboSign; +### Get status - public ContractController(TurboSign turboSign) { - this.turboSign = turboSign; - } +Check the status of a document. - @PostMapping("/send") - public SendContractResponse sendContract(@RequestBody SendContractRequest request) { - SigningResult result = turboSign.prepareForSigningSingle( - SigningRequest.builder() - .fileLink(request.getContractUrl()) - .recipient(Recipient.builder() - .name(request.getRecipientName()) - .email(request.getRecipientEmail()) - .signingOrder(1) - .build()) - .field(Field.builder() - .type(FieldType.SIGNATURE) - .page(1).x(100).y(650).width(200).height(50) - .recipientEmail("alice@example.com") - .build()) - .build() - ); +```java +DocumentStatusResponse status = client.turboSign().getStatus("document-uuid"); - return new SendContractResponse( - result.getDocumentId(), - result.getRecipients().get(0).getSignUrl() - ); - } +System.out.println("Result: " + gson.toJson(status)); +``` - @GetMapping("/{id}/status") - public DocumentStatus getStatus(@PathVariable String id) { - return turboSign.getStatus(id); - } +### Download document - @GetMapping("/{id}/download") - public ResponseEntity download(@PathVariable String id) { - byte[] pdfBytes = turboSign.download(id); - return ResponseEntity.ok() - .contentType(MediaType.APPLICATION_PDF) - .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"signed-document.pdf\"") - .body(pdfBytes); - } -} +Download the completed signed document. -record SendContractRequest(String recipientName, String recipientEmail, String contractUrl) {} -record SendContractResponse(String documentId, String signUrl) {} -``` +```java +byte[] pdfData = client.turboSign().download("document-uuid"); -### Service Layer +// Save to file +Files.write(Paths.get("signed-contract.pdf"), pdfData); +``` -```java -import com.turbodocx.sdk.TurboSign; -import com.turbodocx.sdk.models.*; -import org.springframework.stereotype.Service; +### Get audit trail -@Service -public class ContractService { +Retrieve the audit trail for a document. - private final TurboSign turboSign; +```java +AuditTrailResponse auditTrail = client.turboSign().getAuditTrail("document-uuid"); - public ContractService(TurboSign turboSign) { - this.turboSign = turboSign; - } +System.out.println("Result: " + gson.toJson(auditTrail)); +``` - public SigningResult sendForSignature(String documentUrl, String recipientName, String recipientEmail) { - return turboSign.prepareForSigningSingle( - SigningRequest.builder() - .fileLink(documentUrl) - .recipient(Recipient.builder() - .name(recipientName) - .email(recipientEmail) - .signingOrder(1) - .build()) - .field(Field.builder() - .type(FieldType.SIGNATURE) - .page(1).x(100).y(650).width(200).height(50) - .recipientEmail("alice@example.com") - .build()) - .build() - ); - } +### Void - public DocumentStatus checkStatus(String documentId) { - return turboSign.getStatus(documentId); - } +Cancel/void a signature request. - public byte[] downloadSigned(String documentId) { - return turboSign.download(documentId); - } -} +```java +VoidDocumentResponse result = client.turboSign().voidDocument("document-uuid", "Contract terms changed"); ``` ---- +### Resend -## Error Handling +Resend signature request emails. ```java -import com.turbodocx.sdk.TurboSign; -import com.turbodocx.sdk.exceptions.*; - -try { - SigningResult result = turboSign.prepareForSigningSingle(request); -} catch (UnauthorizedException e) { - System.out.println("Invalid API key"); -} catch (InvalidDocumentException e) { - System.out.println("Could not process document: " + e.getMessage()); -} catch (RateLimitedException e) { - System.out.println("Rate limited, retry after: " + e.getRetryAfter() + " seconds"); -} catch (NotFoundException e) { - System.out.println("Document not found"); -} catch (TurboDocxException e) { - System.out.println("Error " + e.getCode() + ": " + e.getMessage()); -} +// Resend to specific recipients +ResendEmailResponse result = client.turboSign().resendEmail( + "document-uuid", + Arrays.asList("recipient-uuid-1", "recipient-uuid-2") +); ``` --- -## Types +## Error Handling -### Field Types +The SDK provides typed exceptions for different error scenarios: -```java -public enum FieldType { - SIGNATURE, - INITIALS, - TEXT, - DATE, - CHECKBOX, - FULL_NAME, - EMAIL, - TITLE, - COMPANY -} -``` +### Error Types -### Models +| Error Type | Status Code | Description | +| -------------------------------------------- | ----------- | ---------------------------------- | +| `TurboDocxException` | varies | Base exception for all API errors | +| `TurboDocxException.AuthenticationException` | 401 | Invalid or missing API credentials | +| `TurboDocxException.ValidationException` | 400 | Invalid request parameters | +| `TurboDocxException.NotFoundException` | 404 | Document or resource not found | +| `TurboDocxException.RateLimitException` | 429 | Too many requests | +| `TurboDocxException.NetworkException` | - | Network connectivity issues | -```java -@Builder -public class Recipient { - private String name; - private String email; - private int signingOrder; - // Response fields - private String id; - private String status; - private String signUrl; - private Instant signedAt; -} +### Error Properties -@Builder -public class Field { - private FieldType type; - private Integer page; - private Integer x; - private Integer y; - private int width; - private int height; - private String recipientEmail; - private String anchor; // For template-based fields -} +| Property | Type | Description | +| ----------------- | -------- | ---------------------------- | +| `getMessage()` | `String` | Human-readable error message | +| `getStatusCode()` | `int` | HTTP status code | +| `getCode()` | `String` | Error code (if available) | -@Builder -public class SigningRequest { - private String fileLink; - private byte[] file; - private String documentName; - private String senderName; - private String senderEmail; - @Singular private List recipients; - @Singular private List fields; -} +### Example -public class SigningResult { - private String documentId; - private List recipients; -} +```java +import com.turbodocx.TurboDocxException; -public class DocumentStatus { - private String status; // "pending", "completed", "voided" - private Instant completedAt; - private List recipients; +try { + PrepareForSigningResponse result = client.turboSign().prepareForSigningSingle(request); +} catch (TurboDocxException.AuthenticationException e) { + System.err.println("Authentication failed: " + e.getMessage()); + // Check your API key and org ID +} catch (TurboDocxException.ValidationException e) { + System.err.println("Validation error: " + e.getMessage()); + // Check request parameters +} catch (TurboDocxException.NotFoundException e) { + System.err.println("Not found: " + e.getMessage()); + // Document or recipient doesn't exist +} catch (TurboDocxException.RateLimitException e) { + System.err.println("Rate limited: " + e.getMessage()); + // Wait and retry +} catch (TurboDocxException.NetworkException e) { + System.err.println("Network error: " + e.getMessage()); + // Check connectivity +} catch (TurboDocxException e) { + // Base exception for other API errors + System.err.println("API error [" + e.getStatusCode() + "]: " + e.getMessage()); } ``` --- -## Webhook Signature Verification - -Verify that webhooks are genuinely from TurboDocx: - -```java -import com.turbodocx.sdk.WebhookVerifier; -import org.springframework.web.bind.annotation.*; - -@RestController -@RequestMapping("/webhook") -public class WebhookController { - - @Value("${turbodocx.webhook-secret}") - private String webhookSecret; +## Types - @PostMapping - public ResponseEntity handleWebhook( - @RequestBody String body, - @RequestHeader("X-TurboDocx-Signature") String signature, - @RequestHeader("X-TurboDocx-Timestamp") String timestamp) { +### Signature Field Types + +The `type` field accepts the following string values: + +| Type | Description | +| -------------- | ---------------- | +| `"signature"` | Signature field | +| `"initials"` | Initials field | +| `"text"` | Text input field | +| `"date"` | Date field | +| `"checkbox"` | Checkbox field | +| `"full_name"` | Full name field | +| `"first_name"` | First name field | +| `"last_name"` | Last name field | +| `"email"` | Email field | +| `"title"` | Title field | +| `"company"` | Company field | + +### Recipient + +| Property | Type | Required | Description | +| -------------- | -------- | -------- | ------------------------------------------------- | +| `name` | `String` | Yes | Recipient's full name | +| `email` | `String` | Yes | Recipient's email address | +| `signingOrder` | `int` | Yes | Order in which recipient should sign (1, 2, 3...) | + +### Field + +| Property | Type | Required | Description | +| ----------------- | ---------------- | -------- | ------------------------------------------- | +| `type` | `String` | Yes | Field type (see table above) | +| `recipientEmail` | `String` | Yes | Email of the recipient who fills this field | +| `page` | `Integer` | No\* | Page number (1-indexed) | +| `x` | `Integer` | No\* | X coordinate in pixels | +| `y` | `Integer` | No\* | Y coordinate in pixels | +| `width` | `Integer` | No* | Field width in pixels | +| `height` | `Integer` | No* | Field height in pixels | +| `defaultValue` | `String` | No | Pre-filled value | +| `isMultiline` | `Boolean` | No | Enable multiline for text fields | +| `isReadonly` | `Boolean` | No | Make field read-only | +| `required` | `Boolean` | No | Make field required | +| `backgroundColor` | `String` | No | Background color | +| `template` | `TemplateAnchor` | No | Template anchor configuration | + +\*Required when not using template anchors + +#### Template Configuration + +When using `template` instead of coordinates: + +| Property | Type | Required | Description | +| --------------- | --------- | -------- | ------------------------------------------------------------------------------------- | +| `anchor` | `String` | Yes | Text to find in document (e.g., `"{SIGNATURE}"`) | | +| `placement` | `String` | Yes | Position relative to anchor: `"replace"`, `"before"`, `"after"`, `"above"`, `"below"` | +| `size` | `Size` | Yes | Size with `width` and `height` | +| `offset` | `Offset` | No | Offset with `x` and `y` | +| `caseSensitive` | `Boolean` | No | Case-sensitive anchor search | +| `useRegex` | `Boolean` | No | Use regex for anchor search | + +### Request Parameters + +Both `PrepareForReviewRequest` and `PrepareForSigningRequest` accept: + +| Property | Type | Required | Description | +| --------------------- | ----------------- | ----------- | ------------------------ | +| `file` | `byte[]` | Conditional | File content as bytes | +| `fileLink` | `String` | Conditional | URL to document | +| `deliverableId` | `String` | Conditional | TurboDocx deliverable ID | +| `templateId` | `String` | Conditional | TurboDocx template ID | +| `recipients` | `List` | Yes | List of recipients | +| `fields` | `List` | Yes | List of fields | +| `documentName` | `String` | No | Document display name | +| `documentDescription` | `String` | No | Document description | +| `senderName` | `String` | No | Sender's name | +| `senderEmail` | `String` | No | Sender's email | +| `ccEmails` | `List` | No | CC email addresses | + +:::info File Source (Conditional) +Exactly one file source is required: `file`, `fileLink`, `deliverableId`, or `templateId`. +::: - boolean isValid = WebhookVerifier.verifySignature(signature, timestamp, body, webhookSecret); +--- - if (!isValid) { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid signature"); - } +## Additional Documentation - ObjectMapper mapper = new ObjectMapper(); - WebhookEvent event = mapper.readValue(body, WebhookEvent.class); +For detailed information about advanced configuration and API concepts, see: - switch (event.getEvent()) { - case "signature.document.completed": - System.out.println("Document completed: " + event.getData().getDocumentId()); - break; - case "signature.document.voided": - System.out.println("Document voided: " + event.getData().getDocumentId()); - break; - } +### Core API References - return ResponseEntity.ok(Map.of("received", true)); - } -} -``` +- **[Request Body Reference](/docs/TurboSign/API%20Signatures#request-body-multipartform-data)** - Complete request body parameters, file sources, and multipart/form-data structure +- **[Recipients Reference](/docs/TurboSign/API%20Signatures#recipients-reference)** - Recipient properties, signing order, metadata, and configuration options +- **[Field Types Reference](/docs/TurboSign/API%20Signatures#field-types-reference)** - All available field types (signature, date, text, checkbox, etc.) with properties and behaviors +- **[Field Positioning Methods](/docs/TurboSign/API%20Signatures#field-positioning-methods)** - Template-based vs coordinate-based positioning, anchor configuration, and best practices --- diff --git a/docs/SDKs/javascript.md b/docs/SDKs/javascript.md index 71119c5..86f3925 100644 --- a/docs/SDKs/javascript.md +++ b/docs/SDKs/javascript.md @@ -257,17 +257,21 @@ const result = await TurboSign.prepareForSigningSingle({ fields: [ { type: "signature", - template: { anchor: "{SIGNATURE_ALICE}" }, - width: 200, - height: 50, recipientEmail: "alice@example.com", + template: { + anchor: "{SIGNATURE_ALICE}", + placement: "replace", + size: { width: 200, height: 50 }, + }, }, { type: "date", - template: { anchor: "{DATE_ALICE}" }, - width: 100, - height: 30, recipientEmail: "alice@example.com", + template: { + anchor: "{DATE_ALICE}", + placement: "replace", + size: { width: 100, height: 30 }, + }, }, ], }); @@ -288,17 +292,21 @@ const result = await TurboSign.prepareForSigningSingle({ fields: [ { type: "signature", - template: { anchor: "{SIGNATURE_ALICE}" }, - width: 200, - height: 50, recipientEmail: "alice@example.com", + template: { + anchor: "{SIGNATURE_ALICE}", + placement: "replace", + size: { width: 200, height: 50 }, + }, }, { type: "date", - template: { anchor: "{DATE_ALICE}" }, - width: 100, - height: 30, recipientEmail: "alice@example.com", + template: { + anchor: "{DATE_ALICE}", + placement: "replace", + size: { width: 100, height: 30 }, + }, }, ], }); @@ -323,10 +331,12 @@ const result = await TurboSign.prepareForSigningSingle({ fields: [ { type: "signature", - template: { anchor: "{SIGNATURE_ALICE}" }, - width: 200, - height: 50, recipientEmail: "alice@example.com", + template: { + anchor: "{SIGNATURE_ALICE}", + placement: "replace", + size: { width: 200, height: 50 }, + }, }, ], }); @@ -528,10 +538,12 @@ const result = await TurboSign.prepareForSigningSingle({ fields: [ { type: "signature", - template: { anchor: "{SIGNATURE_ALICE}" }, - width: 200, - height: 50, recipientEmail: "alice@example.com", + template: { + anchor: "{SIGNATURE_ALICE}", + placement: "replace", + size: { width: 200, height: 50 }, + }, }, ], }); @@ -550,10 +562,12 @@ const result = await TurboSign.prepareForSigningSingle({ fields: [ { type: "signature", - template: { anchor: "{SIGNATURE_ALICE}" }, - width: 200, - height: 50, recipientEmail: "alice@example.com", + template: { + anchor: "{SIGNATURE_ALICE}", + placement: "replace", + size: { width: 200, height: 50 }, + }, }, ], }); @@ -818,9 +832,6 @@ Resend signature request emails to specific recipients. ```javascript -// Resend to all pending recipients -await TurboSign.resend("document-uuid"); - // Resend to specific recipients await TurboSign.resend("document-uuid", [ "recipient-uuid-1", @@ -832,9 +843,6 @@ await TurboSign.resend("document-uuid", [ ```typescript -// Resend to all pending recipients -await TurboSign.resend("document-uuid"); - // Resend to specific recipients await TurboSign.resend("document-uuid", [ "recipient-uuid-1", @@ -1067,11 +1075,11 @@ Field configuration supporting both coordinate-based and template-based position |----------|------|----------|-------------| | `type` | `SignatureFieldType` | Yes | Field type | | `recipientEmail` | `string` | Yes | Which recipient fills this field | -| `page` | `number` | Yes | Page number (1-indexed) | -| `x` | `number` | Yes | X coordinate position | -| `y` | `number` | Yes | Y coordinate position | -| `width` | `number` | Yes | Field width in pixels | -| `height` | `number` | Yes | Field height in pixels | +| `page` | `number` | No\* | Page number (1-indexed) | +| `x` | `number` | No\* | X coordinate in pixels | +| `y` | `number` | No\* | Y coordinate in pixels | +| `width` | `number` | No* | Field width in pixels | +| `height` | `number` | No* | Field height in pixels | | `defaultValue` | `string` | No | Default value (for checkbox: `"true"` or `"false"`) | | `isMultiline` | `boolean` | No | Enable multiline text | | `isReadonly` | `boolean` | No | Make field read-only (pre-filled) | @@ -1079,12 +1087,13 @@ Field configuration supporting both coordinate-based and template-based position | `backgroundColor` | `string` | No | Background color (hex, rgb, or named) | | `template` | `object` | No | Template anchor configuration | +\*Required when not using template anchors + **Template Configuration:** | Property | Type | Description | |----------|------|-------------| | `anchor` | `string` | Text anchor pattern like `{TagName}` | -| `searchText` | `string` | Alternative: search for any text | | `placement` | `string` | `"replace"` \| `"before"` \| `"after"` \| `"above"` \| `"below"` | | `size` | `object` | `{ width: number; height: number }` | | `offset` | `object` | `{ x: number; y: number }` | @@ -1097,7 +1106,7 @@ Request configuration for `prepareForReview` and `prepareForSigningSingle` metho | Property | Type | Required | Description | |----------|------|----------|-------------| -| `file` | `string \| File \| Buffer` | Conditional | PDF file as file path, Buffer, or browser File | +| `file` | `Buffer` | Conditional | PDF file as Buffer | | `fileLink` | `string` | Conditional | URL to document file | | `deliverableId` | `string` | Conditional | TurboDocx deliverable ID | | `templateId` | `string` | Conditional | TurboDocx template ID | diff --git a/docs/SDKs/python.md b/docs/SDKs/python.md index 8fb7ccf..1304a4a 100644 --- a/docs/SDKs/python.md +++ b/docs/SDKs/python.md @@ -16,7 +16,6 @@ keywords: import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -import ScriptLoader from '@site/src/components/ScriptLoader'; # Python SDK @@ -133,8 +132,24 @@ asyncio.run(send_contract()) result = await TurboSign.prepare_for_signing_single( recipients=[{"name": "Alice Smith", "email": "alice@example.com", "signingOrder": 1}], fields=[ - {"type": "signature", "template": {"anchor": "{SIGNATURE_ALICE}"}, "width": 200, "height": 50, "recipientEmail": "alice@example.com"}, - {"type": "date", "template": {"anchor": "{DATE_ALICE}"}, "width": 100, "height": 30, "recipientEmail": "alice@example.com"} + { + "type": "signature", + "recipientEmail": "alice@example.com", + "template": { + "anchor": "{SIGNATURE_ALICE}", + "placement": "replace", + "size": {"width": 200, "height": 50}, + }, + }, + { + "type": "date", + "recipientEmail": "alice@example.com", + "template": { + "anchor": "{DATE_ALICE}", + "placement": "replace", + "size": {"width": 100, "height": 30}, + }, + }, ], file_link="https://example.com/contract-with-placeholders.pdf", ) @@ -242,10 +257,12 @@ result = await TurboSign.prepare_for_signing_single( fields=[ { "type": "signature", - "template": {"anchor": "{SIGNATURE_ALICE}"}, - "width": 200, - "height": 50, "recipientEmail": "alice@example.com", + "template": { + "anchor": "{SIGNATURE_ALICE}", + "placement": "replace", + "size": {"width": 200, "height": 50}, + }, }, ], ) @@ -332,8 +349,6 @@ Cancel/void a signature request. ```python result = await TurboSign.void_document("document-uuid", reason="Contract terms changed") - -print("Result:", json.dumps(result, indent=2)) ``` ### Resend @@ -342,8 +357,6 @@ Resend signature request emails to specific recipients. ```python result = await TurboSign.resend_email("document-uuid", recipient_ids=["recipient-uuid-1", "recipient-uuid-2"]) - -print("Result:", json.dumps(result, indent=2)) ``` ### Get audit trail @@ -492,11 +505,11 @@ Field configuration supporting both coordinate-based and template-based position | ----------------- | ------ | -------- | --------------------------------------------------- | | `type` | `str` | Yes | Field type (see SignatureFieldType) | | `recipientEmail` | `str` | Yes | Which recipient fills this field | -| `page` | `int` | Yes | Page number (1-indexed) | -| `x` | `int` | Yes | X coordinate position | -| `y` | `int` | Yes | Y coordinate position | -| `width` | `int` | Yes | Field width in pixels | -| `height` | `int` | Yes | Field height in pixels | +| `page` | `int` | No\* | Page number (1-indexed) | +| `x` | `int` | No\* | X coordinate in pixels | +| `y` | `int` | No\* | Y coordinate in pixels | +| `width` | `int` | No* | Field width in pixels | +| `height` | `int` | No* | Field height in pixels | | `defaultValue` | `str` | No | Default value (for checkbox: `"true"` or `"false"`) | | `isMultiline` | `bool` | No | Enable multiline text | | `isReadonly` | `bool` | No | Make field read-only (pre-filled) | @@ -504,12 +517,13 @@ Field configuration supporting both coordinate-based and template-based position | `backgroundColor` | `str` | No | Background color (hex, rgb, or named) | | `template` | `Dict` | No | Template anchor configuration | +\*Required when not using template anchors + **Template Configuration:** | Property | Type | Description | | --------------- | ------ | ---------------------------------------------------------------- | | `anchor` | `str` | Text anchor pattern like `{TagName}` | -| `searchText` | `str` | Alternative: search for any text | | `placement` | `str` | `"replace"` \| `"before"` \| `"after"` \| `"above"` \| `"below"` | | `size` | `Dict` | `{ "width": int, "height": int }` | | `offset` | `Dict` | `{ "x": int, "y": int }` | From 3f9485abbee1198d0ffc82d1b09170bb85a2af35 Mon Sep 17 00:00:00 2001 From: Amit Sharma Date: Wed, 10 Dec 2025 17:06:05 +0000 Subject: [PATCH 4/6] feat: added required columns to some types tables --- docs/SDKs/javascript.md | 16 ++++++++-------- docs/SDKs/python.md | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/docs/SDKs/javascript.md b/docs/SDKs/javascript.md index 86f3925..b961500 100644 --- a/docs/SDKs/javascript.md +++ b/docs/SDKs/javascript.md @@ -1091,14 +1091,14 @@ Field configuration supporting both coordinate-based and template-based position **Template Configuration:** -| Property | Type | Description | -|----------|------|-------------| -| `anchor` | `string` | Text anchor pattern like `{TagName}` | -| `placement` | `string` | `"replace"` \| `"before"` \| `"after"` \| `"above"` \| `"below"` | -| `size` | `object` | `{ width: number; height: number }` | -| `offset` | `object` | `{ x: number; y: number }` | -| `caseSensitive` | `boolean` | Case sensitive search (default: false) | -| `useRegex` | `boolean` | Use regex for anchor/searchText (default: false) | +| Property | Type | Required | Description | +|----------|------|----------|-------------| +| `anchor` | `string` | Yes | Text anchor pattern like `{TagName}` | +| `placement` | `string` | Yes | `"replace"` \| `"before"` \| `"after"` \| `"above"` \| `"below"` | +| `size` | `object` | Yes | `{ width: number; height: number }` | +| `offset` | `object` | No | `{ x: number; y: number }` | +| `caseSensitive` | `boolean` | No | Case sensitive search (default: false) | +| `useRegex` | `boolean` | No | Use regex for anchor/searchText (default: false) | ### PrepareForReviewRequest / PrepareForSigningSingleRequest diff --git a/docs/SDKs/python.md b/docs/SDKs/python.md index 1304a4a..f92a4fd 100644 --- a/docs/SDKs/python.md +++ b/docs/SDKs/python.md @@ -521,14 +521,14 @@ Field configuration supporting both coordinate-based and template-based position **Template Configuration:** -| Property | Type | Description | -| --------------- | ------ | ---------------------------------------------------------------- | -| `anchor` | `str` | Text anchor pattern like `{TagName}` | -| `placement` | `str` | `"replace"` \| `"before"` \| `"after"` \| `"above"` \| `"below"` | -| `size` | `Dict` | `{ "width": int, "height": int }` | -| `offset` | `Dict` | `{ "x": int, "y": int }` | -| `caseSensitive` | `bool` | Case sensitive search (default: False) | -| `useRegex` | `bool` | Use regex for anchor/searchText (default: False) | +| Property | Type | Required | Description | +| --------------- | ------ | -------- | ---------------------------------------------------------------- | +| `anchor` | `str` | Yes | Text anchor pattern like `{TagName}` | +| `placement` | `str` | Yes | `"replace"` \| `"before"` \| `"after"` \| `"above"` \| `"below"` | +| `size` | `Dict` | Yes | `{ "width": int, "height": int }` | +| `offset` | `Dict` | No | `{ "x": int, "y": int }` | +| `caseSensitive` | `bool` | No | Case sensitive search (default: False) | +| `useRegex` | `bool` | No | Use regex for anchor/searchText (default: False) | ```python field: Dict[str, Any] = { From 20f5fa155e84f61fbc46501c92fd01d093782733 Mon Sep 17 00:00:00 2001 From: Amit Sharma Date: Wed, 10 Dec 2025 17:17:16 +0000 Subject: [PATCH 5/6] feat: updated function names in docs acc to the updated names in the SDKs --- docs/SDKs/go.md | 20 +++++++++--------- docs/SDKs/java.md | 36 ++++++++++++++++---------------- docs/SDKs/javascript.md | 46 ++++++++++++++++++++--------------------- docs/SDKs/python.md | 20 +++++++++--------- 4 files changed, 61 insertions(+), 61 deletions(-) diff --git a/docs/SDKs/go.md b/docs/SDKs/go.md index edda646..d95ecd8 100644 --- a/docs/SDKs/go.md +++ b/docs/SDKs/go.md @@ -98,7 +98,7 @@ func main() { ctx := context.Background() - result, err := client.TurboSign.PrepareForSigningSingle(ctx, &turbodocx.PrepareForSigningRequest{ + result, err := client.TurboSign.SendSignature(ctx, &turbodocx.SendSignatureRequest{ FileLink: "https://example.com/contract.pdf", DocumentName: "Service Agreement", SenderName: "Acme Corp", @@ -127,7 +127,7 @@ func main() { ### Using Template-Based Fields ```go -result, err := client.TurboSign.PrepareForSigningSingle(ctx, &turbodocx.PrepareForSigningRequest{ +result, err := client.TurboSign.SendSignature(ctx, &turbodocx.SendSignatureRequest{ FileLink: "https://example.com/contract-with-placeholders.pdf", Recipients: []turbodocx.Recipient{ {Name: "Alice Smith", Email: "alice@example.com", SigningOrder: 1}, @@ -175,7 +175,7 @@ if err != nil { log.Fatal(err) } -result, err := client.TurboSign.PrepareForSigningSingle(ctx, &turbodocx.PrepareForSigningRequest{ +result, err := client.TurboSign.SendSignature(ctx, &turbodocx.SendSignatureRequest{ File: pdfBytes, Recipients: []turbodocx.Recipient{ {Name: "John Doe", Email: "john@example.com", SigningOrder: 1}, @@ -191,7 +191,7 @@ result, err := client.TurboSign.PrepareForSigningSingle(ctx, &turbodocx.PrepareF Provide a publicly accessible URL to your document: ```go -result, err := client.TurboSign.PrepareForSigningSingle(ctx, &turbodocx.PrepareForSigningRequest{ +result, err := client.TurboSign.SendSignature(ctx, &turbodocx.SendSignatureRequest{ FileLink: "https://example.com/documents/contract.pdf", Recipients: []turbodocx.Recipient{ {Name: "John Doe", Email: "john@example.com", SigningOrder: 1}, @@ -211,7 +211,7 @@ Use `FileLink` when your documents are already hosted on cloud storage (S3, Goog Use a document generated by TurboDocx document generation: ```go -result, err := client.TurboSign.PrepareForSigningSingle(ctx, &turbodocx.PrepareForSigningRequest{ +result, err := client.TurboSign.SendSignature(ctx, &turbodocx.SendSignatureRequest{ DeliverableID: "deliverable-uuid-from-turbodocx", Recipients: []turbodocx.Recipient{ {Name: "John Doe", Email: "john@example.com", SigningOrder: 1}, @@ -231,7 +231,7 @@ result, err := client.TurboSign.PrepareForSigningSingle(ctx, &turbodocx.PrepareF Use a pre-configured TurboDocx template: ```go -result, err := client.TurboSign.PrepareForSigningSingle(ctx, &turbodocx.PrepareForSigningRequest{ +result, err := client.TurboSign.SendSignature(ctx, &turbodocx.SendSignatureRequest{ TemplateID: "template-uuid-from-turbodocx", Recipients: []turbodocx.Recipient{ {Name: "John Doe", Email: "john@example.com", SigningOrder: 1}, @@ -275,7 +275,7 @@ Both `APIKey` and `OrgID` parameters are **required** for all API requests. To g Upload a document for preview without sending emails. ```go -result, err := client.TurboSign.PrepareForReview(ctx, &turbodocx.PrepareForReviewRequest{ +result, err := client.TurboSign.CreateSignatureReviewLink(ctx, &turbodocx.CreateSignatureReviewLinkRequest{ FileLink: "https://example.com/document.pdf", DocumentName: "Contract Draft", Recipients: []turbodocx.Recipient{ @@ -294,7 +294,7 @@ b, _ := json.MarshalIndent(result, "", " "); fmt.Println("Result:", string(b)) Upload a document and immediately send signature requests. ```go -result, err := client.TurboSign.PrepareForSigningSingle(ctx, &turbodocx.PrepareForSigningRequest{ +result, err := client.TurboSign.SendSignature(ctx, &turbodocx.SendSignatureRequest{ FileLink: "https://example.com/document.pdf", DocumentName: "Service Agreement", SenderName: "Your Company", @@ -402,7 +402,7 @@ import ( turbodocx "github.com/TurboDocx/SDK/packages/go-sdk" ) -result, err := client.TurboSign.PrepareForSigningSingle(ctx, request) +result, err := client.TurboSign.SendSignature(ctx, request) if err != nil { // Check for specific error types var authErr *turbodocx.AuthenticationError @@ -499,7 +499,7 @@ When using `Template` instead of coordinates: ### Request Parameters -Both `PrepareForReviewRequest` and `PrepareForSigningRequest` accept: +Both `CreateSignatureReviewLinkRequest` and `SendSignatureRequest` accept: | Property | Type | Required | Description | | --------------------- | ------------- | ----------- | ------------------------ | diff --git a/docs/SDKs/java.md b/docs/SDKs/java.md index fa032b6..ec453fe 100644 --- a/docs/SDKs/java.md +++ b/docs/SDKs/java.md @@ -115,8 +115,8 @@ public class Main { Gson gson = new GsonBuilder().setPrettyPrinting().create(); - PrepareForSigningResponse result = client.turboSign().prepareForSigningSingle( - new PrepareForSigningRequest.Builder() + SendSignatureResponse result = client.turboSign().sendSignature( + new SendSignatureRequest.Builder() .fileLink("https://example.com/contract.pdf") .documentName("Service Agreement") .senderName("Acme Corp") @@ -172,8 +172,8 @@ Field templateField = new Field( templateAnchor // template anchor config ); -PrepareForSigningResponse result = client.turboSign().prepareForSigningSingle( - new PrepareForSigningRequest.Builder() +SendSignatureResponse result = client.turboSign().sendSignature( + new SendSignatureRequest.Builder() .fileLink("https://example.com/contract-with-placeholders.pdf") .recipients(Arrays.asList( new Recipient("Alice Smith", "alice@example.com", 1) @@ -203,8 +203,8 @@ import java.nio.file.Paths; byte[] pdfBytes = Files.readAllBytes(Paths.get("/path/to/document.pdf")); -PrepareForSigningResponse result = client.turboSign().prepareForSigningSingle( - new PrepareForSigningRequest.Builder() +SendSignatureResponse result = client.turboSign().sendSignature( + new SendSignatureRequest.Builder() .file(pdfBytes) .recipients(Arrays.asList( new Recipient("John Doe", "john@example.com", 1) @@ -221,8 +221,8 @@ PrepareForSigningResponse result = client.turboSign().prepareForSigningSingle( Provide a publicly accessible URL to your document: ```java -PrepareForSigningResponse result = client.turboSign().prepareForSigningSingle( - new PrepareForSigningRequest.Builder() +SendSignatureResponse result = client.turboSign().sendSignature( + new SendSignatureRequest.Builder() .fileLink("https://example.com/documents/contract.pdf") .recipients(Arrays.asList( new Recipient("John Doe", "john@example.com", 1) @@ -243,8 +243,8 @@ Use `fileLink` when your documents are already hosted on cloud storage (S3, Goog Use a document generated by TurboDocx document generation: ```java -PrepareForSigningResponse result = client.turboSign().prepareForSigningSingle( - new PrepareForSigningRequest.Builder() +SendSignatureResponse result = client.turboSign().sendSignature( + new SendSignatureRequest.Builder() .deliverableId("deliverable-uuid-from-turbodocx") .recipients(Arrays.asList( new Recipient("John Doe", "john@example.com", 1) @@ -265,8 +265,8 @@ PrepareForSigningResponse result = client.turboSign().prepareForSigningSingle( Use a pre-configured TurboDocx template: ```java -PrepareForSigningResponse result = client.turboSign().prepareForSigningSingle( - new PrepareForSigningRequest.Builder() +SendSignatureResponse result = client.turboSign().sendSignature( + new SendSignatureRequest.Builder() .templateId("template-uuid-from-turbodocx") .recipients(Arrays.asList( new Recipient("John Doe", "john@example.com", 1) @@ -303,8 +303,8 @@ TurboDocxClient client = new TurboDocxClient.Builder() Upload a document for preview without sending emails. ```java -PrepareForReviewResponse result = client.turboSign().prepareForReview( - new PrepareForReviewRequest.Builder() +CreateSignatureReviewLinkResponse result = client.turboSign().createSignatureReviewLink( + new CreateSignatureReviewLinkRequest.Builder() .fileLink("https://example.com/document.pdf") .documentName("Contract Draft") .recipients(Arrays.asList( @@ -324,8 +324,8 @@ System.out.println("Result: " + gson.toJson(result)); Upload a document and immediately send signature requests. ```java -PrepareForSigningResponse result = client.turboSign().prepareForSigningSingle( - new PrepareForSigningRequest.Builder() +SendSignatureResponse result = client.turboSign().sendSignature( + new SendSignatureRequest.Builder() .fileLink("https://example.com/document.pdf") .documentName("Service Agreement") .senderName("Your Company") @@ -424,7 +424,7 @@ The SDK provides typed exceptions for different error scenarios: import com.turbodocx.TurboDocxException; try { - PrepareForSigningResponse result = client.turboSign().prepareForSigningSingle(request); + SendSignatureResponse result = client.turboSign().sendSignature(request); } catch (TurboDocxException.AuthenticationException e) { System.err.println("Authentication failed: " + e.getMessage()); // Check your API key and org ID @@ -511,7 +511,7 @@ When using `template` instead of coordinates: ### Request Parameters -Both `PrepareForReviewRequest` and `PrepareForSigningRequest` accept: +Both `CreateSignatureReviewLinkRequest` and `SendSignatureRequest` accept: | Property | Type | Required | Description | | --------------------- | ----------------- | ----------- | ------------------------ | diff --git a/docs/SDKs/javascript.md b/docs/SDKs/javascript.md index b961500..2b6a01c 100644 --- a/docs/SDKs/javascript.md +++ b/docs/SDKs/javascript.md @@ -119,7 +119,7 @@ TurboSign.configure({ }); // Send document with coordinate-based fields -const result = await TurboSign.prepareForSigningSingle({ +const result = await TurboSign.sendSignature({ fileLink: "https://example.com/contract.pdf", documentName: "Service Agreement", senderName: "Acme Corp", @@ -185,7 +185,7 @@ TurboSign.configure({ }); // Send document with coordinate-based fields -const result = await TurboSign.prepareForSigningSingle({ +const result = await TurboSign.sendSignature({ fileLink: "https://example.com/contract.pdf", documentName: "Service Agreement", senderName: "Acme Corp", @@ -249,7 +249,7 @@ console.log(JSON.stringify(result, null, 2)); ```javascript // Use text anchors instead of coordinates -const result = await TurboSign.prepareForSigningSingle({ +const result = await TurboSign.sendSignature({ fileLink: "https://example.com/contract-with-placeholders.pdf", recipients: [ { name: "Alice Smith", email: "alice@example.com", signingOrder: 1 }, @@ -284,7 +284,7 @@ console.log(JSON.stringify(result, null, 2)); ```typescript // Use text anchors instead of coordinates -const result = await TurboSign.prepareForSigningSingle({ +const result = await TurboSign.sendSignature({ fileLink: "https://example.com/contract-with-placeholders.pdf", recipients: [ { name: "Alice Smith", email: "alice@example.com", signingOrder: 1 }, @@ -323,7 +323,7 @@ console.log(JSON.stringify(result, null, 2)); **Alternative:** Use a TurboDocx template with pre-configured anchors: ```typescript -const result = await TurboSign.prepareForSigningSingle({ +const result = await TurboSign.sendSignature({ templateId: "template-uuid-from-turbodocx", // Template already contains anchors recipients: [ { name: "Alice Smith", email: "alice@example.com", signingOrder: 1 }, @@ -361,7 +361,7 @@ const { TurboSign } = require("@turbodocx/sdk"); const fileBuffer = readFileSync("./contract.pdf"); -const result = await TurboSign.prepareForSigningSingle({ +const result = await TurboSign.sendSignature({ file: fileBuffer, recipients: [ { name: "John Doe", email: "john@example.com", signingOrder: 1 }, @@ -389,7 +389,7 @@ import { TurboSign } from "@turbodocx/sdk"; const fileBuffer = readFileSync("./contract.pdf"); -const result = await TurboSign.prepareForSigningSingle({ +const result = await TurboSign.sendSignature({ file: fileBuffer, recipients: [ { name: "John Doe", email: "john@example.com", signingOrder: 1 }, @@ -417,7 +417,7 @@ const result = await TurboSign.prepareForSigningSingle({ ```javascript -const result = await TurboSign.prepareForSigningSingle({ +const result = await TurboSign.sendSignature({ fileLink: "https://storage.example.com/contracts/agreement.pdf", recipients: [ { name: "John Doe", email: "john@example.com", signingOrder: 1 }, @@ -440,7 +440,7 @@ const result = await TurboSign.prepareForSigningSingle({ ```typescript -const result = await TurboSign.prepareForSigningSingle({ +const result = await TurboSign.sendSignature({ fileLink: "https://storage.example.com/contracts/agreement.pdf", recipients: [ { name: "John Doe", email: "john@example.com", signingOrder: 1 }, @@ -473,7 +473,7 @@ Use `fileLink` when your documents are already hosted on cloud storage (S3, Goog ```javascript // Use a previously generated TurboDocx document -const result = await TurboSign.prepareForSigningSingle({ +const result = await TurboSign.sendSignature({ deliverableId: "deliverable-uuid-from-turbodocx", recipients: [ { name: "John Doe", email: "john@example.com", signingOrder: 1 }, @@ -497,7 +497,7 @@ const result = await TurboSign.prepareForSigningSingle({ ```typescript // Use a previously generated TurboDocx document -const result = await TurboSign.prepareForSigningSingle({ +const result = await TurboSign.sendSignature({ deliverableId: "deliverable-uuid-from-turbodocx", recipients: [ { name: "John Doe", email: "john@example.com", signingOrder: 1 }, @@ -530,7 +530,7 @@ const result = await TurboSign.prepareForSigningSingle({ ```javascript // Use a pre-configured TurboSign template -const result = await TurboSign.prepareForSigningSingle({ +const result = await TurboSign.sendSignature({ templateId: "template-uuid-from-turbodocx", // Template already contains anchors recipients: [ { name: "Alice Smith", email: "alice@example.com", signingOrder: 1 }, @@ -554,7 +554,7 @@ const result = await TurboSign.prepareForSigningSingle({ ```typescript // Use a pre-configured TurboSign template -const result = await TurboSign.prepareForSigningSingle({ +const result = await TurboSign.sendSignature({ templateId: "template-uuid-from-turbodocx", // Template already contains anchors recipients: [ { name: "Alice Smith", email: "alice@example.com", signingOrder: 1 }, @@ -641,7 +641,7 @@ Upload a document for preview without sending signature request emails. ```javascript -const { documentId, previewUrl } = await TurboSign.prepareForReview({ +const { documentId, previewUrl } = await TurboSign.createSignatureReviewLink({ fileLink: "https://example.com/document.pdf", documentName: "Contract Draft", recipients: [ @@ -665,7 +665,7 @@ const { documentId, previewUrl } = await TurboSign.prepareForReview({ ```typescript -const { documentId, previewUrl } = await TurboSign.prepareForReview({ +const { documentId, previewUrl } = await TurboSign.createSignatureReviewLink({ fileLink: "https://example.com/document.pdf", documentName: "Contract Draft", recipients: [ @@ -696,7 +696,7 @@ Upload a document and immediately send signature requests to all recipients. ```javascript -const { documentId } = await TurboSign.prepareForSigningSingle({ +const { documentId } = await TurboSign.sendSignature({ fileLink: "https://example.com/document.pdf", documentName: "Service Agreement", senderName: "Your Company", @@ -722,7 +722,7 @@ const { documentId } = await TurboSign.prepareForSigningSingle({ ```typescript -const { documentId } = await TurboSign.prepareForSigningSingle({ +const { documentId } = await TurboSign.sendSignature({ fileLink: "https://example.com/document.pdf", documentName: "Service Agreement", senderName: "Your Company", @@ -912,7 +912,7 @@ const { } = require("@turbodocx/sdk"); try { - const result = await TurboSign.prepareForSigningSingle({ + const result = await TurboSign.sendSignature({ fileLink: "https://example.com/contract.pdf", recipients: [ { name: "John Doe", email: "john@example.com", signingOrder: 1 }, @@ -966,7 +966,7 @@ import { } from "@turbodocx/sdk"; try { - const result = await TurboSign.prepareForSigningSingle({ + const result = await TurboSign.sendSignature({ fileLink: "https://example.com/contract.pdf", recipients: [ { name: "John Doe", email: "john@example.com", signingOrder: 1 }, @@ -1033,8 +1033,8 @@ import type { N8nField, N8nRecipient, // Request types - PrepareForReviewRequest, - PrepareForSigningSingleRequest, + CreateSignatureReviewLinkRequest, + SendSignatureRequest, } from "@turbodocx/sdk"; ``` @@ -1100,9 +1100,9 @@ Field configuration supporting both coordinate-based and template-based position | `caseSensitive` | `boolean` | No | Case sensitive search (default: false) | | `useRegex` | `boolean` | No | Use regex for anchor/searchText (default: false) | -### PrepareForReviewRequest / PrepareForSigningSingleRequest +### CreateSignatureReviewLinkRequest / SendSignatureRequest -Request configuration for `prepareForReview` and `prepareForSigningSingle` methods: +Request configuration for `createSignatureReviewLink` and `sendSignature` methods: | Property | Type | Required | Description | |----------|------|----------|-------------| diff --git a/docs/SDKs/python.md b/docs/SDKs/python.md index f92a4fd..f97ef01 100644 --- a/docs/SDKs/python.md +++ b/docs/SDKs/python.md @@ -102,7 +102,7 @@ TurboSign.configure( ) async def send_contract(): - result = await TurboSign.prepare_for_signing_single( + result = await TurboSign.send_signature( recipients=[ {"name": "Alice Smith", "email": "alice@example.com", "signingOrder": 1}, {"name": "Bob Johnson", "email": "bob@example.com", "signingOrder": 2} @@ -129,7 +129,7 @@ asyncio.run(send_contract()) ### Using Template-Based Fields ```python -result = await TurboSign.prepare_for_signing_single( +result = await TurboSign.send_signature( recipients=[{"name": "Alice Smith", "email": "alice@example.com", "signingOrder": 1}], fields=[ { @@ -173,7 +173,7 @@ TurboSign supports four different ways to provide document files: with open("./contract.pdf", "rb") as f: pdf_buffer = f.read() -result = await TurboSign.prepare_for_signing_single( +result = await TurboSign.send_signature( file=pdf_buffer, recipients=[ {"name": "John Doe", "email": "john@example.com", "signingOrder": 1}, @@ -195,7 +195,7 @@ result = await TurboSign.prepare_for_signing_single( ### 2. File URL (file_link) ```python -result = await TurboSign.prepare_for_signing_single( +result = await TurboSign.send_signature( file_link="https://storage.example.com/contracts/agreement.pdf", recipients=[ {"name": "John Doe", "email": "john@example.com", "signingOrder": 1}, @@ -222,7 +222,7 @@ Use `file_link` when your documents are already hosted on cloud storage (S3, Goo ```python # Use a previously generated TurboDocx document -result = await TurboSign.prepare_for_signing_single( +result = await TurboSign.send_signature( deliverable_id="deliverable-uuid-from-turbodocx", recipients=[ {"name": "John Doe", "email": "john@example.com", "signingOrder": 1}, @@ -249,7 +249,7 @@ result = await TurboSign.prepare_for_signing_single( ```python # Use a pre-configured TurboSign template -result = await TurboSign.prepare_for_signing_single( +result = await TurboSign.send_signature( template_id="template-uuid-from-turbodocx", # Template already contains anchors recipients=[ {"name": "Alice Smith", "email": "alice@example.com", "signingOrder": 1}, @@ -293,7 +293,7 @@ TurboSign.configure( Upload a document for preview without sending signature request emails. ```python -result = await TurboSign.prepare_for_review( +result = await TurboSign.create_signature_review_link( recipients=[{"name": "John Doe", "email": "john@example.com", "signingOrder": 1}], fields=[{"type": "signature", "page": 1, "x": 100, "y": 500, "width": 200, "height": 50, "recipientEmail": "john@example.com"}], file_link="https://example.com/document.pdf", @@ -309,7 +309,7 @@ print(result["previewUrl"]) Upload a document and immediately send signature requests to all recipients. ```python -result = await TurboSign.prepare_for_signing_single( +result = await TurboSign.send_signature( recipients=[{"name": "Recipient Name", "email": "recipient@example.com", "signingOrder": 1}], fields=[{"type": "signature", "page": 1, "x": 100, "y": 500, "width": 200, "height": 50, "recipientEmail": "recipient@example.com"}], file_link="https://example.com/document.pdf", @@ -400,7 +400,7 @@ from turbodocx_sdk import ( ) try: - result = await TurboSign.prepare_for_signing_single( + result = await TurboSign.send_signature( recipients=[{"name": "John Doe", "email": "john@example.com", "signingOrder": 1}], fields=[{ "type": "signature", @@ -544,7 +544,7 @@ field: Dict[str, Any] = { ### Request Parameters -Request configuration for `prepare_for_review` and `prepare_for_signing_single` methods: +Request configuration for `create_signature_review_link` and `send_signature` methods:   From 6b4a88a88eef2ad08584bafbf39047a1c4c47e91 Mon Sep 17 00:00:00 2001 From: Amit Sharma Date: Wed, 10 Dec 2025 17:23:18 +0000 Subject: [PATCH 6/6] fix: updated main SDKs page with updated function names --- docs/SDKs/index.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/SDKs/index.md b/docs/SDKs/index.md index 045977a..caeb291 100644 --- a/docs/SDKs/index.md +++ b/docs/SDKs/index.md @@ -138,7 +138,7 @@ TurboSign.configure({ }); // Send a document for signature -const result = await TurboSign.prepareForSigningSingle({ +const result = await TurboSign.sendSignature({ fileLink: "https://example.com/contract.pdf", recipients: [ { name: "John Doe", email: "john@example.com", signingOrder: 1 }, @@ -172,7 +172,7 @@ TurboSign.configure({ }); // Send a document for signature -const result = await TurboSign.prepareForSigningSingle({ +const result = await TurboSign.sendSignature({ fileLink: "https://example.com/contract.pdf", recipients: [ { name: "John Doe", email: "john@example.com", signingOrder: 1 }, @@ -207,7 +207,7 @@ TurboSign.configure( ) # Send a document for signature -result = TurboSign.prepare_for_signing_single( +result = TurboSign.send_signature( file_link="https://example.com/contract.pdf", recipients=[ {"name": "John Doe", "email": "john@example.com", "signingOrder": 1} @@ -242,7 +242,7 @@ func main() { ) // Send a document for signature - result, err := client.PrepareForSigningSingle(context.Background(), &sdk.SigningRequest{ + result, err := client.TurboSign.SendSignature(context.Background(), &sdk.SendSignatureRequest{ FileLink: "https://example.com/contract.pdf", Recipients: []sdk.Recipient{ {Name: "John Doe", Email: "john@example.com", SigningOrder: 1}, @@ -275,7 +275,7 @@ public class Main { ); // Send a document for signature - SigningResult result = turboSign.prepareForSigningSingle( + SigningResult result = turboSign.sendSignature( SigningRequest.builder() .fileLink("https://example.com/contract.pdf") .recipient(Recipient.builder() @@ -309,10 +309,10 @@ All TurboDocx SDKs provide access to: Send documents for legally-binding eSignatures with full audit trails. -| Method | Description | -| :-------------------------- | :------------------------------------------------------ | -| `prepareForReview()` | Upload document for preview without sending emails | -| `prepareForSigningSingle()` | Upload and immediately send signature requests | +| Method | Description | +| :---------------------------- | :------------------------------------------------------ | +| `createSignatureReviewLink()` | Upload document for preview without sending emails | +| `sendSignature()` | Upload and immediately send signature requests | | `getStatus()` | Check document and recipient signing status | | `download()` | Download the completed signed document | | `void()` | Cancel/void a signature request | @@ -357,7 +357,7 @@ All SDKs provide structured error handling with detailed error codes: const { TurboSign, TurboDocxError } = require("@turbodocx/sdk"); try { - const result = await TurboSign.prepareForSigningSingle({ + const result = await TurboSign.sendSignature({ /* ... */ }); } catch (error) { @@ -378,7 +378,7 @@ try { import { TurboSign, TurboDocxError } from "@turbodocx/sdk"; try { - const result = await TurboSign.prepareForSigningSingle({ + const result = await TurboSign.sendSignature({ /* ... */ }); } catch (error) { @@ -400,7 +400,7 @@ from turbodocx import TurboSign from turbodocx.exceptions import TurboDocxError try: - result = await TurboSign.prepare_for_signing_single(...) + result = await TurboSign.send_signature(...) except TurboDocxError as e: print(f"Error {e.code}: {e.message}") if e.code == "VALIDATION_ERROR": @@ -412,7 +412,7 @@ except TurboDocxError as e: ```go -result, err := client.PrepareForSigningSingle(ctx, request) +result, err := client.TurboSign.SendSignature(ctx, request) if err != nil { var turboErr *sdk.TurboDocxError if errors.As(err, &turboErr) { @@ -433,7 +433,7 @@ import com.turbodocx.sdk.TurboDocxException; import com.turbodocx.sdk.TurboDocxException.*; try { - SigningResult result = turboSign.prepareForSigningSingle(/* ... */); + SigningResult result = turboSign.sendSignature(/* ... */); } catch (AuthenticationException e) { System.err.println("Invalid API key: " + e.getMessage()); } catch (ValidationException e) {