Skip to content

Reviewing 3922 Statements

Two queries are available for reviewing 3922 statements, getStatements and getF3922Statements. The getStatements query is the more flexible of the two, so you should use it whenever possible and only use getF3922Statements when you need to review information that only appears on Form 3922 (for example, number of shares transferred or exercise price per share). More information about the distinction between the generic and distinct statement objects is included in the overview of the API design philosophy.

The getStatements Query

The getStatements query accepts more search parameters than getF3922Statements, and it allows for retrieval of multiple statement types simultaneously. You should use the getStatements query where possible in order to take advantage of the increased flexibility it provides. See the getStatements documentation for more information.

The getF3922Statements Query

Use the getF3922Statements query when you need to retrieve information that only appears on Form 3922, for example number of shares transferred or exercise price per share.

The getF3922Statements query accepts only two arguments, both of which are optional: an array of OTX IDs and an array of uploader IDs. The search logic combines these two criteria. If you supply both parameters, only statements with matching OTX IDs and matching uploader IDs are returned. Generally speaking, you should supply only one parameter or the other. Since each parameter is unique across all statements, the query returns at most one statement for each value in the list. If you wish to search for statements using different criteria, use the getStatements query which supports additional search parameters.

Note

The getF3922Statements query returns at most 100 statements. When querying statements, always check the hasNextPage field of the pageInfo object to ensure you have retrieved all matching statements. If your query does not return all statements, page through the result set by making additional queries.

The sample code below demonstrates using the getF3922Statements query and retrieving a subset of fields. Full and up-to-date documentation is available as part of the introspective GraphQL documentation.

Retrieving All 3922 Statements

The query below retrieves all 3922 statements.

query {
  getF3922Statements {
    errors
    statements {
      pageInfo {
        hasNextPage
      }
      nodes {
        otxId
        uploaderId
        recipientFirstName
        recipientLastName
        dateOptionExercised
        exercisePricePerShare
        numberSharesTransferred
      }
    }
  }
}

# Terminate lines with \ character to allow command to span multiple lines.
# Escape quotation marks in body of mutation or query with backslashes.
# Use here document for data stream.
# Tested using the bash interpreter on Linux.
curl 'https://sandbox.ottertax.com/v2/graphql' \
  -i \
  -X POST \
  -H 'content-type: application/json' \
  -H 'access-token: YOUR ACCESS TOKEN' \
  -H 'client:       YOUR CLIENT ID' \
  -H 'uid:          YOUR UID' \
  -d @- <<END_DATA
    { 
      "query":"
        query {
          getF3922Statements {
            errors
            statements {
              pageInfo {
                hasNextPage
              }
              nodes {
                otxId
                uploaderId
                recipientFirstName
                recipientLastName
                dateOptionExercised
                exercisePricePerShare
                numberSharesTransferred
              }
            }
          }
        }
      "
    }
END_DATA
:: Terminate lines with ^ character to allow command to span multiple lines.
:: Precede quotation marks in body of mutation or query with triple backslashes.
:: Precede other quotation marks in data stream with single backslashes.
:: Tested using a command prompt on Windows 10.
curl "https://sandbox.ottertax.com/v2/graphql" ^
  -i ^
  -X POST ^
  -H "content-type: application/json" ^
  -H "access-token: YOUR ACCESS TOKEN" ^
  -H "client:       YOUR CLIENT ID" ^
  -H "uid:          YOUR UID" ^
  -d "{ \"query\":\" ^
        query { ^
          getF3922Statements { ^
            errors ^
            statements { ^
              pageInfo { ^
                hasNextPage ^
              } ^
              nodes { ^
                otxId ^
                uploaderId ^
                recipientFirstName ^
                recipientLastName ^
                dateOptionExercised ^
                exercisePricePerShare ^
                numberSharesTransferred ^
              } ^
            } ^
          } ^
        } ^
     \" }"
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.GetF3922StatementsResponse;

public class StatementReviewer {

  String endpoint = "https://sandbox.ottertax.com/v2/graphql";
  String gql = String.join("\n",
                           "query {",
                           "  getF3922Statements {",
                           "    errors",
                           "    statements {",
                           "      pageInfo {",
                           "        hasNextPage",
                           "      }",
                           "      nodes {",
                           "        otxId",
                           "        uploaderId",
                           "        recipientFirstName",
                           "        recipientLastName",
                           "        dateOptionExercised",
                           "        exercisePricePerShare",
                           "        numberSharesTransferred",
                           "      }",
                           "    }",
                           "  }",
                           "}");

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

