diff --git a/packages/apps/connector-docusign/README.md b/packages/apps/connector-docusign/README.md new file mode 100644 index 00000000000..a7dc456b7f2 --- /dev/null +++ b/packages/apps/connector-docusign/README.md @@ -0,0 +1,197 @@ +# DocuSign eSignature + +Send documents out for signature, read envelopes and follow their signature +status from your flows. The app calls DocuSign's eSignature REST API in the +DocuSign **developer sandbox** (`https://demo.docusign.net/restapi`) through a +connector built from DocuSign's own OpenAPI document, and exposes three +envelope operations as connector actions. It signs in to DocuSign with OAuth, +using an integration key your administrator registers for your deployment: +the app carries no key of anyone's. + +## What it adds + +- **A connector, `docusign`.** Its actions are DocuSign's own operation ids: + + | Action | Request | What it does | + |:---|:---|:---| + | `Envelopes_PostEnvelopes` | `POST /v2.1/accounts/{accountId}/envelopes` | Creates an envelope; with `status: "sent"` DocuSign emails it to the signers at once | + | `Envelopes_GetEnvelope` | `GET /v2.1/accounts/{accountId}/envelopes/{envelopeId}` | Reads one envelope, its `status` included | + | `Envelopes_GetEnvelopes` | `GET /v2.1/accounts/{accountId}/envelopes` | Lists envelopes, for example those whose status changed since `from_date` | + + No other DocuSign operation is in the package, so none can reach your flows + or agents. +- **An object, `docusign_envelope`**, one record per envelope the app sent: + `envelope_id` (DocuSign's envelope id), `signature_status` (DocuSign's own + status word: `sent`, `delivered`, `completed`, `declined`, `voided`, …), + `email_subject` and `status_changed_at`. The status is DocuSign's, mirrored + each time a flow below reads it. Its list view, **DocuSign Envelopes**, + shows the newest status change first. +- **Three flows that use them**, ready to run or to copy: + - `docusign_send_envelope` takes `account_id`, `email_subject`, + `document_name` (for example `contract.pdf`), `document_base64`, + `signer_name` and `signer_email`, sends the document to that signer and + records the envelope. DocuSign's answer is the run's `send.body` output. + - `docusign_get_envelope` takes `account_id` and `envelope_id`, reads the + envelope and refreshes its record's `signature_status`. DocuSign's + envelope is the run's `envelope.body` output. + - `docusign_poll_status_changes` takes `account_id` and `from_date` (an ISO + 8601 instant), lists the envelopes whose status changed since then (at + most 100) and copies each status onto its record. DocuSign's list is the + run's `changes.body` output. See [Callbacks](#callbacks) for why this + poll exists. + +`account_id` is your DocuSign account's API account id (a GUID): in the +DocuSign developer console it is **API Account ID** on **Apps and Keys**. + +An install adds the connector, the object, its list view and the three +flows, and nothing else: no sample data and no demo users. + +## Credentials + +The app needs one OAuth client, which **you register with DocuSign for your +own deployment**. ObjectStack ships none: a DocuSign OAuth client is an +integration key that accepts only the redirect URIs registered on it, and the +redirect URI belongs to your deployment. Until an administrator connects the +`docusign` connector, it is listed as degraded "not connected" and its flows +fail, naming the connector. + +| What | What it is | Where to get it | +|:---|:---|:---| +| Integration key | DocuSign's client id for your app (a GUID) | DocuSign developer console () → **Apps and Keys** → **Add App and Integration Key** | +| Secret key | The client secret DocuSign issues for that integration key, shown once | The same app → **Authentication** → **Add Secret Key** | +| Redirect URI | Your deployment's OAuth callback, which DocuSign must accept | The connector's connection read (below); register it on the same app under **Additional settings** → **Redirect URIs** | + +1. Read your deployment's redirect URI. It is your deployment's origin plus + `/api/v1/automation/connectors/oauth/callback`, and the server answers the + exact value, before any client exists, as `redirectUri` of + `GET /api/v1/automation/connectors/docusign/connection`; the Console's + **Connect** dialog (step 3) shows the same value. Use that answer, never + your browser's address bar: behind a proxy, or when `OS_AUTH_URL` / + `OS_BASE_URL` name another origin, the two differ. +2. In the DocuSign developer console, add an app and integration key, choose + **Authorization Code Grant**, add a secret key, and add the redirect URI + from step 1. The app needs the `signature` scope, which is DocuSign's + default for the authorization code grant. +3. Connect the connector: on **Setup → Integrations → Connector Connections**, + choose **Connect** on `docusign` and enter the integration key and the + secret key; or begin the connect yourself with + `POST /api/v1/automation/connectors/docusign/connection` and a body of + `{ "clientId": "", "clientSecret": "" }`, then + open the `authorizationUrl` it answers. Sign in to DocuSign and allow + access; DocuSign returns to your redirect URI and the connection is + stored. + +Connecting takes the organization-scoped `manage_connector_connections` +capability (organization owners and admins hold it), from a signed-in +session; an agent or an API key is refused. Each organization connects once. +The platform keeps the integration key and its secret encrypted on the +organization's connection, shows the key but never the secret, and uses them +for the authorization, the code exchange and every token refresh. A reconnect +that names no client reuses the stored one; disconnecting removes it. + +The token is only ever sent to DocuSign. Neither it nor your secret key is +stored in the app's metadata. + +## Running it + +```bash +curl -X POST "$OS_URL/api/v1/automation/docusign_send_envelope/trigger" \ + -H "Authorization: Bearer $SESSION_TOKEN" -H 'Content-Type: application/json' \ + -d '{"params": {"account_id": "", "email_subject": "Please sign the contract", + "document_name": "contract.pdf", "document_base64": "", + "signer_name": "Ada Lovelace", "signer_email": "ada@example.com"}}' +``` + +To use an answer elsewhere, add a `connector_action` node with +`connectorId: 'docusign'` to your own flow and read `.body` in a later +node. An action takes its path parameters as one `path` object, its query +parameters as one `query` object and its request body as one `body` value, +for example +`path: { dialect: 'cel', source: '{"accountId": record.docusign_account_id, "envelopeId": record.envelope_id}' }`. + +A contract app that follows its own records' signatures keeps the same two +fields on its record (`envelope_id`, `signature_status`) and copies them from +the `docusign_envelope` record, or from the action's answer, in its own flow. + +A DocuSign error answer does not fail the step: it lands as +`.status`, `.ok` false and DocuSign's error in +`.body`. The three flows check `ok` and write no record when DocuSign +refused the call. + +## Callbacks + +DocuSign can post envelope events to a URL you name (DocuSign Connect). This +app does **not** take those posts, and you should not point Connect at a +deployment's `api` flow door: Connect signs each post with its own HMAC +header, and the `api` door verifies only the platform's own signature +scheme, so a Connect post cannot be told from a forged one there. Until the +platform verifies vendor callback schemes, follow status changes by polling: +run `docusign_poll_status_changes` on a schedule, passing the time of the +previous run as `from_date`, or `docusign_get_envelope` for one envelope. + +## Limits + +- **Sandbox only.** The connector points at DocuSign's developer environment: + sign-in at `account-d.docusign.com`, API calls at `demo.docusign.net`. A + production DocuSign account signs in at `account.docusign.com` and has an + API host of its own (DocuSign answers it as `base_uri` from + `/oauth/userinfo`), which this package cannot name for you; production is + not supported yet. +- One document and one signer per `docusign_send_envelope` run. The action + itself takes DocuSign's whole envelope definition; copy the flow to send + more. +- `docusign_poll_status_changes` reads one page of at most 100 changed + envelopes. Poll often enough that one page covers the interval. +- Not measured live: the tests run against a fake DocuSign. A round trip + against the DocuSign sandbox (connect, send, read, poll) is owed to the + acceptance run, and the DocuSign console steps under Credentials follow + DocuSign's documentation. +- The request body and response schemas in the packaged document are not + expanded (see Provenance): `Envelopes_PostEnvelopes` takes DocuSign's + envelope definition as one JSON object, described by its type and + description only, so its fields are DocuSign's documented ones (the + `docusign_send_envelope` flow shows the minimal set). The answer itself is + DocuSign's full JSON. + +## Provenance + +`openapi/docusign-esignature.openapi.json` is generated, not written by hand. +It is DocuSign's eSignature REST API document, +`docusign/OpenAPI-Specifications` `esignature.rest.swagger-v2.1.json` at +commit `858a3ae59b0edbc8beea4fa3a6d7fe803833dd68` (Swagger 2.0, 414 +operations), converted to OpenAPI 3.0 and trimmed to the three operations +above by this repository's offline normalizer: + +```bash +pnpm --filter @objectstack/connector-openapi normalize-openapi \ + --in https://raw.githubusercontent.com/docusign/OpenAPI-Specifications/858a3ae59b0edbc8beea4fa3a6d7fe803833dd68/esignature.rest.swagger-v2.1.json \ + --include-pattern '^Envelopes_(PostEnvelopes|GetEnvelope|GetEnvelopes)$' \ + --trim --empty-consumes application/json --max-depth 0 \ + --out packages/apps/connector-docusign/openapi/docusign-esignature.openapi.json +``` + +It printed OpenAPI 3.0.4, 304,080 bytes, 3 operations trimmed from 414, 187 +empty `consumes` lists filled with `application/json`, 7 schema references cut +at `--max-depth 0` and none left, and sha256 +`b741ded5cfc4d0291d7f0e26559f8583f8c9a783f8711d8f9a98405cb27f7cb4`, the hash +of the committed file. The package's tests recompute that hash and fail when +the file and this record disagree. + +`--max-depth 0` keeps every parameter schema as DocuSign wrote it, and +replaces each named schema (the envelope definition the create operation +takes, and each response) with its type, title and description. At the +default depth of 2 the same three operations write 553,922 bytes (sha256 +`e1051eb4bbc381d879e979beac8cb64ead24531c93a19fc26e950a12d9fc961d`), with the +envelope definition's fields expanded two levels. + +`--empty-consumes application/json` is required: DocuSign's document clears +the request media type on its operations (`consumes: []`), which would leave +`Envelopes_PostEnvelopes` with no request body at all. The document's +`servers[0].url` is DocuSign's production host, +`https://www.docusign.net/restapi`; the connector's `providerConfig.baseUrl` +points it at the sandbox instead. The document declares no security scheme, +so the OAuth token is sent on every operation. + +Re-normalize from a newer commit by running the command with that commit, +then update the commit and the sha256 here in the same change. DocuSign's +document is MIT-licensed; its licence notice is `openapi/LICENSE`. diff --git a/packages/apps/connector-docusign/assets/icon.svg b/packages/apps/connector-docusign/assets/icon.svg new file mode 100644 index 00000000000..951676db611 --- /dev/null +++ b/packages/apps/connector-docusign/assets/icon.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/packages/apps/connector-docusign/objectstack.config.ts b/packages/apps/connector-docusign/objectstack.config.ts new file mode 100644 index 00000000000..6a9549c3514 --- /dev/null +++ b/packages/apps/connector-docusign/objectstack.config.ts @@ -0,0 +1,46 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +import { defineStack } from '@objectstack/spec'; +import { ConnectorOpenApiPlugin } from '@objectstack/connector-openapi'; +import { DocuSignConnector } from './src/connectors/docusign.connector.js'; +import { DocuSignEnvelope } from './src/objects/docusign-envelope.object.js'; +import { DocuSignEnvelopeViews } from './src/views/docusign-envelope.view.js'; +import { allFlows } from './src/flows/docusign.flows.js'; + +/** + * DocuSign eSignature connector app — open edition (every key it uses runs on + * the open tier), so it lives in this repository and is validated by its CI + * against the runtime at HEAD. One declarative connector over DocuSign's own + * OpenAPI document, authorized by OAuth with the OAuth client each + * deployment's administrator registers (ADR-0143 D2.1, `clientSource: + * 'installer'`), one object that keeps each envelope's signature status (and the + * list view that shows it), and three flows that use them; no secret and no client of anyone's. The + * listing is `objectstack.manifest.json`, and the README is what the + * administrator reads before installing. + */ +export default defineStack({ + manifest: { + id: 'app.objectstack.connector-docusign', + namespace: 'docusign', + version: '0.1.0', + type: 'app', + name: 'DocuSign eSignature', + description: 'Send documents for signature, read envelopes and follow their signature status from flows, over DocuSign\'s eSignature OpenAPI document (developer sandbox).', + engines: { protocol: '^18' }, + }, + + // `automation` materializes the `connectors:` entry at boot, serves the + // connection door an administrator connects it through, and runs the + // flows; the executor below registers the `openapi` provider with it. + requires: ['automation'], + + // The generic `openapi` executor, zero-arg: it contributes the provider + // factory only, which reads the packaged document through the host's + // package-file capability (confined to this package's root). + plugins: [new ConnectorOpenApiPlugin()], + + objects: [DocuSignEnvelope], + views: [DocuSignEnvelopeViews], + connectors: [DocuSignConnector], + flows: allFlows, +}); diff --git a/packages/apps/connector-docusign/objectstack.manifest.json b/packages/apps/connector-docusign/objectstack.manifest.json new file mode 100644 index 00000000000..764c091cc74 --- /dev/null +++ b/packages/apps/connector-docusign/objectstack.manifest.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://schemas.objectstack.dev/template-manifest.json", + "manifestId": "app.objectstack.connector-docusign", + "name": "connector-docusign", + "specVersion": "^18.0.0", + "displayName": "DocuSign eSignature", + "description": "Send documents for signature, read envelopes and follow their signature status from flows, over DocuSign's eSignature OpenAPI document. Developer sandbox; your administrator registers their own DocuSign integration key.", + "category": "productivity", + "tags": ["docusign", "esignature", "openapi", "connector", "oauth"], + "license": "Apache-2.0", + "iconUrl": "https://raw.githubusercontent.com/objectstack-ai/objectstack/main/packages/apps/connector-docusign/assets/icon.svg", + "homepageUrl": "https://github.com/objectstack-ai/objectstack/tree/main/packages/apps/connector-docusign", + "readmePath": "README.md" +} diff --git a/packages/apps/connector-docusign/openapi/LICENSE b/packages/apps/connector-docusign/openapi/LICENSE new file mode 100644 index 00000000000..0db5a518a74 --- /dev/null +++ b/packages/apps/connector-docusign/openapi/LICENSE @@ -0,0 +1,22 @@ +The MIT License + +Copyright (c) 2017- DocuSign, Inc. (https://www.docusign.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/packages/apps/connector-docusign/openapi/docusign-esignature.openapi.json b/packages/apps/connector-docusign/openapi/docusign-esignature.openapi.json new file mode 100644 index 00000000000..51161070549 --- /dev/null +++ b/packages/apps/connector-docusign/openapi/docusign-esignature.openapi.json @@ -0,0 +1,1438 @@ +{ + "info": { + "version": "v2.1", + "title": "Docusign eSignature REST API", + "description": "The Docusign eSignature REST API provides you with a powerful, convenient, and simple Web services API for interacting with Docusign.", + "termsOfService": "https://www.docusign.com/company/terms-and-conditions/web", + "contact": { + "name": "Docusign Developer Center", + "url": "https://developers.docusign.com/", + "email": "devcenter@docusign.com" + } + }, + "externalDocs": { + "description": "See the Docusign eSignature REST API Guide for more information.", + "url": "https://docs.docusign.com/esign" + }, + "paths": { + "/v2.1/accounts/{accountId}/envelopes": { + "get": { + "tags": [ + "Envelopes" + ], + "summary": "Search for specific sets of envelopes by using search filters.", + "description": "This method lets you [search for envelopes](/docs/esign-rest-api/esign101/concepts/envelopes/search/) in your accounts. A large set of filters let you narrow the scope of your search by date, by envelope ID, or by status codes. Your request must include one or more of the following parameters:\n\n* `from_date`\n* `envelope_ids`\n* `transaction_ids`\n\n### Restrictions\n\nThe number of envelopes returned is limited to 1,000 per call. To retrieve the next or previous set of envelopes, use the `nextUri` and `previousUri` parameters returned in the original call's response.\n\nIf no `from_date` query parameter is specified, envelopes from more than two years ago will not be returned. To fetch older envelopes, set the specific date range using the `from_date` and `to_date` parameters.\n\nTo avoid unnecessary database queries, the Docusign signature platform first checks requests to ensure that the filter set supplied does not result in a zero-size response before querying the database.\n\n### Envelope statuses\n\nThis table shows the valid current envelope statuses (`status` parameter) for the different status qualifiers (`from_to_status` parameter) in the request. If the status and status qualifiers in the API request do not contain any of the values shown in the Valid Current Statuses column, then an empty list is returned.\n\nClient applications should check that the statuses (`status` parameter) they are requesting make sense for a given `from_to_status` parameter value.\n\n| Status Qualifier
(`from_to_status`) | Effective Status Qualifier | Valid Current Statuses |\n| :------------------------------------- | :------------------------- | :-------------------------------------------------------------------------- |\n| any (changed) | StatusChanged | any, created, sent, delivered, signed, completed, declined, voided, deleted |\n| created | Created | any, created, sent, delivered, signed, completed, declined, voided, deleted |\n| sent | Sent | any, sent, delivered, signed, completed, declined, voided, deleted |\n| delivered | StatusChanged | any, delivered, signed, completed, declined, voided, deleted |\n| signed | StatusChanged | any, signed, completed, declined, voided, deleted |\n| completed | Completed | any, completed, declined, voided, deleted |\n| declined | StatusChanged | any, declined, voided, deleted |\n| timedout
always return zero results | StatusChanged | any, voided, deleted |\n| voided | Voided | any, voided, deleted |\n| deleted | StatusChanged | any, deleted |\n\n### Extraneous results\n\nIn some cases, a request for a specific envelope status will\ninclude envelopes with additional statuses. For example, in\na request with a `from_date` of 2017-01-01, a `to_date` of\n2017-01-07 and the status qualifier (`from_to_status`) set\nto `delivered`, the response set might contain envelopes\nthat were created during that time period, but not delivered\nduring the time period. As a workaround, check the envelope\nstatus values in the result set as needed.\n\n\n### Related topics\n\n- [Searching for envelopes](/docs/esign-rest-api/esign101/concepts/envelopes/search/)\n- [How to list envelope status changes](/docs/esign-rest-api/how-to/list-envelope-status-changes/)\n", + "operationId": "Envelopes_GetEnvelopes", + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "accountId", + "in": "path", + "description": "The external account number (int) or account ID GUID.", + "required": true + }, + { + "schema": { + "type": "string" + }, + "name": "ac_status", + "in": "query", + "description": "Specifies the authoritative copy status for the envelopes. Valid values: \n* `Unknown`\n* `Original`\n* `Transferred`\n* `AuthoritativeCopy`\n* `AuthoritativeCopyExportPending`\n* `AuthoritativeCopyExported`\n* `DepositPending`\n* `Deposited`\n* `DepositedEO`\n* `DepositFailed`", + "required": false + }, + { + "schema": { + "type": "string" + }, + "name": "block", + "in": "query", + "required": false, + "description": "Reserved for Docusign.\n" + }, + { + "schema": { + "type": "string" + }, + "name": "cdse_mode", + "in": "query", + "required": false, + "description": "Reserved for Docusign.\n" + }, + { + "schema": { + "type": "string" + }, + "name": "continuation_token", + "in": "query", + "required": false, + "description": "Reserved for Docusign." + }, + { + "schema": { + "type": "string" + }, + "name": "count", + "in": "query", + "required": false, + "description": "The maximum number of results to return.\n\nThe maximum value is 1000. To get the next or previous set of envelopes, use `nextUri` or `previousUri` from the response.\n" + }, + { + "schema": { + "type": "string" + }, + "name": "custom_field", + "in": "query", + "description": "Optional. Specifies an envelope custom field name and value searched for in the envelopes. Format: `custom_envelope_field_name=desired_value`\n\nExample: If you have an envelope custom field named \"Region\" and you want to search for all envelopes where the value is \"West\" you would use set this parameter to `Region=West`. \n \n", + "required": false + }, + { + "schema": { + "type": "string" + }, + "name": "email", + "in": "query", + "required": false, + "description": "Limit results to envelopes\nsent by the account user\nwith this email address.\n\n`user_name` must be given as well,\nand both `email` and `user_name`\nmust refer to an existing account user.\n" + }, + { + "schema": { + "type": "string" + }, + "name": "envelope_ids", + "in": "query", + "required": false, + "description": "Comma separated list of `envelopeId` values." + }, + { + "schema": { + "type": "string" + }, + "name": "exclude", + "in": "query", + "required": false, + "description": "Excludes information from the response. Enter as a comma-separated list (e.g., `folders,powerforms`). \n\nValid values:\n\n- `recipients`\n- `powerforms`\n- `folders`\n" + }, + { + "schema": { + "type": "string" + }, + "name": "folder_ids", + "in": "query", + "required": false, + "description": "Returns the envelopes from specific folders. Enter as a comma-separated list of either valid folder GUIDs or the following values: \n\n- `awaiting_my_signature`\n- `completed`\n- `draft`\n- `drafts`\n- `expiring_soon`\n- `inbox`\n- `out_for_signature`\n- `recyclebin`\n- `sentitems`\n- `waiting_for_others`" + }, + { + "schema": { + "type": "string" + }, + "name": "folder_types", + "in": "query", + "required": false, + "description": "Returns the envelopes from folders of a specific type. Enter as a comma-separated list of the following values:\n\n- `normal`\n- `inbox`\n- `sentitems`\n- `draft`\n- `templates`" + }, + { + "schema": { + "type": "string" + }, + "name": "from_date", + "in": "query", + "description": "Specifies the date and time to start looking for status changes. This parameter is required unless `envelopeIds` or `transactionIds` are set.\n\nAlthough you can use any date format supported by the .NET system library's [`DateTime.Parse()`][msoft] function, Docusign recommends using [ISO 8601][] format dates with an explicit time zone offset. If you do not provide a time zone offset, the method uses the server's time zone.\n\nFor example, the following dates and times refer to the same instant:\n\n* `2017-05-02T01:44Z`\n* `2017-05-01T21:44-04:00`\n* `2017-05-01T18:44-07:00`\n\nIf this property is not included, envelopes from the last two years will be returned.\n\n[msoft]: https://docs.microsoft.com/en-us/dotnet/api/system.datetime.parse?redirectedfrom=MSDN&view=net-5.0#overloads\n[ISO 8601]: https://en.wikipedia.org/wiki/ISO_8601\n", + "required": false + }, + { + "schema": { + "type": "string" + }, + "name": "from_to_status", + "in": "query", + "description": "This is the status type checked for in the `from_date`/`to_date` period. For example, if `Created` is specified, then envelopes created during the period are found. If `Changed` is specified, then envelopes that changed status during the period are returned. The default value is `Changed`.\n\nValid values:\n* `Changed`\n* `Voided`\n* `Created`\n* `Deleted`\n* `Sent`\n* `Delivered`\n* `Signed`\n* `Completed`\n* `Declined`\n* `TimedOut`\n* `Processing`\n", + "required": false + }, + { + "schema": { + "type": "string" + }, + "name": "include", + "in": "query", + "required": false, + "description": "Specifies additional information to return about the envelopes.\nUse a comma-separated list, such as `folders, recipients` to specify information.\nValid values are:\n\n- `custom_fields`: The custom fields associated with the envelope.\n- `documents`: The documents associated with the envelope.\n- `attachments`: The attachments associated with the envelope.\n- `extensions`: Information about the email settings associated with the envelope.\n- `folders`: The folders where the envelope exists.\n- `recipients`: The recipients associated with the envelope.\n- `payment_tabs`: The payment tabs associated with the envelope.\n" + }, + { + "schema": { + "type": "string" + }, + "name": "include_purge_information", + "in": "query", + "required": false, + "description": "When **true,** information about envelopes that have been deleted is included in the response." + }, + { + "schema": { + "type": "string" + }, + "name": "intersecting_folder_ids", + "in": "query", + "required": false, + "description": "A comma-separated list of folders from which you want to get envelopes. Valid values: \n\n- `normal`\n- `inbox`\n- `sentitems`\n- `draft`\n- `templates`\n" + }, + { + "schema": { + "type": "string" + }, + "name": "last_queried_date", + "in": "query", + "required": false, + "description": "Returns envelopes that were modified prior to the specified date and time. \n\nExample: `2020-05-09T21:56:12.2500000Z`" + }, + { + "schema": { + "type": "string" + }, + "name": "order", + "in": "query", + "required": false, + "description": "Returns envelopes in either ascending (`asc`) or descending (`desc`) order." + }, + { + "schema": { + "type": "string" + }, + "name": "order_by", + "in": "query", + "required": false, + "description": "Sorts results according to a specific property. Valid values:\n\n- `last_modified`\n- `action_required`\n- `created`\n- `completed`\n- `envelope_name`\n- `expire`\n- `sent`\n- `signer_list`\n- `status`\n- `subject`\n- `user_name`\n- `status_changed`\n- `last_modified`" + }, + { + "schema": { + "type": "string" + }, + "name": "powerformids", + "in": "query", + "required": false, + "description": "A comma-separated list of `PowerFormId` values." + }, + { + "schema": { + "type": "string" + }, + "name": "query_budget", + "in": "query", + "required": false, + "description": "The time in seconds that the query should run before returning data." + }, + { + "schema": { + "type": "string" + }, + "name": "requester_date_format", + "in": "query", + "required": false, + "description": "" + }, + { + "schema": { + "type": "string" + }, + "name": "search_mode", + "in": "query", + "required": false, + "description": "" + }, + { + "schema": { + "type": "string" + }, + "name": "search_text", + "in": "query", + "required": false, + "description": "Free text search criteria that you can use to filter the list of envelopes that is returned." + }, + { + "schema": { + "type": "string" + }, + "name": "start_position", + "in": "query", + "required": false, + "description": "The zero-based index of the\nresult from which to start returning results.\n\nUse with `count` to limit the number\nof results.\n\nThe default value is `0`.\n" + }, + { + "schema": { + "type": "string" + }, + "name": "status", + "in": "query", + "description": "A comma-separated list of current envelope statuses to be included in the response. Valid values:\n\n* `completed`\n* `created`\n* `declined`\n* `deleted`\n* `delivered`\n* `processing`\n* `sent`\n* `signed`\n* `timedout`\n* `voided`\n\nThe `any` value is equivalent to any status.\n", + "required": false + }, + { + "schema": { + "type": "string" + }, + "name": "to_date", + "in": "query", + "description": "Specifies the date and time\nto stop looking for status changes.\nThe default is the current date and time.\n\nAlthough you can use any date format\nsupported by the .NET system library's\n[`DateTime.Parse()`][msoft] function,\nDocusign recommends\nusing [ISO 8601][] format dates\nwith an explicit time zone offset\nIf you do not provide\na time zone offset,\nthe method uses the server's time zone.\n\nFor example, the following dates and times refer to the same instant:\n\n* `2017-05-02T01:44Z`\n* `2017-05-01T21:44-04:00`\n* `2017-05-01T18:44-07:00`\n\n\n[msoft]: https://docs.microsoft.com/en-us/dotnet/api/system.datetime.parse?redirectedfrom=MSDN&view=net-5.0#overloads\n[ISO 8601]: https://en.wikipedia.org/wiki/ISO_8601\n", + "required": false + }, + { + "schema": { + "type": "string" + }, + "name": "transaction_ids", + "in": "query", + "description": "A comma-separated list of envelope transaction IDs.\n\nGetting envelope status by transaction IDs is useful for offline signing situations to determine if an envelope was created or not. It can be used for the cases where a network connection was lost before the envelope status could be returned.\n\n**Note:** Transaction IDs are only valid in the Docusign system for seven days.\n", + "required": false + }, + { + "schema": { + "type": "string" + }, + "name": "user_filter", + "in": "query", + "required": false, + "description": "Returns envelopes where the current user is the recipient, the sender, or the recipient only. (For example, `user_filter=sender`.) Valid values are:\n\n- `sender`\n- `recipient`\n- `recipient_only`" + }, + { + "schema": { + "type": "string" + }, + "name": "user_id", + "in": "query", + "required": false, + "description": "The ID of the user who created the envelopes to be retrieved. Note that an account can have multiple users, and any user with account access can retrieve envelopes by user_id from the account." + }, + { + "schema": { + "type": "string" + }, + "name": "user_name", + "in": "query", + "required": false, + "description": "Limit results to envelopes\nsent by the account user\nwith this user name.\n\n`email` must be given as well,\nand both `email` and `user_name`\nmust refer to an existing account user.\n" + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "type": "object", + "description": "Result set for the Envelopes: listStatusChanges method\n\n(Not expanded by normalize-openapi: '#/components/schemas/envelopesInformation', deeper than --max-depth 0.)" + } + } + } + }, + "400": { + "description": "Error encountered.", + "content": { + "application/json": { + "schema": { + "type": "object", + "description": "This object describes errors that occur. It is only valid for responses and ignored in requests.\n\n(Not expanded by normalize-openapi: '#/components/schemas/errorDetails', deeper than --max-depth 0.)" + } + } + } + } + }, + "deprecated": false, + "x-ds-methodname": "listStatusChanges", + "x-ds-method": "listStatusChanges", + "x-ds-service": "Envelopes", + "x-ds-in-sdk": true + }, + "post": { + "tags": [ + "Envelopes" + ], + "summary": "Creates an envelope.", + "description": "Creates and sends an envelope or creates a draft envelope.\nEnvelopes are fundamental resources in the Docusign platform.\n\n\nWith this method you can:\n\n* Create and send an envelope\n with [documents][], [recipients][], and [tabs][].\n* [Create and send an envelope from a template](/docs/esign-rest-api/esign101/concepts/templates/).\n* [Create and send an envelope from\n a combination of documents and templates](/docs/esign-rest-api/esign101/concepts/templates/composite/).\n* Create a draft envelope.\n\n\nWhen you use this method\nto create and send an envelope\nin a single request,\nthe following parameters in the request body (an [`envelopeDefinition`][envelopeDefinition] object) are required:\n\n| Parameter | Description |\n| :-------- | :---------- |\n| `status` | Set to `sent` to send the envelope to recipients.
Set to `created` (or don't set at all) to save the envelope as a draft. |\n| `emailSubject` | The subject of the email used to send the envelope. |\n| `documents` | The [documents][] to be signed. |\n| `recipients` | The email addresses of the envelope [recipients][]. |\n\n\nWhen you create an envelope by using a\n[composite template](/docs/esign-rest-api/esign101/concepts/templates/composite/),\nyou should specify the envelope custom fields in the inline template.\nAny custom fields that you specify at the root level are ignored.\n\nIf the envelope has a workflow definition\nand the `workflowStatus` is `paused`,\nthe envelope will not be sent immediately,\neven if the envelope's `status` is `sent`.\n\n\n### Related topics\n\n[Envelope][envelopes] and [template][templates]\nobjects along with [documents][documents],\n[recipients][recipients], and [tabs][tabs]\nare the five object models at the core of the eSignature API.\nThe [eSignature concepts guide](/docs/esign-rest-api/esign101/concepts/)\ndescribes how the five object models work together.\n\nThe following how-to articles contain\npractical examples that show you how to\nto\nconfigure this method's\n[`envelopeDefinition`][envelopeDefinition] request body\nto perform common tasks.\n\nRequesting a signature\n\n- [How to request a signature by email](/docs/esign-rest-api/how-to/request-signature-email-remote/)\n- [How to request a signature through your app](/docs/esign-rest-api/how-to/request-signature-in-app-embedded/)\n- [How to request a signature by email using a template](/docs/esign-rest-api/how-to/request-signature-template-remote/)\n- [How to request a signature using a composite template](/docs/esign-rest-api/how-to/request-signature-composite-template-embedded/)\n- [How to request a signature by SMS or WhatsApp delivery](/docs/esign-rest-api/how-to/request-signature-sms-whatsapp/)\n- [How to send a request for payment](/docs/esign-rest-api/how-to/request-a-payment/)\n- [How to send an envelope to an In Person Signer](/docs/esign-rest-api/how-to/send-envelope-to-in-person-signer/)\n- [How to request a signature by email using CORS](/docs/esign-rest-api/how-to/request-signature-email-browser-cors/)\n- [How to request a signature through your CORS-enabled browser app](/docs/esign-rest-api/how-to/request-signature-in-cors-app-embedded/)\n- [How to request a signature through your app (embedded signing) with a CFR Part 11 account](/docs/esign-rest-api/how-to/request-signature-in-app-embedded-cfrpart11/)\n\nWorking with envelopes and templates\n\n- [How to get envelope information](/docs/esign-rest-api/how-to/get-envelope-information/)\n- [How to list envelope recipients](/docs/esign-rest-api/how-to/get-envelope-recipients/)\n- [How to list envelope status changes](/docs/esign-rest-api/how-to/list-envelope-status-changes/)\n- [How to create a template](/docs/esign-rest-api/how-to/create-template/)\n- [How to send an envelope via your app](/docs/esign-rest-api/how-to/embedded-sending/)\n- [How to bulk send envelopes](/docs/esign-rest-api/how-to/bulk-send-envelopes/)\n\nWorking with advanced recipient routing\n\n- [How to pause a signature workflow](/docs/esign-rest-api/how-to/pause-workflow/)\n- [How to unpause a signature workflow](/docs/esign-rest-api/how-to/unpause-workflow/)\n- [How to use conditional recipients](/docs/esign-rest-api/how-to/use-conditional-recipients/)\n- [How to schedule an envelope](/docs/esign-rest-api/how-to/schedule-an-envelope/)\n- [How to send an envelope with delayed routing](/docs/esign-rest-api/how-to/send-envelope-with-delayed-routing/)\n\nWorking with documents\n\n- [How to list envelope documents](/docs/esign-rest-api/how-to/list-envelope-documents/)\n- [How to download envelope documents](/docs/esign-rest-api/how-to/download-envelope-documents/)\n- [How to attach documents via binary transfer](/docs/esign-rest-api/how-to/send-binary/)\n- [How to create a signable HTML document](/docs/esign-rest-api/how-to/creating-signable-html/)\n- [How to convert a PDF file into a signable HTML document](/docs/esign-rest-api/how-to/converting-pdf/)\n- [How to set document visibility for envelope recipients](/docs/esign-rest-api/how-to/set-document-visibility/)\n- [How to request a signature by email with document generation](/docs/esign-rest-api/how-to/request-signature-email-document-generation/)\n\nWorking with tabs\n\n- [How to get envelope tab values](/docs/esign-rest-api/how-to/get-envelope-tab-values/)\n- [How to get envelope custom tab values](/docs/esign-rest-api/how-to/get-envelope-custom-tab-values/)\n- [How to set envelope tab values](/docs/esign-rest-api/how-to/set-envelope-tab-values/)\n- [How to set tab values in a template](/docs/esign-rest-api/how-to/set-template-tab-values/)\n\nWorking with brands\n\n- [How to create a brand](/docs/esign-rest-api/how-to/create-brand/)\n- [How to apply a brand to an envelope](/docs/esign-rest-api/how-to/apply-brand-to-envelope/)\n- [How to apply a brand and template to an envelope](/docs/esign-rest-api/how-to/apply-brand-and-template-to-envelope/)\n\nWorking with permissions\n\n- [How to create a permission profile](/docs/esign-rest-api/how-to/permission-profile-creating/)\n- [How to update individual permission settings](/docs/esign-rest-api/how-to/permission-profile-updating/)\n- [How to set a permission profile](/docs/esign-rest-api/how-to/permission-profile-setting/)\n- [How to delete a permission profile](/docs/esign-rest-api/how-to/permission-profile-deleting/)\n\nImplementing multi-factor recipient (signer) authentication\n\n- [How to require ID verification (IDV) for a recipient](/docs/esign-rest-api/how-to/id-verification/)\n- [How to require knowledge-based authentication (KBA) for a recipient](/docs/esign-rest-api/how-to/knowledge-based-authentication/)\n- [How to require phone authentication for a recipient](/docs/esign-rest-api/how-to/phone-auth/)\n- [How to require access code authentication for a recipient](/docs/esign-rest-api/how-to/require-access-code-recipient/)\n\n\n\n\n\n[addingdocs]: /docs/esign-rest-api/esign101/concepts/envelopes/\n[attachments]: /docs/esign-rest-api/esign101/concepts/documents/attachments/\n[authcopies]: /docs/esign-rest-api/esign101/concepts/documents/authoritative-copies/\n[conoverview]: /docs/esign-rest-api/esign101/concepts/overview/\n[deleting]: /docs/esign-rest-api/esign101/concepts/envelopes/\n[documents]: /docs/esign-rest-api/esign101/concepts/documents/\n[envelopeDefinition]: /docs/esign-rest-api/reference/envelopes/envelopes/create/#schema__envelopedefinition\n[envelopes]: /docs/esign-rest-api/esign101/concepts/envelopes/\n[locking]: /docs/esign-rest-api/esign101/concepts/envelopes/lock/\n[payments]: /docs/esign-rest-api/esign101/concepts/tabs/payment/\n[purging]: /docs/esign-rest-api/esign101/concepts/documents/purging/\n[recipients]: /docs/esign-rest-api/esign101/concepts/recipients/\n[recipstatus]: /docs/esign-rest-api/esign101/concepts/recipients/#recipient-status\n[reciptypes]: /docs/esign-rest-api/esign101/concepts/recipients/#recipient-types\n[supdocs]: /docs/esign-rest-api/esign101/concepts/documents/supplemental/\n[tabanchor]: /docs/esign-rest-api/esign101/concepts/tabs/auto-place/\n[tabcustom]: /docs/esign-rest-api/esign101/concepts/tabs/custom-tabs/\n[tabs]: /docs/esign-rest-api/esign101/concepts/tabs/\n[tabtypes]: /docs/esign-rest-api/esign101/concepts/tabs/\n[templates]: /docs/esign-rest-api/esign101/concepts/templates/\n[tracking]: /docs/esign-rest-api/esign101/concepts/envelopes/\n\n", + "operationId": "Envelopes_PostEnvelopes", + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "accountId", + "in": "path", + "description": "The external account number (int) or account ID GUID.", + "required": true + }, + { + "schema": { + "type": "string" + }, + "name": "cdse_mode", + "in": "query", + "required": false, + "description": "Reserved for Docusign.\n" + }, + { + "schema": { + "type": "string" + }, + "name": "change_routing_order", + "in": "query", + "required": false, + "description": "When true, users can define the routing order of recipients while sending documents for signature." + }, + { + "schema": { + "type": "string" + }, + "name": "completed_documents_only", + "in": "query", + "description": "Reserved for Docusign.\n", + "required": false + }, + { + "schema": { + "type": "string" + }, + "name": "merge_roles_on_draft", + "in": "query", + "description": "When **true,** template roles will be merged, and empty recipients will be removed. This parameter applies when you create a draft envelope with multiple templates. (To create a draft envelope, the `status` field is set to `created`.)\n\n**Note:** Docusign recommends that this parameter should be set to **true** whenever you create a draft envelope with multiple templates.", + "required": false + } + ], + "responses": { + "201": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "type": "object", + "description": " This object describes an envelope.\n\n(Not expanded by normalize-openapi: '#/components/schemas/envelopeSummary', deeper than --max-depth 0.)" + } + } + } + }, + "400": { + "description": "Error encountered.", + "content": { + "application/json": { + "schema": { + "type": "object", + "description": "This object describes errors that occur. It is only valid for responses and ignored in requests.\n\n(Not expanded by normalize-openapi: '#/components/schemas/errorDetails', deeper than --max-depth 0.)" + } + } + } + } + }, + "deprecated": false, + "x-ds-methodname": "createEnvelope", + "x-ds-method": "create", + "x-ds-service": "Envelopes", + "x-ds-in-sdk": true, + "x-ds-examples": [ + { + "description": "This example shows a request/response that includes:\n\n* A single PDF document to be signed\n* One tab positioned using anchor text\n* One recipient\n", + "direction": "both", + "format": "json", + "response": { + "envelopeId": "63e05468-xxxx-xxxx-xxxx-8b48f7dbeb80", + "status": "sent", + "statusDateTime": "2016-05-29T17:11:56.1800000Z", + "uri": "/envelopes/63e05468-xxxx-xxxx-xxxx-8b48f7dbeb80" + }, + "request": { + "documents": [ + { + "documentBase64": "[Document content (PDF File)]", + "documentId": "1", + "fileExtension": "pdf", + "name": "NDA.pdf" + } + ], + "emailSubject": "Please sign the NDA", + "recipients": { + "signers": [ + { + "email": "the_nda_signer@example.com", + "name": "Darlene Petersen", + "recipientId": "1", + "routingOrder": "1", + "tabs": { + "dateSignedTabs": [ + { + "anchorString": "signer1date", + "anchorYOffset": "-6", + "fontSize": "Size12", + "name": "Date Signed", + "recipientId": "1", + "tabLabel": "date_signed" + } + ], + "fullNameTabs": [ + { + "anchorString": "signer1name", + "anchorYOffset": "-6", + "fontSize": "Size12", + "name": "Full Name", + "recipientId": "1", + "tabLabel": "Full Name" + } + ], + "signHereTabs": [ + { + "anchorString": "signer1sig", + "anchorUnits": "mms", + "anchorXOffset": "0", + "anchorYOffset": "0", + "name": "Please sign here", + "optional": "false", + "recipientId": "1", + "scaleValue": 1, + "tabLabel": "signer1sig" + } + ] + } + } + ] + }, + "status": "sent" + }, + "style": "custom", + "title": "Request Signature on Single Document by Email" + }, + { + "description": "This example shows a request/response that includes:\n\n* Multiple documents, both pdf and Word docx files.\n* Multiple tabs including signing, fullName, dateSigned, and text tabs.\n* Tabs positioned using anchor text.\n* Two recipients, a signer and a cc recipient.\n", + "direction": "both", + "format": "json", + "response": { + "envelopeId": "63e05468-xxxx-xxxx-xxxx-8b48f7dbeb80", + "status": "sent", + "statusDateTime": "2016-05-29T17:11:56.1800000Z", + "uri": "/envelopes/63e05468-xxxx-xxxx-xxxx-8b48f7dbeb80" + }, + "request": { + "documents": [ + { + "documentBase64": "[Document 1 content (PDF File)]", + "documentId": "1", + "fileExtension": "pdf", + "name": "NDA.pdf" + }, + { + "documentBase64": "[Document 2 content (PDF File)]", + "documentId": "2", + "fileExtension": "pdf", + "name": "House.pdf" + }, + { + "documentBase64": "[Document 3 content (Word file)]", + "documentId": "3", + "fileExtension": "docx", + "name": "contractor_agreement.docx" + } + ], + "emailSubject": "Please sign the house documentation package", + "recipients": { + "carbonCopies": [ + { + "email": "cody_vigil@worldwidecorp.example.com", + "name": "Cody Vigil", + "recipientId": "2", + "routingOrder": "2" + } + ], + "signers": [ + { + "email": "darlene_petersen@newco.example.com", + "name": "Darlene Petersen", + "recipientId": "1", + "routingOrder": "1", + "tabs": { + "dateSignedTabs": [ + { + "anchorString": "signer1date", + "anchorYOffset": "-6", + "fontSize": "Size12", + "name": "Date Signed", + "recipientId": "1", + "tabLabel": "date_signed" + }, + { + "documentId": "2", + "fontSize": "Size12", + "name": "Date Signed", + "pageNumber": "1", + "recipientId": "1", + "tabLabel": "doc3_date_signed", + "xPosition": "89", + "yPosition": "100" + } + ], + "fullNameTabs": [ + { + "anchorString": "signer1name", + "anchorYOffset": "-6", + "fontSize": "Size12", + "name": "Full Name", + "recipientId": "1", + "tabLabel": "Full Name" + } + ], + "signHereTabs": [ + { + "anchorString": "signer1sig", + "anchorUnits": "mms", + "anchorXOffset": "0", + "anchorYOffset": "0", + "name": "Please sign here", + "optional": "false", + "recipientId": "1", + "scaleValue": 1, + "tabLabel": "signer1sig" + }, + { + "documentId": "2", + "name": "Please sign here", + "optional": "false", + "pageNumber": "1", + "recipientId": "2", + "scaleValue": 1, + "tabLabel": "signer1_doc2", + "xPosition": "89", + "yPosition": "40" + }, + { + "anchorString": "Client Signature", + "anchorUnits": "mms", + "anchorXOffset": "0", + "anchorYOffset": "-4", + "name": "Please sign here", + "optional": "false", + "recipientId": "1", + "scaleValue": 1, + "tabLabel": "doc3_client_sig" + } + ], + "textTabs": [ + { + "anchorString": "signer1company", + "anchorYOffset": "-8", + "fontSize": "Size12", + "name": "Company", + "recipientId": "1", + "required": "true", + "tabLabel": "Company" + }, + { + "anchorString": "Client Name", + "anchorYOffset": "-38", + "fontSize": "Size12", + "name": "Company", + "recipientId": "1", + "required": "true", + "tabLabel": "Company" + }, + { + "documentId": "3", + "fontSize": "Size10", + "name": "Company", + "pageNumber": "1", + "recipientId": "1", + "required": "true", + "tabLabel": "Company", + "xPosition": "145", + "yPosition": "195" + } + ] + } + } + ] + }, + "status": "sent" + }, + "style": "custom", + "title": "Multiple Documents and Tabs, Signer and CC Recipients" + }, + { + "description": "For some documents, one or more signatures must be witnessed by an appointed [notary public](https://en.wikipedia.org/wiki/Notary_public).\n\nDocusign supports eNotary services for many jurisdictions. This example demonstrates how to send an envelope to be signed by Susan Signer with her signature witnessed by Nancy Notary. You must know the name and email address of the signer and the notary public for a signature to be notarized.\n\nNote that the full legal name of the signer must be used. It will be checked against the signer's government-issued proof of identity by the notary public.\n\nPlease see the [Docusign eNotary resource page](https://support.docusign.com/s/document-item?bundleId=gko1642535666104&topicId=jiv1635359045452.html) for further information. It lists the jurisdictions participating in the Docusign eNotary program and additional reference information.\n\nNote that eNotary services are not included with all account types and are **not** included by default with demo/developer sandbox accounts. To enable eNotary service for your sandbox account, contact your Docusign technical support person or Docusign customer service.\n\nThe example includes event notification via the Connect service.", + "direction": "both", + "format": "json", + "response": { + "envelopeId": "aaaaaaaa-xxxx-xxxx-xxxx-52a86fb94be8", + "uri": "/envelopes/aaaaaaaa-xxxx-xxxx-xxxx-52a86fb94be8", + "statusDateTime": "2017-06-15T11:02:39.3996430Z", + "status": "sent" + }, + "request": { + "emailSubject": "Important agreement for notarized signature", + "status": "sent", + "recipients": { + "inPersonSigners": [ + { + "recipientId": 1, + "email": "susan_signer@gmail.com", + "name": "Susan Anne Signer", + "routingOrder": 1, + "inPersonSigningType": "notary", + "notaryHost": { + "name": "Nancy Notary", + "email": "nancy@notary-svc.com", + "recipientId": 2 + }, + "tabs": { + "signHereTabs": [ + { + "documentId": "1", + "optional": "false", + "recipientId": "1", + "xPosition": "100", + "yPosition": "100", + "pageNumber": "1", + "name": "Please sign here", + "tabLabel": "signer1sig" + } + ] + } + } + ] + }, + "documents": [ + { + "documentId": "1", + "name": "important document.docx", + "fileExtension": "docx", + "documentBase64": "[Contents elided]" + } + ], + "eventNotification": { + "includeDocumentFields": "false", + "envelopeEvents": [ + { + "envelopeEventStatusCode": "Completed" + }, + { + "envelopeEventStatusCode": "Declined" + }, + { + "envelopeEventStatusCode": "Voided" + } + ], + "url": "https://listener.example.com/docusign_notifications", + "requireAcknowledgment": "true", + "includeSenderAccountAsCustomField": "true", + "loggingEnabled": "true", + "includeDocuments": "false", + "signMessageWithX509Cert": "true" + } + }, + "style": "custom", + "title": "Create a Notarized Signing Recipient" + }, + { + "description": "Docusign enables document signers to use\nelectronic or\ndigital signatures.\n\nDocusign has tightly integrated standard digital signatures into the Docusign Signature platform. This enables a single envelope to include:\n\n* electronic signatures,\n* AES digital signatures using certificates from Docusign or from your organization,\n* QES digital signatures from government certified Trust Service Providers (TSPs).\n\nThis example enables the signer to create an eIDAS compliant AES signature with embedded signing. InPerson signers can also use SBS digital signatures.\n\n[More information on creating SBS digital signature requests.](/docs/esign-rest-api/esign101/concepts/standards-based-signatures/)\n\nThe `clientId` in the example tells Docusign that the Signing Ceremony will be embedded.\nThe example also includes an `eventNotification` object for receiving envelope status updates from Docusign.\n\n## Embedding the signing ceremony\n1. Send the `Envelopes: create` call as shown below in the Request/Response section.\n2. The response will include the `envelopeId`\n3. Use the [EnvelopeViews: createRecipient](/docs/esign-rest-api/reference/envelopes/envelopeviews/createrecipient/) request to obtain the redirectURL as follows:\n\n`POST /v2.1/accounts/{accountId}/envelopes/{envelopeId}/views/recipient`\n\n````\n{\n \"clientUserId\": \"1000\",\n \"email\": \"Sam@spade.com\",\n \"userName\": \"Sam Spade\",\n \"returnUrl\": \"https://your_app.example.com\",\n \"AuthenticationMethod\": \"Password\"\n}\n````\nThe response will include the `url`. Redirect the user's browser to the URL to start the signing ceremony.\n\n**Note:** You must **immediately** redirect the user to the URL you receive from Docusign. The URL is only valid for 5 minutes. Don't request a recipient view URL until you are ready to redirect the user's browser.\n", + "direction": "both", + "format": "json", + "response": { + "envelopeId": "caaaaaaa-xxxx-xxxx-xxxx-c171528e99c8", + "uri": "/envelopes/caaaaaaa-xxxx-xxxx-xxxx-c171528e99c8", + "statusDateTime": "2017-06-22T06:44:49.1642220Z", + "status": "sent" + }, + "request": { + "documents": [ + { + "documentId": "1", + "name": " Agreement", + "fileExtension": "html", + "documentBase64": "[Contents elided]" + } + ], + "emailSubject": "NewCo agreement for signature", + "status": "sent", + "recipients": { + "signers": [ + { + "recipientId": "1", + "clientUserId": "1000", + "email": "Sam@spade.com", + "name": "Sam Spade", + "recipientSignatureProviders": [ + { + "signatureProviderName": "UniversalSignaturePen_OpenTrust_Hash_TSP", + "signatureProviderOptions": { + "SMS": "+33134567899" + } + } + ], + "routingOrder": "1", + "tabs": { + "signHereTabs": [ + { + "documentId": "1", + "optional": "false", + "recipientId": "1", + "name": "Please sign here", + "tabLabel": "signer1sig", + "anchorString": "signer1sig" + } + ] + } + } + ] + }, + "eventNotification": { + "includeDocumentFields": "false", + "envelopeEvents": [ + { + "envelopeEventStatusCode": "Completed" + }, + { + "envelopeEventStatusCode": "Declined" + }, + { + "envelopeEventStatusCode": "Voided" + } + ], + "url": "https://your_app.example.com/listener", + "requireAcknowledgment": "true", + "includeSenderAccountAsCustomField": "true", + "loggingEnabled": "true", + "includeDocuments": "false", + "signMessageWithX509Cert": "true" + } + }, + "style": "custom", + "title": "Request an SBS digital signature, with an embedded signing ceremony" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "description": "Envelope object definition.\n\n(Not expanded by normalize-openapi: '#/components/schemas/envelopeDefinition', deeper than --max-depth 0.)" + } + } + }, + "required": false, + "description": "" + } + }, + "parameters": [] + }, + "/v2.1/accounts/{accountId}/envelopes/{envelopeId}": { + "get": { + "tags": [ + "Envelopes" + ], + "summary": "Gets the status of a single envelope.", + "description": "Retrieves the overall status for the specified envelope.\nTo get the status of a list of envelopes, use\n[Envelope: listStatusChanges ](/docs/esign-rest-api/reference/envelopes/envelopes/liststatuschanges/).\n\n### Related topics\n\n- [How to get envelope information](/docs/esign-rest-api/how-to/get-envelope-information/)\n", + "operationId": "Envelopes_GetEnvelope", + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "accountId", + "in": "path", + "description": "The external account number (int) or account ID GUID.", + "required": true + }, + { + "schema": { + "type": "string" + }, + "name": "envelopeId", + "in": "path", + "description": "The envelope's GUID. \n\nExample: `93be49ab-xxxx-xxxx-xxxx-f752070d71ec`\n", + "required": true + }, + { + "schema": { + "type": "string" + }, + "name": "advanced_update", + "in": "query", + "description": "When **true,** envelope information can be added or modified.", + "required": false + }, + { + "schema": { + "type": "string" + }, + "name": "include", + "in": "query", + "required": false, + "description": "Specifies additional information about the envelope to return. Enter a comma-separated list, such as `tabs,recipients`. Valid values are:\n\n- `custom_fields`: The custom fields associated with the envelope.\n- `documents`: The documents associated with the envelope.\n- `attachments`: The attachments associated with the envelope.\n- `extensions`: The email settings associated with the envelope.\n- `folders`: The folder where the envelope exists.\n- `recipients`: The recipients associated with the envelope.\n- `powerform`: The PowerForms associated with the envelope.\n- `prefill_tabs`: The pre-filled tabs associated with the envelope.\n- `tabs`: The tabs associated with the envelope.\n- `payment_tabs`: The payment tabs associated with the envelope.\n- `workflow`: The workflow definition associated with the envelope.\n" + }, + { + "schema": { + "type": "string" + }, + "name": "include_anchor_tab_locations", + "in": "query", + "required": false, + "description": "When **true,** all tabs with anchor tab properties are included in the response. The default value is **false.**" + }, + { + "schema": { + "type": "string" + }, + "name": "user_id", + "in": "query", + "required": false, + "description": "" + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "type": "object", + "description": "(Not expanded by normalize-openapi: '#/components/schemas/envelope', deeper than --max-depth 0.)" + } + } + } + }, + "400": { + "description": "Error encountered.", + "content": { + "application/json": { + "schema": { + "type": "object", + "description": "This object describes errors that occur. It is only valid for responses and ignored in requests.\n\n(Not expanded by normalize-openapi: '#/components/schemas/errorDetails', deeper than --max-depth 0.)" + } + } + } + } + }, + "deprecated": false, + "x-ds-methodname": "getEnvelope", + "x-ds-method": "get", + "x-ds-service": "Envelopes", + "x-ds-in-sdk": true, + "x-ds-examples": [ + { + "description": "This example shows a how to get information about\nan envelope with a given envelope id.\n\nThe request for this endpoint has no payload.\n\n### Request\n```\nGET /restapi/v2.1/accounts/1703061/envelopes/4b728be4-xxxx-xxxx-xxxx-d63e23f822b6\n```\n", + "direction": "response", + "format": "json", + "response": { + "allowMarkup": "false", + "autoNavigation": "true", + "brandId": "56502fe1-xxxx-xxxx-xxxx-97cb5c43176a", + "certificateUri": "/envelopes/4b728be4-xxxx-xxxx-xxxx-d63e23f822b6/documents/certificate", + "createdDateTime": "2016-10-05T01:04:58.1830000Z", + "customFieldsUri": "/envelopes/4b728be4-xxxx-xxxx-xxxx-d63e23f822b6/custom_fields", + "documentsCombinedUri": "/envelopes/4b728be4-xxxx-xxxx-xxxx-d63e23f822b6/documents/combined", + "documentsUri": "/envelopes/4b728be4-xxxx-xxxx-xxxx-d63e23f822b6/documents", + "emailSubject": "Please sign the NDA", + "enableWetSign": "true", + "envelopeId": "4b728be4-xxxx-xxxx-xxxx-d63e23f822b6", + "envelopeIdStamping": "true", + "envelopeUri": "/envelopes/4b728be4-xxxx-xxxx-xxxx-d63e23f822b6", + "initialSentDateTime": "2016-10-05T01:04:58.7770000Z", + "is21CFRPart11": "false", + "isSignatureProviderEnvelope": "false", + "lastModifiedDateTime": "2016-10-05T01:04:58.1830000Z", + "notificationUri": "/envelopes/4b728be4-xxxx-xxxx-xxxx-d63e23f822b6/notification", + "purgeState": "unpurged", + "recipientsUri": "/envelopes/4b728be4-xxxx-xxxx-xxxx-d63e23f822b6/recipients", + "sentDateTime": "2016-10-05T01:04:58.7770000Z", + "status": "sent", + "statusChangedDateTime": "2016-10-05T01:04:58.7770000Z", + "templatesUri": "/envelopes/4b728be4-xxxx-xxxx-xxxx-d63e23f822b6/templates" + }, + "request": null, + "style": "custom", + "title": "Get Status About a Specific Envelope" + } + ] + }, + "parameters": [] + } + }, + "x-ds-allow-editing": false, + "tags": [ + { + "name": "AccountBrands", + "description": "The AcccountBrands resource provides methods that enable you to create and manage [brands](/docs/esign-rest-api/esign101/concepts/branding/) for an account.\n\nBranding enables you to add the look and feel of your organization's brand to the sending, signing, and email processes, making it easier for recipients to identify envelopes coming from your organization.\n\nThe Docusign Account Custom Branding feature enables you to set the colors, logo, and text that recipients see at the account level. The settings associated with a brand are applied to all of the envelopes that use the brand. You can create multiple brand profiles for different corporate brands or internal departments.\n\n**Note:** To use this resource, branding for either signing or sending must be enabled for the account (either `canSelfBrandSend`, `canSelfBrandSign`, or both of these account settings must be set to **true**). \n\n### Next steps\n* See [how to create a brand via the API](/docs/esign-rest-api/how-to/create-brand/).\n* Learn [how to apply a brand to an envelope](/docs/esign-rest-api/how-to/apply-brand-to-envelope/)." + }, + { + "name": "AccountConsumerDisclosures", + "description": "The `AccountConsumerDisclosures` resource provides methods that enable you to enable, retrieve, and manage the Electronic Record and Signature Consent Disclosure (ERSD) options for your account. This is the disclosure that displays to each new recipient who is going to sign or add other information, or who is required to view the documents you send to them. The recipient must read and agree to the terms of the disclosure before they can access and take action on the documents you send. The ERSD does not apply to copy-only recipients, but does apply to recipients who must sign or view your documents.\n\nYou can use either the default ERSD that Docusign provides for U.S.-based transactions, or a custom ERSD. \n\n## Languages\n\n**Important:** The system does not translate the ERSD for you. The default ERSD is always in English. For a custom ERSD, an account administrator must create a version of the disclosure for each language that your signers use. When you create a version of your custom ERSD for a specific signer language, you must:\n\n1. Specify the language code (`langCode`) for the signer language.\n2. Provide the `esignAgreementText` and `esignText` in the language associated with the `langCode`.\n\nFor more information, see [Legal Disclosure](https://support.docusign.com/s/document-item?bundleId=pik1583277475390&topicId=aqg1583277336549.html)." + }, + { + "name": "AccountCustomFields", + "description": "Custom fields enable you to record custom information about envelopes that you can then use for sorting, organizing, searching, and other downstream processes. \n\nFor example, you can use custom fields to copy envelopes or data to multiple areas in Salesforce. eOriginal customers can eVault all of their documents from the web app by setting an account custom field with a name like `eVault with eOriginal` to **true.**\n\nYou can also use account custom fields to set the following information:\n\n- Tracking ID\n- Department \n- Use case\n- Other envelope metadata\n\n## Envelope Custom Field Visibility\n\nWhen you create an envelope custom field for your account, you have the following options: \n\n- Make it a required field for senders at the time of sending\n- Display it as an optional field at the time of sending\n- Set a specific value for the field behind the scenes (NOT SURE IF THIS IS RIGHT; MIGHT JUST BE AN UNUSED DRAFT FIELD)\n\nEnvelope recipients do not see the envelope custom fields.\n\n## Types of Envelope Custom Fields\n\nThere are two types of envelope custom fields:\n\n- `text`: Enables the sender to enter the value for the field. \n- `list`: Enables the sender to select the value of the field from a predetermined list." + }, + { + "name": "EnvelopeCustomFields", + "description": "The EnvelopeCustomFields resource provides methods that allow you manage custom fields in an envelope.\n\nCustom fields can be used in the envelopes for your account to record information about the envelope, help search for envelopes and track information. The envelope custom fields are shown in the Envelope Settings section when a user is creating an envelope in the Docusign member console. The envelope custom fields are not seen by the envelope recipients.\n\nThere are two types of envelope custom fields:\n\n- `text`: Enables the sender to enter the value for the field.\n- `list`: Enables the sender to select the value of the field from a predetermined list.\n\n" + }, + { + "name": "EnvelopeDocumentFields", + "description": "The EnvelopeDocumentFields resource provides methods that allow you to manage custom fields on a document.\n\nCustom fields are name-value pairs that are returned by the API but otherwise not used by Docusign. They are not seen by the envelope recipients.\n\n\n\n\n\n" + }, + { + "name": "EnvelopeLocks", + "description": "Envelope locks allow you to manage locks on an envelope.\n\nTo prevent users from changing an envelope while another user is\nmodifying it, you can lock the envelope and set the time until\nthe lock expires.\n\nTo use envelope locks, the user must have envelope locking capability enabled.\n\n### Related topics\n\n- [Common API Tasks: Locking and unlocking envelopes](https://www.docusign.com/blog/dsdev-common-api-tasks-locking-and-unlocking-envelopes)\n\n" + }, + { + "name": "EnvelopeRecipients", + "description": "The EnvelopeRecipients resource enables you manage the recipients of an envelope.\nAll recipient types share a set of [core parameters](#core-recipient-parameters),\nbut some recipient types have additional parameters.\nYou specify the recipient type using the `recipientType` parameter. The recipient types are as follows:\n\n
\n\n| Recipient type | Description |\n| :-------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| [Agent](#agent-recipient) | Agent recipients can add name and email information for recipients that appear after the agent in routing order. |\n| [Carbon Copy](#carbon-copy-recipient) | Carbon copy recipients get a copy of the envelope but don't need to sign, initial, date, or add information to any of the documents. This type of recipient can be used in any routing order. Carbon copy recipients receive their copy of the envelope when the envelope reaches the recipient's order in the process flow and when the envelope is completed. |\n| [Certified Delivery](#certified-delivery-recipient) | Certified delivery recipients must receive the completed documents for the envelope to be completed. However, they don't need to sign, initial, date, or add information to any of the documents. |\n| [Editor](#editor-recipient) | Editors have the same management and access rights for the envelope as the sender. They can make changes to the envelope as if they were using the Advanced Correct feature. This recipient can add name and email information, add or change the routing order, set authentication options, and can edit signature/initial tabs and data fields for the remaining recipients. The recipient must have a Docusign account to be an editor. |\n| [In-Person Signer](#in-person-signer-recipient) | In-person signer recipients are Docusign users who act as signing hosts in the same physical location as the signer. |\n| [Intermediary](#intermediary-recipient) | Intermediaries are recipients who can, but are not required to, add name and email information for recipients at the same or subsequent level in the routing order, unless subsequent agents, editors, or intermediaries are added. |\n| [Seal](#seal-recipient) | Electronic seal recipients represent legal entities rather than individuals. Organizations and governments can use electronic seals to show evidence of origin and integrity of documents. |\n| [Signer](#signer-recipient) | Signers are recipients who must sign, initial, date, or add data to form fields on the documents in the envelope. |\n| [Witness](#witness-recipient) | Witnesses are recipients whose signatures affirm that the identified signers have signed the documents in the envelope. |\n\n
\n\nNot all recipients are are available to all account types.\nReview your account plan to determine which recipient types are available to you.\nAll recipient types are available in the development environment.\n\n\n## Core Recipient Parameters\n\nAll recipients, regardless of type, have the same common core parameters.\nThe following table contains the descriptions for the core properties for all recipient types:\n\n
\n\n| Name | Required | Schema Type | Description |\n| :--------------------------------------- | :------- | :-------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| email | Yes | Email | Email of the recipient. Notification will be sent to this email id.
Maximum Length: 100 characters. |\n| name | Yes | String | Full legal name of the recipient.
Maximum Length: 100 characters.

**Note:** If you are creating an envelope with Docusign EU advanced signature enabled, ensure that recipient names do not contain any of the following characters: **^ : \\ @ , + <** |\n| accessCode | No | String | This optional element specifies the access code a recipient has to enter to validate the identity.
Maximum Length: 50 characters. |\n| addAccessCodeToEmail | No | Boolean | This optional attribute indicates that the access code is added to the email sent to the recipient; this nullifies the Security measure of Access Code on the recipient. |\n| agentCanEditEmail | No | Boolean | When **true,** the agent recipient associated with this recipient can change the recipient's pre-populated email address. This element is only active if enabled for the account. |\n| agentCanEditName | No | Boolean | When **true,** the agent recipient associated with this recipient can change the recipient's pre-populated name (`UserName`). This element is only active if enabled for the account. |\n| clientUserId | No | String | This specifies whether the recipient is embedded or remote.

If the `clientUserId` property is not null then the recipient is embedded. Note that if the `ClientUserId` property is set and either `SignerMustHaveAccount` or `SignerMustLoginToSign` property of the account settings is set to **true,** an error is generated on sending. Maximum length: 100 characters. |\n| embeddedRecipientStartURL | No | String | This is a sender provided valid URL string for redirecting an embedded recipient. When using this option, the embedded recipient still receives an email from Docusign, just as a remote recipient would, but when the document link in the email is clicked the recipient is redirected, through Docusign, to this URL to complete their actions. When routing to the URL, it is up to the sender's system (the server responding to the URL) to then request a recipient token to launch a signing session.

If the value `SIGN_AT_DOCUSIGN` is used for this node, the recipient is directed to an embedded signing or viewing process directly at Docusign. The signing or viewing action is initiated by the Docusign system and the transaction activity and Certificate of Completion records will reflect this. In all other ways the process is identical to an embedded signing or viewing operation that would be launched by any partner.

It is important to remember that in a typical embedded workflow the authentication of an embedded recipient is the responsibility of the sending application and Docusign expects that senders will follow their own process for establishing the recipient's identity. In this workflow the recipient goes through the sending application before the embedded signing or viewing process in initiated. However, when the sending application sets the `EmbeddedRecipientStartURL` property to `SIGN_AT_DOCUSIGN`, the recipient goes directly to the embedded signing or viewing process bypassing the sending application and any authentication steps the sending application would use. In this case, Docusign recommends that one of the normal Docusign authentication features (Access Code, Phone Authentication, SMS Authentication, etc.) be used to verify the identity of the recipient.

If the `clientUserId` property is NOT set and the `embeddedRecipientStartURL` property is set, Docusign ignores the redirect URL and launch the standard signing process for the email recipient. Information can be appended to the `embeddedRecipientStartURL` property using merge fields. The available merge fields items are: envelopeId, recipientId, recipientName, recipientEmail, and customFields. The customFields must be part of the recipient or envelope. The merge fields are enclosed in double brackets.

_Example_:
`http://senderHost/[[mergeField1]]/ beginSigningSession? [[mergeField2]]&[[mergeField3]]`|\n| customFields | No | customField | An optional array of strings that allows the sender to provide custom data about the recipient. This information is returned in the envelope status but otherwise not used by Docusign. String `customField` properties have a maximum length of 100 characters. |\n| emailNotification | No | emailNotification | An optional property that has information for setting the language for the recipient's email information. It is composed of three elements:

*emailBody*: a string with the email message sent to the recipient.
Maximum Length: 10000 characters.

*emailSubject*: a string with the subject of the email sent to the recipient.
Maximum Length: 100 characters.

*supportedLanguage*: The simple type enumeration (two-letter code) for the language to use for the standard email format and the signing view for the recipient. To retrieve the possible values, use the [Accounts::listSupportedLanguages method][ListLang].

**Note:** You can set the `emailNotification` property separately for each recipient. If you set the value only for certain recipients, the other recipients will inherit the this value from the top-level `emailSubject` and `emailBlurb`. |\n| excludedDocuments | No | Array of Strings | Specifies the documents that are not visible to this recipient. Document Visibility must be enabled for the account and the enforceSignerVisibility property must be set to true for the envelope to use this.

When the enforceSignerVisibility property is set to **true,** documents with tabs can only be viewed by signers that have a tab on that document. Recipients that have an administrative role (Agent, Editor, or Intermediaries) or informational role (Certified Deliveries or Carbon Copies) can always see all the documents in an envelope, unless they are specifically excluded using this setting when an envelope is sent. Documents that do not have tabs are always visible to all recipients, unless they are specifically excluded using this setting when an envelope is sent. |\n| idCheckConfigurationName | No | String | Specifies authentication check by name. The names used here must be the same as the authentication type names used by the account (these name can also be found in the web console sending interface in the Identify list for a recipient). This overrides any default authentication setting.

_Example_:
Your account has ID Check and SMS Authentication available and in the web console Identify list these appear as 'ID Check $' and 'SMS Auth $'. To use ID check in an envelope, the `idCheckConfigurationName` property must be set to `ID Check $`. To use SMS, it must be set to `SMS Auth $` and you must add phone number information to the `smsAuthentication` node. |\n| iDCheckInformationInput | No | IdCheckInformationInput | This complex element contains input information related to a recipient ID check. It can include the following information.

*addressInformationInput*: Used to set recipient address information and consists of:

*addressInformation*: consists of six elements, with street2 and zipPlus4 being optional. The elements are: street1, street2, city, state, zip, zipPlus4\\. The maximum number of characters in each element are: street1/street2 = 150 characters, city = 50 characters, state = 2 characters, and zip/zipPlus4 = 20 characters.

displayLevelCode: Specifies the display level for the recipient. Values are: ReadOnly, Editable, or DoNotDisplay.

*receiveInResponse*: A Boolean element that specifies if the information needs to be returned in the response.

*dobInformationInput*: Used to set recipient date of birth information and consists of:

*dateOfBirth*: Specifies the recipient's date, month and year of birth.

*displayLevelCode*: Specifies the display level for the recipient. Values are: ReadOnly, Editable, or DoNotDisplay.

*receiveInResponse*: A Boolean element that specifies if the information needs to be returned in the response.

*ssn4InformationInput*: Used to set the last four digits of the recipient's SSN information and consists of:

*ssn4*: Specifies the last four digits of the recipient's SSN.

*displayLevelCode*: Specifies the display level for the recipient. Values are: ReadOnly, Editable, or DoNotDisplay.

*receiveInResponse*: A Boolean element that specifies if the information needs to be returned in the response.

*ssn9InformationInput*: Used to set the recipient's SSN information. Note that the ssn9 information can never be returned in the response. The ssn9 input consists of:



*ssn9*: Specifies the recipient's SSN.

*displayLevelCode*: Specifies the display level for the recipient. Values are: ReadOnly, Editable, or DoNotDisplay. |\n| inheritEmailNotificationConfiguration | No | Boolean | Optional element. If true and the envelope recipient creates a Docusign account after signing, the Manage Account Email Notification settings are used as the default settings for the recipient's account. |\n| note | No | String | A note that is unique to this recipient. This note is sent to the recipient via the signing email. The note displays in the signing UI near the upper left corner of the document on the signing screen.
Maximum Length: 1000 characters. |\n| phoneAuthentication | No | RecipientPhoneAuthentication | Optional element. Contains the elements:

*recipMayProvideNumber*:Boolean. When **true** thenrecipient can use whatever phone number they choose to.

*senderProvidedNumbers*: ArrayOfString. A list of phone numbers the recipient can use.
\n| recipientId | No | String

The string must be formatted as an integer or a GUID. | Unique for the recipient. It is used by the tab element to indicate which recipient is to sign the Document. |\n| requireIdLookup | No | Boolean | When **true,** the recipient is required to use the specified ID check method (including Phone and SMS authentication) to validate their identity. |\n| roleName | No* | String | Optional element. Specifies the role name associated with the recipient.

This is required when working with template recipients. |\n| routingOrder | Yes | String | This element specifies the routing order of the recipient in the envelope. |\n| smsAuthentication | No | senderProvidedNumbers | Optional. Contains the element:

*senderProvidedNumbers*: Array that contains a list of phone numbers the recipient can use for SMS text authentication. |\n| templateAccessCodeRequired | No | Boolean | Optional. Used only when working with template recipients. When **true** and the `TemplateLocked` parameter is set to **true,** the sender must enter an access code. |\n| templateLocked | No | Boolean | Optional. Used only when working with template recipients. When **true,** the sender cannot change any attributes of the recipient. |\n| templateRequired | No | Boolean | Optional. Used only when working with template recipients. When **true,** the sender may not remove the recipient. |\n| identityVerification | No | identityVerification | Optional. Specifies ID Verification applied on an envelope by workflow ID.
See the [list](/docs/esign-rest-api/reference/accounts/identityverifications/list/) method in the [IdentityVerifications](/docs/esign-rest-api/reference/accounts/identityverifications/) resource for more information on how to retrieve workflow IDs available for an account.
This can be used in addition to other [recipient authentication](/docs/esign-rest-api/esign101/concepts/recipients/auth/) methods.
Note that ID Verification and ID Check are two distinct methods. ID Verification checks recipients' identity by verifying their ID while ID Check relies on data available on public records (such as current and former address). |\n\n
\n\n\n\n## Agent Recipient\n\nAn agent recipient can add name and email information for recipients that appear after the agent in routing order.\n\nIn addition to the [core parameters](#core-recipient-parameters), this recipient type has the following parameters:\n\n
\n\n| Name | Required | Schema Type | Description |\n| :----------------- | :------- | :----------------- | :--------------------------------------------------------------------------- |\n| documentVisibility | No | documentVisibility | A complex type that specifies which documents are visible to this recipient. |\n\n
\n\n\n\n## Carbon Copy Recipient\n\nCarbon copy recipients receive a copy of the envelope but don't need to sign, initial,\ndate or add information to any of the documents. You can place this type of recipient in any routing order.\nCarbon copy recipients receive their copy of the envelope when the envelope\nreaches the recipient's order in the process flow and when the envelope is completed.\n\nIn addition to the [core parameters](#core-recipient-parameters), this recipient type has the following parameters:\n\n
\n\n| Name | Required | Schema Type | Description |\n| :----------------- | :------- | :----------------- | :--------------------------------------------------------------------------- |\n| documentVisibility | No | documentVisibility | A complex type that specifies which documents are visible to this recipient. |\n\n
\n\n\n\n## Certified Delivery Recipient\n\nCertified delivery recipients must receive the completed documents for the envelope to be completed. However, they don't need to sign, initial, date or add information to any of the documents.\n\nIn addition to the [core parameters](#core-recipient-parameters), this recipient type has the following parameters:\n\n
\n\n| Name | Required | Schema Type | Description |\n| :----------------- | :------- | :----------------- | :--------------------------------------------------------------------------- |\n| documentVisibility | No | documentVisibility | A complex type that specifies which documents are visible to this recipient. |\n\n
\n\n\n\n## Editor Recipient\n\nEditors have the same management and access rights for the envelope as the sender.\nThey can make changes to the envelope as if they were using the Advanced Correct feature.\nThis recipient can add name and email information,\nadd or change the routing order and set authentication options for the remaining recipients.\nAdditionally, this recipient can edit signature/initial tabs and data fields for the remaining recipients.\nThe recipient must have a Docusign account to be an editor.\n\nIn addition to the [core parameters](#core-recipient-parameters), this recipient type has the following parameters:\n\n
\n\n\n| Name | Required | Schema Type | Description |\n| :----------------- | :------- | :----------------- | :--------------------------------------------------------------------------- |\n| documentVisibility | No | documentVisibility | A complex type that specifies which documents are visible to this recipient. |\n\n\n
\n\n\n## In-Person Signer Recipient\n\nAn in-person recipient is a Docusign user, acting as a Signing Host, who is in the same physical location as the signer.\n\nThe following restrictions apply to using electronic notary when sending documents:\n\n* Authentication methods are allowed for the signer but not the notary.\n* The Sign On Paper, Document Markup, Field Markup and Change Signer options cannot be used for the documents.\n* Tabs may be assigned to the signer, but cannot be assigned to the notary.\n\nSee [eNotary Resources][enotary-resources] in the Docusign Support Center for more information about how the eNotary feature works.\n\nIn addition to the [core parameters](#core-recipient-parameters), this type adds the following parameters:\n\n
\n\n\n| Name | Required | Schema Type | Description |\n| :-------------------- | :-------------------------------------------------- | :----------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| inPersonSigningType | No | String | Specifies whether the envelope uses the eNotary feature. The accepted values are:
  • `inPersonSigner` The envelope uses the normal in-person signing flow.
  • `notary`: The envelope uses the eNotary signing flow.
|\n| notaryHost | Yes, when `inPersonSigningType` is `notary` | NotaryHost | Sets the information for the notary host for the notary in person signing flow. The following information is required:
  • `recipientId`: A unique ID (an integer or a GUID) for the notary signing host.
  • `name`: Specifies the notary's full legal name.
  • `email`: Specifies the notary's email address.
|\n| autoNavigation | No | Boolean | Specifies whether auto navigation is set for the recipient. |\n| defaultRecipient | No | Boolean | When **true,** this is the default recipient for the envelope. This option is used when creating an envelope from a template. |\n| documentVisibility | No | documentVisibility | A complex type that specifies which documents are visible to this recipient. |\n| hostName | Yes, when `inPersonSigningType` is `inPersonSigner` | String | The name of the signing host. This is the Docusign user that is hosting the in-person signing session. |\n| hostEmail | Yes, when `inPersonSigningType` is `inPersonSigner` | String | The email address of the signing host. This is the Docusign user that is hosting the in-person signing session. |\n| signerName | Yes, when `inPersonSigningType` is `inPersonSigner` | String | The in-person signer's full legal name. |\n| Name | Yes, when `inPersonSigningType` is `notary` | String | The full legal name of the signer in an eNotary flow. |\n| email | Yes, when `inPersonSigningType` is `notary` | String | The signer's email address in an eNotary flow. |\n| recipientSuppliesTabs | No | Boolean | Indicates whether the recipient supplies tabs in the document. |\n| signatureInfo | No | String | Optional element only used with the recipient types In Person Signers, Signers, and Witnesses.

Allows the sender to pre-specify the signature name, signature initials, and signature font used in the signature stamp for the recipient. |\n| signInEachLocation | No | Boolean | When **true** and the feature is enabled in the sender's account, the signing recipient is required to draw signatures and initials at each signature/initial tab (instead of adopting a signature/initial style or only drawing a signature/initial once). |\n| tabs | No | Tab | Optional element only used with recipient types In Person Signers and Signers.

Specifies the Tabs associated with the recipient. See the [EnvelopeRecipientTabs resource](/docs/esign-rest-api/reference/envelopes/enveloperecipienttabs/) for more information about tabs. |\n\n
\n\n## Intermediary Recipient\n\nAn intermediary is a recipient who can, but is not required to,\nadd name and email information for recipients\nat the same or subsequent level in the routing order,\nunless subsequent agents, editors or intermediaries are added.\n\nIn addition to the [core parameters](#core-recipient-parameters), this recipient type has the following parameters:\n\n
\n\n\n| Name | Required | Schema Type | Description |\n| :----------------- | :------- | :----------------- | :--------------------------------------------------------------------------- |\n| documentVisibility | No | documentVisibility | A complex type that specifies which documents are visible to this recipient. |\n\n
\n\n\n\n## Seal Recipient\n\nAn electronic seal recipient is a legal entity rather than an actual person.\nElectronic Seals can be used by organizations and governments to show evidence\nof origin and integrity of documents.\nEven though electronic seals can be represented by a tab in a document,\nthey do not require user interaction and apply automatically in the order specified by the sender.\nThe sender is therefore the person authorizing usage of the electronic seal in the flow.\n\nElectronic seal recipients rely on a subset of core properties, described as follows, plus the `recipientSignatureProviders` parameter:\n\n\n
\n\n\n| Name | Required | Schema Type | Description |\n| :-------------------------- | :-------------- | :-------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| recipientId | Yes | String

The string must be formatted as an integer or a GUID. | Indicates the unique ID of the applied electronic seal. |\n| routingOrder | No (default: 1) | String | Specifies the routing order of the electronic seal in the envelope.
The routing order assigned to your electronic seal cannot be shared with another recipient. It is recommended that you set a routing order for your electronic seals.. |\n| recipientSignatureProviders | Yes | String | Indicates which electronic seal to apply on documents when creating an envelope. |\n\n\n
\n\nBy default, Electronic Seals apply to all documents in an envelope.\nHowever, the `sealDocumentsWithTabsOnly` property (see `recipientSignatureProvider`)\nallows you to seal only documents that have `signHere` tabs set for the Electronic Seal recipients.\n\n
\n\n\n\n## Signer Recipient\n\nA signer is a recipient who must sign, initial, date, or add data to form fields on the documents in the envelope.\n\nIn addition to the [core parameters](#core-recipient-parameters), this recipient type adds the following parameters:\n\n
\n\n\n| Name | Required | Schema Type | Description |\n| :-------------------- | :------- | :----------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| autoNavigation | No | Boolean | Specifies whether auto navigation is set for the recipient. |\n| defaultRecipient | No | Boolean | When **true,** this is the default recipient for the envelope. This option is used when creating an envelope from a template. |\n| documentVisibility | No | documentVisibility | A complex type that specifies which documents are visible to this recipient. |\n| isBulkRecipient | No | Boolean | Indicates whether the recipient is a bulk send recipient or not. |\n| recipientSuppliesTabs | No | Boolean | Indicates whether the recipient supplies tabs in the document. |\n| signInEachLocation | No | Boolean | When **true** and the feature is enabled in the sender's account, the signing recipient is required to draw signatures and initials at each signature/initial tab (instead of adopting a signature/initial style or only drawing a signature/initial once). |\n| signatureInfo | No | String | Optional element only used with recipient types In Person Signers, Signers, and Witnesses.

Allows the sender to pre-specify the signature name, signature initials, and signature font used in the signature stamp for the recipient. |\n| signerEmail | No | String | Optional element. The email address for an In-Person Signer recipient Type.
Maximum Length: 100 characters. |\n| signerName | Yes | String | Required element with recipient type In Person Signers.
Maximum Length: 100 characters.

The full legal name of a signer for the envelope. |\n| tabs | No | Tab | Optional element only used with recipient types In Person Signers and Signers.

Specifies the Tabs associated with the recipient. See the [EnvelopeRecipientTabs resource](/docs/esign-rest-api/reference/envelopes/enveloperecipienttabs/) for more information about tabs. |\n\n
\n\n[enotary-resources]: https://support.docusign.com/s/document-item?bundleId=gko1642535666104&topicId=jiv1635359045452.html\n\n\n## Witness Recipient\n\nA witness is a recipient whose signature affirms that the identified signers have signed the documents in the envelope.\n\nIn addition to the [core parameters](#core-recipient-parameters), this recipient type adds the following parameters:\n\n
\n\n\n| Name | Required | Schema Type | Description |\n| :-------------------------- | :------- | :----------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| autoNavigation | No | Boolean | Specifies whether auto navigation is set for the recipient. |\n| defaultRecipient | No | Boolean | When **true,** this is the default recipient for the envelope. This option is used when creating an envelope from a template. |\n| documentVisibility | No | documentVisibility | A complex type that specifies which documents are visible to this recipient. |\n| isBulkRecipient | No | Boolean | Indicates whether the recipient is a bulk send recipient or not. |\n| recipientSignatureProviders | Yes | String | Indicates which electronic seal to apply on documents when creating an envelope. |\n| recipientSuppliesTabs | No | Boolean | Indicates whether the recipient supplies tabs in the document. |\n| recipientType | Yes | String | Indicates the recipient type. |\n| requireSignerCertificate | No | Boolean | Indicates whether the envelope requires a signer certificate for this recipient. |\n| requireSignOnPaper | No | Boolean | Specifies whether the signer must print, sign, and upload or fax the signed documents to Docusign. |\n| signatureInfo | No | Boolean | Optional element only used with recipient types In Person Signers, Signers, and Witnesses. Enables the sender to pre-specify the signature name, signature initials, and signature font used in the signature stamp for the recipient. |\n| signInEachLocation | No | Boolean | When **true** and the feature is enabled in the sender's account, specifies that the signing recipient is required to sign and initial at each signature/initial tab (instead of once). |\n| signingGroupId | No | String | The ID of the [signing group](https://support.docusign.com/s/document-item?bundleId=gav1643676262430&topicId=zgn1578456447934.html). |\n| signingGroupName | No | String | The display name for the signing group. Maximum Length: 100 characters. |\n| signingGroupUsers | No | userInfo | A complex type that contains information about the users in the signing group. |\n| witnessFor | Yes | String | Indicates the person or party for whom the recipient is a witness. |\n| witnessForGuid | Yes | String | GUID identifying the person or party for whom the recipient is a witness. |\n\n\n[ListLang]: /docs/esign-rest-api/reference/accounts/accounts/listsupportedlanguages/\n" + }, + { + "name": "EnvelopeRecipientTabs", + "description": "The EnvelopeRecipientTabs resource provides methods that enable you\nto add,\nupdate,\nand delete tabs\nfrom an envelope.\nTabs are associated with a specific recipient\nin an envelope\nand are only used by the recipient types\nIn Person Signers and Signers.\n\n\n\n**On this page**\n\n- [Tab Types](#tab-types)\n- [View Tab](#view-tab)\n- [Requesting Payments](#requesting-payments)\n- [Using Custom Tabs in Envelopes and Templates](#using-custom-tabs-in-envelopes-and-templates)\n- [Anchoring Tabs](#anchoring-tabs)\n- [Automatically Populating Tabs](#automatically-populating-tabs)\n\n\n\n\n## Tab Types\n\nSome tabs enable values to be entered by the signer.\nThose tabs' values can be preset either through the web browser\nor via the API. Other tab types use information that is already\nrecognized by the Docusign platform.\nThese tabs cannot have their value updated on a per-tab basis\nby the API or via the browser. In some cases, the info might be\nsettable using a different technique.\nFor example, the Full name tab uses the signer's name,\nwhich is set elsewhere in the request.\n\nHere is the list of tabs and whether you can or cannot set their values in the tab definition:\n\n
\n\n| Tab Type | Description |\n| :--------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Approve (`approve`) | Allows the recipient to approve documents without placing a signature or initials on the document. If the recipient clicks the tab during the signing process, the recipient is considered to have signed the document. No information is shown on the document of the approval, but it is recorded as a signature in the envelope history. This value **can't** be set. |\n| Checkbox (`checkbox`) | Allows the recipient to select a yes/no (on/off) option. This value can be set. |\n| Commission County (`commissionCounty`) | Displays the county of a notary's commission. This tab can only be assigned to a remote notary recipient using [Docusign Notary](/docs/notary-api/). This value **can't** be set in the API call. |\n| Commission Expiration (`commissionExpiration`) | Displays the expiration date of a notary's commission. This tab can only be assigned to a remote notary recipient using [Docusign Notary](/docs/notary-api/). This value **can't** be set. |\n| Commission Number (`commissionNumber`) | Displays the number of a notary's commission. This tab can only be assigned to a remote notary recipient using [Docusign Notary](/docs/notary-api/). This value **can't** be set. |\n| Commission State (`commissionState`) | Displays the state in which a notary's commission was granted. This tab can only be assigned to a remote notary recipient using [Docusign Notary](/docs/notary-api/). This value **can't** be set. |\n| Company (`company`) | Displays the recipient's company name. This value **can't** be set. |\n| Date Signed (`dateSigned`) | Displays the date that the recipient signed the document. This value **can't** be set. |\n| Date (`date`) | Allows the recipient to enter a date. Date tabs are one-line fields that allow date information to be entered in any format. The tooltip for this tab recommends entering the date as YYYY-MM-DD, but this is not enforced. The format entered by the signer is retained. If you need a particular date format enforced, Docusign recommends using a Text tab with a validation pattern and a validation message to enforce the format. This value can be set. |\n| Decline (`decline`) | Allows the recipient the option of declining an envelope. If the recipient clicks the tab during the signing process, the envelope is voided. This value **can't** be set. |\n| Email Address (`emailAddress`) | Displays the recipient's email as entered in the recipient information. This value **can't** be set. |\n| Email (`email`) | Allows the recipient to enter an email address. This is a one-line field that checks that a valid email address is entered. It uses the same parameters as a Text tab, with the validation message and pattern set for email information.

When getting information that includes this tab type, the original value of the tab when the associated envelope was sent is included in the response. This value can be set. |\n| Envelope ID (`envelopeId`) | Displays the envelope ID. Recipients cannot enter or change the information in this tab. This value **can't** be set. |\n| First Name (`firstName`) | Displays the recipient's first name. This tab takes the recipient's name as entered in the recipient information, splits it into sections based on spaces and uses the first section as the first name. This value **can't** be set. |\n| Formula Tab (`formulaTab`) | The value of a formula tab is calculated from the values of other number or date tabs in the document. When the recipient completes the underlying fields, the formula tab calculates and displays the result. This value can be set. The `formula` property of the tab contains the references to the underlying tabs. See [Calculated Fields] in the Docusign Support Center to learn more about formulas. If a formula tab contains a `paymentDetails` property, the tab is considered a payment item. See [Requesting Payments Along with Signatures] in the Docusign Support Center to learn more about payments. |\n| Full Name (`fullName`) | Displays the recipient's full name. This value **can't** be set. |\n| Initial Here (`initialHere`) | Allows the recipient to initial the document. May be optional. This value **can't** be set. |\n| Last Name (`lastName`) | Displays the recipient's last name. This tab takes the recipient's name as entered in the recipient information, splits it into sections based on spaces and uses the last section as the last name. This value **can't** be set. |\n| List (`list`) | This tab offers a list of options to choose from. The `listItems` property is used to specify the selectable options. This value can be set, |\n| Notarize (`notarize`) | Place this tab on a page to alert Notary recipients that they must take action. Only one notarize tab can appear on a page. This value can be set. |\n| Notary Seal (`notarySeal`) | Enables the recipient to notarize a document. This tab can only be assigned to a remote notary recipient using [Docusign Notary](/docs/notary-api/). This value **can't** be set. |\n| Note (`note`) | Displays additional information, in the form of a note, for the recipient. This value can be set. |\n| Number (`number`) | Allows the recipient to enter numbers and decimal (.) points. This value can be set. |\n| Numerical (`numerical`) | Numerical tabs provide robust display and validation features, including formatting for different regions and currencies, and minimum and maximum value validation. |\n| Phone Number (`phoneNumber`) | Allows the recipient to enter a phone number. This tab can only be assigned to a remote notary recipient using [Docusign Notary](/docs/notary-api/). This value **can't** be set. |\n| Radio Group (`radioGroup`) | This group tab is used to place radio buttons on a document. The `radios` property is used to add and place the radio buttons associated with the group. Only one radio button can be selected in a group. This value can be set. |\n| Sign Here (`signHere`) | Allows the recipient to sign a document. May be optional. This value **can't** be set.
**Note:** `signHere` tabs can also be used to add a visual representation for an electronic seal in a document. |\n| Signer Attachment (`signerAttachment`) | Allows the recipient to attach supporting documents to an envelope. This value **can't** be set. |\n| SSN (`ssn`) | A one-line field that allows the recipient to enter a Social Security Number. The SSN can be typed with or without dashes. It uses the same parameters as a Text tab, with the validation message and pattern set for SSN information. This value can be set. |\n| Text (`text`) | Allows the recipient to enter any type of text. This value can be set. Maximum length: 4000 bytes. |\n| Title (`title`) | Displays the recipient's title. This value **can't** be set. |\n| View (`view`) | The View tab is used with the Approve tab to handle supplemental documents. This value can be set. |\n| Zip (`zip`) | Allows the recipient to enter a ZIP code. The ZIP code can be five digits or nine digits in the ZIP+4 format. The zip code can be typed with or without dashes. It uses the same parameters as a Text tab, with the validation message and pattern set for ZIP code information. This value can be set. |\n\n\n[approve]: /docs/esign-rest-api/reference/envelopes/enveloperecipienttabs/#tab-types\n[checkbox]: /docs/esign-rest-api/reference/envelopes/enveloperecipienttabs/#tab-types\n[company]: /docs/esign-rest-api/reference/envelopes/enveloperecipienttabs/#tab-types\n[dateSigned]: /docs/esign-rest-api/reference/envelopes/enveloperecipienttabs/#tab-types\n[date]: /docs/esign-rest-api/reference/envelopes/enveloperecipienttabs/#tab-types\n[decline]: /docs/esign-rest-api/reference/envelopes/enveloperecipienttabs/#tab-types\n[emailAddress]: /docs/esign-rest-api/reference/envelopes/enveloperecipienttabs/#tab-types\n[email]: /docs/esign-rest-api/reference/envelopes/enveloperecipienttabs/#tab-types\n[envelopeId]: /docs/esign-rest-api/reference/envelopes/enveloperecipienttabs/#tab-types\n[firstName]: /docs/esign-rest-api/reference/envelopes/enveloperecipienttabs/#tab-types\n[formulaTab]: /docs/esign-rest-api/reference/envelopes/enveloperecipienttabs/#tab-types\n[fullName]: /docs/esign-rest-api/reference/envelopes/enveloperecipienttabs/#tab-types\n[initialHere]: /docs/esign-rest-api/reference/envelopes/enveloperecipienttabs/#tab-types\n[lastName]: /docs/esign-rest-api/reference/envelopes/enveloperecipienttabs/#tab-types\n[list]: /docs/esign-rest-api/reference/envelopes/enveloperecipienttabs/#tab-types\n[notarize]: /docs/esign-rest-api/reference/envelopes/enveloperecipienttabs/#tab-types\n[note]: /docs/esign-rest-api/reference/envelopes/enveloperecipienttabs/#tab-types\n[number]: /docs/esign-rest-api/reference/envelopes/enveloperecipienttabs/#tab-types\n[radioGroup]: /docs/esign-rest-api/reference/envelopes/enveloperecipienttabs/#tab-types\n[signerAttachment]: /docs/esign-rest-api/reference/envelopes/enveloperecipienttabs/#tab-types\n[signHere]: /docs/esign-rest-api/reference/envelopes/enveloperecipienttabs/#tab-types\n[ssn]: /docs/esign-rest-api/reference/envelopes/enveloperecipienttabs/#tab-types\n[text]: /docs/esign-rest-api/reference/envelopes/enveloperecipienttabs/#tab-types\n[title]: /docs/esign-rest-api/reference/envelopes/enveloperecipienttabs/#tab-types\n[view]: /docs/esign-rest-api/reference/envelopes/enveloperecipienttabs/#tab-types\n[zip]: /docs/esign-rest-api/reference/envelopes/enveloperecipienttabs/#tab-types\n\n[calculatedfields]: https://support.docusign.com/s/document-item?bundleId=gbo1643332197980&topicId=crs1578456361259.html\n[paymentguide]: https://support.docusign.com/s/document-item?bundleId=juu1573854950452&topicId=fyw1573854935374.html\n\n\n\n\n\n\n## View Tab\n\nThe View tab is used on supplemental documents.\nTo learn more about using the View tab with\nsupplemental documents, see\n[Using Supplemental Documents][usingsupdocs]\nin the [Sending Documents][sendenvelopes] section of\nthe [Envelope: create][envelopecreate] method.\n\n[sendenvelopes]: /docs/esign-rest-api/reference/envelopes/envelopes/create/\n[usingsupdocs]: /docs/esign-rest-api/esign101/concepts/documents/supplemental/\n[envelopecreate]: /docs/esign-rest-api/reference/envelopes/envelopes/create/\n\n
\n\n| Name | Required | Type | Description |\n| :--------------- | :------- | :------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| documentId | Yes | String | The document ID number that the tab is placed on. This must refer to an existing Document's ID attribute. |\n| pageNumber | Yes | String | Must be set to 1. |\n| recipientId | Yes | String | The recipient associated with the tab. Must refer to an existing recipient's ID attribute. |\n| required | No | Boolean | When **true,** the recipient is required to select the supplemental document View button during signing. |\n| tabLabel | No | String | The label used for the tab. If an empty string is provided for this, an empty sting is used. If no value is provided, the tab type is used as the value. Maximum of 500 characters. |\n| templateLocked | No | Boolean | Optional. Used only when working with template tabs. When **true,** the attributes of the tab cannot be changed by the sender. |\n| templateRequired | No | Boolean | Optional. Used only when working with template tabs. When **true,** the tab cannot be removed by the sender. |\n| xPosition | Yes | String | Required, but can be 0. |\n| yPosition | Yes | String | Required, but can be 0. |\n\n\n## Requesting Payments\n\nThe Payments feature of the Docusign eSignature REST API\nlets you collect payments\nalong with signatures and other information.\n\nTo send a request for payment\nand collect payments,\nyou need a payment gateway account,\nwhich is the destination for the payments.\nCreate a payment account\nwith a supported payment gateway,\nand then connect the payment gateway account\nto your Docusign account.\nTo learn how to connect a payment gateway account\nto your Docusign account\nsee [Managing Payment Gateways][paymentgateways]\nin the Docusign Support Center.\nYou must connect and manage payment gateway accounts manually\nthrough the Docusign Admin console\nand through your chosen payment gateway.\nThere is no public API\nfor connecting payment gateway accounts\nto Docusign accounts\nor for managing payment gateway accounts.\n\nCurrently\n[Stripe][stripe],\n[Braintree](https://www.braintreepayments.com/),\n[Authorize.net](https://www.authorize.net/),\n[CyberSource](https://www.cybersource.com/),\n[Zuora](https://www.zuora.com/), and\n[Elavon](https://www.elavon.com/)\nare the supported payment gateways.\n\n### How Payments Work\n\nTo make a request for payment,\nuse a [`formulaTab`][formulatab]\nthat has a\n[`paymentDetails`][paymentdetails] object.\nThis object includes\na list of [`paymentLineItem`][paymentlineitem] objects.\nEach line item refers to a [`number`][numbertab] tab\nthat contains the value of the each item.\nThe purpose of the line items\nis to transmit them to the payment gateway\nas metadata, so that you can use the information\nin the payment processor.\n\n**Note:** If the `fileExtension` parameter is not added in an API call, only base64 converted PDF files will be accepted.\nAny attempt to send a non-PDF file without using `fileExtension` results in an error.\n\nThis is an example request for two books.\nEach book is specified in the `number` tabs\nlabeled \"Hamlet\" and \"Tempest\".\nThe books are referenced as line items\nby their tab labels\nwithin the `paymentDetails` object\nof a `formula` tab.\nThe formula of the `formula` tab\nadd the values of the same two `number` tabs.\n\n**Note:** This example uses US dollars, a decimal currency,\nso the price is multiplied\nby 100. If you are using a zero-decimal currency,\nsuch as Japanese yen, _do not_ multiply by 100.\n\n```json\n{\n \"documents\": [\n {\n \"documentBase64\": \"\",\n \"documentId\": \"1\",\n \"fileExtension\": \"pdf\",\n \"name\": \"payment.pdf\"\n }\n ],\n \"emailSubject\": \"Order Some Books\",\n \"recipients\": {\n \"signers\": [\n {\n \"email\": \"vreader@example.com\",\n \"name\": \"Voracious Reader\",\n \"recipientId\": \"1\",\n \"routingOrder\": \"1\",\n \"tabs\": {\n . . .\n \"numberTabs\": [\n {\n \"value\": \"10.00\",\n \"width\": 78,\n \"required\": \"true\",\n \"locked\": \"true\",\n \"tabLabel\": \"Hamlet\",\n \"documentId\": \"1\",\n \"pageNumber\": \"1\",\n \"xPosition\": \"323\",\n \"yPosition\": \"134\"\n },\n {\n \"value\": \"10.00\",\n \"width\": 78,\n \"required\": \"true\",\n \"locked\": \"true\",\n \"tabLabel\": \"Tempest\",\n \"documentId\": \"1\",\n \"pageNumber\": \"1\",\n \"xPosition\": \"323\",\n \"yPosition\": \"154\"\n }\n ],\n \"formulaTabs\": [\n {\n \"required\": \"true\",\n \"formula\": \"([Hamlet] + [Tempest]) * 100\",\n \"roundDecimalPlaces\": \"2\",\n \"paymentDetails\": {\n \"currencyCode\": \"USD\",\n \"lineItems\": [\n {\n \"name\": \"Hamlet\",\n \"description\": \"The Danish Play\",\n \"itemCode\": \"SHAK1\",\n \"amountReference\": \"Hamlet\"\n },\n {\n \"name\": \"Othello\",\n \"description\": \"The one with Caliban in it\",\n \"itemCode\": \"SHAK2\",\n \"amountReference\": \"Tempest\"\n }\n ],\n \"gatewayAccountId\": \"e76668b4-xxxx-xxxx-xxxx-a208d659e490\"\n },\n \"tabLabel\": \"Payment1\",\n \"documentId\": \"1\",\n \"pageNumber\": \"1\",\n \"xPosition\": 300,\n \"yPosition\": 200\n }\n ]\n }\n }\n ]\n },\n \"status\": \"sent\"\n}\n```\n\nUse the\n[EnvelopeRecipients: list][enveloperecipientslist] method\nto check the status of a payment.\nWhen the payment is successful,\nthe `status` property of\nthe [`paymentDetails`][paymentdetails] object\nis `payment_complete`.\n\n```json\n{\n \"signers\": [\n {\n \"tabs\": {\n . . .\n \"numberTabs\": [\n {\n \"value\": \"10.00\",\n \"tabLabel\": \"Hamlet\",\n \"documentId\": \"1\",\n \"recipientId\": \"1\",\n \"pageNumber\": \"1\",\n \"xPosition\": \"323\",\n \"yPosition\": \"134\",\n },\n {\n \"value\": \"10.00\",\n \"tabLabel\": \"Tempest\",\n \"documentId\": \"1\",\n \"recipientId\": \"1\",\n \"pageNumber\": \"1\",\n }\n ],\n \"formulaTabs\": [\n {\n \"formula\": \"([Hamlet] + [Tempest]) * 100\",\n \"roundDecimalPlaces\": \"2\",\n \"paymentDetails\": {\n \"status\": \"payment_complete\",\n \"currencyCode\": \"USD\",\n \"lineItems\": [\n {\n \"name\": \"Hamlet\",\n \"description\": \"The Danish Play\",\n \"itemCode\": \"SHAK1\",\n \"amountReference\": \"Hamlet\"\n },\n {\n \"name\": \"Tempest\",\n \"description\": \"The one with Caliban in it\",\n \"itemCode\": \"SHAK2\",\n \"amountReference\": \"Tempest\"\n }\n ],\n \"gatewayAccountId\": \"e76668b4-xxxx-xxxx-xxxx-a208d659e490\"\n },\n \"value\": \"20\",\n \"required\": \"true\",\n \"locked\": \"false\",\n \"tabLabel\": \"Payment1\",\n \"documentId\": \"1\",\n \"recipientId\": \"1\",\n \"pageNumber\": \"1\",\n }\n ]\n },\n \"creationReason\": \"sender\",\n \"email\": \"vreader@example.com\",\n \"name\": \"Voracious Reader\",\n \"recipientId\": \"1\",\n \"requireIdLookup\": \"false\",\n \"status\": \"completed\",\n }\n ],\n . . .\n}\n```\n\n#### How to make a request for future payments\n\nUse the following steps to make a request to collect a signer's payment method for future use:\n\n1. Add a text tab with a descriptive `tabLabel` such as `FuturePayment`.\n2. In the formula tab's `paymentDetails` object, add a `lineItem` object that references the `tabLabel` from step 1.\n\n**Note:** Do not include this new `lineItem` in formula calculations.\n\nThe following example builds on the previous code block to also collect a payment method for future use:\n\n```\n{\n \"documents\": [\n {\n \"documentBase64\": \"\",\n \"documentId\": \"1\",\n \"fileExtension\": \"pdf\",\n \"name\": \"payment.pdf\"\n }\n ],\n \"emailSubject\": \"Order Some Books\",\n \"recipients\": {\n \"signers\": [\n {\n \"email\": \"vreader@example.com\",\n \"name\": \"Voracious Reader\",\n \"recipientId\": \"1\",\n \"routingOrder\": \"1\",\n \"tabs\": {\n \"numberTabs\": [\n {\n \"value\": \"10.00\",\n \"width\": 78,\n \"required\": \"true\",\n \"locked\": \"true\",\n \"tabLabel\": \"Hamlet\",\n \"documentId\": \"1\",\n \"pageNumber\": \"1\",\n \"xPosition\": \"323\",\n \"yPosition\": \"134\"\n },\n {\n \"value\": \"10.00\",\n \"width\": 78,\n \"required\": \"true\",\n \"locked\": \"true\",\n \"tabLabel\": \"Tempest\",\n \"documentId\": \"1\",\n \"pageNumber\": \"1\",\n \"xPosition\": \"323\",\n \"yPosition\": \"154\"\n }\n ],\n \"textTabs\": [\n {\n \"value\": \"\",\n \"width\": 78,\n \"required\": \"true\",\n \"locked\": \"true\",\n \"tabLabel\": \"FuturePayment\",\n \"documentId\": \"1\",\n \"pageNumber\": \"1\",\n \"xPosition\": \"323\",\n \"yPosition\": \"174\"\n }\n ],\n \"formulaTabs\": [\n {\n \"required\": \"true\",\n \"formula\": \"([Hamlet] + [Tempest]) * 100\",\n \"roundDecimalPlaces\": \"2\",\n \"paymentDetails\": {\n \"currencyCode\": \"USD\",\n \"paymentOption\": \"save_and_authorize\",\n \"lineItems\": [\n {\n \"name\": \"Hamlet\",\n \"description\": \"The Danish Play\",\n \"itemCode\": \"SHAK1\",\n \"amountReference\": \"Hamlet\"\n },\n {\n \"name\": \"Othello\",\n \"description\": \"The one with Caliban in it\",\n \"itemCode\": \"SHAK2\",\n \"amountReference\": \"Tempest\"\n },\n {\n \"name\": \"Request books\",\n \"description\": \"collect Payment method\",\n \"itemCode\": \"\",\n \"amountReference\": \"FuturePayment\"\n }\n ],\n \"gatewayAccountId\": \"e76668b4-xxxx-xxxx-xxxx-a208d659e490\"\n },\n \"tabLabel\": \"Payment1\",\n \"documentId\": \"1\",\n \"pageNumber\": \"1\",\n \"xPosition\": 300,\n \"yPosition\": 200\n }\n ]\n }\n }\n ]\n },\n \"status\": \"sent\"\n}\n```\n\n### Some Things to Keep in Mind About Payments\n\n* An envelope is not completed until all payments are completed.\n\n* If you are using a a decimal currency, such as USD,\n remember to multiply amounts by 100. If you are using\n zero-decimal currency, such as JPY, _do not_ multiply by 100.\n\n* If a Docusign account Administrator\n deletes a payment gateway account connection\n Docusign cancels all in-process envelopes\n that reference the deleted payment gateway account.\n\n* If the sender voids an envelope,\n all payment authorizations are canceled.\n\n* If a required recipient refuses to sign,\n all authorized payments are canceled.\n\n* If a required recipient's payment fails authorization,\n Docusign attempts to recover\n by sending the recipient\n notification about the failed payment authorization.\n The recipient has the opportunity\n to correct the payment method information.\n\n* Each recipient's payment is authorized separately.\n Accounts are charged and payment made\n after all required tabs are completed,\n and all payments in an envelope for all recipients are authorized.\n\n* Refunds are not supported.\n Conduct refunds or payment changes\n with the payment gateway separately from Docusign.\n\n* At this time, Docusign does not charge a per-transaction fee.\n\n\n[enveloperecipientslist]: /docs/esign-rest-api/reference/envelopes/enveloperecipients/list/\n[formulatab]: /docs/esign-rest-api/reference/envelopes/enveloperecipienttabs/create/\n[ISO 4217]: https://en.wikipedia.org/wiki/ISO_4217\n[numbertab]: /docs/esign-rest-api/reference/envelopes/enveloperecipienttabs/create/\n[paymentgateways]: https://support.docusign.com/s/document-item?bundleId=juu1573854950452&topicId=knc1573854895499.html\n[paymentguide]: https://support.docusign.com/s/document-item?bundleId=juu1573854950452&topicId=fyw1573854935374.html\n[paymentlineitem]: /docs/esign-rest-api/reference/envelopes/enveloperecipienttabs/create/\n[paymentdetails]: /docs/esign-rest-api/reference/envelopes/enveloperecipienttabs/create/\n[stripe]: https://stripe.com/\n\n## Anchoring Tabs\n\nThe tab anchoring option\nallows you to send documents for signature\nthat do not have a fixed layout or format.\nIn these documents you might not know\nthe absolute location of the tabs\nwhen you design your API client application because the tabs must move with text.\nAs an alternative to sending X and Y coordinates for tabs,\nthe Docusign Service can derive an anchor location for the tab\nby correlating anchor information to data within the document.\n\nWhen the Docusign Service receives a request that contains tabs\nwith anchor information,\nit searches the document for instances of the `anchorString` property.\nWhen found,\nit places a tab of the specified type for the designated recipient.\nTab positions are established by setting offsets for the tab.\n\nWhen you apply tabs to the document,\nDocusign does not remove or replace the text in the `anchorString` property. You can hide codified anchors by using the same font color as the background of the document. So the anchor can be used by Docusign processes and it will not be visible on the document.\n\nTo use an anchoring option:\n\n1. Identify the location in the document by text string. You can use a pre-existing text string or add a new one.\nFor best performance Docusign recommends using single word anchor strings when possible, especially when there are a large number of pages in the envelope.\nFor example, you might want to add a Sign Here tab to the \"Borrower's Signature\" lines in a document, but that phrase might occur in places in the document where you don't want to tab to appear. In this case, you could add the text \"BorrowerSignHere\" in white font color (so that isn't visible in the document) to all the places you want Sign Here tabs to appear and use \"BorrowerSignHere\" as the anchor string.\n1. Reference the anchor through the `anchorString` property of the tab.\n1. Determine the offset from the anchor string location to where the tab should be placed.\n\nSetting a positive value in the `anchorXOffset` property moves the tab right on the page and positive values in the `anchorYoffset` prove moves the tab down the page. The `anchorUnits` property specifies the units used for the offsets.\nFor Sign Here and Initial Here tabs the bottom-left of the anchor string is equivalent to position (0,0), and the bottom-left of the tab graphic is placed relative to that.\nFor all other tabs the bottom-left of the anchor string is equivalent to position (0,0), and the top-left of the tab graphic is placed relative to that.\nDocusign does not currently provide tools to derive the offset values. Determination of the proper offset will likely require some trial-and-error.\n\n### Rules for working with anchor tags\n\nWhen anchor tabs are used, all documents in the envelope are searched for the `anchorString` property.\n\n* You set the text of the anchor string in the `anchorString` property. Docusign tabs are created for each instance of the `anchorString` property within the document, so special care must be taken to establish unique anchor strings that do not result in unintentional tabs.\n* You cannot use the same anchored tab for different recipients for the same document.\n* The Docusign system cannot search for text that is embedded in an image when checking for anchor strings.\n* X or Y offsets supplied for a tab apply to all instances of the tab in the document. To use different offsets at different locations in the document for the same recipient, create multiple, unique anchor tabs.\n* If the Y offset value of an anchor string would force a tab outside of the page boundaries, the tag is placed at the page boundary. If the X offset value places a tab outside of the page boundaries, the error message `Invalid_User_Offset` is sent. The error message includes the X offset that resulted in the error.\n* The system does not support an anchor string embedded in the form of a PDF X-object in the document.\n* The system does not re-flow the text that surrounds the anchor tabs. It is the responsibility of the document author to provide sufficient white space to contain the potential width of the ultimate tab value.\n\n### Tips and Tricks\n\nThe following are tips for effective use of anchor tags:\n* In order to avoid unintentional conflicts between text contained in an `anchorString` property and the text that naturally exists in documents, establish a codified syntax for the anchor string text that is unlikely to appear elsewhere in a document.\n* Develop an extensible and consistent syntax that can be used across multiple document types.\n* Especially for documents that have variable numbers of tabs or signers, author the source document to include hidden anchor tabs for all potential signers/permutations. Then, control the tabs that are actually placed by including/excluding the anchor tabs in the request. This approach allows a single document to be used for all use cases instead of maintaining separate documents for each scenario.\n\n## Automatically Populating Tabs\n\nIf you want similar tab types\nto automatically populate with the same data,\nyou must follow these guidelines:\n\n* Each `tabLabel` entry must have the characters\n `\\\\*` in front of the label.\n If you omit the `\\\\*` prefix,\n only the first occurrence of the tab is populated.\n\n When automatically populating tabs,\n the `tabLabel` must not contain any spaces.\n In the JSON example below,\n the Text tabs properties `StudentLastName` and `StudentFirstName`\n will be auto-populated as specified (\"Doe\" and \"John\")\n each place they appear throughout the envelope.\n\n ```\n \"tabs\": {\n \"textTabs\": [\n {\n \"tabLabel\": \"\\\\*StudentLastName\",\n \"value\": \"Doe\"\n },\n {\n \"tabLabel\": \"\\\\*StudentFirstName\",\n \"value\": \"John\"\n }]\n }\n ```\n\n* Note that `\\\\*` matches _anything_. If you were to add\n another tab with the `tabLabel` set to `\\\\*Name` to the\n example above, it would end up matching the other two\n labels as well.\n\n* Each occurrence of the tab must have identical properties.\n\n For example, suppose there are two Text tabs in a document,\n each with `tabLabel` set to `Name`.\n If one tab has the `bold` property set to **true,**\n and the other has the `bold` property set to **false,**\n only the first one will be populated.\n In order to automatically populate both occurrences\n of the `Name` Text tabs,\n the `bold` property must be set to the same value for both tabs.\n" + }, + { + "name": "EnvelopeDocumentTabs", + "description": "Document tabs are tabs that are associated with a document rather than with a recipient." + }, + { + "name": "TemplateDocumentTabs", + "description": "The EnvelopeDocumentTabs resource provides methods that enable you to manage tabs in a template." + }, + { + "name": "EnvelopeViews", + "description": "Provides a URL that you can embed in your application\nto provide access to the Docusign UI.\n\n### Related topics\n\n- [Embedded signing and sending](/docs/esign-rest-api/esign101/concepts/embedding/)\n- [How to send an envelope via your app](/docs/esign-rest-api/how-to/embedded-sending/)\n- [Introducing customizable embedded sending](https://www.docusign.com/blog/developers/introducing-customizable-embedded-sending)\n\n" + }, + { + "name": "AccountSealProviders", + "description": "" + }, + { + "name": "Accounts", + "description": "The Accounts resource provides methods that allow you to create, delete, and manage your accounts." + }, + { + "name": "AccountSignatureProviders", + "description": "Standards-Based Signatures (SBS) is the label used to describe Docusign's suite of signatures that comply with regional and industry regulations, such as the electronic IDentification, Authentication and trust Services (eIDAS) regulation in Europe.\n\n## Feature Differences When Using Standards-Based Signatures\n\nSome Docusign features are not compatible with Standards-Based Signatures, while others work somewhat differently. It's important to understand these key differences.\n\n### Docusign Features Not Compatible with SBS\n\n- Attachment by fax\n- Concatenation of signer attachments\n- Legacy digital signatures\n- Markup\n- Notary\n\n### Docusign Features That Change with SBS\n\nThe following features work slightly differently with SBS:\n\n- **Advanced Correct:** After the first signature, adding or deleting a field is not allowed. This behavior occurs because SBS does not allow adding or removing form fields after a digital signature has already been applied to a PDF.\n- **Downloading Combined Envelopes:** A combined PDF is not digitally signed. This behavior occurs because concatenating digitally signed PDFs breaks the digital signatures on the PDFs.\n- **Freeform Signing:** After someone has signed, allows only signature and initials on free-form. This behavior occurs because if another signer has already signed the document, adding fields other than signature fields will break the existing digital signatures on the document.\n- **Watermarks:** All watermarks are added as PDF annotations. This behavior occurs because burning the watermark into the PDF will break the digital signatures on the document.\n- **Wet Signing:** Wet-signed documents are added as new documents to envelopes. This behavior results in the uploaded or faxed, physically signed document being added as a new document to the envelope. This new document gets only a platform seal.\n\nFor more information, see [Standards Based Signatures](/docs/esign-rest-api/esign101/concepts/standards-based-signatures/)." + }, + { + "name": "BillingPlans", + "description": "The Billing resource provides methods that allow you to manage the billing plans associated with an account." + }, + { + "name": "Invoices", + "description": "The Invoices resource provides methods that allow you to manage the invoices for an account." + }, + { + "name": "Payments", + "description": "The Payments resource provides methods that allow you to manage payments for an account.\n\nThese calls can only be used by users with account administrator privileges." + }, + { + "name": "CloudStorage", + "description": "The CloudStorage resource provides methods that allow you to list files stored on your cloud storage provider." + }, + { + "name": "CloudStorageProviders", + "description": "The following providers are supported:\n\n* Google Drive\n* Dropbox\n* Box\n* Evernote\n* OneDrive\n\nTo use cloud storage files, you must first give Docusign access to your cloud storage provider. You can disconnect authorized a cloud storage provider at any time." + }, + { + "name": "ConnectConfigurations", + "description": "The `ConnectConfigurations` resource enables you to configure the\nDocusign Connect service for your account.\n\nYou can use this resource to configure account-level webhooks\nthat send notifications about every envelope sent from your\naccount. You can set account-level webhooks to listen for events\nfor envelopes sent by a specific user on your account, by\nmultiple specific users, or from any of the users on your\naccount. These events will be tracked, and can be delivered to a\nlistening application.\n\n**Note:** To create an envelope-level webhook instead of using\naccount-level webhooks, use the Envelopes::Create method and add\nan `eventNotification` object to an envelope object.\n\n\nDocusign Connect offers two delivery modes:\n\n- Send Individual Messages\n- Send Aggregated Messages\n\nBy default, the Send Individual Messages mode\nis enabled.\nYou can disable it\n(enabling Aggregated Messages mode)\nthrough Docusign Admin\nin the **Updates** section.\n\n\n**Note:** In either mode, Connect\n[must be enabled](https://support.docusign.com/s/document-item?bundleId=pik1583277475390&topicId=ctv1583277395112.html)\nin your Docusign account. It is not enabled by default.\n\n\n## Send Individual Messages\n\nSend Individual Messages (SIM) mode is the default.\n\nIn this mode Docusign delivers notifications for each\nenvelope events individually.\nWhen a final recipient completes an envelope,\nyour listener will receive\na single **Recipient Signed/Completed** event\nfollowed by a single **Envelope Signed/Completed** event\nfor the final participating party on the agreement.\n\nFor more information about SIM mode, see\n[Using Connect's Send Individual Messages Feature](https://www.docusign.com/blog/developers/using-connects-send-individual-messages-feature).\n\n\n\n## Aggregated Messages\n\nIn Aggregated Messages mode,\nConnect aggregates similar events\ninto a single delivery.\nSimilar or simultaneous events\nare aggregated,\nso that your listener doesn't\nreceive extraneous messages.\n\nWhen the final recipient signs an envelope, the\nsystem delivers a single, aggregated Connect event, rather than\nseparate Recipient: complete and Envelope: complete messages.\nThis aggregation process ensures that you only receive the\nminimal viable number of messages about an envelope's life cycle.\n\n\n" + }, + { + "name": "ConnectEvents", + "description": "The ConnectEvents resource provides methods that allow you to read, delete, and republish the connect logs associated with an envelope.\n\nThe most recent 100 logs are stored. Logs are deleted after 15 days." + }, + { + "name": "CustomTabs", + "description": "The CustomTabs resource provides methods that allow you create and manage custom tabs based on the existing Docusign tabs.\n\nYou can create a tab with pre-defined properties, such as a text tab with a certain font type and validation pattern. Users can access the custom tabs when sending documents through the Docusign web application.\n\nCustom tabs can be created based on the approve, checkbox, company, date, date signed, decline, email, email address, envelope ID, first name, formula, full name, initial here, last name, list, note, number, radio, sign here, signer attachment, SSN, text, title, and zip tabs." + }, + { + "name": "RequestLogs", + "description": "The RequestLogs resource provide methods that allow you to retrieve and delete the API request log files.\n\nThe log files contain the API requests associated with your integration. They can aid you in troubleshooting specific issues within an integration, or if Docusign Support requests an API trace log. " + }, + { + "name": "Resources", + "description": "The Resources resource provides a method which retrieves the base resources that are available." + }, + { + "name": "Services", + "description": "The Services resource provides a method that allow you to retrieve the available service versions." + }, + { + "name": "EnvelopeConsumerDisclosures", + "description": "The `EnvelopeConsumerDisclosures` resource provides methods that enable you to retrieve the Electronic Record and Signature Disclosure (ERSD) for an envelope recipient. This is the disclosure that displays to each new recipient who is going to sign or add other information, or who is required to view the documents you send to them. The recipient must read and agree to the terms of the disclosure before they can access and take action on the documents you send. The ERSD does not apply to copy-only recipients, but does apply to recipients who must sign or view your documents.\n\nYou can retrieve either the default ERSD that Docusign provides for U.S.-based transactions, or a custom ERSD.\n\n## Languages\n\nYou specify the language of the disclosure version that you want to retrieve by using the `langCode` parameter.\n\n**Important:** The system does not translate the ERSD for you. An account administrator must create a version of the account-level disclosure for each language that your signers use. \n\nFor more information, see [Legal Disclosure](https://support.docusign.com/s/document-item?bundleId=pik1583277475390&topicId=aqg1583277336549.html)." + }, + { + "name": "EnvelopeDocuments", + "description": "\n\n\nThe EnvelopeDocuments resource provides methods\nthat manage documents in an envelope.\nYou can:\n* add one or more documents to the envelope\n* retrieve one or more documents from the envelope\n* delete documents from the envelope\n\nAll of the methods in this resource\noperate on on an existing envelope.\nBefore you can add documents\nto an envelope,\nyou must first create it\nwith the [Envelopes: create][envelopescreate] method.\n\n\n[envelopescreate]: /docs/esign-rest-api/reference/envelopes/envelopes/create/\n\n" + }, + { + "name": "EnvelopeEmailSettings", + "description": "The EnvelopeEmailSettings provide methods that allow you to manage the email override settings for an envelope.\n\nEmail override settings change the reply to email address, name, or the BCC for email archive information, for the envelope. Note that changing email settings will only affect email communications that occur after the addition was made.\n\nThe BCC Email address feature is designed to provide a copy of all email communications for external archiving purposes. Docusign recommends that envelopes sent using the BCC for Email Archive feature, including the BCC Email Override option, include additional signer authentication options. To send a copy of the envelope to a recipient who does not need to sign, use a Carbon Copies or Certified Deliveries Recipient Type." + }, + { + "name": "Envelopes", + "description": "The Envelope resource provides methods that allow you to manipulate and monitor envelopes. \n\nOnce you have authenticated the user you can use the Envelopes: createEnvelope method to create an envelope. You can call the EnvelopeDocuments: update or EnvelopeDocuments: updateList method to add additional documents.\n\nSetting the `status` property on the envelope to `sent` allows you to send it or `created` to save it as a draft.\n\nYou can receive envelope event notifications by setting the `eventNotification` properties. When the envelope or recipient status changes to one of the specified status codes, a notification is sent to a URL that you specify.\n\nIf you have an envelope that you have previously saved, you can modify the subject and message, send it, void it, or place it in the purge queue using the Envelope: update method.\n\nIn addition to receiving notifications you can monitor the status of the envelopes using the following methods:\n* Envelope: getEnvelope - To get the status of a envelope. \n* Envelope: listStatus - To get the envelope status for a set of envelopes.\n* Envelope: listStatusChanges - To get status changes information for one or more envelopes. \n\nIf you need to delete a page from a document in an envelope, use the Envelope: deleteDocumentPage method.\n\nThe resource also includes a number of methods that allow you to retrieve and set the initials and signature for certain types of recipients on the document." + }, + { + "name": "EnvelopeTemplates", + "description": "The EnvelopeTemplates resource provides methods that allow you to add and delete templates on envelopes and documents." + }, + { + "name": "Folders", + "description": "The Folders resource provides methods that allow you to view contents of folders on the account and move envelopes and templates between folders." + }, + { + "name": "GroupBrands", + "description": "For the custom groups you define for your account, you can assign brands to specify the ones that group members can use. Group members can use the available brands when they send envelopes or create templates. For more information, see [Assign Brands to Groups](https://support.docusign.com/s/document-item?bundleId=pik1583277475390&topicId=iby1583277366335.html)." + }, + { + "name": "Groups", + "description": "The Groups resource provides methods that allow you to manage groups for the account.\n\nGroups can be used to help manage users by associating users with a group. A group can be associated with a Permission Profile, which sets the user permissions for users in that group without having to set the `userSettings` property for each user. You are not required to set Permission Profiles for a group, but this makes it easier to manage user permissions for a large number of users. Groups can also be used with template sharing to limit user access to templates." + }, + { + "name": "GroupUsers", + "description": "The GroupUsers resource provides methods that allow you to manage the users in a group." + }, + { + "name": "SigningGroups", + "description": "The SigningGroups resource provides methods that enable you to manage [signing groups](https://support.docusign.com/s/document-item?bundleId=gav1643676262430&topicId=zgn1578456447934.html).\n\nSigning groups enable you to create a group of people to which an envelope is sent. Any member of that group can open an envelope and sign the documents in the envelope with their own signature, even though a signature field was not directly assigned to them. When the signing group option is used, group members that open and sign the envelope are tracked in the envelope history and certificate.\n\nWhen one group member opens the envelope, it is temporarily locked and if other members try to open the envelope they will see a message saying the envelope is currently opened. If the group member exits the envelope without finishing the lock expires, allowing other group members to open and complete the envelope.\n\nWhen the envelope is complete, all members of the group will receive a completed notification and can access the completed envelope. \nThe envelope history and Certificate of Completion will show that the envelope was sent to a signing group and record which members viewed and signed the envelope.\n\nAn account can have a maximum of 50 signing groups. Each signing group can have a maximum of 50 group members.\n\nThe signing groups feature is only supported in certain Docusign Enterprise and System Automated Premium plans. To access this functionality, contact your Account Manager or Docusign Support (support@docusign.com) for assistance.\n\nNext steps:\n* Learn [how to create a signing group via the API](https://www.docusign.com/blog/dsdev-common-api-tasks-create-a-signing-group).\n* Learn [how to request a signature from a signing group](https://www.docusign.com/blog/developers/common-api-tasks-requesting-signature-signing-group).\n" + }, + { + "name": "SigningGroupUsers", + "description": "The SigningGroupUsers resource provides methods that allow you to manage users in Signing Groups." + }, + { + "name": "TemplateRecipients", + "description": "The TemplateRecipients resource allows you manage the recipients of an template.\n\nThe exact parameters associated with a recipient depend on the recipient type. There are seven recipient types: Agents, Carbon Copies, Certified Deliveries, Editors, In Person Signers, Intermediaries, and Signers.\n\nNot all recipients are are available to all account types, review you account plan to determine which recipient types are available to you. If you are working in the development environment, all recipient types are available.\n\nEach recipient type is described below:\n\n[Agents](#agents): This recipient can add name and email information for recipients that appear after the recipient in routing order.\n\n[Carbon Copies](#carboncopies): Use this action if the recipient should get a copy of the template, but the recipient does not need to sign, initial, date or add information to any of the documents. This type of recipient can be placed in any order in the recipient list. The recipient receives a copy of the template when the template reaches the recipient's order in the process flow and when the template is completed.\n\n[Certified Deliveries](#certifiedDeliveries): Use this action if the recipient must receive the completed documents for the template to be completed, but the recipient does not need to sign, initial, date or add information to any of the documents.\n\n[Editors](#editors): This recipient has the same management and access rights for the template as the sender and can make changes to the template as if they were using the Advanced Correct feature. This recipient can add name and email information, add or change the routing order and set authentication options for the remaining recipients. Additionally, this recipient can edit signature/initial tabs and data fields for the remaining recipients. The recipient must have a Docusign account to be an editor.\n\n[In Person Signers](#inPerson): Use this action if the signer is in the same physical location as a Docusign user who will act as a Signing Host for the transaction. The recipient added is the Signing Host and new separate Signer Name field appears after Sign in person is selected.\n\n[Intermediaries](#intermediaries): This recipient can, but is not required to, add name and email information for recipients at the same or subsequent level in the routing order (until subsequent Agents, Editors or Intermediaries recipient types are added).\n\n[Signers](#signers): Use this action if your recipient must sign, initial, date or add data to form fields on the documents in the template.\n\n### Core JSON Layout\n\nThe following shows the core JSON layout for a recipient.\n\n```\n\"email\": \"email.name@company.com\",\n \"name\": \"recipient name\",\n \"accessCode\": \"\",\n \"addAccessCodeToEmail\": false,\n \"clientUserIs\": null,\n \"embeddedRecipientStartURL\": \"string\",\n \"customFields\": {\n \"sample string 1\",\n \"sample string 2\"\n },\n \"emailNotification\"{\n \"emailBody\":\"email text\",\n \"emailSubject\":\"Subject text\",\n \"supportedLanguage\":\"en\",\n },\n \"excludedDocuments\": [\"2\", \"4\"],\n \"idCheckConfigurationName\": null,\n \"idCheckInformationInput\": {\n \"addressInformationInput\": {\n \"addressInformation\": {\n \"street1\": \"sample string 1\",\n \"street2\": \"sample string 2\",\n \"city\": \"sample string 3\",\n \"state\": \"sample string 4\",\n \"zip\": \"sample string 5\",\n \"zipPlus4\": \"sample string 6\"\n },\n \"displayLevelCode\": \"sample string 1\",\n \"receiveInResponse\": \"sample string 2\"\n },\n \"dobInformationInput\": {\n \"dateOfBirth\": \"sample string 1\",\n \"displayLevelCode\": \"sample string 2\",\n \"receiveInResponse\": \"sample string 3\"\n },\n \"ssn4InformationInput\": {\n \"ssn4\": \"sample string 1\",\n \"displayLevelCode\": \"sample string 2\",\n \"receiveInResponse\": \"sample string 3\"\n },\n \"ssn9InformationInput\": {\n \"ssn9\": \"sample string 1\",\n \"displayLevelCode\": \"sample string 2\"\n }\n },\n \"inheritEmailNotificationConfiguration\": false,\n \"note\": \"\",\n \"phoneAuthentication\": {\n \"recipMayProvideNumber\": \"sample string 1\",\n \"validateRecipProvidedNumber\": \"sample string 2\",\n \"recordVoicePrint\": \"sample string 3\",\n \"senderProvidedNumbers\": [\n \"sample string 1\",\n \"sample string 2\"\n ]\n },\n \"recipientAttachment\": null,\n \"recipientCaptiveInfo\": null,\n \"recipientId\": \"1\",\n \"requireIdLookup\": false,\n \"roleName\": \"\",\n \"routingOrder\": 1,\n \"samlAuthentication\": {\n \"samlAssertionAttributes\": [\n {\n \"name\": \"string\",\n \"value\": \"string\"\n },\n {\n \"name\": \"string\",\n \"value\": \"string\"\n }\n ]\n },\n \"smsAuthentication\": {\n \"senderProvidedNumbers\":[\n \"sample string 1\",\n \"sample string 2\"\n ]\n },\n \"socialAuthentications\": null,\n \"templateAccessCodeRequired\": false,\n \"templateLocked\": false,\n \"templateRequired\": false,\n...\n```\n\n### Core Recipient Parameters\n\nThe following table contains the descriptions for the core properties for a recipient.\n\n| Name | Required? | Schema Type | Description |\n| --- | --- | --- | --- |\n| email | Yes | Email | Email of the recipient. Notification will be sent to this email id.
Maximum Length: 100 characters. |\n| name | Yes | String | Full legal name of the recipient.
Maximum Length: 100 characters. |\n| accessCode | No | String | This optional element specifies the access code a recipient has to enter to validate the identity.
Maximum Length: 50 characters. |\n| addAccessCodeToEmail | No | Boolean | This optional attribute indicates that the access code is added to the email sent to the recipient; this nullifies the Security measure of Access Code on the recipient. |\n| clientUserId | No | String | This specifies whether the recipient is embedded or remote.

If the `clientUserId` property is not null then the recipient is embedded. Note that if the `ClientUserId` property is set and either `SignerMustHaveAccount` or `SignerMustLoginToSign` property of the account settings is set to **true,** an error is generated on sending. |\n| embeddedRecipientStartURL | No | String | This is a sender provided valid URL string for redirecting an embedded recipient. When using this option, the embedded recipient still receives an email from Docusign, just as a remote recipient would, but when the document link in the email is clicked the recipient is redirected, through Docusign, to this URL to complete their actions. When routing to the URL, it is up to the sender's system (the server responding to the URL) to then request a recipient token to launch a signing session.

If the value `SIGN_AT_DOCUSIGN` is used for this node, the recipient is directed to an embedded signing or viewing process directly at Docusign. The signing or viewing action is initiated by the Docusign system and the transaction activity and Certificate of Completion records will reflect this. In all other ways the process is identical to an embedded signing or viewing operation that would be launched by any partner.

It is important to remember that in a typical embedded workflow the authentication of an embedded recipient is the responsibility of the sending application and Docusign expects that senders will follow their own process for establishing the recipient's identity. In this workflow the recipient goes through the sending application before the embedded signing or viewing process in initiated. However, when the sending application sets the `EmbeddedRecipientStartURL` property to `SIGN_AT_DOCUSIGN`, the recipient goes directly to the embedded signing or viewing process bypassing the sending application and any authentication steps the sending application would use. In this case, Docusign recommends that one of the normal Docusign authentication features (Access Code, Phone Authentication, SMS Authentication, etc.) be used to verify the identity of the recipient.

If the `clientUserId` property is NOT set and the `embeddedRecipientStartURL` property is set, Docusign ignores the redirect URL and launch the standard signing process for the email recipient. Information can be appended to the `embeddedRecipientStartURL` property using merge fields. The available merge fields items are: templateId, recipientId, recipientName, recipientEmail, and customFields. The customFields must be part of the recipient or template. The merge fields are enclosed in double brackets.

_Example_:
`http://senderHost/[[mergeField1]]/ beginSigningSession? [[mergeField2]]&[[mergeField3]]` |\n| customFields | No | customField |An optional array of strings that allows the sender to provide custom data about the recipient. This information is returned in the template status but otherwise not used by Docusign. String `customField` properties have a maximum length of 100 characters. |\n| emailNotification | No | emailNotification | An optional complex type that has information for setting the language for the recipient's email information. It is composed of three elements:

*emailBody*: a string with the email message sent to the recipient.
Maximum Length: 10000 characters.

*emailSubject*: a string with the subject of the email sent to the recipient.
Maximum Length: 100 characters.

*supportedLanguage*: The simple type enumeration of the language used. The supported languages, with the language value shown in parenthesis, are: Arabic (ar), Bahasa Indonesia (id), Bahasa Melayu (ms) Bulgarian (bg), Czech (cs), Chinese Simplified (zh_CN), Chinese Traditional (zh_TW), Croatian (hr), Danish (da), Dutch (nl), English US (en), English UK (en_GB), Estonian (et), Farsi (fa), Finnish (fi), French (fr), French Canada (fr_CA), German (de), Greek (el), Hebrew (he), Hindi (hi), Hungarian (hu), Italian (it), Japanese (ja), Korean (ko), Latvian (lv), Lithuanian (lt), Norwegian (no), Polish (pl), Portuguese (pt), Portuguese Brazil (pt_BR), Romanian (ro),Russian (ru), Serbian (sr), Slovak (sk), Slovenian (sl), Spanish (es),Spanish Latin America (es_MX), Swedish (sv), Thai (th), Turkish (tr), Ukrainian (uk) and Vietnamese (vi).

**IMPORTANT:** If this is enabled for one recipient, it overrides the Template Subject and `EmailBlurb` property settings. Also, you must set the `emailNotification` property for all recipients. |\n| excludedDocuments | No | Array of Strings | Specifies the documents that are not visible to this recipient. Document Visibility must be enabled for the account and the enforceSignerVisibility property must be set to true for the template to use this.

When the enforceSignerVisibility property is set to **true,** documents with tabs can only be viewed by signers that have a tab on that document. Recipients that have an administrative role (Agent, Editor, or Intermediaries) or informational role (Certified Deliveries or Carbon Copies) can always see all the documents in an template, unless they are specifically excluded using this setting when an template is sent. Documents that do not have tabs are always visible to all recipients, unless they are specifically excluded using this setting when an template is sent. |\n| idCheckConfigurationName | No | String |Specifies authentication check by name. The names used here must be the same as the authentication type names used by the account (these name can also be found in the web console sending interface in the Identify list for a recipient). This overrides any default authentication setting.

_Example_:
Your account has ID Check and SMS Authentication available and in the web console Identify list these appear as 'ID Check $' and 'SMS Auth $'. To use ID check in an template, the `idCheckConfigurationName` property must be set to `ID Check $`. To use SMS, it must be set to `SMS Auth $` and you must add phone number information to the `smsAuthentication` node.|\n| iDCheckInformationInput | No | IdCheckInformationInput | This complex element contains input information related to a recipient ID check. It can include the following information.

*addressInformationInput*: Used to set recipient address information and consists of:

*addressInformation*: consists of six elements, with street2 and zipPlus4 being optional. The elements are: street1, street2, city, state, zip, zipPlus4\\. The maximum number of characters in each element are: street1/street2 = 150 characters, city = 50 characters, state = 2 characters, and zip/zipPlus4 = 20 characters.

displayLevelCode: Specifies the display level for the recipient. Values are: ReadOnly, Editable, or DoNotDisplay.

*receiveInResponse*: A Boolean element that specifies if the information needs to be returned in the response.

*dobInformationInput*: Used to set recipient date of birth information and consists of:

*dateOfBirth*: Specifies the recipient's date, month and year of birth.

*displayLevelCode*: Specifies the display level for the recipient. Values are: ReadOnly, Editable, or DoNotDisplay.

*receiveInResponse*: A Boolean element that specifies if the information needs to be returned in the response.

*ssn4InformationInput*: Used to set the last four digits of the recipient's SSN information and consists of:

*ssn4*: Specifies the last four digits of the recipient's SSN.

*displayLevelCode*: Specifies the display level for the recipient. Values are: ReadOnly, Editable, or DoNotDisplay.

*receiveInResponse*: A Boolean element that specifies if the information needs to be returned in the response.

*ssn9InformationInput*: Used to set the recipient's SSN information. Note that the ssn9 information can never be returned in the response. The ssn9 input consists of:



*ssn9*: Specifies the recipient's SSN.

*displayLevelCode*: Specifies the display level for the recipient. Values are: ReadOnly, Editable, or DoNotDisplay. |\n| inheritEmailNotificationConfiguration | No | Boolean | Optional element. If true and the template recipient creates a Docusign account after signing, the Manage Account Email Notification settings are used as the default settings for the recipient's account. |\n| note | No | String | A note that is unique to this recipient. This note is sent to the recipient via the signing email. The note displays in the signing UI near the upper left corner of the document on the signing screen.
Maximum Length: 1000 characters. |\n| phoneAuthentication | No | RecipientPhoneAuthentication | Optional element. Contains the elements:

*recipMayProvideNumber*:Boolean. When **true** thenrecipient can use whatever phone number they choose to.

*senderProvidedNumbers*: ArrayOfString. A list of phone numbers the recipient can use.

*recordVoicePrint* - Reserved

*validateRecipProvidedNumber* - Reserved| |\n| recipientAttachment | No | Attachment | Reserved |\n| recipientId | No | String | Unique for the recipient. It is used by the tab element to indicate which recipient is to sign the Document. |\n| requireIdLookup | No | Boolean | When **true,** the recipient is required to use the specified ID check method (including Phone and SMS authentication) to validate their identity. |\n| roleName | No* | String | Optional element. Specifies the role name associated with the recipient.

This is required when working with template recipients. |\n| routingOrder | Yes | String | This element specifies the routing order of the recipient in the template. |\n| samlAuthentication | No | samlAssertionAttributes | Optional element, account must be set up to use SSO to use this. Contains the name/value pair information for the SAML assertion attributes:

*name*: The name of the SAML assertion attribute.

*value*: The value associated with the named SAML assertion attribute. |\n| smsAuthentication | No | senderProvidedNumbers | Optional element. Contains the element:

*senderProvidedNumbers*: Array that contains a list of phone numbers the recipient can use for SMS text authentication. |\n| socialAuthentications | No | Boolean | Lists the social ID type that can be used for recipient authentication. |\n| templateAccessCodeRequired | No | Boolean | Optional element. Used only when working with template recipients. When **true** and the `TemplateLocked` parameter is set to **true,** the sender must enter an access code. |\n| templateLocked | No | Boolean | Optional element. Used only when working with template recipients. When **true,** the sender cannot change any attributes of the recipient. |\n| templateRequired | No | Boolean | Optional element. Used only when working with template recipients. When **true,** the sender may not remove the recipient. |\n\n## Agents Recipient\n\nThis recipient can add name and email information for recipients that appear after the recipient in routing order.\n\n#### Example Agents layout\n\n```\n\"agents\": [{\n \n \"canEditRecipientEmails\": false,\n \"canEditRecipientNames\": false\n}],\n```\n\nThe additional parameters for Agents recipient are shown below:\n\n| Name | Required? | Schema Type | Description |\n| --- | --- | --- | --- |\n| canEditRecipientEmails | No | Boolean | Optional element. When **true,** the Agents Recipient associated with this Recipient can change the Recipient's pre-populated Email address. This element is only active if enabled for the account. |\n| canEditRecipientNames | No | Boolean | Optional element. When **true,** the Agents Recipient associated with this recipient can change the recipient's pre-populated name (`UserName`). This element is only active if enabled for the account. |\n\n## Carbon Copies Recipient\n\nUse this type if the recipient should get a copy of the template, but the recipient does not need to sign, initial, date, or add information to any of the documents. This type of recipient can be placed in any order in the recipient list. The recipient receives a copy of the template when the template reaches the recipient's order in the process flow and when the template is completed.\n\n#### Example Carbon Copies layout\n\n```\n\"carbonCopies\": [{\n \n```\n\nThe Carbon Copies recipient uses only the core parameters.\n\n## Certified Deliveries Recipient\n\nUse this action if the recipient must receive the completed documents for the template to be completed, but the recipient does not need to sign, initial, date, or add information to any of the documents.\n\n#### Example Certified Deliveries layout\n\n```\n\"certifiedDeliveries\": [{\n\n}],\n```\nThe Certified Deliveries recipient uses only the core parameters.\n\n## Editors Recipient\n\nThis recipient has the same management and access rights for the template as the sender and can make changes to the template as if they were using the Advanced Correct feature. This recipient can add name and email information, add or change the routing order and set authentication options for the remaining recipients. Additionally, this recipient can edit signature/initial tabs and data fields for the remaining recipients. The recipient must have a Docusign account to be an editor.\n\n#### Example Editors layout\n\n```\n\"editors\": [{\n \n \"canEditRecipientEmails\": false,\n \"canEditRecipientNames\": false\n}],\n```\n\nThe additional parameters for Editors recipient are shown below:\n\n| Name | Required? | Schema Type | Description |\n| --- | --- | --- | --- |\n| canEditRecipientEmails | No | Boolean | Optional element. When **true,** the Editors Recipient associated with this Recipient can change the Recipient's pre-populated Email address. This element is only active if enabled for the account. |\n| canEditRecipientNames | No | Boolean | Optional element. When **true,** the Editors Recipient associated with this recipient can change the recipient's pre-populated name (`UserName`). This element is only active if enabled for the account. |\n\n## In Person Signers Recipient\n\nUse this type if the signer is in the same physical location as a Docusign user who will act as a Signing Host for the transaction. The recipient added is the Signing Host and new separate Signer Name field appears after Sign in person is selected.\n\n#### Example In Person Signers layout\n\n```\n\"inPersonSigners\": [{\n \"hostEmail\": \"signing.host@company.com\",\n \"hostName\": \"Mike Host\",\n \n \"autoNavigation\": false,\n \"defaultRecipient\": false,\n \"signInEachLocation\": false,\n \"signatureInfo\": null,\n \"signerEmail\": \"signer.name@company.com\",\n \"signerName\": \"MC Clap Your Hands\",\n \"tabs\": {\n \"approveTabs\": null,\n \"checkboxTabs\": null,\n \"companyTabs\": null,\n \"dateSignedTabs\": null,\n \"dateTabs\": null,\n \"declineTabs\": null,\n \"emailTabs\": null,\n \"templateIdTabs\": null,\n \"fullNameTabs\": null,\n \"initialHereTabs\": null,\n \"listTabs\": null,\n \"noteTabs\": null,\n \"numberTabs\": null,\n \"radioGroupTabs\": null,\n \"signHereTabs\": [{\n \"signerAttachmentTabs\": null,\n \"ssnTabs\": null,\n \"textTabs\": null,\n \"titleTabs\": null,\n \"zipTabs\": null\n }\n}],\n```\n\nThe additional and changed parameters for In Person Signers recipient are shown below:\n\n| Name | Required? | Schema Type | Description |\n| --- | --- | --- | --- |\n| hostEmail | Yes | Email | Required element for In Person Signers recipient Type.
Maximum Length: 100 characters.

Specifies the email for the signing host. |\n| hostName | Yes | String | Required element for In Person Signers recipient Type.
Maximum Length: 100 characters.

Specifies the email for the signing host.|\n| autoNavigation | No | Boolean | Specifies whether auto navigation is set for the recipient.|\n| defaultRecipient | No | Boolean | When **true,** this is the default recipient for the template. This option is used with the CreateTemplateFromTemplatesAndForms method. |\n| signInEachLocation | No | Boolean | When **true** and the feature is enabled in the sender's account, the signing recipient is required to draw signatures and initials at each signature/initial tab (instead of adopting a signature/initial style or only drawing a signature/initial once). |\n| signatureInfo | No | String | Optional element only used with recipient types In Person Signers and Signers.

Allows the sender to pre-specify the signature name, signature initials, and signature font used in the signature stamp for the recipient. |\n| signerEmail | No | String | Optional element. The email address for an InPersonSigner recipient Type.
Maximum Length: 100 characters. |\n| signerName | Yes | String | Required element with recipient type In Person Signers.
Maximum Length: 100 characters.

The full legal name of a signer for the template. |\n| tabs | No | Tab | Optional element only used with recipient types In Person Signers and Signers.

Specifies the Tabs associated with the recipient. See the See the [EnvelopeRecipientTabs resource](/docs/esign-rest-api/reference/envelopes/enveloperecipienttabs/) for more information about tabs. for more information about tabs. |\n\n## Intermediaries Recipient\n\nThis recipient can, but is not required to, add name and email information for recipients at the same or subsequent level in the routing order (until subsequent Agents, Editors or Intermediaries recipient types are added).\n\n#### Example Intermediaries layout\n\n```\n\"intermediaries\": [{\n\n \"canEditRecipientEmails\": false,\n \"canEditRecipientNames\": false\n}],\n```\n\nThe parameters for Intermediaries recipient are shown below:\n\n| Name | Required? | Schema Type | Description |\n| --- | --- | --- | --- |\n| canEditRecipientEmails | No | Boolean | Optional element. When **true,** the Agents Recipient associated with this Recipient can change the Recipient's pre-populated Email address. This element is only active if enabled for the account. |\n| canEditRecipientNames | No | Boolean | Optional element. When **true,** the Agents Recipient associated with this recipient can change the recipient's pre-populated name (`UserName`). This element is only active if enabled for the account. |\n\n## Signers Recipient\n\nUse this action if your recipient must sign, initial, date, or add data to form fields on the documents in the template.\n\n#### Example Signers layout\n\n```\n\"Signers\": [{\n\n \"autoNavigation\": false,\n \"defaultRecipient\": false,\n \"signInEachLocation\": false,\n \"signatureInfo\": null,\n \"tabs\": {\n \"approveTabs\": null,\n \"checkboxTabs\": null,\n \"companyTabs\": null,\n \"dateSignedTabs\": null,\n \"dateTabs\": null,\n \"declineTabs\": null,\n \"emailTabs\": null,\n \"templateIdTabs\": null,\n \"fullNameTabs\": null,\n \"initialHereTabs\": null,\n \"listTabs\": null,\n \"noteTabs\": null,\n \"numberTabs\": null,\n \"radioGroupTabs\": null,\n \"signHereTabs\": [{\n \"signerAttachmentTabs\": null,\n \"ssnTabs\": null,\n \"textTabs\": null,\n \"titleTabs\": null,\n \"zipTabs\": null\n }\n \"deliveryMethod\":\"\",\n \"deliveredDateTime\":\"String Content\",\n \"signedDateTime\":\"String Content\",\n \"offlineAttributes\":{\n \"deviceName\":\"String Content\",\n \"deviceModel\":\"String Content\",\n \"gpsLatitude\":\"String Content\",\n \"gpsLongitude\":\"String Content\",\n \"accountEsignId\":\"String Content\"\n }\n}],\n```\n\nThe additional parameters for Signers recipient are shown below:\n\n| Name | Required? | Schema Type | Description |\n| --- | --- | --- | --- |\n| autoNavigation | No | Boolean | Specifies whether auto navigation is set for the recipient.|\n| defaultRecipient | No | Boolean | When **true,** this is the default recipient for the template. This option is used with the CreateTemplateFromTemplatesAndForms method. |\n| signInEachLocation | No | Boolean | When **true** and the feature is enabled in the sender's account, the signing recipient is required to draw signatures and initials at each signature/initial tab (instead of adopting a signature/initial style or only drawing a signature/initial once). |\n| signatureInfo | No | String | Optional element only used with recipient types In Person Signers and Signers.

Allows the sender to pre-specify the signature name, signature initials, and signature font used in the signature stamp for the recipient. |\n| signerEmail | No | String | Optional element. The email address for an InPersonSigner recipient Type.
Maximum Length: 100 characters. |\n| signerName | Yes | String | Required element with recipient type In Person Signers.
Maximum Length: 100 characters.

The full legal name of a signer for the template. |\n| tabs | No | Tab | Optional element only used with recipient types In Person Signers and Signers.

Specifies the Tabs associated with the recipient. See the See the [EnvelopeRecipientTabs resource](/docs/esign-rest-api/reference/envelopes/enveloperecipienttabs/) for more information about tabs. section for more information about tabs. |\n| deliveryMethod | No | String | Reserved: For Docusign use only.|\n| deliveredDateTime | No | DateTime | Reserved: For Docusign use only. |\n| signedDateTime | No | DateTime | Reserved: For Docusign use only. |\n| offlineAttributes | No |   | Reserved: For Docusign use only.|\n" + }, + { + "name": "TemplateCustomFields", + "description": "The TemplateCustomFields resource provides methods that allow you manage custom fields in an template. \n\nCustom fields can be used in the templates for your account to record information about the template, help search for templates and track information. The template custom fields are shown in the Template Settings section when a user is creating an template in the Docusign member console. The template custom fields are not seen by the template recipients.\n\nThere are two types of custom fields:\n\n- `text`: Enables the sender to enter the value for the field. \n- `list`: Enables the sender to select the value of the field from a predetermined list." + }, + { + "name": "TemplateDocumentFields", + "description": "The TemplateDocumentFields resource provides methods that allow you to manage custom fields on a document.\n\nYou can create custom versions of standard fields that combine of field properties, such as font type or size, or a validation setting. \n\nNote: Some advanced features and options are supported only in certain Docusign plans. Your account plan might not support some options discussed in this help topic. For more information about which options are available for your account, check your account plan or contact your Account Manager." + }, + { + "name": "TemplateDocuments", + "description": "\n\n\nThe TemplateDocuments resource provides methods\nthat manage documents in a template.\nYou can:\n* Add one or more documents to the template\n* Retrieve one or more documents from the template\n* Delete documents from the template\n\nAll of the methods associated with this resource\noperate on an existing template.\nBefore you can add documents\nto a template,\nyou must first create it\nwith the [Templates:: Create][templatescreate] method.\n\n[templatescreate]: /docs/esign-rest-api/reference/templates/templates/create/\n" + }, + { + "name": "TemplateLocks", + "description": "The TemplateLocks resource provides methods that allow you to\nmanage locks on a template.\n\nTo prevent users from changing a template while another user is\nmodifying it, you can lock the template and set the time until\nthe lock expires.\n\nFor example, you would use the following flow:\n\n1. Lock the template.\n2. Make changes to template.\n3. Delete the template lock and save the changes. If the template has a password, you must supply the template password to save the changes.\n\n**Note:** To use template locks, the user must have envelope locking capability enabled." + }, + { + "name": "Templates", + "description": "The Template resource provides methods that allow you to manipulate and monitor templates. \n\nOnce you have authenticated the user you can use the Templates: createTemplate method to create a template. You can call the TemplateDocuments: update or TemplateDocuments: updateList method to add more documents.\n\nIf you have a template that you have previously saved, you can modify the subject and message, send it, void it, or purge it from the Docusign system using the Template: update method.\n\nIn addition to receiving notifications, you can monitor the status of the templates using the following methods:\n\n* Template: getTemplate - Gets the status of a template. \n* Template: listStatus - Gets the status for a set of templates.\n* Template: listStatusChanges - Gets status change information for one or more templates. \n\nIf you need to delete a page from a document in a template, use the Template: deleteDocumentPage method.\n\nThe resource also includes a number of methods that allow you to retrieve and set the initials and signature for certain types of recipients on the document." + }, + { + "name": "TemplateRecipientTabs", + "description": "\n\n\nThe TemplateRecipientTabs resource provides methods that let you\nadd,\nupdate,\nand delete tabs\nfrom an envelope.\nTabs are associated with a specific recipient\nin an envelope\nand are only used by the recipient types\nIn Person Signers and Signers. \n\n\n## Tab Types\n\nThis table lists the available tab types.\n\n
\n\n\n| Tab Type | Description |\n| :------- | :---------- |\n| Approve (`approve`) | Place this tab on the document if you want the recipient to approve documents in an envelope without placing a signature or initials on the document. If the recipient clicks the Approve tab during the signing process, the recipient is considered to have signed the document. No information is shown on the document for the approval, but it is recorded as a signature in the envelope history. |\n| Checkbox (`checkbox`) | Place this tab on the document in a location where the recipient can select a yes/no (on/off) type option. |\n| Company (`company`) | Place this tab on the document where you want the recipient's company name to appear. |\n| Date Signed (`dateSigned`) | Place this tab on the document where you want the date the recipient signed the document to appear. |\n| Date (`date`) | Place this tab on the document where you want the recipient to enter a date. Date tabs are single-line fields that allow date information to be entered in any format. The tooltip for this tab recommends entering the date as YYYY-MM-DD, but this is not enforced. The format entered by the signer is retained. If you need a particular date format enforced, Docusign recommends using a Text tab with a validation pattern and validation message to enforce the format. |\n| Decline (`decline`) | Place this tab on the document where you want to give the recipient the option of declining an envelope. If the recipient clicks the Decline tab during the signing process, the envelope is voided. |\n| Email Address (`emailAddress`) | Place this tab on a document where you want the recipient's email, as entered in the recipient information, to appear. |\n| Email (`email`) | This is a single-line field that accepts all characters. |\n| Envelope ID (`envelopeId`) | Place this tab on the document where you want the envelope ID for to appear. Recipients cannot enter or change the information in this tab. It is for informational purposes only. |\n| First Name (`firstName`) | Place this tab on a document where you want the recipient's first name to appear. This tab splits the recipient's name (as entered in the recipient information) into sections based on spaces and uses the first section as the first name. |\n| Formula (`formulaTab`) | This tab is used to add a calculated field to a document. Envelope recipients cannot directly enter information into the tab. The formula tab calculates and displays a new value when changes are made to the reference tab values. The reference tab information and calculation operations are entered in the \"formula\" element. See the Using the Calculated Fields Feature quick start guide or Docusign Service User Guide for more information about formulas. |\n| Full Name (`fullName`) | Place this tab on the document where you want the recipient's full name to appear. |\n| Initial Here (`initialHere`) | Place this tab where the recipient must initial the document. This tab can be set to be optional. |\n| Last Name (`lastName`) | Place this tab on a document where you want the recipient's last name to appear. This tab splits the recipient's name (as entered in the recipient information) into sections based on spaces and uses the last section as the last name. |\n| List (`list`) | This tab has a list of options that a recipient can select. The `listItems` parameter is used to set the options that can be selected. |\n| Note (`note`) | Place this tab on the document where you want to add a note for the recipient on a document. |\n| Number (`number`) | This tab is a field where the recipient can enter numbers and decimal (.) points. |\n| Radio Group (`radioGroup`) | This group tab is used to place radio buttons on a document. The `radios` parameter is used to add and place the radio buttons associated with the group. Only one radio button can be selected in a group. |\n| Sign Here (`signHere`) | Place this tab where the recipient must sign the document. This tab can be set to be optional. |\n| Signer Attachment (`signerAttachment`) | The signer attachment is where the recipient initiates the process of adding supporting documents to an envelope. |\n| SSN (`ssn`) | This tab is a single-line field where the recipient can enter numbers. A Social Security Number can be typed with or without dashes. |\n| Text (`text`) | This tab is a field where the recipient can enter any type of characters. |\n| Title (`title`) | Place this tab on the document where you want the recipient's title to appear. |\n| Zip (`zip`) | This tab is a single-line field where the recipient can enter numbers. |\n\n\n## Using Custom Tabs in Envelopes and Templates \n\nCustom Tabs can be added to envelopes and templates\nby setting the `customTabId` property\nwhen creating an envelope or template recipient\nor when adding a new tab for an existing recipient.\nThe custom tab must be added as the correct tab type.\nFor example if the custom tab type is text, it cannot be used as a number tab.\n\nWhen the `customTabId` property is set,\nthe new tab inherits all the custom tab properties.\nRequired information that is not included in the custom tab,\nsuch as document ID and page ID, must be included when adding the tab.\nIf the custom tab does not have anchor settings, the X and Y positions must be included.\n\nAfter the tab is created,\nit is treated as any other tab for updating or deleting. \n\n## Anchoring Tabs\n\nThe tab anchoring option\nallows you to send documents for signature\nthat do not have a fixed layout or format.\nIn these documents you might not know\nthe absolute location of the tabs\nwhen you design your API client application because the tabs must move with text.\nAs an alternative to sending X and Y coordinates for tabs,\nthe Docusign Service can derive an anchor location for the tab\nby correlating anchor information to data within the document.\n\nWhen the Docusign Service receives a request that contains tabs\nwith anchor information,\nit searches the document for instances of the `anchorString` property.\nWhen found,\nit places a tab of the specified type for the designated recipient.\nTab positions are established by setting offsets for the tab.\n\nWhen you apply tabs to the document,\nDocusign does not remove or replace the text in the `anchorString` property. You can hide codified anchors by using the same font color as the background of the document. So the anchor can be used by Docusign processes and it will not be visible on the document.\n\nTo use an anchoring option:\n\n1. Identify the location in the document by text string. You can use a pre-existing text string or add a new one.\nFor best performance Docusign recommends using single word anchor strings when possible, especially when there are a large number of pages in the envelope. \nFor example, you might want to add a Sign Here tab to the \"Borrower's Signature\" lines in a document, but that phrase might occur in places in the document where you don't want to tab to appear. In this case, you could add the text \"BorrowerSignHere\" in white font color (so that isn't visible in the document) to all the places you want Sign Here tabs to appear and use \"BorrowerSignHere\" as the anchor string. \n1. Reference the anchor through the `anchorString` property of the tab.\n1. Determine the offset from the anchor string location to where the tab should be placed. \n\nSetting a positive value in the `anchorXOffset` property moves the tab right on the page and positive values in the `anchorYoffset` prove moves the tab down the page. The `anchorUnits` property specifies the units used for the offsets.\nFor Sign Here and Initial Here tabs the bottom-left of the anchor string is equivalent to position (0,0), and the bottom-left of the tab graphic is placed relative to that.\nFor all other tabs the bottom-left of the anchor string is equivalent to position (0,0), and the top-left of the tab graphic is placed relative to that.\nDocusign does not currently provide tools to derive the offset values. Determination of the proper offset will likely require some trial-and-error.\n\n### Rules for working with anchor tags\n\nWhen anchor tabs are used, all documents in the envelope are searched for the `anchorString` property.\n\n* You set the text of the anchor string in the `anchorString` property. Docusign tabs are created for each instance of the `anchorString` property within the document, so special care must be taken to establish unique anchor strings that do not result in unintentional tabs.\n* You cannot use the same anchored tab for different recipients for the same document.\n* The Docusign system cannot search for text that is embedded in an image when checking for anchor strings.\n* X or Y offsets supplied for a tab apply to all instances of the tab in the document. To use different offsets at different locations in the document for the same recipient, create multiple, unique anchor tabs.\n* If the Y offset value of an anchor string would force a tab outside of the page boundaries, the tag is placed at the page boundary. If the X offset value places a tab outside of the page boundaries, the error message `Invalid_User_Offset` is sent. The error message includes the X offset that resulted in the error.\n* The system does not support an anchor string embedded in the form of a PDF X-object in the document.\n* The system does not re-flow the text that surrounds the anchor tabs. It is the responsibility of the document author to provide sufficient white space to contain the potential width of the ultimate tab value.\n\n### Tips and Tricks\n\nThe following are tips for effective use of anchor tags:\n* In order to avoid unintentional conflicts between text contained in an `anchorString` property and the text that naturally exists in documents, establish a codified syntax for the anchor string text that is unlikely to appear elsewhere in a document.\n* Develop an extensible and consistent syntax that can be used across multiple document types.\n* Especially for documents that have variable numbers of tabs or signers, author the source document to include hidden anchor tabs for all potential signers/permutations. Then, control the tabs that are actually placed by including/excluding the anchor tabs in the request. This approach allows a single document to be used for all use cases instead of maintaining separate documents for each scenario.\n\n## Automatically Populating Tabs\n\nIf you want similar tab types\nto automatically populate with the same data,\nyou must follow these guidelines:\n\n* Each `tabLabel` entry must have the characters\n `\\\\*` in front of the label.\n If you omit the `\\\\*` prefix,\n only the first occurrence of the tab is populated.\n\n When automatically populating tabs,\n the `tabLabel` must not contain any spaces.\n In the JSON example below,\n the Text tabs properties `StudentLastName` and `StudentFirstName`\n will be auto-populated as specified (\"Doe\" and \"John\")\n each place they appear throughout the envelope.\n\n ```\n \"tabs\": {\n \"textTabs\": [\n {\n \"tabLabel\": \"\\\\*StudentLastName\",\n \"value\": \"Doe\"\n },\n {\n \"tabLabel\": \"\\\\*StudentFirstName\",\n \"value\": \"John\"\n }]\n }\n ```\n\n* Each occurrence of the tab must have identical properties.\n\n For example, suppose there are two Text tabs in a document,\n each with `tabLabel` set to `Name`.\n If one tab has the `bold` property set to **true,**\n and the other has the `bold` property set to **false,**\n only the first one will be populated.\n In order to automatically populate both occurrences\n of the `Name` Text tabs,\n the `bold` property must be set to the same value for both tabs.\n" + }, + { + "name": "TemplateViews", + "description": "The TemplateViews resource\nprovides a method that returns a URL\nthat you can embed in your application\nto generate a template view that uses the Docusign UI.\n\nOne template view is available:\n\n* Edit View: The Docusign UI for editing a template.\n\nThis resource is related to the [EnvelopeViews][envelopeviews] resource.\nBoth resources enable you to embed the Docusign UI into your application.\n\n\n[envelopeviews]: /docs/esign-rest-api/reference/envelopes/envelopeviews/\n" + }, + { + "name": "UserCustomSettings", + "description": "The UserCustomSettings resource provides methods that allow you to manage the custom settings for a user.\n\nCustom settings are a flexible way to store and retrieve custom user information that can be used in your own system.\n\nThere is a limit on the size for all the custom user settings for a single user. All the custom user settings for a single user is limited to 4,000 characters, which includes the xml and json structure for the settings." + }, + { + "name": "UserProfiles", + "description": "The UserProfiles resource provides methods that allow you to manage a user's profile." + }, + { + "name": "Users", + "description": "The Users resource provides methods that enable you to manage users for an account.\n\nThe following User methods do not use the `title` property in the Users object:\n\n- create\n- delete\n- deleteProfileImage\n- get\n- getProfileImage\n- getSettings\n- list\n- update\n- updateList\n- updateProfileImag\n- updateSettings\n\nInstead, you can retrieve and set the user's job title by using the UserProfiles:Get and UserProfiles:Update methods. For more information, see [UserProfiles](/docs/esign-rest-api/reference/users/userprofiles/)." + }, + { + "name": "UserSignatures", + "description": "The UserSignatures resource provides methods that allow you manage the initials and signature images for a user." + }, + { + "name": "Contacts", + "description": "Docusign eSignature includes a contacts list (also referred to as an address book) to help make sending envelopes even easier. When you send an envelope, the recipients' names and email addresses are automatically added to your contacts list. You can use the contacts list to quickly add recipients to the envelopes you send. The `Contacts` resource provides methods that enable you to manage your contacts.\n\n" + }, + { + "name": "EnvelopeAttachments", + "description": "Envelope attachments are files that are associated an envelope.\nThey are not visible to senders or to recipients\nand are not converted to PDF or any other format.\nThese are files that your application uses to store information.\n\nEnvelope attachments are available only through the API. \n\n\n\n\nIt's easy to confuse envelope attachments,\nwhich are developer-only files associated with an envelope,\nwith signer attachments.\n\nTo learn about the different types of attachments, see [Attachments](/docs/esign-rest-api/esign101/concepts/documents/attachments/) in the concept guide.\n\n\n\n\n" + }, + { + "name": "AccountPermissionProfiles", + "description": "An account permission profile is assigned to a group of users, enabling you to set permissions for all of the users in the group at the same time. You are not required to set Permission Profiles for a group, but it makes it easier to manage user permissions for a large number of users. \n\nDocusign offers the following account permission profiles:\n\n- Docusign Viewer\n- Docusign Sender\n- Account Administrator\n\nYou can also create your own custom account permission profiles.\n" + }, + { + "name": "PowerForms", + "description": "PowerForms enable you to create self-service documents for signature. A PowerForm is an envelope initiated from a URL that you make available for signers to complete. You can either add a PowerForm to your website or email the URL to recipients. Docusign saves the data that recipients enter so you can easily integrate it with your other applications.\n\nFor more information, see [Using PowerForms](https://support.docusign.com/s/document-item?bundleId=jbx1643062255110&topicId=eke1578456649038.html).\n\n**Note:** PowerForms are available only for Docusign Enterprise accounts.\n\n\n### Errors\n\nPowerForm methods return the following 404 errors:\n\n- `PowerForms_Recipient_Denied_Documents`: The recipient is denied access to the documents.\n- `PowerForms_DigitalCerts_Shared_Tabs_Not_Allowed`: Shared tags are not allowed because a digital certificate is required\nfor a signer.\n- `PowerForms_DigitalCerts_Free_Form_Tabs_Not_Allowed`: Signers that are required to use a digital certificate must have at\nleast one required, non-conditional signature or initials tab.\n- `PowerForms_DigitalCerts_Multiple_Recipients_Routing_Order`: Signers that are required to use a digital certificate must be the\nonly recipient in a routing order. Edit the routing order or remove the digital certificate requirement.\n- `PowerForms_DigitalCerts_Markup_Not_Allowed`: Document markup is not allowed because a digital certificate is\nrequired for a signer.\n- `PowerForms_Incomplete_Recipient`: The recipient's username, email, or role is not set.\n- `PowerForms_PowerFormId_Required`: A `powerFormId` is required.\n- `PowerForms_PowerFormId_Mismatch`: A `powerFormId` mismatch has occurred.\n" + }, + { + "name": "PowerFormData", + "description": "The PowerFormData resource provides a method for accessing PowerForm data.\n" + }, + { + "name": "AccountTabSettings", + "description": "The AccountTabSettings resource provides methods that allow you to manage\ntab settings for an account.\n" + }, + { + "name": "ENoteConfigurations", + "description": "The ENoteConfigurations resource provides methods that allow you to manage\ninformation for the eNote eOriginal integration.\n" + }, + { + "name": "WorkspaceItems", + "description": "The WorkspaceItems resource provides methods that enable you to manage\nworkspace items.\n" + }, + { + "name": "Workspaces", + "description": "A workspace is a collaborative space for sharing documents and managing workflows. A workspace has a single owner who must be a Docusign user. The owner can invite others to the workspace as collaborators. Individuals who are not Docusign users must create a Docusign account to join a workspace as a collaborator.\n \nYou can create an envelope directly from a workspace.\n\nWorkspaces store the following information:\n\n- **Files:** Files uploaded to a workspace for storage or reuse.\n- **Documents:** A document is a component of a transaction, template, or workspace. When a file is added to a transaction, template, or workspace, it is copied as a document. Each document in a workspace has a single owner.\n- **Templates:** A set of documents that you can use to create a transaction or a workspace.\n- **Transactions:** A transaction is a series of workflow events related to one or more documents. These events route the documents to one or more individuals for the purposes of doing business. Each transaction has a single owner (the sender).\n\n**Note:** Documents in a template are not individually listed as files." + }, + { + "name": "ChunkedUploads", + "description": "A chunked upload is a temporary file that you upload in parts and stage at Docusign, then refer to as the content for other API calls. For example, you might use it for document content when assembling an envelope or template. \n\nA chunked upload is linked to the Docusign account member who initiated the API call. This user is the only user who is able to reference the chunked upload.\n\nA ChunkedUpload is intended to be an area for briefly staging data for use with other Docusign API calls. The ChunkedUpload API endpoints do not provide an action to download the ChunkedUpload's content.\n\nThe typical flow for using a chunked upload involves the following steps:\n\n1) Initiate the chunked upload with content representing part 0.\n\n2) Add more parts to the chunked upload until you have transmitted the entirety of the content.\n\n3) Commit the chunked upload, preparing it for use with other API calls.\n\n4) Assemble a Docusign envelope with a document that includes a reference to the chunked upload as the content.\n\n5) Continue with envelope-related processes.\n\n**Note:** You must fully upload and use a chunked upload within 20 minutes of initializing it.\n\nAfter the chunked upload has been correctly referenced within another API call, it becomes unavailable for any further use and is promptly removed from the system.\n\nChunked uploads have the following limits, which are configured per Docusign environment, account, or integrator:\n\n- The maximum number of all of a member's unexpired, unconsumed ChunkedUploads. The default value is 64. \n- The maximum total size of all of a member's unexpired, unconsumed ChunkedUploads. The default value is 1 GB. \n- The amount of time that a chunked upload is active after you initialize it. The default value is 20 minutes.\n" + }, + { + "name": "EnvelopeFormData", + "description": "The EnvelopeFormData resource provides a method for downloading the data that users have entered into a form associated with an envelope." + }, + { + "name": "EnvelopeDocumentVisibility", + "description": "The Envelope Document Visibility resource provides methods for managing document visibility by recipient.\n" + }, + { + "name": "TemplateDocumentVisibility", + "description": "The Template Document Visibility resource provides methods for managing template document visibility by recipient.\n" + }, + { + "name": "AccountPasswordRules", + "description": "The AccountPasswordRules resource provides methods that allow you to obtain and update account password rules, as well as membership and account rules." + }, + { + "name": "AccountWatermarks", + "description": "The AccountWatermarks resource provides methods that allow you to obtain, preview and update watermark information." + }, + { + "name": "PaymentGatewayAccounts", + "description": "This object contains details about a payment gateway account." + }, + { + "name": "NotaryJournals", + "description": "" + }, + { + "name": "IdentityVerifications", + "description": "The Docusign Identity Verification process requires a signer to submit an image of their valid government ID and wait for the image to be uploaded and verified before they can access a document.\n\nIdentity Verification supports government photo IDs and European eIDs, analyzing the document security features and matching the name on the agreement against the name on the ID. After a successful verification, the signer can view the agreement and sign as usual.\n\nTo use Identity Verification, the [Docusign Identity Verification](https://www.docusign.com/products/identify) product must be enabled for your account.\n\nFor more information, see [Digital ID Verification of Government-Issued IDs](https://www.docusign.com/blog/can-i-see-a-photo-of-your-id-digital-verification-of-real-world-ids)." + }, + { + "name": "EnvelopeDocumentHtmlDefinitions", + "description": "" + }, + { + "name": "DocumentResponsiveHtmlPreview", + "description": "**Note:** Responsive Signing is disabled by default. To use this functionality, an account administrator must switch the account setting `enableResponsiveSigning` to **true.** \nAlso note that Smart Sections (creating a signable HTML document that uses collapsible sections and rotating tables) are premium features. To request them, contact your Docusign account manager." + }, + { + "name": "EnvelopeHtmlDefinitions", + "description": "" + }, + { + "name": "ResponsiveHtmlPreview", + "description": "**Note:** Responsive Signing is disabled by default. To use this functionality, an account administrator must switch the account setting `enableResponsiveSigning` to **true.** \nAlso note that Smart Sections (creating a signable HTML document that uses collapsible sections and rotating tables) are premium features. To request them, contact your Docusign account manager." + }, + { + "name": "TemplateDocumentResponsiveHtmlPreview", + "description": "**Note:** Responsive Signing is disabled by default. To use this functionality, an account administrator must switch the account setting `enableResponsiveSigning` to **true.** \nAlso note that Smart Sections (creating a signable HTML document that uses collapsible sections and rotating tables) are premium features. To request them, contact your Docusign account manager." + }, + { + "name": "TemplateResponsiveHtmlPreview", + "description": "**Note:** Responsive Signing is disabled by default. To use this functionality, an account administrator must switch the account setting `enableResponsiveSigning` to **true.** \nAlso note that Smart Sections (creating a signable HTML document that uses collapsible sections and rotating tables) are premium features. To request them, contact your Docusign account manager." + }, + { + "name": "TemplateDocumentHtmlDefinitions", + "description": "" + }, + { + "name": "TemplateHtmlDefinitions", + "description": "" + }, + { + "name": "EnvelopeTransferRules", + "description": "Envelope transfer rules enable administrators to transfer envelopes and templates from user to another. For example, you might do this when an employee leaves the company. To transfer ownership of envelopes and templates, the **Transfer Custody** feature must be enabled for your account.\n\nFor more information about this functionality, see [Transfer Envelopes and Templates](https://support.docusign.com/s/document-item?bundleId=pik1583277475390&topicId=vyz1583277376194.html)." + }, + { + "name": "NotificationDefaults", + "description": "Docusign eSignature provides email notifications to senders and recipients for many different scenarios. By default, all notification options are turned on. Notification preferences give you control over the communications that you receive. For more information, see [Manage Notifications](https://support.docusign.com/s/document-item?bundleId=jux1643235969954&topicId=dhc1578456326388.html)." + }, + { + "name": "BulkSend", + "description": "You can use bulk send lists send documents to a large number of recipients on a\nrecurring basis. You can use a bulk send list to send up to 1,000 copies at a time.\n\nAfter you create a bulk send list, it persists and can be reused and edited any number of times.\n\nYou can customize individual copies of the envelope. For example, you can customize the email notification and\nlanguage and add personalized notes.\n\n### Requirements and limitations\n\n* Bulk send must be enabled for your account. To enable this feature, contact your Account Manager or Business Development representative.\n* You can include up to 1,000 bulk recipients in each request.\n* When you send an envelope with bulk recipients, envelopes are added to a bulk recipient queue and sent in a metered fashion.\n An account can have a total of 2,000 total envelopes in the queue at a time.\n If you reach this limit, you will get an error message. Wait and resend the envelope at a later time.\n If you hit queue limits often, contact your Account Manager to see about modifying the queue limits for your account.\n\n### Related topics\n\n- [Bulk sending envelopes](/docs/esign-rest-api/esign101/concepts/envelopes/bulk-send/) concept guide\n- [How to bulk send envelopes](/docs/esign-rest-api/how-to/bulk-send-envelopes/)\n\n" + }, + { + "name": "BCCEmailArchive", + "description": "You can configure automatic archiving of emails sent from all of your Docusign accounts.\n\nFor more information, see [Email Archive Configuration](https://support.docusign.com/s/document-item?bundleId=pik1583277475390&topicId=xjm1583277371373.html).\n\n**Note:** This feature is only available for certain account plans and must be enabled by Docusign." + }, + { + "name": "TabsBlob", + "description": "This resource has been deprecated." + }, + { + "name": "Comments", + "description": "The comments resource provides a method that enables you to download a PDF file containing all of the comments that recipients and the sender have added to the documents in an envelope.\n\nComments are disabled by default. To use the comments feature, an account administrator must enable comments on the account (in the `accountSettingsInformation` object, set the `enableSigningExtensionComments` property to **true**). \n\nFor more information, see [Comments Settings](https://support.docusign.com/s/document-item?bundleId=pik1583277475390&topicId=odr1583277453624.html)." + }, + { + "name": "FavoriteTemplates", + "description": "The FavoriteTemplates resource enables you to get, add, and remove favorite templates.\n\n### Related topics\n\n- [Common API Tasks: Working With Favorite Templates](https://www.docusign.com/blog/developers/common-api-tasks-working-favorite-templates)" + }, + { + "name": "Notary", + "description": "Docusign eNotary makes the notarization process fully digital\nfor senders, signers, and notaries.\nIt enables a notary public to act as an in-person witness\nto electronic signing of documents.\nCheck the [Docusign eNotary support documentation](https://support.docusign.com/s/document-item?bundleId=gko1642535666104&topicId=jiv1635359045452.html)\nto see which jurisdictions are supported.\n\n" + }, + { + "name": "NotaryJurisdiction", + "description": "Creating, updating, and deleting notary jurisdiction objects." + }, + { + "name": "EnvelopeWorkflowDefinition", + "description": "Describes the workflow for an envelope or template. These methods enable you to use the [scheduled sending](/docs/esign-rest-api/esign101/concepts/recipients/scheduled-sending/) and [delayed routing](/docs/esign-rest-api/esign101/concepts/recipients/delayed-routing/) features.\n\n\n### Related topics\n\n- [How to schedule an envelope](/docs/esign-rest-api/how-to/schedule-an-envelope/)\n- [How to send an envelope with delayed routing](/docs/esign-rest-api/how-to/send-envelope-with-delayed-routing/)" + }, + { + "name": "AccountSignatures", + "description": "In certain countries, stamps are used to certify documents. This resource provides methods to manage stamps available in your account.\n\nTo use these endpoints, your account must have Managed Stamps enabled. Contact your Docusign account manager for more information.\n\n### Related topics\n- [Sending and Signing with eHanko](https://support.docusign.com/s/articles/Sending-and-Signing-with-eHanko)\n- [Docusign eSignature Admin Guide: Stamps](https://support.docusign.com/s/document-item?=&bundleId=pik1583277475390&topicId=cex1583277417267.html)" + }, + { + "name": "ConnectSecret", + "description": "" + }, + { + "name": "DocumentResponsiveHtml", + "description": "" + }, + { + "name": "ResponsiveHtml", + "description": "" + }, + { + "name": "ReservedDomains", + "description": "" + }, + { + "name": "EnvelopePublish", + "description": "The EnvelopePublish resource provides a method for submitting existing envelopes to a webhook using an ad hoc Connect configuration. See the [Connect category](/docs/esign-rest-api/reference/connect/) for related endpoints." + }, + { + "name": "DocumentGeneration", + "description": "[Document generation][webapp support page] enables you\nto create reusable templates\nwith professionally formatted documents\nthat include personalized data.\nFor example, you can create\na boilerplate employment offer letter\nthat is updated with job details\nfor each candidate from an HR system .\n\nTo learn more about using these endpoints\nwith the eSignature API,\nsee [Document generation][concepts page]\nin the eSignature concepts guide.\n\n[webapp support page]: https://support.docusign.com/s/document-item?bundleId=als1679428547895&topicId=ldx1679428689631.html\n[concepts page]: /docs/esign-rest-api/esign101/concepts/documents/document-generation/\n" + }, + { + "name": "Authorizations", + "description": "A user authorization is created to share access between users on the same account. One user can be given access to sign, manage, or send on behalf of another. These endpoints allow you to create, delete, and manage user authorizations." + }, + { + "name": "EnvelopeShares", + "description": "" + }, + { + "name": "ConnectConfiguration", + "description": "" + } + ], + "x-ds-categories": [ + { + "name": "Envelopes", + "summary": "This section shows you how to create and send envelopes.", + "description": "The eSignature API Envelope category includes the resources and methods for sending and managing envelopes and envelope data.\n\nEnvelopes are the key objects in the Docusign platform. As a result, they are complex data structures with few required fields. See the [How-to guides](/docs/esign-rest-api/how-to/) for examples and solutions.\n\nTo learn more about envelopes, see [Envelopes](/docs/esign-rest-api/esign101/concepts/envelopes/).\n" + }, + { + "name": "Connect", + "summary": "Docusign Connect is the Docusign platform notification service. The service uses webhooks to proactively notify your application when an event occurs that your application wants to know about. Docusign recommends that all applications use Connect instead of polling the Docusign Signature Service. This section provides information about Connect configuration and logging. \n\n**Note:** To make API calls to any of the Connect endpoints, you must be an account administrator.\n\nFor more information about Connect, see [Docusign Connect](/platform/webhooks/connect/) and [Custom Connect Configuration](https://support.docusign.com/s/document-item?bundleId=pik1583277475390&topicId=khw1583277390582.html).", + "description": "The Connect service enables your application to be called via\nHTTPS when an event of interest occurs.\n\nUse the Connect service to \"end the polling madness.\" With\nConnect, there is no need for your application to poll Docusign\nevery 15 minutes to learn the latest about your envelopes.\n\nInstead, you register your interest in one or more types of\nenvelope or recipient events. Then, when an interesting event\noccurs, the Docusign platform will contact your application with\nthe event's details and data. You can register interest in\nenvelopes sent by particular users in your account, or for\nenvelopes sent by any user.\n\nConnect can empower your organization to manage document actions\nas they occur, and allows you to track their changes within your\nown systems. Upon completion, envelope information, including\ndocument content, can be stored in your own databases or CMS\nsystems, and these events can be triggered via webhooks\ndelivering messages to your application.\n\n**Note:** To make API calls to any of the Connect endpoints, you must be an account administrator.\n\n## Incoming Connect Calls\n\nTo use the Connect service, your application needs to provide an\nHTTPS URL that can be called from the public Internet. If your\napplication runs on a server behind your organization's firewall,\nthen you will need to create a \"pinhole\" in the firewall to allow\nthe incoming Connect calls from Docusign to reach your\napplication. You can also use other techniques such as proxy\nservers and DMZ networking for receiving the incoming calls.\n\nConnect delivers events over HTTP requests in JSON or XML.\nSee [Docusign Connect overview](/platform/webhooks/connect/).\n\nIf your application is not configured to accept post messages,\nDocusign will NOT return an additional post error response to\nyour listener application. If you've enabled logging on your\nconfiguration, it will be logged in Admin under the configuration\nfailure log.\n\n## Per-envelope Connect Configuration\n\nInstead of registering a general Connect configuration and\nlistener, an individual envelope can have its own Connect\nconfiguration. See the\n[`eventNotification`](/docs/esign-rest-api/reference/envelopes/envelopes/create/#schema__envelopedefinition_eventnotification)\nproperty for envelopes.\n\n## Historical Publish Endpoint\n\nTo submit existing envelopes to an endpoint, use the [EnvelopePublish](/docs/esign-rest-api/reference/envelopes/envelopepublish/) resource.\n" + }, + { + "name": "Templates", + "summary": "Template creation and management", + "description": "Use the Templates category to manage your account's templates.\n\nThis section shows you how to perform the following tasks:\n\n* Create, list, get, update, and delete templates.\n* Manage the notification and group sharing settings for templates.\n* Fetch and rotate pages from a document used by a template.\n\nYou can create templates either programmatically or through the Docusign web interface and then used by your application." + }, + { + "name": "SigningGroups", + "summary": "Send a signing request to a group of potential signers.", + "description": "Use the Signing Group category to manage signing groups that allow anyone in the group to sign a document. When you send an envelope to a signing group, anyone in the group can open it and sign it with their own signature.\n\nThis category shows account administrators how to create a signing group and manage the users in the group.\n\n**Note:** To create and manage signing groups, you must be an account administrator.\n\nFor more information about this topic, see [Signing Groups](https://support.docusign.com/s/document-item?bundleId=gav1643676262430&topicId=zgn1578456447934.html)." + }, + { + "name": "Folders", + "summary": "Folders allow you to organize envelopes and templates.", + "description": "Use the Folders category to organize envelopes and templates.\nYou can list the folder contents and move envelopes and templates between folders." + }, + { + "name": "CustomTabs", + "summary": "Custom tabs", + "description": "Custom Tabs enable accounts to have one or more pre-configured (custom) tabs. Custom tabs save time when users are tagging documents since the users don't have to manually set the tabs' parameters.\n\nThis category enables custom tabs to be managed programmatically, including creation, deletion, etc. \n" + }, + { + "name": "CloudStorage", + "summary": "This section shows you how to manage your cloud storage providers and users.", + "description": "Use the Cloud Storage category to list the user's cloud storage document contents.\n\nIt is also used to manage the user's authentication/accounts with cloud storage service providers." + }, + { + "name": "Users", + "summary": "User management", + "description": "Use the Users category to manage the users in your accounts.\n\nYou can:\n\n* Set custom user settings.\n* Manage a users profile.\n* Add delete users.\n* Add and delete the initials and signature images for a user." + }, + { + "name": "UserGroups", + "summary": "Group users for common permissions and branding", + "description": "Use the User Groups category to manage your permissions groups.\n\nYou can:\n\n* Create and delete groups.\n* Add users to, and delete users from, your groups.\n* Manage the brand information associated with a group." + }, + { + "name": "BulkEnvelopes", + "summary": "Bulk envelopes", + "description": "Use the BulkEnvelopes category to manage the sending of envelopes to multiple recipients." + }, + { + "name": "Accounts", + "summary": "Account management", + "description": "Use the Account category for various account management tasks including:\n\n* Programmatically creating and deleting accounts.\n* Getting information about an account and its capabilities.\n* Branding the account with custom colors, message text, and more.\n* Account charges.\n\nThe Accounts category also includes end points for listing the recipient names associated with an email address that was used by the account. For example, a single email address is often shared by multiple members of a family.\n\n\n" + }, + { + "name": "Billing", + "summary": "Invoices", + "description": "Use the Billing category to manage the following billing related tasks:\n\n* Retrieve and update billing plan information.\n* Retrieve invoices.\n* Retrieve and update payment information." + }, + { + "name": "Diagnostics", + "summary": "Diagnostics and request logging", + "description": "The Diagnostics category provides miscellaneous end points.\n\nThey include:\n* Requesting and managing the API call-logging feature. (Perfect for debugging your app!)\n* Getting information on the API's resources and versions." + }, + { + "name": "PowerForms", + "summary": "The PowerForms category provides methods that enable you to create and manage fillable forms.", + "description": "The PowerForms category enables you to create and manage PowerForms that you can use for self service and email forms." + }, + { + "name": "Workspaces", + "summary": "A Docusign workspace is a collaboration area for sharing files and data.", + "description": "A workspace is a collaborative space for sharing documents and managing workflows. A workspace has a single owner who must be a Docusign user. The owner can invite others to the workspace as collaborators. Individuals who are not Docusign users must create a Docusign account to join a workspace as a collaborator.\n \nYou can create an envelope directly from a workspace.\n\nWorkspaces store the following information:\n\n- **Files:** Files uploaded to a workspace for storage or reuse.\n- **Documents:** A document is a component of a transaction, template, or workspace. When a file is added to a transaction, template, or workspace, it is copied as a document. Each document in a workspace has a single owner.\n- **Templates:** A set of documents that you can use to create a transaction or a workspace.\n- **Transactions:** A transaction is a series of workflow events related to one or more documents. These events route the documents to one or more individuals for the purposes of doing business. Each transaction has a single owner (the sender).\n\n**Note:** Documents in a template are not individually listed as files." + }, + { + "name": "Payments", + "summary": "Manage Payments", + "description": "This category includes resources for managing payment gateways. Payment information is added to envelopes via methods in the Envelopes category." + }, + { + "name": "EmailArchive", + "summary": "This section shows you how to set and manage the BCC email addresses that you want to use to archive the emails that Docusign sends.", + "description": "Email archives enable Docusign partners and other multi-account customers to easily archive emails sent through Docusign. The `EmailArchive` resource provides methods for managing your email archive configuration, which consists of the BCC email address or addresses that you want to use to archive Docusign emails. Each account can use up to five BCC email addresses for archiving purposes.\n\nFor more information, see [Email Archive Configuration](https://support.docusign.com/s/document-item?bundleId=pik1583277475390&topicId=xjm1583277371373.html).\n\n**Note:** This feature is only available for certain account plans and must be enabled by Docusign." + }, + { + "name": "Notary", + "summary": "Docusign eNotary makes the notarization process fully digital\nfor senders, signers, and notaries.\nIt enables a notary public to act as an in-person witness\nto electronic signing of documents.\n", + "description": "Docusign eNotary makes the notarization process fully digital\nfor senders, signers, and notaries.\nIt enables a notary public to act as an in-person witness\nto electronic signing of documents.\n" + }, + { + "name": "Envelope", + "summary": "", + "description": "" + } + ], + "openapi": "3.0.4", + "servers": [ + { + "url": "https://www.docusign.net/restapi" + } + ], + "components": { + "securitySchemes": {} + } +} diff --git a/packages/apps/connector-docusign/package.json b/packages/apps/connector-docusign/package.json new file mode 100644 index 00000000000..a38dce7a397 --- /dev/null +++ b/packages/apps/connector-docusign/package.json @@ -0,0 +1,31 @@ +{ + "name": "@objectstack/connector-app-docusign", + "version": "0.1.0", + "description": "DocuSign eSignature connector app (open edition, sandbox first): DocuSign's eSignature OpenAPI document, trimmed offline to three envelope operations, as a declarative `openapi` connector instance authorized by OAuth 2.0 (`authorization_code`) with an OAuth client each deployment's administrator registers and enters on the connection.", + "license": "Apache-2.0", + "private": true, + "type": "module", + "main": "./objectstack.config.ts", + "types": "./objectstack.config.ts", + "exports": { + ".": "./objectstack.config.ts", + "./objectstack.config": "./objectstack.config.ts" + }, + "scripts": { + "build": "objectstack build", + "validate": "objectstack validate", + "typecheck": "tsc --noEmit", + "test": "objectstack validate && vitest run" + }, + "dependencies": { + "@objectstack/connector-openapi": "workspace:*", + "@objectstack/spec": "workspace:*" + }, + "devDependencies": { + "@objectstack/cli": "workspace:*", + "@objectstack/verify": "workspace:*", + "@types/node": "^26.6.5", + "typescript": "^6.0.3", + "vitest": "^4.1.11" + } +} diff --git a/packages/apps/connector-docusign/src/connectors/docusign.connector.ts b/packages/apps/connector-docusign/src/connectors/docusign.connector.ts new file mode 100644 index 00000000000..6c59f79d70a --- /dev/null +++ b/packages/apps/connector-docusign/src/connectors/docusign.connector.ts @@ -0,0 +1,83 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +import { defineConnector } from '@objectstack/spec/integration'; + +/** + * The three operations this app exposes, by DocuSign's own `operationId`: + * create (and send) an envelope, read one envelope, and list the envelopes + * whose status changed since a date. The packaged document carries nothing + * else, because it was trimmed to exactly these by the offline normalizer + * (`--include-pattern … --trim`; the README records the command, the upstream + * commit and the sha256 it printed). + */ +export const DOCUSIGN_ENVELOPE_OPERATIONS = [ + 'Envelopes_PostEnvelopes', + 'Envelopes_GetEnvelope', + 'Envelopes_GetEnvelopes', +] as const; + +/** + * DocuSign's developer (sandbox) environment. The ruling that shaped this app + * puts the sandbox first: its account server and its one API host are fixed, + * while a production account's API host is per account (DocuSign answers it + * as `base_uri` from `/oauth/userinfo`), which a packaged instance cannot name. + */ +export const DOCUSIGN_SANDBOX = { + authorizationUrl: 'https://account-d.docusign.com/oauth/auth', + tokenUrl: 'https://account-d.docusign.com/oauth/token', + baseUrl: 'https://demo.docusign.net/restapi', +} as const; + +/** + * DocuSign eSignature over its own OpenAPI document — recipe 6 of the + * integration recipes (`skills/objectstack-automation/references/integration-recipes.md`, + * recipe 1's shape with a grant-bearing `auth`): a declarative + * `provider: 'openapi'` instance (ADR-0097, ADR-0023) the `openapi` executor + * materializes at boot, one action per operation. + * + * The document is `docusign/OpenAPI-Specifications` + * `esignature.rest.swagger-v2.1.json` (MIT), a Swagger 2.0 document converted + * to OpenAPI 3.0 and trimmed offline. Its `servers[0].url` is DocuSign's + * production host; `providerConfig.baseUrl` points the instance at the + * sandbox's API host instead. The document declares no security scheme, so + * the access token rides on every operation as `Authorization: Bearer`. + * + * `auth` is OAuth 2.0 `authorization_code` (ADR-0143 D2.1) with + * `clientSource: 'installer'`: the definition names no client. A DocuSign + * OAuth client is an integration key, registered in a DocuSign account + * together with the redirect URIs it accepts, and the redirect URI belongs to + * each deployment — so the administrator of each deployment registers their + * own integration key against their deployment's callback and enters it when + * they connect the instance (the README's Credentials section). ⛔ No client + * id or secret of anyone's is in this package. Until an administrator + * connects it, once per organization, the instance is degraded "not + * connected"; the platform then refreshes the token per dispatch, and no + * metadata key ever holds it. + * + * `include` repeats the trimmed document's whole operation set on purpose: an + * entry the document does not carry is refused at materialization, so a + * re-normalized document that lost an operation fails the boot loudly instead + * of bringing the instance up without it. + */ +export const DocuSignConnector = defineConnector({ + name: 'docusign', + label: 'DocuSign eSignature (sandbox)', + type: 'api', + description: + 'DocuSign eSignature API (developer sandbox): send an envelope for signature, read an envelope, and list ' + + 'envelope status changes, authorized by OAuth with the integration key your administrator registers.', + provider: 'openapi', + providerConfig: { + spec: './openapi/docusign-esignature.openapi.json', + baseUrl: DOCUSIGN_SANDBOX.baseUrl, + include: [...DOCUSIGN_ENVELOPE_OPERATIONS], + }, + auth: { + type: 'oauth2', + grant: 'authorization_code', + authorizationUrl: DOCUSIGN_SANDBOX.authorizationUrl, + tokenUrl: DOCUSIGN_SANDBOX.tokenUrl, + scopes: ['signature'], + clientSource: 'installer', + }, +}); diff --git a/packages/apps/connector-docusign/src/flows/docusign.flows.ts b/packages/apps/connector-docusign/src/flows/docusign.flows.ts new file mode 100644 index 00000000000..ba206407a0a --- /dev/null +++ b/packages/apps/connector-docusign/src/flows/docusign.flows.ts @@ -0,0 +1,254 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +import { defineFlow } from '@objectstack/spec'; + +/** + * Send one document out for signature and record the envelope. Run it with + * `POST /api/v1/automation/docusign_send_envelope/trigger` and a body of + * `{ "params": { "account_id": "…", "email_subject": "…", "document_name": + * "contract.pdf", "document_base64": "…", "signer_name": "…", "signer_email": + * "…" } }`. + * + * The envelope is created with `status: "sent"`, so DocuSign emails the + * signer at once. DocuSign's answer (`envelopeId`, `status`, + * `statusDateTime`) is the run's `send.body` output; when DocuSign accepted + * it, a `docusign_envelope` record keeps the envelope id and its status. + * When DocuSign refused it, the step does not fail: the answer lands as + * `send.status`, `send.ok` false and DocuSign's error in `send.body`, and no + * record is written. + * + * An `openapi` action takes its path parameters as one `path` object and its + * request body as one `body` value, each a single CEL value envelope. A CEL + * map's values share one type, so every literal beside a variable read is + * wrapped in `dyn(…)`, at every level. + */ +export const DocuSignSendEnvelopeFlow = defineFlow({ + name: 'docusign_send_envelope', + label: 'DocuSign: send envelope', + description: 'Sends one document to one signer through the docusign connector and records the envelope.', + type: 'autolaunched', + status: 'active', + variables: [ + { name: 'account_id', type: 'text', isInput: true }, + { name: 'email_subject', type: 'text', isInput: true }, + { name: 'document_name', type: 'text', isInput: true }, + { name: 'document_base64', type: 'text', isInput: true }, + { name: 'signer_name', type: 'text', isInput: true }, + { name: 'signer_email', type: 'text', isInput: true }, + { name: 'send.body', type: 'json', isOutput: true }, + ], + nodes: [ + { id: 'start', type: 'start', label: 'Start' }, + { + id: 'send', + type: 'connector_action', + label: 'Envelopes_PostEnvelopes', + connectorConfig: { + connectorId: 'docusign', + actionId: 'Envelopes_PostEnvelopes', + input: { + path: { dialect: 'cel', source: '{"accountId": account_id}' }, + body: { + dialect: 'cel', + source: + '{"emailSubject": email_subject, "status": dyn("sent"), ' + + '"documents": dyn([{"documentId": dyn("1"), "name": document_name, "documentBase64": document_base64}]), ' + + '"recipients": dyn({"signers": [{"recipientId": dyn("1"), "routingOrder": dyn("1"), "name": signer_name, "email": signer_email}]})}', + }, + }, + }, + }, + { id: 'accepted', type: 'decision', label: 'Did DocuSign accept it?' }, + { + id: 'record', + type: 'create_record', + label: 'Record the envelope', + config: { + objectName: 'docusign_envelope', + fields: { + envelope_id: { dialect: 'cel', source: 'send.body.envelopeId' }, + signature_status: { dialect: 'cel', source: 'has(send.body.status) ? send.body.status : null' }, + email_subject: { dialect: 'cel', source: 'email_subject' }, + status_changed_at: { dialect: 'cel', source: 'has(send.body.statusDateTime) ? send.body.statusDateTime : null' }, + }, + }, + }, + { id: 'end', type: 'end', label: 'End' }, + ], + edges: [ + { id: 'e1', source: 'start', target: 'send' }, + { id: 'e2', source: 'send', target: 'accepted' }, + { id: 'e3', source: 'accepted', target: 'record', label: 'accepted', condition: 'send.ok && has(send.body.envelopeId)' }, + { id: 'e4', source: 'accepted', target: 'end', label: 'refused', isDefault: true }, + { id: 'e5', source: 'record', target: 'end' }, + ], +}); + +/** + * Read one envelope and refresh its record. Run it with + * `POST /api/v1/automation/docusign_get_envelope/trigger` and a body of + * `{ "params": { "account_id": "…", "envelope_id": "…" } }`; DocuSign's + * envelope is the run's `envelope.body` output. When DocuSign answered it, + * every `docusign_envelope` record with that `envelope_id` takes DocuSign's + * `status` and `statusChangedDateTime` (none, when the envelope was not sent + * by this app: the update matches no record and writes nothing). + */ +export const DocuSignGetEnvelopeFlow = defineFlow({ + name: 'docusign_get_envelope', + label: 'DocuSign: get envelope', + description: "Reads one DocuSign envelope through the docusign connector and refreshes its record's signature status.", + type: 'autolaunched', + status: 'active', + variables: [ + { name: 'account_id', type: 'text', isInput: true }, + { name: 'envelope_id', type: 'text', isInput: true }, + { name: 'envelope.body', type: 'json', isOutput: true }, + ], + nodes: [ + { id: 'start', type: 'start', label: 'Start' }, + { + id: 'envelope', + type: 'connector_action', + label: 'Envelopes_GetEnvelope', + connectorConfig: { + connectorId: 'docusign', + actionId: 'Envelopes_GetEnvelope', + input: { path: { dialect: 'cel', source: '{"accountId": account_id, "envelopeId": envelope_id}' } }, + }, + }, + { id: 'answered', type: 'decision', label: 'Did DocuSign answer it?' }, + { + id: 'refresh', + type: 'update_record', + label: 'Refresh the signature status', + config: { + objectName: 'docusign_envelope', + filter: { envelope_id: { dialect: 'cel', source: 'envelope_id' } }, + fields: { + signature_status: { dialect: 'cel', source: 'envelope.body.status' }, + status_changed_at: { + dialect: 'cel', + source: 'has(envelope.body.statusChangedDateTime) ? envelope.body.statusChangedDateTime : null', + }, + }, + // The filter names the envelope, not the record id: a predicate update. + multi: true, + }, + }, + { id: 'end', type: 'end', label: 'End' }, + ], + edges: [ + { id: 'e1', source: 'start', target: 'envelope' }, + { id: 'e2', source: 'envelope', target: 'answered' }, + { id: 'e3', source: 'answered', target: 'refresh', label: 'answered', condition: 'envelope.ok && has(envelope.body.status)' }, + { id: 'e4', source: 'answered', target: 'end', label: 'refused', isDefault: true }, + { id: 'e5', source: 'refresh', target: 'end' }, + ], +}); + +/** + * Reflect DocuSign's status changes on the envelope records — the poll that + * stands in for a callback. DocuSign Connect signs its posts with its own + * header, which the `api` flow door does not verify, so this app does not + * take Connect posts; run this flow on a schedule instead. Run it with + * `POST /api/v1/automation/docusign_poll_status_changes/trigger` and a body of + * `{ "params": { "account_id": "…", "from_date": "2026-11-01T00:00:00Z" } }`. + * + * It lists the account's envelopes whose status changed since `from_date` + * (at most 100), returns DocuSign's list as `changes.body`, and gives every + * `docusign_envelope` record named in it DocuSign's status. An envelope this + * app did not send matches no record and writes nothing. Each update is + * contained: one refused write does not end the sweep. + */ +export const DocuSignPollStatusChangesFlow = defineFlow({ + name: 'docusign_poll_status_changes', + label: 'DocuSign: poll status changes', + description: 'Lists DocuSign envelope status changes since a date and copies each status onto its envelope record.', + type: 'autolaunched', + status: 'active', + variables: [ + { name: 'account_id', type: 'text', isInput: true }, + { name: 'from_date', type: 'text', isInput: true }, + { name: 'changes.body', type: 'json', isOutput: true }, + ], + nodes: [ + { id: 'start', type: 'start', label: 'Start' }, + { + id: 'changes', + type: 'connector_action', + label: 'Envelopes_GetEnvelopes', + connectorConfig: { + connectorId: 'docusign', + actionId: 'Envelopes_GetEnvelopes', + input: { + path: { dialect: 'cel', source: '{"accountId": account_id}' }, + query: { dialect: 'cel', source: '{"from_date": from_date, "count": dyn("100")}' }, + }, + }, + }, + { id: 'listed', type: 'decision', label: 'Did DocuSign answer it?' }, + { + // The node's ordinary out-edge is the after-loop continuation; the + // per-envelope step is `config.body`. + id: 'each_change', + type: 'loop', + label: 'For each changed envelope', + config: { + // DocuSign leaves `envelopes` out of a list with no entries. + collection: { dialect: 'cel', source: 'has(changes.body.envelopes) ? changes.body.envelopes : []' }, + iteratorVariable: 'changed', + // The list asks for at most 100, so this is the same bound on the side + // that iterates: a longer collection fails the node rather than + // truncating in silence. + maxIterations: 100, + body: { + nodes: [ + { + // Per-iteration containment: a loop body has no error handling of + // its own, so one refused update would otherwise end the sweep. + id: 'guard_update', + type: 'try_catch', + label: 'Guarded update', + config: { + try: { + nodes: [ + { + id: 'apply_status', + type: 'update_record', + label: 'Apply the status', + config: { + objectName: 'docusign_envelope', + filter: { envelope_id: { dialect: 'cel', source: 'changed.envelopeId' } }, + fields: { + signature_status: { dialect: 'cel', source: 'changed.status' }, + status_changed_at: { + dialect: 'cel', + source: 'has(changed.statusChangedDateTime) ? changed.statusChangedDateTime : null', + }, + }, + multi: true, + }, + }, + ], + }, + catch: { + nodes: [{ id: 'update_failed', type: 'assignment', label: 'Update failed (contained)' }], + }, + }, + }, + ], + }, + }, + }, + { id: 'end', type: 'end', label: 'End' }, + ], + edges: [ + { id: 'e1', source: 'start', target: 'changes' }, + { id: 'e2', source: 'changes', target: 'listed' }, + { id: 'e3', source: 'listed', target: 'each_change', label: 'answered', condition: 'changes.ok' }, + { id: 'e4', source: 'listed', target: 'end', label: 'refused', isDefault: true }, + { id: 'e5', source: 'each_change', target: 'end' }, + ], +}); + +export const allFlows = [DocuSignSendEnvelopeFlow, DocuSignGetEnvelopeFlow, DocuSignPollStatusChangesFlow]; diff --git a/packages/apps/connector-docusign/src/objects/docusign-envelope.object.ts b/packages/apps/connector-docusign/src/objects/docusign-envelope.object.ts new file mode 100644 index 00000000000..f4f520234bf --- /dev/null +++ b/packages/apps/connector-docusign/src/objects/docusign-envelope.object.ts @@ -0,0 +1,54 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +import { ObjectSchema, Field } from '@objectstack/spec/data'; + +/** + * One envelope this app sent, and the signature status last read from + * DocuSign. The two fields a contract record needs to follow its signature + * are `envelope_id` (DocuSign's envelope GUID) and `signature_status` + * (DocuSign's own status word: `sent`, `delivered`, `completed`, `declined`, + * `voided`, …). + * + * The status is DocuSign's, mirrored: `docusign_send_envelope` writes it when + * the envelope is created, and `docusign_get_envelope` / + * `docusign_poll_status_changes` overwrite it with what DocuSign answers. It is + * a text field, not a select, because DocuSign's status vocabulary is + * DocuSign's to grow; a word this object refused would fail the poll that read + * it. Nothing here is the system of record — DocuSign is — so the object is + * shared across the organization and any flow run may refresh it. + */ +export const DocuSignEnvelope = ObjectSchema.create({ + name: 'docusign_envelope', + label: 'DocuSign Envelope', + pluralLabel: 'DocuSign Envelopes', + icon: 'file-signature', + description: 'An envelope sent for signature through DocuSign, with the signature status last read from DocuSign.', + // Org-shared on purpose: the record mirrors DocuSign's state, and the flows + // that refresh it run for whoever polls. + sharingModel: 'public_read_write', + + fields: { + envelope_id: Field.text({ + label: 'Envelope ID', + description: "DocuSign's envelope GUID, as `Envelopes_PostEnvelopes` answered it.", + required: true, + searchable: true, + maxLength: 64, + }), + signature_status: Field.text({ + label: 'Signature Status', + description: "DocuSign's envelope status word (`sent`, `delivered`, `completed`, `declined`, `voided`, …), as last read.", + maxLength: 32, + }), + email_subject: Field.text({ + label: 'Email Subject', + description: 'The subject line DocuSign sent the envelope under.', + maxLength: 255, + }), + status_changed_at: Field.text({ + label: 'Status Changed At', + description: "DocuSign's `statusChangedDateTime` for the status above, verbatim (an ISO 8601 instant).", + maxLength: 40, + }), + }, +}); diff --git a/packages/apps/connector-docusign/src/views/docusign-envelope.view.ts b/packages/apps/connector-docusign/src/views/docusign-envelope.view.ts new file mode 100644 index 00000000000..a1b538003be --- /dev/null +++ b/packages/apps/connector-docusign/src/views/docusign-envelope.view.ts @@ -0,0 +1,23 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +import { defineView } from '@objectstack/spec'; + +/** + * The envelopes this app sent, newest status change first: what an + * administrator opens to see where each signature stands, as DocuSign last + * answered it. + */ +export const DocuSignEnvelopeViews = defineView({ + list: { + label: 'DocuSign Envelopes', + type: 'grid', + data: { provider: 'object', object: 'docusign_envelope' }, + columns: [ + { field: 'email_subject' }, + { field: 'signature_status' }, + { field: 'status_changed_at' }, + { field: 'envelope_id' }, + ], + sort: [{ field: 'status_changed_at', order: 'desc' }], + }, +}); diff --git a/packages/apps/connector-docusign/test/boot.test.ts b/packages/apps/connector-docusign/test/boot.test.ts new file mode 100644 index 00000000000..039a22225dd --- /dev/null +++ b/packages/apps/connector-docusign/test/boot.test.ts @@ -0,0 +1,353 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +/** + * Boot test: the app, booted the way `objectstack serve` composes it + * (`bootStack` from `@objectstack/verify`), against a FAKE DocuSign — no call + * reaches DocuSign. + * + * Two substitutions, both at the HTTP edge. The app's own + * `ConnectorOpenApiPlugin` is replaced (by plugin name, + * `BootOptions.extraPlugins`) with one whose `fetchImpl` answers DocuSign's + * eSignature API from fixtures and records every request; and the global + * `fetch` answers DocuSign's sandbox token endpoint — the one call the + * platform's OAuth grant makes — and passes every other request through. + * Everything else is the shipped app: the connector entry, the trimmed + * document it reads from this package's root, the object, the flows, and the + * administrator's path to a working instance — boot (the instance is "not + * connected"), read the redirect URI to register, connect with their own + * DocuSign integration key through the connection door, come back through the + * OAuth callback, run the flows. + * + * Every credential below is an obviously-fake placeholder. + */ + +import { describe, it, expect, beforeAll, afterAll } from 'vitest'; +import { fileURLToPath } from 'node:url'; +// Paid at module load, before vitest's clocked windows: both reach the whole +// runtime. +import { bootStack, type VerifyStack } from '@objectstack/verify'; +import { ConnectorOpenApiPlugin } from '@objectstack/connector-openapi'; +import stack from '../objectstack.config.js'; +import { DOCUSIGN_ENVELOPE_OPERATIONS, DOCUSIGN_SANDBOX } from '../src/connectors/docusign.connector.js'; + +const APP_ROOT = fileURLToPath(new URL('..', import.meta.url)); +const NAME = 'docusign'; +const door = `/automation/connectors/${NAME}/connection`; +const ORIGIN = 'https://deployment.example.com'; +const CALLBACK = `${ORIGIN}/api/v1/automation/connectors/oauth/callback`; + +const INTEGRATION_KEY = 'TEST_PLACEHOLDER_INTEGRATION_KEY'; +const SECRET_KEY = 'TEST_PLACEHOLDER_SECRET_KEY'; +const CODE = 'TEST_PLACEHOLDER_AUTHORIZATION_CODE'; +const ACCESS_TOKEN = 'TEST_PLACEHOLDER_ACCESS_TOKEN'; +const REFRESH_TOKEN = 'TEST_PLACEHOLDER_REFRESH_TOKEN'; +const ACCOUNT = 'fixture-account'; +const API = `${DOCUSIGN_SANDBOX.baseUrl}/v2.1/accounts/${ACCOUNT}`; + +interface Recorded { + method: string; + url: string; + authorization: string | null; + body?: unknown; +} + +interface Descriptor { + name: string; + state?: string; + degradedReason?: string; + actions?: Array<{ key: string }>; +} + +const dataOf = (json: any): any => json?.data ?? json; +const basicOf = (id: string, secret: string) => `Basic ${Buffer.from(`${id}:${secret}`).toString('base64')}`; + +/** What the fake DocuSign API answers, by method and path. */ +function fixture(method: string, url: URL, body: any): { status: number; body: unknown } { + const base = `/restapi/v2.1/accounts/${ACCOUNT}/envelopes`; + if (method === 'POST' && url.pathname === base) { + if (body?.emailSubject === 'refuse me') { + return { status: 400, body: { errorCode: 'INVALID_REQUEST_BODY', message: 'fixture refusal' } }; + } + return { + status: 201, + body: { envelopeId: 'env-fixture-1', status: 'sent', statusDateTime: '2026-11-03T05:00:00.0000000Z', uri: '/envelopes/env-fixture-1' }, + }; + } + if (method === 'GET' && url.pathname === `${base}/env-fixture-1`) { + return { + status: 200, + body: { envelopeId: 'env-fixture-1', status: 'delivered', statusChangedDateTime: '2026-11-03T05:10:00.0000000Z' }, + }; + } + if (method === 'GET' && url.pathname === base) { + return { + status: 200, + body: { + resultSetSize: '2', + envelopes: [ + { envelopeId: 'env-fixture-1', status: 'completed', statusChangedDateTime: '2026-11-03T05:20:00.0000000Z' }, + { envelopeId: 'env-not-sent-here', status: 'voided', statusChangedDateTime: '2026-11-03T05:21:00.0000000Z' }, + ], + }, + }; + } + return { status: 404, body: { errorCode: 'NOT_FOUND', message: `fixture has no answer for ${method} ${url.pathname}` } }; +} + +describe('connector-docusign app — boot, connect with the installer client, and run against a fake DocuSign', () => { + const requests: Recorded[] = []; + const tokenRequests: Array<{ form: URLSearchParams; authorization: string | null }> = []; + let booted: VerifyStack; + let admin: string; + const realFetch = globalThis.fetch; + const savedOrigin = { auth: process.env.OS_AUTH_URL, base: process.env.OS_BASE_URL }; + + const descriptor = async (): Promise => { + const res = await booted.apiAs(admin, 'GET', '/automation/connectors'); + expect(res.status).toBe(200); + const body = (await res.json()) as { data?: { connectors?: Descriptor[] } }; + return body.data?.connectors?.find((c) => c.name === NAME); + }; + + const envelopes = async (): Promise>> => { + const res = await booted.apiAs(admin, 'GET', '/data/docusign_envelope'); + const text = await res.text(); + expect(res.status, text.slice(0, 400)).toBe(200); + const json = JSON.parse(text) as any; + const rows = json?.data?.records ?? json?.records ?? json?.data ?? json?.value ?? []; + return Array.isArray(rows) ? rows : []; + }; + + beforeAll(async () => { + // The redirect URI is the deployment's fixed origin (ADR-0143 D5). + delete process.env.OS_AUTH_URL; + process.env.OS_BASE_URL = ORIGIN; + // DocuSign's sandbox token endpoint, answered here; nothing else is touched. + globalThis.fetch = (async (input: Parameters[0], init?: RequestInit) => { + const href = typeof input === 'string' ? input : input instanceof URL ? input.href : input.url; + if (href === DOCUSIGN_SANDBOX.tokenUrl) { + tokenRequests.push({ + form: new URLSearchParams(String(init?.body ?? '')), + authorization: new Headers(init?.headers).get('authorization'), + }); + return new Response( + JSON.stringify({ access_token: ACCESS_TOKEN, refresh_token: REFRESH_TOKEN, token_type: 'Bearer', expires_in: 28800 }), + { status: 200, headers: { 'content-type': 'application/json' } }, + ); + } + return realFetch(input, init); + }) as typeof fetch; + + const fetchImpl: typeof fetch = async (input, init) => { + const url = new URL(typeof input === 'string' ? input : input instanceof URL ? input.href : input.url); + const method = init?.method ?? 'GET'; + const headers = new Headers(init?.headers); + const body = typeof init?.body === 'string' ? JSON.parse(init.body) : undefined; + requests.push({ method, url: url.href, authorization: headers.get('authorization'), ...(body === undefined ? {} : { body }) }); + const answer = fixture(method, url, body); + return new Response(JSON.stringify(answer.body), { status: answer.status, headers: { 'content-type': 'application/json' } }); + }; + booted = await bootStack(stack, { + hostRoot: APP_ROOT, + extraPlugins: [new ConnectorOpenApiPlugin({ fetchImpl })], + }); + admin = await booted.signIn(); + }, 120_000); + + afterAll(async () => { + await booted?.stop(); + globalThis.fetch = realFetch; + if (savedOrigin.base === undefined) delete process.env.OS_BASE_URL; + else process.env.OS_BASE_URL = savedOrigin.base; + if (savedOrigin.auth !== undefined) process.env.OS_AUTH_URL = savedOrigin.auth; + }); + + it('before a connect the instance is degraded "not connected", and the read answers the redirect URI to register', async () => { + const docusign = await descriptor(); + expect(docusign, 'docusign is not in the connector registry').toBeDefined(); + expect(docusign!.state).toBe('degraded'); + expect(docusign!.degradedReason).toContain('not connected'); + const read = await booted.apiAs(admin, 'GET', door); + const text = await read.text(); + expect(read.status, text.slice(0, 400)).toBe(200); + expect(dataOf(JSON.parse(text))).toMatchObject({ connector: NAME, status: 'not_connected', redirectUri: CALLBACK }); + expect(requests).toEqual([]); + expect(tokenRequests).toEqual([]); + }); + + it('a flow run before the connect fails and sends nothing to DocuSign', async () => { + const res = await booted.apiAs(admin, 'POST', '/automation/docusign_get_envelope/trigger', { + params: { account_id: ACCOUNT, envelope_id: 'env-fixture-1' }, + }); + const text = await res.text(); + expect(res.status, text.slice(0, 400)).toBe(400); + const error = (JSON.parse(text) as { error?: { code?: string; message?: string } }).error; + expect(error?.code).toBe('FLOW_FAILED'); + // The README's promise: the failure names the connector and why. + expect(error?.message).toContain(`'${NAME}'`); + expect(error?.message).toContain('not connected'); + expect(requests).toEqual([]); + }); + + it('a connect that names no client is refused 400, naming the vendor registration step', async () => { + const res = await booted.apiAs(admin, 'POST', door); + const text = await res.text(); + expect(res.status, text.slice(0, 400)).toBe(400); + const error = (JSON.parse(text) as { error?: { code?: string; message?: string } }).error; + expect(error?.code).toBe('VALIDATION_ERROR'); + expect(error?.message).toContain('Register an OAuth client for this deployment with the vendor'); + expect(error?.message).toContain(CALLBACK); + expect(tokenRequests).toEqual([]); + }); + + it("connects as the administrator's integration key: DocuSign's sandbox sign-in, then the code exchange in HTTP Basic", async () => { + const begun = await booted.apiAs(admin, 'POST', door, { clientId: INTEGRATION_KEY, clientSecret: SECRET_KEY }); + const beginText = await begun.text(); + expect(begun.status, beginText.slice(0, 400)).toBe(200); + expect(beginText).not.toContain(SECRET_KEY); + const authorizationUrl = new URL(dataOf(JSON.parse(beginText)).authorizationUrl); + expect(authorizationUrl.origin + authorizationUrl.pathname).toBe(DOCUSIGN_SANDBOX.authorizationUrl); + expect(authorizationUrl.searchParams.get('response_type')).toBe('code'); + expect(authorizationUrl.searchParams.get('client_id')).toBe(INTEGRATION_KEY); + expect(authorizationUrl.searchParams.get('redirect_uri')).toBe(CALLBACK); + expect(authorizationUrl.searchParams.get('scope')).toBe('signature'); + expect(authorizationUrl.searchParams.get('code_challenge_method')).toBe('S256'); + const state = authorizationUrl.searchParams.get('state'); + expect(state).toBeTruthy(); + + const callback = await booted.apiAs( + admin, 'GET', `/automation/connectors/oauth/callback?state=${encodeURIComponent(state!)}&code=${CODE}`, + ); + const callbackText = await callback.text(); + expect(callback.status, callbackText.slice(0, 400)).toBe(200); + expect(callbackText).not.toContain(SECRET_KEY); + expect(tokenRequests).toHaveLength(1); + expect(tokenRequests[0].authorization).toBe(basicOf(INTEGRATION_KEY, SECRET_KEY)); + expect(tokenRequests[0].form.get('grant_type')).toBe('authorization_code'); + expect(tokenRequests[0].form.get('code')).toBe(CODE); + expect(tokenRequests[0].form.get('redirect_uri')).toBe(CALLBACK); + + const read = await booted.apiAs(admin, 'GET', door); + const readText = await read.text(); + expect(read.status, readText.slice(0, 400)).toBe(200); + expect(readText).not.toContain(SECRET_KEY); + expect(dataOf(JSON.parse(readText))).toMatchObject({ + connector: NAME, + status: 'connected', + client: { source: 'installer', clientId: INTEGRATION_KEY, hasSecret: true }, + }); + // Connecting calls DocuSign's account server only, never its API. + expect(requests).toEqual([]); + }); + + it('once connected, it materializes with exactly the three envelope operations', async () => { + let docusign = await descriptor(); + for (let i = 0; i < 100 && docusign?.state !== 'ready'; i++) { + await new Promise((r) => setTimeout(r, 50)); + docusign = await descriptor(); + } + expect(docusign?.state, docusign?.degradedReason).toBe('ready'); + expect(docusign!.actions?.map((a) => a.key).sort()).toEqual([...DOCUSIGN_ENVELOPE_OPERATIONS].sort()); + }); + + it('docusign_send_envelope posts the envelope to the sandbox as the bearer of the token, and records it', async () => { + const res = await booted.apiAs(admin, 'POST', '/automation/docusign_send_envelope/trigger', { + params: { + account_id: ACCOUNT, + email_subject: 'Please sign the contract', + document_name: 'contract.pdf', + document_base64: 'JVBERi0xLjQK', + signer_name: 'Ada Lovelace', + signer_email: 'ada@example.com', + }, + }); + const text = await res.text(); + expect(res.status, text.slice(0, 600)).toBe(200); + expect(requests.at(-1)).toEqual({ + method: 'POST', + url: `${API}/envelopes`, + authorization: `Bearer ${ACCESS_TOKEN}`, + body: { + emailSubject: 'Please sign the contract', + status: 'sent', + documents: [{ documentId: '1', name: 'contract.pdf', documentBase64: 'JVBERi0xLjQK' }], + recipients: { signers: [{ recipientId: '1', routingOrder: '1', name: 'Ada Lovelace', email: 'ada@example.com' }] }, + }, + }); + expect(text).toContain('env-fixture-1'); + const rows = await envelopes(); + expect(rows).toHaveLength(1); + expect(rows[0]).toMatchObject({ + envelope_id: 'env-fixture-1', + signature_status: 'sent', + email_subject: 'Please sign the contract', + status_changed_at: '2026-11-03T05:00:00.0000000Z', + }); + }); + + it('a refused envelope lands in the step output and writes no record', async () => { + const res = await booted.apiAs(admin, 'POST', '/automation/docusign_send_envelope/trigger', { + params: { + account_id: ACCOUNT, + email_subject: 'refuse me', + document_name: 'contract.pdf', + document_base64: 'JVBERi0xLjQK', + signer_name: 'Ada Lovelace', + signer_email: 'ada@example.com', + }, + }); + const text = await res.text(); + expect(res.status, text.slice(0, 600)).toBe(200); + expect(text).toContain('INVALID_REQUEST_BODY'); + expect(await envelopes()).toHaveLength(1); + }); + + it("docusign_get_envelope reads one envelope and refreshes its record's signature status", async () => { + const res = await booted.apiAs(admin, 'POST', '/automation/docusign_get_envelope/trigger', { + params: { account_id: ACCOUNT, envelope_id: 'env-fixture-1' }, + }); + const text = await res.text(); + expect(res.status, text.slice(0, 600)).toBe(200); + expect(requests.at(-1)).toEqual({ + method: 'GET', + url: `${API}/envelopes/env-fixture-1`, + authorization: `Bearer ${ACCESS_TOKEN}`, + }); + const rows = await envelopes(); + expect(rows).toHaveLength(1); + expect(rows[0]).toMatchObject({ signature_status: 'delivered', status_changed_at: '2026-11-03T05:10:00.0000000Z' }); + }); + + it('docusign_poll_status_changes lists changes since from_date and copies each status onto its record', async () => { + const res = await booted.apiAs(admin, 'POST', '/automation/docusign_poll_status_changes/trigger', { + params: { account_id: ACCOUNT, from_date: '2026-11-03T00:00:00Z' }, + }); + const text = await res.text(); + expect(res.status, text.slice(0, 600)).toBe(200); + const sent = requests.at(-1)!; + expect(sent.method).toBe('GET'); + expect(sent.authorization).toBe(`Bearer ${ACCESS_TOKEN}`); + const url = new URL(sent.url); + expect(`${url.origin}${url.pathname}`).toBe(`${API}/envelopes`); + expect(Object.fromEntries(url.searchParams)).toEqual({ from_date: '2026-11-03T00:00:00Z', count: '100' }); + // The envelope this app sent takes DocuSign's status; the one it did not + // send matches no record and writes none. + const rows = await envelopes(); + expect(rows).toHaveLength(1); + expect(rows[0]).toMatchObject({ + envelope_id: 'env-fixture-1', + signature_status: 'completed', + status_changed_at: '2026-11-03T05:20:00.0000000Z', + }); + }); + + it("every API request went to DocuSign's sandbox host, as the bearer of the connection's token", () => { + expect(requests.length).toBeGreaterThan(0); + for (const r of requests) { + expect(new URL(r.url).origin).toBe('https://demo.docusign.net'); + expect(r.authorization).toBe(`Bearer ${ACCESS_TOKEN}`); + } + // One code exchange; the token outlived the run, so no refresh followed. + expect(tokenRequests).toHaveLength(1); + }); +}); diff --git a/packages/apps/connector-docusign/test/listing.test.ts b/packages/apps/connector-docusign/test/listing.test.ts new file mode 100644 index 00000000000..66a542987ba --- /dev/null +++ b/packages/apps/connector-docusign/test/listing.test.ts @@ -0,0 +1,235 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +/** + * The listing this package publishes as, and the document it packages: + * `objectstack.manifest.json` parses against the spec's own schema for that + * file, names the same package id as the stack and points at the icon the + * package owns (a public http(s) `iconUrl`); the connector takes its OAuth + * client from the installer and names none of its own (ADR-0143 D2.1, + * `clientSource: 'installer'`), and the README's credentials section tells + * the administrator where that client comes from and which redirect URI to + * register; the packaged OpenAPI document is the one the README's provenance + * names — its sha256 is the hash recorded there — and it carries exactly the + * operations the connector includes. + */ + +import { describe, it, expect } from 'vitest'; +import { createHash } from 'node:crypto'; +import { readFileSync, readdirSync, existsSync, statSync } from 'node:fs'; +import { join } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { TemplateManifestSchema } from '@objectstack/spec/marketplace'; +import { DeclarativeConnectorEntrySchema } from '@objectstack/spec/integration'; +import stack from '../objectstack.config.js'; +import { + DOCUSIGN_ENVELOPE_OPERATIONS, + DOCUSIGN_SANDBOX, + DocuSignConnector, +} from '../src/connectors/docusign.connector.js'; + +const PACKAGE_ROOT = fileURLToPath(new URL('..', import.meta.url)); +const manifestPath = fileURLToPath(new URL('../objectstack.manifest.json', import.meta.url)); +const manifest = JSON.parse(readFileSync(manifestPath, 'utf8')) as Record; + +/** Where the listing icon is served from once this package's tree is on `main`. */ +const ICON_URL = 'https://raw.githubusercontent.com/objectstack-ai/objectstack/main/packages/apps/connector-docusign/assets/icon.svg'; + +/** + * Why a manifest's `iconUrl` cannot be the listing icon, or `null` when it can: + * `os package publish` sends the key as the package's `icon_url`, and the + * marketplace handbook asks for a public http(s) URL. `TemplateManifestSchema` + * accepts any URL scheme, so the scheme is checked here. + */ +function iconUrlProblem(m: Record): string | null { + const value = m.iconUrl; + if (typeof value !== 'string') return 'iconUrl is absent'; + let url: URL; + try { + url = new URL(value); + } catch { + return `iconUrl is not a URL: ${value}`; + } + return url.protocol === 'https:' || url.protocol === 'http:' ? null : `iconUrl is not http(s): ${value}`; +} + +const readme = readFileSync(fileURLToPath(new URL('../README.md', import.meta.url)), 'utf8'); +const documentBytes = readFileSync(fileURLToPath(new URL('../openapi/docusign-esignature.openapi.json', import.meta.url))); +const document = JSON.parse(documentBytes.toString('utf8')) as { + openapi?: string; + servers?: Array<{ url?: string }>; + security?: unknown; + components?: { securitySchemes?: Record }; + paths?: Record>; +}; + +/** A README `## ` section, up to the next second-level heading. */ +function section(title: string): string { + const start = readme.indexOf(`\n## ${title}\n`); + expect(start, `README has no "## ${title}" section`).toBeGreaterThan(-1); + const rest = readme.slice(start + 1); + const next = rest.indexOf('\n## ', 1); + return next === -1 ? rest : rest.slice(0, next); +} + +/** Every text file this package ships, by path relative to its root. */ +function packageTextFiles(dir = PACKAGE_ROOT, prefix = ''): string[] { + const out: string[] = []; + for (const entry of readdirSync(dir, { withFileTypes: true })) { + if (entry.name === 'node_modules' || entry.name === '.objectstack' || entry.name === 'dist') continue; + const rel = prefix ? `${prefix}/${entry.name}` : entry.name; + if (entry.isDirectory()) out.push(...packageTextFiles(join(dir, entry.name), rel)); + else if (/\.(ts|json|md|svg)$/.test(entry.name)) out.push(rel); + } + return out; +} + +describe('connector-docusign listing', () => { + it('parses against TemplateManifestSchema', () => { + const parsed = TemplateManifestSchema.safeParse(manifest); + expect(parsed.success, JSON.stringify(parsed.error?.issues)).toBe(true); + }); + + it('names the stack manifest id as its package id', () => { + expect(manifest.manifestId).toBe(stack.manifest!.id); + }); + + it('declares no key the publisher ignores', () => { + // The handbook: the platform decides `publisher` and `isStarter`; the CLI never sends them. + expect(manifest).not.toHaveProperty('publisher'); + expect(manifest).not.toHaveProperty('isStarter'); + }); + + it('carries a public http(s) iconUrl', () => { + expect(iconUrlProblem(manifest)).toBeNull(); + }); + + it('refuses an iconUrl that is absent or not http(s)', () => { + const withoutIcon = { ...manifest }; + delete withoutIcon.iconUrl; + expect(iconUrlProblem(withoutIcon)).toBe('iconUrl is absent'); + expect(iconUrlProblem({ ...manifest, iconUrl: 'ftp://example.com/icon.svg' })).toMatch(/not http\(s\)/); + expect(iconUrlProblem({ ...manifest, iconUrl: 'assets/icon.svg' })).toMatch(/not a URL/); + }); + + it('points iconUrl at the icon this package owns', () => { + expect(manifest.iconUrl).toBe(ICON_URL); + const iconPath = fileURLToPath(new URL('../assets/icon.svg', import.meta.url)); + expect(existsSync(iconPath)).toBe(true); + // `--icon-file` takes up to 256 KB; hold the owned asset to the same bound. + expect(statSync(iconPath).size).toBeLessThan(256 * 1024); + expect(readFileSync(iconPath, 'utf8')).toMatch(/^<svg [^>]*viewBox="0 0 256 256"/); + }); +}); + +describe('connector-docusign OAuth client: the installer supplies it, the package names none', () => { + const auth = DocuSignConnector.auth as Record<string, unknown>; + + it('is an oauth2 authorization_code instance that takes its client from the installer', () => { + expect(auth).toEqual({ + type: 'oauth2', + grant: 'authorization_code', + authorizationUrl: DOCUSIGN_SANDBOX.authorizationUrl, + tokenUrl: DOCUSIGN_SANDBOX.tokenUrl, + scopes: ['signature'], + clientSource: 'installer', + }); + expect(auth).not.toHaveProperty('clientId'); + expect(auth).not.toHaveProperty('credentialRef'); + }); + + it("parses against the spec's declarative connector entry schema", () => { + const parsed = DeclarativeConnectorEntrySchema.safeParse(DocuSignConnector); + expect(parsed.success, JSON.stringify(parsed.error?.issues)).toBe(true); + }); + + it('targets the DocuSign developer sandbox: its account server and its API host', () => { + expect(new URL(DOCUSIGN_SANDBOX.authorizationUrl).host).toBe('account-d.docusign.com'); + expect(new URL(DOCUSIGN_SANDBOX.tokenUrl).host).toBe('account-d.docusign.com'); + expect(DocuSignConnector.providerConfig).toMatchObject({ baseUrl: 'https://demo.docusign.net/restapi' }); + }); + + it('ships no client id, secret or token: no GUID-shaped value in any file it owns but the upstream document', () => { + // A DocuSign integration key, secret key and account id are GUIDs. The + // packaged document is DocuSign's and carries example GUIDs of its own. + const guid = /\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/i; + const files = packageTextFiles().filter((f) => !f.startsWith('openapi/')); + expect(files).toContain('src/connectors/docusign.connector.ts'); + for (const file of files) { + const text = readFileSync(join(PACKAGE_ROOT, file), 'utf8'); + expect(text, `${file} carries a GUID-shaped value`).not.toMatch(guid); + } + for (const c of stack.connectors ?? []) { + expect(JSON.stringify(c)).not.toMatch(/clientSecret|accessToken|refreshToken/); + } + }); + + it("the README's credentials section says where the client comes from and which redirect URI to register", () => { + expect(existsSync(fileURLToPath(new URL(`../${String(manifest.readmePath)}`, import.meta.url)))).toBe(true); + const credentials = section('Credentials'); + expect(credentials).toContain('Integration key'); + expect(credentials).toContain('Secret key'); + expect(credentials).toContain('/api/v1/automation/connectors/oauth/callback'); + expect(credentials).toContain('GET /api/v1/automation/connectors/docusign/connection'); + expect(credentials).toContain('POST /api/v1/automation/connectors/docusign/connection'); + expect(credentials).toContain('`redirectUri`'); + expect(credentials).toContain('manage_connector_connections'); + }); + + it("the README's callbacks section names the unverified Connect signature and the poll that replaces it", () => { + const callbacks = section('Callbacks'); + expect(callbacks).toContain('DocuSign Connect'); + expect(callbacks).toContain('docusign_poll_status_changes'); + const flowNames = (stack.flows ?? []).map((f) => (f as { name: string }).name); + expect(flowNames).toContain('docusign_poll_status_changes'); + }); +}); + +describe('connector-docusign packaged OpenAPI document', () => { + it("is the document the README's provenance names: its sha256 is the one recorded there", () => { + const provenance = section('Provenance'); + const recorded = provenance.match(/sha256\s+`([0-9a-f]{64})`/)?.[1]; + expect(recorded, 'README provenance records no sha256').toBeDefined(); + expect(createHash('sha256').update(documentBytes).digest('hex')).toBe(recorded); + }); + + it("is OpenAPI 3.x naming DocuSign's production host, with no security scheme (the token rides on every operation)", () => { + expect(document.openapi).toMatch(/^3\.\d+(\.\d+)?$/); + expect(document.servers?.[0]?.url).toBe('https://www.docusign.net/restapi'); + expect(document.security).toBeUndefined(); + expect(document.components?.securitySchemes ?? {}).toEqual({}); + }); + + it('carries exactly the operations the connector includes', () => { + const operations: Array<{ method: string; path: string; operationId?: string }> = []; + for (const [path, item] of Object.entries(document.paths ?? {})) { + for (const [method, op] of Object.entries(item)) { + if (['get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace'].includes(method)) { + operations.push({ method, path, operationId: op.operationId }); + } + } + } + expect(operations.map((o) => o.operationId).sort()).toEqual([...DOCUSIGN_ENVELOPE_OPERATIONS].sort()); + expect(operations.find((o) => o.operationId === 'Envelopes_PostEnvelopes')).toMatchObject({ + method: 'post', + path: '/v2.1/accounts/{accountId}/envelopes', + }); + expect(DocuSignConnector.providerConfig).toMatchObject({ include: [...DOCUSIGN_ENVELOPE_OPERATIONS] }); + }); + + it('gives Envelopes_PostEnvelopes a JSON request body (the --empty-consumes repair landed)', () => { + const post = (document.paths?.['/v2.1/accounts/{accountId}/envelopes'] as Record<string, unknown>)?.post as { + requestBody?: { content?: Record<string, { schema?: { type?: string; description?: string } }> }; + }; + expect(Object.keys(post?.requestBody?.content ?? {})).toEqual(['application/json']); + // `--max-depth 0`: the envelope definition is a typed, explained stub. + const schema = post?.requestBody?.content?.['application/json']?.schema; + expect(schema?.type).toBe('object'); + expect(schema?.description).toContain('envelopeDefinition'); + }); + + it("ships the upstream document's MIT licence notice beside it", () => { + const notice = readFileSync(fileURLToPath(new URL('../openapi/LICENSE', import.meta.url)), 'utf8'); + expect(notice).toContain('The MIT License'); + expect(notice).toContain('DocuSign, Inc.'); + }); +}); diff --git a/packages/apps/connector-docusign/tsconfig.json b/packages/apps/connector-docusign/tsconfig.json new file mode 100644 index 00000000000..3dfe4ff581b --- /dev/null +++ b/packages/apps/connector-docusign/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "strict": true, + "esModuleInterop": true, + "resolveJsonModule": true, + "forceConsistentCasingInFileNames": true, + "skipLibCheck": true, + "noEmit": true, + "types": ["node"] + }, + "include": ["src/**/*", "objectstack.config.ts", "test/**/*", "vitest.config.ts"] +} diff --git a/packages/apps/connector-docusign/vitest.config.ts b/packages/apps/connector-docusign/vitest.config.ts new file mode 100644 index 00000000000..dce0f58c1b4 --- /dev/null +++ b/packages/apps/connector-docusign/vitest.config.ts @@ -0,0 +1,35 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +import { defineConfig } from 'vitest/config'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const HERE = path.dirname(fileURLToPath(import.meta.url)); + +export default defineConfig({ + test: { + // A late console.* must not redden a green suite: the boot test starts a + // whole kernel, whose logger can write after teardown. Mechanism and + // measured costs: examples/app-showcase/vitest.config.ts; enforced + // repo-wide by scripts/check-console-intercept-disarm.mjs. + disableConsoleIntercept: true, + // Quiet the registry's per-item registration chatter — the engine's own + // `OS_REGISTRY_LOG` seam, not a change to its shipped default. Enforced by + // scripts/check-registry-log-declared.mjs. + env: { OS_REGISTRY_LOG: 'warn' }, + }, + resolve: { + // Every workspace package these tests import resolves to its SOURCE, so a + // verdict here is about the checkout, never about whatever `dist/` was last + // built (`pnpm check:test-source-alias`). Anchored, array form: a bare + // string key matches by prefix and would swallow the subpaths. + alias: [ + { find: /^@objectstack\/connector-openapi$/, replacement: path.resolve(HERE, '../../connectors/connector-openapi/src/index.ts') }, + { find: /^@objectstack\/spec$/, replacement: path.resolve(HERE, '../../spec/src/index.ts') }, + { find: /^@objectstack\/spec\/data$/, replacement: path.resolve(HERE, '../../spec/src/data/index.ts') }, + { find: /^@objectstack\/spec\/integration$/, replacement: path.resolve(HERE, '../../spec/src/integration/index.ts') }, + { find: /^@objectstack\/spec\/marketplace$/, replacement: path.resolve(HERE, '../../spec/src/marketplace/index.ts') }, + { find: /^@objectstack\/verify$/, replacement: path.resolve(HERE, '../../verify/src/index.ts') }, + ], + }, +}); diff --git a/packages/spec/src/system/constants/platform-object-names.test.ts b/packages/spec/src/system/constants/platform-object-names.test.ts index afa1d6f7a66..a12f687d957 100644 --- a/packages/spec/src/system/constants/platform-object-names.test.ts +++ b/packages/spec/src/system/constants/platform-object-names.test.ts @@ -15,10 +15,12 @@ // inverting the spec → * dependency direction. // // If this fails: you added/removed/moved a platform object. Update -// PLATFORM_OBJECTS_BY_PACKAGE in ./platform-object-names.ts to match. +// PLATFORM_OBJECTS_BY_PACKAGE in ./platform-object-names.ts to match. An +// installable app package (one with an `objectstack.manifest.json` listing at +// its root) declares its own objects, not platform ones, and is left out. import { describe, it, expect } from 'vitest'; -import { readFileSync, readdirSync, statSync } from 'node:fs'; +import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs'; import { dirname, join, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; import { @@ -42,6 +44,23 @@ const PACKAGES_DIR = join(REPO_ROOT, 'packages'); */ const FIXTURE_PACKAGES = new Set(['qa', 'create-objectstack']); +/** + * The listing file of an installable app package (`packages/apps/connector-*`): + * its presence at the package root marks a package that is published to the + * marketplace and INSTALLED into an environment, like any third-party app. Its + * objects are the app's own (namespaced `<namespace>_*` by its stack manifest), + * never platform-provided, so they are not this registry's to list. The key is + * the file, not the `apps/` directory: `packages/apps/{account,setup,studio}` + * are platform apps the runtime itself serves, carry no listing, and stay + * scanned. + */ +const INSTALLABLE_LISTING_FILE = 'objectstack.manifest.json'; + +/** Whether the package rooted at `packageRoot` is an installable app listing. */ +function isInstallableAppPackage(packageRoot: string): boolean { + return existsSync(join(packageRoot, INSTALLABLE_LISTING_FILE)); +} + function walk(dir: string, out: string[] = []): string[] { let entries: string[]; try { @@ -81,26 +100,45 @@ function owningPackage(file: string): string | undefined { return parts[srcAt - 1]; } -function scanDeclaredObjects(): Map<string, Set<string>> { - const byPackage = new Map<string, Set<string>>(); +/** `packages/apps/connector-x/src/objects/y.object.ts` → `…/packages/apps/connector-x`. */ +function owningPackageRoot(file: string): string | undefined { + const parts = file.slice(PACKAGES_DIR.length + 1).split(/[\\/]/); + const srcAt = parts.indexOf('src'); + if (srcAt < 1) return undefined; + return join(PACKAGES_DIR, ...parts.slice(0, srcAt)); +} + +interface ObjectScan { + /** Platform contributions: owning package → declared object names. */ + readonly platform: Map<string, Set<string>>; + /** Installable app listings, left out of `platform`: owning package → declared object names. */ + readonly installable: Map<string, Set<string>>; +} + +function scanDeclaredObjects(): ObjectScan { + const platform = new Map<string, Set<string>>(); + const installable = new Map<string, Set<string>>(); for (const file of walk(PACKAGES_DIR)) { const pkg = owningPackage(file); - if (!pkg) continue; + const root = owningPackageRoot(file); + if (!pkg || !root) continue; const rel = file.slice(PACKAGES_DIR.length + 1); const topLevel = rel.split(/[\\/]/)[0]; if (FIXTURE_PACKAGES.has(topLevel) || FIXTURE_PACKAGES.has(pkg)) continue; const name = declaredObjectName(readFileSync(file, 'utf8')); if (!name) continue; - if (!byPackage.has(pkg)) byPackage.set(pkg, new Set()); - byPackage.get(pkg)!.add(name); + const into = isInstallableAppPackage(root) ? installable : platform; + if (!into.has(pkg)) into.set(pkg, new Set()); + into.get(pkg)!.add(name); } - return byPackage; + return { platform, installable }; } const sorted = (v: Iterable<string>) => [...v].sort(); describe('PLATFORM_PROVIDED_OBJECT_NAMES — conformance with what packages register', () => { - const declared = scanDeclaredObjects(); + const scan = scanDeclaredObjects(); + const declared = scan.platform; it('finds the platform object sources (guards against a broken scan)', () => { // A silently-empty scan would make every assertion below vacuously pass. @@ -116,6 +154,32 @@ describe('PLATFORM_PROVIDED_OBJECT_NAMES — conformance with what packages regi } }); + it('leaves installable app listings out of the registry, by their listing file and nothing broader', () => { + // The exclusion is keyed on the package's own listing file: an app package + // with one is installed like a third-party app; the platform apps beside it + // in `packages/apps/` have none and are scanned like any other package. + expect(isInstallableAppPackage(join(PACKAGES_DIR, 'apps', 'connector-docusign'))).toBe(true); + expect(isInstallableAppPackage(join(PACKAGES_DIR, 'apps', 'connector-stripe'))).toBe(true); + for (const platformApp of ['account', 'setup', 'studio']) { + expect(isInstallableAppPackage(join(PACKAGES_DIR, 'apps', platformApp)), platformApp).toBe(false); + } + expect(isInstallableAppPackage(join(PACKAGES_DIR, 'platform-objects'))).toBe(false); + // A lit control: the listing that declares an object is seen, and kept out. + expect(scan.installable.get('connector-docusign')?.has('docusign_envelope')).toBe(true); + expect(declared.has('connector-docusign')).toBe(false); + expect(PLATFORM_PROVIDED_OBJECT_NAMES).not.toContain('docusign_envelope'); + }); + + it('an installable app listing declares no object under a reserved platform prefix', () => { + // The exclusion above must not become a hiding place: an app that shipped a + // `sys_*`-style object would collide with the platform's namespace. + for (const [pkg, names] of scan.installable) { + for (const name of names) { + expect(hasPlatformObjectPrefix(name), `"${name}" in installable app "${pkg}" carries a platform prefix`).toBe(false); + } + } + }); + it('has no registry group for a package that declares no objects', () => { for (const pkg of Object.keys(PLATFORM_OBJECTS_BY_PACKAGE)) { expect(declared.has(pkg), `registry group "${pkg}" no longer matches any package`).toBe(true); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ec2260abe1c..fd716b90821 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -426,6 +426,31 @@ importers: specifier: ^4.1.11 version: 4.1.12(@opentelemetry/api@1.9.1)(@types/node@26.6.5)(@vitest/coverage-v8@4.1.12)(@vitest/ui@4.1.10)(happy-dom@21.0.4)(jsdom@30.1.1(@noble/hashes@2.4.0))(msw@2.15.0(@types/node@26.6.5)(typescript@6.0.3))(vite@8.3.5(@types/node@26.6.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15(patch_hash=4d1d35da1809be2a945519cb26890e48107810b9b66c6913a6a89abb5ad6a1e6))(yaml@2.9.1)) + packages/apps/connector-docusign: + dependencies: + '@objectstack/connector-openapi': + specifier: workspace:* + version: link:../../connectors/connector-openapi + '@objectstack/spec': + specifier: workspace:* + version: link:../../spec + devDependencies: + '@objectstack/cli': + specifier: workspace:* + version: link:../../cli + '@objectstack/verify': + specifier: workspace:* + version: link:../../verify + '@types/node': + specifier: ^26.6.5 + version: 26.6.5 + typescript: + specifier: ^6.0.3 + version: 6.0.3 + vitest: + specifier: ^4.1.11 + version: 4.1.12(@opentelemetry/api@1.9.1)(@types/node@26.6.5)(@vitest/coverage-v8@4.1.12)(@vitest/ui@4.1.10)(happy-dom@21.0.4)(jsdom@30.1.1(@noble/hashes@2.4.0))(msw@2.15.0(@types/node@26.6.5)(typescript@6.0.3))(vite@8.3.5(@types/node@26.6.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15(patch_hash=4d1d35da1809be2a945519cb26890e48107810b9b66c6913a6a89abb5ad6a1e6))(yaml@2.9.1)) + packages/apps/connector-github: dependencies: '@objectstack/connector-mcp': @@ -1179,7 +1204,7 @@ importers: version: link:../../types knex: specifier: ^3.3.0 - version: 3.3.0(better-sqlite3@13.0.3) + version: 3.3.0(better-sqlite3@13.0.3)(mysql2@3.24.6(@types/node@26.6.5))(pg@8.23.1)(tedious@18.6.2) mysql2: specifier: ^3.0.0 version: 3.24.6(@types/node@26.6.5) @@ -1223,7 +1248,7 @@ importers: version: link:../../spec knex: specifier: ^3.3.0 - version: 3.3.0(better-sqlite3@13.0.3) + version: 3.3.0(better-sqlite3@13.0.3)(mysql2@3.24.6(@types/node@26.6.5))(pg@8.23.1)(tedious@18.6.2) nanoid: specifier: ^6.0.1 version: 6.0.1 @@ -19361,7 +19386,7 @@ snapshots: kleur@3.0.3: {} - knex@3.3.0(better-sqlite3@13.0.3): + knex@3.3.0(better-sqlite3@13.0.3)(mysql2@3.24.6(@types/node@26.6.5))(pg@8.23.1)(tedious@18.6.2): dependencies: colorette: 2.0.19 commander: 10.0.1 @@ -19379,6 +19404,9 @@ snapshots: tildify: 2.0.0 optionalDependencies: better-sqlite3: 13.0.3 + mysql2: 3.24.6(@types/node@26.6.5) + pg: 8.23.1 + tedious: 18.6.2 transitivePeerDependencies: - supports-color