NAV
shell python javascript

Introduction

The Feathery API is organized around REST. Our API has predictable resource-oriented URLs, accepts form-encoded request bodies, returns JSON-encoded responses, and uses standard HTTP response codes, authentication, and verbs.

You can use our API to access and modify your Feathery resources - forms, fields, documents, and more.

Authentication

Provide your Feathery admin API key to authenticate access. You can get an API key by creating an account.

API keys are environment-specific (production, development, etc.), which allows you to separate real from test data. All API keys have access to the same fields and forms, but users are environment-specific.

Include your API key as a request header that looks like the following:

Authorization: Token <API KEY>

Account

Retrieve Account Information

import requests

url = "https://api.feathery.io/api/account/";
headers = {"Authorization": "Token <API KEY>"}
result = requests.get(url, headers=headers)
print(result.json())
curl "https://api.feathery.io/api/account/" \
    -H "Authorization: Token <API KEY>"
const url = "https://api.feathery.io/api/account/";
const options = { headers: { Authorization: "Token <API KEY>" } };
fetch(url, options)
    .then((response) => response.json())
    .then(result => console.log(result));

The above command outputs JSON structured like this:

{"team": "my-team-name"}

Retrieve your Feathery account information.

HTTP Request

GET https://api.feathery.io/api/account/

Response Body

The response will be an object containing the following parameters.

Parameter Type Description
team String The name of your team in Feathery

Invite Accounts

import requests

url = "https://api.feathery.io/api/account/invite/";
headers = {"Authorization": "Token <API KEY>"}
result = requests.post(url, headers=headers, json=[{"email": "new@invite.com"}])
print(result.json())
curl "https://api.feathery.io/api/account/invite/" \
    -X POST \
    -d "[{'email': 'new@invite.com'}]" \
    -H "Authorization: Token <API KEY>" \
    -H "Content-Type: application/json"
const url = "https://api.feathery.io/api/account/invite/";
const data = [{email: 'new@invite.com'}];
const headers = {
  Authorization: "Token <API KEY>",
  "Content-Type": "application/json"
};
const options = {
  headers,
  method: 'POST',
  body: JSON.stringify(data)
};
fetch(url, options)
  .then((response) => response.json())
  .then(result => console.log(result));

Invite new users to your Feathery team.

HTTP Request

POST https://api.feathery.io/api/account/invite/

Request Body Parameters

An array of objects with the following parameters, where each object is a new user to invite:

Parameter Type Description
email string The email of the new user to invite
role string (optional) Either 'admin', 'editor', or 'viewer'. Defaults to 'admin'.
permission_edit_form_results boolean (optional) If 'editor', if they're allowed to edit form results. Defaults to true.
permission_invite_collaborators boolean (optional) If 'editor', if they're allowed to invite form collaborators. Defaults to true.
permission_edit_collaborator_template boolean (optional) If 'editor', if they're allowed to edit form collaborator settings. Defaults to true.
permission_edit_logic boolean (optional) If 'editor', if they're allowed to edit form custom logic rules. Defaults to true.
permission_edit_theme boolean (optional) If 'editor', if they're allowed to edit form themes. Defaults to true.
user_groups string An array of user group names to add the invited account to

Response Body

The response will be an object containing the following parameters.

Parameter Type Description
team String The name of your team in Feathery

Remove Account

import requests

url = "https://api.feathery.io/api/account/uninvite/";
headers = {"Authorization": "Token <API KEY>"}
result = requests.patch(url, headers=headers, json=[{"email": "new@invite.com"}])
print(result.json())
curl "https://api.feathery.io/api/account/uninvite/" \
    -X PATCH \
    -d "[{'email': 'new@invite.com'}]" \
    -H "Authorization: Token <API KEY>" \
    -H "Content-Type: application/json"
const url = "https://api.feathery.io/api/account/uninvite/";
const data = [{email: 'new@invite.com'}];
const headers = {
  Authorization: "Token <API KEY>",
  "Content-Type": "application/json"
};
const options = {
  headers,
  method: 'PATCH',
  body: JSON.stringify(data)
};
fetch(url, options)
  .then((response) => response.json())
  .then(result => console.log(result));

Remove user from your Feathery team.

HTTP Request

PATCH https://api.feathery.io/api/account/uninvite/

Request Body Parameters

Parameter Type Description
email string The email of the user to remove

Document Intelligence

List Extraction Runs

import requests

url = "https://api.feathery.io/api/ai/run/batch/<extraction_id>/";
headers = {"Authorization": "Token <API KEY>"}
result = requests.get(url, headers=headers)
print(result.json())
curl "https://api.feathery.io/api/ai/run/batch/<extraction_id>/" \
    -H "Authorization: Token <API KEY>"
const url = "https://api.feathery.io/api/ai/run/batch/<extraction_id>/";
const headers = { Authorization: "Token <API KEY>" };
fetch(url, { headers })
    .then((response) => response.json())
    .then(result => console.log(result));

The above command outputs JSON structured like this:

[{
  "id": "<RunId>",
  "success": true,
  "approved": true,
  "approver": "reviewer@mail.com",
  "error_message": "",
  "display_pdf": null,
  "created_at": "2020-06-01T00:00:00Z",
  "email_extracted_at": "2020-06-02T00:00:00Z",
  "document_extracted_at": "2020-06-03T00:00:00Z",
  "updated_at": "2020-06-04T00:00:00Z",
}]

List runs for a particular AI document extraction

HTTP Request

GET https://api.feathery.io/api/ai/run/batch/<EXTRACTION ID>/

Request Query Parameters

Parameter Type Description
start_time Datetime (Optional) Fetch runs that started after this start time
end_time Datetime (Optional) Fetch runs that started before this end time

