From d7041ab198fd0790258811843f63cae201b275c6 Mon Sep 17 00:00:00 2001 From: Amit Sharma Date: Wed, 19 Aug 2026 18:02:51 +0530 Subject: [PATCH] feat(turbosign): add conditional (IF/THEN) fields to the SDK quickstart Adds a "Conditional (IF/THEN) fields" subsection to all six language references (a controlling checkbox with metadata.fieldKey and a dependent field with metadata.conditional { controllingFieldKey, operator, action }), updates the SKILL and README so the skill offers it, and adds an eval. Typed languages use the SDK's real types (Go structs, PHP enums, Java FieldMetadata factories). Co-Authored-By: Claude Opus 4.8 (1M context) --- README.md | 1 + evals/evals.json | 58 ++++++++++++++++++ skills/turbodocx-sdk/SKILL.md | 1 + skills/turbodocx-sdk/references/go.md | 52 ++++++++++++++++ skills/turbodocx-sdk/references/java.md | 43 +++++++++++++ skills/turbodocx-sdk/references/javascript.md | 38 ++++++++++++ skills/turbodocx-sdk/references/php.md | 60 +++++++++++++++++++ skills/turbodocx-sdk/references/python.md | 38 ++++++++++++ skills/turbodocx-sdk/references/ruby.md | 40 +++++++++++++ 9 files changed, 331 insertions(+) diff --git a/README.md b/README.md index 887bde5..c0804cd 100644 --- a/README.md +++ b/README.md @@ -138,6 +138,7 @@ Skip the product selection prompt: ### TurboSign Integration - Client configuration with env var loading - `sendSignature()`, `getStatus()`, `download()` — send, track, retrieve signed PDFs +- Conditional (IF/THEN) fields — a controlling `checkbox` plus dependent fields that show or unlock only when it is ticked (via optional field `metadata`) - Optional: `void()`, `resend()`, `getAuditTrail()` — cancellation, reminders, tamper-evident audit log - Route handlers wired into your existing app diff --git a/evals/evals.json b/evals/evals.json index 4394b11..76413ba 100644 --- a/evals/evals.json +++ b/evals/evals.json @@ -1229,6 +1229,64 @@ } ] }, + { + "id": 51, + "prompt": "Set up TurboSign in my Express app. On the signing document I need a conditional field: the signer ticks a checkbox to opt into relocation assistance, and only then should a second signature field become visible. Add an endpoint that sends this document.", + "expected_output": "Creates a TurboSign config file and a sendSignature endpoint whose fields array contains a controlling checkbox field with metadata.fieldKey and a dependent field with metadata.conditional (controllingFieldKey matching the checkbox fieldKey, operator is_checked, action show), wires routes into the main app, adds .env with TurboSign vars", + "files": [ + "package.json", + "tsconfig.json", + "src/index.ts", + "package-lock.json" + ], + "assertions": [ + { + "name": "config-file-created", + "type": "file_exists", + "description": "A TurboSign config/client file was created" + }, + { + "name": "uses-sendSignature", + "type": "file_contains", + "description": "Route handler calls TurboSign.sendSignature with a fields array" + }, + { + "name": "has-checkbox-field", + "type": "file_contains", + "description": "The fields array includes a field with type 'checkbox' acting as the controlling field" + }, + { + "name": "checkbox-has-fieldKey", + "type": "file_contains", + "description": "The controlling checkbox field carries metadata.fieldKey (a stable id, e.g. metadata: { fieldKey: '...' })" + }, + { + "name": "dependent-has-conditional", + "type": "file_contains", + "description": "A dependent field carries metadata.conditional with controllingFieldKey, operator, and action keys" + }, + { + "name": "controllingFieldKey-matches", + "type": "file_contains", + "description": "The dependent field's conditional.controllingFieldKey matches the controlling checkbox's metadata.fieldKey value exactly" + }, + { + "name": "uses-valid-operator-and-action", + "type": "file_contains", + "description": "conditional.operator is 'is_checked' or 'is_not_checked' and conditional.action is 'show' or 'unlock'" + }, + { + "name": "routes-wired", + "type": "file_contains", + "description": "Main app file (src/index.ts) was modified to import and register the signature routes" + }, + { + "name": "env-has-sign-vars", + "type": "file_contains", + "description": ".env contains TURBODOCX_API_KEY and TURBODOCX_SENDER_EMAIL" + } + ] + }, { "id": 10, "skill_name": "turbodocx-html-to-docx", diff --git a/skills/turbodocx-sdk/SKILL.md b/skills/turbodocx-sdk/SKILL.md index 9c48737..8ad0c03 100644 --- a/skills/turbodocx-sdk/SKILL.md +++ b/skills/turbodocx-sdk/SKILL.md @@ -183,6 +183,7 @@ Create working route handlers / endpoint code for the selected product(s). The l **For TurboSign, generate:** - `sendSignature()` endpoint — accepts file (or `fileLink` / `deliverableId` / `templateId`), recipients, fields +- If the user wants **conditional (IF/THEN) fields** — a field that shows or unlocks only when the signer ticks a box: add a controlling `checkbox` field carrying `metadata.fieldKey`, and a dependent field carrying `metadata.conditional` (`{ controllingFieldKey, operator: "is_checked" | "is_not_checked", action: "show" | "unlock" }`) whose `controllingFieldKey` matches the checkbox's `fieldKey`. `action: "show"` keeps the dependent field hidden until the condition is met; `action: "unlock"` shows it but read-only until met. `metadata` is optional and both live on the normal `sendSignature()` field array — see the language reference for the exact per-language shape. - `getStatus()` endpoint — check the document-level status by ID - `getRecipients()` endpoint — every recipient with their signing status, email history, and who sent the document. Generate this whenever the user wants to know **who has signed / who is still pending**; `getStatus()` alone cannot answer that. Note each recipient carries both `status` (raw: `pending`/`viewed`/`completed`) and `effectiveStatus` (adds `voided`/`expired`) — generated code should branch on `effectiveStatus`, since an unsigned signer on a voided document still reads `pending` in the raw field. - `download()` endpoint — stream signed PDF (returns `Blob`/`ArrayBuffer` per language) diff --git a/skills/turbodocx-sdk/references/go.md b/skills/turbodocx-sdk/references/go.md index 2d85a9c..166c31d 100644 --- a/skills/turbodocx-sdk/references/go.md +++ b/skills/turbodocx-sdk/references/go.md @@ -76,6 +76,58 @@ if err != nil { fmt.Printf("Document ID: %s\n", result.DocumentID) ``` +### Conditional (IF/THEN) fields + +Any field can be made to depend on a **controlling checkbox** so it only appears — or only becomes editable — once the signer ticks that box. Give the checkbox a stable `Metadata.FieldKey`, then reference that key from the dependent field's `Metadata.Conditional.ControllingFieldKey`. `Field.Metadata` is an **optional** `*turbodocx.FieldMetadata`; a nil `Metadata` (the default) behaves exactly as before. + +```go +result, err := client.TurboSign.SendSignature(ctx, &turbodocx.SendSignatureRequest{ + File: pdfFile, + FileName: "contract.pdf", + DocumentName: "Employment Agreement", + Recipients: []turbodocx.Recipient{ + {Name: "John Doe", Email: "john@example.com", SigningOrder: 1}, + }, + Fields: []turbodocx.Field{ + // Controlling checkbox — the box the signer ticks. Its FieldKey is the stable id others reference. + { + Type: "checkbox", + RecipientEmail: "john@example.com", + Page: 1, + X: 100, + Y: 400, + Width: 20, + Height: 20, + Metadata: &turbodocx.FieldMetadata{ + FieldKey: "relocation_optin", + }, + }, + // Dependent field — hidden until the box above is checked (Action: "show"). + { + Type: "signature", + RecipientEmail: "john@example.com", + Page: 1, + X: 100, + Y: 460, + Width: 200, + Height: 50, + Metadata: &turbodocx.FieldMetadata{ + Conditional: &turbodocx.FieldConditional{ + ControllingFieldKey: "relocation_optin", // = the checkbox's Metadata.FieldKey + Operator: "is_checked", // "is_checked" | "is_not_checked" + Action: "show", // "show" = hidden until met; "unlock" = visible but read-only until met + }, + }, + }, + }, +}) +if err != nil { + log.Fatal(err) +} +``` + +The link is `FieldKey` → `ControllingFieldKey`: the two strings must match exactly (the checkbox carries `Metadata.FieldKey`, the dependent field points at it via `Metadata.Conditional.ControllingFieldKey`). `Operator` chooses which checkbox state satisfies the condition — `"is_checked"` or `"is_not_checked"`. `Action` chooses what happens while the condition is unmet: `"show"` keeps the dependent field **hidden until met**, while `"unlock"` renders it **visible but read-only (locked) until met**. + ### GetStatus ```go diff --git a/skills/turbodocx-sdk/references/java.md b/skills/turbodocx-sdk/references/java.md index e208fbc..c4b0b43 100644 --- a/skills/turbodocx-sdk/references/java.md +++ b/skills/turbodocx-sdk/references/java.md @@ -77,6 +77,49 @@ SendSignatureResponse result = client.turboSign().sendSignature( System.out.println("Document ID: " + result.getDocumentId()); ``` +### Conditional (IF/THEN) fields + +Any field can be made to depend on a **controlling checkbox** so it only appears — or only becomes editable — once the signer ticks that box. Give the checkbox a stable `FieldMetadata` with a `fieldKey`, then reference that key from the dependent field's `FieldMetadata` → `FieldConditional` → `controllingFieldKey`. `.metadata(...)` on the builder is **optional**; a field left without it behaves exactly as before. + +```java +SendSignatureResponse result = client.turboSign().sendSignature( + new SendSignatureRequest.Builder() + .file(pdfFile) + .fileName("contract.pdf") + .documentName("Employment Agreement") + .recipients(Arrays.asList( + new Recipient("John Doe", "john@example.com", 1) + )) + .fields(Arrays.asList( + // Controlling checkbox — the box the signer ticks. Its fieldKey is the stable id others reference. + new Field.Builder() + .type("checkbox") + .recipientEmail("john@example.com") + .page(1) + .x(100).y(400).width(20).height(20) + .metadata(FieldMetadata.forFieldKey("relocation_optin")) + .build(), + // Dependent field — hidden until the box above is checked (action "show"). + new Field.Builder() + .type("signature") + .recipientEmail("john@example.com") + .page(1) + .x(100).y(460).width(200).height(50) + .metadata(FieldMetadata.forConditional( + new FieldConditional( + "relocation_optin", // controllingFieldKey = the checkbox's metadata fieldKey + "is_checked", // "is_checked" | "is_not_checked" + "show"))) // "show" = hidden until met; "unlock" = visible but read-only until met + .build() + )) + .build() +); + +System.out.println("Document ID: " + result.getDocumentId()); +``` + +The link is `fieldKey` → `controllingFieldKey`: the two strings must match exactly (the checkbox carries `FieldMetadata.fieldKey`, the dependent field points at it via `FieldConditional.controllingFieldKey`). `operator` chooses which checkbox state satisfies the condition — `"is_checked"` or `"is_not_checked"`. `action` chooses what happens while the condition is unmet: `"show"` keeps the dependent field **hidden until met**, while `"unlock"` renders it **visible but read-only (locked) until met**. + ### getStatus ```java diff --git a/skills/turbodocx-sdk/references/javascript.md b/skills/turbodocx-sdk/references/javascript.md index c824a72..bc6209b 100644 --- a/skills/turbodocx-sdk/references/javascript.md +++ b/skills/turbodocx-sdk/references/javascript.md @@ -102,6 +102,44 @@ console.log(result.recipients); // ReviewRecipient[] with { id, name, email, m Fields support either coordinate-based (`page` + `x` / `y` / `width` / `height`) or anchor-based placement via `template: { anchor: '{TagName}', placement: 'replace', size: {...} }`. +### Conditional (IF/THEN) fields + +Any field can be made to depend on a **controlling checkbox** so it only appears — or only becomes editable — once the signer ticks that box. Give the checkbox a stable `metadata.fieldKey`, then reference that key from the dependent field's `metadata.conditional.controllingFieldKey`. Both live in an **optional** `metadata` object on the field; fields without it behave exactly as before. + +```typescript +const result = await TurboSign.sendSignature({ + file: pdfBuffer, + documentName: 'Employment Agreement', + recipients: [ + { name: 'John Doe', email: 'john@example.com', signingOrder: 1 }, + ], + fields: [ + // Controlling checkbox — the box the signer ticks. Its metadata.fieldKey is the stable id others reference. + { + type: 'checkbox', + page: 1, x: 100, y: 400, width: 20, height: 20, + recipientEmail: 'john@example.com', + metadata: { fieldKey: 'relocation_optin' }, + }, + // Dependent field — hidden until the box above is checked (action: 'show'). + { + type: 'signature', + page: 1, x: 100, y: 460, width: 200, height: 50, + recipientEmail: 'john@example.com', + metadata: { + conditional: { + controllingFieldKey: 'relocation_optin', // = the checkbox's metadata.fieldKey + operator: 'is_checked', // 'is_checked' | 'is_not_checked' + action: 'show', // 'show' = hidden until met; 'unlock' = visible but read-only until met + }, + }, + }, + ], +}); +``` + +The link is `fieldKey` → `controllingFieldKey`: the two strings must match exactly (the checkbox carries `metadata.fieldKey`, the dependent field points at it via `metadata.conditional.controllingFieldKey`). `operator` chooses which checkbox state satisfies the condition — `is_checked` or `is_not_checked`. `action` chooses what happens while the condition is unmet: `show` keeps the dependent field **hidden until met**, while `unlock` renders it **visible but read-only (locked) until met**. + ### TurboSign.getStatus ```typescript diff --git a/skills/turbodocx-sdk/references/php.md b/skills/turbodocx-sdk/references/php.md index 1c25691..7036083 100644 --- a/skills/turbodocx-sdk/references/php.md +++ b/skills/turbodocx-sdk/references/php.md @@ -83,6 +83,66 @@ $result = TurboSign::sendSignature( echo "Document ID: {$result->documentId}\n"; ``` +### Conditional (IF/THEN) fields + +Any field can be made to depend on a **controlling checkbox** so it only appears — or only becomes editable — once the signer ticks that box. Give the checkbox a stable `metadata` with a `fieldKey`, then reference that key from the dependent field's `metadata->conditional->controllingFieldKey`. The `metadata:` argument on `Field` is **optional**; a `Field` without it behaves exactly as before. + +```php +use TurboDocx\TurboSign; +use TurboDocx\Types\Recipient; +use TurboDocx\Types\Field; +use TurboDocx\Types\SignatureFieldType; +use TurboDocx\Types\FieldMetadata; +use TurboDocx\Types\FieldConditional; +use TurboDocx\Types\ConditionalOperator; +use TurboDocx\Types\ConditionalAction; +use TurboDocx\Types\Requests\SendSignatureRequest; + +$result = TurboSign::sendSignature( + new SendSignatureRequest( + file: file_get_contents('contract.pdf'), + documentName: 'Employment Agreement', + recipients: [ + new Recipient('John Doe', 'john@example.com', 1), + ], + fields: [ + // Controlling checkbox — the box the signer ticks. Its fieldKey is the stable id others reference. + new Field( + type: SignatureFieldType::CHECKBOX, + recipientEmail: 'john@example.com', + page: 1, + x: 100, + y: 400, + width: 20, + height: 20, + metadata: new FieldMetadata(fieldKey: 'relocation_optin'), + ), + // Dependent field — hidden until the box above is checked (action: "show"). + new Field( + type: SignatureFieldType::SIGNATURE, + recipientEmail: 'john@example.com', + page: 1, + x: 100, + y: 460, + width: 200, + height: 50, + metadata: new FieldMetadata( + conditional: new FieldConditional( + controllingFieldKey: 'relocation_optin', // = the checkbox's metadata fieldKey + operator: ConditionalOperator::IS_CHECKED, // ::IS_CHECKED | ::IS_NOT_CHECKED + action: ConditionalAction::SHOW, // ::SHOW = hidden until met; ::UNLOCK = visible but read-only until met + ), + ), + ), + ], + ) +); + +echo "Document ID: {$result->documentId}\n"; +``` + +The link is `fieldKey` → `controllingFieldKey`: the two strings must match exactly (the checkbox carries `metadata->fieldKey`, the dependent field points at it via `metadata->conditional->controllingFieldKey`). `operator` chooses which checkbox state satisfies the condition — `'is_checked'` or `'is_not_checked'`. `action` chooses what happens while the condition is unmet: `'show'` keeps the dependent field **hidden until met**, while `'unlock'` renders it **visible but read-only (locked) until met**. + ### getStatus ```php diff --git a/skills/turbodocx-sdk/references/python.md b/skills/turbodocx-sdk/references/python.md index 13980d3..352b59a 100644 --- a/skills/turbodocx-sdk/references/python.md +++ b/skills/turbodocx-sdk/references/python.md @@ -76,6 +76,44 @@ result = await TurboSign.send_signature( print(f"Document ID: {result['documentId']}") ``` +### Conditional (IF/THEN) fields + +Any field can be made to depend on a **controlling checkbox** so it only appears — or only becomes editable — once the signer ticks that box. Give the checkbox a stable `metadata["fieldKey"]`, then reference that key from the dependent field's `metadata["conditional"]["controllingFieldKey"]`. Both live in an **optional** `metadata` dict on the field; fields without it behave exactly as before. Note the keys inside `metadata` stay camelCase (`fieldKey`, `controllingFieldKey`) — they are forwarded to the API verbatim. + +```python +result = await TurboSign.send_signature( + file=pdf_file, + document_name="Employment Agreement", + recipients=[ + {"name": "John Doe", "email": "john@example.com", "signingOrder": 1}, + ], + fields=[ + # Controlling checkbox — the box the signer ticks. Its metadata.fieldKey is the stable id others reference. + { + "type": "checkbox", + "page": 1, "x": 100, "y": 400, "width": 20, "height": 20, + "recipientEmail": "john@example.com", + "metadata": {"fieldKey": "relocation_optin"}, + }, + # Dependent field — hidden until the box above is checked (action: "show"). + { + "type": "signature", + "page": 1, "x": 100, "y": 460, "width": 200, "height": 50, + "recipientEmail": "john@example.com", + "metadata": { + "conditional": { + "controllingFieldKey": "relocation_optin", # = the checkbox's metadata.fieldKey + "operator": "is_checked", # "is_checked" | "is_not_checked" + "action": "show", # "show" = hidden until met; "unlock" = visible but read-only until met + }, + }, + }, + ], +) +``` + +The link is `fieldKey` → `controllingFieldKey`: the two strings must match exactly (the checkbox carries `metadata["fieldKey"]`, the dependent field points at it via `metadata["conditional"]["controllingFieldKey"]`). `operator` chooses which checkbox state satisfies the condition — `is_checked` or `is_not_checked`. `action` chooses what happens while the condition is unmet: `show` keeps the dependent field **hidden until met**, while `unlock` renders it **visible but read-only (locked) until met**. + ### get_status ```python diff --git a/skills/turbodocx-sdk/references/ruby.md b/skills/turbodocx-sdk/references/ruby.md index 48b47c4..fe82802 100644 --- a/skills/turbodocx-sdk/references/ruby.md +++ b/skills/turbodocx-sdk/references/ruby.md @@ -106,6 +106,46 @@ result["recipients"].each { |r| puts "#{r['name']} <#{r['email']}> #{r['id']}" } Fields support either coordinate-based placement (`page` + `x`/`y`/`width`/`height`) or anchor-based placement via `template: { anchor: "{signature1}", placement: "replace", size: { width: 100, height: 30 } }`. The anchor text must literally exist in the document. +### Conditional (IF/THEN) fields + +Any field can be made to depend on a **controlling checkbox** so it only appears — or only becomes editable — once the signer ticks that box. Give the checkbox a stable `metadata` with a `fieldKey`, then reference that key from the dependent field's `metadata[:conditional][:controllingFieldKey]`. The `metadata:` hash is **optional**; a field without it behaves exactly as before. As everywhere in this SDK, the keys **inside** `metadata` stay camelCase (`fieldKey`, `controllingFieldKey`) — they are forwarded to the API verbatim, so a snake_case key silently drops the value. + +```ruby +result = TurboDocxSdk::TurboSign.send_signature( + fileLink: "https://example.com/contract.pdf", + documentName: "Employment Agreement", + recipients: [ + { name: "Alice", email: "alice@example.com", signingOrder: 1 } + ], + fields: [ + # Controlling checkbox — the box the signer ticks. Its fieldKey is the stable id others reference. + { + type: "checkbox", + page: 1, x: 100, y: 400, width: 20, height: 20, + recipientEmail: "alice@example.com", + metadata: { fieldKey: "relocation_optin" } + }, + # Dependent field — hidden until the box above is checked (action: "show"). + { + type: "signature", + page: 1, x: 100, y: 460, width: 200, height: 50, + recipientEmail: "alice@example.com", + metadata: { + conditional: { + controllingFieldKey: "relocation_optin", # = the checkbox's metadata fieldKey + operator: "is_checked", # "is_checked" | "is_not_checked" + action: "show" # "show" = hidden until met; "unlock" = visible but read-only until met + } + } + } + ] +) + +puts result["documentId"] +``` + +The link is `fieldKey` → `controllingFieldKey`: the two strings must match exactly (the checkbox carries `metadata[:fieldKey]`, the dependent field points at it via `metadata[:conditional][:controllingFieldKey]`). `operator` chooses which checkbox state satisfies the condition — `"is_checked"` or `"is_not_checked"`. `action` chooses what happens while the condition is unmet: `"show"` keeps the dependent field **hidden until met**, while `"unlock"` renders it **visible but read-only (locked) until met**. + ### TurboSign.get_status ```ruby