Skip to content

Java Quick Start

Prerequisites

This quick start guide illustrates how to add two new 1099-NEC statements to the OtterTax API and download PDF versions of the statements. Before starting, you should create an account on the sanbdbox and have your username and password handy.

These examples use the open-source libraries httpclient and httpcore from the Apache HttpComponents project. See their project page for more information and links for downloading the libraries. These examples also use the Google Gson library for serializing and deserializing Json objects. These examples also use the OtterTax java classes which are freely available on our github page.

Confirming your registration

After registering an account in the sandbox or the production system, you need to confirm your registration. Run the code below, substituting your confirmation token and password where indicated on lines 29 and 30 below. If successful, the API will respond with a message indicating that your registration confirmation succeeded.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;

// This example uses version 2.9.1 of the
// open source gson library from Google.
// See https://github.com/google/gson.
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

// This example uses httpclient (version 4.5.13)
// and httpcore (version 4.4.13) libraries of the
// open source Apache HttpComponents project.
// See https://hc.apache.org/index.html.
import org.apache.http.entity.StringEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;

// Download the OtterTax java classes for the GraphQL
// query and mutation responses at
// https://github.com/OtterTax/graphql-java-classes
import com.ottertax.support.ConfirmRegistrationResponse;

public class RegistrationConfirmer {
  private String mutation = "mutation {\n" +
                            "  confirmRegistration(\n" +
                            "    confirmationToken: \"YOUR CONFIRMATION TOKEN\",\n" +
                            "    password:          \"YOUR SECURE PASSWORD\"\n" +
                            "  ) {\n" +
                            "       message\n" +
                            "  }\n" +
                            "}\n";
  private String endpoint = "https://sandbox.ottertax.com/v2/graphql";

  private void confirm() {
    CloseableHttpClient httpClient = HttpClients.createDefault();

    try {
      HttpPost httpPost = new HttpPost(endpoint);
      httpPost.addHeader("Content-Type", "application/json");

      StringEntity stringEntity = new StringEntity(querify(mutation));
      httpPost.setEntity(stringEntity);
      CloseableHttpResponse httpResponse = httpClient.execute(httpPost);

      BufferedReader reader = new BufferedReader(new InputStreamReader(
          httpResponse.getEntity().getContent()));
      StringBuffer responseBuffer = new StringBuffer();
      String inputLine;
      while ((inputLine = reader.readLine()) != null) {
        responseBuffer.append(inputLine);
      }
      reader.close();
      String response = responseBuffer.toString();
      int responseCode = httpResponse.getStatusLine().getStatusCode();
      if( responseCode == 200  ) {
        String successMessage = "Registration confirmation succeeded. " +
                                "You can log in and begin processing statements.";
        Gson gson = new Gson();
        ConfirmRegistrationResponse confirmRegistrationResponse = gson.fromJson(response, ConfirmRegistrationResponse.class);
        String message = confirmRegistrationResponse.getData().getConfirmRegistration().getMessage();
        if(message.equals(successMessage)) {
          System.out.println("\nRegistration confirmation succeeded.");
        }
        else {
          System.out.println(message);
          System.out.println("Registration failed.  Please correct the error above and try again.");
          System.exit(1);
        }
      } else {
        System.out.println("Registration failed.  Response code from server was " + String.valueOf(responseCode) + ".");
        System.exit(1);
      }
      httpResponse.close();
      httpClient.close();
    } catch(IOException e) {
      System.out.println("Error confirming registration.\nExiting");
      System.exit(1);
    }
  }

  private String querify(String rawGraphql) {
    Gson gson = new Gson();
    return("{\"query\":" + gson.toJson(rawGraphql) + "}");
  }

  public static void main(String[] args) {
    RegistrationConfirmer registrationConfirmer = new RegistrationConfirmer();
    registrationConfirmer.confirm();
  }
}

Obtaining an Authentication Token

The API requires authentication for running queries and mutations and looks for authentication information in the request headers. To obtain the necessary header data, run the program below, substituting your email address and password where indicated on lines 27 and 28 below. Save the output as we will use it in the next steps.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;