Response Body

An array of extraction run entries

Document Templates

Fill or Sign a Document Template

import requests

url = "https://api.feathery.io/api/document/fill/";
headers = {"Authorization": "Token <API KEY>"}
data = {"document": <DOCUMENT ID>, "field_values": {<FIELD ID>: <FIELD VALUE>}, "signer_email": "signer@company.com"}
result = requests.post(url, json=data, headers=headers)
print(result.json())
curl "https://api.feathery.io/api/document/fill/" \
    -X POST \
    -d "{'document': <DOCUMENT ID>, 'field_values': {<FIELD ID>: <FIELD VALUE>}, 'signer_email': 'signer@company.com'" \
    -H "Authorization: Token <API KEY>" \
    -H "Content-Type: application/json"
const url = "https://api.feathery.io/api/document/fill/";
const data = {document: '<DOCUMENT ID>', field_values: {'<FIELD ID>': '<FIELD VALUE>'}, signer_email: 'signer@company.com'}
const headers = {
    Authorization: "Token <API KEY>",
    "Content-Type": "application/json"
};
const options = {
    headers, 
    method: 'POST',
    body: JSON.stringify(data)
};
fetch(url, options)
    .then((response) => response.json())
    .then(result => console.log(result));

The above command outputs JSON structured like this:

{"file_url": "URL TO FILE"}

Fill out and/or sign a document that you've uploaded and mapped in your Feathery account.

HTTP Request

POST https://api.feathery.io/api/document/fill/

Request Body Parameters

Parameter Type Description
document UUID String The ID of the document to fill
field_values Object (optional) A mapping of field ID to field value to fill the document
signer_email String (optional) The document will route to the specified email for signature after being filled

Response Body

The response will be an object containing the following parameters.

Parameter Type Description
file_url String The URL to the filled out document

List Document Envelopes

import requests

url = "https://api.feathery.io/api/document/envelope/list/";
headers = {"Authorization": "Token <API KEY>"}
params = {"type": "document", "id": <DOCUMENT ID>}
result = requests.get(url, params=params, headers=headers)
print(result.json())
curl "https://api.feathery.io/api/document/envelope/list/?type=document&id=<DOCUMENT ID>" \
    -H "Authorization: Token <API KEY>"
const url = "https://api.feathery.io/api/document/envelope/list/?type=document&id=<DOCUMENT ID>";
const headers = { Authorization: "Token <API KEY>" };
const options = { headers };
fetch(url, options)
    .then((response) => response.json())
    .then(result => console.log(result));

The above command outputs JSON structured like this:

[{
  "id": "<ENVELOPE ID>",
  "document": "<DOCUMENT ID>",
  "user": "<FEATHERY USER ID>",
  "signer": "signer@mail.com",
  "sender": "sender@mail.com",
  "file": "https://link-to-filled-file.com",
  "type": "pdf",
  "viewed": true,
  "signed": true,
  "tags": ["document-tag"],
  "created_at": "2020-06-03T00:00:00Z"
}]

List document envelopes that your users have filled out in Feathery. You can find the envelopes corresponding to either a particular document template or a particular submission.

HTTP Request

GET https://api.feathery.io/api/document/envelope/list/

Request Query Parameters

Parameter Type Description
type String Either document or user, specifying how to look up envelopes of interest.
id String If method is document, this is the document ID. If method is user, this is the user ID.

Response Body

The response will be an array of objects containing the following parameters.

Parameter Type Description
id String The ID of the envelope
document String (Optional) The ID of the document template this envelope was generated from
user String (Optional) The ID of the Feathery user that filled out this envelope
signer String (Optional) The email of the envelope signer
sender String (Optional) The email of the envelope sender
file URL The URL to the file associated with this envelope
type String Enum The type of document - pdf, docx, xlsx
viewed Boolean If envelope was routed for signature, if the signer has viewed it
signed Boolean If envelope was routed for signature, if the signer has signed it
tags String[] An array of tags that contain metadata about the envelope
created_at Datetime When this envelope was created

Forms

Retrieve a Form Schema

import requests

url = "https://api.feathery.io/api/form/my_form/";
headers = {"Authorization": "Token <API KEY>"}
result = requests.get(url, headers=headers)
print(result.json())
curl "https://api.feathery.io/api/form/my_form/" \
    -H "Authorization: Token <API KEY>"
const url = "https://api.feathery.io/api/form/my_form/";
const options = { headers: { Authorization: "Token <API KEY>" } };
fetch(url, options)
    .then((response) => response.json())
    .then(result => console.log(result));

The above command outputs JSON structured like this:

            {
  "form_id": "aSdsa5",
  "form_name": "My Form",
  "steps": [
    {
      "id": "Step 1",
      "origin": true,
      "images": [],
      "progress_bars": [],
      "texts": [],
      "buttons": [],
      "fields": [
        {
          "id": "field_key",
          "internal_id": "46d1ba89-0dcc-42cd-839c-a12ce6668347",
          "icon_url": "",
          "placeholder": "",
          "tooltipText": "",
          "submit_trigger": "none",
          "metadata": {},
          "display_text": "a",
          "format": "",
          "repeated": false,
          "repeat_trigger": "",
          "required": true,
          "type": "integer_field",
          "max_length": null,
          "min_length": null,
          "has_data": true
        }
      ],
      "previous_conditions": [],
      "next_conditions": [],
      "created_at": "2020-06-01T00:00:00Z",
      "updated_at": "2020-06-01T00:00:00Z"
    },
    {
      "id": "Step 2",
      "origin": false,
      "images": [],
      "videos": [],
      "progress_bars": [],
      "texts": [],
      "buttons": [],
      "fields": [],
      "previous_conditions": [],
      "next_conditions": [],
      "created_at": "2020-06-01T00:00:00Z",
      "updated_at": "2020-06-01T00:00:00Z"
    }
  ],
  "rules": [
    {
      "name": "Rule 1",
      "description": "Rule description",
      "trigger_event": "form_complete",
      "index": 0,
      "steps": [],
      "code": "code",
      "elements": [],
      "enabled": true,
      "valid": true,
      "mode": "code_editor",
      "created_at": "2020-06-01T00:00:00Z",
      "updated_at": "2020-06-01T00:00:00Z"
    }  
  ]
}

