@verdocs/js-sdk 6.2.0-beta.1 → 6.2.0-beta.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1688,8 +1688,8 @@ const updateEnvelope = async (endpoint, envelopeId, params) => endpoint.api //
1688
1688
  * @apiBody value . Value to set.
1689
1689
  * @apiSuccess IEnvelopeField . A copy of the newly-updated field.
1690
1690
  */
1691
- const updateEnvelopeField = async (endpoint, envelopeId, roleName, fieldName, value) => endpoint.api //
1692
- .put(`/v2/envelopes/${envelopeId}/recipients/${roleName}/fields/${fieldName}`, { value })
1691
+ const updateEnvelopeField = async (endpoint, envelopeId, roleName, fieldName, value, prepared) => endpoint.api //
1692
+ .put(`/v2/envelopes/${envelopeId}/recipients/${roleName}/fields/${fieldName}`, { value, prepared })
1693
1693
  .then((r) => r.data);
1694
1694
  /**
1695
1695
  * Upload an attachment to an attachment field.
@@ -1697,6 +1697,7 @@ const updateEnvelopeField = async (endpoint, envelopeId, roleName, fieldName, va
1697
1697
  const uploadEnvelopeFieldAttachment = async (endpoint, envelopeId, roleName, fieldName, file, onUploadProgress) => {
1698
1698
  const formData = new FormData();
1699
1699
  formData.append('document', file, file.name);
1700
+ formData.append('value', '');
1700
1701
  return endpoint.api //
1701
1702
  .put(`/v2/envelopes/${envelopeId}/recipients/${roleName}/fields/${fieldName}`, formData, {
1702
1703
  timeout: 120000,
@@ -1763,1721 +1764,1802 @@ const getEnvelopeDocumentPageDisplayUri = async (endpoint, documentId, page, var
1763
1764
  const getEnvelopes = (endpoint, params) => endpoint.api //
1764
1765
  .get('/v2/envelopes', { params })
1765
1766
  .then((r) => r.data);
1766
-
1767
1767
  /**
1768
- * Create an initials block. In a typical signing workflow, the user is asked at the beginning of the process to
1769
- * "adopt" an initials block to be used for all initials fields in the document. Thus, this is typically called
1770
- * one time to create and store an initials block. Thereafter, the ID of the initials block may be re-used for each
1771
- * initials field to be "stamped" by the user.
1772
- *
1773
- * Note: Both "guest" signers and authenticated users can create initials blocks. Guest signers
1774
- * typically only ever have one, tied to that session. But authenticated users can create more than
1775
- * one, and can use them interchangeably.
1776
- *
1777
- * @group Signatures and Initials
1778
- * @api POST /v2/profiles/initials Create Initial Block
1779
- * @apiBody string initial Blob containing initials image to store.
1780
- * @apiSuccess IInitial . The newly-created initial block.
1768
+ * Generate a ZIP file containing all data for the specified envelopes. The caller must be the
1769
+ * owner of each envelope. The returned ZIP file contains a folder for each envelope.
1781
1770
  */
