Skip to content

Downloading PDF Statements

PDF versions of statements can be downloaded and reviewed along with any other field on the statement. PDFs are returned as base 64 encoded plain text. The text string must be converted to binary data in order to view the PDF.

Note

The PDF version of all statements contains a "Draft" watermark for all statements that have not been finalized. If you are working in the production system, finalizing statements removes the watermark.

The code snippets below illustrate converting a PDF to a binary string in a few different languages. See the documentation on authentication for information about obtaining the credential data passed in the header.

import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
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 =
     String.join("\n",
                 "query {",
                 "  getStatements(",
                 "    otxIds: [",
                 "      \"8e51c49b-a3d5-4cf3-831f-190e89e86f72\"",
                 "      \"b36f82a3-b95f-40da-b28f-8718d896d69f\"",
                 "      \"70a96872-5339-4439-8b1d-1a093adf801d\"",
                 "      \"6b7eb373-140f-4422-9e1f-1ee604fd2ddf\"",
                 "      \"2fae9430-8185-45c6-a516-2642f2418113\"",
                 "    ]",
                 "  ) {",
                 "    errors",
                 "    statements {",
                 "      nodes {",
                 "        otxId",
                 "        uploaderId",
                 "        pdf",
                 "      }",
                 "    }",
                 "  }",
                 "}");

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

  private DownloadResponse getStatements() {
    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);
    }
    DownloadResponse downloadResponse = gson.fromJson(response, DownloadResponse.class);
    return(downloadResponse);
  }

  private void saveStatements(DownloadResponse downloadResponse) {
    for(DownloadResponse.Data.GetStatements.Statements.Node node :
      downloadResponse.getData().getGetStatements().getStatements().getNodes()) {
      String otxId = node.getOtxId();
      String encryptedPdf = node.getPdf();
      byte[] decodedBytes = Base64.getMimeDecoder().decode(encryptedPdf);

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

  public static void main(String[] args) {
    StatementDownloader statementDownloader = new StatementDownloader();
    DownloadResponse downloadResponse = statementDownloader.getStatements();
    statementDownloader.saveStatements(downloadResponse);
  }
}
// Using graphql-request from
// https://github.com/prisma-labs/graphql-request
// Example tested with node version 14.16.0
import { GraphQLClient, gql } from 'graphql-request'
import fs from 'fs';

async function main() {
  const endpoint = 'https://sandbox.ottertax.com/v2/graphql'

  const graphQLClient = new GraphQLClient(endpoint, {
    headers: {
      'access-token': 'YOUR ACCESS TOKEN',
      'client':       'YOUR CLIENT ID',
      'uid':          'YOUR UID'
    },
  })
  const query = gql`
    query {
      getStatements(
        otxIds: [
          "8e51c49b-a3d5-4cf3-831f-190e89e86f72"
          "b36f82a3-b95f-40da-b28f-8718d896d69f"
          "70a96872-5339-4439-8b1d-1a093adf801d"
          "6b7eb373-140f-4422-9e1f-1ee604fd2ddf"
          "2fae9430-8185-45c6-a516-2642f2418113"
        ]
      ) {
        errors
        statements {
          nodes {
            otxId
            uploaderId
            pdf
          }
        }
      }
    }
  `

  const data = await graphQLClient.request(query)
  const statements = data.getStatements.statements.nodes
  const statmentsDirectory = './statements'
  statements.forEach((statement) => {
    let filename = `${statmentsDirectory}/${statement.otxId}.pdf`
    let buffer = Buffer.from(statement.pdf, 'base64')
    fs.writeFile(filename, buffer, err => {})
  })
}

main().catch((error) => console.error(error))
<?php
// Tested with php-cli version 8.0.5.
$query =<<<'END_DATA'
  query {
    getStatements(
      otxIds: [
        "8e51c49b-a3d5-4cf3-831f-190e89e86f72"
        "b36f82a3-b95f-40da-b28f-8718d896d69f"
        "70a96872-5339-4439-8b1d-1a093adf801d"
        "6b7eb373-140f-4422-9e1f-1ee604fd2ddf"
        "2fae9430-8185-45c6-a516-2642f2418113"
      ]
    ) {
      errors
      statements {
        nodes {
          otxId
          uploaderId
          pdf
        }
      }
    }
  }
END_DATA;
$payload = array ('query' => $query);
$options = array(
  'http' => array(
    'method'  => 'POST',
    'content' => json_encode( $payload ),
    'header'=>  "Content-Type: application/json\r\n" .
                "access-token: YOUR ACCESS TOKEN\r\n" .
                "client:       YOUR CLIENT ID\r\n" .
                "uid:          YOUR UID\r\n"
    )
);

$context  = stream_context_create( $options );
$response = file_get_contents( 'https://sandbox.ottertax.com/v2/graphql',
                              false, $context );
if( $response === FALSE ) {
  echo "Call to server failed.\n";
} else {
  $json = json_decode( $response, true );
  $statements = $json['data']['getStatements']['statements']['nodes'];
  $statements_directory = './statements';
  foreach( $statements as $statement ) {
    $file_name = "{$statements_directory}/{$statement['otxId']}.pdf";
    $pdf_file = fopen( $file_name, 'w' );
    fwrite( $pdf_file, base64_decode( $statement['pdf'] ) );
    fclose( $pdf_file );
  }
}
?>
# Using GQL from
# https://github.com/graphql-python/gql
# Tested using python version 3.8.8
from gql import gql, Client
from gql.transport.aiohttp import AIOHTTPTransport
import json
import base64

transport = AIOHTTPTransport(url="https://sandbox.ottertax.com/v2/graphql",
                            headers={ 'access-token': 'YOUR ACCESS TOKEN',
                                      'client':       'YOUR CLIENT ID',
                                      'uid':          'YOUR UID' })
client = Client(transport=transport, fetch_schema_from_transport=True)
query = gql(
    """
      query {
        getStatements(
          otxIds: [
            "8e51c49b-a3d5-4cf3-831f-190e89e86f72"
            "b36f82a3-b95f-40da-b28f-8718d896d69f"
            "70a96872-5339-4439-8b1d-1a093adf801d"
            "6b7eb373-140f-4422-9e1f-1ee604fd2ddf"
            "2fae9430-8185-45c6-a516-2642f2418113"
          ]
        ) {
          errors
          statements {
            nodes {
              otxId
              uploaderId
              pdf
            }
          }
        }
      }
    """
)

result = client.execute(query)
statements = result['getStatements']['statements']['nodes']
statements_directory = './statements'
for statement in statements:
  file_name = f'{statements_directory}/{statement["otxId"]}.pdf'
  pdf_file = open(file_name, 'w+b')
  pdf_file.write(base64.b64decode(statement['pdf']))
  pdf_file.close()
# If you wish to use a library instead, see
# https://github.com/github/graphql-client
# Tested using ruby 2.7.2.
require( 'net/http' )
require( 'uri' )
require( 'json' )
require( 'base64' )

uri = URI( "https://sandbox.ottertax.com/v2/graphql" )
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
headers = { 'Content-Type': 'application/json',
            'access-token': 'YOUR ACCESS TOKEN',
            'client':       'YOUR CLIENT ID',
            'uid':          'YOUR UID' }

query = <<-END_QUERY
  query {
    getStatements(
      otxIds: [
        "8e51c49b-a3d5-4cf3-831f-190e89e86f72"
        "b36f82a3-b95f-40da-b28f-8718d896d69f"
        "70a96872-5339-4439-8b1d-1a093adf801d"
        "6b7eb373-140f-4422-9e1f-1ee604fd2ddf"
        "2fae9430-8185-45c6-a516-2642f2418113"
      ]
    ) {
      errors
      statements {
        nodes {
          otxId
          uploaderId
          pdf
        }
      }
    }
  }
END_QUERY

http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
request = Net::HTTP::Post.new(uri.request_uri, headers )
request.body = {query: query}.to_json
response = http.request(request)
unless( response.code == '200' )
  STDOUT.puts( "Response code was #{response.code}." )
  exit
end
resp = JSON.parse( response.body )
statements = resp.dig( 'data', 'getStatements', 'statements', 'nodes' )
statements_directory = './statements'
statements.each do |statement|
  file_name = "#{statements_directory}/#{statement.dig( 'otxId' )}.pdf"
  File.open( file_name, 'wb' ) do |f|
    f.write( Base64.decode64( statement.dig( 'pdf' ) ) )
  end
end