Retrieve the schema of a form created in Feathery.

HTTP Request

GET https://api.feathery.io/api/form/<form_id>/

Response Body

The response will be an object containing the following parameters.

Parameter Type Description
form_id String Your form's ID
form_name String Your form's name
steps Array An array of step objects
rules Array An array of rule objects

Each steps object contains the following parameters.

Parameter Type Description
id String The ID of the step, unique to the form
origin Boolean Is this the first step of the form
images Array Images on this step
videos Array Videos on this step
progress_bars Array Progress bars on this step
texts Array Text elements on this step
buttons Array Buttons on this step
fields Array Fields on this step
previous_conditions Array Navigation rules that connect previous steps to the current step
next_conditions Array Navigation rules that connect the current step to next steps
created_at Datetime When this step was created
updated_at Datetime When this step was last updated

Each form element (images, videos, progress_bars, texts, buttons, fields) has a common id parameter that uniquely identifies it.

Each rule object contains the following parameters.

Parameter Type Description
name String The user-friendly name of the rule
description String Description of the rule
trigger_event Enum On what event the rule runs
index Number The ordering of this rule when executing
steps Array A list of step IDs corresponding to the steps that the rule runs on
elements Array A list of element IDs that trigger the rule
enabled Boolean Is this rule runnable
valid Boolean Is the rule configuration in a valid state
mode Enum Is the rule defined via the code editor or no-code logic
created_at Datetime When the rule was created
updated_at Datetime When the rule was last updated

List Forms

import requests

url = "https://api.feathery.io/api/form/";
headers = {"Authorization": "Token <API KEY>"}
result = requests.get(url, headers=headers)
print(result.json())
curl "https://api.feathery.io/api/form/" \
    -H "Authorization: Token <API KEY>"
const url = "https://api.feathery.io/api/form/";
const options = { headers: { Authorization: "Token <API KEY>" } };
fetch(url, options)
    .then((response) => response.json())
    .then(result => console.log(result));

The above command outputs JSON structured like this:

[
  {
    "id": "aSdsa5",
    "name": "My Form",
    "active": true, 
    "created_at": "2020-06-01T00:00:00Z",
    "updated_at": "2020-06-01T00:00:00Z"
  }
]

List all of your forms in Feathery.

HTTP Request

GET https://api.feathery.io/api/form/

Response Body

The response will be an array of objects containing the following parameters.

Parameter Type Description
id String The form ID
name String The form name
active Boolean Is the form turned on
created_at Datetime When this form was created
updated_at Datetime When this form was last updated

Create a Form

import requests

url = "https://api.feathery.io/api/form/";
headers = {"Authorization": "Token <API KEY>"}
data = {
    "form_name": "My New Form",
    "template_form_id": "aaaaaa",
    "steps": [
        {
            "step_id": "name_of_new_step",
            "template_step_id": "step_to_copy",
            "origin": True,
            "fields": [
                {
                    "id": "field_to_change",
                    "field_id": "new_field_name"
                }
            ],
            "texts": [
                {
                    "id": "text_element_to_change",
                    "text": "New Text to replace copied text",
                }
            ],
            "buttons": [
                {
                    "id": "button_element_to_change",
                    "text": "New Button Text",
                }
            ],
            "images": [
                {
                    "id": "image_element_to_change",
                    "source_url": "https://new-image-to-replace-old.com",
                }
            ],
            "progress_bars": [
                {
                    "id": "name_of_progress_bar_to_change",
                    "progress": 10,
                }
            ],
        },
        {
            "step_id": "name_of_new_step_2",
            "template_step_id": "step_to_copy_2",
            "fields": [],
            "texts": [],
            "buttons": [],
            "images": [],
            "progress_bars": [],
        },
    ],
    "navigation_rules": [
        {
            "previous_step_id": "name_of_new_step",
            "next_step_id": "name_of_new_step_2",
            "trigger": "click",
            "element_type": "button",
            "element_id": "button_element_to_change",
            "rules": [
                {
                    "comparison": "equal",
                    "field_key": "",
                    "value": "",
                },
            ],
        },
    ],
}
result = requests.post(url, data=data, headers=headers)
print(result.json())
curl "https://api.feathery.io/api/form/" \
    -X POST \
    -d "{
        'form_name': 'My New Form',
        'template_form_id': 'aaaaaa',
        'steps': [
            {
                'step_id': 'name_of_new_step',
                'template_step_id': 'step_to_copy',
                'origin': True,
                'fields': [
                    {
                        'id': 'field_to_change',
                        'field_id': 'new_field_name'
                    }
                ],
                'texts': [
                    {
                        'id': 'text_element_to_change',
                        'text': 'New Text to replace copied text',
                    }
                ],
                'buttons': [
                    {
                        'id': 'button_element_to_change',
                        'text': 'New Button Text',
                    }
                ],
                'images': [
                    {
                        'id': 'image_element_to_change',
                        'source_url': 'https://new-image-to-replace-old.com',
                    }
                ],
                'progress_bars': [
                    {
                        'id': 'name_of_progress_bar_to_change',
                        'progress': 10,
                    }
                ],
            },
            {
                'step_id': 'name_of_new_step_2',
                'template_step_id': 'step_to_copy_2',
                'fields': [],
                'texts': [],
                'buttons': [],
                'images': [],
                'progress_bars': [],
            },
        ],
        'navigation_rules': [
            {
                'previous_step_id': 'name_of_new_step',
                'next_step_id': 'name_of_new_step_2',
                'trigger': 'click',
                'element_type': 'button',
                'element_id': 'button_element_to_change',
                'rules': [
                    {
                        'comparison': 'equal',
                        'field_key': '',
                        'value': '",
                    },
                ],
            },
        ],
    }" \
    -H "Authorization: Token <API KEY>" \
    -H "Content-Type: application/json"
