API Endpoints

EndpointMethodDescription
authentication/loginGETSPF API endpoints uses a JWT token for authentication and authorization. An authentication endpoint is available that will accept your product API Key or Hash and provide a valid JWT token.
formsPOSTThe forms endpoint receives a single home address and one or more work address(es) representing the employee’s physical work locations. It returns a list of applicable forms for the employee’s withholding scenario.
guided-flowsPOSTThe guided-flows endpoint receives one home address and one or more work address(es) representing the employee’s physical work locations. It returns a list of applicable flows for the employee’s withholding scenario.

Resulting flows are then used to filter the list of all applicable forms down to a list of required forms. Note that some states/scenarios do not required additional information to filter the applicable forms.
flowQuestionSet​/{flowId}​/{questionSetId}GETThe flowQuestionSet endpoint returns a question set for a given flow. Flow question sets are used to filter the list of potentially applicable forms down to a list of required forms. Each question set may contain one or more questions, validation regular expressions, and navigation aids.
formQuestionSet​/{formId}​/{questionSetId}GETThe formQuestionSet endpoint returns a question set for a given form. Each question set may contain one or more questions, validation regular expressions, and navigation aids.
fillPDFPOSTThe fillPdf endpoint receives a JSON object containing employee and employer information, along with answers to the form question sets. This endpoint then validates and processes the data returning a complete PDF in base 64 format and the relevant tax parameters. Validation is done using the regular expressions provided in the question sets to check validity, in addition to validation of question dependencies.
fillPdf​/{formId}GETA GET method request to the fillpdf endpoint will return information for a given form. It provides all the fields that can be expected as well as each field’s validation regular expression. This endpoint also returns the tax parameters schema for the requested form.
getAllFormIdsGETThe getAllFormsIds endpoint is used to return a list of all form IDs currently supported by the Symmetry Payroll Forms API. Both US and Canadian forms are returned from this endpoint.
getPdf​/{formId}GETThe getPdf endpoint returns the requested unflattened PDF by form ID as a byte stream with an Accept header value of application/pdf. See the getAllFormIds endpoint for all the available form IDs that are currently supported.
getAllFormsCatalogGETThe getAllFormsCatalog endpoint is used to return a list of all form IDs and their Versions currently supported by the Symmetry Payroll Forms API.
docs​/configGETThe docs/config endpoint returns the Open API (Swagger) configuration used to generate the SPF API Open API documentation page. This configuration can be used to generate a client API, see Swagger CodeGen for more details.

authentication/login (GET)

The SPF API endpoints use a JWT token for authentication and authorization. An authentication endpoint is available that will accept your product API Key or Hash and provide a valid JWT token.

To request a new JWT access token, make an HTTP GET request to the URL below, setting the api-key header to the API Key or Hash provided by Symmetry.

Authentication URL
https://api-staging.symmetry.com/authentication/login

curl --location --request GET 'https://api-staging.symmetry.com/authentication/login' \
--header 'Accept: application/json' \
--header 'api-key: y0urAPI-KeyG0esH3re'

Authentication Login Response

A successful authentication endpoint response will contain a JSON object with an access token that can be used for API requests.

{
  "accessToken" : "yoUrTokEnISh3re!"
}

An invalid API key will produce the following response.

{
    "status": "Unauthorized",
    "statusCode": 401,
    "reason": "Api key is invalid and/or expired"
}

🚧

Token Expiration

Important note: JWT tokens are valid for 24 hours. Once expired, you will receive the following response from the SPF-API endpoints and must request a new token.

Unauthorized Response

Below is a sample SPF-API endpoint response when an invalid, expired, or empty JWT token is provided.

{ "message" : "Unauthorized" }

JSON Web Token (JWT) Usage

The encrypted JWT token is to be included in each request to the SPF-API as a Bearer token in the Authorization header with the following formatting:

Authorization: Bearer  eyJhbGciOiJSU0EtT0FF...

curl --location --request GET 'https://api-staging.symmetry.com/spf/getPdf/W4101' \
--header 'Accept: application/pdf' \
--header 'Authorization: Bearer yoUrTokEnG0esH3re!

forms (POST)

The forms endpoint receives a single home address and one or more work addresse) representing the employee’s physical work locations. See the sample JSON requests below.

{
	"homeAddress": {
	    "streetAddress1": "11 S Union St",
		"city": "Montgomery",
		"state": "AL",
		"zipCode": "36130"
	},
	"workAddresses": [
		{
			"streetAddress1": "64 N Union St",
			"city": "Montgomery",
			"state": "AL",
			"zipCode": "36130"
		}
	]
}
{
    "homeAddress"  : {
        "streetAddress1": "5003 50 St",
        "streetAddress2": "",
        "city": "Red Deer",
        "provinceTerritory": "AB",
        "postalCode": "T4N 1Y2"
    },
	"workAddresses": [
		{
            "streetAddress1": "5003 50 St",
            "streetAddress2": "",
            "city": "Red Deer",
            "provinceTerritory": "AB",
            "postalCode": "T4N 1Y2"
		},
        {
            "streetAddress1": "2070 Harvey Ave",
            "streetAddress2": "#22",
            "city": "Kelowna",
            "provinceTerritory": "BC",
            "postalCode": "V1Y 8P8"
		}
	]
}

The /forms endpoint returns a list of applicable forms for the employee’s withholding scenario and various details about the form. In addition for clients onboarding employees using the United States, this endpoint returns a location data object that validates the accuracy of the home and work addresses, normalize them, and returns the location's geocoordinates.

🚧

Location Data

For more information about the location data object returned for US addresses, navigate to Location Data Page

{
    "forms": [
        {
            "id": "W4101",
            "name": "W-4",
            "title": "Employee's Withholding Certificate",
            "formVersion": "2021.1.0",
            "formLocality": "FEDERAL",
            "formType": "RESIDENT",
            "recommended": true,
            "initialQuestionSet": "QS1"
        },
        {
            "id": "AL101",
            "name": "A4",
            "title": "Employee's Withholding Exemption Tax Certificate",
            "formVersion": "2014.3.0",
            "formLocality": "AL",
            "formType": "RESIDENT",
            "recommended": true,
            "initialQuestionSet": "QS1"
        }
    ],
    "locationData": [
        {
            "id": "home",
            "inputAddress": {
                "streetAddress1": "11 S Union St",
                "city": "Montgomery",
                "state": "AL",
                "zipCode": "36130"
            },
            "normalizedAddress": {
                "streetAddress1": "11 S Union St",
                "city": "Montgomery",
                "state": "AL",
                "zipCode": "36130-2102"
            },
            "addressResultMessages": {
                "GS01": {
                    "message": "Geocoded to Street Level",
                    "description": "The record was coded to the street level (Zip+4 for US, full postal code for CA)."
                },
                "AS01": {
                    "message": "Valid Address",
                    "description": "The address is valid and deliverable according to official postal agencies."
                }
            },
            "latitude": "32.376634",
            "longitude": "-86.299645",
            "verified": true,
            "geocoded": false
        },
        {
            "id": "work1",
            "inputAddress": {
                "streetAddress1": "64 N Union St",
                "city": "Montgomery",
                "state": "AL",
                "zipCode": "36130"
            },
            "normalizedAddress": {
                "streetAddress1": "64 N Union St",
                "city": "Montgomery",
                "state": "AL",
                "zipCode": "36130-3020"
            },
            "addressResultMessages": {
                "GS01": {
                    "message": "Geocoded to Street Level",
                    "description": "The record was coded to the street level (Zip+4 for US, full postal code for CA)."
                },
                "AS01": {
                    "message": "Valid Address",
                    "description": "The address is valid and deliverable according to official postal agencies."
                }
            },
            "latitude": "32.378229",
            "longitude": "-86.299726",
            "verified": true,
            "geocoded": false
        }
    ]
}
{
    "errors": [],
    "forms": [
        {
            "id": "TD1",
            "name": "TD1",
            "title": "2023 Personal Tax Credits Return",
            "formVersion": "2023.01.0",
            "formLocality": "CANADA_FEDERAL",
            "formType": "CANADA_RESIDENT",
            "recommended": true,
            "initialQuestionSet": "QS1"
        },
        {
            "id": "TD1FR",
            "name": "TD1(FR)",
            "title": "Déclaration des crédits d'impôt personnels pour 2023",
            "formVersion": "2023.01.0",
            "formLocality": "CANADA_FEDERAL",
            "formType": "CANADA_RESIDENT",
            "recommended": true,
            "initialQuestionSet": "QS1"
        },
        {
            "id": "TD1AB",
            "name": "TD1AB",
            "title": "2023 Alberta Personal Tax Credits Return",
            "formVersion": "2023.01.0",
            "formLocality": "AB",
            "formType": "CANADA_RESIDENT",
            "recommended": false,
            "initialQuestionSet": "QS1"
        },
        {
            "id": "TD1ABFR",
            "name": "TD1AB(FR)",
            "title": "Déclaration des crédits d'impôt personnels de l'Alberta pour 2023",
            "formVersion": "2023.01.0",
            "formLocality": "AB",
            "formType": "CANADA_RESIDENT",
            "recommended": false,
            "initialQuestionSet": "QS1"
        },
        {
            "id": "TD1BC",
            "name": "TD1BC",
            "title": "2023 British Columbia Personal Tax Credits Return",
            "formVersion": "2023.01.0",
            "formLocality": "BC",
            "formType": "CANADA_RESIDENT",
            "recommended": false,
            "initialQuestionSet": "QS1"
        },
        {
            "id": "TD1BCFR",
            "name": "TD1BC(FR)",
            "title": "Déclaration des crédits d'impôt personnels de la Colombie-Britannique pour 2023",
            "formVersion": "2023.01.0",
            "formLocality": "BC",
            "formType": "CANADA_RESIDENT",
            "recommended": false,
            "initialQuestionSet": "QS1"
        }
    ],
    "locationData": []
}

Above are the example objects returned based upon the home and work addresses provided for US and Canadian addresses.

  1. id represents the actual form id which can be referenced from the getAllFormIds endpoint.
  2. name the actual name of the form to be completed
  3. formVersion the latest and recommended form version to be completed
  4. formLocality what the forms locality is
  5. formType what type of form resident, nonresident, military, exempt, pension etc..
  6. Based upon your home and work addresses, the response also provides whether a form is recommended to be completed by your employee.
  7. initialQuestionSet is provided and most always defaults to QS1 for what question set is the start of the form process

You can use the id and initialQuestionSet to submit to the /formQuestionSet/{formId}/{questionSetId} endpoint to begin the form completion process and get details about the next question set.

📘

Extended Support for US SPF Implementations

The US SPF implementation supports nexus, lock in letters, and interstate employees, features unique to the US context. Learn more below!

📘

Timezones