// This example uses version 2.9.1 of the
// open source gson library from Google.
// See https://github.com/google/gson.
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

// This example uses httpclient (version 4.5.13)
// and httpcore (version 4.4.13) libraries of the
// open source Apache HttpComponents project.
// See https://hc.apache.org/index.html.
import org.apache.http.entity.StringEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;

// Download the OtterTax java classes for the GraphQL
// query and mutation responses at
// https://github.com/OtterTax/graphql-java-classes
import com.ottertax.support.Credential;

public class CredentialFetcher {
  private String loginData = "{ \"email\": \"YOUR LOGIN EMAIL\",\n" +
                             "\"password\": \"YOUR PASSWORD\" }\n";
  private String endpoint = "https://sandbox.ottertax.com/v2/auth/sign_in";

  private void getCredential() {
    CloseableHttpClient httpClient = HttpClients.createDefault();
    Credential credential = new Credential();

    try {
      HttpPost httpPost = new HttpPost(endpoint);
      httpPost.addHeader("Content-Type", "application/json");

      StringEntity stringEntity = new StringEntity(loginData);
      httpPost.setEntity(stringEntity);
      CloseableHttpResponse httpResponse = httpClient.execute(httpPost);

      int responseCode = httpResponse.getStatusLine().getStatusCode();
      boolean headersPresent = (httpResponse.containsHeader("access-token") &&
                                httpResponse.containsHeader("client") &&
                                httpResponse.containsHeader("uid"));
      if( responseCode == 200 && headersPresent ) {
        String accessToken = httpResponse.getFirstHeader("access-token").getValue();
        String client = httpResponse.getFirstHeader("client").getValue();
        String uid = httpResponse.getFirstHeader("uid").getValue();
        credential = new Credential(accessToken, client, uid);
      } else {
        BufferedReader reader = new BufferedReader(new InputStreamReader(
            httpResponse.getEntity().getContent()));
        StringBuffer responseBuffer = new StringBuffer();
        String inputLine;
        while ((inputLine = reader.readLine()) != null) {
          responseBuffer.append(inputLine);
        }
        reader.close();
        System.out.println("Error retrieving credential.  Status code was " + String.valueOf(responseCode) + "." );
        System.out.println(responseBuffer.toString());
        System.out.println("Exiting.");
        System.exit(1);
      }
      httpResponse.close();
      httpClient.close();
    } catch(IOException e) {
      System.out.println("Error retrieving credential.\nExiting");
      System.exit(1);
    }
    // Print a JSON format version of the credential.
    Gson gson = new GsonBuilder().setPrettyPrinting().create();
    System.out.println(gson.toJson(credential));
  }

  public static void main(String[] args) {
    CredentialFetcher credentialFetcher = new CredentialFetcher();
    credentialFetcher.getCredential();
  }
}

Adding Statements

Once you've received your authentication credential, you're ready to add statements. Run the code below to add the two sample statements. Use the access token, client ID, and user ID you received in the last, replacing the placeholders on lines 28-30 below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;

// This example uses version 2.9.1 of the
// open source gson library from Google.
// See https://github.com/google/gson.
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

// This example uses httpclient (version 4.5.13)
// and httpcore (version 4.4.13) libraries of the
// open source Apache HttpComponents project.
// See https://hc.apache.org/index.html.
import org.apache.http.entity.StringEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;

// Download the OtterTax java classes for the GraphQL
// query and mutation responses at
// https://github.com/OtterTax/graphql-java-classes
import com.ottertax.support.AddF1099necStatementsResponse;