  public void get() {
    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", "YOUR ACCESS TOKEN");
      httpPost.addHeader("client", "YOUR CLIENT ID");
      httpPost.addHeader("uid", "YOUR UID");

      // Put the query in the post body.
      StringEntity stringEntity = new StringEntity(querify(gql));
      httpPost.setEntity(stringEntity);
      CloseableHttpResponse httpResponse = httpClient.execute(httpPost);

      // Read the response.
      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);
    }
    GetF3922StatementsResponse getF3922StatementsResponse = gson.fromJson(response, GetF3922StatementsResponse.class);
    System.out.println(gson.toJson(getF3922StatementsResponse));
  }

  public static void main(String[] args) {
    StatementReviewer statementReviewer = new StatementReviewer();
    statementReviewer.get();
  }

}
// Using graphql-request from
// https://github.com/prisma-labs/graphql-request
// Example tested with node version 16.16.0
import { GraphQLClient, gql } from 'graphql-request'

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

  const graphQLClient = new GraphQLClient(endpoint, {
    headers: { 'content-type': 'application/json',
               'access-token': 'YOUR ACCESS TOKEN',
               'client': 'YOUR CLIENT ID',
               'uid': 'YOUR UID' }
  })
  const query = gql`
    query {
      getF3922Statements {
        errors
        statements {
          pageInfo {
            hasNextPage
          }
          nodes {
            otxId
            uploaderId
            recipientFirstName
            recipientLastName
            dateOptionExercised
            exercisePricePerShare
            numberSharesTransferred
          }
        }
      }
    }

  `

  const data = await graphQLClient.request(query)
  console.log(JSON.stringify(data))
}

main().catch((error) => console.error(error))
<?php
// Tested with php-cli version 8.1.2.
$query =<<<'END_DATA'
  query {
    getF3922Statements {
      errors
      statements {
        pageInfo {
          hasNextPage
        }
        nodes {
          otxId
          uploaderId
          recipientFirstName
          recipientLastName
          dateOptionExercised
          exercisePricePerShare
          numberSharesTransferred
        }
      }
    }
  }

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 {
  echo $response . "\n";
}
?>
# Using GQL from
# https://github.com/graphql-python/gql
# Tested using python version 3.10.4
from gql import gql, Client
from gql.transport.aiohttp import AIOHTTPTransport

transport = AIOHTTPTransport(url = "https://sandbox.ottertax.com/v2/graphql",
                             headers = { 'content-type': 'application/json',
                                         'access-token': 'YOUR ACCESS TOKEN',
                                         'client': 'YOUR CLIENT ID',
                                         'uid': 'YOUR UID' })
client = Client(transport=transport, fetch_schema_from_transport=True)
query = gql(
  """
    query {
      getF3922Statements {
        errors
        statements {
          pageInfo {
            hasNextPage
          }
          nodes {
            otxId
            uploaderId
            recipientFirstName
            recipientLastName
            dateOptionExercised
            exercisePricePerShare
            numberSharesTransferred
          }
        }
      }
    }

  """
)

result = client.execute(query)
print(result)
# Tested using ruby 3.1.2.
require( 'net/http' )
require( 'uri' )
require( 'json' )

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

query = <<-END_DATA
  query {
    getF3922Statements {
      errors
      statements {
        pageInfo {
          hasNextPage
        }
        nodes {
          otxId
          uploaderId
          recipientFirstName
          recipientLastName
          dateOptionExercised
          exercisePricePerShare
          numberSharesTransferred
        }
      }
    }
  }

END_DATA
request = Net::HTTP::Post.new(uri.request_uri, headers )
request.body = {query: query}.to_json
response = http.request(request)
if( response.code == '200' )
  payload = JSON.parse( response.body )
  STDOUT.puts( payload )
else
  STDOUT.puts( "Response code was #{response.code}:\n#{response.inspect}" )
end

Retrieving Specific 3922 Statements

The example that follows illustrates the getF3922Statements query to get a specific set of statements. A list of OTX IDs is passed to the query, and only statements with matching OTX IDs are returned. To search by uploader ID, supply a list of uploaderIds instead.