The /forms endpoint may be passed a timezone in the headers, as the Timezone header, of the request to specify the timezone that should be used when signing and dating a completed form. Specified timezones will only be used if signForm is set to SIGN. If no value is passed with the request, then the API with default to UTC.

Information on accepted Timezone IDs can be found here.


guided-flows (POST)

The guided-flows endpoint receives one home address and one or more work addresses representing the employee’s physical work location(s). It returns a list of applicable flows for the employee’s withholding scenario.

{
	"homeAddress": {
	    "streetAddress1": "11 S Union St",
		"city": "Montgomery",
		"state": "AL",
		"zipCode": "36130"
	},
	"workAddresses": [
		{
			"streetAddress1": "64 N Union St",
			"city": "Montgomery",
			"state": "AL",
			"zipCode": "36130"
		},
		{
			"streetAddress1": "1700 W Washington St",
			"city": "Phoenix",
			"state": "AZ",
			"zipCode": "85007"
		}
	]
}
{
	"homeAddress": {
	    "streetAddress1": "11 S Union St",
		"city": "Montgomery",
		"provinceTerritory": "AB",
		"postalCode": "M5H 2N2"
	},
	"workAddresses": [
		{
			"streetAddress1": "64 N Union St",
			"city": "Montgomery",
			"provinceTerritory": "AB",
			"postalCode": "M5H 2N2"
		}
	]
}

Resulting flows are then used to filter the list of all applicable forms down to a list of required forms. Note that some states/scenarios do not require additional information to filter the applicable forms.

{
    "flows": [
        {
            "id": "FEDERAL",
            "locality": "FEDERAL",
            "hasFlowQuestions": true,
            "initialQuestionSetId": "QS1",
            "localityFullName": "Federal"
        },
        {
            "id": "AL_RESIDENT",
            "locality": "AL",
            "residencyStatus": "RESIDENT",
            "hasFlowQuestions": true,
            "initialQuestionSetId": "QS1",
            "localityFullName": "Alabama"
        },
        {
            "id": "AZ_NONRESIDENT",
            "locality": "AZ",
            "residencyStatus": "NONRESIDENT",
            "hasFlowQuestions": true,
            "initialQuestionSetId": "QS1",
            "localityFullName": "Arizona"
        }
    ],
    "locationData": [
        {
            "id": "home",
            "inputAddress": {
                "streetAddress1": "11 S Union St",
                "city": "Montgomery",
                "state": "AL",
                "zipCode": "36130"
            },
            "normalizedAddress": {
                "streetAddress1": "11 S Union St",
                "city": "Montgomery",
                "state": "AL",
                "zipCode": "36130-2102"
            },
            "addressResultMessages": {
                "GS01": {
                    "message": "Geocoded to Street Level",
                    "description": "The record was coded to the street level (Zip+4 for US, full postal code for CA)."
                },
                "AS01": {
                    "message": "Valid Address",
                    "description": "The address is valid and deliverable according to official postal agencies."
                }
            },
            "latitude": "32.376634",
            "longitude": "-86.299645",
            "geocoded": false,
            "verified": true
        },
        {
            "id": "work1",
            "inputAddress": {
                "streetAddress1": "64 N Union St",
                "city": "Montgomery",
                "state": "AL",
                "zipCode": "36130"
            },
            "normalizedAddress": {
                "streetAddress1": "64 N Union St",
                "city": "Montgomery",
                "state": "AL",
                "zipCode": "36130-3020"
            },
            "addressResultMessages": {
                "GS01": {
                    "message": "Geocoded to Street Level",
                    "description": "The record was coded to the street level (Zip+4 for US, full postal code for CA)."
                },
                "AS01": {
                    "message": "Valid Address",
                    "description": "The address is valid and deliverable according to official postal agencies."
                }
            },
            "latitude": "32.378229",
            "longitude": "-86.299726",
            "geocoded": false,
            "verified": true
        },
        {
            "id": "work2",
            "inputAddress": {
                "streetAddress1": "1700 W Washington St",
                "city": "Phoenix",
                "state": "AZ",
                "zipCode": "85007"
            },
            "normalizedAddress": {
                "streetAddress1": "1700 W Washington St",
                "city": "Phoenix",
                "state": "AZ",
                "zipCode": "85007-2812"
            },
            "addressResultMessages": {
                "GS05": {
                    "message": "Geocoded to Rooftop Level",
                    "description": "The record was geocoded down to the rooftop level, meaning the point is within the property boundaries, usually the center."
                },
                "AS17": {
                    "message": "No USPS Mail Delivery",
                    "description": "US Only. The address is classified as not receiving mail by the USPS. This may be deliverable by third party delivery companies."
                },
                "AS02": {
                    "message": "Street Only Match",
                    "description": "The street address was verified but the suite/apartment number is missing or invalid."
                }
            },
            "latitude": "33.448573",
            "longitude": "-112.094486",
            "geocoded": true,
            "verified": true
        }
    ]
}
{
    "errors": [],
    "flows": [
        {
            "id": "CANADA_FEDERAL",
            "locality": "CANADA_FEDERAL",
            "hasFlowQuestions": true,
            "initialQuestionSetId": "QS1",
            "localityFullName": "Canada Federal"
        }
    ],
    "locationData": []
}

Above are example objects we will return based upon the home and work address(es) sent in.

  1. id represents the ID for flow path you can take. AL_RESIDENT means that you would follow the path to filter down to Alabama forms that an Alabama resident would fill out based on your home and work addresses.
  2. locality represents the current flows state abbreviation. If locality is FEDERAL it will not include a residency status.
  3. residencyStatus represents that as an employee, you are a resident or nonresident of the state abbreviation listed in locality.
  4. hasFlowQuestions is a boolean that when set to true, means that there are additional question sets needed to filter down to the appropriate forms list.
  5. initialQuestionSetId represents the starting question set for the guided-flows process.
  6. localityFullName represents the current flows state full name.

You can use the id and initialQuestionSetId to submit to the /flowQuestionSet​/{flowId}​/{questionSetId} endpoint to get details about the next question set.

Reciprocal States and States without Guided Flows

In certain scenarios within this United States, some localities do not have guided flows. This could be due to a reciprocal agreement. Other times, it's because certain forms are always returned based upon a set of home and work addresses. Unlike the United States, all Canadian provinces will return guided flows.

The example request below will not return a guided flow.

{
	"homeAddress": {
	  "streetAddress1": "501 N 3rd St",
		"city": "Harrisburg",
		"state": "PA",
		"zipCode": "17120"
	},
	"workAddresses": [
		{
			"streetAddress1": "200 W Washington St",
			"city": "Indianapolis",
			"state": "IN",
			"zipCode": "46204"
		}
	]
}

Given the agreement between Pennsylvania and Indiana, it can be determined that the IN_NONRESIDENT flow will result in form IN102 being required. Because of this, hasFlowQuestions is set to false and the specific formsToComplete array is provided without any further flow-based filtering with the flowQuestionSet endpoint.

See the example response below:

{
    "flows": [
        {
            "id": "FEDERAL",
            "locality": "FEDERAL",
            "hasFlowQuestions": true,
            "initialQuestionSetId": "QS1",
            "localityFullName": "Federal"
        },
        {
            "id": "PA_RESIDENT",
            "locality": "PA",
            "residencyStatus": "RESIDENT",
            "hasFlowQuestions": true,
            "initialQuestionSetId": "QS1",
            "localityFullName": "Pennsylvania"
        },
        {
            "id": "IN_NONRESIDENT",
            "locality": "IN",
            "residencyStatus": "NONRESIDENT",
            "hasFlowQuestions": false,
            "formsToComplete": [
                {
                    "id": "IN101",
                    "name": "WH-4",
                    "title": "Employee's Withholding Exemption and County Status Certificate",
                    "formVersion": "2020.12.0",
                    "formLocality": "IN",
                    "formType": "RESIDENT",
                    "initialQuestionSet": "QS1"
                },
                {
                    "id": "IN102",
                    "name": "WH-47",
                    "title": "Certificate of Residence",
                    "formVersion": "2021.3.0",
                    "formLocality": "IN",
                    "formType": "NON_RESIDENT",
                    "initialQuestionSet": "QS1"
                }
            ],
            "localityFullName": "Indiana"
        }
    ]
}

Above is an example object of formsToComplete returned based upon the home and work addresses provided. These forms will come with additional information you can collect and display in your application.

  1. id represents the actual form id which can be referenced from the getAllFormIds endpoint.
  2. name the actual name of the form to be completed
  3. formVersion the latest and recommended form version to be completed
  4. formLocality what the forms locality is
  5. formType what type of form resident, nonresident, military, exempt, pension etc..
  6. initialQuestionSet is provided and most always defaults to QS1 for what question set is the start of the form process

flowQuestionSet​/{flowId}​/{questionSetId} (GET)

The flowQuestionSet endpoint returns a question set for a given flow. Flow question sets are used to filter the list of potentially applicable forms down to a list of required forms. Each question set may contain one or more questions, validation regular expressions, and navigation aids.

Below are sample responses of the flowQuestionSet for the Federal flow, question set 1, and Canadian Federal question set 1.