const url = "https://api.feathery.io/api/form/";
const data = {
  "form_name": "My New Form",
  "template_form_id": "aaaaaa",
  "steps": [
    {
      "step_id": "name_of_new_step",
      "template_step_id": "step_to_copy",
      "origin": True,
      "fields": [
        {
          "id": "field_to_change",
          "field_id": "new_field_name"
        }
      ],
      "texts": [
        {
          "id": "text_element_to_change",
          "text": "New Text to replace copied text",
        }
      ],
      "buttons": [
        {
          "id": "button_element_to_change",
          "text": "New Button Text",
        }
      ],
      "images": [
        {
          "id": "image_element_to_change",
          "source_url": "https://new-image-to-replace-old.com",
        }
      ],
      "progress_bars": [
        {
          "id": "name_of_progress_bar_to_change",
          "progress": 10,
        }
      ],
    },
    {
      "step_id": "name_of_new_step_2",
      "template_step_id": "step_to_copy_2",
      "fields": [],
      "texts": [],
      "buttons": [],
      "images": [],
      "progress_bars": [],
    },
  ],
  "navigation_rules": [
    {
      "previous_step_id": "name_of_new_step",
      "next_step_id": "name_of_new_step_2",
      "trigger": "click",
      "element_type": "button",
      "element_id": "button_element_to_change",
      "rules": [
        {
          "comparison": "equal",
          "field_key": "",
          "value": "",
        },
      ],
    },
  ],
};
const headers = {
    Authorization: "Token <API KEY>",
    "Content-Type": "application/json"
};
const options = {
    headers, 
    method: 'POST',
    body: JSON.stringify(data)
};
fetch(url, options)
    .then((response) => response.json())
    .then(result => console.log(result));

The above command returns a 201 status on success

Create a form based off an existing template form. Any integrations on the template form will be copied over as well.

HTTP Request

POST https://api.feathery.io/api/form/

Body Parameters

Parameter Type Description
form_name String The name of the new form being created (must be unique)
template_form_id String The ID of the template form to copy from
steps Array An array of steps to create
navigation_rules Array An array of navigation rule connecting steps to be created

Each steps object contains the following parameters. The form element arrays allow you to edit attributes from the elements in the copied step. If nothing is specified, the elements on the step will be copied as they are.

Parameter Type Description
step_id String The ID of the new step to create, unique to the form
template_step_id Optional String The ID of the step to copy from the original form. If not specified, a step will be auto-created with a single column layout.
origin Boolean true if this step is the first of the form
images Array Image elements to edit on this step
videos Array Video elements to edit on this step
progress_bars Array Progress bar elements to edit on this step
texts Array Text elements to edit on this step
buttons Array Button elements to edit on this step
fields Array Field elements to edit on this step

Each image object contains the following parameters.

Parameter Type Description
id String The ID of the image to edit. If template_step_id is specified, it must be from that step.
source_url String A new image URL
asset Optional String The name of the image asset to inherit from. This asset must belong to the template form's theme.

Each video object contains the following parameters.

Parameter Type Description
id String The ID of the video to edit. If template_step_id is specified, it must be from that step.
source_url String A new video URL
asset Optional String The name of the video asset to inherit from. This asset must belong to the template form's theme.

Each progress_bar object contains the following parameters.

Parameter Type Description
id String The ID of the progress bar to edit. If template_step_id is specified, it must be from that step.
progress Integer The percentage of progress to display between 0 and 100
asset Optional String The name of the progress bar asset to inherit from. This asset must belong to the template form's theme.

Each text object contains the following parameters.

Parameter Type Description
id String The ID of the text element to edit. If template_step_id is specified, it must be from that step.
text String The new text to display
asset Optional String The name of the text asset to inherit from. This asset must belong to the template form's theme.

Each button object contains the following parameters.

Parameter Type Description
id String The ID of the button to edit. If template_step_id is specified, it must be from that step.
text String Text to display on the new button
asset Optional String The name of the button asset to inherit from. This asset must belong to the template form's theme.

Each field object may contain the following parameters, depending on what type of field you are copying or changing.

Parameter Type Description
id String The ID of the field to copy. If template_step_id is specified, it must be from that step.
field_id Optional String The ID to set for the field on the new step. If this ID references an existing field, it will automatically link to the field's properties and user data. If field_id isn't specified, it will be automatically generated.
type Optional String The new field type
description Optional String The label / description of the field
required Optional Boolean Does the user need to fill out this field before submitting the step? Defaults to true
max_length Optional Integer Maximum length of the field value
min_length Optional Integer Minimum length of the field value
placeholder Optional String New placeholder text for the field
tooltipText Optional String New tooltip text for the field
submit_trigger Optional String Does filling out the field trigger submission of the current step? Options are "auto" or "none"
metadata Optional Object Specifies field options and file types
asset Optional String The name of the field asset to inherit from. This asset must belong to the template form's theme.

