> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nekt.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Jotform as a data source

> Bring data from Jotform to your Lakehouse.

Jotform is an online form builder used to create forms, surveys, registrations and order forms and to collect their responses. The connector reads your Jotform account through the Jotform API and brings your forms, their questions and settings, every submission with its answers (also as one row per question), reports, labels and sub-users to your Lakehouse.

## Configuring Jotform as a Source

In the [Sources](https://app.nekt.ai/sources) tab, click on the "Add source" button located on the top right of your screen. Then, select the Jotform option from the list of connectors.

Click **Next** and you'll be prompted to add your access.

### 1. Add account access

The connector uses a Jotform **API key**. To create one, sign in to Jotform, open **My Account > API**, click **Create New Key** and copy it. **Read Access** is enough: the connector never changes anything in your account.

<Tip>Create the key with the account that owns the forms (or an admin of your Jotform Enterprise or Teams workspace). The connector sees only the forms and submissions this account can see.</Tip>

<Warning>Every Jotform plan limits how many API calls an account can make per day (1,000 on Starter, 10,000 on Bronze, 50,000 on Silver, 100,000 on Gold and 1,000,000 on Enterprise), and that limit is shared by every integration using your account. The connector is built to spend as little as possible (see API Limits & Performance at the end of this page), checks before reading the rest of the data that today's remaining calls are enough for the sync, and stops with a clear message when Jotform reports the limit as reached. The limit resets at midnight US Eastern time.</Warning>

The following configurations are available:

* **API Key**: the Jotform API key. Required.

* **API Region**: (Default: Standard) where your Jotform account is hosted. Choose **EU** if you sign in at `eu.jotform.com`, **HIPAA** for HIPAA-compliant accounts and **Enterprise** if your forms are on your company's own Jotform domain. Required.

* **Enterprise API URL**: shown only for Enterprise. The API address of your Enterprise domain, such as `https://forms.your-company.com/API` or `https://your-company.jotform.com/API`. Only HTTPS addresses with a domain name are accepted.

* **Team ID**: only if your forms live in a Jotform Teams workspace: the ID of that team. Leave it empty for forms in your own workspace. After the first sync, check that the Forms table has as many forms as the team sees in Jotform.

* **Start Date**: only submissions received on or after this date are extracted. Leave it empty to extract every submission. It does not apply to forms, reports, labels and sub-users, which are always read in full.

* **Lookback Days**: (Default: 3) on every sync, submissions received or edited up to this many days before the previous sync are read again, so late arrivals (for example, submissions made offline in the Jotform mobile app and uploaded later) are not missed.

* **API Time Zone**: (Default: `America/New_York`) Jotform's API returns dates without a time zone, in Jotform's server time (US Eastern), regardless of the time zone of your account. The connector converts them to UTC using this setting. Change it only if every date in your tables comes out shifted by the same number of hours.

Once you're done, click **Next**.

### 2. Select streams

Choose which data streams you want to sync. For faster extractions, select only the streams that are relevant to your analysis. You can select entire groups of streams or pick specific ones.

> Tip: The stream can be found more easily by typing its name.

<Info>Form Questions and Form Properties cost one API call per form, but only for forms edited since the previous sync, plus one per form once a week to refresh them. Label Resources costs one API call per label. All the other streams cost a handful of calls per sync.</Info>

Select the streams and click **Next**.

### 3. Configure data streams

Customize how you want your data to appear in your catalog. Select the desired layer where the data will be placed, a folder to organize it inside the layer, a name for each table (which will effectively contain the fetched data) and the type of sync.

* **Layer**: choose between the existing layers on your catalog. This is where you will find your new extracted tables as the extraction runs successfully.
* **Folder**: a folder can be created inside the selected layer to group all tables being created from this new data source.
* **Table name**: we suggest a name, but feel free to customize it. You have the option to add a **prefix** to all tables at once and make this process faster!
* **Sync Type**: use INCREMENTAL for Submissions, Submission Answers, Form Questions and Form Properties: each sync then reads only new and edited submissions and only the forms edited since the previous sync (plus, once a week, every form). Forms, Reports, Labels, Label Resources and Sub-users are FULL\_TABLE: each sync reads their current state. Read more about Sync Types [here](https://docs.nekt.com/get-started/core-concepts/types-of-sync).

<Note>A full sync of Submissions reads every submission again, at about one API call per 980 submissions, and is the way to remove from the table submissions that were deleted in Jotform (see Data Quality Considerations). If Submission Answers is set to FULL\_TABLE, every submission is read on every sync, because that table is replaced each time.</Note>

Once you are done configuring, click **Next**.

### 4. Configure data source

Describe your data source for easy identification within your organization, not exceeding 140 characters.

To define your [Trigger](https://docs.nekt.com/runs/scheduling-and-triggers), consider how often you want data to be extracted from this source. This decision usually depends on how frequently you need the new table data updated (every day, once a week, or only at specific times). On Jotform plans with a low daily API limit, prefer hourly or less frequent syncs.

Optionally, you can define some additional settings:

* Configure Delta Log Retention and determine for how long we should store old states of this table as it gets updated. Read more about this resource [here](https://docs.nekt.com/get-started/core-concepts/resource-control).
* An **Additional [Full Sync](https://docs.nekt.com/get-started/core-concepts/types-of-sync#additional-full-sync)** is only needed if you want submissions deleted in Jotform, or questions removed from forms, to disappear from your tables. Each one costs one API call per form for Form Questions and Form Properties, plus about one call per 980 submissions, so on plans with a low daily limit prefer an occasional manual full sync over a frequent schedule. Check that it finished successfully (see API Limits & Performance).

Once you are ready, click **Next** to finalize the setup.

### 5. Check your new source

You can view your new source on the [Sources](https://app.nekt.ai/sources) page. If needed, manually trigger the source extraction by clicking on the arrow button. Once executed, your data will appear in your Catalog.

<Warning>For you to be able to see it on your [Catalog](https://app.nekt.ai/catalog), you need at least one successful source run.</Warning>

# Streams and Fields

## Available streams

The table below lists every stream, its **slug** (the exact identifier to pass when creating the source via API) and a short description.

| Stream | Slug | Description |
| - | - | - |
| Forms | `forms` | Every form of the account, with its status and submission counters. |
| Form Questions | `form_questions` | One row per question of each form: label, type, order and properties. |
| Form Properties | `form_properties` | All settings of each form, as JSON. |
| Submissions | `submissions` | Every submission (response) with all its answers as JSON. Incremental. |
| Submission Answers | `submission_answers` | One row per answered question of each submission. Incremental. |
| Reports | `reports` | Reports created on the account's forms. |
| Labels | `labels` | Labels (folders) of the workspace, including nested ones. |
| Label Resources | `label_resources` | Forms and other assets inside each label. |
| Sub-users | `subusers` | Sub-user accounts and what they can access. |

## Fields by stream

All dates are in UTC. Nested values (answers, question properties, payment settings, permissions) are JSON strings. A submission's answers are keyed by question ID (`question_id`), the same ID used in Form Questions.

<AccordionGroup>
  <Accordion title="Forms">
    Every form of the account returned by Jotform (enabled and disabled; forms in the trash appear with status DELETED when Jotform lists them), with submission counters. Read in full on every sync.

    Primary key: `id` · Full table

    | Field | Type | Description |
    | :- | :- | :- |
    | `id` | String | Form ID (the number in the form URL). |
    | `username` | String | Jotform username of the form owner. |
    | `title` | String | Title of the form. |
    | `url` | String | Public URL of the form. |
    | `status` | String | Form status: ENABLED, DISABLED or DELETED (in the trash). |
    | `type` | String | Form layout type: LEGACY (classic) or CARD. |
    | `height` | Integer | Form height in pixels. |
    | `new` | Integer | Number of unread submissions of the form. |
    | `count` | Integer | Total number of submissions of the form. |
    | `favorite` | Integer | 1 when the form is marked as favorite, otherwise 0. |
    | `archived` | Integer | 1 when the form is archived, otherwise 0. |
    | `has_payment` | Boolean | Whether the form has a payment field. |
    | `payment_props` | String (JSON) | Payment settings of the form (gateway, type, question id), as a JSON object. |
    | `created_at` | Datetime | When the form was created (UTC). |
    | `updated_at` | Datetime | When the form was last edited (UTC). |
    | `last_submission` | Datetime | When the form last received a submission (UTC). |
  </Accordion>

  <Accordion title="Form Questions">
    One row per question (field) of each form: label, type, order and every property Jotform returns. A form's questions are requested again when the form was edited since the previous sync, and at least once a week. Forms in the trash are not read.

    Primary key: `form_id`, `question_id` · Replication key: `form_updated_at`

    | Field | Type | Description |
    | :- | :- | :- |
    | `form_id` | String | ID of the form the question belongs to. |
    | `question_id` | String | Question ID (qid), unique within the form; keys the answers of submissions. |
    | `order` | Integer | Position of the question in the form. |
    | `name` | String | Unique field name of the question in the form. |
    | `text` | String | Question label shown on the form. |
    | `type` | String | Question type, e.g. control\_textbox, control\_dropdown, control\_fullname. |
    | `required` | String | Whether an answer is required (Jotform sends Yes or No). |
    | `properties` | String (JSON) | Every property Jotform returns for the question (options, sublabels, validation...), as a JSON object. |
    | `form_updated_at` | Datetime | When the form was last edited (UTC) at the time the questions were read; used as the incremental replication key. |
  </Accordion>

  <Accordion title="Form Properties">
    One row per form with all of its settings (thank-you page, notification e-mails, limits, styles) as JSON. Refreshed for forms edited since the previous sync, and at least once a week.

    Primary key: `form_id` · Replication key: `form_updated_at`

    | Field | Type | Description |
    | :- | :- | :- |
    | `form_id` | String | ID of the form. |
    | `title` | String | Title of the form, from its properties. |
    | `properties` | String (JSON) | Every form property Jotform returns (thank-you text, emails, limits, styles...), as a JSON object. |
    | `form_updated_at` | Datetime | When the form was last edited (UTC) at the time the properties were read; used as the incremental replication key. |
  </Accordion>

  <Accordion title="Submissions">
    Every response received by every form of the account, with all its answers as a JSON object.

    Primary key: `id` · Replication key: `last_updated_at`

    | Field | Type | Description |
    | :- | :- | :- |
    | `id` | String | Submission ID. |
    | `form_id` | String | ID of the form that received the submission. |
    | `ip` | String | IP address of the submitter. |
    | `status` | String | Submission status as Jotform reports it (ACTIVE, OVERQUOTA, ARCHIVED, DELETED...). |
    | `new` | Integer | 1 while the submission has not been read in Jotform, otherwise 0 (value as of the last time the row was extracted). |
    | `flag` | Integer | 1 when the submission is flagged (starred) in Jotform, otherwise 0 (value as of the last time the row was extracted). |
    | `notes` | String | Notes added to the submission in Jotform. |
    | `workflow_status` | String | Approval status, only for forms connected to a Jotform workflow. |
    | `answers` | String (JSON) | Every answer of the submission keyed by question ID, as a JSON object (each with text, type, answer and prettyFormat). The same answers, one row per question, are in the submission\_answers stream. |
    | `created_at` | Datetime | When the submission was received (UTC). |
    | `updated_at` | Datetime | When the submission was last edited (UTC); empty when it was never edited. |
    | `last_updated_at` | Datetime | updated\_at when the submission was edited, otherwise created\_at (UTC); used as the incremental replication key. |
  </Accordion>

  <Accordion title="Submission Answers">
    One row per answered question of each submission, taken from the same response as Submissions (no extra API call). Join with Form Questions on `form_id` and `question_id`.

    Primary key: `submission_id`, `question_id` · Replication key: `submission_last_updated_at`

    | Field | Type | Description |
    | :- | :- | :- |
    | `submission_id` | String | ID of the submission. |
    | `question_id` | String | Question ID (qid) within the form; matches form\_questions.question\_id. |
    | `form_id` | String | ID of the form that received the submission. |
    | `name` | String | Unique field name of the question in the form. |
    | `order` | Integer | Position of the question in the form. |
    | `text` | String | Question label at the time of the submission. |
    | `type` | String | Question type, e.g. control\_textbox, control\_fullname, control\_fileupload. |
    | `answer` | String | The submitted answer: plain text as sent, or a JSON value for structured answers (full name, address, multiple choices, uploaded file URLs...). |
    | `pretty_format` | String | Human-readable version of the answer, as Jotform formats it. |
    | `other_properties` | String (JSON) | Any other property Jotform returns for the answer (e.g. sublabels), as a JSON object. |
    | `submission_created_at` | Datetime | When the submission was received (UTC). |
    | `submission_last_updated_at` | Datetime | When the submission was last edited, or received if never edited (UTC); used as the incremental replication key. |
  </Accordion>

  <Accordion title="Reports">
    Reports (Excel, CSV, grid, table, visual...) created on the account's forms.

    Primary key: `id` · Full table

    | Field | Type | Description |
    | :- | :- | :- |
    | `id` | String | Report ID. |
    | `form_id` | String | ID of the form the report is built on. |
    | `title` | String | Title of the report. |
    | `list_type` | String | Report type: excel, csv, grid, table, calendar, rss or visual. |
    | `type` | String | Report kind as returned by Jotform. |
    | `status` | String | Report status: ENABLED or DELETED. |
    | `fields` | String | Comma-separated list of the fields in the report (ip, dt and question IDs). |
    | `url` | String | URL of the report. |
    | `is_protected` | Boolean | Whether the report is password protected. |
    | `settings` | String (JSON) | Report settings, as a JSON object. |
    | `share` | String (JSON) | Sharing settings of the report, as a JSON object. |
    | `properties` | String (JSON) | Other report properties, as JSON. |
    | `form_title` | String | Title of the form the report is built on. |
    | `form_url` | String | URL of the form the report is built on. |
    | `form_count` | Integer | Number of forms in the report. |
    | `created_at` | Datetime | When the report was created (UTC). |
    | `updated_at` | Datetime | When the report was last edited (UTC). |
    | `last_submission` | Datetime | When the report's form last received a submission (UTC). |
  </Accordion>

  <Accordion title="Labels">
    Labels (the folders of your Jotform workspace), one row per label including nested ones.

    Primary key: `id` · Full table

    | Field | Type | Description |
    | :- | :- | :- |
    | `id` | String | Label ID. |
    | `name` | String | Name of the label. |
    | `parent_label_id` | String | ID of the parent label (for a top-level label, the ID of the account's root label). |
    | `depth` | Integer | Nesting level of the label: 1 for top-level labels. |
    | `order` | Integer | Position of the label among its siblings. |
    | `color` | String | Color of the label (hex). |
    | `owner` | String | Username of the label owner. |
    | `created_at` | Datetime | When the label was created (UTC). |
    | `updated_at` | Datetime | When the label was last changed (UTC). |
  </Accordion>

  <Accordion title="Label Resources">
    What each label contains: forms, workflows, apps and other assets.

    Primary key: `label_id`, `asset_type`, `asset_id` · Full table

    | Field | Type | Description |
    | :- | :- | :- |
    | `label_id` | String | ID of the label. |
    | `asset_type` | String | Kind of asset in the label: form, workflow, portal (app), table. |
    | `asset_id` | String | ID of the asset; for forms, the form ID. |
    | `title` | String | Title of the asset. |
    | `status` | String | Status of the asset (ENABLED, DISABLED...). |
    | `username` | String | Username of the asset owner. |
    | `created_at` | Datetime | When the asset was created (UTC). |
    | `updated_at` | Datetime | When the asset was last edited (UTC). |
    | `details` | String (JSON) | Every property Jotform returns for the asset, as a JSON object. |
  </Accordion>

  <Accordion title="Sub-users">
    Sub-user accounts and the forms and folders they can access.

    Primary key: `id` · Full table

    | Field | Type | Description |
    | :- | :- | :- |
    | `id` | String | Sub-user identifier: the username, or the e-mail while the invitation is pending. |
    | `username` | String | Username of the sub-user. |
    | `email` | String | E-mail of the sub-user. |
    | `owner` | String | Username of the account that created the sub-user. |
    | `status` | String | Sub-user status: LIVE, DELETED or PENDING (invitation not accepted). |
    | `permissions` | String (JSON) | Forms and folders the sub-user can access, with the access type (full or readOnly), as a JSON array. |
    | `created_at` | Datetime | When the sub-user was created (UTC). |
  </Accordion>
</AccordionGroup>

## Implementation Notes

### Data Quality Considerations

* Jotform's API returns dates in Jotform's server time (US Eastern) whatever the time zone of your account, so dates shown in Jotform's own screens can differ by some hours from the UTC dates in your tables. They refer to the same moment.
* Submissions deleted in Jotform are not removed from the Submissions and Submission Answers tables by incremental syncs: Jotform's API offers no way to list what was deleted. A full sync of these streams (or an Additional Full Sync) brings the tables back in line with Jotform.
* An edited submission is read again on the next sync and replaces the old version. The `new` (unread), `flag` (starred), `status` and `workflow_status` columns may change in Jotform without the submission counting as edited, so on rows older than **Lookback Days** they can show the value of the last time the submission was extracted. A full sync refreshes them.
* Form Questions and Form Properties are refreshed when the form is edited in Jotform, and every form is read again at least once a week, in case Jotform does not mark the form as edited when only a question changes. A question removed from a form stays in the Form Questions table until a full sync of that stream. Forms in the trash are not read.
* Late submissions (for example, made offline and uploaded later) are caught as long as they arrive within **Lookback Days** of the previous sync.

### API Limits & Performance

Calls made by each sync, which count toward your Jotform daily API limit:

| Part of the sync | Incremental sync | Full sync |
| :- | :- | :- |
| Forms list (always read, also used by the other streams) | about 1 per 980 forms | about 1 per 980 forms |
| Form Questions, Form Properties | 1 per form edited since the previous sync or last read over a week ago, for each stream | 1 per form, for each stream |
| Submissions and Submission Answers | at least 3 (1 check + 2 reads), plus about 1 per 980 new or edited submissions | about 1 per 980 submissions, plus 1 check |
| Reports, Labels, Sub-users | 1 each | 1 each |
| Label Resources | 1 per label | 1 per label |

* With every stream selected and nothing changed in Jotform, a sync costs about 7 calls plus one per label (and, once a week, one per form for Form Questions and Form Properties). Lists are read in pages of 1,000 that overlap by 20 rows, which is why a page brings about 980 new records. Each sync ends with a line in the run log saying how many calls it made and how many are left for today.
* Before reading the rest of the data, the connector estimates the calls the sync still needs and compares them with what Jotform reports as left for today. If they are not enough, the sync stops before spending them and without changing any table. If the sync fits but leaves fewer than 20 calls for the day, the run log warns that your other integrations may run out.
* Everything that is replaced on each sync (Forms, Reports, Labels, Label Resources, Sub-users, and, in a full sync, Form Questions, Form Properties, Submissions and Submission Answers) is read completely before the connector writes anything. If Jotform answers with errors, refuses access or reports the daily limit as reached while that data is being read, the sync stops and those tables stay as they were. Incremental reads that stop midway do not change what is already in your tables, and the next sync picks up from the same point.
* If a sync is interrupted for another reason while it is writing a table that is replaced on each sync (for example, during a full sync), that table can be left incomplete until the next successful run. Run the sync again until it succeeds.

## Troubleshooting

| Error or symptom | What it means | What to do |
| :- | :- | :- |
| The run fails saying Jotform did not accept the API key | The key is wrong or was deleted, or the API Region does not match your account. | Check the key in Jotform > My Account > API and the API Region (and Team ID, for Teams) in the source settings. |
| The run fails saying Nekt could not connect to the Jotform API | The Enterprise API URL or the region points to an address that does not answer. | Fix the Enterprise API URL (it usually ends in `/API`) or the API Region. |
| The run fails saying the account reached its daily API call limit, or that the sync needs more calls than are left today | Your Jotform plan's daily limit is used up, by Nekt or by other integrations of the account. | Run it again after midnight US Eastern time, sync less often or fewer streams, or upgrade your Jotform plan. |
| The run fails saying the API key cannot read a stream | The key's account cannot read that data (for example, sub-users on a plan without them). | Use a key of the account owner, or deselect the stream. |
| A warning says the questions or properties of some forms could not be read | Those forms were deleted during the sync or are not accessible to the key. | Nothing, if they were deleted; otherwise check the key's access. They are read again on the next sync. In a full sync, a form the key cannot read stops the sync instead. |
| The run fails saying Jotform did not return the list of forms | The API Region, Enterprise API URL or Team ID does not match the account of the key. | Fix them in the source settings. |
| A full sync failed | Jotform errors or the daily limit, or an interruption while writing. | Run it again until it succeeds; until then the tables replaced by that sync may be incomplete. |
| Team forms are missing | The forms belong to a Jotform Teams workspace. | Fill **Team ID** in the source settings and compare the number of rows in Forms with the forms the team sees in Jotform. |
| Dates are shifted by the same number of hours | Your Jotform API returns dates in a different time zone. | Set **API Time Zone** to that time zone. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.