{
    "id": "QS1",
    "questions": [
        {
            "id": "federalGuidedQ1",
            "questionText": "Select the correct choice: Foreign Earned Income Exclusion, Nonresident Alien, Standard Federal Form W4",
            "validationRegex": "",
            "htmlType": "INPUT",
            "displayType": "RADIO",
            "required": {
                "whenRequired": "ALWAYS"
            },
            "questionOptions": [
                {
                    "id": "foreignIncome",
                    "label": "Foreign Earned Income Exclusion - I expect to qualify for the foreign earned income exclusion under either the bona fide residence or physical presence test for calendar year or other tax year",
                    "formsToComplete": [
                        {
                            "id": "W6101",
                            "name": "673",
                            "title": "Statement For Claiming Exemption From Withholding on Foreign Earned Income Eligible for the Exclusion(s) Provided by Section 911",
                            "formVersion": "2019.1.0",
                            "formLocality": "FEDERAL",
                            "formType": "EXEMPT",
                            "initialQuestionSet": "QS1"
                        }
                    ]
                },
                {
                    "id": "nra",
                    "label": "Nonresident Alien who is EXEMPT - I am exempt from withholding on compensation for independent (or eligible dependent) personal services of a Nonresident Alien Individual, see instructions for Form 8233",
                    "formsToComplete": [
                        {
                            "id": "W8101",
                            "name": "8233",
                            "title": "Exemption From Withholding on Compensation for Independent (and Certain Dependent) Personal Services of a Nonresident Alien Individual",
                            "formVersion": "2018.9.0",
                            "formLocality": "FEDERAL",
                            "formType": "EXEMPT",
                            "initialQuestionSet": "QS1"
                        }
                    ]
                },
                {
                    "id": "standardFormEnglish",
                    "label": "Standard Federal Withholding (English) - I am not qualified for a Foreign Earned Income Exclusion. I want to complete the standard Federal W4",
                    "formsToComplete": [
                        {
                            "id": "W4101",
                            "name": "W-4",
                            "title": "Employee's Withholding Certificate",
                            "formVersion": "2023.12.0",
                            "formLocality": "FEDERAL",
                            "formType": "RESIDENT",
                            "initialQuestionSet": "QS1"
                        }
                    ]
                },
                {
                    "id": "standardFormSpanish",
                    "label": "Retención Federal Estándar (Español) - No estoy calificado para una exclusión de ingresos del trabajo en el extranjero. Quiero completar el estándar Federal W4",
                    "formsToComplete": [
                        {
                            "id": "W4101SP",
                            "name": "W-4(SP)",
                            "title": "Certificado de Retenciones del Empleado",
                            "formVersion": "2023.12.0",
                            "formLocality": "FEDERAL",
                            "formType": "RESIDENT",
                            "initialQuestionSet": "QS1"
                        }
                    ]
                }
            ],
            "isCalculated": false
        }
    ],
    "breadcrumbTitle": "Survey",
    "navigation": {
        "navigationType": "CONSTANT",
        "navigationMapping": {
            "hasMoreQuestions": false
        }
    },
    "notes": [
        {
            "htmlType": "PARAGRAPH",
            "suggestedPlacement": "BELOW_ALL_QUESTIONS",
            "text": "Nonresident Alien: If you are an alien individual (that is, an individual who is not a U.S. citizen), specific rules apply to determine if you are a resident alien or a nonresident alien for tax purposes. Generally, you are a resident alien if you meet either the \"green card test\" or the \"substantial presence test\" for the calendar year. Any person not meeting either test is generally a nonresident alien. Additionally, an alien individual who qualifies as a resident of a treaty country (defined later) or a bona fide resident of Puerto Rico, Guam, the Commonwealth of the Northern Mariana Islands, the U.S. Virgin Islands, or American Samoa is a nonresident alien individual."
        },
        {
            "htmlType": "PARAGRAPH",
            "suggestedPlacement": "BELOW_ALL_QUESTIONS",
            "text": "Instructions for Form 8233:",
            "href": "https://spfcdn.symmetry.com/pdfs/unitedStates/W8101/2018.9.0/i8233.pdf",
            "hrefText": "i8233"
        }
    ]
}
{
    "id": "QS1",
    "questions": [
        {
            "id": "residencyStatus",
            "questionText": "What is the residency status? Please read all the following options:",
            "validationRegex": "^(fullTimeResident|notFullTimeResident|employerIsNotInCanada)$",
            "validationErrorMessage": "Please select an option",
            "htmlType": "INPUT",
            "displayType": "RADIO",
            "required": {
                "whenRequired": "ALWAYS"
            },
            "questionOptions": [
                {
                    "id": "fullTimeResident",
                    "label": "Employee is a resident of Canada",
                    "value": "fullTimeResident"
                },
                {
                    "id": "notFullTimeResident",
                    "label": "Employee is not a resident of Canada for the full year",
                    "value": "notFullTimeResident"
                },
                {
                    "id": "employerIsNotInCanada",
                    "label": "Employer does not have an establishment in Canada but employee works in Canada",
                    "value": "employerIsNotInCanada"
                }
            ],
            "isCalculated": false
        }
    ],
    "breadcrumbTitle": "Residency Status",
    "navigation": {
        "navigationType": "VARIABLE",
        "questionId": "residencyStatus",
        "navigationMappings": {
            "notFullTimeResident": {
                "nextQuestionSetId": "QS17",
                "hasMoreQuestions": true
            },
            "employerIsNotInCanada": {
                "nextQuestionSetId": "QS22",
                "hasMoreQuestions": true
            },
            "fullTimeResident": {
                "nextQuestionSetId": "QS2",
                "hasMoreQuestions": true
            }
        }
    }
}

The information below will detail the various aspects of the response object above.

Outer Objects

  1. id represents which question set you are on.
  2. notes represents an object of one of the following html types PARAGRAPH, UNORDERED_LIST, IMAGE, or TABLE that is recommend for displaying the question or instructions.
  3. questions represents an array of question objects to help filter down to a formsToComplete array.
  4. breadcrumbTitle represents our recommended title for breadcrumbs relating to the question.
  5. navigation represents an object to help navigate through the guided-flows.

Notes Object

  1. notes take four html types (PARAGRAPH, UNORDERED_LIST, IMAGE, or TABLE).
  • Paragraph returns a string argument and a suggest placement.
{
      "htmlType": "PARAGRAPH",
      "suggestedPlacement": "BELOW_ALL_QUESTIONS",
      "text": "Nonresident Alien: If you are an alien individual (that is, an individual who is not a U.S. citizen), specific rules apply to determine if you are a resident alien or a nonresident alien for tax purposes. Generally, you are a resident alien if you meet either the \"green card test\" or the \"substantial presence test\" for the calendar year. Any person not meeting either test is generally a nonresident alien. Additionally, an alien individual who qualifies as a resident of a treaty country (defined later) or a bona fide resident of Puerto Rico, Guam, the Commonwealth of the Northern Mariana Islands, the U.S. Virgin Islands, or American Samoa is a nonresident alien individual."
}
  • Unordered lists return an array of strings, each representing an item in the list and a suggested placement. Unordered lists may also return an optional label for the list.
{
  "htmlType": "UNORDERED_LIST",
  "suggestedPlacement": "ABOVE_ALL_QUESTIONS",
  "optionalLabel": "Form A4-MS is to be used only for employees claiming exemption from Alabama's income tax withholding requirements based on the conditions set forth under the Military Spouses Residency Relief Act (P.L. 111-97). In order to qualify for this exemption, the employee must be able to answer true to all of the following conditions:",
  "listItems": [
    "My Spouse is an active duty military servicemember",
    "I am not a military servicemember",
    "My Spouse's current military orders assign him/her to a location in/near Alabama",
    "I am present in/near Alabama solely to be with my servicemember Spouse",
    "I and my military service member Spouse live at the same address",
    "My domicile is a state other than Alabama",
    "My military servicemember Spouse's domicile is the same as mine, or I will be selecting my Spouse's domicile for tax purposes"
  ]
}
  • Image returns the href of the image itself, a suggested placement, and may an optional label.
{
   "htmlType": "IMAGE",
   "suggestedPlacement": "ABOVE_ALL_QUESTIONS",
   "href": "https://spfcdn.symmetry.com/images/unitedStates/NY108/2018.1.0/ny108_opt-out-instructions.png"
}
  • Tables return an object containing an array of headers and an array of rows, representing the respective cells in each row. Table note types will also include a suggested placement and may include an optional label. An example can be found below.
"notes": [
                {
                    "htmlType": "TABLE",
                    "suggestedPlacement": "ABOVE_QUESTION",
                    "headerPlacement": "ROW",
                    "headers": [
                        {
                            "textValue": "Filing Status & Dependents",
                            "headerIndex": 0
                        },
                        {
                            "textValue": "Column A (Income)",
                            "headerIndex": 1
                        },
                        {
                            "textValue": "Column B (Income)",
                            "headerIndex": 2
                        }
                    ],
                    "rows": [
                        {
                            "cells": [
                                {
                                    "textValue": "Single",
                                    "isCalculated": false,
                                    "columnIndex": 0
                                },
                                {
                                    "textValue": "$0 to 13,447",
                                    "isCalculated": false,
                                    "columnIndex": 1
                                },
                                {
                                    "textValue": "Greater than $16,000",
                                    "isCalculated": false,
                                    "columnIndex": 2
                                }
                            ],
                            "rowIndex": 0
                        },
                        {
                            "cells": [
                                {
                                    "textValue": "Married Filing Jointly (1 or less dependents)",
                                    "isCalculated": false,
                                    "columnIndex": 0
                                },
                                {
                                    "textValue": "$0 to $22,676",
                                    "isCalculated": false,
                                    "columnIndex": 1
                                },
                                {
                                    "textValue": "Greater than $26,700",
                                    "isCalculated": false,
                                    "columnIndex": 2
                                }
                            ],
                            "rowIndex": 1
                        },
                        {
                            "cells": [
                                {
                                    "textValue": "Married Filing Jointly (2 or more dependents)",
                                    "isCalculated": false,
                                    "columnIndex": 0
                                },
                                {
                                    "textValue": "$0 to $27,292",
                                    "isCalculated": false,
                                    "columnIndex": 1
                                },
                                {
                                    "textValue": "Greater than $33,100",
                                    "isCalculated": false,
                                    "columnIndex": 2
                                }
                            ],
                            "rowIndex": 2
                        },
                        {
                            "cells": [
                                {
                                    "textValue": "Head of Household/Qualifying Widow(er) (1 or less dependents)",
                                    "isCalculated": false,
                                    "columnIndex": 0
                                },
                                {
                                    "textValue": "$0 to $19,118",
                                    "isCalculated": false,
                                    "columnIndex": 1
                                },
                                {
                                    "textValue": "Greater than $23,300",
                                    "isCalculated": false,
                                    "columnIndex": 2
                                }
                            ],
                            "rowIndex": 3
                        },
                        {
                            "cells": [
                                {
                                    "textValue": "Head of Household/Qualifying Widow(er) (2 or more dependents)",
                                    "isCalculated": false,
                                    "columnIndex": 0
                                },
                                {
                                    "textValue": "$0 to $22,790",
                                    "isCalculated": false,
                                    "columnIndex": 1
                                },
                                {
                                    "textValue": "Greater than $26,600",
                                    "isCalculated": false,
                                    "columnIndex": 2
                                }
                            ],
                            "rowIndex": 4
                        }
                    ]
                }
            ]

Questions Objects

  1. id represents the question id for the current question set.
  2. questionText represents the text recommend tp display the question.
  3. validationRegex represents the only values that the api will allow in relation to the matching question.
  4. validationErrorMessage represents a recommended error message related to the question.
  5. htmlType represents the html recommendation for a UI to display. HTML types used are INPUT, SELECT, TEXTAREA, PARAGRAPH, UNORDERED_LIST, IMAGE, and TABLE.
  6. displayType represents additional information about our HTML recommendation if required for that HTML type. Display types used are TEXT, RADIO, CHECKBOX, MULTI_SELECT_CHECKBOX, MONTH_YEAR, YEAR, TELEPHONE, EMAIL, ZIPCODE, INTEGER, PERCENT, and DOLLAR.
  7. required contains the element whenRequired. The whenRequired element has three enum values (ALWAYS, DEPENDENT, and NEVER). Based upon these values, it can be determined whether a question must be answered and sent back to the API to complete the fillPdf process.
  8. questionOptions can be represented in three ways. This will be broken down below.

🚧

Required, Validation Regex, and Validation Error Message

Before moving on it we want to make it clear that the required field is independent from the validation regex and validation error message field.