query {
  getF3922Statements(
    otxIds: [
      "1c5ac920-31d4-46df-9826-c7064d9a75c5"
      "1b0b2947-8ad9-4a58-8017-9200a6c98d99"
      "8a102da3-455a-41c6-8538-24d99986d80f"
      "0b84d520-041b-435a-8645-d754f3fcca80"
      "ad548fe6-a4b1-41a9-99e1-5b3ff291dad2"
    ]
  ) {
    errors
    statements {
      pageInfo {
        hasNextPage
      }
      nodes {
        otxId
        uploaderId
        recipientFirstName
        recipientLastName
        dateOptionExercised
        exercisePricePerShare
        numberSharesTransferred
      }
    }
  }
}
# Terminate lines with \ character to allow command to span multiple lines.
# Escape quotation marks in body of mutation or query with backslashes.
# Use here document for data stream.
# Tested using the bash interpreter on Linux.
curl 'https://sandbox.ottertax.com/v2/graphql' \
  -i \
  -X POST \
  -H 'content-type: application/json' \
  -H 'access-token: YOUR ACCESS TOKEN' \
  -H 'client:       YOUR CLIENT ID' \
  -H 'uid:          YOUR UID' \
  -d @- <<END_DATA
    { 
      "query":"
        query {
          getF3922Statements(
            otxIds: [
              \"1c5ac920-31d4-46df-9826-c7064d9a75c5\"
              \"1b0b2947-8ad9-4a58-8017-9200a6c98d99\"
              \"8a102da3-455a-41c6-8538-24d99986d80f\"
              \"0b84d520-041b-435a-8645-d754f3fcca80\"
              \"ad548fe6-a4b1-41a9-99e1-5b3ff291dad2\"
            ]
          ) {
            errors
            statements {
              pageInfo {
                hasNextPage
              }
              nodes {
                otxId
                uploaderId
                recipientFirstName
                recipientLastName
                dateOptionExercised
                exercisePricePerShare
                numberSharesTransferred
              }
            }
          }
        }
      "
    }
END_DATA
:: Terminate lines with ^ character to allow command to span multiple lines.
:: Precede quotation marks in body of mutation or query with triple backslashes.
:: Precede other quotation marks in data stream with single backslashes.
:: Tested using a command prompt on Windows 10.
curl "https://sandbox.ottertax.com/v2/graphql" ^
  -i ^
  -X POST ^
  -H "content-type: application/json" ^
  -H "access-token: YOUR ACCESS TOKEN" ^
  -H "client:       YOUR CLIENT ID" ^
  -H "uid:          YOUR UID" ^
  -d "{ \"query\":\" ^
        query { ^
          getF3922Statements( ^
            otxIds: [ ^
              \\\"1c5ac920-31d4-46df-9826-c7064d9a75c5\\\" ^
              \\\"1b0b2947-8ad9-4a58-8017-9200a6c98d99\\\" ^
              \\\"8a102da3-455a-41c6-8538-24d99986d80f\\\" ^
              \\\"0b84d520-041b-435a-8645-d754f3fcca80\\\" ^
              \\\"ad548fe6-a4b1-41a9-99e1-5b3ff291dad2\\\" ^
            ] ^
          ) { ^
            errors ^
            statements { ^
              pageInfo { ^
                hasNextPage ^
              } ^
              nodes { ^
                otxId ^
                uploaderId ^
                recipientFirstName ^
                recipientLastName ^
                dateOptionExercised ^
                exercisePricePerShare ^
                numberSharesTransferred ^
              } ^
            } ^
          } ^
        } ^
     \" }"
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.GetF3922StatementsResponse;

public class StatementReviewer {

  String endpoint = "https://sandbox.ottertax.com/v2/graphql";
  String gql = String.join("\n",
                           "query {",
                           "  getF3922Statements(",
                           "    otxIds: [",
                           "      \"1c5ac920-31d4-46df-9826-c7064d9a75c5\"",
                           "      \"1b0b2947-8ad9-4a58-8017-9200a6c98d99\"",
                           "      \"8a102da3-455a-41c6-8538-24d99986d80f\"",
                           "      \"0b84d520-041b-435a-8645-d754f3fcca80\"",
                           "      \"ad548fe6-a4b1-41a9-99e1-5b3ff291dad2\"",
                           "    ]",
                           "  ) {",
                           "    errors",
                           "    statements {",
                           "      pageInfo {",
                           "        hasNextPage",
                           "      }",
                           "      nodes {",
                           "        otxId",
                           "        uploaderId",
                           "        recipientFirstName",
                           "        recipientLastName",
                           "        dateOptionExercised",
                           "        exercisePricePerShare",
                           "        numberSharesTransferred",
                           "      }",
                           "    }",
                           "  }",
                           "}");

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

