API variables
Looking up an answer from a server.
Using an API variable, you can fetch a new answer from a server that returns JSON responses. This lookup can even use other variables collected with the flow as inputs or parameters.
Common uses of API variables include:
- Finding the current price of an item.
- Locating the closest store to a user, based on their zipcode.
- Looking up an account ID based on a user's email.
API variables behave just like any other variable: they can be used for conditions, templated into most text, and are sent to your analytics and integrations.
Doing a simple calculation with variables you already have present?
If you don't need access to data on a server, you can use a Calculated answer to perform a calculation directly within the form flow.
To add an API variable, go to Schema > API variables and click Add API variable...
Provide the URL of the endpoint you wish to hit in the API URL field.
You can use existing answers from the flow to template the API lookup URL with the standard templating syntax. The API will not be called unless all of the variables used in the template are defined.
For example, if you ask the responder for their postal code in an answer with variable name
user_zip
, you can use that to look up store locations with a URL like https://example.com/api/stores?zip_code={{user_zip}}
.Optional parameters
The default template formatting function default values for an API variable's URL. If I have a lookup like
https://example.com?age={{age}}&promo={{promo | default ""}}
then the API will be called whenever the age
variable is defined, whether or not promo
is itself defined.In the above example, the URL would be called with
https://example.com?age=24&promo=
ifpromo
is undefinedhttps://example.com?age=24&promo=FREEMONTH
ifpromo
is defined
CORS
API-answer
http
requests come directly from the responder's browser. If your server enforces Cross-Origin Resource Sharing restrictions (CORS), you will need to whitelist the following in the Access-Control-Allow-Origin
response header:<client-id>.formsort.app

You can find your Client ID here, among other places.
studio.formsort.com
(for testing within the studio)
Additionally, if you are using Custom domains to host your flows, you will need to whitelist those domains.
Alternatively, you can enable the
*
origin in the Access-Control-Allow-Origin
header if you would like to disable CORS restrictions.Variable type sets the expected data type that we are expecting from the response:
string
, number
, or boolean
If Is array? is enabled, then we will expect the response to contain multiple answers.
If your API returns a payload with a shape that isn't directly the desired variable type, you can process the result within Formsort, to avoid having to change the shape of your API responses.
For the below examples, consider a response payload for a list of stores.
// Sample JSON response body
// Object
Result: {
"person": {
"name": {
firstName: "Frodo"
lastName: "Baggins"
}
"location": "The Shire"
}
}
// Array
[
{
address: "123 Main St",
isOpen: false,
},
{
address: "4 Back St",
isOpen: true,
}
]
If the answer you want to access can be accessed directly with keys and indices, you can write out the path to the desired data with the JSON accessor. Formsort opens an object upon receipt, so it is unnecessary to include the object name in the accessor function.
For example, in the object above,
Result.person.location
person.location
would return "The Shire"
. You can drill down a level as well: person.name.firstName
would return "Frodo"
.In the array example,
0.address
would return the address of the first result, which in the above example would be "123 Main St"
.For more complex processing of results, you can write a mapping function using javascript, similar to a calculated answer's getter function body.
This function has an argument,
res
, which is the body of the raw JSON object returned from the server. It should return
the desired answer.For example, if we'd like to access the address of the first open address from the response above, the mapping function would be:
const openStores = res.filter(function(store) { return store.isOpen });
if (openStores.length) {
return openStores[0].address;
}
return undefined;
... which would result in
"4 Back st"
The getter function will only be evaluated for a successful response. If the server returns any kind of 400 or 500 response, it will not be evaluated.
Sometimes, you may need to extract multiple fields from a JSON response body. Imagine an endpoint like
GET /userLookup?email={{email}}
. This endpoint takes the email address value from the email
query param. And, if the email address is found in the database, returns the id and name associated with that email. In many cases, you may need to extract both fields -- id
and name
-- from the response body.// Sample JSON response body: GET /userLookup?email={{email}}
[
{
id: 1234,
name: "Shinji Ikari",
}
]
To extract multiple fields from a single JSON response object, you need to create multiple API variables: one API variable per field. Of course, you likely would prefer not to have to make a separate API call for each field. So, to avoid making multiple API requests when using multiple API variables, simply give every API variable the same URL string and query params.
By creating multiple API variables with the same URL string and query params, a single request to the given URL is made. However, the response is processed by every API variable with that URL string and query params signature. So, by using different JSON accessors or Mapping Functions (see above) for each API variable, you can extract different fields. Continuing the example above, we could use the set of API variables below to successfully retrieve the
id
and name
fields. Note that both API variables are the same, except for their accessors. This will result in a single API request, but allow us to retrieve both fields.// API variable 1:
Variable Name: responder_id
URL: /userLookup
Query params: email={{email}}
JSON accessor [0].id
// API variable 2:
Variable Name: responder_name
URL: /userLookup
Query params: email={{email}}
JSON accessor [0].name
If the API variables have different conditions for the Is Conditional? field, whichever API variable has their conditions met first will be the API variable which gets to make the API request. Whatever response is received from that request will be used across all other API variables with matching API URL string and query params.
If an API answer has no dependencies (there are no templated answers within its URL), the API lookup will happen as soon as the flow loads.
If an API answer has dependencies, the API lookup will happen as soon as all of its inputs are defined, and will be re-run whenever those inputs change. If any of the inputs become undefined, the API answer will be cleared.
For more control over when the API lookup happens, you can set a condition for its evaluation with Is conditional?
If an API answer was retrieved in a previous session, and the responder returns to the flow, the default behavior is not to re-fetch the answer.
To always fetch the answer on load, enable Re-calculate on load. This is useful for situations when calculated answers are not idempotent, meaning that repeated invocations do not result in the same result, such as answers depending on an API response that changes over time.
If there is a pending API answer lookup, and the user is trying to advance to a step that uses the resultant answer, a loading indicator will be displayed while they await the result of the lookup.
Avoiding the loading spinner
Consider interleaving steps that provide inputs for API answers with steps that contain static content, or content orthogonal to the API lookup.
That way, even if an API request is slow, it will not matter, as the user will be viewing other content while the request happens in the background.
If an API answer request fails, or returns an invalid response, the answer will be undefined, and the answer will be marked as failed.
You can use the Had a loading error condition operator on a group, step, or question to affect the logic of a flow based on a failed answer, for example to put up a custom error message, or provide the responder an alternative way forward.
In authenticated flows, credentials are passed along with every request. This allows the use of cookies to authenticate requests to your own APIs.
Last modified 2mo ago