The required field only pertains to whether a question is required to be submitted to the API. If whenRequired is set to NEVER and no value is submitted then the validationRegex and validationErrorMessage will never be triggered. On the other hand, if a value is submitted, then it will be validated by the validationRegex and if it is an incorrect value we will display the validationErrorMessage.

"questionOptions": [
  {
    "label": "Yes",
    "value": true
  },
  {
    "label": "No",
    "value": false
  }
]
  1. questionOptions can represent an object array with labels and values. Labels represent the text that should be displayed.

These types of questionOptions allow you to continue to the next question set to determine forms.

"questionOptions": [
                {
                    "label": "I am not a RESIDENT of Alabama and meet the conditions set forth under the Military Spouses Residency Relief Act (P. L. 111-97) and will have no Alabama income tax liability",
                    "formsToComplete": [
                        {
                            "id": "AL103",
                            "name": "A4-MS",
                            "title": "Nonresident Military Spouse Withholding Tax Exemption Certificate",
                            "formVersion": "2019.9.0",
                            "formLocality": "AL",
                            "formType": "EXEMPT_MILITARY_SPOUSE",
                            "initialQuestionSet": "QS1"
                        }
                    ]
                },
                {
                    "label": "Quiero continuar en Español",
                    "formsToComplete": [
                        {
                            "id": "AL101SP",
                            "name": "A4(SP)",
                            "title": "Certificado para todo Empleado de Exención de Retencion de Ingresos para Pago de Impuestos",
                            "formVersion": "2016.5.0",
                            "formLocality": "AL",
                            "formType": "RESIDENT",
                            "initialQuestionSet": "QS1"
                        }
                    ]
                },
                {
                    "label": "I want to continue in English",
                    "formsToComplete": [
                        {
                            "id": "AL101",
                            "name": "A4",
                            "title": "Employee's Withholding Exemption Tax Certificate",
                            "formVersion": "2014.3.0",
                            "formLocality": "AL",
                            "formType": "RESIDENT",
                            "initialQuestionSet": "QS1"
                        }
                    ]
                }
            ]
  1. questionOptions can represent labels and a formsToComplete array. Labels represent the text recommend to be displayed, while the formsToComplete array gives the list of forms recommended and other important information about the form based on the question option chosen.
"questionOptions": [
                {
                    "label": "I want to claim exemption from Kentucky withholding",
                    "formsToComplete": [
                        {
                            "id": "KY101",
                            "name": "K-4",
                            "title": "Kentucky's Withholding Certificate",
                            "formVersion": "2020.12.0",
                            "formLocality": "KY",
                            "formType": "RESIDENT",
                            "initialQuestionSet": "QS1"
                        }
                    ]
                },
                {
                    "label": "I want to increase my Kentucky additional withholding amount",
                    "formsToComplete": [
                        {
                            "id": "KY101",
                            "name": "K-4",
                            "title": "Kentucky's Withholding Certificate",
                            "formVersion": "2020.12.0",
                            "formLocality": "KY",
                            "formType": "RESIDENT",
                            "initialQuestionSet": "QS1"
                        }
                    ]
                },
                {
                    "label": "None of the above"
                }
            ],
  1. questionOptions can represent labels and an empty formsToComplete array. Given a certain type of question option, it is possible that you will have no forms to complete. Above if the user selects "None of the above", they will not receive a formsToComplete array.

Forms To Complete Object

  1. id represents the actual form id which can be referenced from the getAllFormIds endpoint.
  2. name the actual name of the form to be completed.
  3. formVersion the latest and recommended form version to be completed.
  4. formLocality what the forms locality is.
  5. formType what type of form resident, nonresident, military, exempt, pension, etc.
  6. initialQuestionSet is provided and most always defaults to QS1 for what question set is the start of the form process.

Navigation Objects

navigationType represents one of the two types of navigation a question can take (CONSTANT or VARIABLE).

CONSTANT navigation objects represent static navigation, in which, the next question set is always the next question set, regardless of the answers to questions contained within the current question set. Variable navigation objects represent dynamic navigation, in which, the next question is dependent upon the selected answer for question within the current question set.

A constant navigation object will consist of the following:

  1. navigationType with a value of CONSTANT
  2. navigationMapping which will contain a boolean flag hasMoreQuestions.
  • When hasMoreQuestions with a value of true represents that there are additional question sets that need to be completed. The navigationMapping will include nextQuestionSetId for the ID of the next question set.
  • When hasMoreQuestions with a value of false represents that there are no more questions to be asked. The navigationMapping will not include nextQuestionSetId.
"navigation": {
  "navigationType": "CONSTANT",
  "navigationMapping": {
    "hasMoreQuestions": false
  }
}