Below we specify which parameters apply to which field type. They are only required if you aren't linking from an existing field.

Parameter Field Types Required
max_length, min_length text_field, text_area, integer_field, select, multiselect, email Optional
max_length pin_input Required
placeholder, toolTipText dropdown, email, login, phone_number, gmap_line_1, gmap_line_2, gmap_city, gmap_state, gmap_zip, integer_field, ssn, text_field, text_area, url Optional
submit_trigger button_group, file_upload, dropdown, gmap_state, pin_input, select, hex_color, login, phone_number, ssn Optional
metadata.other select, multiselect, dropdown Required
metadata.options button_group, select, multiselect, dropdown Required
metadata.file_types file_upload Required

Each navigation_rules object controls how the user navigates between 2 steps. For example, one navigation rule might specify that clicking the button Next on Step 1 leads to Step 2.

Navigation rules contain the following parameters.

Parameter Type Description
previous_step_id String The step the user is coming from.
next_step_id String The step the user is going to.
trigger String Enum How navigation is trigger - "click", "change", or "load"
element_type String Enum What type of element triggers navigation - "button", "field", or "text"
element_id String The ID of the element on the previous step that triggers navigation.
rules Array The conditions that must be fulfilled for navigation to occur

Each rules object contains the following parameters.

Parameter Type Description
comparison String Comparison - "equal", "not_equal"
field_key String The ID of the field whose value is being used for this comparison
value String The value to compare the field value against

Create or Update Form Submissions

import requests

url = "https://api.feathery.io/api/form/submission/";

# Using field ID or internal ID
data = {"fields": {"age": 21}, "user_id": "alice@feathery.io", "forms": ["My Form"], "complete": True}

headers = {
    "Authorization": "Token <API KEY>",
    "Content-Type": "application/json",
}

result = requests.post(url, data=data, headers=headers)
print(result.json())
curl "https://api.feathery.io/api/form/submission/" \
    -X POST \
    -d "{'fields': {'age': 21}, 'user_id': 'alice@feathery.io', 'forms': ['My Form'], 'complete': True}" \
    -H "Authorization: Token <API KEY>" \
    -H "Content-Type: application/json"
const url = "https://api.feathery.io/api/form/submission/";
const data = {"fields": {"age": 21}, "user_id": "alice@feathery.io", "forms": ["My Form"], "complete": true}
const headers = {
    Authorization: "Token <API KEY>",
    "Content-Type": "application/json"
};
const options = {
    headers, 
    method: 'POST',
    body: JSON.stringify(data)
};
fetch(url, options)
    .then((response) => response.json())
    .then(result => console.log(result));

The above command outputs JSON structured like this:

{
  "fields": {"age": 21},
  "user_id": "alice@feathery.io",
  "forms": ["My Form"],
  "complete": true
}

Set field values for a user and initialize form submissions

HTTP Request

POST https://api.feathery.io/api/form/submission/

Request Body Parameters

Parameter Type Description
fields Object A mapping from field identifier (ID or Internal ID) to associated field values to create. For a signature field, pass {"file": <base64 string>, "name": <file name>}
user_id Optional String A new or existing user ID. If not provided, a random ID will be generated and returned.
forms Optional String[] An array of form IDs to initialize submissions for
complete Optional Boolean A boolean value to indicate if this submission will be set as a form completion. Default to false if not provided

Response Body

Same as request body parameters

List Form Submissions

import requests

url = "https://api.feathery.io/api/form/submission/batch/abcdef/";
headers = {"Authorization": "Token <API KEY>"}
result = requests.get(url, headers=headers)
print(result.json())
curl "https://api.feathery.io/api/form/submission/batch/abcdef/" \
    -H "Authorization: Token <API KEY>"
const url = "https://api.feathery.io/api/form/submission/batch/abcdef/";
const headers = { Authorization: "Token <API KEY>" };
fetch(url, { headers })
    .then((response) => response.json())
    .then(result => console.log(result));

The above command outputs JSON structured like this:

[{
  "values": [
    {
      "id": "TestField",
      "type": "text_field",
      "created_at": "2024-10-28T07:56:09.391398Z",
      "updated_at": "2024-10-28T16:39:32.577794Z",
      "value": "Test Value",
      "hidden": false,
      "display_text": "",
      "internal_id": "ef5ed054-73de-4463-ba61-82c36aca5afc",
    }
  ],
    "user_id": "131e7132-dg6d-4a8c-9d70-cgd493c2a368",
    "submission_start": "2024-10-30T02:07:32Z",
    "last_submitted": "2024-10-30T02:07:32Z",
}]

List submission data for a particular form

HTTP Request

GET https://api.feathery.io/api/form/submission/batch/<FORM ID>/

Request Query Parameters

Parameter Type Description
start_time Datetime (Optional) Fetch submissions from after this start time
end_time Datetime (Optional) Fetch submissions from before this end time
completed Boolean (Optional) If specified, only fetch submissions that are either completed or incomplete
no_field_values Boolean (Optional) Don't return field data. If this is enabled, you may fetch more records and the endpoint is more performant.
sort String (Optional) If "layout", the returned values will be sorted in the way fields are laid out in the form. Otherwise, values will be sorted by field ID alphabetically.

Response Body

An array of submission entries

Export Form Submission PDF

import requests

url = "https://api.feathery.io/api/form/submission/pdf/";
data = {"form_id": "abcdef", "user_id": "alice@feathery.io"}
headers = {
    "Authorization": "Token <API KEY>",
    "Content-Type": "application/json",
}
result = requests.post(url, data=data, headers=headers)
print(result.json())
curl "https://api.feathery.io/api/form/submission/pdf/" \
    -X POST \
    -d "{'form_id': 'abcdef, 'user_id': 'alice@feathery.io'}" \
    -H "Authorization: Token <API KEY>" \
    -H "Content-Type: application/json"
