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