A variable navigation object will consist of the following:

  1. navigationType
  2. questionId matches the id from the current or a previous questionSet object.
  3. navigationMappings represent an additional object to determine how to navigate through question sets. It will contain additional objects whose keys match the question options values. These objects will contain two additional keys, nextQuestionSetId and hasMoreQuestions.
  • nextQuestionSetId will return a string value in the format (QS#).
  • hasMoreQuestions will return a boolean. A value of true means that there are additional question sets that need to be completed. A value of false means that this question is the last question in the question set.

In the example below, we can see that selecting an answer of true will result in hasMoreQuestions returning false, and a formsToComplete array returned with the selected question option. Selecting false requires additional questions to make a form determination.

{
    "id": "QS1",
    "questions": [
        {
            "id": "isTerminatingMilitarySpouseExemption",
            "questionText": "Do you currently have a military spouse exemption that you no longer qualify for OR no longer wish to claim?",
            "validationErrorMessage": "Please select an option",
            "htmlType": "INPUT",
            "displayType": "RADIO",
            "required": {
                "whenRequired": "ALWAYS"
            },
            "questionOptions": [
                {
                    "label": "Yes",
                    "value": true,
                    "formsToComplete": [
                        {
                            "id": "AZ101",
                            "name": "A-4",
                            "title": "Employee's Arizona Withholding Election",
                            "formVersion": "2020.12.0",
                            "formLocality": "AZ",
                            "formType": "RESIDENT",
                            "initialQuestionSet": "QS1"
                        },
                        {
                            "id": "AZ102",
                            "name": "WEC",
                            "title": "Employee Withholding Exemption Certificate",
                            "formVersion": "2020.12.0",
                            "formLocality": "AZ",
                            "formType": "EXEMPT",
                            "initialQuestionSet": "QS1"
                        }
                    ]
                },
                {
                    "label": "No",
                    "value": false
                }
            ],
            "isCalculated": false
        }
    ],
    "breadcrumbTitle": "Military Spouse Exemption",
    "navigation": {
        "navigationType": "VARIABLE",
        "questionId": "isTerminatingMilitarySpouseExemption",
        "navigationMappings": {
            "false": {
                "nextQuestionSetId": "QS2",
                "hasMoreQuestions": true
            },
            "true": {
                "hasMoreQuestions": false
            }
        }
    }
}

🚧

Before Moving On From Navigation!

Due to the nature of using a VARIABLE navigation type, the next page (question set) could have multiple paths. As specifically designed, our API returns navigationMappings as there could be more than one path.

In contrast, using a CONSTANT navigation type, the next page (question set) is always the next question with no variance. Therefore, by design our API will return navigationMapping as there is only one path.

Language Support

The flowQuestionSet endpoint also provides language support for English, Spanish, and French using the Accept-Language request HTTP header.


formQuestionSet​/{formId}​/{questionSetId} (GET)

The formQuestionSet endpoint returns a question set for a given form. Each question set may contain one or more questions, validation regular expression, and navigation aids.

We provide form versioning through a query parameter. You can collect all the details of a form and its version from the ​fillPdf/{formId} (GET) endpoint. This is an optional query param and if not included will provide the latest form version. It should be noted that we recommend that a form version is included and will send back a warning with additional information if it is not.

We provide language support for English, Spanish and French.

👍

Endpoint Demo

Clients can log into Symmetry's Client Support Center to see a full end-to-end demo of this endpoint.

Below are example responses returned for Question Set 6 (QS6) of the Federal W4 form and Question Set 1 (QS2) of Form TD1AB.

{
    "id": "QS6",
    "notes": [
        {
            "htmlType": "PARAGRAPH",
            "suggestedPlacement": "ABOVE_ALL_QUESTIONS",
            "text": "If you choose the option in Step 2(b) on Form W-4, complete this worksheet (which calculates the total extra tax for all jobs) on only ONE Form W-4. Withholding will be most accurate if you complete the worksheet and enter the result on the Form W-4 for the highest paying job."
        },
        {
            "htmlType": "PARAGRAPH",
            "suggestedPlacement": "ABOVE_ALL_QUESTIONS",
            "text": "If more than one job has annual wages of more than $120,000 or there are more than three jobs, see Publication 505 for additional tables."
        },
        {
            "htmlType": "IMAGE",
            "suggestedPlacement": "ABOVE_ALL_QUESTIONS",
            "href": "https://spfcdn-test.symmetry.com/images/W4101/2020.1.0/w4101_single-table.png",
            "optionalLabel": "Page 4 Taxable Wage and Salary Tables"
        }
    ],
    "questions": [
        {
            "id": "multipleJobsWorksheetLine1",
            "questionText": "1. Two jobs. If you have two jobs or you're married filing jointly and you and your spouse each have one  job, find the amount from the appropriate table on page 4. Using the \"Higher Paying Job\" row and the \"Lower Paying Job\" column, find the value at the intersection of the two household salaries and enter that value on line 1. Then, skip to line 3.",
            "validationRegex": "^[+-]?[0-9]{1,3}(?:,?[0-9]{3})*(?:\\.[0-9]{2})?$",
            "validationErrorMessage": "Please enter a valid dollar amount",
            "htmlType": "INPUT",
            "displayType": "DOLLAR",
            "required": {
                "whenRequired": "DEPENDENT"
            },
            "isCalculated": false
        },
        {
            "id": "multipleJobsWorksheetLine3",
            "questionText": "3. Enter the number of pay periods per year for the highest paying job. For example, if that job pays weekly, enter 52; if it pays every other week, enter 26; if it pays monthly, enter 12, etc.",
            "validationRegex": "^[1-9]\\d*|0$",
            "validationErrorMessage": "Value must be a whole number zero or greater",
            "htmlType": "INPUT",
            "displayType": "INTEGER",
            "required": {
                "whenRequired": "DEPENDENT"
            },
            "isCalculated": false
        },
        {
            "id": "multipleJobsWorksheetLine4",
            "questionText": "4. Divide the annual amount on line 1 or 2c by the number of pay periods on line 3. (You may round this  to the closest whole dollar amount.)  Enter this amount here and on line 4c of Form W-4 for the highest paying job.",
            "htmlType": "INPUT",
            "displayType": "DOLLAR",
            "required": {
                "whenRequired": "NEVER"
            },
            "calculation": {
                "formula": "( multipleJobsWorksheetLine3 > 0 ) ? ( multipleJobsWorksheetLine1 / multipleJobsWorksheetLine3 ) : 0",
                "scale": 2
            },
            "isCalculated": true
        }
    ],
    "breadcrumbTitle": "Step 2b",
    "navigation": {
        "navigationType": "CONSTANT",
        "navigationMapping": {
            "nextQuestionSetId": "QS21",
            "hasMoreQuestions": true
        }
    }
}
{
    "id": "QS1",
    "notice": {
        "type": "WARNING",
        "message": "Query parameter formVersion not provided, api returning current version of the form.  Form version may change if a new version of the form is release, and may result in questions values already captured becoming invalid when generating a pdf.  For best practice, please use the formVersion query parameter."
    },
    "questions": [
        {
            "id": "isEligible",
            "questionText": "Select one",
            "validationRegex": "^(true|false)$",
            "validationErrorMessage": "Please select an option",
            "htmlType": "INPUT",
            "displayType": "RADIO",
            "required": {
                "whenRequired": "ALWAYS"
            },
            "questionOptions": [
                {
                    "label": "Yes - One of the above scenarios applies to me",
                    "value": true
                },
                {
                    "label": "No - The above scenarios do not apply to me",
                    "value": false
                }
            ],
            "isCalculated": false
        }
    ],
    "breadcrumbTitle": "Qualifications Page",
    "navigation": {
        "navigationType": "VARIABLE",
        "questionId": "isEligible",
        "navigationMappings": {
            "false": {
                "nextQuestionSetId": "INELIGIBLE",
                "hasMoreQuestions": false
            },
            "true": {
                "nextQuestionSetId": "QS2",
                "hasMoreQuestions": true
            }
        }
    },
    "notes": [
        {
            "htmlType": "UNORDERED_LIST",
            "suggestedPlacement": "ABOVE_ALL_QUESTIONS",
            "optionalLabel": "Fill out this form only if you are an employee working in Alberta or a pensioner residing in Alberta and any of the following apply:",
            "listItems": [
                "you have a new employer or payer and you will receive salary, wages, commissions, pensions, employment insurance benefits, or any other remuneration",
                "you want to change amounts you previously claimed (for example, the number of your eligible dependants has changed)",
                "you want to increase the amount of tax deducted at source"
            ]
        },
        {
            "htmlType": "PARAGRAPH",
            "suggestedPlacement": "ABOVE_ALL_QUESTIONS",
            "text": "If you do not fill out Form TD1AB, your employer or payer will deduct taxes after allowing the basic personal amount only."
        }
    ]
}

Outer Objects

  1. id represents which question set you are on.
  2. questions represents an array of question objects to help you filter down to a formsToComplete array.
  3. breadcrumbTitle represents our recommended title for breadcrumbs relating to the question.
  4. navigation represents an object to help navigate through the guided flows.

Questions Objects

  1. id represents the question id for the current question set.
  2. questionText represents the text we recommend you display related to the question.
  3. validationRegex represents the only values that the API will allow in relation to the matching question.
  4. validationErrorMessage represents a recommended error message related to the question.
  5. isCalculated a boolean flag to specify if a question is result of performing a calculation based on response to other questions from the form and are provided for context. Note: when isCalculated is true, validationRegex and validationErrorMessage will not be provided, instead returning a calculation object. Calculated fields, if displayed, should be disabled to prevent users from setting their value, instead the value should be derived using the calculation provided in the calculation object. Calculated questions are not required when submitting question responses to the (fillpdf POST) endpoint and will be ignored if provided. Current clients can log into Symmetry's Client Support Center to view a demo of this element in the API.
  6. calculation represents a formula, scaling, and rounding mode to calculate the value for a calculated field. Calculation is only returned if isCalculated is true and will always contain a formula and a scale, with a rounding mode returned when required by the particular form. The formula will contain the question IDs, for which, the question's value should be substituted to process the calculation. Please note, the formula can contain values from a previous answer. If you choose to implement calculated fields, as you collect answers to questions, it will be necessary to compare the previously answered question id to see if it is a value included in the calculation formula.

In the calculation below, we must retrieve the user's response to question specified in the formula, worksheetB_line7. Next, we divide worksheetB_line7's value by 1000, and finally, we round the result to a scale of 0 decimal places, i.e. a whole number.

"calculation": {
  "formula": "worksheetB_line7 / 1000",
  "roundingMode": "HALF_UP",
  "scale": 0
}

📘

Supported rounding modes

SPF API supports 3 different rounding modes:

CEILING: Rounding should be performed to the next largest value, i.e. rounding up.
Ex: 1.5 -> 2

FLOOR: Rounding should be performed to the next smallest value, i.e. rounding down.
Ex: 1.5 -> 1

HALF_UP: Rounding should be performed to the nearest value with ties rounding up.
Ex: 1.2 -> 1
1.7 -> 2
1.5 -> 2

  1. htmlType represents our html recommendation for a UI to display. HTML types we use are INPUT, SELECT, TEXTAREA, PARAGRAPH, UNORDERED_LIST, IMAGE, and TABLE.
  2. displayType represents additional information about our html recommendation if required for that html type. Display types we use are TEXT, RADIO, CHECKBOX, MULTI_SELECT_CHECKBOX, MONTH_YEAR, YEAR, TELEPHONE, EMAIL, ZIPCODE, INTEGER, PERCENT, and DOLLAR.
  3. required represents an object whenRequired. This object whenRequired has three enum values (ALWAYS, DEPENDENT, and NEVER). Based upon these values you will know whether a question must be answered and sent back to the API to complete the process. Existing clients can log into Symmetry's Client Support Center to watch a demo led by one of Symmetry's engineers on how to use this element.

Field Validation whenRequired is set to NEVER

🚧

Required, Validation Regex, and Validation Error Message

Before moving on it we want to make it clear that the required field is independent from the validation regex and validation error message field.

The required field only pertains to whether a question is required to be submitted to the API. If whenRequired is set to NEVER and no value is submitted then the validationRegex and validationErrorMessage will never be triggered. On the other hand, if a value is submitted, then it will be validated by the validationRegex and if it is an incorrect value we will display the validationErrorMessage.

whenRequired is set to DEPENDENT

🚧

When the whenRequired field is set to DEPENDENT, this means that a question is situationally required based on the answer to a previous question. This is done so that the API can process form questions during pdf generation, requiring DEPENDENT questions only when situationally required. Client applications should treat DEPENDENT questions as required, and validate user input as such.

  1. questionOptions represents an object array containing labels and values. Labels represent the text we recommend is displayed, while values are what we accept to be returned to the API. Radio and select will come with questionOptions.
"questionOptions": [
  {
    "label": "I choose to have Arizona withholding at the rate of 0.8% of my gross taxable wages",
    "value": "0.8"
  },
  {
    "label": "I choose to have Arizona withholding at the rate of 1.3% of my gross taxable wages",
    "value": "1.3"
  },
  {
    "label": "I choose to have Arizona withholding at the rate of 1.8% of my gross taxable wages",
    "value": "1.8"
  },
  ...
]
  1. multiSelectQuestionOptions represents a form of questionOptions that will be provided if the question has a display type of MULTI_SELECT_CHECKBOX.
"multiSelectQuestionOptions": [
    {
           "id": "noOneCanClaimMe",
           "label": "No one else can claim me as a dependent"
     },
     {
            "id": "claimSpouse",
            "label": "I claim my spouse as a dependent"
     }
 ]
  1. notes represents an object of one of the following html types PARAGRAPH, IMAGE, UNORDERED_LIST, or TABLE we recommend you display related to the question. Notes are returned in order by array index and will also contain a suggested placement in relation to the questions in the question set. Suggest placement will be one of four values: ABOVE_QUESTION, BELOW_QUESTION, ABOVE_ALL_QUESTIONS, BELOW_ALL_QUESTIONS.
  • Paragraph returns a string argument and a suggest placement.
{
   "htmlType": "PARAGRAPH",
   "suggestedPlacement": "ABOVE_QUESTION",
   "text": "You may elect an Arizona withholding percentage of zero if you expect to have no Arizona income tax liability for the current year. Arizona tax liability is gross tax liability less any tax credits, such as the family tax credit, school tax credits, or credits for taxes paid to other states. If you make this election, your employer will not withhold Arizona income tax from your wages for payroll periods beginning after the date you file the form. To keep this election for the next calendar year, you must give your employer an updated Form A-4. If you do not, your employer may withhold Arizona income tax from your wages and salary until you submit an updated Form A-4."
}
  • Unordered lists return an array of strings, each representing an item in the list and a suggested placement. Unordered lists may also return an optional label for the list.
{
  "htmlType": "UNORDERED_LIST",
  "suggestedPlacement": "BELOW_QUESTION",
  "optionalLabel": "To qualify as your dependent (line 2 of form), a person must:",
  "listItems": [
        "(a) receive more than 1/2 of their support from you for the year",
         "(b) not be claimed as a dependent by such person's spouse",
         "(c) be a citizen or resident of the United States and,",
         "(d) have your home as their principal residence and be a member of your household for the entire year or be related to you as follows: son, daughter, grandchild, stepson, stepdaughter, son-in-law or daughter-in-law; your father, mother, grandparent, stepfather, stepmother, father-in-law or mother-in-law; your brother, sister, stepbrother, stepsister, half-brother, half-sister, brother-in-law or sister-in-law; your uncle, aunt, nephew or niece (but only if related by blood)."
   ]
 }
  • Image returns the href of the image itself, a suggested placement, and may an optional label. SPF API provides images of each page of every unfilled withholding form, with a standard width of 1280 pixels, that can be displayed to allow users to view the form without needing to render the pdf. Other images, returned for question context, will always have a maximum width of 800 pixels.
{
  "htmlType": "IMAGE",
  "suggestedPlacement": "BELOW_QUESTION",
  "href": "https://spfcdn-test.symmetry.com/images/unitedStates/CA101/2024.01.0/ca101_worksheetC-table.png",
  "optionalLabel": "Worksheet C Income Tables"
}
  • Tables return an object containing an array of headers and an array of rows, representing the respective cells in each row. Table note types will also include a suggested placement and may include an optional label. An example can be found below.
{
     "htmlType": "TABLE",
     "suggestedPlacement": "BELOW_ALL_QUESTIONS",
     "headerPlacement": "ROW",
     "headers": [
         {
             "textValue": "Filing Status & Dependents",
             "headerIndex": 0
         },
         {
             "textValue": "Income range from all sources",
             "headerIndex": 1
         }
     ],
     "rows": [
         {
             "cells": [
                 {
                     "textValue": "Single",
                     "isCalculated": false,
                     "columnIndex": 0
                 },
                 {
                     "textValue": "$13,447 to $16,000",
                     "isCalculated": false,
                     "columnIndex": 1
                 }
             ],
             "rowIndex": 0
         },
         {
             "cells": [
                  {
                     "textValue": "Married Filing Jointly (1 or less dependents)",
                     "isCalculated": false,
                     "columnIndex": 0
                 },
                 {
                     "textValue": "$22,676 to $26,700",
                     "isCalculated": false,
                     "columnIndex": 1
                 }
             ],
             "rowIndex": 1
         },
         {
             "cells": [
                 {
                     "textValue": "Married Filing Jointly (2 or more dependents)",
                     "isCalculated": false,
                     "columnIndex": 0
                 },
                 {
                     "textValue": "$27,292 to $33,100",
                     "isCalculated": false,
                     "columnIndex": 1
                 }
             ],
             "rowIndex": 2
         },
         {
             "cells": [
                 {
                     "textValue": "Head of Household/Qualifying Widow(er) (1 or less dependents)",
                     "isCalculated": false,
                     "columnIndex": 0
                 },
                 {
                     "textValue": "$19,118 to $23,300",
                     "isCalculated": false,
                     "columnIndex": 1
                 }
             ],
             "rowIndex": 3
         },
         {
             "cells": [
                 {
                     "textValue": "Head of Household/Qualifying Widow(er) (2 or more dependents)",
                     "isCalculated": false,
                     "columnIndex": 0
                 },
                 {
                     "textValue": "$22,790 to $26,600",
                     "isCalculated": false,
                     "columnIndex": 1
                 }
             ],
             "rowIndex": 4
         }
     ]
 }

Navigation Objects:
navigationType represents one of the two types of navigation a question can take (CONSTANT or VARIABLE).

CONSTANT navigation objects represent static navigation, in which, the next question set is always the next question set, regardless of the answers to questions contained within the current question set. Variable navigation objects represent dynamic navigation, in which, the next question is dependent upon the selected answer for question within the current question set.

A constant navigation object will consist of the following:

  1. navigationType with a value of CONSTANT
  2. navigationMapping which will contain a boolean flag hasMoreQuestions.
  • When hasMoreQuestions with a value of true represents that there are additional question sets that need to be completed. The navigationMapping will include nextQuestionSetId for the ID of the next question set.
"navigation": {
  "navigationType": "CONSTANT",
  "navigationMapping": {
    "nextQuestionSetId": "QS9",
    "hasMoreQuestions": true
  }
}
  • When hasMoreQuestions with a value of false represents that there are no more questions to be asked. The navigationMapping will not include nextQuestionSetId and will include a perjury statement from the form that should be displayed.
"navigation": {
  "navigationType": "CONSTANT",
  "navigationMapping": {
    "hasMoreQuestions": false,
    "perjuryStatement": "Under penalties of perjury, I certify that I have examined this certificate and to the best of my knowledge and belief, it is true, correct, and complete."
  }
}

A variable navigation object will consists of the following:

  1. navigationType with a value of VARIABLE
  2. questionId will contain the ID of the question for which the associated answer determines the next question.
  3. navigationMappings represent a mapping of answers to the question referenced by questionId. Based on the answer provided by the user, one mapping should be used to determine if additional questions exist, and if so, the next question set ID. Each mapping may contain the following:
  • hasMoreQuestions is a boolean that represent if there are additional questions. A value of true means that there are additional question sets that need to be completed. A value of false means that you this question is the last question in the question set.
  • nextQuestionSetId will return a string value in the format (QS#) giving the next question set id OR the string INELIGIBLE. INELIGIBLE represents that based on this answer the employee is not eligible to complete the form. As with constant navigation types, nextQuestionSetId will not be returned if hasMoreQuestions is false.
  • perjuryStatement will return a string. This string represents a form specific, legal statement, that must be agreed upon before signing the form. A form can have one or more perjury statements based upon the question set process and these can contain dynamic variables that must be parsed and replaced.

The example below represents a variable navigation object, in which, the next question set is dependent on the user's selected answer to the question filingStatus in the current question set. If the user had select EXEMPT, we can see that the hasMoreQuestions is false and a perjury statement is available to display to the user. Otherwise, selecting any other filing status value would result in additional questions, with the next question ID of QS2.

"navigation": {
        "navigationType": "VARIABLE",
        "questionId": "filingStatus",
        "navigationMappings": {
            "EXEMPT": {
                "hasMoreQuestions": false,
                "perjuryStatement": "Under penalties of perjury, I certify that I have examined this certificate and to the best of my knowledge and belief, it is true, correct, and complete."
            },
            "MARRIED_FILING_SEPARATELY": {
                "nextQuestionSetId": "QS2",
                "hasMoreQuestions": true
            },
            "MARRIED": {
                "nextQuestionSetId": "QS2",
                "hasMoreQuestions": true
            },
            "HEAD_OF_FAMILY": {
                "nextQuestionSetId": "QS2",
                "hasMoreQuestions": true
            },
            "SINGLE": {
                "nextQuestionSetId": "QS2",
                "hasMoreQuestions": true
            }
        }
    }

In the next example, we show an example of dynamic perjury statements. A dynamic perjury statement is simply a perjury statement that requires a value to be added to the statement itself. This is done through parsing and replacing the variable within the perjury statement string. Variables within perjury statement will be wrapped by double curly braces ("{{ }}"), with the contents representing questionId. The perjury statement should be checked for variables, and updated as appropriate with the user's answer to the respective question before displaying.

Known forms that have dynamic perjury statements are the AZ107, IL102, IN102, NY105, WI103, and the WV102.

"navigation": {
  "navigationType": "CONSTANT",
  "navigationMapping": {
    "hasMoreQuestions": false,
    "perjuryStatement": "I declare under penalties of perjury that I am a resident of the state of {{ reciprocalResidentState }}"
  }
}

🚧

Before Moving On From Navigation!

Due to the nature of using a VARIABLE navigation type, the next page (question set) could have multiple paths. As specifically designed, our API returns navigationMappings as there could be more than one path.

In contrast, using a CONSTANT navigation type, the next page (question set) is always the next question with no variance. Therefore, by design our API will return navigationMapping as there is only one path.

Capturing and Storing Answers

Client implementations of the SPF API are responsible for capturing and storing answers from each question set.

Once all answers have been collected, they can be submitted to the fillPdf (POST) endpoint to generate the completed form and relevant tax parameters.

Language Support

The formQuestionSet endpoint also provides language support for English, Spanish, and French using the Accept-Language request HTTP header.

Support for Multiple Form Revisions

SPF API can support multiple form versions or revisions. Specifying a form version can be accomplished with a query parameter. You can collect all the details of a form and its version from the fillPdf​/{formId} (GET) endpoint. This is an optional query param and if not included, will provide the latest form version.

🚧

Form Versioning Syntax Example

formQuestionSet/{{formId}}/{{questionSetId}}?formVersion={{formVersion}}

❗️

Form Versions

The SPF API will typically support the current version or revision of a form. As a convenience, SPF API will sometimes support the previous version or revision of a form. This is provided as a short-term convenience to allow endpoint clients to transition to new revisions of a form when substantial engineering effort may be required to implement a new form revision.

It is not recommended to target a specific revision of a form as a long-term workaround. Typically, previous versions of a form are removed from the SPF API catalog three to six months after it has been succeeded with a new revision.

Federal values

Several states use the federal Form W-4 for state purposes and each may have various rules governing values that an employee may or may not change from the values used for federal withholding. These rules can also change how a form should be presented. Existing clients can log into Symmetry's Client Support Center to see a demo of federal values lead by an SPF engineer.

To support the rules governing federal withholding values, the formQuestionSet endpoint provides an optional query parameter that maybe used to specify that federal withholding values are known and available to be used. Federal withholding values should be considered known if the employee has previously completely a federal Form W-4 and its values are held within your system of record.

📘

Using federal values

To use federal values for the formQuestionSet endpoint, provide the following query parameter with the request for a question set.

formQuestionSet/{{formId}}/{{questionSetId}}?hasFederalValues=(true|false)

Providing this query parameter will impact various forms by returning new question sets based on rules for each state in regards to values that the employee may diverge from the federal withholding values that they have already declared. The parameter defaults to false if not provided.


fillPdf (POST)

The fillPdf endpoint accepts a JSON object containing employee information, employer information, and the answers to the form questions. The endpoint validates and processes the data and returns a completed PDF in base 64 format along with the relevant tax parameters for the form. Validation is done using the regular expressions provided in the question sets and validation of question dependencies is performed.

Current clients can log into Symmetry's Client Support Center to see a demo of this endpoint lead by one of Symmetry's talented SPF engineers.

Form Versions

Specifying a form version can be done by adding formVersion to the outer object of your request. This is an optional attribute and if not included, the endpoint will expect the latest form version input parameters. If a form version is not specified we will return a warning message of the reasons why specifying a form version is best practice. In addition this is the same place we will send back a warning if a form has now been deprecated.

You can collect all the relevant input parameters of a particular form version and the currently supported versions from the ​getPdf​/{formId} (GET) endpoint.

Example Requests

{ 
  "formId": "AL101", 
  "employee": { 
    "firstName": "John", 
    "lastName": "Smith", 
    "socialSecurityNumber": "123-45-6789", 
    "address": { 
      "streetAddress1": "11 S Union St", 
      "city": "Montgomery", 
      "state": "AL", 
      "zipCode": "36130" 
    } 
  }, 
  "employer": { 
    "name": "Symmetry", 
    "federalEIN": "98-7654321", 
    "telephoneNumber": "456-867-5309", 
    "address": { 
      "streetAddress1": "64 N Union St", 
      "city": "Montgomery", 
      "state": "AL", 
      "zipCode": "36130" 
    } 
  }, 
  "signForm": "PREVIEW", 
  "fields": { 
    "filingStatus": "SINGLE", 
    "dependents": 1 
  } 
}
{
        "formId": "TD1AB",
        "employee": {
                "firstName": "Alejandro",
                "middleInitial": "",
                "lastName": "Perez",
                "socialInsuranceNumber": "964-977-441",
                "socialInsuranceNumberExpiryDate": "2032-09-22",
                "address": {
                    "streetAddress1": "2070 Harvey Ave",
                    "streetAddress2": "#22",
                    "city": "Kelowna",
                    "provinceTerritory": "BC",
                    "postalCode": "V1Y 8P8"
                }
        },

        "fields": {
            "isEligible": "true",
            "hasMultipleEmployers": "true",
            "dateOfBirth": "1990-11-23",
            "employeeNumber": "132143"
},
        "signForm": "SIGN"
}

Example Responses

{ 
  "form": "AL101", 
  "pdf": "JVBERi0xLjYKJfbk/...”, 
  "taxParameters": [ 
  { 
    "id": "AL.dependents", 
    "value": "1" 
  }, 
  { 
    "id": "AL.filingStatus", 
    "value": "S" 
  }, 
  ... 
]
{
    "form": "TD1AB",
    "notice": {
        "type": "WARNING",
        "message": "formVersion not provided, api processed data using the current version of the form.  Form version may change if a new version of the form is release, and may result in values becoming invalid when generating a pdf.  For best practice, please include the formVersion field in your request."
    },
    "formVersion": "2023.01.0",
    "formType": "CANADA_RESIDENT",
    "name": "TD1AB",
    "title": "2023 Alberta Personal Tax Credits Return",
    "locality": "AB",
    "taxParameters": [
        {
            "id": "CA.hasMoreThanOnePayer",
            "value": "true",
            "valueType": "boolean"
        },
        {
            "id": "CA.hasTotalIncomeLessThanTotalClaim",
            "value": "false",
            "valueType": "boolean"
        },
        {
            "id": "CA.totalClaimAmount",
            "value": "0.00",
            "valueType": "dollarAmount"
        }
    ],
    "pdf": "{{BASE 64 pdf exceeds length of comment will upload file}}"
}

In the response we send back we have three objects (form, pdf, and taxParameters).

  1. form will return a string of the forms id.
  2. pdf will return a complete pdf in base 64 format. When decoded will produce a filed out pdf.
  3. taxParameters will return an array of objects with two keys (id and value).
  • id will represent a single tax parameter name in the form of a string.
  • value will represent the associated value in the form of a string.

It is important to note the taxParameters object consists of all the values that will be needed for a payroll system to properly calculate payroll taxes.

Form Signing Options

The fillPdf endpoint has a signForm attribute as part of the API request. The signForm will accept three different values: PREVIEW, SIGN, SIGN_EXTERNALLY. The default value is PREVIEW.

PREVIEW: this is used to produce an un-signed preview of the form. In this mode, the Social Security Number (SSN) for locations in the United States and Social Insurance Number (SIN) and for locations in Canada will be masked and all form fields will be flattened.

SIGN: this is used to produce a completed and fully executed form. When this option is set, the signature line on the form will include the employee's name, a date/time stamp, and the SSN/SIN will be unmasked. All form fields will be flattened.

SIGN_EXTERNALLY: this is used to produce a completed form with an unmasked SSN/SIN and without an electronic signature or date. This can mode can be used if an external signature process or tool like DocuSign is desired to produce a fully executed form. This mode flattens all fields on the PDF except for the signature and date fields.

If using an external signing platform there are some important details to note for US implementations.

  1. Most US forms use two field ids for signature and date. The signature field uses the id signatureLine and the date field uses the id date. All Canadian forms use standard signature fields.
  2. A few forms use nonstandard signatures. These are the MO101, MO102, MO103, and NJ102.
  • The MO101, MO102, and MO103 forms use a signature field with the id signatureLine and three date fields with the ids date_month, date_day, and date_year.

  • For the NJ102 form, two signature fields with the ids; signatureLine_1, signatureLine_2 and two date fields with the ids date_1 and date_2.

📘

Timezones

The fillPdf endpoint may be passed a timezone in the headers, as the Timezone header, of the request to specify the timezone that should be used when signing and dating a completed form. Specified timezones will only be used if signForm is set to SIGN. If no value is passed with the request, then the API with default to UTC.

Information on accepted Timezone IDs can be found here.

📘

Federal values

Several states use the Federal W-4 for state purposes and have various requirements surrounding whether employees can diverge from values declared on the Federal W-4 for state withholding. Values used for federal values correspond to the tax parameter values returned for the W4101.

The API provides functionality to apply captured federal values to CO101, NM101, ND104, and UT101. Please note when using federal values for state forms, you should provide the federal tax parameter instead of the specific federal fields. Additionally, clients will need to remove the FED. prefix from the tax parameter name. See below for the list of fields that can be used in the federalValues object for state forms.

Field for State FormTypeFederal Tax Parameter (TP)Type
filingStatusstringFED.filingStatusstring
isExemptbooleanexemptFromFedWithholdingboolean
isNonResidentAlienbooleanFED.nonResidentAlienboolean
twoJobsbooleanFED.twoJobsboolean
additionalWithholdingnumberFED.additionalFedWithholdingdollarAmount
dependentsAmtnumberFED.dependentsAmtdollarAmount
otherIncomenumberFED.otherIncomedollarAmount
deductionsnumberFED.deductionsdollarAmount

It's important to note that the field names from the Federal W-4 and state forms are distinct from one another. For example, the isNra field from the Federal W-4 is returned in the FED.nonResidentAlien tax parameter. The tax parameter value is then passed into the isNonResidentAlien field in the federalValues object for state forms.

The following federal tax parameter values are allowed:

  • filingStatus (required if isExempt is false) - Employee’s Federal Filing Status
  • isExempt (required) - Flag denoting if the employee is exempt from federal withholding. Nonresident aliens may not claim exempt.
  • isNonResidentAlien - Whether the employee is a Nonresident Alien working within the United States or its Territories. Must be false if federalValues.isExempt is true.
  • twoJobs - W4 Line 2c, employee and spouse, together, have 2 jobs total.
  • additionalWithholding - Additional dollar amount the employee wishes to be applied to their federal withholding. Must be greater than or equal to 0 if isExempt is false. NM101 will convert to 0 if isExempt is true.
  • dependentsAmt - W4 Line 3, total credits for qualifying children and other dependents. Allowed values >= 0.00.
  • otherIncome - W4 Line 4a, income from other sources (not jobs) that employee wishes taxes to be withheld. I.e. interest/dividends, retirement income, etc. Allowed values >= 0.00.
  • deductions - W4 Line 4b, total deductions claimed if not claiming the standard deduction. Allowed values >= 0.00.

Example using federal values to complete NM101:

{
  "formId": "NM101",
  "employee": {
    "firstName": "John",
    "lastName": "Smith",
    "socialSecurityNumber": "123-45-6789",
    "address": {
      "streetAddress1": "11 S Union St",
		"city": "Montgomery",
		"state": "AL",
		"zipCode": "36130"
    }
  },
  "employer": {
    "name": "Symmetry",
    "federalEIN": "98-7654321",
    "telephoneNumber": "456-867-5309",
    "address": {
    	"streetAddress1": "64 N Union St",
		"city": "Montgomery",
		"state": "AL",
		"zipCode": "36130"
    }
  },
  "signForm": "PREVIEW",
  "fields": {
  	"adjustFederalValues": "false"
  },
  "federalValues": {
      "filingStatus": "SINGLE_OR_MARRIED_FILING_SEPARATELY",
      "isExempt": "false",
      "isNonResidentAlien": "false",
      "twoJobs": "false",
      "additionalWithholding": 25,
      "dependentsAmt": 0,
      "deductions": 0,
      "otherIncome": 0

  }
}

fillPdf​/{formId} (GET)

A GET method request to the fillpdf endpoint will return information for a given form, all the fields that can be expected, in addition to each field’s validation regular expressions. It also returns the tax parameters schema for the requested form.

SPF API supports form versioning through a query parameter. You can collect all the details of a form and its version by including the formVersion query parameter. This is an optional query parameter and if not included will provide the latest form version.

🚧

Form Versioning Syntax Example

fillPdf/{{formId}}?formVersion={{formVersion}}

Sample Responses

{ 
  "id": "W4101", 
  "locality": "FEDERAL", 
  "name": "W-4", 
  "title": "Employee's Withholding Certificate", 
  "formType": "Resident", 
  "initialQuestionSetId": "QS1", 
  "effectiveDate": "2020-01-01", 
  "formVersion": "1.20.0", 
  "formFields": { 
    "formId": "W4101", 
    "formVersion": "1.20.0", 
    "employee": { 
      "firstName": "^[A-Za-z.\\'\\- \\xC0-\\xD6\\xD8-\\xF6\\xF8-\\xFF]{1,}$", 
      ... 
    }, 
    "employer": { 
      "name": "^(.*)$", 
      "federalEIN": "^[0-9]{2}[-]?[0-9]{7}$|^[0-9]{9}$", 
      ... 
    }, 
    "fields": { 
      "otherAdjustmentsSurvey": "^(true|false)$", 
      "interestAndDividends": "^[+-]?[0-9]{1,3}(?:,?[0-9]{3})*(?:\\.[0-9]{2})?$", 
      ... 
    }, 
    "signForm": "PREVIEW" 
  }, 
  "blankFormImages": [
    "https://spfcdn-test.symmetry.com/images/W4101/2021.1.0/w4101_1.png",
    "https://spfcdn-test.symmetry.com/images/W4101/2021.1.0/w4101_2.png",
    "https://spfcdn-test.symmetry.com/images/W4101/2021.1.0/w4101_3.png",
    "https://spfcdn-test.symmetry.com/images/W4101/2021.1.0/w4101_4.png"
    ],
  "taxParameters": [ 
    { 
      "id": "FED.dependentsAmt", 
      "valueType": "dollarAmount", 
      "description": "Amount under the dependents credit claimed by employee" 
    }, 
    ... 
  ] 
}
{
    "id": "TD1AB",
    "locality": "AB",
    "name": "TD1AB",
    "title": "2023 Alberta Personal Tax Credits Return",
    "formType": "Canada Resident",
    "initialQuestionSetId": "QS1",
    "effectiveDate": "2023-01-01",
    "formVersion": "2023.01.0",
    "formFields": {
        "formId": "TD1AB",
        "formVersion": "2023.01.0",
        "employee": {
            "firstName": "^[A-Za-z.', \\-\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\xFF]{1,}$",
            "middleInitial": "^[A-Za-z\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\xFF]{0,1}$",
            "lastName": "^[A-Za-z.', \\-\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\xFF]{1,}$",
            "address": {
                "streetAddress1": "^[A-Za-z0-9#,.&'\\- \\xC0-\\xD6\\xD8-\\xF6\\xF8-\\xFF]+$",
                "streetAddress2": "^[A-Za-z0-9#,.&'\\- \\xC0-\\xD6\\xD8-\\xF6\\xF8-\\xFF]+$",
                "city": "^[A-Za-z.,&'\\- \\xC0-\\xD6\\xD8-\\xF6\\xF8-\\xFF]+$",
                "provinceTerritory": "^(AB|BC|MB|NB|NL|NT|NS|NU|ON|PE|QC|SK|YT)$",
                "postalCode": "^(?!.*[DFIOQU])[A-VXY][0-9][A-Z] ?[0-9][A-Z][0-9]$"
            },
            "socialInsuranceNumber": "^([0-9]{3}-[0-9]{3}-[0-9]{3}|[0-9]{9}|[0-9]{3} [0-9]{3} [0-9]{3})$",
            "replFirstName": "^[A-Z*-z.', \\-\\x**-\\x**\\x**-\\x**\\x**-\\x**]{1,}$",
            "replLastName": "^[A-Z*-z.', \\-\\x**-\\x**\\x**-\\x**\\x**-\\x**]{1,}$"
        },
        "fields": {
            "hasTotalIncomeLessThanTotalClaim": "^(true|false)$",
            "infirmDependantAmount": "^[+]?[0-9]{1,3}(?:,?[0-9]{3})*(?:\\.[0-9]{2})?$",
            "spousePartnerTransferAmount": "^[+]?[0-9]{1,3}(?:,?[0-9]{3})*(?:\\.[0-9]{2})?$",
            "dependantTransferredAmount": "^[+]?[0-9]{1,3}(?:,?[0-9]{3})*(?:\\.[0-9]{2})?$",
            "line8DependantNetIncome": "^[+]?[0-9]{1,3}(?:,?[0-9]{3})*(?:\\.[0-9]{2})?$",
            "line8EligibleDependantAmount": "^[+]?[0-9]{1,3}(?:,?[0-9]{3})*(?:\\.[0-9]{2})?$",
            "isEligible": "^(true|false)$",
            "ageAmountSurvey": "^(true|false)$",
            "hasMultipleEmployers": "^(true|false)$",
            "dateOfBirth": "^(?:(?:0[13578]|1[02])(\/)(?:31)(\/)|(?:(?:0[13-9]|1[0-2])(\/)(?:29|30)(\/)))(?:(?:1[6-9]|[2-9]\\d){1}\\d{2})$|^(?:02(\/)29(\/)(?:(?:(?:1[6-9]|[2-9]\\d){1}(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:(?:0[1-9])|(?:1[0-2]))(\/)(?:0[1-9]|1\\d|2[0-8])(\/)(?:(?:1[6-9]|[2-9]\\d){1}\\d{2})$|^(?:(?:1[6-9]|[2-9]\\d){1}\\d{2})(\\-)(?:(?:0[13578]|1[02])(\\-)(?:31)|(?:(?:0[13-9]|1[0-2])(\\-)(?:29|30)))$|^((?:(?:(?:1[6-9]|[2-9]\\d){1}(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00)))(\\-)02(\\-)29)$|^(?:(?:1[6-9]|[2-9]\\d){1}\\d{2})(\\-)(?:(?:0[1-9])|(?:1[0-2]))(\\-)(?:0[1-9]|1\\d|2[0-8])$",
            "employeeNumber": "^[a-zA-Z0-9\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\xFF,.!\\?\\-' ]*$",
            "countryOrResidence": "^(AC|AD|AE|AF|AG|AI|AL|AM|AN|AO|AQ|AR|AS|AT|AU|AW|AX|AZ|BA|BB|BD|BE|BF|BG|BH|BI|BJ|BL|BM|BN|BO|BQ|BR|BS|BT|BU|BV|BW|BY|BZ|CA|CC|CD|CF|CG|CH|CI|CK|CL|CM|CN|CO|CP|CR|CS|CU|CV|CW|CX|CY|CZ|DE|DG|DJ|DK|DM|DO|DZ|EA|EC|EE|EG|EH|ER|ES|ET|EU|EZ|FI|FJ|FK|FM|FO|FR|FX|GA|GB|GD|GE|GF|GG|GH|GI|GL|GM|GN|GP|GQ|GR|GS|GT|GU|GW|GY|HK|HM|HN|HR|HT|HU|IC|ID|IE|IL|IM|IN|IO|IQ|IR|IS|IT|JE|JM|JO|JP|KE|KG|KH|KI|KM|KN|KP|KR|KW|KY|KZ|LA|LB|LC|LI|LK|LR|LS|LT|LU|LV|LY|MA|MC|MD|ME|MF|MG|MH|MK|ML|MM|MN|MO|MP|MQ|MR|MS|MT|MU|MV|MW|MX|MY|MZ|NA|NC|NE|NF|NG|NI|NL|NO|NP|NR|NT|NU|NZ|OM|PA|PE|PF|PG|PH|PK|PL|PM|PN|PR|PS|PT|PW|PY|QA|RE|RO|RS|RU|RW|SA|SB|SC|SD|SE|SF|SG|SH|SI|SJ|SK|SL|SM|SN|SO|SR|SS|ST|SU|SV|SX|SY|SZ|TA|TC|TD|TF|TG|TH|TJ|TK|TL|TM|TN|TO|TP|TR|TT|TV|TW|TZ|UA|UG|UK|UM|US|UY|UZ|VA|VC|VE|VG|VI|VN|VU|WF|WS|XK|YE|YT|YU|ZA|ZM|ZR|ZW)$",
            "ageAmountCredit": "^[+]?[0-9]{1,3}(?:,?[0-9]{3})*(?:\\.[0-9]{2})?$",
            "pensionIncomeAmount": "^[+]?[0-9]{1,3}(?:,?[0-9]{3})*(?:\\.[0-9]{2})?$",
            "disabilityCreditAmount": "^(0|16201)$",
            "spouseCommonLawAmount": "^[+]?[0-9]{1,3}(?:,?[0-9]{3})*(?:\\.[0-9]{2})?$",
            "eligibleDependantAmount": "^[+]?[0-9]{1,3}(?:,?[0-9]{3})*(?:\\.[0-9]{2})?$",
            "line2NetIncome": "^[+]?[0-9]{1,3}(?:,?[0-9]{3})*(?:\\.[0-9]{2})?$",
            "line7DependantNetIncome": "^[+]?[0-9]{1,3}(?:,?[0-9]{3})*(?:\\.[0-9]{2})?$",
            "caregiverDependantAmountSurvey": "^(true|false)$",
            "infirmDependantAmountSurvey": "^(true|false)$",
            "caregiverDependantAmount": "^[+]?[0-9]{1,3}(?:,?[0-9]{3})*(?:\\.[0-9]{2})?$"
        },
        "signForm": "PREVIEW"
    },
    "blankFormImages": [
        "https://spfcdn.symmetry.com/images/canada/TD1AB/2023.01.0/td1ab_1.png",
        "https://spfcdn.symmetry.com/images/canada/TD1AB/2023.01.0/td1ab_2.png"
    ],
    "taxParameters": [
        {
            "id": "CA.hasMoreThanOnePayer",
            "valueType": "boolean",
            "description": "Has more than one employer or payer and already has claimed personal tax credits on another TD1AB"
        },
        {
            "id": "CA.hasTotalIncomeLessThanTotalClaim",
            "valueType": "boolean",
            "description": "Total income from all employers or payers is less than Line 13"
        },
        {
            "id": "CA.totalClaimAmount",
            "valueType": "dollarAmount",
            "description": "Total claim amount from line 13"
        }
    ]
}

It is important to note the taxParameters object consists of all the values that will be needed for a payroll system to properly calculate payroll taxes.

Form Deprecation

{
    "id": "W4101",
    "notice": {
        "type": "DEPRECATION",
        "message": "Form has been deprecated, current form version: 2021.1.0"
    }

We always recommend passing in a form version as a query parameter as the best practice when using the SPF API. This way when hitting the /fillPdf GET endpoint we can alert a user when a form is deprecated. Above is a code snippet of part of the response we will return when an older form version is passed in as a query parameter.


getAllFormIds (GET)

The getAllFormsIds endpoint is used to return a list of all form IDs currently supported by the Symmetry Payroll Forms API. Both US and Canadian forms are returned from this endpoint.

[ 
  "W4101", 
  "W4101SP", 
  "W6101", 
  "W8101", 
  "AL101", 
  "AL101SP", 
  "AL103", 
  "AR101", 
  ... 
]

getPdf​/{formId} (GET)

The getPdf endpoint returns a blank and unflattened pdf for the requested form ID. It is returned as a byte stream with an Accept header value of application/pdf. See the getAllFormIds endpoint for the available form IDs.

Accept Header

curl --location --request GET 'https://api-staging.symmetry.com/spf/getPdf/W4101' \
--header 'Accept: application/pdf' \
--header 'Authorization: Bearer yoUrTokEnG0esH3re!

Sample PDF Byte Stream Response

Below is a truncated byte stream response for a typical getPdf endpoint request.

%PDF-1.7
%����
2555 0 obj
<</Linearized 1/L 104187/O 2557/E 37116/N 4/T 103652/H [ 663 318]>>
endobj
             
2597 0 obj
<</DecodeParms<</Columns 4/Predictor 12>>/Filter/FlateDecode/ID[<F14163AEF278624BA3425A8083743D2C><D4ADC0335EAE45F49A59756AEF6654D9>]/Index[2555 97]/Info 2554 0 R/Length 147/Prev 103653/Root 2556 0 R/Size 2652/Type/XRef/W[1 2 1]>>stream
h�bbd``b`>
$�փ�y@��D���u �2�(��U����28�����@��@Bx�`�q�X@�U��* B�X$&R�	���?Hp�	�e �� %�C �`��������p&F�Y %�#�����@�
 ...
endstream
endobj
startxref
113751
%%EOF

Support for Multiple Form Revisions

SPF API can support multiple form versions or revisions. You can request a specific form using the ​getPdf​/{formId} (GET) endpoint. Specifying a form version can be accomplished with a the formVersion query parameter. This is an optional query param and if not included, will provide the latest form version.

🚧

Form Versioning Syntax Example

getPdf/{{formId}}/?formVersion={{formVersion}}

getPdf Demo

If you're an existing Symmetry client, log into our Client Support Center to see a demo of this endpoint lead by one of Symmetry's talented engineers!


getAllFormsCatalog endpoint

The getAllFormsCatalog endpoint returns a list of all form IDs and their versions currently supported.

[
    {
        "id": "W4101",
        "version": "2022.12.0"
    },
    {
        "id": "AL101",
        "version": "2014.3.0"
    },
    {
        "id": "AL101SP",
        "version": "2016.5.0"
    },
    {
        "id": "AL103",
        "version": "2019.9.0"
    },
    {
        "id": "AR101",
        "version": "2022.11.0"
    },
    {
        "id": "AR102",
        "version": "2022.11.0"
    },
    {
        "id": "AR103",
        "version": "2014.1.0"
    },
    ...
    {
        "id": "TD1",
        "version": "2023.01.0"
    },
    ...
]

docs​/config (GET)

The docs/config endpoint returns the Open API (Swagger) configuration used to generate the SPF API Open API documentation page.

This configuration can be used to generate a client API, see Swagger CodeGen for more details.

Sample Response

---
openapi: "3.0.1"
info:
  title: "SPF Api"
  contact:
    name: "Symmetry Software"
    url: "https://www.symmetry.com"
    email: "[email protected]"
  license:
    name: "Symmetry Software/Subscription & Service Agreement"
  version: "0.0.1"
servers:
- url: "https://api-staging.symmetry.com/spf"
  description: "Staging"
  variables: {}
tags:
- name: "Forms"
  description: "Determine all applicable forms and retrieve information for each"
- name: "Questions"
  description: "Retrieve form and guide flow questions"
- name: "PDF and Tax Parameters"
  description: "Retrieve pdf and tax parameter schemas and generate filled documents"
- name: "Documentation"
  description: "Retrieve OpenApi configuration yaml and use it to generate a client\
    \ api"
...