const url = "https://api.feathery.io/api/form/submission/pdf/";
const data = {"form_id": "abcdef", "user_id": "alice@feathery.io"}
const headers = {
    Authorization: "Token <API KEY>",
    "Content-Type": "application/json"
};
const options = {
    headers, 
    method: 'POST',
    body: JSON.stringify(data)
};
fetch(url, options)
    .then((response) => response.json())
    .then(result => console.log(result));

The above command outputs JSON structured like this:

{"pdf_url": "<PDF URL>"}

Create a PDF export for a specific form submission. The returned URL points to the generated PDF, with a few caveats: * The PDF may not be immediately available. We recommend polling the URL up to 5 times, once per second, to ensure availability. * Once a given submission is completed, the exported PDF will no longer be updated for it even if requested multiple times.

HTTP Request

POST https://api.feathery.io/api/form/submission/pdf/

Request Body Parameters

Parameter Type Description
form_id String The unique ID of the form whose submission you want to export.
user_id String The unique ID corresponding to a Feathery submission / user who you want to export.

Response Body

Parameter Type Description
pdf_url URL A URL where the PDF export can be downloaded from. The file may not be immediately available.

Users

List All Users

import requests

url = "https://api.feathery.io/api/user/";
headers = {"Authorization": "Token <API KEY>"}
result = requests.get(url, headers=headers)
print(result.json())
curl "https://api.feathery.io/api/user/" \
    -H "Authorization: Token <API KEY>"
const url = "https://api.feathery.io/api/user/";
const options = { headers: { Authorization: "Token <API KEY>" } };
fetch(url, options)
    .then((response) => response.json())
    .then(result => console.log(result));

The above command outputs JSON structured like this:

[
  {
    "id": "alice@feathery.io",
    "created_at": "2020-06-01T00:00:00Z",
    "updated_at": "2020-06-02T00:00:00Z"
  },
  {
    "id": "bob@feathery.io",
    "created_at": "2020-06-03T00:00:00Z",
    "updated_at": "2020-06-04T00:00:00Z"
  }
]

List all of your users (corresponding to form submissions) in Feathery.

HTTP Request

GET https://api.feathery.io/api/user/

Request Query Parameters

Parameter Type Description
filter_field_id String (Optional) The ID of a form or hidden field to filter users by.
filter_field_value String (Optional) The value of the field to filter on. Paired with filter_field_id to only return users who have this field value.

Response Body

The response will be an array of objects with the following parameters.

Parameter Type Description
id String Your unique user ID
created_at Datetime When this user was created
updated_at Datetime When this user was last updated

List All Data for a User

import requests

url = "https://api.feathery.io/api/field/?id=alice@feathery.io";
headers = {"Authorization": "Token <API KEY>"}
result = requests.get(url, headers=headers)
print(result.json())
curl "https://api.feathery.io/api/field/?id=alice@feathery.io" \
    -H "Authorization: Token <API KEY>"
const url = "https://api.feathery.io/api/field/?id=alice@feathery.io";
const options = { headers: { Authorization: "Token <API KEY>" } };
fetch(url, options)
    .then((response) => response.json())
    .then(result => console.log(result));

The above command outputs JSON structured like this:

[
  {
    "id": "TextField1",
    "type": "text_field",
    "display_text": "What is your name?",
    "value": "Alice Smith",
    "created_at": "2020-06-01T00:00:00Z",
    "updated_at": "2020-06-02T00:00:00Z",
    "internal_id": "50c15c23-7558-4d51-810a-1a02dlf0bf58",
  }
]

For a specific user, list all of the field data they submitted

HTTP Request

GET https://api.feathery.io/api/field/

Request Query Parameters

Parameter Type Description
id String (Optional) Your unique user ID

Response Body

The response will be an array of objects with the following parameters.

Parameter Type Description
id String Your unique field ID
hidden Boolean If true, this is a hidden field. Otherwise, it's a form field.
type String Enum (Optional) The form field type. Not present for hidden fields.
display_text String (Optional) Human-friendly text to display for this field
value Polymorphic (Optional) Submitted value of the user whose key was passed in.
created_at Datetime When this field was created
updated_at Datetime When this field was last updated
internal_id String Feathery-internal identifier of your field. Always static

Get User Session Data

import requests

url = "https://api.feathery.io/api/user/<user_id>/session/";
headers = {"Authorization": "Token <API KEY>"}
result = requests.get(url, headers=headers)
print(result.json())
curl "https://api.feathery.io/api/user/<user_id>/session/" \
    -H "Authorization: Token <API KEY>"
const url = "https://api.feathery.io/api/user/<user_id>/session/";
const options = { headers: { Authorization: "Token <API KEY>" } };
fetch(url, options)
    .then((response) => response.json())
    .then(result => console.log(result));

The above command outputs JSON structured like this:

{
  "auth_id": "identity-code",
  "internal_id": "user-id",
  "forms": [
    {
      "current_step_id": "step 1",
      "completed_at": null,
      "name": "My form",
      "track_location": false
    },
    {
      "current_step_id": null,
      "completed_at": "2024-04-17T23:10:45.992617Z",
      "name": "My other form",
      "track_location": false
    }
  ]
}

Get session data for a user, including all forms and their progress

HTTP Request

GET https://api.feathery.io/api/user/<user_id>/session/

Response Body

The response will be an object with the following parameters.

Parameter Type Description
auth_id String (Optional) Identity provider ID
internal_id String Unique User ID
forms Array List of forms the user is working on

Each form object will have the following parameters:

