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