package com.hellosign.sdk; import com.hellosign.sdk.http.Authentication; import com.hellosign.sdk.http.HttpClient; import com.hellosign.sdk.resource.AbstractRequest; import com.hellosign.sdk.resource.AbstractResourceList; import com.hellosign.sdk.resource.Account; import com.hellosign.sdk.resource.ApiApp; import com.hellosign.sdk.resource.BulkSendJobById; import com.hellosign.sdk.resource.EmbeddedRequest; import com.hellosign.sdk.resource.EmbeddedResponse; import com.hellosign.sdk.resource.FileUrlResponse; import com.hellosign.sdk.resource.SignatureRequest; import com.hellosign.sdk.resource.Team; import com.hellosign.sdk.resource.Template; import com.hellosign.sdk.resource.TemplateDraft; import com.hellosign.sdk.resource.TemplateSignatureRequest; import com.hellosign.sdk.resource.UnclaimedDraft; import com.hellosign.sdk.resource.support.ApiAppList; import com.hellosign.sdk.resource.support.OauthData; import com.hellosign.sdk.resource.support.Signature; import com.hellosign.sdk.resource.support.SignatureRequestList; import com.hellosign.sdk.resource.support.TemplateList; import com.hellosign.sdk.resource.support.types.BulkSendJobsList; import com.hellosign.sdk.resource.support.types.FieldType; import java.io.File; import java.lang.reflect.Constructor; import java.net.HttpURLConnection; import java.util.List; import java.util.Map; import org.json.JSONException; import org.json.JSONObject; /** * You'll need the HelloSignClient to do just about everything, from creating signatures to updating * account information. * * To use this class, instantiate a client with valid credentials like so: * * HelloSignClient client = new HelloSignClient(user, key); * * Then, use the client to perform your requests. The client uses java.net.HttpURLConnection to * perform its HTTP requests. */ public class HelloSignClient { public static final String DEFAULT_ENCODING = "UTF-8"; public static final String DEFAULT_BASE_API_URL = "https://api.hellosign.com/v3"; public static final String DEFAULT_OAUTH_TOKEN_URL = "https://app.hellosign.com/oauth/token"; public static final String ACCOUNT_URI = "/account"; public static final String VALIDATE_ACCOUNT_URI = "/account/validate"; public static final String ACCOUNT_CREATE_URI = "/account/create"; public static final String TEAM_URI = "/team"; public static final String TEAM_CREATE_URI = "/team/create"; public static final String TEAM_DESTROY_URI = "/team/destroy"; public static final String TEAM_ADD_MEMBER_URI = "/team/add_member"; public static final String TEAM_REMOVE_MEMBER_URI = "/team/remove_member"; public static final String SIGNATURE_REQUEST_URI = "/signature_request"; public static final String SIGNATURE_REQUEST_LIST_URI = "/signature_request/list"; public static final String SIGNATURE_REQUEST_SEND_URI = "/signature_request/send"; public static final String TEMPLATE_URI = "/template"; public static final String TEMPLATE_FILE_URI = "/template/files"; public static final String TEMPLATE_LIST_URI = "/template/list"; public static final String TEMPLATE_ADD_USER_URI = "/template/add_user"; public static final String TEMPLATE_REMOVE_USER_URI = "/template/remove_user"; public static final String TEMPLATE_DELETE_URI = "/template/delete"; public static final String TEMPLATE_UPDATE_FILES_URI = "/template/update_files"; public static final String TEMPLATE_CREATE_EMBEDDED_DRAFT_URI = "/template/create_embedded_draft"; public static final String TEMPLATE_SIGNATURE_REQUEST_URI = "/signature_request/send_with_template"; public static final String SIGNATURE_REQUEST_CANCEL_URI = "/signature_request/cancel"; public static final String SIGNATURE_REQUEST_REMIND_URI = "/signature_request/remind"; public static final String SIGNATURE_REQUEST_FINAL_COPY_URI = "/signature_request/final_copy"; public static final String SIGNATURE_REQUEST_FILES_URI = "/signature_request/files"; public static final String SIGNATURE_REQUEST_UPDATE_URI = "/signature_request/update"; public static final String SIGNATURE_REQUEST_EMBEDDED_URI = "/signature_request/create_embedded"; public static final String SIGNATURE_REQUEST_EMBEDDED_TEMPLATE_URI = "/signature_request/create_embedded_with_template"; public static final String EMBEDDED_SIGN_URL_URI = "/embedded/sign_url"; public static final String EMBEDDED_EDIT_URL_URI = "/embedded/edit_url"; public static final String UNCLAIMED_DRAFT_CREATE_URI = "/unclaimed_draft/create"; public static final String UNCLAIMED_DRAFT_CREATE_EMBEDDED_URI = "/unclaimed_draft/create_embedded"; public static final String UNCLAIMED_DRAFT_CREATE_EMBEDDED_WITH_TEMPLATE_URI = "/unclaimed_draft/create_embedded_with_template"; public static final String API_APP_URI = "/api_app"; public static final String API_APP_LIST_URI = "/api_app/list"; public static final String BULK_SEND_JOBS_LIST = "/bulk_send_job/list"; public static final String BULK_SEND_JOBS = "/bulk_send_job/"; public static final String SIGNATURE_REQUEST_REMOVE_URI = "/signature_request/remove"; public static final String PARAM_FILE_TYPE_URI = "file_type"; public static final String PARAM_GET_URL = "get_url"; public static final String FINAL_COPY_FILE_NAME = "final-copy"; public static final String FINAL_COPY_FILE_EXT = "pdf"; public static final String FILES_FILE_NAME = "files"; public static final String FILES_FILE_EXT = "pdf"; public static final String TEMPLATE_FILE_NAME = "template"; public static final String TEMPLATE_FILE_EXT = "pdf"; public static final String OAUTH_CODE = "code"; public static final String OAUTH_STATE = "state"; public static final String OAUTH_GRANT_TYPE = "grant_type"; public static final String OAUTH_REFRESH_TOKEN = "refresh_token"; public static final String OAUTH_GRANT_TYPE_AUTHORIZE_CODE = "authorization_code"; public static final String OAUTH_GRANT_TYPE_REFRESH_TOKEN = "refresh_token"; public static final String CLIENT_SECRET = "client_secret"; public static final String CLIENT_ID = "client_id"; public static final String EMBEDDED_TEMPLATE_SKIP_SIGNER_ROLES = "skip_signer_roles"; public static final String EMBEDDED_TEMPLATE_SKIP_SUBJECT_MESSAGE = "skip_subject_message"; public static final String EMBEDDED_TEMPLATE_MERGE_FIELDS = "merge_fields"; public static final String EMBEDDED_TEMPLATE_CC_ROLES = "cc_roles"; private Authentication auth; private HttpClient httpClient; // The base URL for all standard API endpoints, which can be // overridden by setting the "hellosign.base.url" system property. private String BASE_URI; // The base URL for retrieving an OAuth tokens, which can be // overridden by setting the "hellosign.oauth.base.url" system property. private String OAUTH_TOKEN_URL; /** * Default constructor for injection of dependencies (testing). * * @param client HttpClient * @param auth Authentication * @see #HelloSignClient(String) */ protected HelloSignClient(HttpClient client, Authentication auth) { this.httpClient = client; this.auth = auth; // Set overrides if present BASE_URI = DEFAULT_BASE_API_URL; String baseUrl = System.getProperty("hellosign.base.url"); if (baseUrl != null && !baseUrl.isEmpty()) { BASE_URI = baseUrl; } OAUTH_TOKEN_URL = DEFAULT_OAUTH_TOKEN_URL; String customOauthToken = System.getProperty("hellosign.oauth.base.url"); if (customOauthToken != null && !customOauthToken.isEmpty()) { OAUTH_TOKEN_URL = customOauthToken; } } /** * Creates a new HelloSign client using your API key. * * @param apiKey String API key * @see <a href= "https://app.hellosign.com/home/myAccount/current_tab/api">Account Settings</a> */ public HelloSignClient(String apiKey) { this(new HttpClient(), new Authentication(apiKey)); auth.setApiKey(apiKey); } /** * Creates a new HelloSign client using then given Authentication object. * * @param auth Authentication used primarily for setting OAuth token/secret */ public HelloSignClient(Authentication auth) { this(new HttpClient(), auth); } /** * Allows overriding of the authentication mechanism. Used mainly for setting an OAuth * token/secret. * * @param auth Authentication used for setting the auth method for this client */ public void setAuthentication(Authentication auth) { this.auth = auth; } /** * Retrieves the authentication details being used by this client. Used for testing purposes. * * @return Authentication */ protected Authentication getAuth() { return auth; } /** * Returns the current user's account information. * * @return Account * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public Account getAccount() throws HelloSignException { return new Account(httpClient.withAuth(auth).get(BASE_URI + ACCOUNT_URI).asJson()); } /** * Returns true if an account exists with the provided email address. Note this is limited to * the visibility of the currently authenticated user. * * @param email String email address * @return true if the account exists, false otherwise * @throws HelloSignException Thrown if there's a problem communicating with the HelloSign API. */ public boolean isAccountValid(String email) throws HelloSignException { if (email == null || email.isEmpty()) { return false; } Account account = new Account( httpClient.withAuth(auth).withPostField(Account.ACCOUNT_EMAIL_ADDRESS, email) .post(BASE_URI + VALIDATE_ACCOUNT_URI).asJson()); return (account.hasEmail() && email.equalsIgnoreCase(account.getEmail())); } /** * Update your account callback URL. * * This URL is used to notify you of any signature request events that occur when your account * is a party -- e.g., sender or signer/recipient. * * @param callback String URL * @return Account * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public Account setCallback(String callback) throws HelloSignException { return new Account( httpClient.withAuth(auth).withPostField(Account.ACCOUNT_CALLBACK_URL, callback) .post(BASE_URI + ACCOUNT_URI).asJson()); } /** * Creates a new HelloSign account. The user will still need to validate their email address to * complete the creation process to set a password. Note: This request does not require * authentication, so just performs the basic POST. * * @param email String New user's email address * @return Account New user's account information * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public Account createAccount(String email) throws HelloSignException { return createAccount(email, null, null); } /** * Creates a new HelloSign account and provides OAuth app credentials to automatically generate * an OAuth token with the user Account response. * * @param email String New user's email address * @param clientId String Client ID * @param clientSecret String App secret * @return Account New user's account information * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public Account createAccount(String email, String clientId, String clientSecret) throws HelloSignException { HttpClient client = httpClient.withAuth(auth) .withPostField(Account.ACCOUNT_EMAIL_ADDRESS, email); if (clientId != null && clientSecret != null) { client = client.withPostField(CLIENT_ID, clientId) .withPostField(CLIENT_SECRET, clientSecret); } JSONObject response = client.post(BASE_URI + ACCOUNT_CREATE_URI).asJson(); JSONObject copy; try { copy = new JSONObject(response.toString()); } catch (JSONException e) { throw new HelloSignException(e); } OauthData data = new OauthData(copy); Account account = new Account(response); account.setOauthData(data); return account; } /** * Performs an OAuth request and returns the necessary data for authorizing an API application, * and will automatically set the access token and code for making authenticated requests with * this client. * * @param code String OAuth code * @param clientId String OAuth client ID * @param secret String OAuth secret * @param state String OAuth client state * @param autoSetRequestToken true if the token should be applied to this client for future * requests * @return OauthData object containing OAuth token details * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public OauthData getOauthData(String code, String clientId, String secret, String state, boolean autoSetRequestToken) throws HelloSignException { OauthData data = new OauthData(httpClient.withAuth(auth).withPostField(OAUTH_STATE, state) .withPostField(OAUTH_CODE, code).withPostField(CLIENT_ID, clientId) .withPostField(OAUTH_GRANT_TYPE, OAUTH_GRANT_TYPE_AUTHORIZE_CODE) .withPostField(CLIENT_SECRET, secret) .post(OAUTH_TOKEN_URL).asJson()); if (autoSetRequestToken) { setAccessToken(data.getAccessToken(), data.getTokenType()); } return data; } /** * Refreshes the OauthData for this client with the provided refresh token. * * @param refreshToken String * @return OauthData new OAuthData returned from HelloSign * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public OauthData refreshOauthData(String refreshToken) throws HelloSignException { OauthData data = new OauthData( httpClient.withAuth(auth) .withPostField(OAUTH_GRANT_TYPE, OAUTH_GRANT_TYPE_REFRESH_TOKEN) .withPostField(OAUTH_REFRESH_TOKEN, refreshToken).post(OAUTH_TOKEN_URL).asJson()); setAccessToken(data.getAccessToken(), data.getTokenType()); return data; } /** * Retrieves the Team for the current user account. * * @return Team * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public Team getTeam() throws HelloSignException { return new Team(httpClient.withAuth(auth).get(BASE_URI + TEAM_URI).asJson()); } /** * Creates a new team for the current user with the given name. * * @param teamName String team name * @return Team * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public Team createTeam(String teamName) throws HelloSignException { return new Team( httpClient.withAuth(auth).withPostField(Team.TEAM_NAME, teamName) .post(BASE_URI + TEAM_URI).asJson()); } /** * Destroys the current user's team. * * @return boolean if destroy was successful * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public boolean destroyTeam() throws HelloSignException { return HttpURLConnection.HTTP_OK == httpClient.withAuth(auth) .post(BASE_URI + TEAM_DESTROY_URI).asHttpCode(); } /** * Updates the current user's team name. * * @param teamName String team name * @return Team * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public Team updateTeamName(String teamName) throws HelloSignException { return new Team( httpClient.withAuth(auth).withPostField(Team.TEAM_NAME, teamName) .post(BASE_URI + TEAM_URI).asJson()); } /** * Adds the user to the current user's team. * * @param idOrEmail String new team member's account ID or email address * @return Team * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public Team inviteTeamMember(String idOrEmail) throws HelloSignException { String key = (idOrEmail != null && idOrEmail.contains("@")) ? Account.ACCOUNT_EMAIL_ADDRESS : Account.ACCOUNT_ID; return new Team( httpClient.withAuth(auth).withPostField(key, idOrEmail) .post(BASE_URI + TEAM_ADD_MEMBER_URI).asJson()); } /** * Removes the team member indicated by the user account ID or email address. * * @param idOrEmail String removed team member's account ID or email address * @return Team * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public Team removeTeamMember(String idOrEmail) throws HelloSignException { String key = (idOrEmail != null && idOrEmail.contains("@")) ? Account.ACCOUNT_EMAIL_ADDRESS : Account.ACCOUNT_ID; return new Team(httpClient.withAuth(auth).withPostField(key, idOrEmail) .post(BASE_URI + TEAM_REMOVE_MEMBER_URI) .asJson()); } /** * Retrieves a Signature Request with the given ID. * * @param id String signature ID * @return SignatureRequest * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public SignatureRequest getSignatureRequest(String id) throws HelloSignException { String url = BASE_URI + SIGNATURE_REQUEST_URI + "/" + id; return new SignatureRequest(httpClient.withAuth(auth).get(url).asJson()); } /** * Retrieves the current user's signature requests. The resulting object represents a paged * query result. The page information can be retrieved on from the ListInfo object on the * SignatureRequestList. * * @return SignatureRequestList * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public SignatureRequestList getSignatureRequests() throws HelloSignException { return new SignatureRequestList( httpClient.withAuth(auth).get(BASE_URI + SIGNATURE_REQUEST_LIST_URI).asJson()); } /** * Retrieves a specific page of the current user's signature requests. * * @param page int * @return SignatureRequestList * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public SignatureRequestList getSignatureRequests(int page) throws HelloSignException { return new SignatureRequestList( httpClient.withAuth(auth) .withGetParam(AbstractResourceList.PAGE, Integer.toString(page)) .get(BASE_URI + SIGNATURE_REQUEST_LIST_URI).asJson()); } /** * Retrieves a specific page of the current user's signature requests. * * @param page int * @param pageSize int Must be between 1 and 100. * @return SignatureRequestList * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public SignatureRequestList getSignatureRequests(int page, int pageSize) throws HelloSignException { return new SignatureRequestList( httpClient.withAuth(auth) .withGetParam(AbstractResourceList.PAGE, Integer.toString(page)) .withGetParam(AbstractResourceList.PAGE_SIZE, Integer.toString(pageSize)) .get(BASE_URI + SIGNATURE_REQUEST_LIST_URI).asJson()); } /** * Sends the provided signature request to HelloSign. * * @param req SignatureRequest * @return SignatureRequest * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public SignatureRequest sendSignatureRequest(SignatureRequest req) throws HelloSignException { if (req.hasId()) { throw new HelloSignException("Sending an existing signature request is not supported"); } return new SignatureRequest(httpClient.withAuth(auth).withPostFields(req.getPostFields()) .post(BASE_URI + SIGNATURE_REQUEST_SEND_URI).asJson()); } /** * Update a signer's email address. * * This method requires the ID of the siganture request that has already been sent, as well as * the signature_id that represents the signer that should be updated. The ema * * @param signatureRequestId String ID of the signature request that has already been sent and * needs to be updated. * @param signatureId String ID of the signer that needs to be updated. * @param newEmailAddress String email address that the signer should be changed to * @return SignatureRequest The updated request data * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public SignatureRequest updateSignatureRequest(String signatureRequestId, String signatureId, String newEmailAddress) throws HelloSignException { String url = BASE_URI + SIGNATURE_REQUEST_UPDATE_URI + "/" + signatureRequestId; return new SignatureRequest( httpClient.withAuth(auth).withPostField(Signature.SIGNATURE_ID, signatureId) .withPostField(SignatureRequest.SIGREQ_SIGNER_EMAIL, newEmailAddress).post(url) .asJson()); } /** * Retrieves the templates for the current user account. * * @return TemplateList * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public TemplateList getTemplates() throws HelloSignException { return new TemplateList( httpClient.withAuth(auth).get(BASE_URI + TEMPLATE_LIST_URI).asJson()); } /** * Retrieves a page of templates for the current user account. * * @param page int * @return TemplateList * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public TemplateList getTemplates(int page) throws HelloSignException { return new TemplateList( httpClient.withAuth(auth) .withGetParam(AbstractResourceList.PAGE, Integer.toString(page)) .get(BASE_URI + TEMPLATE_LIST_URI).asJson()); } /** * Retrieves a page of templates with a specific pageSize * * @param page int * @param pageSize int Must be between 1 and 100. * @return TemplateList * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public TemplateList getTemplates(int page, int pageSize) throws HelloSignException { return new TemplateList( httpClient.withAuth(auth) .withGetParam(AbstractResourceList.PAGE, Integer.toString(page)) .withGetParam(AbstractResourceList.PAGE_SIZE, Integer.toString(pageSize)) .get(BASE_URI + TEMPLATE_LIST_URI).asJson()); } /** * Retrieves the PDF file backing the Template specified by the provided Template ID. * * @param templateId String Template ID * @return File PDF file object * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public File getTemplateFile(String templateId) throws HelloSignException { String url = BASE_URI + TEMPLATE_FILE_URI + "/" + templateId; String fileName = TEMPLATE_FILE_NAME + "." + TEMPLATE_FILE_EXT; return httpClient.withAuth(auth).get(url).asFile(fileName); } /** * Returns a signed URL that can be used to retrieve the file backing a template. * * @param templateId String Template ID * @return String URL or null if no file URL can be retrieved * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public String getTemplateFileUrl(String templateId) throws HelloSignException { String fileUrl = null; String url = BASE_URI + TEMPLATE_FILE_URI + "/" + templateId; JSONObject response = httpClient.withAuth(auth).withGetParam(PARAM_GET_URL, "1").get(url) .asJson(); if (response.has("file_url")) { try { fileUrl = response.getString("file_url"); } catch (JSONException ex) { throw new HelloSignException(ex); } } return fileUrl; } /** * Retrieves a specific Template based on the provided Template ID. * * @param templateId String Template ID * @return Template * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public Template getTemplate(String templateId) throws HelloSignException { String url = BASE_URI + TEMPLATE_URI + "/" + templateId; return new Template(httpClient.withAuth(auth).get(url).asJson()); } /** * Adds the provided user to the template indicated by the provided template ID. The new user * can be designated using their account ID or email address. * * @param templateId String template ID * @param idOrEmail String account ID or email address * @return Template * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public Template addTemplateUser(String templateId, String idOrEmail) throws HelloSignException { String url = BASE_URI + TEMPLATE_ADD_USER_URI + "/" + templateId; String key = (idOrEmail != null && idOrEmail.contains("@")) ? Account.ACCOUNT_EMAIL_ADDRESS : Account.ACCOUNT_ID; return new Template( httpClient.withAuth(auth).withPostField(key, idOrEmail).post(url).asJson()); } /** * Delete the template designated by the template id * * @param templateId String template ID * @return true if the delete was successful, false otherwise * @throws HelloSignException thrown if there is a problem processing the HTTP request */ public boolean deleteTemplate(String templateId) throws HelloSignException { String url = BASE_URI + TEMPLATE_DELETE_URI + "/" + templateId; return HttpURLConnection.HTTP_OK == httpClient.withAuth(auth).post(url).asHttpCode(); } /** * Replaces the backing documents for the given template with the given files. * * Note that certain rules apply to this endpoint: https://app.hellosign.com/api/reference#update_template_files * * @param existingTemplateId String ID of the template from which the overlay data (signatures, * text fields, etc.) will be retrieved. * @param newTemplate TemplateDraft that holds the data and documents which will be used as the * basis for the new template. The following fields can be set in this request: files or * file_urls, subject, message, and test_mode. * @param clientId String optional ID of the app which is generating this new template. Set to * null if not used. * @return String ID of the template to be created * @throws HelloSignException thrown if there is a problem processing the HTTP request */ public String updateTemplateFiles(String existingTemplateId, TemplateDraft newTemplate, String clientId) throws HelloSignException { String url = BASE_URI + TEMPLATE_UPDATE_FILES_URI + "/" + existingTemplateId; HttpClient client = httpClient.withAuth(auth).withPostFields(newTemplate.getPostFields()); if (clientId != null) { client = client.withPostField(EmbeddedRequest.EMBEDDED_CLIENT_ID, clientId); } Template t = new Template(client.post(url).asJson()); return t.getId(); } /** * Adds the provided user to the template indicated by the provided template ID. The new user * can be designated using their account ID or email address. * * @param templateId String template ID * @param idOrEmail String account ID or email address * @return Template * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public Template removeTemplateUser(String templateId, String idOrEmail) throws HelloSignException { String url = BASE_URI + TEMPLATE_REMOVE_USER_URI + "/" + templateId; String key = (idOrEmail != null && idOrEmail.contains("@")) ? Account.ACCOUNT_EMAIL_ADDRESS : Account.ACCOUNT_ID; return new Template( httpClient.withAuth(auth).withPostField(key, idOrEmail).post(url).asJson()); } /** * Creates a new Signature Request based on the template provided. * * @param req TemplateSignatureRequest * @return SignatureRequest * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public SignatureRequest sendTemplateSignatureRequest(TemplateSignatureRequest req) throws HelloSignException { return new SignatureRequest(httpClient.withAuth(auth).withPostFields(req.getPostFields()) .post(BASE_URI + TEMPLATE_SIGNATURE_REQUEST_URI).asJson()); } /** * Cancels an existing signature request. If it has been completed, it will delete the signature * request from your account. * * @param id SignatureRequest id * @return boolean true if successful * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public boolean cancelSignatureRequest(String id) throws HelloSignException { String url = BASE_URI + SIGNATURE_REQUEST_CANCEL_URI + "/" + id; return HttpURLConnection.HTTP_OK == httpClient.withAuth(auth).post(url).asHttpCode(); } /** * Instructs HelloSign to email the given address with a reminder to sign the SignatureRequest * referenced by the given request ID. * * Note: You cannot send a reminder within 1 hours of the last reminder that was sent, manually * or automatically. * * @param requestId String SignatureRequest ID * @param email String email * @return SignatureRequest The request to be reminded * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public SignatureRequest requestEmailReminder(String requestId, String email) throws HelloSignException { String url = BASE_URI + SIGNATURE_REQUEST_REMIND_URI + "/" + requestId; return new SignatureRequest( httpClient.withAuth(auth).withPostField(Account.ACCOUNT_EMAIL_ADDRESS, email).post(url) .asJson()); } /** * Retrieves the final PDF copy of a signature request, if it exists. * * @param requestId String SignatureRequest ID * @return File final copy file, or null if it does not yet exist * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. * @deprecated Use {{@link #getFiles(String)} */ @Deprecated public File getFinalCopy(String requestId) throws HelloSignException { String url = BASE_URI + SIGNATURE_REQUEST_FINAL_COPY_URI + "/" + requestId; String filename = FINAL_COPY_FILE_NAME + "." + FINAL_COPY_FILE_EXT; return httpClient.withAuth(auth).get(url).asFile(filename); } /** * Retrieves a PDF copy of the files associated with a signature request. * * @param requestId String signature ID * @return File PDF file * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public File getFiles(String requestId) throws HelloSignException { return getFiles(requestId, SignatureRequest.SIGREQ_FORMAT_PDF); } /** * Retrieves the file associated with a signature request. * * @param requestId String signature request ID * @param format String format, see SignatureRequest for available types * @return File * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public File getFiles(String requestId, String format) throws HelloSignException { if (format == null || format.isEmpty()) { format = FILES_FILE_EXT; } String url = BASE_URI + SIGNATURE_REQUEST_FILES_URI + "/" + requestId; String fileName = FILES_FILE_NAME + "." + format; return httpClient.withAuth(auth).withGetParam(PARAM_FILE_TYPE_URI, format).get(url) .asFile(fileName); } /** * Retrieves a URL for a file associated with a signature request. * * @param requestId String signature request ID * @return {@link FileUrlResponse} * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. * @see <a href="https://app.hellosign.com/api/reference#get_files">https://app.hellosign.com/api/reference#get_files</a> */ public FileUrlResponse getFilesUrl(String requestId) throws HelloSignException { String url = BASE_URI + SIGNATURE_REQUEST_FILES_URI + "/" + requestId; HttpClient client = this.httpClient.withAuth(auth).withGetParam(PARAM_GET_URL, "1") .get(url); if (client.getLastResponseCode() == 404) { throw new HelloSignException( String.format("Could not find request with id=%s", requestId)); } return new FileUrlResponse(client.asJson()); } /** * Creates a signature request that can be embedded within your website. * * @param embeddedReq EmbeddedRequest * @return SignatureRequest * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public AbstractRequest createEmbeddedRequest(EmbeddedRequest embeddedReq) throws HelloSignException { String url = BASE_URI; Class<?> returnType = SignatureRequest.class; AbstractRequest req = embeddedReq.getRequest(); if (req instanceof TemplateSignatureRequest) { url += SIGNATURE_REQUEST_EMBEDDED_TEMPLATE_URI; } else if (req instanceof UnclaimedDraft) { if (((UnclaimedDraft) req).getRequest() instanceof TemplateSignatureRequest) { url += UNCLAIMED_DRAFT_CREATE_EMBEDDED_WITH_TEMPLATE_URI; } else { url += UNCLAIMED_DRAFT_CREATE_EMBEDDED_URI; } returnType = UnclaimedDraft.class; } else { url += SIGNATURE_REQUEST_EMBEDDED_URI; } try { Constructor<?> constructor = returnType.getConstructor(JSONObject.class); return (AbstractRequest) constructor.newInstance( httpClient.withAuth(auth).withPostFields(embeddedReq.getPostFields()).post(url) .asJson()); } catch (Exception ex) { throw new HelloSignException(ex); } } /** * Retrieves the necessary information to build an embedded signature request. * * @param signatureId String ID of the signature request to embed * @return EmbeddedResponse * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public EmbeddedResponse getEmbeddedSignUrl(String signatureId) throws HelloSignException { String url = BASE_URI + EMBEDDED_SIGN_URL_URI + "/" + signatureId; return new EmbeddedResponse(httpClient.withAuth(auth).post(url).asJson()); } /** * Retrieves the necessary information to edit an embedded template. * * @param templateId String ID of the signature request to embed * @return EmbeddedResponse * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public EmbeddedResponse getEmbeddedTemplateEditUrl(String templateId) throws HelloSignException { return getEmbeddedTemplateEditUrl(templateId, false, false, false); } /** * Retrieves the necessary information to edit an embedded template. * * @param templateId String ID of the signature request to embed * @param skipSignerRoles true if the edited template should not allow the user to modify the * template's signer roles. Defaults to false. * @param skipSubjectMessage true if the edited template should not allow the user to modify the * template's subject and message. Defaults to false. * @return EmbeddedResponse * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public EmbeddedResponse getEmbeddedTemplateEditUrl(String templateId, boolean skipSignerRoles, boolean skipSubjectMessage) throws HelloSignException { return getEmbeddedTemplateEditUrl(templateId, skipSignerRoles, skipSubjectMessage, false); } /** * Retrieves the necessary information to edit an embedded template. * * @param templateId String ID of the signature request to embed * @param skipSignerRoles true if the edited template should not allow the user to modify the * template's signer roles. Defaults to false. * @param skipSubjectMessage true if the edited template should not allow the user to modify the * template's subject and message. Defaults to false. * @param testMode true if this request is a test request. Useful for editing locked templates. * @return EmbeddedResponse * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public EmbeddedResponse getEmbeddedTemplateEditUrl(String templateId, boolean skipSignerRoles, boolean skipSubjectMessage, boolean testMode) throws HelloSignException { return getEmbeddedTemplateEditUrl(templateId, skipSignerRoles, skipSubjectMessage, false, null, null); } /** * Big kahuna method for editing an embedded template. This method allows the updating of merge * fields and CC roles (both optional parameters). * * @param templateId String ID of the signature request to embed * @param skipSignerRoles true if the edited template should not allow the user to modify the * template's signer roles. Defaults to false. * @param skipSubjectMessage true if the edited template should not allow the user to modify the * template's subject and message. Defaults to false. * @param testMode true if this request is a test request. Useful for editing locked templates. * @param mergeFields These will overwrite the current merge fields for the embedded template. * To remove all merge fields, pass in an empty Map. * @param ccRoles These will overwrite the current CC Roles for the embedded template. To remove * all CC roles, pass in null or an empty List. * @return EmbeddedResponse * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public EmbeddedResponse getEmbeddedTemplateEditUrl(String templateId, boolean skipSignerRoles, boolean skipSubjectMessage, boolean testMode, Map<String, FieldType> mergeFields, List<String> ccRoles) throws HelloSignException { String url = BASE_URI + EMBEDDED_EDIT_URL_URI + "/" + templateId; HttpClient client = httpClient.withAuth(auth) .withPostField(EMBEDDED_TEMPLATE_SKIP_SIGNER_ROLES, skipSignerRoles) .withPostField(EMBEDDED_TEMPLATE_SKIP_SUBJECT_MESSAGE, skipSubjectMessage) .withPostField(AbstractRequest.REQUEST_TEST_MODE, testMode); String mergeFieldsStr = TemplateDraft.serializeMergeFields(mergeFields); if (mergeFieldsStr != null) { client = client.withPostField(EMBEDDED_TEMPLATE_MERGE_FIELDS, mergeFieldsStr); } if (ccRoles == null || ccRoles.isEmpty()) { // Per documentation: https://app.hellosign.com/api/reference#get_embedded_template_edit_url client = client.withPostField(EMBEDDED_TEMPLATE_CC_ROLES + "[0]", ""); } else { for (int i = 0; i < ccRoles.size(); i++) { String cc = ccRoles.get(i); client = client.withPostField(EMBEDDED_TEMPLATE_CC_ROLES + "[" + i + "]", cc); } } return new EmbeddedResponse(client.post(url).asJson()); } /** * Creates an unclaimed draft using the provided request draft object. * * @param draft UnclaimedDraft * @return UnclaimedDraft The created draft * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public UnclaimedDraft createUnclaimedDraft(UnclaimedDraft draft) throws HelloSignException { String url = BASE_URI; if (draft.isForEmbeddedSigning()) { url += UNCLAIMED_DRAFT_CREATE_EMBEDDED_URI; } else { url += UNCLAIMED_DRAFT_CREATE_URI; } return new UnclaimedDraft( httpClient.withAuth(auth).withPostFields(draft.getPostFields()).post(url).asJson()); } /** * Creates a template draft that can be used for embedded template creation. * * @param req EmbeddedRequest * @return Template the unclaimed template draft * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public TemplateDraft createEmbeddedTemplateDraft(EmbeddedRequest req) throws HelloSignException { return new TemplateDraft(httpClient.withAuth(auth).withPostFields(req.getPostFields()) .post(BASE_URI + TEMPLATE_CREATE_EMBEDDED_DRAFT_URI).asJson()); } /** * Retrieves the API app configuration for the given Client ID. * * @param clientId String * @return ApiApp * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public ApiApp getApiApp(String clientId) throws HelloSignException { String url = BASE_URI + API_APP_URI + "/" + clientId; return new ApiApp(httpClient.withAuth(auth).get(url).asJson()); } /** * Retrieves a paged list of API apps for the authenticated account. * * @return ApiAppList * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public ApiAppList getApiApps() throws HelloSignException { return new ApiAppList(httpClient.withAuth(auth).get(BASE_URI + API_APP_LIST_URI).asJson()); } /** * Creates a new ApiApp using the properties set on the provided ApiApp. * * @param app ApiApp * @return ApiApp newly created ApiApp * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public ApiApp createApiApp(ApiApp app) throws HelloSignException { return new ApiApp( httpClient.withAuth(auth).withPostFields(app.getPostFields()) .post(BASE_URI + API_APP_URI).asJson()); } /** * Attempts to delete the API app with the given client ID. * * @param clientId String The Client ID of the app that should be deleted. * @return boolean true if the API app was successfully deleted * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public boolean deleteApiApp(String clientId) throws HelloSignException { String url = BASE_URI + API_APP_URI + "/" + clientId; return HttpURLConnection.HTTP_NO_CONTENT == httpClient.withAuth(auth).delete(url) .asHttpCode(); } /** * Updates the API app with the given ApiApp object properties. * * @param app ApiApp * @return ApiApp updated ApiApp * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public ApiApp updateApiApp(ApiApp app) throws HelloSignException { if (!app.hasClientId()) { throw new HelloSignException( "Cannot update an ApiApp without a client ID. Create one first!"); } String url = BASE_URI + API_APP_URI + "/" + app.getClientId(); return new ApiApp( httpClient.withAuth(auth).withPostFields(app.getPostFields()).post(url).asJson()); } /** * Sets the access token for the OAuth user that this client will use to perform requests. * * @param accessToken String access token * @param tokenType String token type * @throws HelloSignException thrown if there's a problem setting the access token. */ public void setAccessToken(String accessToken, String tokenType) throws HelloSignException { auth.setAccessToken(accessToken, tokenType); } /** * Returns a list of BulkSendJob that you can access. * * @return bulkSendJobsList * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public BulkSendJobsList getBulkSendJobList() throws HelloSignException { return new BulkSendJobsList(httpClient.withAuth(auth).get(BASE_URI + BULK_SEND_JOBS_LIST).asJson()); } /** * Retrieves a specific page number of the BulkSendJob List. * * @param page int * @return BulkSendJobsList * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public BulkSendJobsList getBulkSendJobList(int page) throws HelloSignException { return new BulkSendJobsList( httpClient.withAuth(auth) .withGetParam(AbstractResourceList.PAGE, Integer.toString(page)) .get(BASE_URI + BULK_SEND_JOBS_LIST).asJson()); } /** * Retrieves a specific page number of the BulkSendJob List, with Number of objects to be returned per page. * Must be between 1 and 100. Default is 20. * * @param page int * @param pageSize int Must be between 1 and 100. * @return BulkSendJobsList * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public BulkSendJobsList getBulkSendJobList(int page, int pageSize) throws HelloSignException { return new BulkSendJobsList( httpClient.withAuth(auth) .withGetParam(AbstractResourceList.PAGE, Integer.toString(page)) .withGetParam(AbstractResourceList.PAGE_SIZE, Integer.toString(pageSize)) .get(BASE_URI + BULK_SEND_JOBS_LIST).asJson()); } /** * Returns the status of the BulkSendJob and its SignatureRequests specified by the bulk_send_job_id parameter. * @param bulk_send_job_id * @return * @throws HelloSignException */ public BulkSendJobById getBulkSendByJobId(String bulk_send_job_id) throws HelloSignException{ String url = BASE_URI + BULK_SEND_JOBS + "/" + bulk_send_job_id; return new BulkSendJobById(httpClient.withAuth(auth).get(url).asJson()); } /** * Removes your access to a completed signature request. This action is not reversible. * * The signature request must be fully executed by all parties (signed or declined to sign). * Other parties will continue to maintain access to the completed signature request document(s). * * @param signature_request_id SignatureRequest id * @return boolean true if successful * @throws HelloSignException thrown if there's a problem processing the HTTP request or the * JSON response. */ public boolean removeSignatureRequestAccess(String signature_request_id) throws HelloSignException { String url = BASE_URI + SIGNATURE_REQUEST_REMOVE_URI + "/" + signature_request_id; return HttpURLConnection.HTTP_OK == httpClient.withAuth(auth).post(url).asHttpCode(); } }