Parameter Type Description
id String Form's ID
current_step_id String ID of the step the user most recently visited
completed_at String Timestamp of when the user completed the form - null if not complete
name String Name of the form
track_location Boolean Whether the form remembers the user's location

Create and Fetch a User

import requests

url = "https://api.feathery.io/api/user/";
data = {"id": "alice@feathery.io"}
headers = {
    "Authorization": "Token <API KEY>",
    "Content-Type": "application/json",
}
result = requests.post(url, data=data, headers=headers)
print(result.json())
curl "https://api.feathery.io/api/user/" \
    -X POST \
    -d "{'id': 'alice@feathery.io'}" \
    -H "Authorization: Token <API KEY>" \
    -H "Content-Type: application/json"
const url = "https://api.feathery.io/api/user/";
const data = {id: "alice@feathery.io"}
const headers = {
    Authorization: "Token <API KEY>",
    "Content-Type": "application/json"
};
const options = {
    headers, 
    method: 'POST',
    body: JSON.stringify(data)
};
fetch(url, options)
    .then((response) => response.json())
    .then(result => console.log(result));

The above command outputs JSON structured like this:

{
  "sdk_key": "b8629a53-b1ba-49c1-99bc-0117cf12fd98",
  "id": "alice@feathery.io",
  "created_at": "2020-06-03T00:00:00Z",
  "updated_at": "2020-06-04T00:00:00Z"
}

Create, update, or retrieve the information of a specific user.

If the user corresponding to the ID you specify doesn't exist, they'll be created based on the request body parameters. Otherwise, the user will be updated based on those parameters.

The user SDK key will also be returned, which can be used for authenticating the React library for displaying forms.

HTTP Request

POST https://api.feathery.io/api/user/

Body Parameters

Parameter Type Description
id String Your unique user ID

Response Body

Parameter Type Description
sdk_key String This user's SDK key
id String Your unique user ID
created_at Datetime When this user was created
updated_at Datetime When this user was last updated

Delete a User

import requests

url = "https://api.feathery.io/api/user/alice@feathery.io/";
headers = {
    "Authorization": "Token <API KEY>",
    "Content-Type": "application/json",
}
result = requests.delete(url, headers=headers)
print(result.status_code)
curl "https://api.feathery.io/api/user/alice@feathery.io/" \
    -X DELETE \
    -d "{'key': 'alice@feathery.io'}" \
    -H "Authorization: Token <API KEY>" \
    -H "Content-Type: application/json"
const url = "https://api.feathery.io/api/user/alice@feathery.io/";
const headers = {
    Authorization: "Token <API KEY>",
    "Content-Type": "application/json"
};
const options = { headers, method: 'DELETE' };
fetch(url, options).then((response) => console.log(response.status))

The above command does not return a response body

Delete a specific user.

HTTP Request

DELETE https://api.feathery.io/api/user/<id>/

URL Parameters

Parameter Description
id The ID of the user to delete

Workspaces

List All Workspaces

import requests

url = "https://api.feathery.io/api/workspace/";
headers = {"Authorization": "Token <API KEY>"}
result = requests.get(url, headers=headers)
print(result.json())
curl "https://api.feathery.io/api/workspace/" \
    -H "Authorization: Token <API KEY>"
const url = "https://api.feathery.io/api/workspace/";
const options = { headers: { Authorization: "Token <API KEY>" } };
fetch(url, options)
    .then((response) => response.json())
    .then(result => console.log(result));

The above command outputs JSON structured like this:

[
  {
    "id": "<WORKSPACE UUID>",
    "name": "Workspace 1",
    "logo": "https://url-to-logo.com",
    "brand_url": "https://feathery.io",
    "brand_name": "Brand 1",
    "accounts": [{"email": "user@mail.com", "role": "admin"}]
  }
]

List all of the Feathery workspaces connected to your main account. This is only available for Feathery's white label product.

HTTP Request

GET https://api.feathery.io/api/workspace/

Response Body

The response will be an array of objects with the following parameters.

Parameter Type Description
id UUID Your unique workspace ID
name String The human-readable name of the workspace, e.g. "Company 1"
logo URL A URL to the logo to display in this workspace
brand_url URL A link to the brand website
brand_name String The name of the white label brand
accounts {email: string; role: string}[] A list of accounts in this workspace

Create a Workspace

import requests

url = "https://api.feathery.io/api/workspace/";
data = {"name": "New Workspace"}
headers = {
    "Authorization": "Token <API KEY>",
    "Content-Type": "application/json",
}
result = requests.post(url, data=data, headers=headers)
print(result.json())
curl "https://api.feathery.io/api/workspace/" \
    -X POST \
    -d "{'name': 'New Workspace'}" \
    -H "Authorization: Token <API KEY>" \
    -H "Content-Type: application/json"
const url = "https://api.feathery.io/api/workspace/";
const data = {name: "New Workspace"}
const headers = {
    Authorization: "Token <API KEY>",
    "Content-Type": "application/json"
};
const options = {
    headers, 
    method: 'POST',
    body: JSON.stringify(data)
};
fetch(url, options)
    .then((response) => response.json())
    .then(result => console.log(result));

The above command outputs JSON structured like this:

  {
  "id": "<WORKSPACE UUID>",
  "name": "Workspace 1",
  "logo": "https://url-to-logo.com",
  "brand_url": "https://feathery.io",
  "brand_name": "Brand 1",
  "accounts": [{"email": "user@mail.com", "role": "admin"}]
}

Create a new workspace connected to your main account.

HTTP Request

POST https://api.feathery.io/api/workspace/

Body Parameters