  public void get() {
    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", "YOUR ACCESS TOKEN");
      httpPost.addHeader("client", "YOUR CLIENT ID");
      httpPost.addHeader("uid", "YOUR UID");

      // Put the query in the post body.
      StringEntity stringEntity = new StringEntity(querify(gql));
      httpPost.setEntity(stringEntity);
      CloseableHttpResponse httpResponse = httpClient.execute(httpPost);

      // Read the response.
      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);
    }
    GetF3922StatementsResponse getF3922StatementsResponse = gson.fromJson(response, GetF3922StatementsResponse.class);
    System.out.println(gson.toJson(getF3922StatementsResponse));
  }

  public static void main(String[] args) {
    StatementReviewer statementReviewer = new StatementReviewer();
    statementReviewer.get();
  }

}
// Using graphql-request from
// https://github.com/prisma-labs/graphql-request
// Example tested with node version 16.16.0
import { GraphQLClient, gql } from 'graphql-request'

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

  const graphQLClient = new GraphQLClient(endpoint, {
    headers: { 'content-type': 'application/json',
               'access-token': 'YOUR ACCESS TOKEN',
               'client': 'YOUR CLIENT ID',
               'uid': 'YOUR UID' }
  })
  const query = gql`
    query {
      getF3922Statements(
        otxIds: [
          "1c5ac920-31d4-46df-9826-c7064d9a75c5"
          "1b0b2947-8ad9-4a58-8017-9200a6c98d99"
          "8a102da3-455a-41c6-8538-24d99986d80f"
          "0b84d520-041b-435a-8645-d754f3fcca80"
          "ad548fe6-a4b1-41a9-99e1-5b3ff291dad2"
        ]
      ) {
        errors
        statements {
          pageInfo {
            hasNextPage
          }
          nodes {
            otxId
            uploaderId
            recipientFirstName
            recipientLastName
            dateOptionExercised
            exercisePricePerShare
            numberSharesTransferred
          }
        }
      }
    }

  `

  const data = await graphQLClient.request(query)
  console.log(JSON.stringify(data))
}

main().catch((error) => console.error(error))
<?php
// Tested with php-cli version 8.1.2.
$query =<<<'END_DATA'
  query {
    getF3922Statements(
      otxIds: [
        "1c5ac920-31d4-46df-9826-c7064d9a75c5"
        "1b0b2947-8ad9-4a58-8017-9200a6c98d99"
        "8a102da3-455a-41c6-8538-24d99986d80f"
        "0b84d520-041b-435a-8645-d754f3fcca80"
        "ad548fe6-a4b1-41a9-99e1-5b3ff291dad2"
      ]
    ) {
      errors
      statements {
        pageInfo {
          hasNextPage
        }
        nodes {
          otxId
          uploaderId
          recipientFirstName
          recipientLastName
          dateOptionExercised
          exercisePricePerShare
          numberSharesTransferred
        }
      }
    }
  }

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 {
  echo $response . "\n";
}
?>
# Using GQL from
# https://github.com/graphql-python/gql
# Tested using python version 3.10.4
from gql import gql, Client
from gql.transport.aiohttp import AIOHTTPTransport

transport = AIOHTTPTransport(url = "https://sandbox.ottertax.com/v2/graphql",
                             headers = { 'content-type': 'application/json',
                                         'access-token': 'YOUR ACCESS TOKEN',
                                         'client': 'YOUR CLIENT ID',
                                         'uid': 'YOUR UID' })
client = Client(transport=transport, fetch_schema_from_transport=True)
query = gql(
  """
    query {
      getF3922Statements(
        otxIds: [
          "1c5ac920-31d4-46df-9826-c7064d9a75c5"
          "1b0b2947-8ad9-4a58-8017-9200a6c98d99"
          "8a102da3-455a-41c6-8538-24d99986d80f"
          "0b84d520-041b-435a-8645-d754f3fcca80"
          "ad548fe6-a4b1-41a9-99e1-5b3ff291dad2"
        ]
      ) {
        errors
        statements {
          pageInfo {
            hasNextPage
          }
          nodes {
            otxId
            uploaderId
            recipientFirstName
            recipientLastName
            dateOptionExercised
            exercisePricePerShare
            numberSharesTransferred
          }
        }
      }
    }

  """
)

result = client.execute(query)
print(result)
# Tested using ruby 3.1.2.
require( 'net/http' )
require( 'uri' )
require( 'json' )

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

query = <<-END_DATA
  query {
    getF3922Statements(
      otxIds: [
        "1c5ac920-31d4-46df-9826-c7064d9a75c5"
        "1b0b2947-8ad9-4a58-8017-9200a6c98d99"
        "8a102da3-455a-41c6-8538-24d99986d80f"
        "0b84d520-041b-435a-8645-d754f3fcca80"
        "ad548fe6-a4b1-41a9-99e1-5b3ff291dad2"
      ]
    ) {
      errors
      statements {
        pageInfo {
          hasNextPage
        }
        nodes {
          otxId
          uploaderId
          recipientFirstName
          recipientLastName
          dateOptionExercised
          exercisePricePerShare
          numberSharesTransferred
        }
      }
    }
  }

END_DATA
request = Net::HTTP::Post.new(uri.request_uri, headers )
request.body = {query: query}.to_json
response = http.request(request)
if( response.code == '200' )
  payload = JSON.parse( response.body )
  STDOUT.puts( payload )
else
  STDOUT.puts( "Response code was #{response.code}:\n#{response.inspect}" )
end

Downloading PDFs

You may wish to download PDF versions of statements as part of your review process. The PDF is available on both the generic statement object and the distinct 3922 statement object. The PDF is encoded as base 64 text, and the process for decoding it is explained here.