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

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