@verdocs/js-sdk 6.2.0-beta.4 → 6.2.0-beta.5

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