public class StatementAdder {
  private String endpoint = "https://sandbox.ottertax.com/v2/graphql";
  private String accessToken = "YOUR ACCESS TOKEN";
  private String client = "YOUR CLIENT ID";
  private String uid = "YOUR UID";
  private String gql =
     String.join("\n",
                 "mutation {",
                 "  addF1099necStatements(",
                 "    statements: [",
                 "      {",
                 "        nonemployeeComp: \"10318.62\"",
                 "        recipientAddress1: \"784 East 77th Street\"",
                 "        recipientCity: \"New York\"",
                 "        recipientFirstName: \"Leif\"",
                 "        recipientLastName: \"Babson\"",
                 "        recipientState: \"NY\"",
                 "        recipientTin: \"200213492\"",
                 "        recipientZipCode: \"10162\"",
                 "        senderAddress1: \"406 Chambers Street\"",
                 "        senderCity: \"New York\"",
                 "        senderName: \"Empire Savings Bank\"",
                 "        senderPhoneNumber: \"2125557318\"",
                 "        senderState: \"NY\"",
                 "        senderTin: \"104123456\"",
                 "        senderZipCode: \"10282\"",
                 "        tags: [\"group 7\", \"New York\"]",
                 "        uploaderId: \"20113\"",
                 "      }",
                 "      {",
                 "        nonemployeeComp: \"6370.97\"",
                 "        recipientAddress1: \"617 West 53rd Street\"",
                 "        recipientAddress2: \"Apartment 1022\"",
                 "        recipientCity: \"New York\"",
                 "        recipientFirstName: \"Anjelica\"",
                 "        recipientLastName: \"Heximer\"",
                 "        recipientState: \"NY\"",
                 "        recipientTin: \"200223492\"",
                 "        recipientZipCode: \"10019\"",
                 "        senderAddress1: \"406 Chambers Street\"",
                 "        senderCity: \"New York\"",
                 "        senderName: \"Empire Savings Bank\"",
                 "        senderPhoneNumber: \"2125557318\"",
                 "        senderState: \"NY\"",
                 "        senderTin: \"104123456\"",
                 "        senderZipCode: \"10282\"",
                 "        tags: [\"group 7\", \"New York\", \"send for review\"]",
                 "        uploaderId: \"20114\"",
                 "      }",
                 "    ]",
                 "  ) {",
                 "    errors",
                 "    statements {",
                 "      recordNumber",
                 "      statement {",
                 "        otxId",
                 "        uploaderId",
                 "        recipientFirstName",
                 "        recipientLastName",
                 "        tags",
                 "      }",
                 "      messages",
                 "    }",
                 "  }",
                 "}");

  private String querify(String rawGraphql) {
    Gson gson = new Gson();
    return("{\"query\":" + gson.toJson(rawGraphql) + "}");
  }

  private void add() {
    CloseableHttpClient httpClient = HttpClients.createDefault();
    Gson gson = new GsonBuilder().setPrettyPrinting().create();
    String response = "";

    try {
      HttpPost httpPost = new HttpPost(endpoint);
      httpPost.addHeader("content-type", "application/json");
      httpPost.addHeader("access-token", accessToken);
      httpPost.addHeader("client", client);
      httpPost.addHeader("uid", uid);

      StringEntity stringEntity = new StringEntity(querify(gql));
      httpPost.setEntity(stringEntity);
      CloseableHttpResponse httpResponse = httpClient.execute(httpPost);

      BufferedReader reader = new BufferedReader(new InputStreamReader(
          httpResponse.getEntity().getContent()));
      StringBuffer responseBuffer = new StringBuffer();
      String inputLine;
      while ((inputLine = reader.readLine()) != null) {
        responseBuffer.append(inputLine);
      }
      reader.close();
      response = responseBuffer.toString();
      int responseCode = httpResponse.getStatusLine().getStatusCode();
      if( responseCode != 200  ) {
        System.out.println("Response code from server was " + String.valueOf(responseCode) + ".");
      }
      httpResponse.close();
      httpClient.close();
    } catch(IOException e) {
      System.out.println("Error posting GraphQL.\nExiting");
      System.exit(1);
    }
    AddF1099necStatementsResponse addF1099necStatementsResponse = gson.fromJson(response, AddF1099necStatementsResponse.class);
    // Show the statements that were added.
    System.out.println(gson.toJson(addF1099necStatementsResponse));
  }

  public static void main(String[] args) {
    StatementAdder statementAdder = new StatementAdder();
    statementAdder.add();
  }
}

Downloading PDFs

After you've uploaded statement data, you can download PDF versions of the statements. Use the code below, again substituting the access token, client ID, and user ID you obtained earlier.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.Base64;
import java.nio.file.Files;
import java.nio.file.Paths;