Parameter Type Description
name String The human-readable name of the workspace, e.g. "Company 1"
logo URL (Optional) A URL to the logo to display in this workspace
brand_url URL (Optional) A link to the brand website
brand_name String (Optional) The name of the white label brand

Response Body

Parameter Type Description
id UUID Your unique workspace ID
name String The human-readable name of the workspace, e.g. "Company 1"
logo URL A URL to the logo to display in this workspace
brand_url URL A link to the brand website
brand_name String The name of the white label brand

Retrieve a Workspace

import requests

url = "https://api.feathery.io/api/workspace/<workspace_id>/";
headers = {"Authorization": "Token <API KEY>"}
result = requests.get(url, headers=headers)
print(result.json())
curl "https://api.feathery.io/api/workspace/<workspace_id>/" \
    -H "Authorization: Token <API KEY>"
const url = "https://api.feathery.io/api/workspace/<workspace_id>/";
const options = { headers: { Authorization: "Token <API KEY>" } };
fetch(url, options)
    .then((response) => response.json())
    .then(result => console.log(result));

The above command outputs JSON structured like this:

{
  "id": "<WORKSPACE UUID>",
  "name": "Workspace 1",
  "logo": "https://url-to-logo.com",
  "brand_url": "https://feathery.io",
  "brand_name": "Brand 1",
  "live_api_key": "<LIVE API KEY>",
  "test_api_key": "<TEST API KEY>",
  "accounts": [{"email": "user@mail.com", "role": "admin"}]
}

Retrieve a specific Feathery workspace connected to your main account. This is only available for Feathery's white label product.

HTTP Request

GET https://api.feathery.io/api/workspace/

Response Body

The response will be an array of objects with the following parameters.

Parameter Type Description
id UUID Your unique workspace ID
name String The human-readable name of the workspace, e.g. "Company 1"
logo URL A URL to the logo to display in this workspace
brand_url URL A link to the brand website
brand_name String The name of the white label brand
live_api_key String The live API key of the workspace which can be used to perform operations on it
test_api_key String The test API key of the workspace which can be used to perform operations on it
accounts {email: string; role: string}[] A list of accounts in this workspace

Update a Workspace

import requests

url = "https://api.feathery.io/api/workspace/<workspace_id>/";
data = {"brand_url": "https://feathery.io"}
headers = {
    "Authorization": "Token <API KEY>",
    "Content-Type": "application/json",
}
result = requests.post(url, data=data, headers=headers)
print(result.json())
curl "https://api.feathery.io/api/workspace/<workspace_id>/" \
    -X POST \
    -d "{'brand_url': 'https://feathery.io'}" \
    -H "Authorization: Token <API KEY>" \
    -H "Content-Type: application/json"
const url = "https://api.feathery.io/api/workspace/<workspace_id>/";
const data = {brand_url: "https://feathery.io"}
const headers = {
    Authorization: "Token <API KEY>",
    "Content-Type": "application/json"
};
const options = {
    headers, 
    method: 'POST',
    body: JSON.stringify(data)
};
fetch(url, options)
    .then((response) => response.json())
    .then(result => console.log(result));

The above command outputs JSON structured like this:

  {
  "id": "<WORKSPACE UUID>",
  "name": "Workspace 1",
  "logo": "https://url-to-logo.com",
  "brand_url": "https://feathery.io",
  "brand_name": "Brand 1",
  "accounts": [{"email": "user@mail.com", "role": "admin"}]
}

Update an existing workspace connected to your main account.

HTTP Request

POST https://api.feathery.io/api/workspace/<workspace_id>/

Body Parameters

Parameter Type Description
name String (Optional) The human-readable name of the workspace, e.g. "Company 1"
logo URL (Optional) A URL to the logo to display in this workspace
brand_url URL (Optional) A link to the brand website
brand_name String (Optional) The name of the white label brand

Response Body

Parameter Type Description
id UUID Your unique workspace ID
name String The human-readable name of the workspace, e.g. "Company 1"
logo URL A URL to the logo to display in this workspace
brand_url URL A link to the brand website
brand_name String The name of the white label brand

Delete a Workspace

import requests

url = "https://api.feathery.io/api/workspace/<workspace_id>/";
headers = {
    "Authorization": "Token <API KEY>",
    "Content-Type": "application/json",
}
result = requests.delete(url, headers=headers)
print(result.status_code)
curl "https://api.feathery.io/api/workspace/<workspace_id>/" \
    -X DELETE \
    -H "Authorization: Token <API KEY>" \
    -H "Content-Type: application/json"
const url = "https://api.feathery.io/api/workspace/<workspace_id>/";
const headers = {
    Authorization: "Token <API KEY>",
    "Content-Type": "application/json"
};
const options = { headers, method: 'DELETE' };
fetch(url, options).then((response) => console.log(response.status))

The above command does not return a response body

Delete a workspace connected to your main Feathery account.

HTTP Request

DELETE https://api.feathery.io/api/workspace/<workspace_id>/

URL Parameters

Parameter Description
workspace_id The ID of the workspace to delete

HTTP Status Codes

The Feathery API uses the following codes:

Status Code Meaning
200 OK -- Everything worked as expected.
201 Created -- Everything worked as expected. A resource was created.
204 No Content -- Everything worked as expected. No content was returned.
400 Bad Request -- The request was unacceptable, often due to missing a required parameter.
401 Unauthorized -- Your API key is wrong.
403 Forbidden -- The API key doesn't have permissions to perform the request.
404 Not Found -- The requested resource doesn't exist.
405 Method Not Allowed -- You specified an invalid method on the requestedURL.
429 Too Many Requests -- Too many requests hit the API too quickly. We recommend an exponential backoff of your requests.
500 Something went wrong on Feathery's end. (These are rare.)