1782
- const createInitials = (endpoint, name, initials) => {
1783
- const data = new FormData();
1784
- data.append('initial', initials, name);
1785
- return endpoint.api //
1786
- .post(`/v2/profiles/initials`, data)
1787
- .then((r) => r.data);
1771
+ const getEnvelopesZip = (endpoint, envelope_ids) => endpoint.api //
1772
+ .get(`/v2/envelopes/zip/${envelope_ids.join(',')}`, { responseType: 'blob', timeout: 120000 });
1773
+
1774
+ const canPerformTemplateAction = (profile, action, template) => {
1775
+ if (!template && !action.includes('create')) {
1776
+ return { canPerform: false, message: 'Missing required template object' };
1777
+ }
1778
+ // We use BOGUS here to force the option-chain in things like template?.profile_id to NOT match profile?.profile_id because if both
1779
+ // were undefined, they would actually match.
1780
+ const profile_id = profile?.id || 'BOGUS';
1781
+ const organization_id = profile?.organization_id || 'BOGUS';
1782
+ const isCreator = template?.profile_id === profile_id;
1783
+ const isSameOrg = template?.organization_id === organization_id;
1784
+ const isPersonal = template?.is_personal ?? false;
1785
+ const isPublic = template?.is_public ?? false;
1786
+ const permissionsRequired = [];
1787
+ switch (action) {
1788
+ case 'create_personal':
1789
+ permissionsRequired.push('template:creator:create:personal');
1790
+ break;
1791
+ case 'create_org':
1792
+ permissionsRequired.push('template:creator:create:org');
1793
+ break;
1794
+ case 'create_public':
1795
+ permissionsRequired.push('template:creator:create:public');
1796
+ break;
1797
+ case 'read':
1798
+ if (!isCreator) {
1799
+ if ((!isPersonal && isSameOrg) || !isPublic) {
1800
+ permissionsRequired.push('template:member:read');
1801
+ }
1802
+ }
1803
+ break;
1804
+ case 'write':
1805
+ if (!isCreator) {
1806
+ permissionsRequired.push('template:member:read');
1807
+ permissionsRequired.push('template:member:write');
1808
+ }
1809
+ break;
1810
+ case 'change_visibility_personal':
1811
+ if (isCreator) {
1812
+ permissionsRequired.push('template:creator:create:personal');
1813
+ }
1814
+ else {
1815
+ permissionsRequired.push('template:member:visibility');
1816
+ }
1817
+ break;
1818
+ case 'change_visibility_org':
1819
+ if (isCreator) {
1820
+ permissionsRequired.push('template:creator:create:org');
1821
+ }
1822
+ else {
1823
+ permissionsRequired.push('template:member:visibility');
1824
+ }
1825
+ break;
1826
+ case 'change_visibility_public':
1827
+ if (isCreator) {
1828
+ permissionsRequired.push('template:creator:create:public');
1829
+ permissionsRequired.push('template:creator:visibility');
1830
+ }
1831
+ else {
1832
+ permissionsRequired.push('template:member:visibility');
1833
+ }
1834
+ break;
1835
+ case 'delete':
1836
+ if (isCreator) {
1837
+ permissionsRequired.push('template:creator:delete');
1838
+ }
1839
+ else {
1840
+ permissionsRequired.push('template:member:delete');
1841
+ }
1842
+ break;
1843
+ default:
1844
+ return { canPerform: false, message: 'Action is not defined' };
1845
+ }
1846
+ if (hasRequiredPermissions(profile, permissionsRequired)) {
1847
+ return { canPerform: true, message: '' };
1848
+ }
1849
+ return { canPerform: false, message: `Insufficient access to perform '${action}'. Needed permissions: ${permissionsRequired.toString()}` };
1788
1850
  };
1851
+ const hasRequiredPermissions = (profile, permissions) => permissions.every((perm) => (profile?.permissions || []).includes(perm));
1789
1852
 
1790
1853
  /**
1791
- * Get the current KBA status. Note that this may only be called by the recipient and requires a
1792
- * valid signing session to proceed. Although the Recipient object itself contains indications of
1793
- * whether KBA is required, it will not contain the current status of the process. If
1794
- * `recipient.auth_methods` is set (not empty), and `recipient.kba_completed` is false, this endpoint
1795
- * should be called to determine the next KBA step required.
1854
+ * Add a field to a template.
1855
+ *
1856
+ * ```typescript
1857
+ * import {createField} from '@verdocs/js-sdk/Templates';
1858
+ *
1859
+ * await createField((VerdocsEndpoint.getDefault(), template_id, { ... });
1860
+ * ```
1861
+ *
1862
+ * @group Fields
1863
+ * @api POST /v2/fields/:template_id Add a field to a template
1864
+ * @apiBody string name Name for the new field. Field names must be unique within a template. Although special characters are allowed, they must be URL-encoded in subsequent requests, so it is recommended to use only alphanumeric characters and hyphens if possible.
1865
+ * @apiBody string role_name Role to assign to the field. Role names must be valid, so it is recommended to create roles before fields.
1866
+ * @apiBody string document_id ID of the document upon which to place the field.
1867
+ * @apiBody string(enum: 'signature' | 'initial' | 'checkbox' | 'radio' | 'textbox' | 'timestamp' | 'date' | 'dropdown' | 'textarea' | 'attachment' | 'payment') type Type of field to create
1868
+ * @apiBody boolean(default: false) required Whether the field is required
1869
+ * @apiBody integer(min: 0) page 0-based page number upon which to place the field
1870
+ * @apiBody integer(min: 0) x X position for the field (left to right)
1871
+ * @apiBody integer(min: 0) y Y position for the field (_bottom to top!_)
1872
+ * @apiBody string label? Optional label to display above the field
1873
+ * @apiBody integer(min: 50) width? Width of the field. Note that all fields have built-in defaults, and it is recommended that this only be set on text fields.
1874
+ * @apiBody integer(min: 15) height? Height of the field. Note that all fields have built-in defaults, and it is recommended that this only be set on text fields.
1875
+ * @apiBody string placeholder? Optional placeholder to display in text fields
1876
+ * @apiBody string group? For fields that support grouping (radio buttons and check boxes) the value selected will be stored under this name
1877
+ * @apiBody array(items:IDropdownOption) options? For dropdown fields, the options to display
1878
+ * @apiBody string value? Optional default value to set on the field
1879
+ * @apiSuccess ITemplateField . Template field
1796
1880
  */
1797
- const getKbaStep = (endpoint, envelope_id, role_name) => endpoint.api //
1798
- .get(`/v2/kba/${envelope_id}/${encodeURIComponent(role_name)}`)
1881
+ const createField = (endpoint, templateId, params) => endpoint.api //
1882
+ .post(`/v2/fields/${templateId}`, params)
1799
1883
  .then((r) => r.data);
1800
1884
  /**
1801
- * Submit a response to a KBA PIN challenge.
1885
+ * Update a template field.
1886
+ *
1887
+ * ```typescript
1888
+ * import {updateField} from '@verdocs/js-sdk/Templates';
1889
+ *
1890
+ * await updateField((VerdocsEndpoint.getDefault(), template_id, field_name, { ... });
1891
+ * ```
1892
+ *
1893
+ * @group Fields
1894
+ * @api PATCH /v2/fields/:template_id/:field_name Update a field. See createField for additional details on the supported parameters.
1895
+ * @apiBody string name? Rename the field. Note that template field names must be unique within a template.
1896
+ * @apiBody string role_name Role to assign to the field.
1897
+ * @apiBody string document_id ID of the document upon which to place the field.
1898
+ * @apiBody string(enum: 'signature' | 'initial' | 'checkbox' | 'radio' | 'textbox' | 'timestamp' | 'date' | 'dropdown' | 'textarea' | 'attachment' | 'payment') type? Change the field type. Note that while this is technically allowed, fields have different behaviors, validators, default sizes, etc. It is usually easier to add a new field and delete the old one.
1899
+ * @apiBody boolean(default: false) required? Whether the field is required
1900
+ * @apiBody integer(min: 0) page? 0-based page number upon which to place the field
1901
+ * @apiBody integer(min: 0) x? X position for the field (left to right)
1902
+ * @apiBody integer(min: 0) y? Y position for the field (_bottom to top!_)
1903
+ * @apiBody string label? Optional label to display above the field
1904
+ * @apiBody integer(min: 50) width? Width of the field. Note that all fields have built-in defaults, and it is recommended that this only be set on text fields.
1905
+ * @apiBody integer(min: 15) height? Height of the field. Note that all fields have built-in defaults, and it is recommended that this only be set on text fields.
1906
+ * @apiBody string placeholder? Optional placeholder to display in text fields
1907
+ * @apiBody string group? For fields that support grouping (radio buttons and check boxes) the value selected will be stored under this name
1908
+ * @apiBody array(items:IDropdownOption) options? For dropdown fields, the options to display
1909
+ * @apiBody string value? Optional default value to set on the field
1910
+ * @apiSuccess ITemplateField . Updated template field
1802
1911
  */
1803
- const submitKbaPin = (endpoint, envelope_id, role_name, pin) => endpoint.api //
1804
- .post(`/v2/kba/pin`, { envelope_id, role_name, pin })
1912
+ const updateField = (endpoint, templateId, name, params) => endpoint.api //
1913
+ .patch(`/v2/fields/${templateId}/${encodeURIComponent(name)}`, params)
1805
1914
  .then((r) => r.data);
1806
1915
  /**
1807
- * Submit an identity response to a KBA challenge.
1916
+ * Remove a field from a template.
1917
+ *
1918
+ * ```typescript
1919
+ * import {deleteField} from '@verdocs/js-sdk/Templates';
1920
+ *
1921
+ * await deleteField((VerdocsEndpoint.getDefault(), template_id, field_name);
1922
+ * ```
1923
+ *
1924
+ * @group Fields
1925
+ * @api DELETE /v2/fields/:template_id/:field_name Delete a field
1926
+ * @apiSuccess string . Success
1808
1927
  */
1809
- const submitKbaIdentity = (endpoint, envelope_id, role_name, identity) => endpoint.api //
1810
- .post(`/v2/kba/identity`, { envelope_id, role_name, identity })
1928
+ const deleteField = (endpoint, templateId, name) => endpoint.api //
1929
+ .delete(`/v2/fields/${templateId}/${encodeURIComponent(name)}`)
1811
1930
  .then((r) => r.data);
1931
+
1812
1932
  /**
1813
- * Submit an identity response to a KBA challenge. Answers should be submitted in the same order as
1814
- * the challenges were listed in `IRecipientKbaStepChallenge.questions`.
1933
+ * A map of the permissions each role confers.
1815
1934
  */
1816
- const submitKbaChallengeResponse = (endpoint, envelope_id, role_name, responses) => endpoint.api //
1817
- .post(`/v2/kba/response`, { envelope_id, role_name, responses })
1818
- .then((r) => r.data);
1935
+ const RolePermissions = {
1936
+ owner: [
1937
+ 'template:creator:create:public',
1938
+ 'template:creator:create:org',
1939
+ 'template:creator:create:personal',
1940
+ 'template:creator:delete',
1941
+ 'template:creator:visibility',
1942
+ 'template:member:read',
1943
+ 'template:member:write',
1944
+ 'template:member:delete',
1945
+ 'template:member:visibility',
1946
+ 'owner:add',
1947
+ 'owner:remove',
1948
+ 'admin:add',
1949
+ 'admin:remove',
1950
+ 'member:view',
1951
+ 'member:add',
1952
+ 'member:remove',
1953
+ 'org:create',
1954
+ 'org:view',
1955
+ 'org:update',
1956
+ 'org:delete',
1957
+ 'org:transfer',
1958
+ 'org:list',
1959
+ 'envelope:create',
1960
+ 'envelope:cancel',
1961
+ 'envelope:view',
1962
+ ],
1963
+ admin: [
1964
+ 'template:creator:create:public',
1965
+ 'template:creator:create:org',
1966
+ 'template:creator:create:personal',
1967
+ 'template:creator:delete',
1968
+ 'template:creator:visibility',
1969
+ 'template:member:read',
1970
+ 'template:member:write',
1971
+ 'template:member:delete',
1972
+ 'template:member:visibility',
1973
+ 'admin:add',
1974
+ 'admin:remove',
1975
+ 'member:view',
1976
+ 'member:add',
1977
+ 'member:remove',
1978
+ 'org:create',
1979
+ 'org:view',
1980
+ 'org:update',
1981
+ 'org:list',
1982
+ 'envelope:create',
1983
+ 'envelope:cancel',
1984
+ 'envelope:view',
1985
+ ],
1986
+ member: [
1987
+ 'template:creator:create:public',
1988
+ 'template:creator:create:org',
1989
+ 'template:creator:create:personal',
1990
+ 'template:creator:delete',
1991
+ 'template:creator:visibility',
1992
+ 'template:member:read',
1993
+ 'template:member:write',
1994
+ 'template:member:delete',
1995
+ 'member:view',
1996
+ 'org:create',
1997
+ 'org:view',
1998
+ 'org:list',
1999
+ 'envelope:create',
2000
+ 'envelope:cancel',
2001
+ 'envelope:view',
2002
+ ],
2003
+ basic_user: ['template:member:read', 'member:view', 'org:view', 'org:list'],
2004
+ contact: ['org:view', 'org:list', 'org:create'],
2005
+ };
2006
+ /**
2007
+ * Confirm whether the user has all of the specified permissions.
2008
+ */
2009
+ const userHasPermissions = (profile, permissions) => {
2010
+ // No need to de-dupe here, we're just checking present-at-least-once set membership.
2011
+ const netPermissions = [...(profile?.permissions || [])];
2012
+ (profile?.roles || []).forEach((role) => {
2013
+ netPermissions.push(...(RolePermissions[role] || []));
2014
+ });
2015
+ (profile?.group_profiles || []).forEach((groupProfile) => {
2016
+ netPermissions.push(...(groupProfile.group?.permissions || []));
2017
+ });
2018
+ return permissions.every((perm) => netPermissions.includes(perm));
2019
+ };
1819
2020
 
1820
2021
  /**
1821
- * Agree to electronic signing dislosures.
2022
+ * Various helpers to identify available operations for a template by a user.
1822
2023
  *
1823
- * @group Recipients
1824
- * @api POST /envelopes/:envelope_id/recipients/:role_name/agree Agree to e-Signing Disclosures
1825
- * @apiParam string(format:uuid) envelope_id The envelope to operate on.
1826
- * @apiParam string role_name The role to operate on.
1827
- * @apiSuccess IRecipient . The updated Recipient.
2024
+ * @module
1828
2025
  */
1829
- const envelopeRecipientAgree = (endpoint, envelopeId, roleName, disclosures) => endpoint.api //
1830
- .post(`/v2/envelopes/${envelopeId}/recipients/${encodeURIComponent(roleName)}/agree`, { disclosures })
2026
+ /**
2027
+ * Check to see if the user created the template.
2028
+ */
2029
+ const userIsTemplateCreator = (profile, template) => profile && template && profile.id === template.profile_id;
2030
+ /**
2031
+ * Check to see if a template is "shared" with the user.
2032
+ */
2033
+ const userHasSharedTemplate = (profile, template) => profile && template && !template.is_personal && profile.organization_id === template.organization_id;
2034
+ /**
2035
+ * Check to see if the user can create a personal/private template.
2036
+ */
2037
+ const userCanCreatePersonalTemplate = (profile) => userHasPermissions(profile, ['template:creator:create:personal']);
2038
+ /**
2039
+ * Check to see if the user can create an org-shared template.
2040
+ */
2041
+ const userCanCreateOrgTemplate = (profile) => userHasPermissions(profile, ['template:creator:create:org']);
2042
+ /**
2043
+ * Check to see if the user can create a public template.
2044
+ */
2045
+ const userCanCreatePublicTemplate = (profile) => userHasPermissions(profile, ['template:creator:create:public']);
2046
+ /**
2047
+ * Check to see if the user can read/view a template.
2048
+ */
2049
+ const userCanReadTemplate = (profile, template) => template.is_public ||
2050
+ userIsTemplateCreator(profile, template) ||
2051
+ (userHasSharedTemplate(profile, template) && userHasPermissions(profile, ['template:member:read']));
2052
+ /**
2053
+ * Check to see if the user can update a tempate.
2054
+ */
2055
+ const userCanUpdateTemplate = (profile, template) => userIsTemplateCreator(profile, template) ||
2056
+ (userHasSharedTemplate(profile, template) && userHasPermissions(profile, ['template:member:read', 'template:member:write']));
2057
+ /**
2058
+ * Check to see if the user can change whether a template is personal vs org-shared.
2059
+ */
2060
+ const userCanMakeTemplatePrivate = (profile, template) => userIsTemplateCreator(profile, template)
2061
+ ? userHasPermissions(profile, ['template:creator:create:personal'])
2062
+ : userHasPermissions(profile, ['template:member:visibility']);
2063
+ /**
2064
+ * Check to see if the user can change whether a template is personal vs org-shared.
2065
+ */
2066
+ const userCanMakeTemplateShared = (profile, template) => userIsTemplateCreator(profile, template)
2067
+ ? userHasPermissions(profile, ['template:creator:create:org'])
2068
+ : userHasPermissions(profile, ['template:member:visibility']);
2069
+ /**
2070
+ * Check to see if the user can change whether a template is personal vs org-shared.
2071
+ */
2072
+ const userCanMakeTemplatePublic = (profile, template) => userIsTemplateCreator(profile, template)
2073
+ ? userHasPermissions(profile, ['template:creator:create:public'])
2074
+ : userHasPermissions(profile, ['template:member:visibility']);
2075
+ /**
2076
+ * Check to see if the user can change whether a template is personal vs org-shared.
2077
+ */
2078
+ const userCanChangeOrgVisibility = (profile, template) => userIsTemplateCreator(profile, template) && userHasPermissions(profile, ['template:creator:create:personal']);
2079
+ /**
2080
+ * Check to see if the user can change whether a template is personal vs org-shared.
2081
+ */
2082
+ const userCanDeleteTemplate = (profile, template) => userIsTemplateCreator(profile, template)
2083
+ ? userHasPermissions(profile, ['template:creator:delete'])
2084
+ : userHasPermissions(profile, ['template:member:delete']);
2085
+ /**
2086
+ * Confirm whether the user can create an envelope using the specified template.
2087
+ */
2088
+ const userCanSendTemplate = (profile, template) => {
2089
+ switch (template.visibility) {
2090
+ case 'private':
2091
+ return userIsTemplateCreator(profile, template);
2092
+ case 'shared':
2093
+ return userIsTemplateCreator(profile, template) || template.organization_id === profile?.organization_id;
2094
+ case 'public':
2095
+ return true;
2096
+ }
2097
+ };
2098
+ /**
2099
+ * Confirm whether the user can create a new template.
2100
+ */
2101
+ const userCanCreateTemplate = (profile) => userCanCreatePersonalTemplate(profile) || userCanCreateOrgTemplate(profile) || userCanCreatePublicTemplate(profile);
2102
+ /**
2103
+ * Check to see if the user can "build" the template (use the field builder). The user must have write access to the
2104
+ * template, and the template must have at least one signer role.
2105
+ */
2106
+ const userCanBuildTemplate = (profile, template) => userCanUpdateTemplate(profile, template) && (template.roles || []).filter((role) => role.type === 'signer').length > 0;
2107
+ const getFieldsForRole = (template, role_name) => (template.fields || []).filter((field) => field.role_name === role_name);
2108
+ /**
2109
+ * Check to see if the user can preview the template. The user must have read access to the template, the template must
2110
+ * have at least one signer, and every signer must have at least one field.
2111
+ */
2112
+ const userCanPreviewTemplate = (profile, template) => {
2113
+ const hasPermission = userCanReadTemplate(profile, template);
2114
+ const signers = (template.roles || []).filter((role) => role.type === 'signer');
2115
+ return hasPermission && signers.length > 0 && signers.every((signer) => getFieldsForRole(template, signer.name).length > 0);
2116
+ };
2117
+
2118
+ /**
2119
+ * A "role" is an individual participant in a signing flow, such as a signer or CC contact.
2120
+ * A role is a placeholder that will eventually become a named recipient. For example, "Tenant 1"
2121
+ * might be replaced with "John Smith" when the document is sent out for signature.
2122
+ *
2123
+ * Role names must be unique within a template, e.g. 'Recipient 1'. They may contain any [a-zA-Z0-9_- ]
2124
+ * characters, although it is recommended to keep them simple and human-readable, and to avoid
2125
+ * spaces (although they are allowed). If spaces are used in role names, be sure to URL-encode them
2126
+ * when calling endpoints like `updateRole()` e.g. 'Recipient%201'.
2127
+ *
2128
+ * NOTE: Roles are always enumerated under Template objects, so there are no "list" or "get" endpoints
2129
+ * for them. To get a template's latest role list, simply call `getTemplate()`.
2130
+ *
2131
+ * @module
2132
+ */
2133
+ /**
2134
+ * Create a role.
2135
+ *
2136
+ * ```typescript
2137
+ * import {createTemplateRole} from '@verdocs/js-sdk';
2138
+ *
2139
+ * const role = await createTemplateRole(VerdocsEndpoint.getDefault(), template_id, params...);
2140
+ * ```
2141
+ *
2142
+ * @group Roles
2143
+ * @api POST /v2/roles/:template_id Add a role to a template
2144
+ * @apiBody string name Name for the new role. Must be unique within the template. May include spaces, but later calls must URL-encode any references to this role, so it is recomended that special characters be avoided.
2145
+ * @apiBody string(enum:'signer' | 'cc' | 'approver') type Type of role to create. Signers act on documents by filling and signing fields. CC recipients receive a copy but do not act on the document. Approvers control the final submission of a document, but do not have fields of their own to fill out.
2146
+ * @apiBody string full_name? Default full name for the role. May be completed/overridden later, when envelopes are made from the template.
2147
+ * @apiBody string email? Default email address for the role. May be completed/overridden later, when envelopes are made from the template.
2148
+ * @apiBody string phone? Default (SMS-capable) phone number for the role. May be completed/overridden later, when envelopes are made from the template.
2149
+ * @apiBody string message? Optional message to include in email and SMS signing invitations.
2150
+ * @apiBody integer(min: 1, default: 1) sequence? Optional 1-based sequence number for the role. Roles that share the same sequence number act in parallel, and will receive invitations at the same time.
2151
+ * @apiBody integer(min: 1, default: 1) order? Optional 1-based order number for the role. Controls the left-to-right display order of roles at the same sequence number in the UI components e.g. `<verdocs-template-roles />`.
2152
+ * @apiBody boolean delegator? If true, the role may delegate their signing responsibility to another party.
2153
+ * @apiSuccess IRole . The newly-created role
2154
+ */
2155
+ const createTemplateRole = (endpoint, template_id, params) => endpoint.api //
2156
+ .post(`/v2/roles/${template_id}`, params)
1831
2157
  .then((r) => r.data);
1832
2158
  /**
1833
- * Decline electronic signing dislosures. Note that if any recipient declines, the entire envelope
1834
- * becomes non-viable and later recipients may no longer act. The creator will receive a notification
1835
- * when this occurs.
2159
+ * Update a role.
1836
2160
  *
1837
- * @group Recipients
1838
- * @api POST /envelopes/:envelope_id/recipients/:role_name/decline Decline e-Signing Disclosures
1839
- * @apiParam string(format:uuid) envelope_id The envelope to operate on.
1840
- * @apiParam string role_name The role to adjust.
1841
- * @apiSuccess IRecipient . The updated Recipient.
2161
+ * ```typescript
2162
+ * import {updateTemplateRole} from '@verdocs/js-sdk';
2163
+ *
2164
+ * const role = await updateTemplateRole(VerdocsEndpoint.getDefault(), template_id, name, params...);
2165
+ * ```
2166
+ *
2167
+ * @group Roles
2168
+ * @api PATCH /v2/roles/:template_id/:role_id Update a role. See createRole for additional details on the parameters available.
2169
+ * @apiBody string name? Rename the role. Note that role names must be unique within a template, so this may fail if the new name is already in use.
2170
+ * @apiBody string(enum:'signer' | 'cc' | 'approver') type? Type of role.
2171
+ * @apiBody string full_name? Default full name for the role.
2172
+ * @apiBody string email? Default email address for the role.
2173
+ * @apiBody string phone? Default (SMS-capable) phone number for the role.
2174
+ * @apiBody string message? Optional message to include in email and SMS signing invitations.
2175
+ * @apiBody integer(min: 1, default: 1) sequence? Optional 1-based sequence number for the role.
2176
+ * @apiBody integer(min: 1, default: 1) order? Optional 1-based order number for the role.
2177
+ * @apiBody boolean delegator? If true, the role may delegate their signing responsibility to another party.
2178
+ * @apiBody string(enum:'pin'|'identity'|'') kba_method? Active PIN- or Identity-based KBA for the role.
2179
+ * @apiSuccess IRole . The newly-created role
1842
2180
  */
1843
- const envelopeRecipientDecline = (endpoint, envelopeId, roleName) => endpoint.api //
1844
- .post(`/v2/envelopes/${envelopeId}/recipients/${encodeURIComponent(roleName)}/decline`)
2181
+ const updateTemplateRole = (endpoint, template_id, name, params) => endpoint.api //
2182
+ .patch(`/v2/roles/${template_id}/${encodeURIComponent(name)}`, params)
1845
2183
  .then((r) => r.data);
1846
2184
  /**
1847
- * Submit an envelope (signing is finished). Note that all fields must be valid/completed for this to succeed.
2185
+ * Delete a role.
1848
2186
  *
1849
- * @group Recipients
1850
- * @api POST /envelopes/:envelope_id/recipients/:role_name/submit Submit envelope
1851
- * @apiParam string(format:uuid) envelope_id The envelope to operate on.
1852
- * @apiParam string role_name The role to submit.
1853
- * @apiSuccess IRecipient . The updated Recipient.
2187
+ * ```typescript
2188
+ * import {deleteTemplateRole} from '@verdocs/js-sdk';
2189
+ *
2190
+ * const profiles = await deleteTemplateRole(VerdocsEndpoint.getDefault(), template_id, name);
2191
+ * ```
2192
+ *
2193
+ * @group Roles
2194
+ * @api DELETE /v2/roles/:template_id/:role_id Delete a role.
2195
+ * @apiSuccess string . Success
1854
2196
  */
1855
- const envelopeRecipientSubmit = (endpoint, envelopeId, roleName) => endpoint.api //
1856
- .put(`/v2/envelopes/${envelopeId}/recipients/${roleName}/submit`)
2197
+ const deleteTemplateRole = (endpoint, template_id, name) => endpoint.api //
2198
+ .delete(`/v2/roles/${template_id}/${encodeURIComponent(name)}`)
1857
2199
  .then((r) => r.data);
2200
+
1858
2201
  /**
1859
- * Begin a signing session for an Envelope. This path requires an invite code, and should generally
1860
- * be called with a NON-default Endpoint to avoid conflicting with any active user session the user
1861
- * may have. To initiate in-person signing by an authenticated user (e.g. self-signing), call
1862
- * getInPersonLink() instead. The response from that call includes both a link for direct signing
1863
- * via a Web browser as well as an in-person access_key. That access_key.key may be used here as well.
2202
+ * A Template defines how a Verdocs signing flow will be performed, including attachments, signing fields, and
2203
+ * recipients.
2204
+ *
2205
+ * @module
2206
+ */
2207
+ /**
2208
+ * Get all templates accessible by the caller, with optional filters.
2209
+ *
2210
+ * ```typescript
2211
+ * import {getTemplates} from '@verdocs/js-sdk/Templates';
2212
+ *
2213
+ * await getTemplates((VerdocsEndpoint.getDefault());
2214
+ * await getTemplates((VerdocsEndpoint.getDefault(), { is_starred: true });
2215
+ * await getTemplates((VerdocsEndpoint.getDefault(), { is_creator: true });
2216
+ * await getTemplates((VerdocsEndpoint.getDefault(), { is_organization: true });
2217
+ * ```
2218
+ *
2219
+ * @group Templates
2220
+ * @api GET /v2/templates Get Templates
2221
+ * @apiQuery string q? Find templates whose names/descriptions contain the specified query string
2222
+ * @apiQuery boolean is_starred? If true, returns only templates with at least one "star".
2223
+ * @apiQuery boolean is_creator? If true, returns only templates that the caller created.
2224
+ * @apiQuery string(enum: 'private_shared' | 'private' | 'shared' | 'public') visibility? Return only templates with the specified visibility.
2225
+ * @apiQuery string(enum: 'created_at' | 'updated_at' | 'name' | 'last_used_at' | 'counter' | 'star_counter') sort_by? Return results sorted by this criteria
2226
+ * @apiQuery boolean ascending? Set true/false to override the sort direction. Note that the default depends on `sort_by`. Date-based sorts default to descending, while name defaults to ascending.
2227
+ * @apiQuery integer(default: 20) rows? Limit the number of rows returned
2228
+ * @apiQuery integer(default: 0) page? Specify which page of results to return
2229
+ * @apiSuccess integer(format: int32) count The total number of records matching the query, helpful for pagination
2230
+ * @apiSuccess integer(format: int32) rows The number of rows returned in this response page
2231
+ * @apiSuccess integer(format: int32) page The page number of this response
2232
+ * @apiSuccess array(items: ITemplate) templates List of templates found
2233
+ */
2234
+ const getTemplates = (endpoint, params) => endpoint.api //
2235
+ .get('/v2/templates', { params })
2236
+ .then((r) => r.data);
2237
+ /**
2238
+ * Get one template by its ID.
1864
2239
  *
1865
- * @group Recipients
1866
- * @api POST /v2/sign/unauth/:envelope_id/:role_name/:key Start Signing Session
1867
- * @apiParam string(format:uuid) envelope_id The envelope to operate on.
1868
- * @apiParam string role_name The role to request.
1869
- * @apiParam string key Access key generated by the envelope creator or email/SMS invite.
1870
- * @apiSuccess ISignerTokenResponse . Signing session token and envelope/recipient metadata.
2240
+ * ```typescript
2241
+ * import {getTemplate} from '@verdocs/js-sdk/Templates';
2242
+ *
2243
+ * const template = await getTemplate((VerdocsEndpoint.getDefault(), '83da3d70-7857-4392-b876-c4592a304bc9');
2244
+ * ```
2245
+ *
2246
+ * @group Templates
2247
+ * @api GET /v2/templates/:template_id Get a template. Note that the caller must have at least View access to the template.
2248
+ * @apiSuccess ITemplate . The requested template
1871
2249
  */
1872
- const startSigningSession = async (endpoint, envelope_id, role_name, key) => {
2250
+ const getTemplate = (endpoint, templateId) => {
1873
2251
  return endpoint.api //
1874
- .post(`/v2/sign/unauth/${envelope_id}/${encodeURIComponent(role_name)}/${key}`)
2252
+ .get(`/v2/templates/${templateId}`)
1875
2253
  .then((r) => {
1876
- endpoint.setToken(r.data.access_token, 'signing');
1877
- return r.data;
2254
+ const template = r.data;
2255
+ // Post-process the template to upgrade to new data fields
2256
+ if (!template.documents && template.template_documents) {
2257
+ template.documents = template.template_documents;
2258
+ }
2259
+ template.documents?.forEach((document) => {
2260
+ if (!document.order) {
2261
+ document.order = 0;
2262
+ }
2263
+ if (document.page_numbers) {
2264
+ document.pages = document.page_numbers;
2265
+ }
2266
+ });
2267
+ // Temporary upgrade from legacy app
2268
+ template.fields?.forEach((field) => {
2269
+ if (field.setting) {
2270
+ field.settings = field.setting;
2271
+ }
2272
+ });
2273
+ return template;
1878
2274
  });
1879
2275
  };
2276
+ const ALLOWED_CREATE_FIELDS = [
2277
+ 'name',
2278
+ 'is_personal',
2279
+ 'is_public',
2280
+ 'sender',
2281
+ 'description',
2282
+ 'roles',
2283
+ 'fields',
2284
+ ];
1880
2285
  /**
1881
- * Get an in-person signing link. Must be called by the owner/creator of the envelope. The response
1882
- * also includes the raw access key that may be used to directly initiate a signing session (see
1883
- * `startSigningSession`) as well as an access token representing a valid signing session for
1884
- * immediate use in embeds or other applications. Note that in-person signing is considered a
1885
- * lower-security operation than authenticated signing, and the final envelope certificate will
1886
- * reflect this.
2286
+ * Create a template.
1887
2287
  *
1888
- * @group Recipients
1889
- * @api POST /v2/sign/in-person/:envelope_id/:role_name Get In-Person Signing Link
1890
- * @apiParam string(format:uuid) envelope_id The envelope to operate on.
1891
- * @apiParam string role_name The role to request.
1892
- * @apiSuccess IInPersonLinkResponse . Signing session token and envelope/recipient metadata.
2288
+ * ```typescript
2289
+ * import {createTemplate} from '@verdocs/js-sdk/Templates';
2290
+ *
2291
+ * const newTemplate = await createTemplate((VerdocsEndpoint.getDefault(), {...});
2292
+ * ```
2293
+ *
2294
+ * @group Templates
2295
+ * @api POST /v2/templates Create a template
2296
+ * @apiBody string name Template name
2297
+ * @apiBody string description? Optional description
2298
+ * @apiBody TTemplateVisibility visibility? Visibility setting
2299
+ * @apiBody boolean is_personal? Deprecated. If true, the template is personal and can only be seen by the caller. (Use "visibility" for new calls.)
2300
+ * @apiBody boolean is_public? Deprecated. If true, the template is public and can be seen by anybody. (Use "visibility" for new calls.)
2301
+ * @apiBody TTemplateSender sender? Who may send envelopes using this template
2302
+ * @apiBody number initial_reminder? Delay (in seconds) before the first reminder is sent (min: 4hrs). Set to 0 or null to disable.
2303
+ * @apiBody number followup_reminders? Delay (in seconds) before the subsequent reminders are sent (min: 12hrs). Set to 0 or null to disable.
2304
+ * @apiBody array(items:object) documents? Optional list of documents to attach to the template
2305
+ * @apiBody array(items:IRole) roles? Optional list of roles to create. Note that if roles are not included in the request, fields will be ignored.
2306
+ * @apiBody array(fields:ITemplateField) fields? Optional list of fields to create. Note that if fields that do not match a role will be ignored.
2307
+ * @apiSuccess ITemplate . The newly-created template
1893
2308
  */
1894
- const getInPersonLink = (endpoint, envelope_id, role_name) => endpoint.api //
1895
- .post(`/v2/sign/in-person/${envelope_id}/${encodeURIComponent(role_name)}`)
1896
- .then((r) => r.data);
2309
+ const createTemplate = (endpoint, params, onUploadProgress) => {
2310
+ const options = {
2311
+ timeout: 120000,
2312
+ onUploadProgress: (event) => {
2313
+ const total = event.total || 1;
2314
+ const loaded = event.loaded || 0;
2315
+ onUploadProgress?.(Math.floor((loaded * 100) / (total)), loaded, total);
2316
+ },
2317
+ };
2318
+ if (params.documents && params.documents[0] instanceof File) {
2319
+ const formData = new FormData();
2320
+ ALLOWED_CREATE_FIELDS.forEach((allowedKey) => {
2321
+ if (params[allowedKey] !== undefined) {
2322
+ formData.append(allowedKey, params[allowedKey]);
2323
+ }
2324
+ });
2325
+ params.documents.forEach((file) => {
2326
+ formData.append('documents', file, file.name);
2327
+ });
2328
+ return endpoint.api.post('/v2/templates', formData, options).then((r) => r.data);
2329
+ }
2330
+ else {
2331
+ return endpoint.api.post('/v2/templates', params, options).then((r) => r.data);
2332
+ }
2333
+ };
1897
2334
  /**
1898
- * Verify a recipient within a signing session. All signing sessions use an invite code at a minimum,
1899
- * but many scenarios require more robust verification of recipients, so one or more verification
1900
- * methods may be attached to each recipient. If an authentication method is enabled, the
1901
- * signer must first accept the e-signature disclosures, then complete each verification step
1902
- * before attempting to view/display documents, complete any fields, or submit the envelope.
1903
- * This endpoint should be called to complete each step. If the call fails an error will be
1904
- * thrown.
2335
+ * Duplicate a template. Creates a complete clone, including all settings (e.g. reminders), fields,
2336
+ * roles, and documents.
1905
2337
  *
1906
- * @group Recipients
1907
- * @api POST /v2/sign/verify Verify recipient/signer
1908
- * @apiParam string(enum:'passcode'|'email'|'sms'|'kba'|'id') auth_method The authentication method being completed
1909
- * @apiParam string code? The passcode or OTP entered. Required for passcode, email, and SMS methods.
1910
- * @apiParam boolean resend? For SMS or email methods, set to send a new code.
1911
- * @apiParam boolean first_name? For KBA, the recipient's first name
1912
- * @apiParam boolean last_name? For KBA, the recipient's last name
1913
- * @apiParam boolean address? For KBA, the recipient's address
1914
- * @apiParam boolean city? For KBA, the recipient's city
1915
- * @apiParam boolean state? For KBA, the recipient's state
1916
- * @apiParam boolean zip? For KBA, the recipient's zip code
1917
- * @apiParam boolean ssn_last_4? For KBA, the last 4 digits of the recipient's SSN
1918
- * @apiParam boolean dob? For KBA, the recipient's date of birth
1919
- * @apiParam array(items:IKBAResponse) responses? For KBA, responses to any challenge questions presented
1920
- * @apiSuccess ISignerTokenResponse . Updated signing session.
2338
+ * ```typescript
2339
+ * import {duplicateTemplate} from '@verdocs/js-sdk/Templates';
2340
+ *
2341
+ * const newTemplate = await duplicateTemplate((VerdocsEndpoint.getDefault(), originalTemplateId, 'My Template Copy');
2342
+ * ```
2343
+ *
2344
+ * @group Templates
2345
+ * @api PUT /v2/templates/:template_id Perform an operation on a template
2346
+ * @apiBody string(enum:'duplicate') action Action to perform
2347
+ * @apiBody string name? If duplicating the template, a name for the new copy
2348
+ * @apiSuccess ITemplate . The newly-copied template
1921
2349
  */
1922
- const verifySigner = (endpoint, params) => endpoint.api //
1923
- .post(`/v2/sign/verify`, params)
2350
+ const duplicateTemplate = (endpoint, templateId, name) => endpoint.api //
2351
+ .put(`/v2/templates/${templateId}`, { action: 'duplicate', name })
1924
2352
  .then((r) => r.data);
1925
2353
  /**
1926
- * Delegate a recipient's signing responsibility. The envelope sender must enable this before the
1927
- * recipient calls this endpoint, and only the recipient may call it, or the call will be rejected.
1928
- * The recipient's role will be renamed and configured to indicate to whom the delegation was made,
1929
- * and a new recipient entry with the updated details (e.g. name and email address) will be added
1930
- * to the flow with the same role_name, order, and sequence of the original recipient. Unless
1931
- * no_contact is set on the envelope, the delegation recipient and envelope creator will also be
1932
- * notified.
2354
+ * Create a template from a Sharepoint asset.
1933
2355
  *
1934
- * @group Recipients
1935
- * @api PUT /v2/envelopes/:envelope_id/recipients/:role_name Delegate Recipient
1936
- * @apiParam string(format:uuid) envelope_id The envelope to operate on.
1937
- * @apiParam string role_name The role to operate on.
1938
- * @apiBody string(enum:'delegate') action The operation to perform (delegate).
1939
- * @apiBody string first_name The first name of the new recipient.
1940
- * @apiBody string last_name The last name of the new recipient.
1941
- * @apiBody string email The email address of the new recipient.
1942
- * @apiBody string phone? Optional phone number for the new recipient.
1943
- * @apiBody string message? Optional phone number for the new recipient's invitation.
1944
- * @apiSuccess string . Success message.
2356
+ * ```typescript
2357
+ * import {createTemplateFromSharepoint} from '@verdocs/js-sdk/Templates';
2358
+ *
2359
+ * const newTemplate = await createTemplateFromSharepoint((VerdocsEndpoint.getDefault(), {...});
2360
+ * ```
2361
+ *
2362
+ * @group Templates
2363
+ * @api POST /v2/templates/from-sharepoint Create a template from an asset in Sharepoint
2364
+ * @apiBody string name Name for the new template
2365
+ * @apiBody string siteId Name for the new template
2366
+ * @apiBody string itemId Name for the new template
2367
+ * @apiBody string oboToken On-Behalf-Of token for calls to Sharepoint. Should be generated as a short-expiration token with at least Read privileges to the siteId/itemId. This token will be discarded after being used.
2368
+ * @apiSuccess ITemplate . The newly-created template
1945
2369
  */
1946
- const delegateRecipient = (endpoint, envelopeId, roleName, params) => endpoint.api //
1947
- .put(`/v2/envelopes/${envelopeId}/recipients/${encodeURIComponent(roleName)}`, { action: 'delegate', ...params })
1948
- .then((r) => r.data);
2370
+ const createTemplateFromSharepoint = (endpoint, params) => {
2371
+ const options = {
2372
+ timeout: 120000,
2373
+ };
2374
+ return endpoint.api.post('/v2/templates/from-sharepoint', params, options).then((r) => r.data);
2375
+ };
1949
2376
  /**
1950
- * Update a recipient. NOTE: User interfaces should rate-limit this operation to avoid spamming recipients.
1951
- * Excessive use of this endpoint may result in Verdocs rate-limiting the calling application to prevent
1952
- * abuse. This endpoint will return a 200 OK even if the no_contact flag is set on the envelope (in which
1953
- * case the call will be silently ignored).
2377
+ * Update a template.
1954
2378
  *
1955
- * @group Recipients
1956
- * @api PATCH /envelopes/:envelope_id/recipients/:role_name Update Recipient
1957
- * @apiParam string(format:uuid) envelope_id The envelope to operate on.
1958
- * @apiParam string role_name The role name to update.
1959
- * @apiBody string(enum:'remind'|'reset') action? Trigger a reminder, or fully reset the recipient
1960
- * @apiBody string first_name? Update the recipient's first name.
1961
- * @apiBody string last_name? Update the recipient's last name.
1962
- * @apiBody string email? Update the recipient's email address.
1963
- * @apiBody string message? Update the recipient's invite message.
1964
- * @apiBody string phone? Update the recipient's phone number.
1965
- * @apiBody string passcode? If passcode authentication is used, the recipient's address to prefill. May only be changed if the recipient has not already completed passcode-based auth.
1966
- * @apiBody string address? If KBA-based authentication is used, the recipient's address to prefill. May only be changed if the recipient has not already completed KBA-based auth.
1967
- * @apiBody string city? If KBA-based authentication is used, the recipient's city to prefill. May only be changed if the recipient has not already completed KBA-based auth.
1968
- * @apiBody string state? If KBA-based authentication is used, the recipient's state to prefill. May only be changed if the recipient has not already completed KBA-based auth.
1969
- * @apiBody string zip? If KBA-based authentication is used, the recipient's zip code to prefill. May only be changed if the recipient has not already completed KBA-based auth.
1970
- * @apiBody string dob? If KBA-based authentication is used, the recipient's date of birth to prefill. May only be changed if the recipient has not already completed KBA-based auth.
1971
- * @apiBody string ssn_last_4? If KBA-based authentication is used, the recipient's SSN-last-4 to prefill. May only be changed if the recipient has not already completed KBA-based auth.
1972
- * @apiSuccess IRecipient . The updated Recipient.
2379
+ * ```typescript
2380
+ * import {updateTemplate} from '@verdocs/js-sdk/Templates';
2381
+ *
2382
+ * const updatedTemplate = await updateTemplate((VerdocsEndpoint.getDefault(), '83da3d70-7857-4392-b876-c4592a304bc9', { name: 'New Name' });
2383
+ * ```
2384
+ *
2385
+ * @group Templates
2386
+ * @api PATCH /v2/templates/:template_id Update a template
2387
+ * @apiBody string name? Template name
2388
+ * @apiBody string description? Optional description
2389
+ * @apiBody TTemplateVisibility visibility? Visibility setting
2390
+ * @apiBody boolean is_personal? Deprecated. If true, the template is personal and can only be seen by the caller. (Use "visibility" for new calls.)
2391
+ * @apiBody boolean is_public? Deprecated. If true, the template is public and can be seen by anybody. (Use "visibility" for new calls.)
2392
+ * @apiBody TTemplateSender sender? Who may send envelopes using this template
2393
+ * @apiBody number initial_reminder? Delay (in seconds) before the first reminder is sent (min: 4hrs). Set to 0 or null to disable.
2394
+ * @apiBody number followup_reminders? Delay (in seconds) before the subsequent reminders are sent (min: 12hrs). Set to 0 or null to disable.
2395
+ * @apiSuccess ITemplate . The updated template
1973
2396
  */
1974
- const updateRecipient = (endpoint, envelopeId, roleName, params) => endpoint.api //
1975
- .patch(`/v2/envelopes/${envelopeId}/recipients/${encodeURIComponent(roleName)}`, params)
2397
+ const updateTemplate = (endpoint, templateId, params) => endpoint.api //
2398
+ .patch(`/v2/templates/${templateId}`, params)
1976
2399
  .then((r) => r.data);
1977
2400
  /**
1978
- * Send a reminder to a recipient. The recipient must still be an active member of the signing flow
1979
- * (e.g. not declined, already submitted, etc.)
2401
+ * Delete a template.
2402
+ *
2403
+ * ```typescript
2404
+ * import {deleteTemplate} from '@verdocs/js-sdk/Templates';
2405
+ *
2406
+ * await deleteTemplate((VerdocsEndpoint.getDefault(), '83da3d70-7857-4392-b876-c4592a304bc9');
2407
+ * ```
2408
+ *
2409
+ * @group Templates
2410
+ * @api DELETE /v2/templates/:template_id Delete a template
2411
+ * @apiSuccess string . Success
1980
2412
  */
1981
- const remindRecipient = (endpoint, envelopeId, roleName) => endpoint.api //
1982
- .patch(`/v2/envelopes/${envelopeId}/recipients/${encodeURIComponent(roleName)}`, { action: 'remind' })
2413
+ const deleteTemplate = (endpoint, templateId) => endpoint.api //
2414
+ .delete(`/v2/templates/${templateId}`)
1983
2415
  .then((r) => r.data);
1984
- /**
1985
- * Fully reset a recipient. This allows the recipient to restart failed KBA flows, change
1986
- * fields they may have filled in incorrectly while signing, etc. This cannot be used on a
1987
- * canceled or completed envelope, but may be used to restart an envelope marked declined.
2416
+ /**
2417
+ * Toggle the template star for a template.
2418
+ *
2419
+ * ```typescript
2420
+ * import {toggleTemplateStar} from '@verdocs/js-sdk/Templates';
2421
+ *
2422
+ * await toggleTemplateStar((VerdocsEndpoint.getDefault(), '83da3d70-7857-4392-b876-c4592a304bc9');
2423
+ * ```
2424
+ *
2425
+ * @group Templates
2426
+ * @api POST /v2/templates/:template_id/star Star or unstar a template (toggle state)
2427
+ * @apiSuccess ITemplate . Success
1988
2428
  */
1989
- const resetRecipient = (endpoint, envelopeId, roleName) => endpoint.api //
1990
- .patch(`/v2/envelopes/${envelopeId}/recipients/${encodeURIComponent(roleName)}`, { action: 'reset' })
2429
+ const toggleTemplateStar = (endpoint, templateId) => endpoint.api //
2430
+ .post(`/v2/templates/${templateId}/stars/toggle`)
1991
2431
  .then((r) => r.data);
1992
2432
 
1993
2433
  /**
1994
- * Various helpers to identify available operations for an envelope by a user.
2434
+ * A TemplateDocument represents a PDF or other attachment in a Template.
1995
2435
  *
1996
2436
  * @module
1997
2437
  */
1998
2438
  /**
1999
- * Check to see if the profile ID owns the envelope.
2000
- */
2001
- const isEnvelopeOwner = (profile_id, envelope) => envelope.profile_id === profile_id;
2002
- /**
2003
- * Check to see if the profile ID is a recipient within the envelope.
2004
- */
2005
- const isEnvelopeRecipient = (profile_id, envelope) => (envelope.recipients || []).some((recipient) => recipient.profile_id === profile_id);
2006
- /**
2007
- * Check to see if the profile ID is the envelope's sender or one of the recipients.
2008
- */
2009
- const canAccessEnvelope = (profile_id, envelope) => isEnvelopeOwner(profile_id, envelope) || isEnvelopeRecipient(profile_id, envelope);
2010
- /**
2011
- * Check to see if the user owns the envelope.
2012
- */
2013
- const userIsEnvelopeOwner = (profile, envelope) => envelope.profile_id === profile?.id;
2014
- /**
2015
- * Check to see if the user is a recipient within the envelope.
2016
- */
2017
- const userIsEnvelopeRecipient = (profile, envelope) => (envelope.recipients || []).some((recipient) => recipient.profile_id === profile?.id);
2018
- /**
2019
- * Check to see if the profile ID is the envelope's sender or one of the recipients.
2439
+ * Create a Document for a particular Template.
2440
+ *
2441
+ * ```typescript
2442
+ * import {TemplateDocument} from '@verdocs/js-sdk/Templates';
2443
+ *
2444
+ * await TemplateDocument.createDocument(VerdocsEndpoint.getDefault(), templateID, params);
2445
+ * ```
2446
+ *
2447
+ * @group Template Documents
2448
+ * @api POST /v2/templates/:template_id/documents Attach a document to a template
2449
+ * @apiBody string(format:binary) file Document file to attach. The file name will automatically be used as the document name.
2450
+ * @apiBody string(format:uuid) template_id Template ID to attach the document to
2451
+ * @apiSuccess ITemplateDocument . Template document
2020
2452
  */
2021
- const useCanAccessEnvelope = (profile, envelope) => userIsEnvelopeOwner(profile, envelope) || userIsEnvelopeRecipient(profile, envelope);
2453
+ const createTemplateDocument = (endpoint, template_id, file, onUploadProgress) => {
2454
+ const formData = new FormData();
2455
+ formData.append('file', file, file.name);
2456
+ formData.append('template_id', template_id);
2457
+ return endpoint.api //
2458
+ .post(`/v2/template-documents`, formData, {
2459
+ timeout: 120000,
2460
+ onUploadProgress: (event) => {
2461
+ const total = event.total || 1;
2462
+ const loaded = event.loaded || 0;
2463
+ onUploadProgress?.(Math.floor((loaded * 100) / (total)), loaded, total);
2464
+ },
2465
+ })
2466
+ .then((r) => r.data);
2467
+ };
2022
2468
  /**
2023
- * Check to see if the envelope has pending actions.
2469
+ * Delete a specific Document.
2470
+ *
2471
+ * ```typescript
2472
+ * import {deleteTemplateDocument} from '@verdocs/js-sdk/Templates';
2473
+ *
2474
+ * await deleteTemplateDocument(VerdocsEndpoint.getDefault(), documentID);
2475
+ * ```
2476
+ *
2477
+ * @group Template Documents
2478
+ * @api DELETE /v2/template-documents/:document_id Delete a template document
2479
+ * @apiSuccess string . Success
2024
2480
  */
2025
- const envelopeIsActive = (envelope) => envelope.status !== 'complete' && envelope.status !== 'declined' && envelope.status !== 'canceled';
2481
+ const deleteTemplateDocument = (endpoint, documentId) => endpoint.api //
2482
+ .delete(`/v2/template-documents/${documentId}`)
2483
+ .then((r) => r.data);
2026
2484
  /**
2027
- * Check to see if the envelope has been completed.
2485
+ * Get all metadata for a template document. Note that when called by non-creators (e.g. Org Collaborators)
2486
+ * this will return only the **metadata** the caller is allowed to view.
2487
+ *
2488
+ * @group Template Documents
2489
+ * @api GET /v2/envelope-documents/:id Get envelope document
2490
+ * @apiParam string(format: 'uuid') document_id The ID of the document to retrieve.
2491
+ * @apiSuccess IEnvelopeDocument . The detailed metadata for the document requested
2028
2492
  */
2029
- const envelopeIsComplete = (envelope) => envelope.status !== 'complete';
2493
+ const getTemplateDocument = async (endpoint, documentId) => endpoint.api //
2494
+ .get(`/v2/template-documents/${documentId}`)
2495
+ .then((r) => r.data);
2030
2496
  /**
2031
- * Check to see if the user owns the envelope.
2497
+ * Download a document directly.
2032
2498
  */
2033
- const userCanCancelEnvelope = (profile, envelope) => userIsEnvelopeOwner(profile, envelope) &&
2034
- envelope.status !== 'complete' &&
2035
- envelope.status !== 'declined' &&
2036
- envelope.status !== 'canceled';
2499
+ const downloadTemplateDocument = async (endpoint, documentId) => endpoint.api //
2500
+ .get(`/v2/template-documents/${documentId}?type=file`, { responseType: 'blob' })
2501
+ .then((r) => r.data);
2037
2502
  /**
2038
- * Check to see if the user owns the envelope.
2503
+ * Get an envelope document's metadata, or the document itself. If no "type" parameter is specified,
2504
+ * the document metadata is returned. If "type" is set to "file", the document binary content is
2505
+ * returned with Content-Type set to the MIME type of the file. If "type" is set to "download", a
2506
+ * string download link will be returned. If "type" is set to "preview" a string preview link will
2507
+ * be returned. This link expires quickly, so it should be accessed immediately and never shared.
2508
+ *
2509
+ * @group Template Documents
2510
+ * @api GET /v2/envelope-documents/:document_id Preview, Download, or Link to a Document
2511
+ * @apiParam string(format: 'uuid') document_id The ID of the document to retrieve.
2512
+ * @apiQuery string(enum:'file'|'download'|'preview') type? Download the file directly, generate a download link, or generate a preview link.
2513
+ * @apiSuccess string . The generated link.
2039
2514
  */
2040
- const userCanFinishEnvelope = (profile, envelope) => userIsEnvelopeOwner(profile, envelope) &&
2041
- envelope.status !== 'complete' &&
2042
- envelope.status !== 'declined' &&
2043
- envelope.status !== 'canceled';
2515
+ const getTemplateDocumentDownloadLink = async (endpoint, _envelopeId, documentId) => endpoint.api //
2516
+ .get(`/v2/template-documents/${documentId}?type=download`)
2517
+ .then((r) => r.data);
2044
2518
  /**
2045
- * Returns true if the recipient has a pending action. Note that this does not necessarily mean the recipient can act (yet).
2519
+ * Get a pre-signed preview link for an Envelope Document. This link expires quickly, so it should
2520
+ * be accessed immediately and never shared. Content-Disposition will be set to "inline".
2046
2521
  */
2047
- const recipientHasAction = (recipient) => !['submitted', 'canceled', 'declined'].includes(recipient.status);
2522
+ const getTemplateDocumentPreviewLink = async (endpoint, _envelopeId, documentId) => endpoint.api //
2523
+ .get(`/v2/envelope-documents/${documentId}?type=preview`)
2524
+ .then((r) => r.data);
2048
2525
  /**
2049
- * Returns the recipients who still have a pending action. Note that not all of these recipients may be able to act (yet).
2526
+ * Get (binary download) a file attached to a Template. It is important to use this method
2527
+ * rather than a direct A HREF or similar link to set the authorization headers for the
2528
+ * request.
2050
2529
  */
2051
- const getRecipientsWithActions = (envelope) => ['complete', 'declined', 'canceled'].includes(envelope.status) ? [] : (envelope?.recipients || []).filter(recipientHasAction);
2530
+ const getTemplateDocumentFile = async (endpoint, templateId, documentId) => endpoint.api //
2531
+ .get(`/v2/templates/${templateId}/documents/${documentId}?file=true`, { responseType: 'blob' })
2532
+ .then((r) => r.data);
2052
2533
  /**
2053
- * Returns true if the recipient can act.
2534
+ * Get (binary download) a file attached to a Template. It is important to use this method
2535
+ * rather than a direct A HREF or similar link to set the authorization headers for the
2536
+ * request.
2054
2537
  */
2055
- const recipientCanAct = (recipient, recipientsWithActions) => recipient.sequence === recipientsWithActions?.[0]?.sequence;
2538
+ const getTemplateDocumentThumbnail = async (endpoint, templateId, documentId) => endpoint.api //
2539
+ .get(`/v2/templates/${templateId}/documents/${documentId}?thumbnail=true`, { responseType: 'blob' })
2540
+ .then((r) => r.data);
2056
2541
  /**
2057
- * Returns true if the user can act.
2542
+ * Get a display URI for a given page in a file attached to a template document. These pages are rendered server-side
2543
+ * into PNG resources suitable for display in IMG tags although they may be used elsewhere. Note that these are intended
2544
+ * for DISPLAY ONLY, are not legally binding documents, and do not contain any encoded metadata from participants. The
2545
+ * original asset may be obtained by calling `getTemplateDocumentFile()` or similar.
2058
2546
  */
2059
- const userCanAct = (email, recipientsWithActions) => {
2060
- const recipient = recipientsWithActions.find((r) => r.email === email);
2061
- return recipient && recipient.sequence === recipientsWithActions?.[0]?.sequence;
2547
+ const getTemplateDocumentPageDisplayUri = async (endpoint, documentId, page, variant = 'original') => endpoint.api.get(`/v2/template-documents/page-image/${documentId}/${variant}/${page}`, { timeout: 20000 }).then((r) => r.data);
2548
+
2549
+ const EMAIL_REGEX = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
2550
+ // @see https://www.regextester.com/1978
2551
+ const PHONE_REGEX = /((?:\+|00)[17](?: |\-)?|(?:\+|00)[1-9]\d{0,2}(?: |\-)?|(?:\+|00)1\-\d{3}(?: |\-)?)?(0\d|\([0-9]{3}\)|[1-9]{0,3})(?:((?: |\-)[0-9]{2}){4}|((?:[0-9]{2}){4})|((?: |\-)[0-9]{3}(?: |\-)[0-9]{4})|([0-9]{7}))/;
2552
+ const URL_REGEX = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/;
2553
+ const POSTAL_CODE_REGEX = /^[A-Za-z0-9-\s]{3,10}$/;
2554
+ const NUMBER_REGEX = /^\d+$/;
2555
+ const DATE_REGEX = /^(\d{4}[-\/]\d{2}[-\/]\d{2})|(\d{2}[-\/]\d{2}[-\/]\d{4})$/;
2556
+ const VALIDATORS = {
2557
+ email: { regex: EMAIL_REGEX, label: 'Email Address' },
2558
+ phone: { regex: PHONE_REGEX, label: 'Phone Number' },
2559
+ url: { regex: URL_REGEX, label: 'URL' },
2560
+ postal_code: { regex: POSTAL_CODE_REGEX, label: 'Zip/Postal Code' },
2561
+ number: { regex: NUMBER_REGEX, label: 'Number' },
2562
+ date: { regex: DATE_REGEX, label: 'Date' },
2062
2563
  };
2564
+ const isValidInput = (value, validator) => Object.keys(VALIDATORS).includes(validator) && VALIDATORS[validator].regex.test(value);
2063
2565
  /**
2064
- * Returns true if the user can act.
2566
+ * Get a list of available validators for field inputs. Note that validators always check strings,
2567
+ * because that is all a user can enter in an HTML input field. Numeric-format validators should
2568
+ * perform any necessary conversions internally. Validators never throw - they just return a boolean.
2569
+ * indicating whether the value is valid.
2065
2570
  */
2066
- const userCanSignNow = (profile, envelope) => {
2067
- if (!profile) {
2068
- return false;
2571
+ const getValidators = () => Object.keys(VALIDATORS);
2572
+ const isValidEmail = (email) => !!email && EMAIL_REGEX.test(email);
2573
+ const isValidPhone = (phone) => !!phone && PHONE_REGEX.test(phone);
2574
+ const isValidRoleName = (value, roles) => roles.findIndex((role) => role.name === value) !== -1;
2575
+ const TagRegEx = /^[a-zA-Z0-9-]{0,32}$/;
2576
+ const isValidTag = (value, tags) => TagRegEx.test(value) || tags.findIndex((tag) => tag === value) !== -1;
2577
+
2578
+ const isFieldFilled = (field, allRecipientFields) => {
2579
+ const { value = '' } = field;
2580
+ switch (field.type) {
2581
+ case 'textarea':
2582
+ case 'textbox':
2583
+ switch (field.validator || '') {
2584
+ case 'email':
2585
+ return value && isValidInput(value, 'email');
2586
+ case 'phone':
2587
+ return value && isValidInput(value, 'phone');
2588
+ default:
2589
+ return (value || '').trim() !== '';
2590
+ }
2591
+ case 'signature':
2592
+ return value === 'signed';
2593
+ case 'initial':
2594
+ return value === 'initialed';
2595
+ // Timestamp fields get automatically filled when the envelope is submitted.
2596
+ case 'timestamp':
2597
+ return true;
2598
+ case 'date':
2599
+ return !!value;
2600
+ case 'attachment':
2601
+ return value === 'attached';
2602
+ case 'dropdown':
2603
+ return value !== '';
2604
+ case 'checkbox':
2605
+ return value === 'true';
2606
+ case 'radio':
2607
+ if (!!field.group) {
2608
+ return allRecipientFields.filter((f) => f.group === field.group).some((field) => field.value === 'true');
2609
+ }
2610
+ return field.value === 'true';
2611
+ default:
2612
+ return false;
2069
2613
  }
2070
- const recipientsWithActions = getRecipientsWithActions(envelope);
2071
- const myRecipient = recipientsWithActions.find((r) => r.profile_id === profile?.id || r.email === profile?.email);
2072
- return (myRecipient &&
2073
- envelopeIsActive(envelope) &&
2074
- userIsEnvelopeRecipient(profile, envelope) &&
2075
- recipientCanAct(myRecipient, recipientsWithActions));
2076
2614
  };
2077
- const getNextRecipient = (envelope) => {
2078
- const recipientsWithActions = getRecipientsWithActions(envelope);
2079
- return recipientsWithActions?.[0];
2615
+ // TODO: Only allow !required to bypass validation if the field is empty.
2616
+ const isFieldValid = (field, allRecipientFields) => {
2617
+ return !field.required || isFieldFilled(field, allRecipientFields);
2080
2618
  };
2081
2619
 
2082
2620
  /**
2083
- * Create a signature block. In a typical signing workflow, the user is asked at the beginning of the process to
2084
- * "adopt" a signature block to be used for all signature fields in the document. Thus, this is typically called one
2085
- * time to create and store a signature block. Thereafter, the ID of the signature block may be re-used for each
2086
- * signature field to be "stamped" by the user.
2621
+ * Create an initials block. In a typical signing workflow, the user is asked at the beginning of the process to
2622
+ * "adopt" an initials block to be used for all initials fields in the document. Thus, this is typically called
2623
+ * one time to create and store an initials block. Thereafter, the ID of the initials block may be re-used for each
2624
+ * initials field to be "stamped" by the user.
2087
2625
  *
2088
2626
  * Note: Both "guest" signers and authenticated users can create initials blocks. Guest signers
2089
2627
  * typically only ever have one, tied to that session. But authenticated users can create more than
2090
2628
  * one, and can use them interchangeably.
2091
2629
  *
2092
2630
  * @group Signatures and Initials
2093
- * @api POST /v2/profiles/signatures Create Signature Block
2094
- * @apiBody string signature Blob containing signature image to store.
2095
- * @apiSuccess ISignature . The newly-created signature block.
2631
+ * @api POST /v2/profiles/initials Create Initial Block
2632
+ * @apiBody string initial Blob containing initials image to store.
2633
+ * @apiSuccess IInitial . The newly-created initial block.
2096
2634
  */
2097
- const createSignature = (endpoint, name, signature) => {
2635
+ const createInitials = (endpoint, name, initials) => {
2098
2636
  const data = new FormData();
2099
- data.append('signature', signature, name);
2637
+ data.append('initial', initials, name);
2100
2638
  return endpoint.api //
2101
- .post(`/v2/profiles/signatures`, data)
2639
+ .post(`/v2/profiles/initials`, data)
2102
2640
  .then((r) => r.data);
2103
2641
  };
2104
2642
 
2105
2643
  /**
2106
- * API keys are used to authenticate server-to-server calls. (API keys should **never** be used for client-to-server operations!)
2107
- * To generate a key, either use the Verdocs admin interface and make note of the client_id and client_secret generated, or call
2108
- * createKey as shown below. Then call {@link Users.Auth.authenticateApp} to obtain an access token using the provided ID and
2109
- * secret. Note that server-to-server authentication requests return shorter-lived tokens, so it is important to check the `exp`
2110
- * field and re-authenticate as needed for subsequent calls.
2111
- *
2112
- * API keys may be updated or rotated at any time. Regular rotation is recommended. Rotation will not expire or invalidate
2113
- * existing server-to-server sessions, so it may be done at any time without disrupting your application.
2114
- *
2115
- * @module
2116
- */
2117
- /**
2118
- * Get a list of keys for a given organization. The caller must have admin access to the organization.
2119
- *
2120
- * ```typescript
2121
- * import {getApiKeys} from '@verdocs/js-sdk';
2122
- *
2123
- * const keys = await getApiKeys(ORGID);
2124
- * ```
2125
- *
2126
- * @group API Keys
2127
- * @api GET /v2/api-keys Get API keys
2128
- * @apiSuccess array(items: IApiKey) . A list of the API keys for the caller's organization. Secrets will not be included.
2129
- */
2130
- const getApiKeys = (endpoint) => endpoint.api //
2131
- .get(`/v2/api-keys`)
2132
- .then((r) => r.data);
2133
- /**
2134
- * Create an API key.
2135
- *
2136
- * ```typescript
2137
- * import {createApiKey} from '@verdocs/js-sdk';
2138
- *
2139
- * await createApiKey(ORGID, {name: NEWNAME});
2140
- * ```
2141
- *
2142
- * @group API Keys
2143
- * @api POST /v2/api-keys Create API key
2144
- * @apiBody string name A name used to identify the key in the Verdocs Web App
2145
- * @apiBody string(format:uuid) profile_id The profile ID that calls made using the key will act as
2146
- * @apiBody array(items:string) permission An array of permissions to assign to the new key. Extends (but does not override) the API key's profile permissions.
2147
- * @apiSuccess IApiKey . The newly-created API key, including its secret.
2644
+ * Get the current KBA status. Note that this may only be called by the recipient and requires a
2645
+ * valid signing session to proceed. Although the Recipient object itself contains indications of
2646
+ * whether KBA is required, it will not contain the current status of the process. If
2647
+ * `recipient.auth_methods` is set (not empty), and `recipient.kba_completed` is false, this endpoint
2648
+ * should be called to determine the next KBA step required.
2148
2649
  */
2149
- const createApiKey = (endpoint, params) => endpoint.api //
2150
- .post('/v2/api-keys', params)
2650
+ const getKbaStep = (endpoint, envelope_id, role_name) => endpoint.api //
2651
+ .get(`/v2/kba/${envelope_id}/${encodeURIComponent(role_name)}`)
2151
2652
  .then((r) => r.data);
2152
2653
  /**
2153
- * Rotate the secret for an API key. The caller must have admin access to the organization.
2154
- *
2155
- * ```typescript
2156
- * import {rotateApiKey} from '@verdocs/js-sdk';
2157
- *
2158
- * const {client_secret: newSecret} = await rotateApiKey(ORGID, CLIENTID);
2159
- * ```
2160
- *
2161
- * @group API Keys
2162
- * @api POST /v2/api-keys/:client_id/rotate Rotate API key
2163
- * @apiParam string(format:uuid) client_id The client ID of the key to rotate
2164
- * @apiSuccess IApiKey . The updated API key with its new secret.
2654
+ * Submit a response to a KBA PIN challenge.
2165
2655
  */
2166
- const rotateApiKey = (endpoint, clientId) => endpoint.api //
2167
- .post(`/v2/api-keys/${clientId}/rotate`)
2656
+ const submitKbaPin = (endpoint, envelope_id, role_name, pin) => endpoint.api //
2657
+ .post(`/v2/kba/pin`, { envelope_id, role_name, pin })
2168
2658
  .then((r) => r.data);
2169
2659
  /**
2170
- * Update an API key to change its assigned Profile ID or Name.
2171
- *
2172
- * ```typescript
2173
- * import {updateApiKey} from '@verdocs/js-sdk';
2174
- *
2175
- * await updateApiKey(ORGID, CLIENTID, {name: NEWNAME});
2176
- * ```
2177
- *
2178
- * @group API Keys
2179
- * @api PATCH /v2/api-keys/:client_id Update API key
2180
- * @apiBody string name? New name for the API key
2181
- * @apiBody array(items:string) permission New array of permissions to assign to the new key. Extends (but does not override) the API key's profile permissions.
2182
- * @apiSuccess IApiKey . The updated API key. The secret will not be included.
2660
+ * Submit an identity response to a KBA challenge.
2183
2661
  */
2184
- const updateApiKey = (endpoint, clientId, params) => endpoint.api //
2185
- .patch(`/v2/api-keys/${clientId}`, params)
2662
+ const submitKbaIdentity = (endpoint, envelope_id, role_name, identity) => endpoint.api //
2663
+ .post(`/v2/kba/identity`, { envelope_id, role_name, identity })
2186
2664
  .then((r) => r.data);
2187
2665
  /**
2188
- * Delete an API key.
2189
- *
2190
- * ```typescript
2191
- * import {deleteApiKey} from '@verdocs/js-sdk';
2192
- *
2193
- * await deleteApiKey(ORGID, CLIENTID);
2194
- * ```
2195
- *
2196
- * @group API Keys
2197
- * @api DELETE /v2/api-keys/:client_id Delete API key
2198
- * @apiSuccess string . Success.
2666
+ * Submit an identity response to a KBA challenge. Answers should be submitted in the same order as
2667
+ * the challenges were listed in `IRecipientKbaStepChallenge.questions`.
2199
2668
  */
2200
- const deleteApiKey = (endpoint, clientId) => endpoint.api //
2201
- .delete(`/v2/api-keys/${clientId}`)
2669
+ const submitKbaChallengeResponse = (endpoint, envelope_id, role_name, responses) => endpoint.api //
2670
+ .post(`/v2/kba/response`, { envelope_id, role_name, responses })
2202
2671
  .then((r) => r.data);
2203
2672
 
2204
2673
  /**
2205
- * An Organization Contact (aka Profile) is an individual user with no access to an organization. These entries
2206
- * appear only in contact lists, usually to populate quick-search dropdowns when sending envelopes.
2674
+ * Agree to electronic signing dislosures.
2207
2675
  *
2208
- * @module
2676
+ * @group Recipients
2677
+ * @api POST /envelopes/:envelope_id/recipients/:role_name/agree Agree to e-Signing Disclosures
2678
+ * @apiParam string(format:uuid) envelope_id The envelope to operate on.
2679
+ * @apiParam string role_name The role to operate on.
2680
+ * @apiSuccess IRecipient . The updated Recipient.
2209
2681
  */
2682
+ const envelopeRecipientAgree = (endpoint, envelopeId, roleName, disclosures) => endpoint.api //
2683
+ .post(`/v2/envelopes/${envelopeId}/recipients/${encodeURIComponent(roleName)}/agree`, { disclosures })
2684
+ .then((r) => r.data);
2210
2685
  /**
2211
- * Get a list of the contacts in the caller's organization.
2212
- *
2213
- * ```typescript
2214
- * import {getOrganizationContacts} from '@verdocs/js-sdk';
2215
- *
2216
- * const members = await getOrganizationContacts(VerdocsEndpoint.getDefault()});
2217
- * ```
2686
+ * Decline electronic signing dislosures. Note that if any recipient declines, the entire envelope
2687
+ * becomes non-viable and later recipients may no longer act. The creator will receive a notification
2688
+ * when this occurs.
2218
2689
  *
2219
- * @group Organization Contacts
2220
- * @api GET /v2/organization-contacts Get a list of organization contacts
2221
- * @apiBody string email Email address for the invitee
2222
- * @apiBody string token Invite token for the invitee
2223
- * @apiSuccess string . Success. The invitation will be marked declined and the token will be invalidated.
2690
+ * @group Recipients
2691
+ * @api POST /envelopes/:envelope_id/recipients/:role_name/decline Decline e-Signing Disclosures
2692
+ * @apiParam string(format:uuid) envelope_id The envelope to operate on.
2693
+ * @apiParam string role_name The role to adjust.
2694
+ * @apiSuccess IRecipient . The updated Recipient.
2224
2695
  */
2225
- const getOrganizationContacts = (endpoint) => endpoint.api //
2226
- .get(`/v2/organization-contacts`)
2696
+ const envelopeRecipientDecline = (endpoint, envelopeId, roleName) => endpoint.api //
2697
+ .post(`/v2/envelopes/${envelopeId}/recipients/${encodeURIComponent(roleName)}/decline`)
2227
2698
  .then((r) => r.data);
2228
2699
  /**
2229
- * Delete a contact from the caller's organization. Note that the caller must be an admin or owner.
2230
- *
2231
- * ```typescript
2232
- * import {deleteOrganizationContact} from '@verdocs/js-sdk';
2233
- *
2234
- * await deleteOrganizationContact(VerdocsEndpoint.getDefault(), 'PROFILEID'});
2235
- * ```
2700
+ * Submit an envelope (signing is finished). Note that all fields must be valid/completed for this to succeed.
2236
2701
  *
2237
- * @group Organization Contacts
2238
- * @api POST /v2/organization-invitations/decline GET a list of pending invitations
2239
- * @apiBody string email Email address for the invitee
2240
- * @apiBody string token Invite token for the invitee
2241
- * @apiSuccess string . Success. The invitation will be marked declined and the token will be invalidated.
2702
+ * @group Recipients
2703
+ * @api POST /envelopes/:envelope_id/recipients/:role_name/submit Submit envelope
2704
+ * @apiParam string(format:uuid) envelope_id The envelope to operate on.
2705
+ * @apiParam string role_name The role to submit.
2706
+ * @apiSuccess IRecipient . The updated Recipient.
2242
2707
  */
2243
- const deleteOrganizationContact = (endpoint, profileId) => endpoint.api //
2244
- .delete(`/v2/organization-contacts/${profileId}`)
2708
+ const envelopeRecipientSubmit = (endpoint, envelopeId, roleName) => endpoint.api //
2709
+ .post(`/v2/envelopes/${envelopeId}/recipients/${roleName}/submit`)
2245
2710
  .then((r) => r.data);
2246
2711
  /**
2247
- * Update a member.
2248
- *
2249
- * ```typescript
2250
- * import {createOrganizationContact} from '@verdocs/js-sdk';
2712
+ * Begin a signing session for an Envelope. This path requires an invite code, and should generally
2713
+ * be called with a NON-default Endpoint to avoid conflicting with any active user session the user
2714
+ * may have. To initiate in-person signing by an authenticated user (e.g. self-signing), call
2715
+ * getInPersonLink() instead. The response from that call includes both a link for direct signing
2716
+ * via a Web browser as well as an in-person access_key. That access_key.key may be used here as well.
2251
2717
  *
2252
- * const result = await createOrganizationContact(VerdocsEndpoint.getDefault(), 'PROFILEID', {first_name:'First', last_name:'Last', email:'a@b.com'});
2253
- * ```
2718
+ * @group Recipients
2719
+ * @api POST /v2/sign/unauth/:envelope_id/:role_name/:key Start Signing Session
2720
+ * @apiParam string(format:uuid) envelope_id The envelope to operate on.
2721
+ * @apiParam string role_name The role to request.
2722
+ * @apiParam string key Access key generated by the envelope creator or email/SMS invite.
2723
+ * @apiSuccess ISignerTokenResponse . Signing session token and envelope/recipient metadata.
2254
2724
  */
2255
- const createOrganizationContact = (endpoint, params) => endpoint.api //
2256
- .post(`/v2/organization-contacts`, params)
2257
- .then((r) => r.data);
2725
+ const startSigningSession = async (endpoint, envelope_id, role_name, key) => {
2726
+ return endpoint.api //
2727
+ .post(`/v2/sign/unauth/${envelope_id}/${encodeURIComponent(role_name)}/${key}`)
2728
+ .then((r) => {
2729
+ endpoint.setToken(r.data.access_token, 'signing');
2730
+ return r.data;
2731
+ });
2732
+ };
2258
2733
  /**
2259
- * Update a member.
2260
- *
2261
- * ```typescript
2262
- * import {updateOrganizationContact} from '@verdocs/js-sdk';
2734
+ * Get an in-person signing link. Must be called by the owner/creator of the envelope. The response
2735
+ * also includes the raw access key that may be used to directly initiate a signing session (see
2736
+ * `startSigningSession`) as well as an access token representing a valid signing session for
2737
+ * immediate use in embeds or other applications. Note that in-person signing is considered a
2738
+ * lower-security operation than authenticated signing, and the final envelope certificate will
2739
+ * reflect this.
2263
2740
  *
2264
- * const result = await updateOrganizationContact(VerdocsEndpoint.getDefault(), 'PROFILEID', {first_name:'NewFirst'});
2265
- * ```
2741
+ * @group Recipients
2742
+ * @api POST /v2/sign/in-person/:envelope_id/:role_name Get In-Person Signing Link
2743
+ * @apiParam string(format:uuid) envelope_id The envelope to operate on.
2744
+ * @apiParam string role_name The role to request.
2745
+ * @apiSuccess IInPersonLinkResponse . Signing session token and envelope/recipient metadata.
2266
2746
  */
2267
- const updateOrganizationContact = (endpoint, profileId, params) => endpoint.api //
2268
- .patch(`/v2/organization-contacts/${profileId}`, params)
2747
+ const getInPersonLink = (endpoint, envelope_id, role_name) => endpoint.api //
2748
+ .post(`/v2/sign/in-person/${envelope_id}/${encodeURIComponent(role_name)}`)
2269
2749
  .then((r) => r.data);
2270
-
2271
2750
  /**
2272
- * Organizations may contain "Groups" of user profiles, called Members. Groups may have permissions assigned that
2273
- * apply to all Members, making it easy to configure role-based access control (RBAC) within an Organization. Note
2274
- * that permissions are **additive**. A user may be a member of more than one group, and may also have permissions
2275
- * assigned directly. In that case, the user will have the combined set of all permissions inherited from all
2276
- * sources.
2751
+ * Verify a recipient within a signing session. All signing sessions use an invite code at a minimum,
2752
+ * but many scenarios require more robust verification of recipients, so one or more verification
2753
+ * methods may be attached to each recipient. If an authentication method is enabled, the
2754
+ * signer must first accept the e-signature disclosures, then complete each verification step
2755
+ * before attempting to view/display documents, complete any fields, or submit the envelope.
2756
+ * This endpoint should be called to complete each step. If the call fails an error will be
2757
+ * thrown.
2277
2758
  *
2278
- * @module
2759
+ * @group Recipients
2760
+ * @api POST /v2/sign/verify Verify recipient/signer
2761
+ * @apiParam string(enum:'passcode'|'email'|'sms'|'kba'|'id') auth_method The authentication method being completed
2762
+ * @apiParam string code? The passcode or OTP entered. Required for passcode, email, and SMS methods.
2763
+ * @apiParam boolean resend? For SMS or email methods, set to send a new code.
2764
+ * @apiParam boolean first_name? For KBA, the recipient's first name
2765
+ * @apiParam boolean last_name? For KBA, the recipient's last name
2766
+ * @apiParam boolean address? For KBA, the recipient's address
2767
+ * @apiParam boolean city? For KBA, the recipient's city
2768
+ * @apiParam boolean state? For KBA, the recipient's state
2769
+ * @apiParam boolean zip? For KBA, the recipient's zip code
2770
+ * @apiParam boolean ssn_last_4? For KBA, the last 4 digits of the recipient's SSN
2771
+ * @apiParam boolean dob? For KBA, the recipient's date of birth
2772
+ * @apiParam array(items:IKBAResponse) responses? For KBA, responses to any challenge questions presented
2773
+ * @apiSuccess ISignerTokenResponse . Updated signing session.
2279
2774
  */
2775
+ const verifySigner = (endpoint, params) => endpoint.api //
2776
+ .post(`/v2/sign/verify`, params)
2777
+ .then((r) => r.data);
2280
2778
  /**
2281
- * Get a list of groups for the caller's organization. NOTE: Any organization member may request
2282
- * the list of groups, but only Owners and Admins may update them.
2283
- *
2284
- * ```typescript
2285
- * import {getGroups} from '@verdocs/js-sdk';
2779
+ * Delegate a recipient's signing responsibility. The envelope sender must enable this before the
2780
+ * recipient calls this endpoint, and only the recipient may call it, or the call will be rejected.
2781
+ * The recipient's role will be renamed and configured to indicate to whom the delegation was made,
2782
+ * and a new recipient entry with the updated details (e.g. name and email address) will be added
2783
+ * to the flow with the same role_name, order, and sequence of the original recipient. Unless
2784
+ * no_contact is set on the envelope, the delegation recipient and envelope creator will also be
2785
+ * notified.
2286
2786
  *
2287
- * const groups = await getGroups();
2288
- * ```
2787
+ * @group Recipients
2788
+ * @api PUT /v2/envelopes/:envelope_id/recipients/:role_name Delegate Recipient
2789
+ * @apiParam string(format:uuid) envelope_id The envelope to operate on.
2790
+ * @apiParam string role_name The role to operate on.
2791
+ * @apiBody string(enum:'delegate') action The operation to perform (delegate).
2792
+ * @apiBody string first_name The first name of the new recipient.
2793
+ * @apiBody string last_name The last name of the new recipient.
2794
+ * @apiBody string email The email address of the new recipient.
2795
+ * @apiBody string phone? Optional phone number for the new recipient.
2796
+ * @apiBody string message? Optional phone number for the new recipient's invitation.
2797
+ * @apiSuccess string . Success message.
2289
2798
  */
2290
- const getGroups = (endpoint) => endpoint.api //
2291
- .get(`/v2/organization-groups`)
2799
+ const delegateRecipient = (endpoint, envelopeId, roleName, params) => endpoint.api //
2800
+ .put(`/v2/envelopes/${envelopeId}/recipients/${encodeURIComponent(roleName)}`, { action: 'delegate', ...params })
2292
2801
  .then((r) => r.data);
2293
2802
  /**
2294
- * Get the details for a group, including its member profiles and list of permissions.
2295
- *
2296
- * ```typescript
2297
- * import {getGroup} from '@verdocs/js-sdk/v2/organization-groups';
2803
+ * Update a recipient. NOTE: User interfaces should rate-limit this operation to avoid spamming recipients.
2804
+ * Excessive use of this endpoint may result in Verdocs rate-limiting the calling application to prevent
2805
+ * abuse. This endpoint will return a 200 OK even if the no_contact flag is set on the envelope (in which
2806
+ * case the call will be silently ignored).
2298
2807
  *
2299
- * const group = await getGroup(GROUPID);
2300
- * ```
2808
+ * @group Recipients
2809
+ * @api PATCH /envelopes/:envelope_id/recipients/:role_name Update Recipient
2810
+ * @apiParam string(format:uuid) envelope_id The envelope to operate on.
2811
+ * @apiParam string role_name The role name to update.
2812
+ * @apiBody string(enum:'remind'|'reset') action? Trigger a reminder, or fully reset the recipient
2813
+ * @apiBody string first_name? Update the recipient's first name.
2814
+ * @apiBody string last_name? Update the recipient's last name.
2815
+ * @apiBody string email? Update the recipient's email address.
2816
+ * @apiBody string message? Update the recipient's invite message.
2817
+ * @apiBody string phone? Update the recipient's phone number.
2818
+ * @apiBody string passcode? If passcode authentication is used, the recipient's address to prefill. May only be changed if the recipient has not already completed passcode-based auth.
2819
+ * @apiBody string address? If KBA-based authentication is used, the recipient's address to prefill. May only be changed if the recipient has not already completed KBA-based auth.
2820
+ * @apiBody string city? If KBA-based authentication is used, the recipient's city to prefill. May only be changed if the recipient has not already completed KBA-based auth.
2821
+ * @apiBody string state? If KBA-based authentication is used, the recipient's state to prefill. May only be changed if the recipient has not already completed KBA-based auth.
2822
+ * @apiBody string zip? If KBA-based authentication is used, the recipient's zip code to prefill. May only be changed if the recipient has not already completed KBA-based auth.
2823
+ * @apiBody string dob? If KBA-based authentication is used, the recipient's date of birth to prefill. May only be changed if the recipient has not already completed KBA-based auth.
2824
+ * @apiBody string ssn_last_4? If KBA-based authentication is used, the recipient's SSN-last-4 to prefill. May only be changed if the recipient has not already completed KBA-based auth.
2825
+ * @apiSuccess IRecipient . The updated Recipient.
2301
2826
  */
2302
- const getGroup = (endpoint, groupId) => endpoint.api //
2303
- .get(`/v2/organization-groups/${groupId}`)
2827
+ const updateRecipient = (endpoint, envelopeId, roleName, params) => endpoint.api //
2828
+ .patch(`/v2/envelopes/${envelopeId}/recipients/${encodeURIComponent(roleName)}`, params)
2304
2829
  .then((r) => r.data);
2305
2830
  /**
2306
- * Create a group. Note that "everyone" is a reserved name and may not be created.
2307
- *
2308
- * ```typescript
2309
- * import {createGroup} from '@verdocs/js-sdk';
2310
- *
2311
- * const group = await createGroup(VerdocsEndpoint.getDefault(), {name:'newgroup'});
2312
- * ```
2831
+ * Send a reminder to a recipient. The recipient must still be an active member of the signing flow
2832
+ * (e.g. not declined, already submitted, etc.)
2313
2833
  */
2314
- const createGroup = (endpoint, params) => endpoint.api //
2315
- .post('/v2/organization-groups', params)
2834
+ const remindRecipient = (endpoint, envelopeId, roleName) => endpoint.api //
2835
+ .patch(`/v2/envelopes/${envelopeId}/recipients/${encodeURIComponent(roleName)}`, { action: 'remind' })
2316
2836
  .then((r) => r.data);
2317
2837
  /**
2318
- * Update a group. Note that "everyone" is a reserved name and may not be changed.
2319
- *
2320
- * ```typescript
2321
- * import {updateGroup} from '@verdocs/js-sdk';
2322
- *
2323
- * const updated = await updateGroup(VerdocsEndpoint.getDefault(), {name:'newname'});
2324
- * ```
2838
+ * Fully reset a recipient. This allows the recipient to restart failed KBA flows, change
2839
+ * fields they may have filled in incorrectly while signing, etc. This cannot be used on a
2840
+ * canceled or completed envelope, but may be used to restart an envelope marked declined.
2325
2841
  */
2326
- const updateGroup = (endpoint, groupId, params) => endpoint.api //
2327
- .patch(`/v2/organization-groups/${groupId}`, params)
2842
+ const resetRecipient = (endpoint, envelopeId, roleName) => endpoint.api //
2843
+ .patch(`/v2/envelopes/${envelopeId}/recipients/${encodeURIComponent(roleName)}`, { action: 'reset' })
2328
2844
  .then((r) => r.data);
2845
+
2329
2846
  /**
2330
- * Get an organization by ID. Note that the "everyone" group cannot be deleted.
2331
- *
2332
- * ```typescript
2333
- * import {deleteGroup} from '@verdocs/js-sdk';
2847
+ * Various helpers to identify available operations for an envelope by a user.
2334
2848
  *
2335
- * await deleteGroup(VerdocsEndpoint.getDefault(), 'ORGID');
2336
- * ```
2849
+ * @module
2337
2850
  */
2338
- const deleteGroup = (endpoint, groupId) => endpoint.api //
2339
- .delete(`/v2/organization-groups/${groupId}`)
2340
- .then((r) => r.data);
2341
2851
  /**
2342
- * Add a member to a group.
2343
- *
2344
- * ```typescript
2345
- * import {addGroupMember} from '@verdocs/js-sdk';
2346
- *
2347
- * await addGroupMember(VerdocsEndpoint.getDefault(), 'GROUPID', 'PROFILEID');
2348
- * ```
2852
+ * Check to see if the profile ID owns the envelope.
2349
2853
  */
2350
- const addGroupMember = (endpoint, groupId, profile_id) => endpoint.api //
2351
- .post(`/v2/organization-groups/${groupId}/members`, { profile_id })
2352
- .then((r) => r.data);
2854
+ const isEnvelopeOwner = (profile_id, envelope) => envelope.profile_id === profile_id;
2353
2855
  /**
2354
- * Remove a member from a group.
2355
- *
2356
- * ```typescript
2357
- * import {deleteGroupMember} from '@verdocs/js-sdk';
2358
- *
2359
- * await deleteGroupMember(VerdocsEndpoint.getDefault(), 'GROUPID', 'PROFILEID');
2360
- * ```
2856
+ * Check to see if the profile ID is a recipient within the envelope.
2361
2857
  */
2362
- const deleteGroupMember = (endpoint, groupId, profile_id) => endpoint.api //
2363
- .delete(`/v2/organization-groups/${groupId}/members/${profile_id}`)
2364
- .then((r) => r.data);
2365
-
2858
+ const isEnvelopeRecipient = (profile_id, envelope) => (envelope.recipients || []).some((recipient) => recipient.profile_id === profile_id);
2366
2859
  /**
2367
- * An invitation represents an opportunity for a Member to join an Organization.
2368
- *
2369
- * @module
2860
+ * Check to see if the profile ID is the envelope's sender or one of the recipients.
2370
2861
  */
2862
+ const canAccessEnvelope = (profile_id, envelope) => isEnvelopeOwner(profile_id, envelope) || isEnvelopeRecipient(profile_id, envelope);
2371
2863
  /**
2372
- * Get a list of invitations pending for the caller's organization. The caller must be an admin or owner.
2373
- *
2374
- * @group Organization Invitations
2375
- * @api GET /v2/organization-invitations Get a list of pending invitations
2376
- * @apiBody array(items:TRole) roles URL to send Webhook events to. An empty or invalid URL will disable Webhook calls.
2377
- * @apiBody string first_name First name. The user may override this after accepting the invitation.
2378
- * @apiBody string last_name Last name. The user may override this after accepting the invitation.
2379
- * @apiSuccess array(items:IProfile) . List of caller's current organization's members
2864
+ * Check to see if the user owns the envelope.
2380
2865
  */
2381
- const getOrganizationInvitations = (endpoint) => endpoint.api //
2382
- .get(`/v2/organization-invitations`)
2383
- .then((r) => r.data);
2866
+ const userIsEnvelopeOwner = (profile, envelope) => envelope.profile_id === profile?.id;
2384
2867
  /**
2385
- * Invite a new user to join the organization.
2386
- *
2387
- * @group Organization Invitations
2388
- * @api POST /v2/organization-invitations Invite a new user to join the organization
2389
- * @apiBody string email Email address to send the invitation to
2390
- * @apiBody string first_name First name. The user may override this after accepting the invitation.
2391
- * @apiBody string last_name Last name. The user may override this after accepting the invitation.
2392
- * @apiBody TRole role Initial role to assign to the user once they accept.
2393
- * @apiSuccess IOrganizationInvitation . The newly-created invitation.
2868
+ * Check to see if the user is a recipient within the envelope.
2394
2869
  */
2395
- const createOrganizationInvitation = (endpoint, params) => endpoint.api //
2396
- .post(`/v2/organization-invitations`, params)
2397
- .then((r) => r.data);
2870
+ const userIsEnvelopeRecipient = (profile, envelope) => (envelope.recipients || []).some((recipient) => recipient.profile_id === profile?.id);
2398
2871
  /**
2399
- * Delete an invitation. Note that no cancellation message will be sent. Invitations are also one-time-use.
2400
- * If the invitee attempts to join after the invitation is deleted, accepted, or decline, they will be
2401
- * shown an error.
2402
- *
2403
- * @group Organization Invitations
2404
- * @api DELETE /v2/organization-invitations/:email Delete a pending invitation
2405
- * @apiSuccess string . Success
2872
+ * Check to see if the profile ID is the envelope's sender or one of the recipients.
2406
2873
  */
2407
- const deleteOrganizationInvitation = (endpoint, email) => endpoint.api //
2408
- .delete(`/v2/organization-invitations/${email}`)
2409
- .then((r) => r.data);
2874
+ const useCanAccessEnvelope = (profile, envelope) => userIsEnvelopeOwner(profile, envelope) || userIsEnvelopeRecipient(profile, envelope);
2410
2875
  /**
2411
- * Update an invitation. Note that email may not be changed after the invite is sent. To change
2412
- * an invitee's email, delete the incorrect entry and create one with the correct value.
2413
- *
2414
- * @group Organization Invitations
2415
- * @api PATCH /v2/organization-invitations/:email Update a pending invitation
2416
- * @apiBody string first_name First name. The user may override this after accepting the invitation.
2417
- * @apiBody string last_name Last name. The user may override this after accepting the invitation.
2418
- * @apiBody TRole role Initial role to assign to the user once they accept.
2419
- * @apiSuccess IOrganizationInvitation . The updated invitation.
2876
+ * Check to see if the envelope has pending actions.
2420
2877
  */
2421
- const updateOrganizationInvitation = (endpoint, email, params) => endpoint.api //
2422
- .patch(`/v2/organization-invitations/${email}`, params)
2423
- .then((r) => r.data);
2878
+ const envelopeIsActive = (envelope) => envelope.status !== 'complete' && envelope.status !== 'declined' && envelope.status !== 'canceled';
2424
2879
  /**
2425
- * Send a reminder to the invitee to join the organization.
2426
- *
2427
- * @group Organization Invitations
2428
- * @api POST /v2/organization-invitations/resend Send a reminder to a pending invitee
2429
- * @apiBody string email The recipient to send the reminder to
2430
- * @apiSuccess IOrganizationInvitation . The updated invitation
2880
+ * Check to see if the envelope has been completed.
2431
2881
  */
2432
- const resendOrganizationInvitation = (endpoint, email) => endpoint.api //
2433
- .post('/v2/organization-invitations/resend', { email })
2434
- .then((r) => r.data);
2882
+ const envelopeIsComplete = (envelope) => envelope.status !== 'complete';
2435
2883
  /**
2436
- * Get an invitation's details. This is generally used as the first step of accepting the invite.
2437
- * A successful response will indicate that the invite token is still valid, and include some
2438
- * metadata for the organization to style the acceptance screen.
2439
- *
2440
- * @group Organization Invitations
2441
- * @api GET /v2/organization-invitations/:email/:token Get a pending invitation (_Authenticated via invite token, not an active session._). Intended to be called by the invitee to get details about the invitation they are about to accept.
2442
- * @apiSuccess IOrganizationInvitation . Requested invitation's details. Will always include summary details for the organization, to be used for branding the accept-invite view.
2884
+ * Check to see if the user owns the envelope.
2443
2885
  */
2444
- const getOrganizationInvitation = (endpoint, email, token) => endpoint.api //
2445
- .get(`/v2/organization-invitations/${email}/${token}`)
2446
- .then((r) => r.data);
2886
+ const userCanCancelEnvelope = (profile, envelope) => userIsEnvelopeOwner(profile, envelope) &&
2887
+ envelope.status !== 'complete' &&
2888
+ envelope.status !== 'declined' &&
2889
+ envelope.status !== 'canceled';
2447
2890
  /**
2448
- * Accept an invitation. This will automatically create a user record for the caller as well as a profile
2449
- * with the appropriate role as specified in the invite. The profile will be set as "current" for the caller,
2450
- * and session tokens will be returned to access the new profile. The profile's email_verified flag will
2451
- * also be set to true.
2452
- *
2453
- * @group Organization Invitations
2454
- * @api POST /v2/organization-invitations/accept Accept an invitation
2455
- * @apiBody string email Email address for the invitee
2456
- * @apiBody string token Invite token for the invitee
2457
- * @apiBody string first_name First name
2458
- * @apiBody string last_name Last name
2459
- * @apiBody string password Password
2460
- * @apiSuccess IAuthenticateResponse . Session credentials for the newly-created user's profile. If the user already had a profile for another organization, the new profile will be made "current" automatically.
2891
+ * Check to see if the user owns the envelope.
2461
2892
  */
2462
- const acceptOrganizationInvitation = (endpoint, params) => endpoint.api //
2463
- .post('/v2/organization-invitations/accept', params)
2464
- .then((r) => r.data);
2893
+ const userCanFinishEnvelope = (profile, envelope) => userIsEnvelopeOwner(profile, envelope) &&
2894
+ envelope.status !== 'complete' &&
2895
+ envelope.status !== 'declined' &&
2896
+ envelope.status !== 'canceled';
2465
2897
  /**
2466
- * Decline an invitation. This will mark the status "declined," providing a visual indication to the
2467
- * organization's admins that the invite was declined, preventing further invites from being created
2468
- * to the same email address, and also preventing the invitee from receiving reminders to join.
2469
- *
2470
- * @group Organization Invitations
2471
- * @api POST /v2/organization-invitations/decline Decline an invitation
2472
- * @apiDescription Mark the status "declined," providing a visual indication to the organization's admins that the invite was declined, preventing further invites from being created to the same email address, and also preventing the invitee from receiving reminders to join.
2473
- * @apiBody string email Email address for the invitee
2474
- * @apiBody string token Invite token for the invitee
2475
- * @apiSuccess string . Success. The invitation will be marked declined and the token will be invalidated.
2898
+ * Returns true if the recipient has a pending action. Note that this does not necessarily mean the recipient can act (yet).
2476
2899
  */
2477
- const declineOrganizationInvitation = (endpoint, email, token) => endpoint.api //
2478
- .post('/v2/organization-invitations/decline', { email, token })
2479
- .then((r) => r.data);
2480
-
2900
+ const recipientHasAction = (recipient) => !['submitted', 'canceled', 'declined'].includes(recipient.status);
2481
2901
  /**
2482
- * An Organization Member (aka Profile) is an individual user with access to an organization.
2483
- *
2484
- * @module
2902
+ * Returns the recipients who still have a pending action. Note that not all of these recipients may be able to act (yet).
2485
2903
  */
2904
+ const getRecipientsWithActions = (envelope) => ['complete', 'declined', 'canceled'].includes(envelope.status) ? [] : (envelope?.recipients || []).filter(recipientHasAction);
2486
2905
  /**
2487
- * Get a list of the members in the caller's organization.
2488
- *
2489
- * ```typescript
2490
- * import {getOrganizationMembers} from '@verdocs/js-sdk';
2491
- *
2492
- * const members = await getOrganizationMembers(VerdocsEndpoint.getDefault()});
2493
- * ```
2494
- *
2495
- * @group Organization Members
2496
- * @api GET /v2/organization-members List current organization's members
2497
- * @apiSuccess array(items:IProfile) . List of caller's current organization's members
2906
+ * Returns true if the recipient can act.
2498
2907
  */
2499
- const getOrganizationMembers = (endpoint) => endpoint.api //
2500
- .get(`/v2/organization-members`)
2501
- .then((r) => r.data);
2908
+ const recipientCanAct = (recipient, recipientsWithActions) => recipient.sequence === recipientsWithActions?.[0]?.sequence;
2502
2909
  /**
2503
- * Delete a member from the caller's organization. Note that the caller must be an admin or owner,
2504
- * may not delete him/herself.
2505
- *
2506
- * ```typescript
2507
- * import {deleteOrganizationMember} from '@verdocs/js-sdk';
2508
- *
2509
- * await deleteOrganizationMember(VerdocsEndpoint.getDefault(), 'PROFILEID'});
2510
- * ```
2511
- *
2512
- * @group Organization Members
2513
- * @api DELETE /v2/organization-members/:profile_id Delete a member from the organization
2514
- * @apiSuccess string . Success
2910
+ * Returns true if the user can act.
2515
2911
  */
2516
- const deleteOrganizationMember = (endpoint, profileId) => endpoint.api //
2517
- .delete(`/v2/organization-members/${profileId}`)
2518
- .then((r) => r.data);
2912
+ const userCanAct = (email, recipientsWithActions) => {
2913
+ const recipient = recipientsWithActions.find((r) => r.email === email);
2914
+ return recipient && recipient.sequence === recipientsWithActions?.[0]?.sequence;
2915
+ };
2519
2916
  /**
2520
- * Update a member.
2521
- *
2522
- * ```typescript
2523
- * import {updateOrganizationMember} from '@verdocs/js-sdk';
2917
+ * Returns true if the user can act.
2918
+ */
2919
+ const userCanSignNow = (profile, envelope) => {
2920
+ if (!profile) {
2921
+ return false;
2922
+ }
2923
+ const recipientsWithActions = getRecipientsWithActions(envelope);
2924
+ const myRecipient = recipientsWithActions.find((r) => r.profile_id === profile?.id || r.email === profile?.email);
2925
+ return (myRecipient &&
2926
+ envelopeIsActive(envelope) &&
2927
+ userIsEnvelopeRecipient(profile, envelope) &&
2928
+ recipientCanAct(myRecipient, recipientsWithActions));
2929
+ };
2930
+ const getNextRecipient = (envelope) => {
2931
+ const recipientsWithActions = getRecipientsWithActions(envelope);
2932
+ return recipientsWithActions?.[0];
2933
+ };
2934
+
2935
+ /**
2936
+ * Create a signature block. In a typical signing workflow, the user is asked at the beginning of the process to
2937
+ * "adopt" a signature block to be used for all signature fields in the document. Thus, this is typically called one
2938
+ * time to create and store a signature block. Thereafter, the ID of the signature block may be re-used for each
2939
+ * signature field to be "stamped" by the user.
2524
2940
  *
2525
- * const result = await updateOrganizationMember(VerdocsEndpoint.getDefault(), 'PROFILEID', {roles:['member']});
2526
- * ```
2941
+ * Note: Both "guest" signers and authenticated users can create initials blocks. Guest signers
2942
+ * typically only ever have one, tied to that session. But authenticated users can create more than
2943
+ * one, and can use them interchangeably.
2527
2944
  *
2528
- * @group Organization Members
2529
- * @api PATCH /v2/organization-members/:profile_id Update an organization member.
2530
- * @apiBody array(items:TRole) roles URL to send Webhook events to. An empty or invalid URL will disable Webhook calls.
2531
- * @apiBody string first_name Set to true to enable Webhooks calls.
2532
- * @apiBody string last_name Record<TWebhookEvent, boolean> map of events to enable/disable.
2533
- * @apiSuccess array(items:IProfile) . List of caller's current organization's members
2945
+ * @group Signatures and Initials
2946
+ * @api POST /v2/profiles/signatures Create Signature Block
2947
+ * @apiBody string signature Blob containing signature image to store.
2948
+ * @apiSuccess ISignature . The newly-created signature block.
2534
2949
  */
2535
- const updateOrganizationMember = (endpoint, profileId, params) => endpoint.api //
2536
- .patch(`/v2/organization-members/${profileId}`, params)
2537
- .then((r) => r.data);
2950
+ const createSignature = (endpoint, name, signature) => {
2951
+ const data = new FormData();
2952
+ data.append('signature', signature, name);
2953
+ return endpoint.api //
2954
+ .post(`/v2/profiles/signatures`, data)
2955
+ .then((r) => r.data);
2956
+ };
2538
2957
 
2539
2958
  /**
2540
- * An Organization is the top level object for ownership for Members, Documents, and Templates.
2541
- *
2542
- * NOTE: There is no call specifically to create an organization. Every organization must have
2543
- * at least one "owner" type member. To create a new organization, call createProfile() with
2544
- * the desired new orgName to create. The caller will become the first owner of the new org, and
2545
- * can then invite new members to join as well.
2959
+ * These disclosures will be used if no overrides are supplied by the caller. Overrides must
2960
+ * be applied at the Organization level before creating an envelope.
2961
+ */
2962
+ const DEFAULT_DISCLOSURES = `
2963
+ <ul>
2964
+ <li>
2965
+ Agree to use electronic records and signatures, and confirm you have read the
2966
+ <a href="https://verdocs.com/en/electronic-record-signature-disclosure/" target="_blank">
2967
+ Electronic Record and Signatures Disclosure</a>.</li>
2968
+ <li>
2969
+ Agree to Verdocs'
2970
+ <a href="https://verdocs.com/en/eula" target="_blank">
2971
+ End User License Agreement</a>
2972
+ and confirm you have read Verdocs'
2973
+ <a href="https://verdocs.com/en/privacy-policy/" target="_blank">
2974
+ Privacy Policy</a>.
2975
+ </li>
2976
+ </ul>`;
2977
+
2978
+ /**
2979
+ * API keys are used to authenticate server-to-server calls. (API keys should **never** be used for client-to-server operations!)
2980
+ * To generate a key, either use the Verdocs admin interface and make note of the client_id and client_secret generated, or call
2981
+ * createKey as shown below. Then call {@link Users.Auth.authenticateApp} to obtain an access token using the provided ID and
2982
+ * secret. Note that server-to-server authentication requests return shorter-lived tokens, so it is important to check the `exp`
2983
+ * field and re-authenticate as needed for subsequent calls.
2546
2984
  *
2547
- * NOTE: There is no call to delete an organization. For safety, this is a manual process. Please
2548
- * contact support@verdocs.com if you wish to completely delete an organization and all its records.
2985
+ * API keys may be updated or rotated at any time. Regular rotation is recommended. Rotation will not expire or invalidate
2986
+ * existing server-to-server sessions, so it may be done at any time without disrupting your application.
2549
2987
  *
2550
2988
  * @module
2551
2989
  */
2552
2990
  /**
2553
- * Get an organization by ID. Note that this endpoint will return only a subset of fields
2554
- * if the caller is not a member of the organization (the public fields).
2991
+ * Get a list of keys for a given organization. The caller must have admin access to the organization.
2555
2992
  *
2556
2993
  * ```typescript
2557
- * import {getOrganization} from '@verdocs/js-sdk';
2994
+ * import {getApiKeys} from '@verdocs/js-sdk';
2558
2995
  *
2559
- * const organizations = await getOrganization(VerdocsEndpoint.getDefault(), 'ORGID');
2996
+ * const keys = await getApiKeys(ORGID);
2560
2997
  * ```
2561
2998
  *
2562
- * @group Organizations
2563
- * @api GET /v2/organizations/:organization_id Get organization
2564
- * @apiSuccess IOrganization . The requested organization. The caller must be a member.
2999
+ * @group API Keys
3000
+ * @api GET /v2/api-keys Get API keys
3001
+ * @apiSuccess array(items: IApiKey) . A list of the API keys for the caller's organization. Secrets will not be included.
2565
3002
  */
2566
- const getOrganization = (endpoint, organizationId) => endpoint.api //
2567
- .get(`/v2/organizations/${organizationId}`)
3003
+ const getApiKeys = (endpoint) => endpoint.api //
3004
+ .get(`/v2/api-keys`)
2568
3005
  .then((r) => r.data);
2569
3006
  /**
2570
- * Get an organization's "children".
3007
+ * Create an API key.
2571
3008
  *
2572
3009
  * ```typescript
2573
- * import {getOrganizationChildren} from '@verdocs/js-sdk';
3010
+ * import {createApiKey} from '@verdocs/js-sdk';
2574
3011
  *
2575
- * const children = await getOrganizationChildren(VerdocsEndpoint.getDefault(), 'ORGID');
3012
+ * await createApiKey(ORGID, {name: NEWNAME});
2576
3013
  * ```
2577
3014
  *
2578
- * @group Organizations
2579
- * @api GET /v2/organizations/:organization_id/children Get an organization's children
2580
- * @apiSuccess IOrganization[] . Any child organizations found.
3015
+ * @group API Keys
3016
+ * @api POST /v2/api-keys Create API key
3017
+ * @apiBody string name A name used to identify the key in the Verdocs Web App
3018
+ * @apiBody string(format:uuid) profile_id The profile ID that calls made using the key will act as
3019
+ * @apiBody array(items:string) permission An array of permissions to assign to the new key. Extends (but does not override) the API key's profile permissions.
3020
+ * @apiSuccess IApiKey . The newly-created API key, including its secret.
2581
3021
  */
2582
- const getOrganizationChildren = (endpoint, organizationId) => endpoint.api //
2583
- .get(`/v2/organizations/${organizationId}/children`)
3022
+ const createApiKey = (endpoint, params) => endpoint.api //
3023
+ .post('/v2/api-keys', params)
2584
3024
  .then((r) => r.data);
2585
3025
  /**
2586
- * Get an organization's usage data. If the organization is a parent, usage data for children
2587
- * will be included as well. The response will be a nested object keyed by organization ID,
2588
- * with each entry being a dictionary of usageType:count entries.
3026
+ * Rotate the secret for an API key. The caller must have admin access to the organization.
2589
3027
  *
2590
3028
  * ```typescript
2591
- * import {getOrganizationUsage} from '@verdocs/js-sdk';
3029
+ * import {rotateApiKey} from '@verdocs/js-sdk';
2592
3030
  *
2593
- * const usage = await getOrganizationUsage(VerdocsEndpoint.getDefault(), 'ORGID');
3031
+ * const {client_secret: newSecret} = await rotateApiKey(ORGID, CLIENTID);
2594
3032
  * ```
2595
3033
  *
2596
- * @group Organizations
2597
- * @api GET /v2/organizations/:organization_id/usage Get an organization's usage metrics
2598
- * @apiSuccess TOrganizationUsage . Usage data grouped by organization ID
3034
+ * @group API Keys
3035
+ * @api POST /v2/api-keys/:client_id/rotate Rotate API key
3036
+ * @apiParam string(format:uuid) client_id The client ID of the key to rotate
3037
+ * @apiSuccess IApiKey . The updated API key with its new secret.
2599
3038
  */
2600
- const getOrganizationUsage = (endpoint, organizationId, params) => endpoint.api //
2601
- .get(`/v2/organizations/${organizationId}/usage`, { params })
3039
+ const rotateApiKey = (endpoint, clientId) => endpoint.api //
3040
+ .post(`/v2/api-keys/${clientId}/rotate`)
2602
3041
  .then((r) => r.data);
2603
3042
  /**
2604
- * Create an organization. The caller will be assigned an "Owner" profile in the new organization,
2605
- * and it will be set to "current" automatically. A new set of session tokens will be issued to
2606
- * the caller, and the caller should update their endpoint to use the new tokens.
3043
+ * Update an API key to change its assigned Profile ID or Name.
2607
3044
  *
2608
3045
  * ```typescript
2609
- * import {createOrganization} from '@verdocs/js-sdk';
3046
+ * import {updateApiKey} from '@verdocs/js-sdk';
2610
3047
  *
2611
- * const organization = await createOrganization(VerdocsEndpoint.getDefault(), {name: 'NewOrg'});
3048
+ * await updateApiKey(ORGID, CLIENTID, {name: NEWNAME});
2612
3049
  * ```
2613
3050
  *
2614
- * @group Organizations
2615
- * @api POST /v2/organizations Create organization
2616
- * @apiDescription The caller will be assigned an "Owner" profile in the new organization, and it will be set to "current" automatically. A new set of session tokens will be issued to the caller, and the caller should update their endpoint to use the new tokens.
2617
- * @apiBody string name The name of the new organization
2618
- * @apiBody string parent_id? If set, the new organization will be created as a child of the specified parent organization. The caller must be an admin of the parent organization.
2619
- * @apiBody string contact_email? Contact email for the new organization
2620
- * @apiBody string url? URL for the new organization
2621
- * @apiBody string full_logo_url? URL of a large-format PNG logo
2622
- * @apiBody string thumbnail_url? URL of a small-format (square is recommended) PNG logo
2623
- * @apiBody string primary_color? URL of a small-format (square is recommended) PNG logo
2624
- * @apiBody string secondary_color? URL of a small-format (square is recommended) PNG logo
2625
- * @apiSuccess IAuthenticateResponse . Authentication credentials for user in the new organization. The user will be made an Owner automatically.
3051
+ * @group API Keys
3052
+ * @api PATCH /v2/api-keys/:client_id Update API key
3053
+ * @apiBody string name? New name for the API key
3054
+ * @apiBody array(items:string) permission New array of permissions to assign to the new key. Extends (but does not override) the API key's profile permissions.
3055
+ * @apiSuccess IApiKey . The updated API key. The secret will not be included.
2626
3056
  */
2627
- const createOrganization = (endpoint, params) => endpoint.api //
2628
- .post(`/v2/organizations`, params)
3057
+ const updateApiKey = (endpoint, clientId, params) => endpoint.api //
3058
+ .patch(`/v2/api-keys/${clientId}`, params)
2629
3059
  .then((r) => r.data);
2630
3060
  /**
2631
- * Update an organization. This can only be called by an admin or owner.
3061
+ * Delete an API key.
2632
3062
  *
2633
3063
  * ```typescript
2634
- * import {updateOrganization} from '@verdocs/js-sdk';
3064
+ * import {deleteApiKey} from '@verdocs/js-sdk';
2635
3065
  *
2636
- * const organizations = await updateOrganization(VerdocsEndpoint.getDefault(), organizationId, {name:'ORGNAME'});
3066
+ * await deleteApiKey(ORGID, CLIENTID);
2637
3067
  * ```
2638
3068
  *
2639
- * @group Organizations
2640
- * @api PATCH /v2/organizations/:organization_id Update organization
2641
- * @apiBody string name The name of the new organization
2642
- * @apiBody string contact_email? Contact email for the new organization
2643
- * @apiBody string url? URL for the new organization
2644
- * @apiBody string full_logo_url? URL of a large-format PNG logo
2645
- * @apiBody string thumbnail_url? URL of a small-format (square is recommended) PNG logo
2646
- * @apiBody string primary_color? URL of a small-format (square is recommended) PNG logo
2647
- * @apiBody string secondary_color? URL of a small-format (square is recommended) PNG logo
2648
- * @apiSuccess IOrganization . The details for the updated organization
3069
+ * @group API Keys
3070
+ * @api DELETE /v2/api-keys/:client_id Delete API key
3071
+ * @apiSuccess string . Success.
2649
3072
  */
2650
- const updateOrganization = (endpoint, organizationId, params) => endpoint.api //
2651
- .patch(`/v2/organizations/${organizationId}`, params)
3073
+ const deleteApiKey = (endpoint, clientId) => endpoint.api //
3074
+ .delete(`/v2/api-keys/${clientId}`)
2652
3075
  .then((r) => r.data);
3076
+
2653
3077
  /**
2654
- * Delete an organization. This can only be called by an owner. Inclusion of the organization ID to delete
2655
- * is just a safety check. The caller may only delete the organization they have currently selected.
3078
+ * An Organization Contact (aka Profile) is an individual user with no access to an organization. These entries
3079
+ * appear only in contact lists, usually to populate quick-search dropdowns when sending envelopes.
3080
+ *
3081
+ * @module
3082
+ */
3083
+ /**
3084
+ * Get a list of the contacts in the caller's organization.
2656
3085
  *
2657
3086
  * ```typescript
2658
- * import {deleteOrganization} from '@verdocs/js-sdk';
3087
+ * import {getOrganizationContacts} from '@verdocs/js-sdk';
2659
3088
  *
2660
- * const newSession = await deleteOrganization(VerdocsEndpoint.getDefault(), organizationId);
3089
+ * const members = await getOrganizationContacts(VerdocsEndpoint.getDefault()});
2661
3090
  * ```
2662
3091
  *
2663
- * @group Organizations
2664
- * @api DELETE /v2/organizations/:organization_id Delete organization
2665
- * @apiSuccess IAuthenticateResponse . If the caller is a member of another organization, authentication credentials for the next organization available. If not, this will be null and the caller will be logged out.
3092
+ * @group Organization Contacts
3093
+ * @api GET /v2/organization-contacts Get a list of organization contacts
3094
+ * @apiBody string email Email address for the invitee
3095
+ * @apiBody string token Invite token for the invitee
3096
+ * @apiSuccess string . Success. The invitation will be marked declined and the token will be invalidated.
2666
3097
  */
2667
- const deleteOrganization = (endpoint, organizationId) => endpoint.api //
2668
- .delete(`/v2/organizations/${organizationId}`)
3098
+ const getOrganizationContacts = (endpoint) => endpoint.api //
3099
+ .get(`/v2/organization-contacts`)
2669
3100
  .then((r) => r.data);
2670
3101
  /**
2671
- * Update the organization's full or thumbnail logo. This can only be called by an admin or owner.
3102
+ * Delete a contact from the caller's organization. Note that the caller must be an admin or owner.
2672
3103
  *
2673
3104
  * ```typescript
2674
- * import {updateOrganizationLogo} from '@verdocs/js-sdk';
3105
+ * import {deleteOrganizationContact} from '@verdocs/js-sdk';
2675
3106
  *
2676
- * await updateOrganizationLogo((VerdocsEndpoint.getDefault(), organizationId, file);
3107
+ * await deleteOrganizationContact(VerdocsEndpoint.getDefault(), 'PROFILEID'});
2677
3108
  * ```
2678
3109
  *
2679
- * @group Organizations
2680
- * @api PATCH /v2/organizations/:organization_id Update organization full or thumbnail logo.
2681
- * @apiBody image/png logo? Form-url-encoded file to upload
2682
- * @apiBody image/png thumbnail? Form-url-encoded file to upload
2683
- * @apiSuccess IOrganization . The updated organization.
3110
+ * @group Organization Contacts
3111
+ * @api POST /v2/organization-invitations/decline GET a list of pending invitations
3112
+ * @apiBody string email Email address for the invitee
3113
+ * @apiBody string token Invite token for the invitee
3114
+ * @apiSuccess string . Success. The invitation will be marked declined and the token will be invalidated.
2684
3115
  */
2685
- const updateOrganizationLogo = (endpoint, organizationId, file, onUploadProgress) => {
2686
- const formData = new FormData();
2687
- formData.append('logo', file, file.name);
2688
- return endpoint.api //
2689
- .patch(`/v2/organizations/${organizationId}`, formData, {
2690
- timeout: 120000,
2691
- onUploadProgress: (event) => {
2692
- const total = event.total || 1;
2693
- const loaded = event.loaded || 0;
2694
- onUploadProgress?.(Math.floor((loaded * 100) / (total)), loaded, total);
2695
- },
2696
- })
2697
- .then((r) => r.data);
2698
- };
3116
+ const deleteOrganizationContact = (endpoint, profileId) => endpoint.api //
3117
+ .delete(`/v2/organization-contacts/${profileId}`)
3118
+ .then((r) => r.data);
2699
3119
  /**
2700
- * Update the organization's thumbnail. This can only be called by an admin or owner.
3120
+ * Update a member.
2701
3121
  *
2702
3122
  * ```typescript
2703
- * import {updateOrganizationThumbnail} from '@verdocs/js-sdk';
3123
+ * import {createOrganizationContact} from '@verdocs/js-sdk';
2704
3124
  *
2705
- * await updateOrganizationThumbnail((VerdocsEndpoint.getDefault(), organizationId, file);
3125
+ * const result = await createOrganizationContact(VerdocsEndpoint.getDefault(), 'PROFILEID', {first_name:'First', last_name:'Last', email:'a@b.com'});
2706
3126
  * ```
2707
3127
  */
2708
- const updateOrganizationThumbnail = (endpoint, organizationId, file, onUploadProgress) => {
2709
- const formData = new FormData();
2710
- formData.append('thumbnail', file, file.name);
2711
- return endpoint.api //
2712
- .patch(`/v2/organizations/${organizationId}`, formData, {
2713
- timeout: 120000,
2714
- onUploadProgress: (event) => {
2715
- const total = event.total || 1;
2716
- const loaded = event.loaded || 0;
2717
- onUploadProgress?.(Math.floor((loaded * 100) / (total)), loaded, total);
2718
- },
2719
- })
2720
- .then((r) => r.data);
2721
- };
2722
- const getEntitlements = async (endpoint) => endpoint.api.get(`/v2/organizations/entitlements`).then((r) => r.data);
3128
+ const createOrganizationContact = (endpoint, params) => endpoint.api //
3129
+ .post(`/v2/organization-contacts`, params)
3130
+ .then((r) => r.data);
2723
3131
  /**
2724
- * Largely intended to be used internally by Web SDK components but may be informative for other cases.
2725
- * Entitlements are feature grants such as "ID-based KBA" that require paid contracts to enable, typically
2726
- * because the underlying services that support them are fee-based. Entitlements may run concurrently,
2727
- * and may have different start/end dates e.g. "ID-based KBA" may run 1/1/2026-12/31/2026 while
2728
- * "SMS Authentication" may be added later and run 6/1/2026-5/31/2027. The entitlements list is a simple
2729
- * array of enablements and may include entries that are not YET enabled or have now expired.
2730
- *
2731
- * In client code it is helpful to simply know "is XYZ feature currently enabled?" This function collapses
2732
- * the entitlements list to a simplified dictionary of current/active entitlements. Note that it is async
2733
- * because it calls the server to obtain the "most current" entitlements list. Existence of an entry in the
2734
- * resulting dictionary implies the feature is active. Metadata inside each entry can be used to determine
2735
- * limits, etc.
3132
+ * Update a member.
2736
3133
  *
2737
3134
  * ```typescript
2738
- * import {getActiveEntitlements} from '@verdocs/js-sdk';
3135
+ * import {updateOrganizationContact} from '@verdocs/js-sdk';
2739
3136
  *
2740
- * const activeEntitlements = await getActiveEntitlements((VerdocsEndpoint.getDefault());
2741
- * const isSMSEnabled = !!activeEntitlements.sms_auth;
2742
- * const monthlyKBALimit = activeEntitlements.kba_auth?.monthly_max;
2743
- * ```
2744
- */
2745
- const getActiveEntitlements = async (endpoint) => {
2746
- if (!endpoint.session) {
2747
- throw new Error('No active session');
2748
- }
2749
- const entitlements = await getEntitlements(endpoint);
2750
- return collapseEntitlements(entitlements);
2751
- };
3137
+ * const result = await updateOrganizationContact(VerdocsEndpoint.getDefault(), 'PROFILEID', {first_name:'NewFirst'});
3138
+ * ```
3139
+ */
3140
+ const updateOrganizationContact = (endpoint, profileId, params) => endpoint.api //
3141
+ .patch(`/v2/organization-contacts/${profileId}`, params)
3142
+ .then((r) => r.data);
2752
3143
 
2753
3144
  /**
2754
- * Webhooks are callback triggers from Verdocs to your servers that notify your applications
2755
- * of various events, such as signing operations.
3145
+ * Organizations may contain "Groups" of user profiles, called Members. Groups may have permissions assigned that
3146
+ * apply to all Members, making it easy to configure role-based access control (RBAC) within an Organization. Note
3147
+ * that permissions are **additive**. A user may be a member of more than one group, and may also have permissions
3148
+ * assigned directly. In that case, the user will have the combined set of all permissions inherited from all
3149
+ * sources.
2756
3150
  *
2757
3151
  * @module
2758
3152
  */
2759
3153
  /**
2760
- * Get the registered Webhook configuration for the caller's organization.
3154
+ * Get a list of groups for the caller's organization. NOTE: Any organization member may request
3155
+ * the list of groups, but only Owners and Admins may update them.
2761
3156
  *
2762
3157
  * ```typescript
2763
- * import {getWebhooks} from '@verdocs/js-sdk';
3158
+ * import {getGroups} from '@verdocs/js-sdk';
2764
3159
  *
2765
- * await getWebhooks(ORGID, params);
3160
+ * const groups = await getGroups();
2766
3161
  * ```
2767
- *
2768
- * @group Webhooks
2769
- * @api GET /v2/webhooks Get organization Webhooks config
2770
- * @apiSuccess IWebhook . The current Webhooks config for the caller's organization.
2771
3162
  */
2772
- const getWebhooks = (endpoint) => endpoint.api //
2773
- .get(`/v2/webhooks`)
3163
+ const getGroups = (endpoint) => endpoint.api //
3164
+ .get(`/v2/organization-groups`)
2774
3165
  .then((r) => r.data);
2775
3166
  /**
2776
- * Update the registered Webhook configuration for the caller's organization. Note that
2777
- * Webhooks cannot currently be deleted, but may be easily disabled by setting `active`
2778
- * to `false` and/or setting the `url` to an empty string.
3167
+ * Get the details for a group, including its member profiles and list of permissions.
2779
3168
  *
2780
3169
  * ```typescript
2781
- * import {setWebhooks} from '@verdocs/js-sdk';
3170
+ * import {getGroup} from '@verdocs/js-sdk/v2/organization-groups';
2782
3171
  *
2783
- * await setWebhooks(ORGID, params);
3172
+ * const group = await getGroup(GROUPID);
2784
3173
  * ```
2785
- *
2786
- * @group Webhooks
2787
- * @api PATCH /v2/webhooks Update organization Webhooks config
2788
- * @apiDescription Note that Webhooks cannot currently be deleted, but may be easily disabled by setting `active` to `false` and/or setting the `url` to an empty string.
2789
- * @apiBody string url URL to send Webhook events to. An empty or invalid URL will disable Webhook calls.
2790
- * @apiBody boolean active Set to true to enable Webhooks calls.
2791
- * @apiBody object events Record<TWebhookEvent, boolean> map of events to enable/disable.
2792
- * @apiSuccess IWebhook . The updated Webhooks config for the caller's organization.
2793
3174
  */
2794
- const setWebhooks = (endpoint, params) => endpoint.api //
2795
- .patch(`/v2/webhooks`, params)
3175
+ const getGroup = (endpoint, groupId) => endpoint.api //
3176
+ .get(`/v2/organization-groups/${groupId}`)
2796
3177
  .then((r) => r.data);
2797
-
2798
- /**
2799
- * A map of the permissions each role confers.
2800
- */
2801
- const RolePermissions = {
2802
- owner: [
2803
- 'template:creator:create:public',
2804
- 'template:creator:create:org',
2805
- 'template:creator:create:personal',
2806
- 'template:creator:delete',
2807
- 'template:creator:visibility',
2808
- 'template:member:read',
2809
- 'template:member:write',
2810
- 'template:member:delete',
2811
- 'template:member:visibility',
2812
- 'owner:add',
2813
- 'owner:remove',
2814
- 'admin:add',
2815
- 'admin:remove',
2816
- 'member:view',
2817
- 'member:add',
2818
- 'member:remove',
2819
- 'org:create',
2820
- 'org:view',
2821
- 'org:update',
2822
- 'org:delete',
2823
- 'org:transfer',
2824
- 'org:list',
2825
- 'envelope:create',
2826
- 'envelope:cancel',
2827
- 'envelope:view',
2828
- ],
2829
- admin: [
2830
- 'template:creator:create:public',
2831
- 'template:creator:create:org',
2832
- 'template:creator:create:personal',
2833
- 'template:creator:delete',
2834
- 'template:creator:visibility',
2835
- 'template:member:read',
2836
- 'template:member:write',
2837
- 'template:member:delete',
2838
- 'template:member:visibility',
2839
- 'admin:add',
2840
- 'admin:remove',
2841
- 'member:view',
2842
- 'member:add',
2843
- 'member:remove',
2844
- 'org:create',
2845
- 'org:view',
2846
- 'org:update',
2847
- 'org:list',
2848
- 'envelope:create',
2849
- 'envelope:cancel',
2850
- 'envelope:view',
2851
- ],
2852
- member: [
2853
- 'template:creator:create:public',
2854
- 'template:creator:create:org',
2855
- 'template:creator:create:personal',
2856
- 'template:creator:delete',
2857
- 'template:creator:visibility',
2858
- 'template:member:read',
2859
- 'template:member:write',
2860
- 'template:member:delete',
2861
- 'member:view',
2862
- 'org:create',
2863
- 'org:view',
2864
- 'org:list',
2865
- 'envelope:create',
2866
- 'envelope:cancel',
2867
- 'envelope:view',
2868
- ],
2869
- basic_user: ['template:member:read', 'member:view', 'org:view', 'org:list'],
2870
- contact: ['org:view', 'org:list', 'org:create'],
2871
- };
2872
3178
  /**
2873
- * Confirm whether the user has all of the specified permissions.
3179
+ * Create a group. Note that "everyone" is a reserved name and may not be created.
3180
+ *
3181
+ * ```typescript
3182
+ * import {createGroup} from '@verdocs/js-sdk';
3183
+ *
3184
+ * const group = await createGroup(VerdocsEndpoint.getDefault(), {name:'newgroup'});
3185
+ * ```
2874
3186
  */
2875
- const userHasPermissions = (profile, permissions) => {
2876
- // No need to de-dupe here, we're just checking present-at-least-once set membership.
2877
- const netPermissions = [...(profile?.permissions || [])];
2878
- (profile?.roles || []).forEach((role) => {
2879
- netPermissions.push(...(RolePermissions[role] || []));
2880
- });
2881
- (profile?.group_profiles || []).forEach((groupProfile) => {
2882
- netPermissions.push(...(groupProfile.group?.permissions || []));
2883
- });
2884
- return permissions.every((perm) => netPermissions.includes(perm));
2885
- };
2886
-
2887
- const canPerformTemplateAction = (profile, action, template) => {
2888
- if (!template && !action.includes('create')) {
2889
- return { canPerform: false, message: 'Missing required template object' };
2890
- }
2891
- // We use BOGUS here to force the option-chain in things like template?.profile_id to NOT match profile?.profile_id because if both
2892
- // were undefined, they would actually match.
2893
- const profile_id = profile?.id || 'BOGUS';
2894
- const organization_id = profile?.organization_id || 'BOGUS';
2895
- const isCreator = template?.profile_id === profile_id;
2896
- const isSameOrg = template?.organization_id === organization_id;
2897
- const isPersonal = template?.is_personal ?? false;
2898
- const isPublic = template?.is_public ?? false;
2899
- const permissionsRequired = [];
2900
- switch (action) {
2901
- case 'create_personal':
2902
- permissionsRequired.push('template:creator:create:personal');
2903
- break;
2904
- case 'create_org':
2905
- permissionsRequired.push('template:creator:create:org');
2906
- break;
2907
- case 'create_public':
2908
- permissionsRequired.push('template:creator:create:public');
2909
- break;
2910
- case 'read':
2911
- if (!isCreator) {
2912
- if ((!isPersonal && isSameOrg) || !isPublic) {
2913
- permissionsRequired.push('template:member:read');
2914
- }
2915
- }
2916
- break;
2917
- case 'write':
2918
- if (!isCreator) {
2919
- permissionsRequired.push('template:member:read');
2920
- permissionsRequired.push('template:member:write');
2921
- }
2922
- break;
2923
- case 'change_visibility_personal':
2924
- if (isCreator) {
2925
- permissionsRequired.push('template:creator:create:personal');
2926
- }
2927
- else {
2928
- permissionsRequired.push('template:member:visibility');
2929
- }
2930
- break;
2931
- case 'change_visibility_org':
2932
- if (isCreator) {
2933
- permissionsRequired.push('template:creator:create:org');
2934
- }
2935
- else {
2936
- permissionsRequired.push('template:member:visibility');
2937
- }
2938
- break;
2939
- case 'change_visibility_public':
2940
- if (isCreator) {
2941
- permissionsRequired.push('template:creator:create:public');
2942
- permissionsRequired.push('template:creator:visibility');
2943
- }
2944
- else {
2945
- permissionsRequired.push('template:member:visibility');
2946
- }
2947
- break;
2948
- case 'delete':
2949
- if (isCreator) {
2950
- permissionsRequired.push('template:creator:delete');
2951
- }
2952
- else {
2953
- permissionsRequired.push('template:member:delete');
2954
- }
2955
- break;
2956
- default:
2957
- return { canPerform: false, message: 'Action is not defined' };
2958
- }
2959
- if (hasRequiredPermissions(profile, permissionsRequired)) {
2960
- return { canPerform: true, message: '' };
2961
- }
2962
- return { canPerform: false, message: `Insufficient access to perform '${action}'. Needed permissions: ${permissionsRequired.toString()}` };
2963
- };
2964
- const hasRequiredPermissions = (profile, permissions) => permissions.every((perm) => (profile?.permissions || []).includes(perm));
2965
-
3187
+ const createGroup = (endpoint, params) => endpoint.api //
3188
+ .post('/v2/organization-groups', params)
3189
+ .then((r) => r.data);
2966
3190
  /**
2967
- * Add a field to a template.
3191
+ * Update a group. Note that "everyone" is a reserved name and may not be changed.
2968
3192
  *
2969
3193
  * ```typescript
2970
- * import {createField} from '@verdocs/js-sdk/Templates';
3194
+ * import {updateGroup} from '@verdocs/js-sdk';
2971
3195
  *
2972
- * await createField((VerdocsEndpoint.getDefault(), template_id, { ... });
3196
+ * const updated = await updateGroup(VerdocsEndpoint.getDefault(), {name:'newname'});
2973
3197
  * ```
3198
+ */
3199
+ const updateGroup = (endpoint, groupId, params) => endpoint.api //
3200
+ .patch(`/v2/organization-groups/${groupId}`, params)
3201
+ .then((r) => r.data);
3202
+ /**
3203
+ * Get an organization by ID. Note that the "everyone" group cannot be deleted.
2974
3204
  *
2975
- * @group Fields
2976
- * @api POST /v2/fields/:template_id Add a field to a template
2977
- * @apiBody string name Name for the new field. Field names must be unique within a template. Although special characters are allowed, they must be URL-encoded in subsequent requests, so it is recommended to use only alphanumeric characters and hyphens if possible.
2978
- * @apiBody string role_name Role to assign to the field. Role names must be valid, so it is recommended to create roles before fields.
2979
- * @apiBody string document_id ID of the document upon which to place the field.
2980
- * @apiBody string(enum: 'signature' | 'initial' | 'checkbox' | 'radio' | 'textbox' | 'timestamp' | 'date' | 'dropdown' | 'textarea' | 'attachment' | 'payment') type Type of field to create
2981
- * @apiBody boolean(default: false) required Whether the field is required
2982
- * @apiBody integer(min: 0) page 0-based page number upon which to place the field
2983
- * @apiBody integer(min: 0) x X position for the field (left to right)
2984
- * @apiBody integer(min: 0) y Y position for the field (_bottom to top!_)
2985
- * @apiBody string label? Optional label to display above the field
2986
- * @apiBody integer(min: 50) width? Width of the field. Note that all fields have built-in defaults, and it is recommended that this only be set on text fields.
2987
- * @apiBody integer(min: 15) height? Height of the field. Note that all fields have built-in defaults, and it is recommended that this only be set on text fields.
2988
- * @apiBody string placeholder? Optional placeholder to display in text fields
2989
- * @apiBody string group? For fields that support grouping (radio buttons and check boxes) the value selected will be stored under this name
2990
- * @apiBody array(items:IDropdownOption) options? For dropdown fields, the options to display
2991
- * @apiBody string value? Optional default value to set on the field
2992
- * @apiSuccess ITemplateField . Template field
3205
+ * ```typescript
3206
+ * import {deleteGroup} from '@verdocs/js-sdk';
3207
+ *
3208
+ * await deleteGroup(VerdocsEndpoint.getDefault(), 'ORGID');
3209
+ * ```
2993
3210
  */
2994
- const createField = (endpoint, templateId, params) => endpoint.api //
2995
- .post(`/v2/fields/${templateId}`, params)
3211
+ const deleteGroup = (endpoint, groupId) => endpoint.api //
3212
+ .delete(`/v2/organization-groups/${groupId}`)
2996
3213
  .then((r) => r.data);
2997
3214
  /**
2998
- * Update a template field.
3215
+ * Add a member to a group.
2999
3216
  *
3000
3217
  * ```typescript
3001
- * import {updateField} from '@verdocs/js-sdk/Templates';
3218
+ * import {addGroupMember} from '@verdocs/js-sdk';
3002
3219
  *
3003
- * await updateField((VerdocsEndpoint.getDefault(), template_id, field_name, { ... });
3220
+ * await addGroupMember(VerdocsEndpoint.getDefault(), 'GROUPID', 'PROFILEID');
3004
3221
  * ```
3005
- *
3006
- * @group Fields
3007
- * @api PATCH /v2/fields/:template_id/:field_name Update a field. See createField for additional details on the supported parameters.
3008
- * @apiBody string name? Rename the field. Note that template field names must be unique within a template.
3009
- * @apiBody string role_name Role to assign to the field.
3010
- * @apiBody string document_id ID of the document upon which to place the field.
3011
- * @apiBody string(enum: 'signature' | 'initial' | 'checkbox' | 'radio' | 'textbox' | 'timestamp' | 'date' | 'dropdown' | 'textarea' | 'attachment' | 'payment') type? Change the field type. Note that while this is technically allowed, fields have different behaviors, validators, default sizes, etc. It is usually easier to add a new field and delete the old one.
3012
- * @apiBody boolean(default: false) required? Whether the field is required
3013
- * @apiBody integer(min: 0) page? 0-based page number upon which to place the field
3014
- * @apiBody integer(min: 0) x? X position for the field (left to right)
3015
- * @apiBody integer(min: 0) y? Y position for the field (_bottom to top!_)
3016
- * @apiBody string label? Optional label to display above the field
3017
- * @apiBody integer(min: 50) width? Width of the field. Note that all fields have built-in defaults, and it is recommended that this only be set on text fields.
3018
- * @apiBody integer(min: 15) height? Height of the field. Note that all fields have built-in defaults, and it is recommended that this only be set on text fields.
3019
- * @apiBody string placeholder? Optional placeholder to display in text fields
3020
- * @apiBody string group? For fields that support grouping (radio buttons and check boxes) the value selected will be stored under this name
3021
- * @apiBody array(items:IDropdownOption) options? For dropdown fields, the options to display
3022
- * @apiBody string value? Optional default value to set on the field
3023
- * @apiSuccess ITemplateField . Updated template field
3024
3222
  */
3025
- const updateField = (endpoint, templateId, name, params) => endpoint.api //
3026
- .patch(`/v2/fields/${templateId}/${encodeURIComponent(name)}`, params)
3223
+ const addGroupMember = (endpoint, groupId, profile_id) => endpoint.api //
3224
+ .post(`/v2/organization-groups/${groupId}/members`, { profile_id })
3027
3225
  .then((r) => r.data);
3028
3226
  /**
3029
- * Remove a field from a template.
3227
+ * Remove a member from a group.
3030
3228
  *
3031
3229
  * ```typescript
3032
- * import {deleteField} from '@verdocs/js-sdk/Templates';
3230
+ * import {deleteGroupMember} from '@verdocs/js-sdk';
3033
3231
  *
3034
- * await deleteField((VerdocsEndpoint.getDefault(), template_id, field_name);
3232
+ * await deleteGroupMember(VerdocsEndpoint.getDefault(), 'GROUPID', 'PROFILEID');
3035
3233
  * ```
3036
- *
3037
- * @group Fields
3038
- * @api DELETE /v2/fields/:template_id/:field_name Delete a field
3039
- * @apiSuccess string . Success
3040
3234
  */
3041
- const deleteField = (endpoint, templateId, name) => endpoint.api //
3042
- .delete(`/v2/fields/${templateId}/${encodeURIComponent(name)}`)
3235
+ const deleteGroupMember = (endpoint, groupId, profile_id) => endpoint.api //
3236
+ .delete(`/v2/organization-groups/${groupId}/members/${profile_id}`)
3043
3237
  .then((r) => r.data);
3044
3238
 
3045
3239
  /**
3046
- * Various helpers to identify available operations for a template by a user.
3240
+ * An invitation represents an opportunity for a Member to join an Organization.
3047
3241
  *
3048
3242
  * @module
3049
3243
  */
3050
3244
  /**
3051
- * Check to see if the user created the template.
3052
- */
3053
- const userIsTemplateCreator = (profile, template) => profile && template && profile.id === template.profile_id;
3054
- /**
3055
- * Check to see if a template is "shared" with the user.
3056
- */
3057
- const userHasSharedTemplate = (profile, template) => profile && template && !template.is_personal && profile.organization_id === template.organization_id;
3058
- /**
3059
- * Check to see if the user can create a personal/private template.
3060
- */
3061
- const userCanCreatePersonalTemplate = (profile) => userHasPermissions(profile, ['template:creator:create:personal']);
3062
- /**
3063
- * Check to see if the user can create an org-shared template.
3064
- */
3065
- const userCanCreateOrgTemplate = (profile) => userHasPermissions(profile, ['template:creator:create:org']);
3066
- /**
3067
- * Check to see if the user can create a public template.
3068
- */
3069
- const userCanCreatePublicTemplate = (profile) => userHasPermissions(profile, ['template:creator:create:public']);
3070
- /**
3071
- * Check to see if the user can read/view a template.
3072
- */
3073
- const userCanReadTemplate = (profile, template) => template.is_public ||
3074
- userIsTemplateCreator(profile, template) ||
3075
- (userHasSharedTemplate(profile, template) && userHasPermissions(profile, ['template:member:read']));
3076
- /**
3077
- * Check to see if the user can update a tempate.
3078
- */
3079
- const userCanUpdateTemplate = (profile, template) => userIsTemplateCreator(profile, template) ||
3080
- (userHasSharedTemplate(profile, template) && userHasPermissions(profile, ['template:member:read', 'template:member:write']));
3081
- /**
3082
- * Check to see if the user can change whether a template is personal vs org-shared.
3083
- */
3084
- const userCanMakeTemplatePrivate = (profile, template) => userIsTemplateCreator(profile, template)
3085
- ? userHasPermissions(profile, ['template:creator:create:personal'])
3086
- : userHasPermissions(profile, ['template:member:visibility']);
3087
- /**
3088
- * Check to see if the user can change whether a template is personal vs org-shared.
3089
- */
3090
- const userCanMakeTemplateShared = (profile, template) => userIsTemplateCreator(profile, template)
3091
- ? userHasPermissions(profile, ['template:creator:create:org'])
3092
- : userHasPermissions(profile, ['template:member:visibility']);
3093
- /**
3094
- * Check to see if the user can change whether a template is personal vs org-shared.
3095
- */
3096
- const userCanMakeTemplatePublic = (profile, template) => userIsTemplateCreator(profile, template)
3097
- ? userHasPermissions(profile, ['template:creator:create:public'])
3098
- : userHasPermissions(profile, ['template:member:visibility']);
3099
- /**
3100
- * Check to see if the user can change whether a template is personal vs org-shared.
3101
- */
3102
- const userCanChangeOrgVisibility = (profile, template) => userIsTemplateCreator(profile, template) && userHasPermissions(profile, ['template:creator:create:personal']);
3103
- /**
3104
- * Check to see if the user can change whether a template is personal vs org-shared.
3105
- */
3106
- const userCanDeleteTemplate = (profile, template) => userIsTemplateCreator(profile, template)
3107
- ? userHasPermissions(profile, ['template:creator:delete'])
3108
- : userHasPermissions(profile, ['template:member:delete']);
3109
- /**
3110
- * Confirm whether the user can create an envelope using the specified template.
3245
+ * Get a list of invitations pending for the caller's organization. The caller must be an admin or owner.
3246
+ *
3247
+ * @group Organization Invitations
3248
+ * @api GET /v2/organization-invitations Get a list of pending invitations
3249
+ * @apiBody array(items:TRole) roles URL to send Webhook events to. An empty or invalid URL will disable Webhook calls.
3250
+ * @apiBody string first_name First name. The user may override this after accepting the invitation.
3251
+ * @apiBody string last_name Last name. The user may override this after accepting the invitation.
3252
+ * @apiSuccess array(items:IProfile) . List of caller's current organization's members
3111
3253
  */
3112
- const userCanSendTemplate = (profile, template) => {
3113
- switch (template.visibility) {
3114
- case 'private':
3115
- return userIsTemplateCreator(profile, template);
3116
- case 'shared':
3117
- return userIsTemplateCreator(profile, template) || template.organization_id === profile?.organization_id;
3118
- case 'public':
3119
- return true;
3120
- }
3121
- };
3254
+ const getOrganizationInvitations = (endpoint) => endpoint.api //
3255
+ .get(`/v2/organization-invitations`)
3256
+ .then((r) => r.data);
3122
3257
  /**
3123
- * Confirm whether the user can create a new template.
3258
+ * Invite a new user to join the organization.
3259
+ *
3260
+ * @group Organization Invitations
3261
+ * @api POST /v2/organization-invitations Invite a new user to join the organization
3262
+ * @apiBody string email Email address to send the invitation to
3263
+ * @apiBody string first_name First name. The user may override this after accepting the invitation.
3264
+ * @apiBody string last_name Last name. The user may override this after accepting the invitation.
3265
+ * @apiBody TRole role Initial role to assign to the user once they accept.
3266
+ * @apiSuccess IOrganizationInvitation . The newly-created invitation.
3124
3267
  */
3125
- const userCanCreateTemplate = (profile) => userCanCreatePersonalTemplate(profile) || userCanCreateOrgTemplate(profile) || userCanCreatePublicTemplate(profile);
3268
+ const createOrganizationInvitation = (endpoint, params) => endpoint.api //
3269
+ .post(`/v2/organization-invitations`, params)
3270
+ .then((r) => r.data);
3126
3271
  /**
3127
- * Check to see if the user can "build" the template (use the field builder). The user must have write access to the
3128
- * template, and the template must have at least one signer role.
3272
+ * Delete an invitation. Note that no cancellation message will be sent. Invitations are also one-time-use.
3273
+ * If the invitee attempts to join after the invitation is deleted, accepted, or decline, they will be
3274
+ * shown an error.
3275
+ *
3276
+ * @group Organization Invitations
3277
+ * @api DELETE /v2/organization-invitations/:email Delete a pending invitation
3278
+ * @apiSuccess string . Success
3129
3279
  */
3130
- const userCanBuildTemplate = (profile, template) => userCanUpdateTemplate(profile, template) && (template.roles || []).filter((role) => role.type === 'signer').length > 0;
3131
- const getFieldsForRole = (template, role_name) => (template.fields || []).filter((field) => field.role_name === role_name);
3280
+ const deleteOrganizationInvitation = (endpoint, email) => endpoint.api //
3281
+ .delete(`/v2/organization-invitations/${email}`)
3282
+ .then((r) => r.data);
3132
3283
  /**
3133
- * Check to see if the user can preview the template. The user must have read access to the template, the template must
3134
- * have at least one signer, and every signer must have at least one field.
3284
+ * Update an invitation. Note that email may not be changed after the invite is sent. To change
3285
+ * an invitee's email, delete the incorrect entry and create one with the correct value.
3286
+ *
3287
+ * @group Organization Invitations
3288
+ * @api PATCH /v2/organization-invitations/:email Update a pending invitation
3289
+ * @apiBody string first_name First name. The user may override this after accepting the invitation.
3290
+ * @apiBody string last_name Last name. The user may override this after accepting the invitation.
3291
+ * @apiBody TRole role Initial role to assign to the user once they accept.
3292
+ * @apiSuccess IOrganizationInvitation . The updated invitation.
3135
3293
  */
3136
- const userCanPreviewTemplate = (profile, template) => {
3137
- const hasPermission = userCanReadTemplate(profile, template);
3138
- const signers = (template.roles || []).filter((role) => role.type === 'signer');
3139
- return hasPermission && signers.length > 0 && signers.every((signer) => getFieldsForRole(template, signer.name).length > 0);
3140
- };
3141
-
3294
+ const updateOrganizationInvitation = (endpoint, email, params) => endpoint.api //
3295
+ .patch(`/v2/organization-invitations/${email}`, params)
3296
+ .then((r) => r.data);
3142
3297
  /**
3143
- * A "role" is an individual participant in a signing flow, such as a signer or CC contact.
3144
- * A role is a placeholder that will eventually become a named recipient. For example, "Tenant 1"
3145
- * might be replaced with "John Smith" when the document is sent out for signature.
3146
- *
3147
- * Role names must be unique within a template, e.g. 'Recipient 1'. They may contain any [a-zA-Z0-9_- ]
3148
- * characters, although it is recommended to keep them simple and human-readable, and to avoid
3149
- * spaces (although they are allowed). If spaces are used in role names, be sure to URL-encode them
3150
- * when calling endpoints like `updateRole()` e.g. 'Recipient%201'.
3151
- *
3152
- * NOTE: Roles are always enumerated under Template objects, so there are no "list" or "get" endpoints
3153
- * for them. To get a template's latest role list, simply call `getTemplate()`.
3298
+ * Send a reminder to the invitee to join the organization.
3154
3299
  *
3155
- * @module
3300
+ * @group Organization Invitations
3301
+ * @api POST /v2/organization-invitations/resend Send a reminder to a pending invitee
3302
+ * @apiBody string email The recipient to send the reminder to
3303
+ * @apiSuccess IOrganizationInvitation . The updated invitation
3156
3304
  */
3305
+ const resendOrganizationInvitation = (endpoint, email) => endpoint.api //
3306
+ .post('/v2/organization-invitations/resend', { email })
3307
+ .then((r) => r.data);
3157
3308
  /**
3158
- * Create a role.
3159
- *
3160
- * ```typescript
3161
- * import {createTemplateRole} from '@verdocs/js-sdk';
3162
- *
3163
- * const role = await createTemplateRole(VerdocsEndpoint.getDefault(), template_id, params...);
3164
- * ```
3309
+ * Get an invitation's details. This is generally used as the first step of accepting the invite.
3310
+ * A successful response will indicate that the invite token is still valid, and include some
3311
+ * metadata for the organization to style the acceptance screen.
3165
3312
  *
3166
- * @group Roles
3167
- * @api POST /v2/roles/:template_id Add a role to a template
3168
- * @apiBody string name Name for the new role. Must be unique within the template. May include spaces, but later calls must URL-encode any references to this role, so it is recomended that special characters be avoided.
3169
- * @apiBody string(enum:'signer' | 'cc' | 'approver') type Type of role to create. Signers act on documents by filling and signing fields. CC recipients receive a copy but do not act on the document. Approvers control the final submission of a document, but do not have fields of their own to fill out.
3170
- * @apiBody string full_name? Default full name for the role. May be completed/overridden later, when envelopes are made from the template.
3171
- * @apiBody string email? Default email address for the role. May be completed/overridden later, when envelopes are made from the template.
3172
- * @apiBody string phone? Default (SMS-capable) phone number for the role. May be completed/overridden later, when envelopes are made from the template.
3173
- * @apiBody string message? Optional message to include in email and SMS signing invitations.
3174
- * @apiBody integer(min: 1, default: 1) sequence? Optional 1-based sequence number for the role. Roles that share the same sequence number act in parallel, and will receive invitations at the same time.
3175
- * @apiBody integer(min: 1, default: 1) order? Optional 1-based order number for the role. Controls the left-to-right display order of roles at the same sequence number in the UI components e.g. `<verdocs-template-roles />`.
3176
- * @apiBody boolean delegator? If true, the role may delegate their signing responsibility to another party.
3177
- * @apiSuccess IRole . The newly-created role
3313
+ * @group Organization Invitations
3314
+ * @api GET /v2/organization-invitations/:email/:token Get a pending invitation (_Authenticated via invite token, not an active session._). Intended to be called by the invitee to get details about the invitation they are about to accept.
3315
+ * @apiSuccess IOrganizationInvitation . Requested invitation's details. Will always include summary details for the organization, to be used for branding the accept-invite view.
3178
3316
  */
3179
- const createTemplateRole = (endpoint, template_id, params) => endpoint.api //
3180
- .post(`/v2/roles/${template_id}`, params)
3317
+ const getOrganizationInvitation = (endpoint, email, token) => endpoint.api //
3318
+ .get(`/v2/organization-invitations/${email}/${token}`)
3181
3319
  .then((r) => r.data);
3182
3320
  /**
3183
- * Update a role.
3184
- *
3185
- * ```typescript
3186
- * import {updateTemplateRole} from '@verdocs/js-sdk';
3187
- *
3188
- * const role = await updateTemplateRole(VerdocsEndpoint.getDefault(), template_id, name, params...);
3189
- * ```
3321
+ * Accept an invitation. This will automatically create a user record for the caller as well as a profile
3322
+ * with the appropriate role as specified in the invite. The profile will be set as "current" for the caller,
3323
+ * and session tokens will be returned to access the new profile. The profile's email_verified flag will
3324
+ * also be set to true.
3190
3325
  *
3191
- * @group Roles
3192
- * @api PATCH /v2/roles/:template_id/:role_id Update a role. See createRole for additional details on the parameters available.
3193
- * @apiBody string name? Rename the role. Note that role names must be unique within a template, so this may fail if the new name is already in use.
3194
- * @apiBody string(enum:'signer' | 'cc' | 'approver') type? Type of role.
3195
- * @apiBody string full_name? Default full name for the role.
3196
- * @apiBody string email? Default email address for the role.
3197
- * @apiBody string phone? Default (SMS-capable) phone number for the role.
3198
- * @apiBody string message? Optional message to include in email and SMS signing invitations.
3199
- * @apiBody integer(min: 1, default: 1) sequence? Optional 1-based sequence number for the role.
3200
- * @apiBody integer(min: 1, default: 1) order? Optional 1-based order number for the role.
3201
- * @apiBody boolean delegator? If true, the role may delegate their signing responsibility to another party.
3202
- * @apiBody string(enum:'pin'|'identity'|'') kba_method? Active PIN- or Identity-based KBA for the role.
3203
- * @apiSuccess IRole . The newly-created role
3326
+ * @group Organization Invitations
3327
+ * @api POST /v2/organization-invitations/accept Accept an invitation
3328
+ * @apiBody string email Email address for the invitee
3329
+ * @apiBody string token Invite token for the invitee
3330
+ * @apiBody string first_name First name
3331
+ * @apiBody string last_name Last name
3332
+ * @apiBody string password Password
3333
+ * @apiSuccess IAuthenticateResponse . Session credentials for the newly-created user's profile. If the user already had a profile for another organization, the new profile will be made "current" automatically.
3204
3334
  */
3205
- const updateTemplateRole = (endpoint, template_id, name, params) => endpoint.api //
3206
- .patch(`/v2/roles/${template_id}/${encodeURIComponent(name)}`, params)
3335
+ const acceptOrganizationInvitation = (endpoint, params) => endpoint.api //
3336
+ .post('/v2/organization-invitations/accept', params)
3207
3337
  .then((r) => r.data);
3208
3338
  /**
3209
- * Delete a role.
3210
- *
3211
- * ```typescript
3212
- * import {deleteTemplateRole} from '@verdocs/js-sdk';
3213
- *
3214
- * const profiles = await deleteTemplateRole(VerdocsEndpoint.getDefault(), template_id, name);
3215
- * ```
3339
+ * Decline an invitation. This will mark the status "declined," providing a visual indication to the
3340
+ * organization's admins that the invite was declined, preventing further invites from being created
3341
+ * to the same email address, and also preventing the invitee from receiving reminders to join.
3216
3342
  *
3217
- * @group Roles
3218
- * @api DELETE /v2/roles/:template_id/:role_id Delete a role.
3219
- * @apiSuccess string . Success
3343
+ * @group Organization Invitations
3344
+ * @api POST /v2/organization-invitations/decline Decline an invitation
3345
+ * @apiDescription Mark the status "declined," providing a visual indication to the organization's admins that the invite was declined, preventing further invites from being created to the same email address, and also preventing the invitee from receiving reminders to join.
3346
+ * @apiBody string email Email address for the invitee
3347
+ * @apiBody string token Invite token for the invitee
3348
+ * @apiSuccess string . Success. The invitation will be marked declined and the token will be invalidated.
3220
3349
  */
3221
- const deleteTemplateRole = (endpoint, template_id, name) => endpoint.api //
3222
- .delete(`/v2/roles/${template_id}/${encodeURIComponent(name)}`)
3350
+ const declineOrganizationInvitation = (endpoint, email, token) => endpoint.api //
3351
+ .post('/v2/organization-invitations/decline', { email, token })
3223
3352
  .then((r) => r.data);
3224
3353
 
3225
3354
  /**
3226
- * A Template defines how a Verdocs signing flow will be performed, including attachments, signing fields, and
3227
- * recipients.
3355
+ * An Organization Member (aka Profile) is an individual user with access to an organization.
3228
3356
  *
3229
3357
  * @module
3230
3358
  */
3231
3359
  /**
3232
- * Get all templates accessible by the caller, with optional filters.
3360
+ * Get a list of the members in the caller's organization.
3233
3361
  *
3234
3362
  * ```typescript
3235
- * import {getTemplates} from '@verdocs/js-sdk/Templates';
3363
+ * import {getOrganizationMembers} from '@verdocs/js-sdk';
3236
3364
  *
3237
- * await getTemplates((VerdocsEndpoint.getDefault());
3238
- * await getTemplates((VerdocsEndpoint.getDefault(), { is_starred: true });
3239
- * await getTemplates((VerdocsEndpoint.getDefault(), { is_creator: true });
3240
- * await getTemplates((VerdocsEndpoint.getDefault(), { is_organization: true });
3365
+ * const members = await getOrganizationMembers(VerdocsEndpoint.getDefault()});
3241
3366
  * ```
3242
3367
  *
3243
- * @group Templates
3244
- * @api GET /v2/templates Get Templates
3245
- * @apiQuery string q? Find templates whose names/descriptions contain the specified query string
3246
- * @apiQuery boolean is_starred? If true, returns only templates with at least one "star".
3247
- * @apiQuery boolean is_creator? If true, returns only templates that the caller created.
3248
- * @apiQuery string(enum: 'private_shared' | 'private' | 'shared' | 'public') visibility? Return only templates with the specified visibility.
3249
- * @apiQuery string(enum: 'created_at' | 'updated_at' | 'name' | 'last_used_at' | 'counter' | 'star_counter') sort_by? Return results sorted by this criteria
3250
- * @apiQuery boolean ascending? Set true/false to override the sort direction. Note that the default depends on `sort_by`. Date-based sorts default to descending, while name defaults to ascending.
3251
- * @apiQuery integer(default: 20) rows? Limit the number of rows returned
3252
- * @apiQuery integer(default: 0) page? Specify which page of results to return
3253
- * @apiSuccess integer(format: int32) count The total number of records matching the query, helpful for pagination
3254
- * @apiSuccess integer(format: int32) rows The number of rows returned in this response page
3255
- * @apiSuccess integer(format: int32) page The page number of this response
3256
- * @apiSuccess array(items: ITemplate) templates List of templates found
3368
+ * @group Organization Members
3369
+ * @api GET /v2/organization-members List current organization's members
3370
+ * @apiSuccess array(items:IProfile) . List of caller's current organization's members
3257
3371
  */
3258
- const getTemplates = (endpoint, params) => endpoint.api //
3259
- .get('/v2/templates', { params })
3372
+ const getOrganizationMembers = (endpoint) => endpoint.api //
3373
+ .get(`/v2/organization-members`)
3260
3374
  .then((r) => r.data);
3261
3375
  /**
3262
- * Get one template by its ID.
3376
+ * Delete a member from the caller's organization. Note that the caller must be an admin or owner,
3377
+ * may not delete him/herself.
3263
3378
  *
3264
3379
  * ```typescript
3265
- * import {getTemplate} from '@verdocs/js-sdk/Templates';
3380
+ * import {deleteOrganizationMember} from '@verdocs/js-sdk';
3266
3381
  *
3267
- * const template = await getTemplate((VerdocsEndpoint.getDefault(), '83da3d70-7857-4392-b876-c4592a304bc9');
3382
+ * await deleteOrganizationMember(VerdocsEndpoint.getDefault(), 'PROFILEID'});
3268
3383
  * ```
3269
3384
  *
3270
- * @group Templates
3271
- * @api GET /v2/templates/:template_id Get a template. Note that the caller must have at least View access to the template.
3272
- * @apiSuccess ITemplate . The requested template
3385
+ * @group Organization Members
3386
+ * @api DELETE /v2/organization-members/:profile_id Delete a member from the organization
3387
+ * @apiSuccess string . Success
3273
3388
  */
3274
- const getTemplate = (endpoint, templateId) => {
3275
- return endpoint.api //
3276
- .get(`/v2/templates/${templateId}`)
3277
- .then((r) => {
3278
- const template = r.data;
3279
- // Post-process the template to upgrade to new data fields
3280
- if (!template.documents && template.template_documents) {
3281
- template.documents = template.template_documents;
3282
- }
3283
- template.documents?.forEach((document) => {
3284
- if (!document.order) {
3285
- document.order = 0;
3286
- }
3287
- if (document.page_numbers) {
3288
- document.pages = document.page_numbers;
3289
- }
3290
- });
3291
- // Temporary upgrade from legacy app
3292
- template.fields?.forEach((field) => {
3293
- if (field.setting) {
3294
- field.settings = field.setting;
3295
- }
3296
- });
3297
- return template;
3298
- });
3299
- };
3300
- const ALLOWED_CREATE_FIELDS = [
3301
- 'name',
3302
- 'is_personal',
3303
- 'is_public',
3304
- 'sender',
3305
- 'description',
3306
- 'roles',
3307
- 'fields',
3308
- ];
3389
+ const deleteOrganizationMember = (endpoint, profileId) => endpoint.api //
3390
+ .delete(`/v2/organization-members/${profileId}`)
3391
+ .then((r) => r.data);
3309
3392
  /**
3310
- * Create a template.
3393
+ * Update a member.
3311
3394
  *
3312
3395
  * ```typescript
3313
- * import {createTemplate} from '@verdocs/js-sdk/Templates';
3396
+ * import {updateOrganizationMember} from '@verdocs/js-sdk';
3314
3397
  *
3315
- * const newTemplate = await createTemplate((VerdocsEndpoint.getDefault(), {...});
3398
+ * const result = await updateOrganizationMember(VerdocsEndpoint.getDefault(), 'PROFILEID', {roles:['member']});
3316
3399
  * ```
3317
3400
  *
3318
- * @group Templates
3319
- * @api POST /v2/templates Create a template
3320
- * @apiBody string name Template name
3321
- * @apiBody string description? Optional description
3322
- * @apiBody TTemplateVisibility visibility? Visibility setting
3323
- * @apiBody boolean is_personal? Deprecated. If true, the template is personal and can only be seen by the caller. (Use "visibility" for new calls.)
3324
- * @apiBody boolean is_public? Deprecated. If true, the template is public and can be seen by anybody. (Use "visibility" for new calls.)
3325
- * @apiBody TTemplateSender sender? Who may send envelopes using this template
3326
- * @apiBody number initial_reminder? Delay (in seconds) before the first reminder is sent (min: 4hrs). Set to 0 or null to disable.
3327
- * @apiBody number followup_reminders? Delay (in seconds) before the subsequent reminders are sent (min: 12hrs). Set to 0 or null to disable.
3328
- * @apiBody array(items:object) documents? Optional list of documents to attach to the template
3329
- * @apiBody array(items:IRole) roles? Optional list of roles to create. Note that if roles are not included in the request, fields will be ignored.
3330
- * @apiBody array(fields:ITemplateField) fields? Optional list of fields to create. Note that if fields that do not match a role will be ignored.
3331
- * @apiSuccess ITemplate . The newly-created template
3401
+ * @group Organization Members
3402
+ * @api PATCH /v2/organization-members/:profile_id Update an organization member.
3403
+ * @apiBody array(items:TRole) roles URL to send Webhook events to. An empty or invalid URL will disable Webhook calls.
3404
+ * @apiBody string first_name Set to true to enable Webhooks calls.
3405
+ * @apiBody string last_name Record<TWebhookEvent, boolean> map of events to enable/disable.
3406
+ * @apiSuccess array(items:IProfile) . List of caller's current organization's members
3332
3407
  */
3333
- const createTemplate = (endpoint, params, onUploadProgress) => {
3334
- const options = {
3335
- timeout: 120000,
3336
- onUploadProgress: (event) => {
3337
- const total = event.total || 1;
3338
- const loaded = event.loaded || 0;
3339
- onUploadProgress?.(Math.floor((loaded * 100) / (total)), loaded, total);
3340
- },
3341
- };
3342
- if (params.documents && params.documents[0] instanceof File) {
3343
- const formData = new FormData();
3344
- ALLOWED_CREATE_FIELDS.forEach((allowedKey) => {
3345
- if (params[allowedKey] !== undefined) {
3346
- formData.append(allowedKey, params[allowedKey]);
3347
- }
3348
- });
3349
- params.documents.forEach((file) => {
3350
- formData.append('documents', file, file.name);
3351
- });
3352
- return endpoint.api.post('/v2/templates', formData, options).then((r) => r.data);
3353
- }
3354
- else {
3355
- return endpoint.api.post('/v2/templates', params, options).then((r) => r.data);
3356
- }
3357
- };
3408
+ const updateOrganizationMember = (endpoint, profileId, params) => endpoint.api //
3409
+ .patch(`/v2/organization-members/${profileId}`, params)
3410
+ .then((r) => r.data);
3411
+
3358
3412
  /**
3359
- * Duplicate a template. Creates a complete clone, including all settings (e.g. reminders), fields,
3360
- * roles, and documents.
3413
+ * An Organization is the top level object for ownership for Members, Documents, and Templates.
3414
+ *
3415
+ * NOTE: There is no call specifically to create an organization. Every organization must have
3416
+ * at least one "owner" type member. To create a new organization, call createProfile() with
3417
+ * the desired new orgName to create. The caller will become the first owner of the new org, and
3418
+ * can then invite new members to join as well.
3419
+ *
3420
+ * NOTE: There is no call to delete an organization. For safety, this is a manual process. Please
3421
+ * contact support@verdocs.com if you wish to completely delete an organization and all its records.
3422
+ *
3423
+ * @module
3424
+ */
3425
+ /**
3426
+ * Get an organization by ID. Note that this endpoint will return only a subset of fields
3427
+ * if the caller is not a member of the organization (the public fields).
3361
3428
  *
3362
3429
  * ```typescript
3363
- * import {duplicateTemplate} from '@verdocs/js-sdk/Templates';
3430
+ * import {getOrganization} from '@verdocs/js-sdk';
3364
3431
  *
3365
- * const newTemplate = await duplicateTemplate((VerdocsEndpoint.getDefault(), originalTemplateId, 'My Template Copy');
3432
+ * const organizations = await getOrganization(VerdocsEndpoint.getDefault(), 'ORGID');
3366
3433
  * ```
3367
3434
  *
3368
- * @group Templates
3369
- * @api PUT /v2/templates/:template_id Perform an operation on a template
3370
- * @apiBody string(enum:'duplicate') action Action to perform
3371
- * @apiBody string name? If duplicating the template, a name for the new copy
3372
- * @apiSuccess ITemplate . The newly-copied template
3435
+ * @group Organizations
3436
+ * @api GET /v2/organizations/:organization_id Get organization
3437
+ * @apiSuccess IOrganization . The requested organization. The caller must be a member.
3373
3438
  */
3374
- const duplicateTemplate = (endpoint, templateId, name) => endpoint.api //
3375
- .put(`/v2/templates/${templateId}`, { action: 'duplicate', name })
3439
+ const getOrganization = (endpoint, organizationId) => endpoint.api //
3440
+ .get(`/v2/organizations/${organizationId}`)
3376
3441
  .then((r) => r.data);
3377
3442
  /**
3378
- * Create a template from a Sharepoint asset.
3443
+ * Get an organization's "children".
3379
3444
  *
3380
3445
  * ```typescript
3381
- * import {createTemplateFromSharepoint} from '@verdocs/js-sdk/Templates';
3446
+ * import {getOrganizationChildren} from '@verdocs/js-sdk';
3382
3447
  *
3383
- * const newTemplate = await createTemplateFromSharepoint((VerdocsEndpoint.getDefault(), {...});
3448
+ * const children = await getOrganizationChildren(VerdocsEndpoint.getDefault(), 'ORGID');
3384
3449
  * ```
3385
3450
  *
3386
- * @group Templates
3387
- * @api POST /v2/templates/from-sharepoint Create a template from an asset in Sharepoint
3388
- * @apiBody string name Name for the new template
3389
- * @apiBody string siteId Name for the new template
3390
- * @apiBody string itemId Name for the new template
3391
- * @apiBody string oboToken On-Behalf-Of token for calls to Sharepoint. Should be generated as a short-expiration token with at least Read privileges to the siteId/itemId. This token will be discarded after being used.
3392
- * @apiSuccess ITemplate . The newly-created template
3451
+ * @group Organizations
3452
+ * @api GET /v2/organizations/:organization_id/children Get an organization's children
3453
+ * @apiSuccess IOrganization[] . Any child organizations found.
3393
3454
  */
3394
- const createTemplateFromSharepoint = (endpoint, params) => {
3395
- const options = {
3396
- timeout: 120000,
3397
- };
3398
- return endpoint.api.post('/v2/templates/from-sharepoint', params, options).then((r) => r.data);
3399
- };
3455
+ const getOrganizationChildren = (endpoint, organizationId) => endpoint.api //
3456
+ .get(`/v2/organizations/${organizationId}/children`)
3457
+ .then((r) => r.data);
3400
3458
  /**
3401
- * Update a template.
3459
+ * Get an organization's usage data. If the organization is a parent, usage data for children
3460
+ * will be included as well. The response will be a nested object keyed by organization ID,
3461
+ * with each entry being a dictionary of usageType:count entries.
3402
3462
  *
3403
3463
  * ```typescript
3404
- * import {updateTemplate} from '@verdocs/js-sdk/Templates';
3464
+ * import {getOrganizationUsage} from '@verdocs/js-sdk';
3405
3465
  *
3406
- * const updatedTemplate = await updateTemplate((VerdocsEndpoint.getDefault(), '83da3d70-7857-4392-b876-c4592a304bc9', { name: 'New Name' });
3466
+ * const usage = await getOrganizationUsage(VerdocsEndpoint.getDefault(), 'ORGID');
3407
3467
  * ```
3408
3468
  *
3409
- * @group Templates
3410
- * @api PATCH /v2/templates/:template_id Update a template
3411
- * @apiBody string name? Template name
3412
- * @apiBody string description? Optional description
3413
- * @apiBody TTemplateVisibility visibility? Visibility setting
3414
- * @apiBody boolean is_personal? Deprecated. If true, the template is personal and can only be seen by the caller. (Use "visibility" for new calls.)
3415
- * @apiBody boolean is_public? Deprecated. If true, the template is public and can be seen by anybody. (Use "visibility" for new calls.)
3416
- * @apiBody TTemplateSender sender? Who may send envelopes using this template
3417
- * @apiBody number initial_reminder? Delay (in seconds) before the first reminder is sent (min: 4hrs). Set to 0 or null to disable.
3418
- * @apiBody number followup_reminders? Delay (in seconds) before the subsequent reminders are sent (min: 12hrs). Set to 0 or null to disable.
3419
- * @apiSuccess ITemplate . The updated template
3420
- */
3421
- const updateTemplate = (endpoint, templateId, params) => endpoint.api //
3422
- .patch(`/v2/templates/${templateId}`, params)
3469
+ * @group Organizations
3470
+ * @api GET /v2/organizations/:organization_id/usage Get an organization's usage metrics
3471
+ * @apiSuccess TOrganizationUsage . Usage data grouped by organization ID
3472
+ */
3473
+ const getOrganizationUsage = (endpoint, organizationId, params) => endpoint.api //
3474
+ .get(`/v2/organizations/${organizationId}/usage`, { params })
3423
3475
  .then((r) => r.data);
3424
3476
  /**
3425
- * Delete a template.
3477
+ * Create an organization. The caller will be assigned an "Owner" profile in the new organization,
3478
+ * and it will be set to "current" automatically. A new set of session tokens will be issued to
3479
+ * the caller, and the caller should update their endpoint to use the new tokens.
3426
3480
  *
3427
3481
  * ```typescript
3428
- * import {deleteTemplate} from '@verdocs/js-sdk/Templates';
3482
+ * import {createOrganization} from '@verdocs/js-sdk';
3429
3483
  *
3430
- * await deleteTemplate((VerdocsEndpoint.getDefault(), '83da3d70-7857-4392-b876-c4592a304bc9');
3484
+ * const organization = await createOrganization(VerdocsEndpoint.getDefault(), {name: 'NewOrg'});
3431
3485
  * ```
3432
3486
  *
3433
- * @group Templates
3434
- * @api DELETE /v2/templates/:template_id Delete a template
3435
- * @apiSuccess string . Success
3487
+ * @group Organizations
3488
+ * @api POST /v2/organizations Create organization
3489
+ * @apiDescription The caller will be assigned an "Owner" profile in the new organization, and it will be set to "current" automatically. A new set of session tokens will be issued to the caller, and the caller should update their endpoint to use the new tokens.
3490
+ * @apiBody string name The name of the new organization
3491
+ * @apiBody string parent_id? If set, the new organization will be created as a child of the specified parent organization. The caller must be an admin of the parent organization.
3492
+ * @apiBody string contact_email? Contact email for the new organization
3493
+ * @apiBody string url? URL for the new organization
3494
+ * @apiBody string full_logo_url? URL of a large-format PNG logo
3495
+ * @apiBody string thumbnail_url? URL of a small-format (square is recommended) PNG logo
3496
+ * @apiBody string primary_color? URL of a small-format (square is recommended) PNG logo
3497
+ * @apiBody string secondary_color? URL of a small-format (square is recommended) PNG logo
3498
+ * @apiSuccess IAuthenticateResponse . Authentication credentials for user in the new organization. The user will be made an Owner automatically.
3436
3499
  */
3437
- const deleteTemplate = (endpoint, templateId) => endpoint.api //
3438
- .delete(`/v2/templates/${templateId}`)
3500
+ const createOrganization = (endpoint, params) => endpoint.api //
3501
+ .post(`/v2/organizations`, params)
3439
3502
  .then((r) => r.data);
3440
3503
  /**
3441
- * Toggle the template star for a template.
3504
+ * Update an organization. This can only be called by an admin or owner.
3442
3505
  *
3443
3506
  * ```typescript
3444
- * import {toggleTemplateStar} from '@verdocs/js-sdk/Templates';
3507
+ * import {updateOrganization} from '@verdocs/js-sdk';
3445
3508
  *
3446
- * await toggleTemplateStar((VerdocsEndpoint.getDefault(), '83da3d70-7857-4392-b876-c4592a304bc9');
3509
+ * const organizations = await updateOrganization(VerdocsEndpoint.getDefault(), organizationId, {name:'ORGNAME'});
3447
3510
  * ```
3448
3511
  *
3449
- * @group Templates
3450
- * @api POST /v2/templates/:template_id/star Star or unstar a template (toggle state)
3451
- * @apiSuccess ITemplate . Success
3512
+ * @group Organizations
3513
+ * @api PATCH /v2/organizations/:organization_id Update organization
3514
+ * @apiBody string name The name of the new organization
3515
+ * @apiBody string contact_email? Contact email for the new organization
3516
+ * @apiBody string url? URL for the new organization
3517
+ * @apiBody string full_logo_url? URL of a large-format PNG logo
3518
+ * @apiBody string thumbnail_url? URL of a small-format (square is recommended) PNG logo
3519
+ * @apiBody string primary_color? URL of a small-format (square is recommended) PNG logo
3520
+ * @apiBody string secondary_color? URL of a small-format (square is recommended) PNG logo
3521
+ * @apiSuccess IOrganization . The details for the updated organization
3452
3522
  */
3453
- const toggleTemplateStar = (endpoint, templateId) => endpoint.api //
3454
- .post(`/v2/templates/${templateId}/stars/toggle`)
3523
+ const updateOrganization = (endpoint, organizationId, params) => endpoint.api //
3524
+ .patch(`/v2/organizations/${organizationId}`, params)
3455
3525
  .then((r) => r.data);
3456
-
3457
3526
  /**
3458
- * A TemplateDocument represents a PDF or other attachment in a Template.
3527
+ * Delete an organization. This can only be called by an owner. Inclusion of the organization ID to delete
3528
+ * is just a safety check. The caller may only delete the organization they have currently selected.
3459
3529
  *
3460
- * @module
3530
+ * ```typescript
3531
+ * import {deleteOrganization} from '@verdocs/js-sdk';
3532
+ *
3533
+ * const newSession = await deleteOrganization(VerdocsEndpoint.getDefault(), organizationId);
3534
+ * ```
3535
+ *
3536
+ * @group Organizations
3537
+ * @api DELETE /v2/organizations/:organization_id Delete organization
3538
+ * @apiSuccess IAuthenticateResponse . If the caller is a member of another organization, authentication credentials for the next organization available. If not, this will be null and the caller will be logged out.
3461
3539
  */
3540
+ const deleteOrganization = (endpoint, organizationId) => endpoint.api //
3541
+ .delete(`/v2/organizations/${organizationId}`)
3542
+ .then((r) => r.data);
3462
3543
  /**
3463
- * Create a Document for a particular Template.
3544
+ * Update the organization's full or thumbnail logo. This can only be called by an admin or owner.
3464
3545
  *
3465
3546
  * ```typescript
3466
- * import {TemplateDocument} from '@verdocs/js-sdk/Templates';
3547
+ * import {updateOrganizationLogo} from '@verdocs/js-sdk';
3467
3548
  *
3468
- * await TemplateDocument.createDocument((VerdocsEndpoint.getDefault(), templateID, params);
3549
+ * await updateOrganizationLogo((VerdocsEndpoint.getDefault(), organizationId, file);
3469
3550
  * ```
3470
3551
  *
3471
- * @group Template Documents
3472
- * @api POST /v2/templates/:template_id/documents Attach a document to a template
3473
- * @apiBody string(format:binary) file Document file to attach. The file name will automatically be used as the document name.
3474
- * @apiSuccess ITemplateDocument . Template document
3552
+ * @group Organizations
3553
+ * @api PATCH /v2/organizations/:organization_id Update organization full or thumbnail logo.
3554
+ * @apiBody image/png logo? Form-url-encoded file to upload
3555
+ * @apiBody image/png thumbnail? Form-url-encoded file to upload
3556
+ * @apiSuccess IOrganization . The updated organization.
3475
3557
  */
3476
- const createTemplateDocument = (endpoint, file, onUploadProgress) => {
3558
+ const updateOrganizationLogo = (endpoint, organizationId, file, onUploadProgress) => {
3477
3559
  const formData = new FormData();
3478
- formData.append('document', file, file.name);
3560
+ formData.append('logo', file, file.name);
3479
3561
  return endpoint.api //
3480
- .post(`/v2/template-documents`, formData, {
3562
+ .patch(`/v2/organizations/${organizationId}`, formData, {
3481
3563
  timeout: 120000,
3482
3564
  onUploadProgress: (event) => {
3483
3565
  const total = event.total || 1;
@@ -3488,118 +3570,108 @@ const createTemplateDocument = (endpoint, file, onUploadProgress) => {
3488
3570
  .then((r) => r.data);
3489
3571
  };
3490
3572
  /**
3491
- * Delete a specific Document.
3573
+ * Update the organization's thumbnail. This can only be called by an admin or owner.
3492
3574
  *
3493
3575
  * ```typescript
3494
- * import {TemplateDocument} from '@verdocs/js-sdk/Templates';
3576
+ * import {updateOrganizationThumbnail} from '@verdocs/js-sdk';
3495
3577
  *
3496
- * await TemplateDocument.deleteDocument((VerdocsEndpoint.getDefault(), templateID, documentID);
3578
+ * await updateOrganizationThumbnail((VerdocsEndpoint.getDefault(), organizationId, file);
3497
3579
  * ```
3498
- *
3499
- * @group Template Documents
3500
- * @api DELETE /v2/templates/:temlate_id/documents/:document_id Delete a template document
3501
- * @apiSuccess string . Success
3502
3580
  */
3503
- const deleteTemplateDocument = (endpoint, templateId, documentId) => endpoint.api //
3504
- .delete(`/v2/templates/${templateId}/documents/${documentId}`)
3505
- .then((r) => r.data);
3581
+ const updateOrganizationThumbnail = (endpoint, organizationId, file, onUploadProgress) => {
3582
+ const formData = new FormData();
3583
+ formData.append('thumbnail', file, file.name);
3584
+ return endpoint.api //
3585
+ .patch(`/v2/organizations/${organizationId}`, formData, {
3586
+ timeout: 120000,
3587
+ onUploadProgress: (event) => {
3588
+ const total = event.total || 1;
3589
+ const loaded = event.loaded || 0;
3590
+ onUploadProgress?.(Math.floor((loaded * 100) / (total)), loaded, total);
3591
+ },
3592
+ })
3593
+ .then((r) => r.data);
3594
+ };
3595
+ const getEntitlements = async (endpoint) => endpoint.api.get(`/v2/organizations/entitlements`).then((r) => r.data);
3506
3596
  /**
3507
- * Get all metadata for a template document. Note that when called by non-creators (e.g. Org Collaborators)
3508
- * this will return only the **metadata** the caller is allowed to view.
3597
+ * Largely intended to be used internally by Web SDK components but may be informative for other cases.
3598
+ * Entitlements are feature grants such as "ID-based KBA" that require paid contracts to enable, typically
3599
+ * because the underlying services that support them are fee-based. Entitlements may run concurrently,
3600
+ * and may have different start/end dates e.g. "ID-based KBA" may run 1/1/2026-12/31/2026 while
3601
+ * "SMS Authentication" may be added later and run 6/1/2026-5/31/2027. The entitlements list is a simple
3602
+ * array of enablements and may include entries that are not YET enabled or have now expired.
3509
3603
  *
3510
- * @group Template Documents
3511
- * @api GET /v2/envelope-documents/:id Get envelope document
3512
- * @apiParam string(format: 'uuid') document_id The ID of the document to retrieve.
3513
- * @apiSuccess IEnvelopeDocument . The detailed metadata for the document requested
3514
- */
3515
- const getTemplateDocument = async (endpoint, documentId) => endpoint.api //
3516
- .get(`/v2/template-documents/${documentId}`)
3517
- .then((r) => r.data);
3518
- /**
3519
- * Download a document directly.
3520
- */
3521
- const downloadTemplateDocument = async (endpoint, documentId) => endpoint.api //
3522
- .get(`/v2/template-documents/${documentId}?type=file`, { responseType: 'blob' })
3523
- .then((r) => r.data);
3524
- /**
3525
- * Get an envelope document's metadata, or the document itself. If no "type" parameter is specified,
3526
- * the document metadata is returned. If "type" is set to "file", the document binary content is
3527
- * returned with Content-Type set to the MIME type of the file. If "type" is set to "download", a
3528
- * string download link will be returned. If "type" is set to "preview" a string preview link will
3529
- * be returned. This link expires quickly, so it should be accessed immediately and never shared.
3604
+ * In client code it is helpful to simply know "is XYZ feature currently enabled?" This function collapses
3605
+ * the entitlements list to a simplified dictionary of current/active entitlements. Note that it is async
3606
+ * because it calls the server to obtain the "most current" entitlements list. Existence of an entry in the
3607
+ * resulting dictionary implies the feature is active. Metadata inside each entry can be used to determine
3608
+ * limits, etc.
3530
3609
  *
3531
- * @group Template Documents
3532
- * @api GET /v2/envelope-documents/:document_id Preview, Download, or Link to a Document
3533
- * @apiParam string(format: 'uuid') document_id The ID of the document to retrieve.
3534
- * @apiQuery string(enum:'file'|'download'|'preview') type? Download the file directly, generate a download link, or generate a preview link.
3535
- * @apiSuccess string . The generated link.
3610
+ * ```typescript
3611
+ * import {getActiveEntitlements} from '@verdocs/js-sdk';
3612
+ *
3613
+ * const activeEntitlements = await getActiveEntitlements((VerdocsEndpoint.getDefault());
3614
+ * const isSMSEnabled = !!activeEntitlements.sms_auth;
3615
+ * const monthlyKBALimit = activeEntitlements.kba_auth?.monthly_max;
3616
+ * ```
3536
3617
  */
3537
- const getTemplateDocumentDownloadLink = async (endpoint, _envelopeId, documentId) => endpoint.api //
3538
- .get(`/v2/template-documents/${documentId}?type=download`)
3539
- .then((r) => r.data);
3618
+ const getActiveEntitlements = async (endpoint) => {
3619
+ if (!endpoint.session) {
3620
+ throw new Error('No active session');
3621
+ }
3622
+ const entitlements = await getEntitlements(endpoint);
3623
+ return collapseEntitlements(entitlements);
3624
+ };
3625
+
3540
3626
  /**
3541
- * Get a pre-signed preview link for an Envelope Document. This link expires quickly, so it should
3542
- * be accessed immediately and never shared. Content-Disposition will be set to "inline".
3627
+ * Webhooks are callback triggers from Verdocs to your servers that notify your applications
3628
+ * of various events, such as signing operations.
3629
+ *
3630
+ * @module
3543
3631
  */
3544
- const getTemplateDocumentPreviewLink = async (endpoint, _envelopeId, documentId) => endpoint.api //
3545
- .get(`/v2/envelope-documents/${documentId}?type=preview`)
3546
- .then((r) => r.data);
3547
3632
  /**
3548
- * Get (binary download) a file attached to a Template. It is important to use this method
3549
- * rather than a direct A HREF or similar link to set the authorization headers for the
3550
- * request.
3633
+ * Get the registered Webhook configuration for the caller's organization.
3634
+ *
3635
+ * ```typescript
3636
+ * import {getWebhooks} from '@verdocs/js-sdk';
3637
+ *
3638
+ * await getWebhooks(ORGID, params);
3639
+ * ```
3640
+ *
3641
+ * @group Webhooks
3642
+ * @api GET /v2/webhooks Get organization Webhooks config
3643
+ * @apiSuccess IWebhook . The current Webhooks config for the caller's organization.
3551
3644
  */
3552
- const getTemplateDocumentFile = async (endpoint, templateId, documentId) => endpoint.api //
3553
- .get(`/v2/templates/${templateId}/documents/${documentId}?file=true`, { responseType: 'blob' })
3645
+ const getWebhooks = (endpoint) => endpoint.api //
3646
+ .get(`/v2/webhooks`)
3554
3647
  .then((r) => r.data);
3555
3648
  /**
3556
- * Get (binary download) a file attached to a Template. It is important to use this method
3557
- * rather than a direct A HREF or similar link to set the authorization headers for the
3558
- * request.
3649
+ * Update the registered Webhook configuration for the caller's organization. Note that
3650
+ * Webhooks cannot currently be deleted, but may be easily disabled by setting `active`
3651
+ * to `false` and/or setting the `url` to an empty string.
3652
+ *
3653
+ * ```typescript
3654
+ * import {setWebhooks} from '@verdocs/js-sdk';
3655
+ *
3656
+ * await setWebhooks(ORGID, params);
3657
+ * ```
3658
+ *
3659
+ * @group Webhooks
3660
+ * @api PATCH /v2/webhooks Update organization Webhooks config
3661
+ * @apiDescription Note that Webhooks cannot currently be deleted, but may be easily disabled by setting `active` to `false` and/or setting the `url` to an empty string.
3662
+ * @apiBody string url URL to send Webhook events to. An empty or invalid URL will disable Webhook calls.
3663
+ * @apiBody boolean active Set to true to enable Webhooks calls.
3664
+ * @apiBody object events Record<TWebhookEvent, boolean> map of events to enable/disable.
3665
+ * @apiSuccess IWebhook . The updated Webhooks config for the caller's organization.
3559
3666
  */
3560
- const getTemplateDocumentThumbnail = async (endpoint, templateId, documentId) => endpoint.api //
3561
- .get(`/v2/templates/${templateId}/documents/${documentId}?thumbnail=true`, { responseType: 'blob' })
3667
+ const setWebhooks = (endpoint, params) => endpoint.api //
3668
+ .patch(`/v2/webhooks`, params)
3562
3669
  .then((r) => r.data);
3563
- /**
3564
- * Get a display URI for a given page in a file attached to a template document. These pages are rendered server-side
3565
- * into PNG resources suitable for display in IMG tags although they may be used elsewhere. Note that these are intended
3566
- * for DISPLAY ONLY, are not legally binding documents, and do not contain any encoded metadata from participants. The
3567
- * original asset may be obtained by calling `getTemplateDocumentFile()` or similar.
3568
- */
3569
- const getTemplateDocumentPageDisplayUri = async (endpoint, documentId, page, variant = 'original') => endpoint.api.get(`/v2/template-documents/page-image/${documentId}/${variant}/${page}`, { timeout: 20000 }).then((r) => r.data);
3570
-
3571
- const EMAIL_REGEX = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
3572
- // @see https://www.regextester.com/1978
3573
- const PHONE_REGEX = /((?:\+|00)[17](?: |\-)?|(?:\+|00)[1-9]\d{0,2}(?: |\-)?|(?:\+|00)1\-\d{3}(?: |\-)?)?(0\d|\([0-9]{3}\)|[1-9]{0,3})(?:((?: |\-)[0-9]{2}){4}|((?:[0-9]{2}){4})|((?: |\-)[0-9]{3}(?: |\-)[0-9]{4})|([0-9]{7}))/;
3574
- const URL_REGEX = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/;
3575
- const POSTAL_CODE_REGEX = /^[A-Za-z0-9-\s]{3,10}$/;
3576
- const NUMBER_REGEX = /^\d+$/;
3577
- const DATE_REGEX = /^(\d{4}[-\/]\d{2}[-\/]\d{2})|(\d{2}[-\/]\d{2}[-\/]\d{4})$/;
3578
- const VALIDATORS = {
3579
- email: { regex: EMAIL_REGEX, label: 'Email Address' },
3580
- phone: { regex: PHONE_REGEX, label: 'Phone Number' },
3581
- url: { regex: URL_REGEX, label: 'URL' },
3582
- postal_code: { regex: POSTAL_CODE_REGEX, label: 'Zip/Postal Code' },
3583
- number: { regex: NUMBER_REGEX, label: 'Number' },
3584
- date: { regex: DATE_REGEX, label: 'Date' },
3585
- };
3586
- const isValidInput = (value, validator) => Object.keys(VALIDATORS).includes(validator) && VALIDATORS[validator].regex.test(value);
3587
- /**
3588
- * Get a list of available validators for field inputs. Note that validators always check strings,
3589
- * because that is all a user can enter in an HTML input field. Numeric-format validators should
3590
- * perform any necessary conversions internally. Validators never throw - they just return a boolean.
3591
- * indicating whether the value is valid.
3592
- */
3593
- const getValidators = () => Object.keys(VALIDATORS);
3594
- const isValidEmail = (email) => !!email && EMAIL_REGEX.test(email);
3595
- const isValidPhone = (phone) => !!phone && PHONE_REGEX.test(phone);
3596
- const isValidRoleName = (value, roles) => roles.findIndex((role) => role.name === value) !== -1;
3597
- const TagRegEx = /^[a-zA-Z0-9-]{0,32}$/;
3598
- const isValidTag = (value, tags) => TagRegEx.test(value) || tags.findIndex((tag) => tag === value) !== -1;
3599
3670
 
3600
3671
  exports.ALL_PERMISSIONS = ALL_PERMISSIONS;
3601
3672
  exports.AtoB = AtoB;
3602
3673
  exports.Countries = Countries;
3674
+ exports.DEFAULT_DISCLOSURES = DEFAULT_DISCLOSURES;
3603
3675
  exports.DEFAULT_FIELD_HEIGHTS = DEFAULT_FIELD_HEIGHTS;
3604
3676
  exports.DEFAULT_FIELD_WIDTHS = DEFAULT_FIELD_WIDTHS;
3605
3677
  exports.FIELD_TYPES = FIELD_TYPES;
@@ -3674,6 +3746,7 @@ exports.getEnvelopeDocumentPageDisplayUri = getEnvelopeDocumentPageDisplayUri;
3674
3746
  exports.getEnvelopeDocumentPreviewLink = getEnvelopeDocumentPreviewLink;
3675
3747
  exports.getEnvelopeFile = getEnvelopeFile;
3676
3748
  exports.getEnvelopes = getEnvelopes;
3749
+ exports.getEnvelopesZip = getEnvelopesZip;
3677
3750
  exports.getFieldsForRole = getFieldsForRole;
3678
3751
  exports.getGroup = getGroup;
3679
3752
  exports.getGroups = getGroups;
@@ -3716,6 +3789,8 @@ exports.isCanada = isCanada;
3716
3789
  exports.isDominicanRepublic = isDominicanRepublic;
3717
3790
  exports.isEnvelopeOwner = isEnvelopeOwner;
3718
3791
  exports.isEnvelopeRecipient = isEnvelopeRecipient;
3792
+ exports.isFieldFilled = isFieldFilled;
3793
+ exports.isFieldValid = isFieldValid;
3719
3794
  exports.isFrenchGuiana = isFrenchGuiana;
3720
3795
  exports.isGuadeloupe = isGuadeloupe;
3721
3796
  exports.isMartinique = isMartinique;