// This example uses version 2.9.1 of the
// open source gson library from Google.
// See https://github.com/google/gson.
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

// This example uses httpclient (version 4.5.13)
// and httpcore (version 4.4.13) libraries of the
// open source Apache HttpComponents project.
// See https://hc.apache.org/index.html.
import org.apache.http.entity.StringEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;

// Download the OtterTax java classes for the GraphQL
// query and mutation responses at
// https://github.com/OtterTax/graphql-java-classes
import com.ottertax.support.DownloadResponse;

public class StatementDownloader {
  private String endpoint = "https://sandbox.ottertax.com/v2/graphql";
  private String accessToken = "YOUR ACCESS TOKEN";
  private String client = "YOUR CLIENT ID";
  private String uid = "YOUR UID";
  private String gql = "query {\n" +
                       "  getStatements(\n" +
                       "    uploaderIds: [\"20113\" \"20114\"]\n" +
                       "  ) {\n" +
                       "    errors\n" +
                       "    statements {\n" +
                       "      nodes {\n" +
                       "        uploaderId\n" +
                       "        pdf\n" +
                       "      }\n" +
                       "    }\n" +
                       "  }\n" +
                       "}\n";

  private String querify(String rawGraphql) {
    Gson gson = new Gson();
    return("{\"query\":" + gson.toJson(rawGraphql) + "}");
  }

  private String postGraphql() {
    CloseableHttpClient httpClient = HttpClients.createDefault();
    String response = "";

    try {
      HttpPost httpPost = new HttpPost(endpoint);
      httpPost.addHeader("Content-Type", "application/json");
      httpPost.addHeader("access-token", accessToken);
      httpPost.addHeader("client", client);
      httpPost.addHeader("uid", uid);

      StringEntity stringEntity = new StringEntity(querify(gql));
      httpPost.setEntity(stringEntity);
      CloseableHttpResponse httpResponse = httpClient.execute(httpPost);

      BufferedReader reader = new BufferedReader(new InputStreamReader(
          httpResponse.getEntity().getContent()));
      StringBuffer responseBuffer = new StringBuffer();
      String inputLine;
      while ((inputLine = reader.readLine()) != null) {
        responseBuffer.append(inputLine);
      }
      reader.close();
      response = responseBuffer.toString();
      int responseCode = httpResponse.getStatusLine().getStatusCode();
      if( responseCode != 200  ) {
        System.out.println("Response code from server was " + String.valueOf(responseCode) + ".");
      }
      httpResponse.close();
      httpClient.close();
    } catch(IOException e) {
      System.out.println("Error posting GraphQL.\nExiting");
      System.exit(1);
    }
    return(response);
  }

  private void download() {
    String response = postGraphql();

    Gson gson = new GsonBuilder().setPrettyPrinting().create();
    DownloadResponse downloadResponse = gson.fromJson(response, DownloadResponse.class);
    for(DownloadResponse.Data.GetStatements.Statements.Node node :
      downloadResponse.getData().getGetStatements().getStatements().getNodes()) {
      String uploaderId = node.getUploaderId();
      String encryptedPdf = node.getPdf();
      byte[] decodedBytes = Base64.getMimeDecoder().decode(encryptedPdf);

      String fileName = "./statements/stmt-" + uploaderId + ".pdf";
      try {
        Files.write(Paths.get(fileName), decodedBytes);
      } catch (IOException e) {
        System.out.println("Unable to write file " + fileName + ".\nExiting.");
        System.exit(1);
      }
    }
    System.out.println("Statements were downloaded to directory 'statements'.");
  }

  public static void main(String[] args) {
    StatementDownloader statementDownloader = new StatementDownloader();
    statementDownloader.download();
  }
}

Your statements are in a subdirectory called statements.

Where to Go from Here

Directional indicators

If you need to make changes to statements after they've been uploaded, you can use the update mutation.

If you're using the production version of the API, you can remove the watermark from statements by finalizing them.

The production system also allows you to submit statements to the IRS to meet your filing requirements.