diff --git a/docs/TurboSign/Setting-up-TurboSign.md b/docs/TurboSign/Setting-up-TurboSign.md index 6ca41ad6..e6558388 100644 --- a/docs/TurboSign/Setting-up-TurboSign.md +++ b/docs/TurboSign/Setting-up-TurboSign.md @@ -501,6 +501,18 @@ After clicking "Send Document": 3. **You track progress** on your TurboSign dashboard as each person signs 4. **Everyone receives** a copy of the fully signed document, including you as the sender +### What Your Signers See + +When a signer opens the document, a **Pages** panel shows every page as a thumbnail. A mark sits wherever that signer has a field: indigo for a field they still need to fill, green for one that's done, and dashed for an optional one. Each page also shows how many required fields are left on it. Clicking a thumbnail jumps to that page, and the highlight follows the page the signer is reading. + +![The signing page on a desktop, with the Pages panel open on the left showing 6 pages, page 2 highlighted with 2 left, page 3 with 1 left, and the Needs you, Done and Optional legend](/img/turbosign/page-navigation/01-desktop-rail.png) + +- **Wide screens:** the panel is open on the left. +- **Narrower screens:** the panel collapses to slim tabs with a count badge. The signer can open it again, and their choice is remembered. +- **Tablets and phones:** a page button in the bottom bar (a grid icon with the current page, like **3/6**) opens the same thumbnails in a drawer or a bottom sheet. + +![The Pages bottom sheet on a phone, showing 4 required fields left and a row of page thumbnails with 2 left on page 2 and 1 left on page 3](/img/turbosign/page-navigation/02-phone-pages-sheet.png) +
## Congratulations! 🎉 diff --git a/docs/TurboSign/embedded-signing/build/own-iframe.md b/docs/TurboSign/embedded-signing/build/own-iframe.md index 869dcedc..c5b0edea 100644 --- a/docs/TurboSign/embedded-signing/build/own-iframe.md +++ b/docs/TurboSign/embedded-signing/build/own-iframe.md @@ -73,6 +73,10 @@ In your page, ask your server for a URL and set it as the iframe's `src`. Give t The browser code is the same whichever language your server uses. +The signing page shows a page thumbnail rail that follows the iframe's width, not the browser window's: open at 1200px and wider, collapsed to slim tabs from 900px to 1199px, and under 900px a page button in the bottom bar (a grid icon with the current page, like **3/6**). Picking a page scrolls only the document inside the iframe, never your page. To start the rail collapsed or hide it, pass `pageNavigation` when you request the signing URL. See [Page navigation](../reference.md#page-navigation). + +![The signing page in a host app's iframe, with the page thumbnail rail collapsed to slim tabs and count badges on the pages that still need the signer](/img/embedded-signing/page-navigation-iframe.png) + ## Step 4: Listen for the completion message When the signer finishes, the signing page posts a `turbosign:completed` message to your page. Accept it only when it comes from the TurboSign origin **and** from your own iframe. diff --git a/docs/TurboSign/embedded-signing/build/react-widget.md b/docs/TurboSign/embedded-signing/build/react-widget.md index 3c28f506..8a14557f 100644 --- a/docs/TurboSign/embedded-signing/build/react-widget.md +++ b/docs/TurboSign/embedded-signing/build/react-widget.md @@ -108,6 +108,8 @@ export function SignStep({ name, email }: { name: string; email: string }) { | `onDeclined`, `onError` | No | Reserved. The signing page does not send these events yet. | | `allowAnyOrigin` | No | Development only. Accepts messages from any origin. Never ship it. | +The widget has no prop for the signing page's page navigation. The thumbnail rail follows the widget's width (open at 1200px and wider, collapsed from 900px to 1199px, a page button in the bottom bar under 900px). To start it collapsed or hide it, pass `pageNavigation` on your server when you request the signing URL. See [Page navigation](../reference.md#page-navigation). + :::warning Don't leave origin empty The widget fails closed. If `origin` is missing, it ignores every message, logs a one-time warning in the console, and `onCompleted` never fires. `allowAnyOrigin` turns that off for local debugging, but then any frame on your page could fake a completion. ::: diff --git a/docs/TurboSign/embedded-signing/build/web-component.md b/docs/TurboSign/embedded-signing/build/web-component.md index e1f11c08..90c6e6cb 100644 --- a/docs/TurboSign/embedded-signing/build/web-component.md +++ b/docs/TurboSign/embedded-signing/build/web-component.md @@ -110,6 +110,8 @@ Add a route to your server, for example `POST /api/signing-session`. It calls `c | `title` | No | The iframe's accessible name. Defaults to `TurboSign signing`. | | `allow-any-origin` | No | Development only. Accepts messages from any origin. Never ship it. | +The element has no attribute for the signing page's page navigation. The thumbnail rail follows the element's width (open at 1200px and wider, collapsed from 900px to 1199px, a page button in the bottom bar under 900px). To start it collapsed or hide it, pass `pageNavigation` on your server when you request the signing URL. See [Page navigation](../reference.md#page-navigation). + The element re-emits `turbosign:completed` as a bubbling `CustomEvent`, so a listener on a parent element works too. It also defines `turbosign:declined` and `turbosign:error`, which the signing page does not send yet. :::warning Don't leave origin empty diff --git a/docs/TurboSign/embedded-signing/reference.md b/docs/TurboSign/embedded-signing/reference.md index f7363de1..d920d1e0 100644 --- a/docs/TurboSign/embedded-signing/reference.md +++ b/docs/TurboSign/embedded-signing/reference.md @@ -11,6 +11,7 @@ keywords: - identityVerification - signing-url endpoint - turbosign:completed + - pageNavigation - embedded signing errors - OtpOverrideNotAllowed --- @@ -47,6 +48,7 @@ Creates the document, adds each signer, and returns a signing URL for whoever si | `fields` | No | Full field list. When set, it replaces every recipient's `fields` shorthand. | | `sendEmail` | No | Defaults to `false`. | | `returnUrl` | No | `https` URL the signer returns to after signing. | +| `pageNavigation` | No | `auto` (default), `collapsed` or `hidden`. How the signing page shows its page thumbnails in your iframe. See [Page navigation](#page-navigation). | A recipient's `auth` asks for a passcode, and its `fields` places fields on text anchors such as `{signature1}`. Leave `auth` off for no passcode (the [organization default](#the-organization-default) then applies). @@ -106,6 +108,7 @@ REST: `POST /turbosign/documents/{documentId}/signing-url` |---|---|---| | `recipientId` or `externalId` | Exactly one | Picks the recipient. | | `returnUrl` | No | `https` URL the signer returns to after signing. See [Return URL](#return-url). | +| `pageNavigation` | No | `auto` (default), `collapsed` or `hidden`. How the signing page shows its page thumbnails in your iframe. See [Page navigation](#page-navigation). | | `identityAssertion` | For `external_idv` | Your provider's verification. See [External identity verification](../identity-verification/external-identity-verification.md). | Unknown keys are rejected. @@ -150,6 +153,24 @@ The REST response wraps the result in `data.results`. The SDKs return the inner - The signing page reads it back when it loads and redirects there after the signer finishes. - It is set per request. Requesting a new signing URL without `returnUrl` clears the one stored earlier. +#### Page navigation {#page-navigation} + +The signing page shows every page of the document as a thumbnail, marks where the signer's fields are, and counts the required fields left on each page. `pageNavigation` sets how that navigation appears in your iframe: + +| Value | What the signer sees | +|---|---| +| `auto` (default) | Sized to the iframe's width, not the browser window's: the thumbnail rail is open at 1200px and wider, collapsed to slim tabs from 900px to 1199px, and replaced under 900px by a page button in the bottom bar (a grid icon with the current page, like **3/6**). | +| `collapsed` | The rail starts collapsed at any width. The signer can still open it. | +| `hidden` | No rail and no page button in the bottom bar. | + +- Any other value is rejected with a `400`. +- It is set per request, like `returnUrl`. Requesting a new signing URL without `pageNavigation`, or with `auto`, resets it to `auto`. +- `createEmbeddedSignature` passes it to the signing URL it returns for the first signer. When you mint a URL for a later signer with `createSigningUrl`, pass it again. +- It applies only to signing URLs from `createSigningUrl` and `createEmbeddedSignature`. Signing links sent by email always behave as `auto`. +- When the signer picks a page, only the document inside the iframe scrolls, never your page. + +In the SDKs: `pageNavigation` in JavaScript / TypeScript (type `PageNavigationMode`), `page_navigation=` in Python, `PageNavigation` with the `PageNavigationAuto`, `PageNavigationCollapsed` and `PageNavigationHidden` constants in Go, the `PageNavigation` enum in PHP and Java, and `page_navigation:` on `create_signing_url` in Ruby (`pageNavigation:` in the `create_embedded_signature` request hash). + ### Example @@ -161,6 +182,7 @@ const { url, expiresAt, identityVerificationMode, pendingChecks } = await TurboSign.createSigningUrl(documentId, { externalId: "your_customer_123", // or recipientId, exactly one returnUrl: "https://app.yourcompany.com/signed", + pageNavigation: "collapsed", // optional: "auto" (default), "collapsed" or "hidden" }); ``` @@ -173,6 +195,7 @@ link = await TurboSign.create_signing_url( document_id, external_id="your_customer_123", # or recipient_id, exactly one return_url="https://app.yourcompany.com/signed", + page_navigation="collapsed", # optional: "auto" (default), "collapsed" or "hidden" ) url, pending_checks = link["url"], link["pendingChecks"] ``` @@ -181,11 +204,13 @@ url, pending_checks = link["url"], link["pendingChecks"] ```php +use TurboDocx\Types\PageNavigation; use TurboDocx\Types\Requests\CreateSigningUrlRequest; $link = TurboSign::createSigningUrl($documentId, new CreateSigningUrlRequest( externalId: 'your_customer_123', // or recipientId, exactly one - returnUrl: 'https://app.yourcompany.com/signed' + returnUrl: 'https://app.yourcompany.com/signed', + pageNavigation: PageNavigation::COLLAPSED // optional: AUTO (default), COLLAPSED or HIDDEN )); $url = $link->url; ``` @@ -196,8 +221,9 @@ $url = $link->url; ```go // Inside a function that returns error, with client and ctx as in "Your own iframe". pdf holds the PDF bytes. link, err := client.TurboSign.CreateSigningURL(ctx, documentID, &turbodocx.CreateSigningURLRequest{ - ExternalID: "your_customer_123", // or RecipientID, exactly one - ReturnURL: "https://app.yourcompany.com/signed", + ExternalID: "your_customer_123", // or RecipientID, exactly one + ReturnURL: "https://app.yourcompany.com/signed", + PageNavigation: turbodocx.PageNavigationCollapsed, // optional: Auto (default), Collapsed or Hidden }) if err != nil { return err @@ -215,6 +241,7 @@ CreateSigningUrlResponse link = client.turboSign().createSigningUrl( new CreateSigningUrlRequest.Builder() .externalId("your_customer_123") // or recipientId, exactly one .returnUrl("https://app.yourcompany.com/signed") + .pageNavigation(PageNavigation.COLLAPSED) // optional: AUTO (default), COLLAPSED or HIDDEN .build()); String url = link.getUrl(); ``` @@ -227,7 +254,8 @@ String url = link.getUrl(); link = TurboDocxSdk::TurboSign.create_signing_url( document_id, external_id: "your_customer_123", # or recipient_id:, exactly one - return_url: "https://app.yourcompany.com/signed" + return_url: "https://app.yourcompany.com/signed", + page_navigation: "collapsed" # optional: "auto" (default), "collapsed" or "hidden" ) url = link["url"] ``` @@ -239,8 +267,9 @@ url = link["url"] | Status | `code` | Meaning | |---|---|---| -| 400 | `RecipientSelectorInvalid`, `InvalidReturnUrl` | Send exactly one selector; `returnUrl` must be `https`. | +| 400 | `RecipientSelectorInvalid` | Send exactly one selector. | | 400 | `IdentityAssertion*`, `IdentityProviderMismatch`, `IdentityEmailMismatch` | The `external_idv` assertion is missing or fails a check. | +| 400 | none (`type` is `ValidationError`) | The body failed validation: `returnUrl` is not an `https` URL, or `pageNavigation` is not `auto`, `collapsed` or `hidden`. | | 403 | none (a plain `403 Forbidden`) | The API key belongs to a **User**. | | 403 | `EmbeddedSigningNotEnabled`, `ExternalIdvNotAllowed`, `IdentityOverrideNotAllowed` | The organization has not turned on what the recipient uses. | | 404 | `RecipientNotFound` | No recipient matches the selector. | @@ -523,7 +552,7 @@ Identity and passcode errors from sending a document and from `createSigningUrl` | 400 | `IdentityConfigInvalid`, `IdvProviderRequired`, `OverrideNotAcknowledged`, `IdentityModeConflict` | The recipient's `identityVerification` block is invalid or incomplete. | | 400 | `DuplicateExternalId` | Two recipients on the document share an `externalId`. | | 400 | `RecipientSelectorInvalid` | `createSigningUrl` needs exactly one of `recipientId` or `externalId`. | -| 400 | `InvalidReturnUrl` | `returnUrl` is not an `https` URL. | +| 400 | none (`type` is `ValidationError`) | The `createSigningUrl` body failed validation: a `returnUrl` that is not `https`, or a `pageNavigation` other than `auto`, `collapsed` or `hidden`. This body is `{ "message", "type", "data": { "errors" } }`, not the four-field body above. | | 400 | `IdentityAssertionRequired`, `IdentityAssertionInvalid`, `IdentityProviderMismatch`, `IdentityEmailMismatch`, `IdentityAssertionStale`, `IdentityAssertionReused` | The `external_idv` assertion is missing or fails a check (see [External identity verification](../identity-verification/external-identity-verification.md#errors)). | | 402 | `OtpNotEntitled`, `SmsOtpLimitExceeded` | The plan does not include this verification, or the SMS allowance is used up. | | 403 | none (a plain `403 Forbidden`) | The API key belongs to a **User**. Requesting a signing URL needs an **Administrator** or **Contributor** key. | diff --git a/docs/TurboSign/identity-verification/external-identity-verification.md b/docs/TurboSign/identity-verification/external-identity-verification.md index 512eb5f6..9dd2fb2f 100644 --- a/docs/TurboSign/identity-verification/external-identity-verification.md +++ b/docs/TurboSign/identity-verification/external-identity-verification.md @@ -418,7 +418,7 @@ url = link["url"] Open `url` right away: it is single-use and expires in about five minutes. To show it inside your app, hand it to the [React widget](../embedded-signing/build/react-widget.md), frame it in [your own iframe](../embedded-signing/build/own-iframe.md), or use the [web component](../embedded-signing/build/web-component.md). -The REST equivalent is `POST /turbosign/documents/{documentId}/signing-url` with the same body (`externalId` or `recipientId`, `identityAssertion`, optional `returnUrl`). Unknown keys are rejected. +The REST equivalent is `POST /turbosign/documents/{documentId}/signing-url` with the same body (`externalId` or `recipientId`, `identityAssertion`, optional `returnUrl`, optional `pageNavigation`). Unknown keys are rejected. ### Required assertion fields diff --git a/static/img/embedded-signing/page-navigation-iframe.png b/static/img/embedded-signing/page-navigation-iframe.png new file mode 100644 index 00000000..56717061 Binary files /dev/null and b/static/img/embedded-signing/page-navigation-iframe.png differ diff --git a/static/img/turbosign/page-navigation/01-desktop-rail.png b/static/img/turbosign/page-navigation/01-desktop-rail.png new file mode 100644 index 00000000..37a2c06f Binary files /dev/null and b/static/img/turbosign/page-navigation/01-desktop-rail.png differ diff --git a/static/img/turbosign/page-navigation/02-phone-pages-sheet.png b/static/img/turbosign/page-navigation/02-phone-pages-sheet.png new file mode 100644 index 00000000..078cae8a Binary files /dev/null and b/static/img/turbosign/page-navigation/02-phone-pages-sheet.png differ