diff --git a/CHANGELOG.md b/CHANGELOG.md index 223c641c..5e5e8ad0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,16 @@ and this project adheres to [Semantic Versioning](http://semver.org). ## [Unreleased] - TBD +## [1.6.0] - 2025-07-22 +### Added +- Document Groups: + - Create Document Group from Template: create a document group directly from a document group template + - Document Group Invites: create, get status, cancel, resend, reassign signer, and list pending invites for a document group signing workflow + - Document Group Embedded: create/cancel embedded invites, generate embedded invite links, and embedded editor/sending links for a document group + - Document Group Recipients: get and update recipients, expiration, reminder and signing order settings for a document group + - Document Group Template Recipients: get and update recipients for a document group template +- Event Subscriptions & Webhooks: + - Create Event Subscription V2: create subscriptions via the `/v2/event-subscriptions` endpoint (`CreateEventSubscriptionV2Async`) ## [1.5.0] - 2025-02-05 ### Added @@ -257,7 +267,11 @@ and this project adheres to [Semantic Versioning](http://semver.org). [create freeform invite]: https://github.com/signnow/SignNow.NET/blob/develop/README.md#create-freeform-invite -[Unreleased]: https://github.com/signnow/SignNow.NET/compare/1.3.0...HEAD +[Unreleased]: https://github.com/signnow/SignNow.NET/compare/1.6.0...HEAD +[1.6.0]: https://github.com/signnow/SignNow.NET/compare/1.5.0...1.6.0 +[1.5.0]: https://github.com/signnow/SignNow.NET/compare/1.4.0...1.5.0 +[1.4.0]: https://github.com/signnow/SignNow.NET/compare/1.3.0...1.4.0 +[1.3.0]: https://github.com/signnow/SignNow.NET/compare/1.2.3...1.3.0 [1.3.0]: https://github.com/signnow/SignNow.NET/compare/1.2.3...1.3.0 [1.2.3]: https://github.com/signnow/SignNow.NET/compare/1.2.2...1.2.3 [1.2.2]: https://github.com/signnow/SignNow.NET/compare/1.2.1...1.2.2 diff --git a/LICENSE b/LICENSE index d81f9f85..6e452d38 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019 airSlate Inc. +Copyright (c) 2003-present SignNow Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index d8f13945..9a548a22 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# signNow.NET +# SignNow API .NET SDK [![Build status][actions build badge]][actions build link] [![Codecov][codecov badge]][codecov link] @@ -6,9 +6,9 @@ [![NuGet Downloads][nuget downloads badge]][nuget downloads link] [![License][license badge]](LICENSE) -## About signNow +## About SignNow -signNow.Net is the official .NET 4.6.2+ and .NET Standard class library for the signNow API. signNow allows you to embed legally-binding e-signatures into your app, CRM or cloud storage. Send documents for signature directly from your website. Invite multiple signers to finalize contracts. Track status of your requests and download signed copies automatically. +SignNow.Net is the official .NET 4.6.2+ and .NET Standard class library for the SignNow API. SignNow allows you to embed legally-binding e-signatures into your app, CRM or cloud storage. Send documents for signature directly from your website. Invite multiple signers to finalize contracts. Track status of your requests and download signed copies automatically. Get your account at @@ -32,11 +32,11 @@ Get your account at - [Get modified documents][get_modified_docs example] - [Get user documents][get_user_docs example] - [Document](#document) - - [Upload a document to signNow][upload_document example] + - [Upload a document to SignNow][upload_document example] - [Upload a document & Extract Fields][upload_doc_extract example] - - [Download a document from signNow][download_signed_doc example] + - [Download a document from SignNow][download_signed_doc example] - [Retrieve a document resource][get_document example] - - [Merge two or more signNow documents into one][merge_documents example] + - [Merge two or more SignNow documents into one][merge_documents example] - [Create a signing link to the document for signature][create_sign_lnk example] - [Create a freeform invite to the document for signature][create_ff_invite example] - [Create a role-based invite to the document for signature][create_rb_invite example] @@ -71,7 +71,7 @@ Get your account at ## Get started -To start using the API you will need an API key. You can get one here . For a full list of accepted parameters, refer to the signNow REST Endpoints API guide: [signNow API Reference][api reference link]. +To start using the API you will need an API key. You can get one here . For a full list of accepted parameters, refer to the SignNow REST Endpoints API guide: [SignNow API Reference][api reference link]. #### API and Application @@ -108,7 +108,7 @@ Install-Package SignNow.Net -Version ## Documentation -Read about the available signNow features in [signNow API Docs][api docs link]. +Read about the available SignNow features in [SignNow API Docs][api docs link]. ## Features @@ -124,7 +124,7 @@ public static class AuthenticationExamples /// /// An example of obtaining an access token via OAuth 2.0 service. /// - /// signNow API base URL. Sandbox: "https://api-eval.signnow.com", Production: "https://api.signnow.com" + /// SignNow API base URL. Sandbox: "https://api-eval.signnow.com", Production: "https://api.signnow.com" /// with Application Client ID and Client Secret /// with User email and User password public static async Task RequestAccessToken(Uri apiBase, CredentialModel clientInfo, CredentialModel userCredentials) @@ -191,15 +191,15 @@ More examples: [Create User][create_user example], [Retrieve User information][g ## Document -### Upload a document to signNow +### Upload a document to SignNow -All the features in signNow require a `document_id`. Once you upload a document to signNow, you get the `document_id` from a successful response. +All the features in SignNow require a `document_id`. Once you upload a document to SignNow, you get the `document_id` from a successful response. ```csharp public static class DocumentExamples { /// - /// Uploads a PDF document to signNow and returns SignNowDocument object. + /// Uploads a PDF document to SignNow and returns SignNowDocument object. /// /// Full qualified path to your PDF file. /// Access token @@ -225,11 +225,11 @@ public static class DocumentExamples More examples: [Upload document][upload_document example], [Upload document with field extract][upload_doc_extract example], [Upload document with complex tags][upload_document_complex_tags] -### Download a document from signNow +### Download a document from SignNow Choose the type of download for your document: -- `PdfOriginal` - download a document in a state it's been when uploaded to signNow, before any changes +- `PdfOriginal` - download a document in a state it's been when uploaded to SignNow, before any changes - `PdfCollapsed` - download a document in PDF file format - `ZipCollapsed` - download a document in ZIP archive - `PdfWithHistory` - download a document with its history, a full log of changes on a separate page. @@ -258,9 +258,9 @@ public static class DocumentExamples More examples: [Download signed document][download_signed_doc example] -### Merge two or more signNow documents into one +### Merge two or more SignNow documents into one -Merges two or more signNow documents into one single PDF file. +Merges two or more SignNow documents into one single PDF file. Steps: @@ -317,9 +317,9 @@ public static partial class DocumentExamples /// /// Response with: /// - /// to sign the document via web browser using signNow credentials. + /// to sign the document via web browser using SignNow credentials. /// - /// to sign the document via web browser without signNow credentials. + /// to sign the document via web browser without SignNow credentials. /// public static async Task CreateSigningLinkToTheDocument(string documentId, Token token) @@ -343,7 +343,7 @@ More examples: [Create signing link][create_sign_lnk example], [Check signing st Simply upload a document and send it for signature right away. No need for adding fields and configuring roles. Just add the signer's email address and customize the message in your email. The document will be available for signing via a button in the email. -Clicking the button opens a document in signNow editor. Signers can click anywhere on a document to add their signature. +Clicking the button opens a document in SignNow editor. Signers can click anywhere on a document to add their signature. Remember: if your document contains even one fillable field, you have to create a role-based invite to get it signed. @@ -353,7 +353,7 @@ public static partial class InviteExamples /// /// Create a freeform invite to the document for signature. /// - /// signNow document you’d like to have signed + /// SignNow document you’d like to have signed /// The email of the invitee. /// Access token /// @@ -389,9 +389,9 @@ Role-based invites allow you to build e-signature workflows. The document can be Upload a document or create one from a template. The document will be available for signing via a button in the email. You can customize email messages for every signer. -Clicking the button opens a document in signNow editor. Signers can sign only the fields designated for their role. +Clicking the button opens a document in SignNow editor. Signers can sign only the fields designated for their role. -You can add more roles either in signNow web app while editing the fields, or with `ISignInvite` interface from SDK while specifying parameters of the `SignerOptions` object. +You can add more roles either in SignNow web app while editing the fields, or with `ISignInvite` interface from SDK while specifying parameters of the `SignerOptions` object. ```csharp public static partial class InviteExamples @@ -399,7 +399,7 @@ public static partial class InviteExamples /// /// Create a role-based invite to the document for signature. /// - /// signNow document with fields you’d like to have signed + /// SignNow document with fields you’d like to have signed /// The email of the invitee. /// Access token /// without any Identity of invite request. @@ -443,7 +443,7 @@ public static partial class InviteExamples /// /// Create an embedded signing invite to the document for signature. /// - /// signNow document you’d like to have signed + /// SignNow document you’d like to have signed /// The email of the invitee. /// Access token /// @@ -529,7 +529,7 @@ More examples: [Get document history][document_history example] ### Create Template by flattening the existing Document -Set required templateName and documentId parameters to create the signNow Template. +Set required templateName and documentId parameters to create the SignNow Template. ```csharp public static class DocumentExamples @@ -602,6 +602,80 @@ public async Task CreateDocumentGroupAsync() ``` More examples: [Document group operations][doc_group_operations example] +### Send a document group invite + +Sends a signing invite to a document group, then checks its status. + +```csharp +public async Task SendDocumentGroupInviteAsync(string documentGroupId, string documentId, string roleId, string roleName) +{ + var signNowContext = new SignNowContext(token); + + var request = new CreateGroupInviteRequest + { + InviteSteps = new List + { + new GroupInviteStep + { + Order = 1, + InviteEmails = new List + { + new GroupInviteEmail { Email = "signer@example.com", Role = roleName, RoleId = roleId, Order = 1 } + }, + InviteActions = new List + { + new GroupInviteAction { Email = "signer@example.com", RoleName = roleName, DocumentId = documentId } + } + } + } + }; + + var invite = await signNowContext.GroupInvites + .CreateGroupInviteAsync(documentGroupId, request) + .ConfigureAwait(false); + + // Check invite status, list pending signers, cancel/resend/reassign as needed + return await signNowContext.GroupInvites + .GetGroupInviteAsync(documentGroupId, invite.Data.Id) + .ConfigureAwait(false); +} +``` +More examples: [Document group invite operations][doc_group_invite_operations example] + +### Create an embedded session for a document group + +Creates an embedded signing invite for a document group and generates links for the embedded editor, embedded sending and embedded signing flows, without sending emails. + +```csharp +public async Task CreateDocumentGroupEmbeddedEditorLinkAsync(string documentGroupId) +{ + var signNowContext = new SignNowContext(token); + + var editorLink = await signNowContext.DocumentGroup + .GenerateDocumentGroupEmbeddedEditorLinkAsync(documentGroupId, new EmbeddedEditorOptions { LinkExpiration = 30 }) + .ConfigureAwait(false); + + return editorLink.Link; +} +``` +More examples: [Document group embedded operations][doc_group_embedded_operations example] + +### Get and update document group recipients + +Gets and updates the recipients, expiration, reminder and signing order settings for a document group. + +```csharp +public async Task GetDocumentGroupRecipientsAsync(string documentGroupId) +{ + var signNowContext = new SignNowContext(token); + + return await signNowContext.DocumentGroup + .GetDocumentGroupRecipientsAsync(documentGroupId) + .ConfigureAwait(false); +} +``` +More examples: [Document group recipients operations][doc_group_recipients_operations example] + ## Folders @@ -647,9 +721,9 @@ This SDK is distributed under the MIT License, see [LICENSE][license link] for m #### API Contact Information -If you have questions about the signNow API, please visit [signNow API Reference][api reference link] or email api@signnow.com. +If you have questions about the SignNow API, please visit [SignNow API Reference][api reference link] or email api@signnow.com. -**Support**: To contact signNow support, please email support@signnow.com or api@signnow.com. +**Support**: To contact SignNow support, please email support@signnow.com or api@signnow.com. **Sales**: For pricing information, please call (800) 831-2050, email sales@signnow.com or visit . @@ -662,7 +736,7 @@ If you have questions about the signNow API, please visit [signNow API Reference [nuget link]: https://www.nuget.org/packages/SignNow.Net [nuget downloads badge]: https://img.shields.io/nuget/dt/SignNow.Net.svg?style=flat-square [nuget downloads link]: https://www.nuget.org/packages/SignNow.Net "NuGet Downloads" -[license badge]: https://img.shields.io/github/license/signnow/SignNow.NET?style=flat-square "signNow .Net SDK License" +[license badge]: https://img.shields.io/github/license/signnow/SignNow.NET?style=flat-square "SignNow .Net SDK License" [license link]: https://github.com/signnow/SignNow.NET/blob/develop/LICENSE [api docs link]: https://docs.signnow.com [api reference link]: https://docs.signnow.com/docs/signnow/reference @@ -708,7 +782,10 @@ If you have questions about the signNow API, please visit [signNow API Reference [create_document example]: https://github.com/signnow/SignNow.NET/blob/develop/SignNow.Net.Examples/Documents/CreateDocumentFromTheTemplate.cs -[doc_group_operations example]: https://github.com/signnow/SignNow.NET/blob/develop/SignNow.Net.Examples/Document%20group/DocumentGroupOperations.cs +[doc_group_operations example]: https://github.com/signnow/SignNow.NET/blob/develop/SignNow.Net.Examples/Document%20group/DocumentGroupOperations.cs +[doc_group_invite_operations example]: https://github.com/signnow/SignNow.NET/blob/develop/SignNow.Net.Examples/Document%20group/DocumentGroupInviteOperations.cs +[doc_group_embedded_operations example]: https://github.com/signnow/SignNow.NET/blob/develop/SignNow.Net.Examples/Document%20group/DocumentGroupEmbeddedOperations.cs +[doc_group_recipients_operations example]: https://github.com/signnow/SignNow.NET/blob/develop/SignNow.Net.Examples/Document%20group/DocumentGroupRecipientsOperations.cs [get_all_folders example]: https://github.com/signnow/SignNow.NET/blob/develop/SignNow.Net.Examples/Folders/GetAllFolders.cs diff --git a/SignNow.Net.Examples/Document group/DocumentGroupEmbeddedOperations.cs b/SignNow.Net.Examples/Document group/DocumentGroupEmbeddedOperations.cs new file mode 100644 index 00000000..b9701bf4 --- /dev/null +++ b/SignNow.Net.Examples/Document group/DocumentGroupEmbeddedOperations.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using SignNow.Net.Model; +using SignNow.Net.Model.Requests; +using SignNow.Net.Model.Requests.DocumentGroup; + +namespace SignNow.Net.Examples +{ + public partial class DocumentGroupOperations + { + [TestMethod] + public async Task CreateAndManageDocumentGroupEmbeddedInviteAsync() + { + // Upload two documents with signature fields so each has a role to invite + await using var fileStream = File.OpenRead(PdfWithSignatureField); + + var documents = new List(); + for (int i = 0; i < 2; i++) + { + var upload = await testContext.Documents + .UploadDocumentWithFieldExtractAsync(fileStream, $"ForDocumentGroupEmbeddedFile-{i}.pdf") + .ConfigureAwait(false); + var doc = await testContext.Documents.GetDocumentAsync(upload.Id).ConfigureAwait(false); + documents.Add(doc); + } + + // Create document group from uploaded documents + var documentGroup = await testContext.DocumentGroup + .CreateDocumentGroupAsync("DocumentGroupEmbeddedInviteTest", documents) + .ConfigureAwait(false); + + // Create an embedded signing invite for the document group, without sending emails + var embeddedInviteRequest = new CreateDocumentGroupEmbeddedInviteRequest + { + Invites = new List + { + new DocumentGroupEmbeddedInviteSigner + { + Email = "embedded-signer1@signnow.com", + RoleId = documents[0].Roles[0].Id, + SigningOrder = 1 + }, + new DocumentGroupEmbeddedInviteSigner + { + Email = "embedded-signer2@signnow.com", + RoleId = documents[1].Roles[0].Id, + SigningOrder = 1 + } + } + }; + + var embeddedInvite = await testContext.DocumentGroup + .CreateDocumentGroupEmbeddedInviteAsync(documentGroup.Id, embeddedInviteRequest) + .ConfigureAwait(false); + + Assert.AreEqual(2, embeddedInvite.InviteData.Count); + Console.WriteLine("Created {0} embedded invites for the document group", embeddedInvite.InviteData.Count); + + // Generate a signing link for the first embedded invite + var embeddedInviteLink = await testContext.DocumentGroup + .GenerateDocumentGroupEmbeddedInviteLinkAsync( + documentGroup.Id, + embeddedInvite.InviteData[0].Id, + new CreateDocumentGroupEmbedLinkOptions { LinkExpiration = 30 }) + .ConfigureAwait(false); + + Console.WriteLine("Embedded invite link: {0}", embeddedInviteLink.Link.AbsoluteUri); + + // Generate a link to open the embedded editor for the document group + var editorLink = await testContext.DocumentGroup + .GenerateDocumentGroupEmbeddedEditorLinkAsync( + documentGroup.Id, + new EmbeddedEditorOptions { LinkExpiration = 30 }) + .ConfigureAwait(false); + + Console.WriteLine("Embedded editor link: {0}", editorLink.Link.AbsoluteUri); + + // Generate a link to open the embedded sending workflow for the document group + var sendingLink = await testContext.DocumentGroup + .GenerateDocumentGroupEmbeddedSendingLinkAsync( + documentGroup.Id, + new EmbeddedSendingOptions { LinkExpiration = 30 }) + .ConfigureAwait(false); + + Console.WriteLine("Embedded sending link: {0}", sendingLink.Link.AbsoluteUri); + + // Cancel all embedded signing invites for the document group + await testContext.DocumentGroup + .CancelDocumentGroupEmbeddedInviteAsync(documentGroup.Id) + .ConfigureAwait(false); + + // Clean up + await testContext.DocumentGroup.DeleteDocumentGroupAsync(documentGroup.Id).ConfigureAwait(false); + foreach (var document in documents) + { + DeleteTestDocument(document.Id); + } + } + } +} diff --git a/SignNow.Net.Examples/Document group/DocumentGroupInviteOperations.cs b/SignNow.Net.Examples/Document group/DocumentGroupInviteOperations.cs new file mode 100644 index 00000000..37f991c5 --- /dev/null +++ b/SignNow.Net.Examples/Document group/DocumentGroupInviteOperations.cs @@ -0,0 +1,133 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using SignNow.Net.Model; +using SignNow.Net.Model.Requests.DocumentGroup; + +namespace SignNow.Net.Examples +{ + public partial class DocumentGroupOperations + { + [TestMethod] + public async Task SendAndManageDocumentGroupInviteAsync() + { + // Upload two documents with signature fields so each has a role to invite + await using var fileStream = File.OpenRead(PdfWithSignatureField); + + var documents = new List(); + for (int i = 0; i < 2; i++) + { + var upload = await testContext.Documents + .UploadDocumentWithFieldExtractAsync(fileStream, $"ForDocumentGroupInviteFile-{i}.pdf") + .ConfigureAwait(false); + var doc = await testContext.Documents.GetDocumentAsync(upload.Id).ConfigureAwait(false); + documents.Add(doc); + } + + // Create document group from uploaded documents + var documentGroup = await testContext.DocumentGroup + .CreateDocumentGroupAsync("SendDocumentGroupInviteTest", documents) + .ConfigureAwait(false); + + // Send a signing invite to the document group, one signer per document + var request = new CreateGroupInviteRequest + { + InviteSteps = new List + { + new GroupInviteStep + { + Order = 1, + InviteEmails = new List + { + new GroupInviteEmail + { + Email = "signer1@signnow.com", + Role = documents[0].Roles[0].Name, + RoleId = documents[0].Roles[0].Id, + Order = 1 + }, + new GroupInviteEmail + { + Email = "signer2@signnow.com", + Role = documents[1].Roles[0].Name, + RoleId = documents[1].Roles[0].Id, + Order = 1 + } + }, + InviteActions = new List + { + new GroupInviteAction + { + Email = "signer1@signnow.com", + RoleName = documents[0].Roles[0].Name, + DocumentId = documents[0].Id + }, + new GroupInviteAction + { + Email = "signer2@signnow.com", + RoleName = documents[1].Roles[0].Name, + DocumentId = documents[1].Id + } + } + } + } + }; + + var invite = await testContext.GroupInvites + .CreateGroupInviteAsync(documentGroup.Id, request) + .ConfigureAwait(false); + + Assert.AreEqual("pending", invite.Data.Status); + Console.WriteLine("Group invite created: {0} with status {1}", invite.Data.Id, invite.Data.Status); + + // Check the status of the group invite + var inviteStatus = await testContext.GroupInvites + .GetGroupInviteAsync(documentGroup.Id, invite.Data.Id) + .ConfigureAwait(false); + + Assert.AreEqual(invite.Data.Id, inviteStatus.Data.Id); + + // List signers who have not yet signed + var pendingInvites = await testContext.GroupInvites + .GetPendingGroupInvitesAsync(documentGroup.Id, invite.Data.Id) + .ConfigureAwait(false); + + Console.WriteLine("Pending signers: {0}", pendingInvites.Data.Count); + + // Reassign the first invite step to a different signer + var stepId = inviteStatus.Data.Steps.First().Id; + await testContext.GroupInvites + .ReassignSignerAsync(documentGroup.Id, invite.Data.Id, stepId, new ReassignSignerRequest + { + NewSigner = new ReassignSignerInfo + { + Email = "reassigned-signer@signnow.com", + RoleName = documents[0].Roles[0].Name + } + }) + .ConfigureAwait(false); + + Console.WriteLine("Reassigned invite step {0} to a new signer", stepId); + + // Resend invite emails to the current signers + await testContext.GroupInvites + .ResendGroupInviteAsync(documentGroup.Id, invite.Data.Id) + .ConfigureAwait(false); + + // Cancel the group invite + await testContext.GroupInvites + .CancelGroupInviteAsync(documentGroup.Id, invite.Data.Id) + .ConfigureAwait(false); + + // Clean up + await testContext.DocumentGroup.DeleteDocumentGroupAsync(documentGroup.Id).ConfigureAwait(false); + foreach (var document in documents) + { + DeleteTestDocument(document.Id); + } + } + } +} diff --git a/SignNow.Net.Examples/Document group/DocumentGroupRecipientsOperations.cs b/SignNow.Net.Examples/Document group/DocumentGroupRecipientsOperations.cs new file mode 100644 index 00000000..07ce46d4 --- /dev/null +++ b/SignNow.Net.Examples/Document group/DocumentGroupRecipientsOperations.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using SignNow.Net.Model; +using SignNow.Net.Model.Requests.DocumentGroup; +using SignNow.Net.Model.Responses; + +namespace SignNow.Net.Examples +{ + public partial class DocumentGroupOperations + { + [TestMethod] + public async Task GetAndUpdateDocumentGroupRecipientsAsync() + { + // Upload two documents with signature fields so each has a role to assign recipients to + await using var fileStream = File.OpenRead(PdfWithSignatureField); + + var documents = new List(); + for (int i = 0; i < 2; i++) + { + var upload = await testContext.Documents + .UploadDocumentWithFieldExtractAsync(fileStream, $"ForDocumentGroupRecipientsFile-{i}.pdf") + .ConfigureAwait(false); + var doc = await testContext.Documents.GetDocumentAsync(upload.Id).ConfigureAwait(false); + documents.Add(doc); + } + + // Create document group from uploaded documents + var documentGroup = await testContext.DocumentGroup + .CreateDocumentGroupAsync("DocumentGroupRecipientsTest", documents) + .ConfigureAwait(false); + + // Get current recipients, expiration, reminder and signing order settings + var recipients = await testContext.DocumentGroup + .GetDocumentGroupRecipientsAsync(documentGroup.Id) + .ConfigureAwait(false); + + Console.WriteLine("Document group has {0} recipient(s) configured", recipients.Data.Recipients?.Count ?? 0); + + // Assign a recipient to each document and configure expiration/reminder/order + var updateRequest = new UpdateDocumentGroupRecipientsRequest + { + Recipients = new List + { + new UpdateDocumentGroupRecipientEntry + { + Name = "Signer 1", + Email = "recipient1@signnow.com", + Order = 1, + Documents = new List + { + new DocumentGroupRecipientDocument + { + Id = documents[0].Id, + Role = documents[0].Roles[0].Name, + Action = "sign" + } + } + }, + new UpdateDocumentGroupRecipientEntry + { + Name = "Signer 2", + Email = "recipient2@signnow.com", + Order = 2, + Documents = new List + { + new DocumentGroupRecipientDocument + { + Id = documents[1].Id, + Role = documents[1].Roles[0].Name, + Action = "sign" + } + } + } + }, + GeneralExpirationDays = 30, + OrderType = DocumentGroupOrderType.RecipientOrder + }; + + await testContext.DocumentGroup + .UpdateDocumentGroupRecipientsAsync(documentGroup.Id, updateRequest) + .ConfigureAwait(false); + + Console.WriteLine("Document group recipients updated successfully"); + + // Clean up + await testContext.DocumentGroup.DeleteDocumentGroupAsync(documentGroup.Id).ConfigureAwait(false); + foreach (var document in documents) + { + DeleteTestDocument(document.Id); + } + } + } +} diff --git a/SignNow.Net.Test/UnitTests/Service/CreateDocumentGroupFromTemplateTest.cs b/SignNow.Net.Test/UnitTests/Service/CreateDocumentGroupFromTemplateTest.cs new file mode 100644 index 00000000..a9c92c69 --- /dev/null +++ b/SignNow.Net.Test/UnitTests/Service/CreateDocumentGroupFromTemplateTest.cs @@ -0,0 +1,50 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Newtonsoft.Json; +using SignNow.Net.Model; +using SignNow.Net.Model.Requests.DocumentGroup; +using SignNow.Net.Model.Responses; +using SignNow.Net.Service; +using UnitTests; + +namespace UnitTests +{ + [TestClass] + public class CreateDocumentGroupFromTemplateTest : SignNowTestBase + { + private const string TemplateGroupId = "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4a1b2c3d4"; + private const string DocumentGroupId = "b1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4a1b2c3d4"; + + [TestMethod] + public async Task CreateDocumentGroupFromTemplateAsync_Success() + { + var expectedResponse = new DocumentGroupInfoResponse + { + Data = new DocumentGroupData + { + Id = DocumentGroupId, + Name = "Group from template", + State = "active", + Created = new DateTime(2026, 1, 1, 0, 0, 0, DateTimeKind.Utc), + Updated = new DateTime(2026, 1, 1, 0, 0, 0, DateTimeKind.Utc) + } + }; + + var mockClient = SignNowClientMock(JsonConvert.SerializeObject(expectedResponse)); + var service = new DocumentGroupService(ApiBaseUrl, new Token(), mockClient); + var request = new CreateDocumentGroupFromTemplateRequest + { + GroupName = "Group from template" + }; + + var result = await service.CreateDocumentGroupFromTemplateAsync(TemplateGroupId, request, CancellationToken.None); + + Assert.IsNotNull(result); + Assert.IsNotNull(result.Data); + Assert.AreEqual(DocumentGroupId, result.Data.Id); + Assert.AreEqual("Group from template", result.Data.Name); + } + } +} diff --git a/SignNow.Net.Test/UnitTests/Service/DocumentGroupEmbeddedTest.cs b/SignNow.Net.Test/UnitTests/Service/DocumentGroupEmbeddedTest.cs new file mode 100644 index 00000000..8bd3153a --- /dev/null +++ b/SignNow.Net.Test/UnitTests/Service/DocumentGroupEmbeddedTest.cs @@ -0,0 +1,108 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Newtonsoft.Json; +using SignNow.Net.Model; +using SignNow.Net.Model.Requests; +using SignNow.Net.Model.Requests.DocumentGroup; +using SignNow.Net.Model.Responses; +using SignNow.Net.Service; +using UnitTests; + +namespace UnitTests +{ + [TestClass] + public class DocumentGroupEmbeddedTest : SignNowTestBase + { + private const string GroupId = "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4a1b2c3d4"; + private const string EmbeddedInviteId = "b1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4a1b2c3d4"; + + private static string MakeLinkJson(string url = "https://app.signnow.com/group/abc") + => JsonConvert.SerializeObject(new { data = new { link = url } }); + + [TestMethod] + public async Task CreateDocumentGroupEmbeddedInviteAsync_Success() + { + var expectedJson = JsonConvert.SerializeObject(new + { + data = new[] + { + new { id = "inviteId123", email = "s@example.com", role_id = "r1", order = 1, status = "pending" } + } + }); + var mockClient = SignNowClientMock(expectedJson); + var service = new DocumentGroupService(ApiBaseUrl, new Token(), mockClient); + var request = new CreateDocumentGroupEmbeddedInviteRequest + { + Invites = new List + { + new DocumentGroupEmbeddedInviteSigner + { + Email = "s@example.com", + RoleId = "r1", + SigningOrder = 1 + } + } + }; + + var result = await service.CreateDocumentGroupEmbeddedInviteAsync(GroupId, request, CancellationToken.None); + + Assert.IsNotNull(result); + Assert.AreEqual(1, result.InviteData.Count); + } + + [TestMethod] + public async Task GenerateDocumentGroupEmbeddedInviteLinkAsync_Success() + { + var mockClient = SignNowClientMock(MakeLinkJson()); + var service = new DocumentGroupService(ApiBaseUrl, new Token(), mockClient); + var options = new CreateDocumentGroupEmbedLinkOptions { LinkExpiration = 30 }; + + var result = await service.GenerateDocumentGroupEmbeddedInviteLinkAsync(GroupId, EmbeddedInviteId, options, CancellationToken.None); + + Assert.IsNotNull(result); + Assert.IsInstanceOfType(result.Link, typeof(Uri)); + } + + [TestMethod] + public async Task CancelDocumentGroupEmbeddedInviteAsync_Success() + { + var mockClient = SignNowClientMock("{}"); + var service = new DocumentGroupService(ApiBaseUrl, new Token(), mockClient); + + await service.CancelDocumentGroupEmbeddedInviteAsync(GroupId, CancellationToken.None); + } + + [TestMethod] + public async Task GenerateDocumentGroupEmbeddedEditorLinkAsync_Success() + { + var mockClient = SignNowClientMock(MakeLinkJson("https://app.signnow.com/group-editor/abc")); + var service = new DocumentGroupService(ApiBaseUrl, new Token(), mockClient); + + var result = await service.GenerateDocumentGroupEmbeddedEditorLinkAsync( + GroupId, + new EmbeddedEditorOptions { LinkExpiration = 30 }, + CancellationToken.None); + + Assert.IsNotNull(result); + Assert.IsInstanceOfType(result.Link, typeof(Uri)); + } + + [TestMethod] + public async Task GenerateDocumentGroupEmbeddedSendingLinkAsync_Success() + { + var mockClient = SignNowClientMock(MakeLinkJson("https://app.signnow.com/group-sending/abc")); + var service = new DocumentGroupService(ApiBaseUrl, new Token(), mockClient); + + var result = await service.GenerateDocumentGroupEmbeddedSendingLinkAsync( + GroupId, + new EmbeddedSendingOptions { LinkExpiration = 30 }, + CancellationToken.None); + + Assert.IsNotNull(result); + Assert.IsInstanceOfType(result.Link, typeof(Uri)); + } + } +} diff --git a/SignNow.Net.Test/UnitTests/Service/DocumentGroupInviteTest.cs b/SignNow.Net.Test/UnitTests/Service/DocumentGroupInviteTest.cs new file mode 100644 index 00000000..fdc87218 --- /dev/null +++ b/SignNow.Net.Test/UnitTests/Service/DocumentGroupInviteTest.cs @@ -0,0 +1,143 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Newtonsoft.Json; +using SignNow.Net.Model; +using SignNow.Net.Model.Requests.DocumentGroup; +using SignNow.Net.Model.Responses; +using SignNow.Net.Service; +using UnitTests; + +namespace UnitTests +{ + [TestClass] + public class DocumentGroupInviteTest : SignNowTestBase + { + private const string GroupId = "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4a1b2c3d4"; + private const string InviteId = "b1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4a1b2c3d4"; + private const string StepId = "c1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4a1b2c3d4"; + + private static string MakeInviteResponseJson() + => JsonConvert.SerializeObject(new + { + data = new + { + id = InviteId, + status = "pending", + created = 1700000000, + updated = 1700000000, + steps = new[] { new { id = StepId, order = 1, status = "pending" } } + } + }); + + [TestMethod] + public async Task CreateGroupInviteAsync_Success() + { + var mockClient = SignNowClientMock(MakeInviteResponseJson()); + var service = new DocumentGroupService(ApiBaseUrl, new Token(), mockClient); + var request = new CreateGroupInviteRequest + { + InviteSteps = new List + { + new GroupInviteStep + { + Order = 1, + InviteEmails = new List + { + new GroupInviteEmail + { + Email = "signer@example.com", + Role = "Signer 1", + RoleId = "roleId123", + Order = 1 + } + }, + InviteActions = new List + { + new GroupInviteAction + { + Email = "signer@example.com", + RoleName = "Signer 1", + DocumentId = "docId123" + } + } + } + } + }; + + var result = await service.CreateGroupInviteAsync(GroupId, request, CancellationToken.None); + + Assert.IsNotNull(result); + Assert.IsNotNull(result.Data); + Assert.AreEqual("pending", result.Data.Status); + } + + [TestMethod] + public async Task GetGroupInviteAsync_Success() + { + var mockClient = SignNowClientMock(MakeInviteResponseJson()); + var service = new DocumentGroupService(ApiBaseUrl, new Token(), mockClient); + + var result = await service.GetGroupInviteAsync(GroupId, InviteId, CancellationToken.None); + + Assert.IsNotNull(result); + Assert.AreEqual(InviteId, result.Data.Id); + } + + [TestMethod] + public async Task CancelGroupInviteAsync_Success() + { + var mockClient = SignNowClientMock("{}"); + var service = new DocumentGroupService(ApiBaseUrl, new Token(), mockClient); + + await service.CancelGroupInviteAsync(GroupId, InviteId, CancellationToken.None); + } + + [TestMethod] + public async Task ResendGroupInviteAsync_Success() + { + var mockClient = SignNowClientMock("{}"); + var service = new DocumentGroupService(ApiBaseUrl, new Token(), mockClient); + + await service.ResendGroupInviteAsync(GroupId, InviteId, CancellationToken.None); + } + + [TestMethod] + public async Task GetPendingGroupInvitesAsync_Success() + { + var pendingJson = JsonConvert.SerializeObject(new + { + data = new[] + { + new { id = "pending1", email = "signer@example.com", role = "Signer 1", status = "pending" } + } + }); + var mockClient = SignNowClientMock(pendingJson); + var service = new DocumentGroupService(ApiBaseUrl, new Token(), mockClient); + + var result = await service.GetPendingGroupInvitesAsync(GroupId, InviteId, CancellationToken.None); + + Assert.IsNotNull(result); + Assert.AreEqual(1, result.Data.Count); + Assert.AreEqual("signer@example.com", result.Data[0].Email); + } + + [TestMethod] + public async Task ReassignSignerAsync_Success() + { + var mockClient = SignNowClientMock("{}"); + var service = new DocumentGroupService(ApiBaseUrl, new Token(), mockClient); + var request = new ReassignSignerRequest + { + NewSigner = new ReassignSignerInfo + { + Email = "new.signer@example.com", + RoleName = "Signer 1" + } + }; + + await service.ReassignSignerAsync(GroupId, InviteId, StepId, request, CancellationToken.None); + } + } +} diff --git a/SignNow.Net.Test/UnitTests/Service/DocumentGroupRecipientsTest.cs b/SignNow.Net.Test/UnitTests/Service/DocumentGroupRecipientsTest.cs new file mode 100644 index 00000000..965ba268 --- /dev/null +++ b/SignNow.Net.Test/UnitTests/Service/DocumentGroupRecipientsTest.cs @@ -0,0 +1,100 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Newtonsoft.Json; +using SignNow.Net.Model; +using SignNow.Net.Model.Requests.DocumentGroup; +using SignNow.Net.Model.Responses; +using SignNow.Net.Service; +using UnitTests; + +namespace UnitTests +{ + [TestClass] + public class DocumentGroupRecipientsTest : SignNowTestBase + { + private const string DocumentGroupId = "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4a1b2c3d4"; + + [TestMethod] + public async Task GetDocumentGroupRecipientsAsync_Success() + { + var expectedResponse = new DocumentGroupRecipientsResponse + { + Data = new DocumentGroupRecipientsData + { + Recipients = new[] + { + new DocumentGroupRecipient + { + Name = "Signer 1", + Email = "signer@example.com", + Order = 1, + Documents = new[] + { + new DocumentGroupRecipientDocument + { + Id = "docId123", + Role = "Signer", + Action = "sign" + } + } + } + }, + Cc = new[] { "cc@example.com" }, + GeneralExpirationDays = 30, + GeneralReminder = new DocumentGroupTemplateReminder + { + RemindBefore = 5, + RemindAfter = 1, + RemindRepeat = 3 + }, + OrderType = DocumentGroupOrderType.RecipientOrder + } + }; + + var mockClient = SignNowClientMock(JsonConvert.SerializeObject(expectedResponse)); + var service = new DocumentGroupService(ApiBaseUrl, new Token(), mockClient); + + var result = await service.GetDocumentGroupRecipientsAsync(DocumentGroupId, CancellationToken.None); + + Assert.IsNotNull(result); + Assert.IsNotNull(result.Data); + Assert.AreEqual(1, result.Data.Recipients.Count); + Assert.AreEqual("Signer 1", result.Data.Recipients[0].Name); + Assert.AreEqual(DocumentGroupOrderType.RecipientOrder, result.Data.OrderType); + } + + [TestMethod] + public async Task UpdateDocumentGroupRecipientsAsync_Success() + { + var mockClient = SignNowClientMock("{}"); + var service = new DocumentGroupService(ApiBaseUrl, new Token(), mockClient); + var request = new UpdateDocumentGroupRecipientsRequest + { + Recipients = new List + { + new UpdateDocumentGroupRecipientEntry + { + Name = "Signer 1", + Email = "new.signer@example.com", + Order = 1, + Documents = new List + { + new DocumentGroupRecipientDocument + { + Id = "docId123", + Role = "Signer", + Action = "sign" + } + } + } + }, + GeneralExpirationDays = 30, + OrderType = DocumentGroupOrderType.RecipientOrder + }; + + await service.UpdateDocumentGroupRecipientsAsync(DocumentGroupId, request, CancellationToken.None); + } + } +} diff --git a/SignNow.Net.Test/UnitTests/Service/DocumentGroupTemplateRecipientsTest.cs b/SignNow.Net.Test/UnitTests/Service/DocumentGroupTemplateRecipientsTest.cs new file mode 100644 index 00000000..15be9fa6 --- /dev/null +++ b/SignNow.Net.Test/UnitTests/Service/DocumentGroupTemplateRecipientsTest.cs @@ -0,0 +1,86 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Newtonsoft.Json; +using SignNow.Net.Model; +using SignNow.Net.Model.Requests.DocumentGroup; +using SignNow.Net.Model.Responses; +using SignNow.Net.Service; +using UnitTests; + +namespace UnitTests +{ + [TestClass] + public class DocumentGroupTemplateRecipientsTest : SignNowTestBase + { + private const string TemplateGroupId = "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4a1b2c3d4"; + + [TestMethod] + public async Task GetDocumentGroupTemplateRecipientsAsync_Success() + { + var expectedResponse = new DocumentGroupTemplateRecipientsResponse + { + Data = new DocumentGroupTemplateRecipientsData + { + InviteSteps = new[] + { + new DocumentGroupTemplateRecipientStep + { + Order = 1, + Recipients = new[] + { + new DocumentGroupTemplateRecipientRole + { + Id = "roleId123", + Name = "Signer 1", + SigningOrder = 1, + Email = "signer@example.com" + } + } + } + } + } + }; + + var mockClient = SignNowClientMock(JsonConvert.SerializeObject(expectedResponse)); + var service = new DocumentGroupService(ApiBaseUrl, new Token(), mockClient); + + var result = await service.GetDocumentGroupTemplateRecipientsAsync(TemplateGroupId, CancellationToken.None); + + Assert.IsNotNull(result); + Assert.IsNotNull(result.Data); + Assert.AreEqual(1, result.Data.InviteSteps.Count); + Assert.AreEqual("Signer 1", result.Data.InviteSteps[0].Recipients[0].Name); + } + + [TestMethod] + public async Task UpdateDocumentGroupTemplateRecipientsAsync_Success() + { + var mockClient = SignNowClientMock("{}"); + var service = new DocumentGroupService(ApiBaseUrl, new Token(), mockClient); + var request = new UpdateDocumentGroupTemplateRecipientsRequest + { + InviteSteps = new List + { + new DocumentGroupTemplateRecipientStep + { + Order = 1, + Recipients = new[] + { + new DocumentGroupTemplateRecipientRole + { + Id = "roleId123", + Name = "Signer 1", + SigningOrder = 1, + Email = "new.signer@example.com" + } + } + } + } + }; + + await service.UpdateDocumentGroupTemplateRecipientsAsync(TemplateGroupId, request, CancellationToken.None); + } + } +} diff --git a/SignNow.Net.Test/UnitTests/Service/EmbeddedEditorSendingTest.cs b/SignNow.Net.Test/UnitTests/Service/EmbeddedEditorSendingTest.cs new file mode 100644 index 00000000..f8491916 --- /dev/null +++ b/SignNow.Net.Test/UnitTests/Service/EmbeddedEditorSendingTest.cs @@ -0,0 +1,58 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Newtonsoft.Json; +using SignNow.Net.Model; +using SignNow.Net.Model.Requests; +using SignNow.Net.Service; +using UnitTests; + +namespace UnitTests +{ + [TestClass] + public class EmbeddedEditorSendingTest : SignNowTestBase + { + private const string DocumentId = "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4a1b2c3d4"; + + private static string MakeLinkJson(string url = "https://app.signnow.com/editor/abc123") + => JsonConvert.SerializeObject(new { data = new { link = url } }); + + [TestMethod] + public async Task GenerateEmbeddedEditorLinkAsync_Success() + { + var mockClient = SignNowClientMock(MakeLinkJson()); + var service = new UserService(ApiBaseUrl, new Token(), mockClient); + var options = new EmbeddedEditorOptions { LinkExpiration = 30 }; + + var result = await service.GenerateEmbeddedEditorLinkAsync(DocumentId, options, CancellationToken.None); + + Assert.IsNotNull(result); + Assert.IsInstanceOfType(result.Link, typeof(Uri)); + } + + [TestMethod] + public async Task GenerateEmbeddedSendingLinkAsync_Success() + { + var mockClient = SignNowClientMock(MakeLinkJson("https://app.signnow.com/sending/abc123")); + var service = new UserService(ApiBaseUrl, new Token(), mockClient); + var options = new EmbeddedSendingOptions { LinkExpiration = 60 }; + + var result = await service.GenerateEmbeddedSendingLinkAsync(DocumentId, options, CancellationToken.None); + + Assert.IsNotNull(result); + Assert.IsInstanceOfType(result.Link, typeof(Uri)); + } + + [TestMethod] + public async Task GenerateEmbeddedEditorLinkAsync_NoExpiration_Success() + { + var mockClient = SignNowClientMock(MakeLinkJson()); + var service = new UserService(ApiBaseUrl, new Token(), mockClient); + + var result = await service.GenerateEmbeddedEditorLinkAsync(DocumentId, new EmbeddedEditorOptions(), CancellationToken.None); + + Assert.IsNotNull(result); + } + } +} diff --git a/SignNow.Net.Test/UnitTests/Service/EventSubscriptionV2Test.cs b/SignNow.Net.Test/UnitTests/Service/EventSubscriptionV2Test.cs new file mode 100644 index 00000000..1f59f488 --- /dev/null +++ b/SignNow.Net.Test/UnitTests/Service/EventSubscriptionV2Test.cs @@ -0,0 +1,42 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using SignNow.Net.Model; +using SignNow.Net.Model.Requests; +using SignNow.Net.Service; +using UnitTests; + +namespace UnitTests +{ + [TestClass] + public class EventSubscriptionV2Test : SignNowTestBase + { + [TestMethod] + public async Task CreateEventSubscriptionV2Async_Success() + { + var mockClient = SignNowClientMock("{}"); + var service = new EventSubscriptionService(ApiBaseUrl, new Token(), mockClient); + var request = new CreateEventSubscriptionV2( + EventType.DocumentComplete, + "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4a1b2c3d4", + new Uri("https://example.com/webhook")); + + await service.CreateEventSubscriptionV2Async(request, CancellationToken.None); + // No exception = success (void return) + } + + [TestMethod] + public async Task CreateEventSubscriptionV2Async_NullCallback_Throws() + { + await Assert.ThrowsExceptionAsync(() => + { + var _ = new CreateEventSubscriptionV2( + EventType.DocumentComplete, + "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4a1b2c3d4", + null); + return Task.CompletedTask; + }); + } + } +} diff --git a/SignNow.Net/Interfaces/IDocumentGroup.cs b/SignNow.Net/Interfaces/IDocumentGroup.cs index 1060c05b..dace14b7 100644 --- a/SignNow.Net/Interfaces/IDocumentGroup.cs +++ b/SignNow.Net/Interfaces/IDocumentGroup.cs @@ -2,6 +2,7 @@ using System.Threading; using System.Threading.Tasks; using SignNow.Net.Model; +using SignNow.Net.Model.Requests; using SignNow.Net.Model.Requests.DocumentGroup; using SignNow.Net.Model.Responses; @@ -95,6 +96,15 @@ public interface IDocumentGroup /// Task UpdateDocumentGroupTemplateAsync(string documentGroupTemplateId, UpdateDocumentGroupTemplateRequest updateRequest, CancellationToken cancellationToken = default); + /// + /// Creates a document group directly from a document group template. + /// + /// ID of the Document Group Template. + /// Request containing the new document group's name and options. + /// Propagates notification that operations should be canceled. + /// + Task CreateDocumentGroupFromTemplateAsync(string documentGroupTemplateId, CreateDocumentGroupFromTemplateRequest request, CancellationToken cancellationToken = default); + /// /// Creates a document group template from an existing document group. /// @@ -111,5 +121,75 @@ public interface IDocumentGroup /// Propagates notification that operations should be canceled. /// Task GetDocumentGroupTemplatesAsync(GetDocumentGroupTemplatesRequest request, CancellationToken cancellationToken = default); + + /// + /// Gets the list of recipients for a document group template. + /// + /// ID of the Document Group Template. + /// Propagates notification that operations should be canceled. + Task GetDocumentGroupTemplateRecipientsAsync(string templateGroupId, CancellationToken cancellationToken = default); + + /// + /// Updates recipients for a document group template. + /// + /// ID of the Document Group Template. + /// Request containing updated invite steps and recipients. + /// Propagates notification that operations should be canceled. + Task UpdateDocumentGroupTemplateRecipientsAsync(string templateGroupId, UpdateDocumentGroupTemplateRecipientsRequest request, CancellationToken cancellationToken = default); + + /// + /// Gets the recipients, expiration, reminder and signing order settings for a document group. + /// + /// ID of the Document Group. + /// Propagates notification that operations should be canceled. + Task GetDocumentGroupRecipientsAsync(string documentGroupId, CancellationToken cancellationToken = default); + + /// + /// Updates recipients, expiration, reminder and signing order settings for a document group. + /// + /// ID of the Document Group. + /// Request containing updated recipients and signing settings. + /// Propagates notification that operations should be canceled. + Task UpdateDocumentGroupRecipientsAsync(string documentGroupId, UpdateDocumentGroupRecipientsRequest request, CancellationToken cancellationToken = default); + + /// + /// Creates an embedded signing invite for a document group without sending emails. + /// + /// ID of the Document Group. + /// Request containing invite details for each signer. + /// Propagates notification that operations should be canceled. + Task CreateDocumentGroupEmbeddedInviteAsync(string documentGroupId, CreateDocumentGroupEmbeddedInviteRequest request, CancellationToken cancellationToken = default); + + /// + /// Generates a link for an embedded document group signing invite. + /// + /// ID of the Document Group. + /// ID of the embedded invite. + /// Link generation options (auth method, expiration). + /// Propagates notification that operations should be canceled. + Task GenerateDocumentGroupEmbeddedInviteLinkAsync(string documentGroupId, string embeddedInviteId, CreateDocumentGroupEmbedLinkOptions options, CancellationToken cancellationToken = default); + + /// + /// Cancels all embedded signing invites for a document group. + /// + /// ID of the Document Group. + /// Propagates notification that operations should be canceled. + Task CancelDocumentGroupEmbeddedInviteAsync(string documentGroupId, CancellationToken cancellationToken = default); + + /// + /// Generates a link to open the embedded editor for a document group. + /// + /// ID of the Document Group. + /// Options for the embedded editor link. + /// Propagates notification that operations should be canceled. + Task GenerateDocumentGroupEmbeddedEditorLinkAsync(string documentGroupId, EmbeddedEditorOptions options, CancellationToken cancellationToken = default); + + /// + /// Generates a link to open the embedded sending workflow for a document group. + /// + /// ID of the Document Group. + /// Options for the embedded sending link. + /// Propagates notification that operations should be canceled. + Task GenerateDocumentGroupEmbeddedSendingLinkAsync(string documentGroupId, EmbeddedSendingOptions options, CancellationToken cancellationToken = default); } } diff --git a/SignNow.Net/Interfaces/IDocumentGroupInvite.cs b/SignNow.Net/Interfaces/IDocumentGroupInvite.cs new file mode 100644 index 00000000..527cef9b --- /dev/null +++ b/SignNow.Net/Interfaces/IDocumentGroupInvite.cs @@ -0,0 +1,65 @@ +using System.Threading; +using System.Threading.Tasks; +using SignNow.Net.Model.Requests.DocumentGroup; +using SignNow.Net.Model.Responses; + +namespace SignNow.Net.Interfaces +{ + /// + /// Interface for operations with Document Group Invites in signNow. + /// Allows sending, retrieving, canceling, resending, and managing signers + /// for group signing workflows. + /// + public interface IDocumentGroupInvite + { + /// + /// Sends an invite to sign a document group. + /// + /// ID of the Document Group. + /// Invite steps with signers and actions. + /// Propagates notification that operations should be canceled. + Task CreateGroupInviteAsync(string documentGroupId, CreateGroupInviteRequest request, CancellationToken cancellationToken = default); + + /// + /// Gets the status and details of a document group invite. + /// + /// ID of the Document Group. + /// ID of the group invite. + /// Propagates notification that operations should be canceled. + Task GetGroupInviteAsync(string documentGroupId, string inviteId, CancellationToken cancellationToken = default); + + /// + /// Cancels a document group invite. + /// + /// ID of the Document Group. + /// ID of the group invite. + /// Propagates notification that operations should be canceled. + Task CancelGroupInviteAsync(string documentGroupId, string inviteId, CancellationToken cancellationToken = default); + + /// + /// Resends invite emails for a document group. + /// + /// ID of the Document Group. + /// ID of the group invite. + /// Propagates notification that operations should be canceled. + Task ResendGroupInviteAsync(string documentGroupId, string inviteId, CancellationToken cancellationToken = default); + + /// + /// Gets the list of pending invites for a document group. + /// + /// ID of the Document Group. + /// ID of the group invite. + /// Propagates notification that operations should be canceled. + Task GetPendingGroupInvitesAsync(string documentGroupId, string inviteId, CancellationToken cancellationToken = default); + + /// + /// Reassigns a signer in a specific invite step of a document group workflow. + /// + /// ID of the Document Group. + /// ID of the group invite. + /// ID of the invite step to update. + /// New signer details. + /// Propagates notification that operations should be canceled. + Task ReassignSignerAsync(string documentGroupId, string inviteId, string stepId, ReassignSignerRequest request, CancellationToken cancellationToken = default); + } +} diff --git a/SignNow.Net/Interfaces/IEventSubscriptionService.cs b/SignNow.Net/Interfaces/IEventSubscriptionService.cs index 4533f1c1..8af6c48d 100644 --- a/SignNow.Net/Interfaces/IEventSubscriptionService.cs +++ b/SignNow.Net/Interfaces/IEventSubscriptionService.cs @@ -21,6 +21,13 @@ public interface IEventSubscriptionService /// Task CreateEventSubscriptionAsync(CreateEventSubscription createEvent, CancellationToken cancellationToken = default); + /// + /// Creates a new V2 event subscription using the /v2/event-subscriptions endpoint. + /// + /// Event subscription details. + /// Propagates notification that operations should be canceled. + Task CreateEventSubscriptionV2Async(CreateEventSubscriptionV2 createEvent, CancellationToken cancellationToken = default); + /// /// Gets information about all subscriptions to Events made with a specific application. /// diff --git a/SignNow.Net/Interfaces/ISignInvite.cs b/SignNow.Net/Interfaces/ISignInvite.cs index 38ce9bdb..76f161df 100644 --- a/SignNow.Net/Interfaces/ISignInvite.cs +++ b/SignNow.Net/Interfaces/ISignInvite.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using SignNow.Net.Model; using SignNow.Net.Model.Requests; +using SignNow.Net.Model.Responses; namespace SignNow.Net.Interfaces { @@ -69,5 +70,21 @@ public interface ISignInvite /// Propagates notification that operations should be canceled. /// Task ResendEmailInviteAsync(string fieldInviteId, CancellationToken cancellationToken = default); + + /// + /// Generates a link to open the embedded document editor. + /// + /// Identity of the document. + /// Options for the embedded editor link. + /// Propagates notification that operations should be canceled. + Task GenerateEmbeddedEditorLinkAsync(string documentId, EmbeddedEditorOptions options, CancellationToken cancellationToken = default); + + /// + /// Generates a link to open the embedded document sending workflow. + /// + /// Identity of the document. + /// Options for the embedded sending link. + /// Propagates notification that operations should be canceled. + Task GenerateEmbeddedSendingLinkAsync(string documentId, EmbeddedSendingOptions options, CancellationToken cancellationToken = default); } } diff --git a/SignNow.Net/Model/Requests/CreateEventSubscriptionV2.cs b/SignNow.Net/Model/Requests/CreateEventSubscriptionV2.cs new file mode 100644 index 00000000..013096ae --- /dev/null +++ b/SignNow.Net/Model/Requests/CreateEventSubscriptionV2.cs @@ -0,0 +1,28 @@ +using System; +using SignNow.Net.Extensions; +using SignNow.Net.Internal.Helpers; +using SignNow.Net.Model.Requests.EventSubscriptionBase; + +namespace SignNow.Net.Model.Requests +{ + /// + /// Request to create a v2 event subscription (webhook). + /// + public sealed class CreateEventSubscriptionV2 : AbstractEventSubscription + { + /// + /// Constructs a new request. + /// + /// Type of event to subscribe to. + /// ID of the entity the event applies to, e.g. a document, user, document group or template ID. + /// URL SignNow will call when the event occurs. + /// If is null. + public CreateEventSubscriptionV2(EventType eventType, string entityId, Uri callbackUrl) + { + Guard.ArgumentNotNull(callbackUrl, nameof(callbackUrl)); + Event = eventType; + EntityId = entityId.ValidateId(); + Attributes.CallbackUrl = callbackUrl; + } + } +} diff --git a/SignNow.Net/Model/Requests/DocumentGroup/CreateDocumentGroupEmbedLinkOptions.cs b/SignNow.Net/Model/Requests/DocumentGroup/CreateDocumentGroupEmbedLinkOptions.cs new file mode 100644 index 00000000..653b099d --- /dev/null +++ b/SignNow.Net/Model/Requests/DocumentGroup/CreateDocumentGroupEmbedLinkOptions.cs @@ -0,0 +1,24 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace SignNow.Net.Model.Requests.DocumentGroup +{ + /// + /// Options for generating a signing link for an embedded document group invite. + /// + public class CreateDocumentGroupEmbedLinkOptions : JsonHttpContent + { + /// + /// Authentication method the signer must pass before opening the link. + /// + [JsonProperty("auth_method")] + [JsonConverter(typeof(StringEnumConverter))] + public EmbeddedAuthType AuthMethod { get; set; } = EmbeddedAuthType.None; + + /// + /// In how many minutes the link expires. Optional. + /// + [JsonProperty("link_expiration", NullValueHandling = NullValueHandling.Ignore)] + public uint? LinkExpiration { get; set; } + } +} diff --git a/SignNow.Net/Model/Requests/DocumentGroup/CreateDocumentGroupEmbeddedInviteRequest.cs b/SignNow.Net/Model/Requests/DocumentGroup/CreateDocumentGroupEmbeddedInviteRequest.cs new file mode 100644 index 00000000..619d0829 --- /dev/null +++ b/SignNow.Net/Model/Requests/DocumentGroup/CreateDocumentGroupEmbeddedInviteRequest.cs @@ -0,0 +1,49 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace SignNow.Net.Model.Requests.DocumentGroup +{ + /// + /// A signer to add to an embedded signing invite for a document group. + /// + public class DocumentGroupEmbeddedInviteSigner + { + /// + /// Email address of the signer. + /// + [JsonProperty("email")] + public string Email { get; set; } + + /// + /// Unique identifier of the role this signer fulfills. + /// + [JsonProperty("role_id")] + public string RoleId { get; set; } + + /// + /// Position of this signer in the signing order. + /// + [JsonProperty("order")] + public uint SigningOrder { get; set; } + + /// + /// Authentication method the signer must pass before signing. + /// + [JsonProperty("auth_method")] + [JsonConverter(typeof(StringEnumConverter))] + public EmbeddedAuthType AuthMethod { get; set; } = EmbeddedAuthType.None; + } + + /// + /// Request to create an embedded signing invite for a document group without sending emails. + /// + public class CreateDocumentGroupEmbeddedInviteRequest : JsonHttpContent + { + /// + /// Signers to include in the embedded signing invite. + /// + [JsonProperty("invites")] + public IList Invites { get; set; } = new List(); + } +} diff --git a/SignNow.Net/Model/Requests/DocumentGroup/CreateDocumentGroupFromTemplateRequest.cs b/SignNow.Net/Model/Requests/DocumentGroup/CreateDocumentGroupFromTemplateRequest.cs new file mode 100644 index 00000000..7b0a207b --- /dev/null +++ b/SignNow.Net/Model/Requests/DocumentGroup/CreateDocumentGroupFromTemplateRequest.cs @@ -0,0 +1,31 @@ +using System; +using Newtonsoft.Json; +using SignNow.Net.Internal.Helpers.Converters; + +namespace SignNow.Net.Model.Requests.DocumentGroup +{ + /// + /// Request to create a document group directly from a document group template. + /// + public class CreateDocumentGroupFromTemplateRequest : JsonHttpContent + { + /// + /// Name for the new document group. + /// + [JsonProperty("group_name")] + public string GroupName { get; set; } + + /// + /// Client-side timestamp of the request. + /// + [JsonProperty("client_timestamp")] + [JsonConverter(typeof(UnixTimeStampJsonConverter))] + public DateTime ClientTimestamp { get; set; } = DateTime.UtcNow; + + /// + /// ID of the folder to create the document group in. Optional. + /// + [JsonProperty("folder_id", NullValueHandling = NullValueHandling.Ignore)] + public string FolderId { get; set; } + } +} diff --git a/SignNow.Net/Model/Requests/DocumentGroup/CreateGroupInviteRequest.cs b/SignNow.Net/Model/Requests/DocumentGroup/CreateGroupInviteRequest.cs new file mode 100644 index 00000000..38265bb5 --- /dev/null +++ b/SignNow.Net/Model/Requests/DocumentGroup/CreateGroupInviteRequest.cs @@ -0,0 +1,143 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace SignNow.Net.Model.Requests.DocumentGroup +{ + /// + /// A signer to notify by email as part of a document group invite step. + /// + public class GroupInviteEmail + { + /// + /// Email address of the signer to invite. + /// + [JsonProperty("email")] + public string Email { get; set; } + + /// + /// Name of the role this signer fulfills. + /// + [JsonProperty("role")] + public string Role { get; set; } + + /// + /// Unique identifier of the role this signer fulfills. + /// + [JsonProperty("role_id")] + public string RoleId { get; set; } + + /// + /// Position of this signer within the invite step's signing order. + /// + [JsonProperty("order")] + public int Order { get; set; } + + /// + /// Subject line of the invite email. Optional. + /// + [JsonProperty("subject", NullValueHandling = NullValueHandling.Ignore)] + public string Subject { get; set; } + + /// + /// Message body of the invite email. Optional. + /// + [JsonProperty("message", NullValueHandling = NullValueHandling.Ignore)] + public string Message { get; set; } + + /// + /// Number of days before the invite expires. Optional. + /// + [JsonProperty("expiration_days", NullValueHandling = NullValueHandling.Ignore)] + public int? ExpirationDays { get; set; } + + /// + /// Number of days after which a reminder email is sent. Optional. + /// + [JsonProperty("reminder", NullValueHandling = NullValueHandling.Ignore)] + public int? Reminder { get; set; } + } + + /// + /// The signing action a signer must perform on a specific document within a document group invite step. + /// + public class GroupInviteAction + { + /// + /// Email address of the signer performing the action. + /// + [JsonProperty("email")] + public string Email { get; set; } + + /// + /// Name of the role this signer fulfills. + /// + [JsonProperty("role_name")] + public string RoleName { get; set; } + + /// + /// Type of action to perform, e.g. "sign". + /// + [JsonProperty("action")] + public string Action { get; set; } = "sign"; + + /// + /// Unique identifier of the document this action applies to. + /// + [JsonProperty("document_id")] + public string DocumentId { get; set; } + + /// + /// Whether the signer is allowed to reassign this action to someone else. 1 to allow, 0 to disallow. + /// + [JsonProperty("allow_reassign")] + public int AllowReassign { get; set; } + + /// + /// Whether the signer can decline by providing a signature. 1 to allow, 0 to disallow. + /// + [JsonProperty("decline_by_signature")] + public int DeclineBySignature { get; set; } + } + + /// + /// A single step of a document group signing workflow, listing the signers to notify and the actions they must perform. + /// + public class GroupInviteStep + { + /// + /// Position of this step in the overall signing order. + /// + [JsonProperty("order")] + public int Order { get; set; } + + /// + /// Signers to notify by email as part of this step. + /// + [JsonProperty("invite_emails")] + public IList InviteEmails { get; set; } = new List(); + + /// + /// Signing actions to be performed as part of this step. + /// + [JsonProperty("invite_actions")] + public IList InviteActions { get; set; } = new List(); + } + + /// + /// Request to send a signing invite for a document group. + /// + public class CreateGroupInviteRequest : JsonHttpContent + { + /// + /// Ordered signing steps that make up this group invite. + /// + [JsonProperty("invite_steps")] + public IList InviteSteps { get; set; } = new List(); + + /// + /// Email addresses to be copied on invite notifications. Optional. + /// + [JsonProperty("cc", NullValueHandling = NullValueHandling.Ignore)] + public IList Cc { get; set; } + } +} diff --git a/SignNow.Net/Model/Requests/DocumentGroup/ReassignSignerRequest.cs b/SignNow.Net/Model/Requests/DocumentGroup/ReassignSignerRequest.cs new file mode 100644 index 00000000..e4be612d --- /dev/null +++ b/SignNow.Net/Model/Requests/DocumentGroup/ReassignSignerRequest.cs @@ -0,0 +1,34 @@ +using Newtonsoft.Json; + +namespace SignNow.Net.Model.Requests.DocumentGroup +{ + /// + /// The signer to reassign an invite step to. + /// + public class ReassignSignerInfo + { + /// + /// Email address of the new signer. + /// + [JsonProperty("email")] + public string Email { get; set; } + + /// + /// Name of the role the new signer will fulfill. + /// + [JsonProperty("role_name")] + public string RoleName { get; set; } + } + + /// + /// Request to reassign a signer in a specific invite step of a document group workflow. + /// + public class ReassignSignerRequest : JsonHttpContent + { + /// + /// Details of the signer taking over this invite step. + /// + [JsonProperty("new_signer")] + public ReassignSignerInfo NewSigner { get; set; } + } +} diff --git a/SignNow.Net/Model/Requests/DocumentGroup/UpdateDocumentGroupRecipientsRequest.cs b/SignNow.Net/Model/Requests/DocumentGroup/UpdateDocumentGroupRecipientsRequest.cs new file mode 100644 index 00000000..1d4eecab --- /dev/null +++ b/SignNow.Net/Model/Requests/DocumentGroup/UpdateDocumentGroupRecipientsRequest.cs @@ -0,0 +1,56 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using SignNow.Net.Model.Responses; + +namespace SignNow.Net.Model.Requests.DocumentGroup +{ + /// + /// A recipient to assign to a document group, along with the documents they must act on. + /// + public class UpdateDocumentGroupRecipientEntry : DocumentGroupRecipientBase + { + /// + /// Documents and signing actions to assign to this recipient. + /// + [JsonProperty("documents")] + public IList Documents { get; set; } = new List(); + } + + /// + /// Request to update recipients, expiration, reminder and signing order settings for a document group. + /// + public class UpdateDocumentGroupRecipientsRequest : JsonHttpContent + { + /// + /// Recipients to assign to the document group. + /// + [JsonProperty("recipients")] + public IList Recipients { get; set; } = new List(); + + /// + /// Email addresses to be copied on invite notifications. Optional. + /// + [JsonProperty("cc", NullValueHandling = NullValueHandling.Ignore)] + public IList Cc { get; set; } + + /// + /// Default number of days before an invite expires, applied when a recipient doesn't set its own. Optional. + /// + [JsonProperty("general_expiration_days", NullValueHandling = NullValueHandling.Ignore)] + public int? GeneralExpirationDays { get; set; } + + /// + /// Default reminder schedule, applied when a recipient doesn't set its own. Optional. + /// + [JsonProperty("general_reminder", NullValueHandling = NullValueHandling.Ignore)] + public DocumentGroupTemplateReminder GeneralReminder { get; set; } + + /// + /// Signing order mode. Optional. + /// + [JsonProperty("order_type", NullValueHandling = NullValueHandling.Ignore)] + [JsonConverter(typeof(StringEnumConverter))] + public DocumentGroupOrderType? OrderType { get; set; } + } +} diff --git a/SignNow.Net/Model/Requests/DocumentGroup/UpdateDocumentGroupTemplateRecipientsRequest.cs b/SignNow.Net/Model/Requests/DocumentGroup/UpdateDocumentGroupTemplateRecipientsRequest.cs new file mode 100644 index 00000000..35c17ad9 --- /dev/null +++ b/SignNow.Net/Model/Requests/DocumentGroup/UpdateDocumentGroupTemplateRecipientsRequest.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using SignNow.Net.Model.Responses; + +namespace SignNow.Net.Model.Requests.DocumentGroup +{ + /// + /// Request to update the recipients of a document group template. + /// + public class UpdateDocumentGroupTemplateRecipientsRequest : JsonHttpContent + { + /// + /// Ordered signing steps with the roles to assign to this template. + /// + [JsonProperty("invite_steps")] + public IList InviteSteps { get; set; } = new List(); + } +} diff --git a/SignNow.Net/Model/Requests/EmbeddedEditorOptions.cs b/SignNow.Net/Model/Requests/EmbeddedEditorOptions.cs new file mode 100644 index 00000000..bd9df437 --- /dev/null +++ b/SignNow.Net/Model/Requests/EmbeddedEditorOptions.cs @@ -0,0 +1,31 @@ +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace SignNow.Net.Model.Requests +{ + /// + /// Options for generating a link to open the embedded document editor. + /// + public class EmbeddedEditorOptions : JsonHttpContent + { + /// + /// In how many minutes the link expires. Optional. + /// + [JsonProperty("link_expiration", NullValueHandling = NullValueHandling.Ignore)] + public uint? LinkExpiration { get; set; } + + /// + /// URL to redirect to once editing is complete. Optional. + /// + [JsonProperty("redirect_uri", NullValueHandling = NullValueHandling.Ignore)] + public Uri RedirectUri { get; set; } + + /// + /// Whether the redirect should open in the same tab or a new one. Optional. + /// + [JsonProperty("redirect_target", NullValueHandling = NullValueHandling.Ignore)] + [JsonConverter(typeof(StringEnumConverter))] + public RedirectTarget? RedirectTarget { get; set; } + } +} diff --git a/SignNow.Net/Model/Requests/EmbeddedSendingOptions.cs b/SignNow.Net/Model/Requests/EmbeddedSendingOptions.cs new file mode 100644 index 00000000..e1ed41ec --- /dev/null +++ b/SignNow.Net/Model/Requests/EmbeddedSendingOptions.cs @@ -0,0 +1,64 @@ +using System; +using System.Runtime.Serialization; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace SignNow.Net.Model.Requests +{ + /// + /// Embedded sending mode. + /// + [JsonConverter(typeof(StringEnumConverter))] + public enum EmbeddedSendingType + { + /// + /// Recipient can manage the document group (default). + /// + [EnumMember(Value = "manage")] + Manage, + + /// + /// Recipient can edit the document group. + /// + [EnumMember(Value = "edit")] + Edit, + + /// + /// Recipient can send the document group for signing. + /// + [EnumMember(Value = "send-invite")] + SendInvite + } + + /// + /// Options for generating a link to open the embedded sending workflow. + /// + public class EmbeddedSendingOptions : JsonHttpContent + { + /// + /// In how many minutes the link expires. Optional. + /// + [JsonProperty("link_expiration", NullValueHandling = NullValueHandling.Ignore)] + public uint? LinkExpiration { get; set; } + + /// + /// URL to redirect to once sending is complete. Optional. + /// + [JsonProperty("redirect_uri", NullValueHandling = NullValueHandling.Ignore)] + public Uri RedirectUri { get; set; } + + /// + /// Whether the redirect should open in the same tab or a new one. Optional. + /// + [JsonProperty("redirect_target", NullValueHandling = NullValueHandling.Ignore)] + [JsonConverter(typeof(StringEnumConverter))] + public RedirectTarget? RedirectTarget { get; set; } + + /// + /// Embedded sending mode. + /// + [JsonProperty("type")] + [JsonConverter(typeof(StringEnumConverter))] + public EmbeddedSendingType Type { get; set; } = EmbeddedSendingType.Manage; + } +} diff --git a/SignNow.Net/Model/Responses/DocumentGroupEmbeddedInviteResponse.cs b/SignNow.Net/Model/Responses/DocumentGroupEmbeddedInviteResponse.cs new file mode 100644 index 00000000..dba2b894 --- /dev/null +++ b/SignNow.Net/Model/Responses/DocumentGroupEmbeddedInviteResponse.cs @@ -0,0 +1,50 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using SignNow.Net.Model.Responses.GenericResponses; + +namespace SignNow.Net.Model.Responses +{ + /// + /// Details of a single signer added to an embedded document group invite. + /// + public class DocumentGroupEmbeddedInviteData : IdResponse + { + /// + /// Email address of the signer. + /// + [JsonProperty("email")] + public string Email { get; set; } + + /// + /// Unique identifier of the role this signer fulfills. + /// + [JsonProperty("role_id")] + public string RoleId { get; set; } + + /// + /// Position of this signer in the signing order. + /// + [JsonProperty("order")] + public int Order { get; set; } + + /// + /// Current status of the embedded invite, e.g. "pending". + /// + [JsonProperty("status")] + [JsonConverter(typeof(StringEnumConverter))] + public InviteStatus Status { get; set; } + } + + /// + /// Response returned after creating an embedded signing invite for a document group. + /// + public class DocumentGroupEmbeddedInviteResponse + { + /// + /// Signers added to the embedded document group invite. + /// + [JsonProperty("data")] + public IReadOnlyList InviteData { get; internal set; } + } +} diff --git a/SignNow.Net/Model/Responses/DocumentGroupRecipientsResponse.cs b/SignNow.Net/Model/Responses/DocumentGroupRecipientsResponse.cs new file mode 100644 index 00000000..18739c40 --- /dev/null +++ b/SignNow.Net/Model/Responses/DocumentGroupRecipientsResponse.cs @@ -0,0 +1,287 @@ +using System.Collections.Generic; +using System.Runtime.Serialization; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace SignNow.Net.Model.Responses +{ + /// + /// Signing order mode for the recipients of a document group. + /// + [JsonConverter(typeof(StringEnumConverter))] + public enum DocumentGroupOrderType + { + /// + /// All recipients can sign at the same time, in any order. + /// + [EnumMember(Value = "at_the_same_time")] + AtTheSameTime, + + /// + /// Recipients must sign one after another, in the order they were added. + /// + [EnumMember(Value = "recipient_order")] + RecipientOrder, + + /// + /// Recipients must sign according to a custom, explicitly configured order. + /// + [EnumMember(Value = "advanced_order")] + AdvancedOrder + } + + /// + /// Common fields shared by every recipient of a document group, regardless of whether + /// it comes from a "get recipients" response or an "update recipients" request. + /// + public abstract class DocumentGroupRecipientBase + { + /// + /// Name of the recipient. Optional. + /// + [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)] + public string Name { get; set; } + + /// + /// Email address of the recipient. Optional. + /// + [JsonProperty("email", NullValueHandling = NullValueHandling.Ignore)] + public string Email { get; set; } + + /// + /// Position of this recipient in the signing order. + /// + [JsonProperty("order")] + public int Order { get; set; } + } + + /// + /// A group of recipients who share the same email address, e.g. a distribution list. + /// + public class DocumentGroupRecipientEmailGroup + { + /// + /// Unique identifier of the email group. + /// + [JsonProperty("id")] + public string Id { get; set; } + } + + /// + /// The authentication method a recipient must pass before opening the document. + /// + public class DocumentGroupRecipientAuthentication + { + /// + /// Type of authentication required. Optional. + /// + [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)] + [JsonConverter(typeof(StringEnumConverter))] + public AuthenticationType? Type { get; set; } + + /// + /// Value used to authenticate, e.g. the expected password. Optional. + /// + [JsonProperty("value", NullValueHandling = NullValueHandling.Ignore)] + public string Value { get; set; } + + /// + /// Phone number used for phone-based authentication. Optional. + /// + [JsonProperty("phone", NullValueHandling = NullValueHandling.Ignore)] + public string Phone { get; set; } + + /// + /// Authentication delivery method. Optional. + /// + [JsonProperty("method", NullValueHandling = NullValueHandling.Ignore)] + public string Method { get; set; } + } + + /// + /// Per-recipient invite settings such as message text, expiration and reminders. + /// + public class DocumentGroupRecipientAttributes + { + /// + /// Message body of the invite email. Optional. + /// + [JsonProperty("message", NullValueHandling = NullValueHandling.Ignore)] + public string Message { get; set; } + + /// + /// Subject line of the invite email. Optional. + /// + [JsonProperty("subject", NullValueHandling = NullValueHandling.Ignore)] + public string Subject { get; set; } + + /// + /// Number of days before the invite expires. Optional. + /// + [JsonProperty("expiration_days", NullValueHandling = NullValueHandling.Ignore)] + public int? ExpirationDays { get; set; } + + /// + /// Reminder schedule for this recipient. Optional. + /// + [JsonProperty("reminder", NullValueHandling = NullValueHandling.Ignore)] + public DocumentGroupTemplateReminder Reminder { get; set; } + + /// + /// Whether this recipient is allowed to forward the invite to someone else. Optional. + /// + [JsonProperty("allow_forwarding", NullValueHandling = NullValueHandling.Ignore)] + public bool? AllowForwarding { get; set; } + + /// + /// Whether the "decline" button is shown to this recipient. Optional. + /// + [JsonProperty("show_decline_button", NullValueHandling = NullValueHandling.Ignore)] + public bool? ShowDeclineButton { get; set; } + + /// + /// Whether the account owner is themselves the recipient of this step. Optional. + /// + [JsonProperty("i_am_recipient", NullValueHandling = NullValueHandling.Ignore)] + public bool? IAmRecipient { get; set; } + + /// + /// Authentication requirements for this recipient. Optional. + /// + [JsonProperty("authentication", NullValueHandling = NullValueHandling.Ignore)] + public DocumentGroupRecipientAuthentication Authentication { get; set; } + } + + /// + /// A document and the signing action a recipient must perform on it. + /// + public class DocumentGroupRecipientDocument + { + /// + /// Unique identifier of the document. + /// + [JsonProperty("id")] + public string Id { get; set; } + + /// + /// Name of the role this recipient fulfills on the document. + /// + [JsonProperty("role")] + public string Role { get; set; } + + /// + /// Action the recipient must perform, e.g. "sign". + /// + [JsonProperty("action")] + public string Action { get; set; } + } + + /// + /// A recipient of a document group, as returned by the API. + /// + public class DocumentGroupRecipient : DocumentGroupRecipientBase + { + /// + /// Email group this recipient belongs to, if any. Optional. + /// + [JsonProperty("email_group", NullValueHandling = NullValueHandling.Ignore)] + public DocumentGroupRecipientEmailGroup EmailGroup { get; set; } + + /// + /// Invite settings for this recipient, such as message, expiration and reminders. Optional. + /// + [JsonProperty("attributes", NullValueHandling = NullValueHandling.Ignore)] + public DocumentGroupRecipientAttributes Attributes { get; set; } + + /// + /// Documents and signing actions assigned to this recipient. + /// + [JsonProperty("documents")] + public IReadOnlyList Documents { get; set; } + } + + /// + /// A document that has not been assigned to any recipient, along with the action still required on it. + /// + public class DocumentGroupUnmappedSignDocument + { + /// + /// Unique identifier of the document. + /// + [JsonProperty("id")] + public string Id { get; set; } + + /// + /// Name of the role expected to act on the document. + /// + [JsonProperty("role")] + public string Role { get; set; } + + /// + /// Email address of the recipient this document could be assigned to. Optional. + /// + [JsonProperty("recipient", NullValueHandling = NullValueHandling.Ignore)] + public string Recipient { get; set; } + } + + /// + /// Recipients, expiration, reminder and signing order settings for a document group. + /// + public class DocumentGroupRecipientsData + { + /// + /// Recipients currently assigned to the document group. + /// + [JsonProperty("recipients")] + public IReadOnlyList Recipients { get; set; } + + /// + /// Documents in the group that have not been assigned to a recipient yet. Optional. + /// + [JsonProperty("unmapped_documents", NullValueHandling = NullValueHandling.Ignore)] + public IReadOnlyList UnmappedDocuments { get; set; } + + /// + /// Unmapped documents that are still allowed to be signed. Optional. + /// + [JsonProperty("allowed_unmapped_sign_documents", NullValueHandling = NullValueHandling.Ignore)] + public IReadOnlyList AllowedUnmappedSignDocuments { get; set; } + + /// + /// Email addresses copied on invite notifications. Optional. + /// + [JsonProperty("cc", NullValueHandling = NullValueHandling.Ignore)] + public IReadOnlyList Cc { get; set; } + + /// + /// Default number of days before an invite expires, applied when a recipient doesn't set its own. Optional. + /// + [JsonProperty("general_expiration_days", NullValueHandling = NullValueHandling.Ignore)] + public int? GeneralExpirationDays { get; set; } + + /// + /// Default reminder schedule, applied when a recipient doesn't set its own. Optional. + /// + [JsonProperty("general_reminder", NullValueHandling = NullValueHandling.Ignore)] + public DocumentGroupTemplateReminder GeneralReminder { get; set; } + + /// + /// Signing order mode. Optional. + /// + [JsonProperty("order_type", NullValueHandling = NullValueHandling.Ignore)] + [JsonConverter(typeof(StringEnumConverter))] + public DocumentGroupOrderType? OrderType { get; set; } + } + + /// + /// Response returned when getting the recipients of a document group. + /// + public class DocumentGroupRecipientsResponse + { + /// + /// Recipient and signing order data for the document group. + /// + [JsonProperty("data")] + public DocumentGroupRecipientsData Data { get; set; } + } +} diff --git a/SignNow.Net/Model/Responses/DocumentGroupTemplateRecipientsResponse.cs b/SignNow.Net/Model/Responses/DocumentGroupTemplateRecipientsResponse.cs new file mode 100644 index 00000000..48e4e374 --- /dev/null +++ b/SignNow.Net/Model/Responses/DocumentGroupTemplateRecipientsResponse.cs @@ -0,0 +1,89 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace SignNow.Net.Model.Responses +{ + /// + /// A recipient role assigned to a document group template. + /// + public class DocumentGroupTemplateRecipientRole + { + /// + /// Unique identifier of the role. + /// + [JsonProperty("id")] + public string Id { get; set; } + + /// + /// Name of the role, e.g. "Signer 1". + /// + [JsonProperty("name")] + public string Name { get; set; } + + /// + /// Position of this role in the signing order. + /// + [JsonProperty("signing_order")] + public int SigningOrder { get; set; } + + /// + /// Email address currently assigned to this role. Optional. + /// + [JsonProperty("email", NullValueHandling = NullValueHandling.Ignore)] + public string Email { get; set; } + + /// + /// First name of the recipient assigned to this role. Optional. + /// + [JsonProperty("first_name", NullValueHandling = NullValueHandling.Ignore)] + public string FirstName { get; set; } + + /// + /// Last name of the recipient assigned to this role. Optional. + /// + [JsonProperty("last_name", NullValueHandling = NullValueHandling.Ignore)] + public string LastName { get; set; } + } + + /// + /// A single signing step of a document group template, listing the roles assigned to it. + /// + public class DocumentGroupTemplateRecipientStep + { + /// + /// Position of this step in the overall signing order. + /// + [JsonProperty("order")] + public int Order { get; set; } + + /// + /// Roles assigned to this step. + /// + [JsonProperty("recipients")] + public IReadOnlyList Recipients { get; set; } + } + + /// + /// Recipient data for a document group template. + /// + public class DocumentGroupTemplateRecipientsData + { + /// + /// Ordered signing steps configured for this template. + /// + [JsonProperty("invite_steps")] + public IReadOnlyList InviteSteps { get; set; } + } + + /// + /// Response returned when getting the recipients of a document group template. + /// + public class DocumentGroupTemplateRecipientsResponse + { + /// + /// Recipient data for the document group template. + /// + [JsonProperty("data")] + public DocumentGroupTemplateRecipientsData Data { get; set; } + } +} diff --git a/SignNow.Net/Model/Responses/GenericResponses/IdResponse.cs b/SignNow.Net/Model/Responses/GenericResponses/IdResponse.cs index 7e62dc6a..87f2e051 100644 --- a/SignNow.Net/Model/Responses/GenericResponses/IdResponse.cs +++ b/SignNow.Net/Model/Responses/GenericResponses/IdResponse.cs @@ -7,6 +7,9 @@ namespace SignNow.Net.Model.Responses.GenericResponses /// public abstract class IdResponse { + /// + /// Unique identifier of the signNow object. + /// [JsonProperty("id")] public string Id { get; set; } } diff --git a/SignNow.Net/Model/Responses/GroupInviteResponse.cs b/SignNow.Net/Model/Responses/GroupInviteResponse.cs new file mode 100644 index 00000000..c1d6b68c --- /dev/null +++ b/SignNow.Net/Model/Responses/GroupInviteResponse.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using Newtonsoft.Json; +using SignNow.Net.Internal.Helpers.Converters; +using SignNow.Net.Model.Responses.GenericResponses; + +namespace SignNow.Net.Model.Responses +{ + /// + /// A single signing step within a document group invite. + /// + public class GroupInviteStepData + { + /// + /// Unique identifier of the invite step. + /// + [JsonProperty("id")] + public string Id { get; set; } + + /// + /// Position of this step in the signing order. + /// + [JsonProperty("order")] + public int Order { get; set; } + + /// + /// Current status of the invite step, e.g. "pending", "fulfilled". + /// + [JsonProperty("status")] + public string Status { get; set; } + } + + /// + /// Details of a document group invite, including its overall status and per-step progress. + /// + public class GroupInviteData : IdResponse + { + /// + /// Current status of the group invite, e.g. "pending", "fulfilled". + /// + [JsonProperty("status")] + public string Status { get; set; } + + /// + /// Date and time when the group invite was created. + /// + [JsonProperty("created")] + [JsonConverter(typeof(UnixTimeStampJsonConverter))] + public DateTime Created { get; set; } + + /// + /// Date and time when the group invite was last updated. + /// + [JsonProperty("updated")] + [JsonConverter(typeof(UnixTimeStampJsonConverter))] + public DateTime Updated { get; set; } + + /// + /// Signing steps that make up this group invite. + /// + [JsonProperty("steps")] + public IReadOnlyList Steps { get; set; } + } + + /// + /// Response returned after creating or retrieving a document group invite. + /// + public class GroupInviteResponse + { + /// + /// Details of the document group invite. + /// + [JsonProperty("data")] + public GroupInviteData Data { get; set; } + } +} diff --git a/SignNow.Net/Model/Responses/PendingGroupInvitesResponse.cs b/SignNow.Net/Model/Responses/PendingGroupInvitesResponse.cs new file mode 100644 index 00000000..a39b8c41 --- /dev/null +++ b/SignNow.Net/Model/Responses/PendingGroupInvitesResponse.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace SignNow.Net.Model.Responses +{ + /// + /// A signer within a document group invite who has not yet signed. + /// + public class PendingGroupInvite + { + /// + /// Unique identifier of the pending invite. + /// + [JsonProperty("id")] + public string Id { get; set; } + + /// + /// Email address of the signer. + /// + [JsonProperty("email")] + public string Email { get; set; } + + /// + /// Name of the role this signer fulfills. + /// + [JsonProperty("role")] + public string Role { get; set; } + + /// + /// Current status of the pending invite, e.g. "pending". + /// + [JsonProperty("status")] + public string Status { get; set; } + } + + /// + /// Response listing the signers who have not yet signed a document group. + /// + public class PendingGroupInvitesResponse + { + /// + /// Signers who have not yet completed their signing action. + /// + [JsonProperty("data")] + public IReadOnlyList Data { get; set; } + } +} diff --git a/SignNow.Net/Service/DocumentGroupService.cs b/SignNow.Net/Service/DocumentGroupService.cs index 0358593b..23541f26 100644 --- a/SignNow.Net/Service/DocumentGroupService.cs +++ b/SignNow.Net/Service/DocumentGroupService.cs @@ -16,7 +16,7 @@ namespace SignNow.Net.Service { - public class DocumentGroupService : WebClientBase, IDocumentGroup + public class DocumentGroupService : WebClientBase, IDocumentGroup, IDocumentGroupInvite { /// /// Creates new instance of @@ -205,6 +205,25 @@ public async Task UpdateDocumentGroupTemplateAsync(string .ConfigureAwait(false); } + /// + /// If document group template identity is not valid. + public async Task CreateDocumentGroupFromTemplateAsync(string documentGroupTemplateId, CreateDocumentGroupFromTemplateRequest request, CancellationToken cancellationToken = default) + { + Guard.ArgumentNotNull(request, nameof(request)); + Token.TokenType = TokenType.Bearer; + + var requestOptions = new PostHttpRequestOptions + { + RequestUrl = new Uri(ApiBaseUrl, $"/v2/document-group-templates/{documentGroupTemplateId.ValidateId()}/document-group"), + Content = request, + Token = Token + }; + + return await SignNowClient + .RequestAsync(requestOptions, cancellationToken) + .ConfigureAwait(false); + } + /// /// If document group identity is not valid. public async Task CreateDocumentGroupTemplateAsync(string documentGroupId, CreateDocumentGroupTemplateRequest createRequest, CancellationToken cancellationToken = default) @@ -245,5 +264,250 @@ public async Task GetDocumentGroupTemplatesAs .RequestAsync(requestOptions, cancellationToken) .ConfigureAwait(false); } + + /// + public async Task GetDocumentGroupTemplateRecipientsAsync(string templateGroupId, CancellationToken cancellationToken = default) + { + Token.TokenType = TokenType.Bearer; + var requestOptions = new GetHttpRequestOptions + { + RequestUrl = new Uri(ApiBaseUrl, $"/v2/document-group-templates/{templateGroupId.ValidateId()}/recipients"), + Token = Token + }; + + return await SignNowClient + .RequestAsync(requestOptions, cancellationToken) + .ConfigureAwait(false); + } + + /// + public async Task UpdateDocumentGroupTemplateRecipientsAsync(string templateGroupId, UpdateDocumentGroupTemplateRecipientsRequest request, CancellationToken cancellationToken = default) + { + Guard.ArgumentNotNull(request, nameof(request)); + Token.TokenType = TokenType.Bearer; + var requestOptions = new PutHttpRequestOptions + { + RequestUrl = new Uri(ApiBaseUrl, $"/v2/document-group-templates/{templateGroupId.ValidateId()}/recipients"), + Content = request, + Token = Token + }; + + await SignNowClient + .RequestAsync(requestOptions, cancellationToken) + .ConfigureAwait(false); + } + + /// + public async Task GetDocumentGroupRecipientsAsync(string documentGroupId, CancellationToken cancellationToken = default) + { + Token.TokenType = TokenType.Bearer; + var requestOptions = new GetHttpRequestOptions + { + RequestUrl = new Uri(ApiBaseUrl, $"/v2/document-groups/{documentGroupId.ValidateId()}/recipients"), + Token = Token + }; + + return await SignNowClient + .RequestAsync(requestOptions, cancellationToken) + .ConfigureAwait(false); + } + + /// + public async Task UpdateDocumentGroupRecipientsAsync(string documentGroupId, UpdateDocumentGroupRecipientsRequest request, CancellationToken cancellationToken = default) + { + Guard.ArgumentNotNull(request, nameof(request)); + Token.TokenType = TokenType.Bearer; + var requestOptions = new PutHttpRequestOptions + { + RequestUrl = new Uri(ApiBaseUrl, $"/v2/document-groups/{documentGroupId.ValidateId()}/recipients"), + Content = request, + Token = Token + }; + + await SignNowClient + .RequestAsync(requestOptions, cancellationToken) + .ConfigureAwait(false); + } + + /// + public async Task CreateDocumentGroupEmbeddedInviteAsync(string documentGroupId, CreateDocumentGroupEmbeddedInviteRequest request, CancellationToken cancellationToken = default) + { + Guard.ArgumentNotNull(request, nameof(request)); + Token.TokenType = TokenType.Bearer; + var requestOptions = new PostHttpRequestOptions + { + RequestUrl = new Uri(ApiBaseUrl, $"/v2/document-groups/{documentGroupId.ValidateId()}/embedded-invites"), + Content = request, + Token = Token + }; + + return await SignNowClient + .RequestAsync(requestOptions, cancellationToken) + .ConfigureAwait(false); + } + + /// + public async Task GenerateDocumentGroupEmbeddedInviteLinkAsync(string documentGroupId, string embeddedInviteId, CreateDocumentGroupEmbedLinkOptions options, CancellationToken cancellationToken = default) + { + Guard.ArgumentNotNull(options, nameof(options)); + Token.TokenType = TokenType.Bearer; + var requestOptions = new PostHttpRequestOptions + { + RequestUrl = new Uri(ApiBaseUrl, $"/v2/document-groups/{documentGroupId.ValidateId()}/embedded-invites/{embeddedInviteId.ValidateId()}/link"), + Content = options, + Token = Token + }; + + return await SignNowClient + .RequestAsync(requestOptions, cancellationToken) + .ConfigureAwait(false); + } + + /// + public async Task CancelDocumentGroupEmbeddedInviteAsync(string documentGroupId, CancellationToken cancellationToken = default) + { + Token.TokenType = TokenType.Bearer; + var requestOptions = new DeleteHttpRequestOptions + { + RequestUrl = new Uri(ApiBaseUrl, $"/v2/document-groups/{documentGroupId.ValidateId()}/embedded-invites"), + Token = Token + }; + + await SignNowClient + .RequestAsync(requestOptions, cancellationToken) + .ConfigureAwait(false); + } + + /// + public async Task GenerateDocumentGroupEmbeddedEditorLinkAsync(string documentGroupId, EmbeddedEditorOptions options, CancellationToken cancellationToken = default) + { + Guard.ArgumentNotNull(options, nameof(options)); + Token.TokenType = TokenType.Bearer; + var requestOptions = new PostHttpRequestOptions + { + RequestUrl = new Uri(ApiBaseUrl, $"/v2/document-groups/{documentGroupId.ValidateId()}/embedded-editor"), + Content = options, + Token = Token + }; + + return await SignNowClient + .RequestAsync(requestOptions, cancellationToken) + .ConfigureAwait(false); + } + + /// + public async Task GenerateDocumentGroupEmbeddedSendingLinkAsync(string documentGroupId, EmbeddedSendingOptions options, CancellationToken cancellationToken = default) + { + Guard.ArgumentNotNull(options, nameof(options)); + Token.TokenType = TokenType.Bearer; + var requestOptions = new PostHttpRequestOptions + { + RequestUrl = new Uri(ApiBaseUrl, $"/v2/document-groups/{documentGroupId.ValidateId()}/embedded-sending"), + Content = options, + Token = Token + }; + + return await SignNowClient + .RequestAsync(requestOptions, cancellationToken) + .ConfigureAwait(false); + } + + // ===== IDocumentGroupInvite ===== + + /// + public async Task CreateGroupInviteAsync(string documentGroupId, CreateGroupInviteRequest request, CancellationToken cancellationToken = default) + { + Guard.ArgumentNotNull(request, nameof(request)); + Token.TokenType = TokenType.Bearer; + var requestOptions = new PostHttpRequestOptions + { + RequestUrl = new Uri(ApiBaseUrl, $"/documentgroup/{documentGroupId.ValidateId()}/groupinvite"), + Content = request, + Token = Token + }; + + return await SignNowClient + .RequestAsync(requestOptions, cancellationToken) + .ConfigureAwait(false); + } + + /// + public async Task GetGroupInviteAsync(string documentGroupId, string inviteId, CancellationToken cancellationToken = default) + { + Token.TokenType = TokenType.Bearer; + var requestOptions = new GetHttpRequestOptions + { + RequestUrl = new Uri(ApiBaseUrl, $"/documentgroup/{documentGroupId.ValidateId()}/groupinvite/{inviteId.ValidateId()}"), + Token = Token + }; + + return await SignNowClient + .RequestAsync(requestOptions, cancellationToken) + .ConfigureAwait(false); + } + + /// + public async Task CancelGroupInviteAsync(string documentGroupId, string inviteId, CancellationToken cancellationToken = default) + { + Token.TokenType = TokenType.Bearer; + var requestOptions = new PostHttpRequestOptions + { + RequestUrl = new Uri(ApiBaseUrl, $"/documentgroup/{documentGroupId.ValidateId()}/groupinvite/{inviteId.ValidateId()}/cancelinvite"), + Content = new EmptyPayloadRequest(), + Token = Token + }; + + await SignNowClient + .RequestAsync(requestOptions, cancellationToken) + .ConfigureAwait(false); + } + + /// + public async Task ResendGroupInviteAsync(string documentGroupId, string inviteId, CancellationToken cancellationToken = default) + { + Token.TokenType = TokenType.Bearer; + var requestOptions = new PostHttpRequestOptions + { + RequestUrl = new Uri(ApiBaseUrl, $"/documentgroup/{documentGroupId.ValidateId()}/groupinvite/{inviteId.ValidateId()}/resendinvites"), + Content = new EmptyPayloadRequest(), + Token = Token + }; + + await SignNowClient + .RequestAsync(requestOptions, cancellationToken) + .ConfigureAwait(false); + } + + /// + public async Task GetPendingGroupInvitesAsync(string documentGroupId, string inviteId, CancellationToken cancellationToken = default) + { + Token.TokenType = TokenType.Bearer; + var requestOptions = new GetHttpRequestOptions + { + RequestUrl = new Uri(ApiBaseUrl, $"/documentgroup/{documentGroupId.ValidateId()}/groupinvite/{inviteId.ValidateId()}/pendinginvites"), + Token = Token + }; + + return await SignNowClient + .RequestAsync(requestOptions, cancellationToken) + .ConfigureAwait(false); + } + + /// + public async Task ReassignSignerAsync(string documentGroupId, string inviteId, string stepId, ReassignSignerRequest request, CancellationToken cancellationToken = default) + { + Guard.ArgumentNotNull(request, nameof(request)); + Token.TokenType = TokenType.Bearer; + var requestOptions = new PostHttpRequestOptions + { + RequestUrl = new Uri(ApiBaseUrl, $"/documentgroup/{documentGroupId.ValidateId()}/groupinvite/{inviteId.ValidateId()}/invitestep/{stepId.ValidateId()}/update"), + Content = request, + Token = Token + }; + + await SignNowClient + .RequestAsync(requestOptions, cancellationToken) + .ConfigureAwait(false); + } } } diff --git a/SignNow.Net/Service/EventSubscriptionService.cs b/SignNow.Net/Service/EventSubscriptionService.cs index 7668ff2b..2545240f 100644 --- a/SignNow.Net/Service/EventSubscriptionService.cs +++ b/SignNow.Net/Service/EventSubscriptionService.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using SignNow.Net.Interfaces; using SignNow.Net.Extensions; +using SignNow.Net.Internal.Helpers; using SignNow.Net.Model; using SignNow.Net.Model.Requests; using SignNow.Net.Model.Responses; @@ -38,6 +39,23 @@ await SignNowClient .ConfigureAwait(false); } + /// + public async Task CreateEventSubscriptionV2Async(CreateEventSubscriptionV2 createEvent, CancellationToken cancellationToken = default) + { + Guard.ArgumentNotNull(createEvent, nameof(createEvent)); + Token.TokenType = TokenType.Bearer; + var requestOptions = new PostHttpRequestOptions + { + RequestUrl = new Uri(ApiBaseUrl, "/v2/event-subscriptions"), + Content = createEvent, + Token = Token + }; + + await SignNowClient + .RequestAsync(requestOptions, cancellationToken) + .ConfigureAwait(false); + } + /// public async Task GetEventSubscriptionsAsync(IQueryToString options, CancellationToken cancellationToken = default) { diff --git a/SignNow.Net/Service/UserService.cs b/SignNow.Net/Service/UserService.cs index 5520ccea..9382b375 100644 --- a/SignNow.Net/Service/UserService.cs +++ b/SignNow.Net/Service/UserService.cs @@ -294,6 +294,40 @@ public async Task ResendEmailInviteAsync(string fieldInviteId, CancellationToken await SignNowClient.RequestAsync(requestOptions, cancellationToken).ConfigureAwait(false); } + /// + public async Task GenerateEmbeddedEditorLinkAsync(string documentId, EmbeddedEditorOptions options, CancellationToken cancellationToken = default) + { + Guard.ArgumentNotNull(options, nameof(options)); + Token.TokenType = TokenType.Bearer; + var requestOptions = new PostHttpRequestOptions + { + RequestUrl = new Uri(ApiBaseUrl, $"/v2/documents/{documentId.ValidateId()}/embedded-editor"), + Content = options, + Token = Token + }; + + return await SignNowClient + .RequestAsync(requestOptions, cancellationToken) + .ConfigureAwait(false); + } + + /// + public async Task GenerateEmbeddedSendingLinkAsync(string documentId, EmbeddedSendingOptions options, CancellationToken cancellationToken = default) + { + Guard.ArgumentNotNull(options, nameof(options)); + Token.TokenType = TokenType.Bearer; + var requestOptions = new PostHttpRequestOptions + { + RequestUrl = new Uri(ApiBaseUrl, $"/v2/documents/{documentId.ValidateId()}/embedded-sending"), + Content = options, + Token = Token + }; + + return await SignNowClient + .RequestAsync(requestOptions, cancellationToken) + .ConfigureAwait(false); + } + /// /// Returns an enumerable of user's documents. /// diff --git a/SignNow.Net/SignNowContext.cs b/SignNow.Net/SignNowContext.cs index 3e7df518..ea20aa50 100644 --- a/SignNow.Net/SignNowContext.cs +++ b/SignNow.Net/SignNowContext.cs @@ -33,6 +33,9 @@ public class SignNowContext : WebClientBase, ISignNowContext /// public IDocumentGroup DocumentGroup { get; protected set; } + /// + public IDocumentGroupInvite GroupInvites { get; protected set; } + /// /// Create all the services using single instance of and other dependencies. /// @@ -63,6 +66,7 @@ public SignNowContext(Uri baseApiUrl, Token token, ISignNowClient signNowClient Folders = new FolderService(ApiBaseUrl, Token, SignNowClient); Events = new EventSubscriptionService(ApiBaseUrl, Token, SignNowClient); DocumentGroup = new DocumentGroupService(ApiBaseUrl, Token, SignNowClient); + GroupInvites = (IDocumentGroupInvite) DocumentGroup; } /// diff --git a/SignNow.props b/SignNow.props index 59fc0248..101835da 100644 --- a/SignNow.props +++ b/SignNow.props @@ -1,12 +1,12 @@ - 1.5.0 - signNow - signNow + 1.6.0 + SignNow + SignNow SignNow.NET - signNow.Net is a .NET 4.6.2+ and .NET standard class library for the signNow API. (Official Library) -signNow allows you to embed legally-binding e-signatures into your app, CRM or cloud storage. Send documents for signature directly from your website. Invite multiple signers to finalize contracts. Track status of your requests and download signed copies automatically. - airSlate Inc. + SignNow.Net is a .NET 4.6.2+ and .NET standard class library for the SignNow API. (Official Library) +SignNow allows you to embed legally-binding e-signatures into your app, CRM or cloud storage. Send documents for signature directly from your website. Invite multiple signers to finalize contracts. Track status of your requests and download signed copies automatically. + Copyright (c) 2003-present SignNow icon.png e-signature;eSign;electronic-signatures;pdf;forms;document;saas;signnow-api;api-client;REST;document management;workflow automation