Overview

Commenda provides new sales and use tax registrations over API for remote sellers. Since each state’s registration requirements are complex and change frequently, Commenda collects this information over a 2-stage API. First, the API user calls GET /registrations/new-registration/fields to request the list of information that is required for a new registration. The API user is responsible for collecting that information from the seller. The returned object can be dynamically turned into a web form which can be used to collect the information. Once the information is collected, the seller calls POST /registrations/new-registration/fields to provide all required information to Commenda. If any fields are missing or invalid, an error will be returned.

Frontend implementation example:

An example implementation can be seen here.

Get registration fields:

The GET /registrations/new-registration/fields endpoint returns a deeply-nested object which can be parsed to generate a web form. In typescript, the response object’s type is the following:
interface Option {
  display: string;
  value: string;
}

interface Validation {
  regex?: string;
  // Date validations:
  minDate?: string;
  maxDate?: string;
}

interface FormQuestion {
  key: string;
  question: string;
  type: string;
  options?: Option[];
  previousValue?: string;
  required: boolean;
  validations?: Validation;
}

type RegistrationFormField = FormQuestion & {
  // Questions that must be shown only if some specific parent value is selected,
  // represented by the key in this mapping:
  dependentQuestions?: Record<string, FormQuestion>;
};

interface RegistrationFormSection {
  title: string;
  subtitle?: string;
  fields: RegistrationFormField[];
}

interface RegistrationFormStep {
  stepTitle: string;
  sections: RegistrationFormSection[];
}

interface RegistrationForm = RegistrationFormStep[];
It should be used in the following way:
  • Each RegistrationFormStep should be mapped to a single page in the form.
  • Each RegistrationFormSection should be a single section in the form.
  • Each RegistrationFormField should be mapped to a root-level input in the form.
    • The type of the input should depend on the value of RegistrationFormField.type. The available options for type are:
      • text
      • longtext
      • select
      • date
      • url
      • email
      • boolean
    • If the field has dependent questions, those should be rendered if the field’s value matches the key in the dependentQuestions map on the field.
    • If the field has validations, those should be applied to the field value before submission. If the field does not match the validation expressions, it will be rejected by the POST endpoint.

Submit registration request:

Once the information is collected by the dynamically-generated registration form, it can be submitted to the POST /registrations/new-registration/fields endpoint. It should be submitted in a simple shallowly-nested format, like so:
{
    "physicalLocationInState": false,
    "otherRegistrationsInState": false,
    // Dependent questions should not be nested:
    "hasFeinOrItin": false,
    "applyForFEIN": true,
    // Addresses should not be nested:
    "corporation.primaryBusinessLocation.addressLine1": "1776 Main St",
    "corporation.primaryBusinessLocation.city": "Santa Monica",
    "corporation.primaryBusinessLocation.state": "CA",
    "corporation.primaryBusinessLocation.country": "US",
    "corporation.primaryBusinessLocation.postalCode": "90401"
}