# @memberjunction/actions-bizapps-formbuilders
Form builder and survey platform integration actions for MemberJunction. This package provides a unified interface across four major form/survey platforms, enabling AI agents, workflows, and applications to programmatically retrieve responses, analyze submissions, and manage forms through the MemberJunction Actions framework.
This package is part of the [BizApps Actions](../README.md) collection within the broader [MemberJunction Actions Framework](../../README.md). For core action architecture, design philosophy, and when to use actions versus direct code, see the [Actions CLAUDE.md](../../CLAUDE.md).
## Architecture
The package follows a three-tier class hierarchy: a shared base class providing common form operations, provider-specific base classes handling API authentication and data normalization, and individual action classes implementing specific operations.
```mermaid
graph TD
subgraph Framework["Actions Framework"]
BA["BaseAction
@memberjunction/actions"]
end
subgraph Base["Shared Base Layer"]
BFBA["BaseFormBuilderAction
Credential management, CSV export,
statistics helpers, response normalization"]
end
subgraph Providers["Provider Base Classes"]
TF["TypeformBaseAction
Bearer token auth
Axios + rate limiting"]
JF["JotFormBaseAction
API key (query param)
Regional endpoints"]
SM["SurveyMonkeyBaseAction
OAuth 2.0 Bearer
Paginated responses"]
GF["GoogleFormsBaseAction
OAuth 2.0 Bearer
Read-only API"]
end
subgraph Actions["Action Classes (34 total)"]
TFA["Typeform Actions (10)"]
JFA["JotForm Actions (8)"]
SMA["SurveyMonkey Actions (8)"]
GFA["Google Forms Actions (4)"]
end
subgraph Shared["Shared Utilities"]
FCP["FileContentProcessor
PDF, Excel, Word,
image extraction"]
end
BA --> BFBA
BFBA --> TF
BFBA --> JF
BFBA --> SM
BFBA --> GF
TF --> TFA
JF --> JFA
SM --> SMA
GF --> GFA
TFA -.-> FCP
style Framework fill:#64748b,stroke:#475569,color:#fff
style Base fill:#2d6a9f,stroke:#1a4971,color:#fff
style TF fill:#7c5295,stroke:#563a6b,color:#fff
style JF fill:#7c5295,stroke:#563a6b,color:#fff
style SM fill:#7c5295,stroke:#563a6b,color:#fff
style GF fill:#7c5295,stroke:#563a6b,color:#fff
style TFA fill:#2d8659,stroke:#1a5c3a,color:#fff
style JFA fill:#2d8659,stroke:#1a5c3a,color:#fff
style SMA fill:#2d8659,stroke:#1a5c3a,color:#fff
style GFA fill:#2d8659,stroke:#1a5c3a,color:#fff
style FCP fill:#b8762f,stroke:#8a5722,color:#fff
```
## Supported Platforms
| Platform | Auth Method | Actions | Capabilities |
|----------|------------|---------|-------------|
| **Typeform** | Bearer token (personal access token) | 10 | Full CRUD: responses, forms, file content |
| **JotForm** | API key (query parameter) | 8 | Full CRUD: submissions, forms |
| **SurveyMonkey** | OAuth 2.0 Bearer token | 8 | Full CRUD: responses, surveys, collectors |
| **Google Forms** | OAuth 2.0 Bearer token | 4 | Read-only: responses, form details |
## Installation
```bash
npm install @memberjunction/actions-bizapps-formbuilders
```
This package is part of the MemberJunction monorepo. When working within the monorepo, add the dependency to your package's `package.json` and run `npm install` at the repository root.
## Actions Reference
### Typeform Actions (10)
| Action Class | Registration Name | Description |
|-------------|------------------|-------------|
| `GetTypeformResponsesAction` | `GetTypeformResponsesAction` | Retrieve responses with filtering, pagination, and auto-paginate support |
| `GetSingleTypeformResponseAction` | `GetSingleTypeformResponseAction` | Retrieve a specific response by token |
| `GetTypeformStatisticsAction` | `GetTypeformStatisticsAction` | Calculate aggregate analytics (completion rates, distributions, top answers) |
| `ExportTypeformCSVAction` | `ExportTypeformCSVAction` | Export responses to CSV format with optional metadata |
| `WatchNewTypeformResponsesAction` | `WatchNewTypeformResponsesAction` | Poll for new submissions since a given timestamp |
| `GetTypeformAction` | `GetTypeformAction` | Retrieve complete form configuration and field definitions |
| `GetTypeformFormsAction` | `GetTypeformFormsAction` | List all forms in a workspace |
| `TypeformGetFileContentAction` | `TypeformGetFileContentAction` | Download and process file upload answers (PDF, Excel, Word, images) |
| `CreateTypeformAction` | `CreateTypeformAction` | Create new forms programmatically with fields, settings, and logic |
| `UpdateTypeformAction` | `UpdateTypeformAction` | Modify existing forms with safe merge mode |
### JotForm Actions (8)
| Action Class | Registration Name | Description |
|-------------|------------------|-------------|
| `GetJotFormSubmissionsAction` | `GetJotFormSubmissionsAction` | Retrieve submissions with filtering and pagination |
| `GetSingleJotFormSubmissionAction` | `GetSingleJotFormSubmissionAction` | Retrieve a specific submission by ID |
| `GetJotFormStatisticsAction` | `GetJotFormStatisticsAction` | Calculate aggregate analytics from submissions |
| `ExportJotFormCSVAction` | `ExportJotFormCSVAction` | Export submissions to CSV format |
| `WatchNewJotFormSubmissionsAction` | `WatchNewJotFormSubmissionsAction` | Poll for new submissions since a given timestamp |
| `GetJotFormAction` | `GetJotFormAction` | Retrieve form details and questions |
| `CreateJotFormAction` | `CreateJotFormAction` | Create new forms with questions and properties |
| `UpdateJotFormAction` | `UpdateJotFormAction` | Modify existing form configuration |
### SurveyMonkey Actions (8)
| Action Class | Registration Name | Description |
|-------------|------------------|-------------|
| `GetSurveyMonkeyResponsesAction` | `GetSurveyMonkeyResponsesAction` | Retrieve responses with date range and status filtering |
| `GetSingleSurveyMonkeyResponseAction` | `GetSingleSurveyMonkeyResponseAction` | Retrieve a specific response by ID |
| `GetSurveyMonkeyStatisticsAction` | `GetSurveyMonkeyStatisticsAction` | Calculate aggregate analytics from responses |
| `ExportSurveyMonkeyCSVAction` | `ExportSurveyMonkeyCSVAction` | Export responses to CSV format |
| `WatchNewSurveyMonkeyResponsesAction` | `WatchNewSurveyMonkeyResponsesAction` | Poll for new responses since a given timestamp |
| `GetSurveyMonkeyAction` | `GetSurveyMonkeyAction` | Retrieve survey details and configuration |
| `CreateSurveyMonkeyAction` | `CreateSurveyMonkeyAction` | Create new surveys with pages and questions |
| `UpdateSurveyMonkeyAction` | `UpdateSurveyMonkeyAction` | Modify existing survey properties |
### Google Forms Actions (4)
| Action Class | Registration Name | Description |
|-------------|------------------|-------------|
| `GetSingleGoogleFormsResponseAction` | `GetSingleGoogleFormsResponseAction` | Retrieve a specific response by ID |
| `GetGoogleFormsStatisticsAction` | `GetGoogleFormsStatisticsAction` | Calculate aggregate analytics from responses |
| `ExportGoogleFormsCSVAction` | `ExportGoogleFormsCSVAction` | Export responses to CSV format |
| `GetGoogleFormAction` | `GetGoogleFormAction` | Retrieve form details, questions, and quiz settings |
> **Note**: Google Forms API is read-only. There are no endpoints for creating or updating forms programmatically.
## Common Data Model
All providers normalize their responses into a shared `FormResponse` interface, enabling provider-agnostic processing downstream.
```mermaid
classDiagram
class FormResponse {
+string responseId
+string formId
+Date submittedAt
+boolean completed
+FormAnswer[] answerDetails
+Record answers
+metadata
+calculatedFields
+hiddenFields
}
class FormAnswer {
+string fieldId
+string fieldType
+string question
+answer
+string[] choices
}
class FormStatistics {
+number totalResponses
+number completedResponses
+number partialResponses
+number completionRate
+number averageCompletionTime
+Record responsesByDate
+topAnswers
}
FormResponse --> FormAnswer : answerDetails
style FormResponse fill:#2d6a9f,stroke:#1a4971,color:#fff
style FormAnswer fill:#7c5295,stroke:#563a6b,color:#fff
style FormStatistics fill:#2d8659,stroke:#1a5c3a,color:#fff
```
## Authentication and Credentials
All form builder actions use a secure credential resolution chain. Credentials are never passed as direct action parameters; instead, they are resolved from environment variables or the MemberJunction `Company Integrations` entity.
### Credential Resolution Order
1. **Environment variables** (company-specific): `BIZAPPS_{PROVIDER}_{COMPANY_ID}_{CREDENTIAL_TYPE}`
2. **Environment variables** (default): `BIZAPPS_{PROVIDER}_{CREDENTIAL_TYPE}`
3. **Database** (Company Integrations entity): `AccessToken`, `APIKey` fields
### Environment Variable Examples
```bash
# Typeform - Personal access token
export BIZAPPS_TYPEFORM_API_TOKEN=tfp_your_token_here
export BIZAPPS_TYPEFORM_12345_API_TOKEN=tfp_company_specific_token
# JotForm - API key
export BIZAPPS_JOTFORM_API_KEY=your_jotform_api_key
export BIZAPPS_JOTFORM_12345_API_KEY=company_specific_key
# SurveyMonkey - OAuth 2.0 access token
export BIZAPPS_SURVEYMONKEY_ACCESS_TOKEN=your_oauth_token
# Google Forms - OAuth 2.0 access token
export BIZAPPS_GOOGLE_FORMS_ACCESS_TOKEN=your_google_oauth_token
# OAuth2 client credentials (for token refresh)
export BIZAPPS_TYPEFORM_CLIENT_ID=your_client_id
export BIZAPPS_TYPEFORM_CLIENT_SECRET=your_client_secret
```
### Database Configuration
Store credentials in the `Company Integrations` entity linked to the appropriate Integration record:
```sql
-- 1. Create Integration record for the platform
INSERT INTO Integration (Name, Description, NavigationBaseURL, ClassName)
VALUES ('Typeform', 'Typeform form builder', 'https://api.typeform.com', 'TypeformIntegration');
-- 2. Link to Company with credentials
INSERT INTO CompanyIntegration (CompanyID, IntegrationID, AccessToken, IsActive)
VALUES (@CompanyID, @IntegrationID, 'tfp_your_token', 1);
```
## Usage Examples
### Retrieving Form Responses
```typescript
import { ActionEngineServer } from '@memberjunction/actions';
const engine = ActionEngineServer.Instance;
// Get Typeform responses with date filtering
const result = await engine.RunAction({
Action: engine.Actions.find(a => a.Name === 'Get Typeform Responses'),
Params: [
{ Name: 'CompanyID', Type: 'Input', Value: 'company-123' },
{ Name: 'FormID', Type: 'Input', Value: 'abc123' },
{ Name: 'Since', Type: 'Input', Value: '2024-01-01T00:00:00Z' },
{ Name: 'GetAllPages', Type: 'Input', Value: true }
],
ContextUser: currentUser
});
if (result.Success) {
const responses = result.Params.find(p => p.Name === 'Responses')?.Value;
console.log(`Retrieved ${responses.length} responses`);
}
```
### Exporting Responses to CSV
```typescript
const exportResult = await engine.RunAction({
Action: engine.Actions.find(a => a.Name === 'Export JotForm Responses to CSV'),
Params: [
{ Name: 'CompanyID', Type: 'Input', Value: 'company-123' },
{ Name: 'FormID', Type: 'Input', Value: '240123456789' },
{ Name: 'IncludeMetadata', Type: 'Input', Value: true }
],
ContextUser: currentUser
});
const csvData = exportResult.Params.find(p => p.Name === 'CSVData')?.Value;
```
### Creating a Form Programmatically
```typescript
const createResult = await engine.RunAction({
Action: engine.Actions.find(a => a.Name === 'Create Typeform'),
Params: [
{ Name: 'CompanyID', Type: 'Input', Value: 'company-123' },
{ Name: 'Title', Type: 'Input', Value: 'Customer Feedback Survey' },
{
Name: 'Fields', Type: 'Input', Value: [
{
type: 'short_text',
title: 'What is your name?',
ref: 'name'
},
{
type: 'rating',
title: 'How satisfied are you?',
ref: 'satisfaction',
properties: { steps: 5, shape: 'star' }
},
{
type: 'long_text',
title: 'Any additional feedback?',
ref: 'feedback'
}
]
}
],
ContextUser: currentUser
});
const formUrl = createResult.Params.find(p => p.Name === 'FormURL')?.Value;
```
### Watching for New Responses
```typescript
// Poll for new SurveyMonkey responses
const watchResult = await engine.RunAction({
Action: engine.Actions.find(a => a.Name === 'Watch for New SurveyMonkey Responses'),
Params: [
{ Name: 'CompanyID', Type: 'Input', Value: 'company-123' },
{ Name: 'SurveyID', Type: 'Input', Value: 'survey-456' },
{ Name: 'LastCheckedTimestamp', Type: 'Input', Value: '2024-06-01T00:00:00Z' },
{ Name: 'OnlyCompleted', Type: 'Input', Value: true }
],
ContextUser: currentUser
});
const hasNew = watchResult.Params.find(p => p.Name === 'HasNewResponses')?.Value;
const newResponses = watchResult.Params.find(p => p.Name === 'NewResponses')?.Value;
```
## File Content Processing
The package includes a `FileContentProcessor` utility used by the Typeform `GetFileContentAction` to intelligently extract content from file upload answers. It supports multiple file formats:
| Format | Processing | Output |
|--------|-----------|--------|
| PDF | Text extraction via `pdf-parse` | Plain text |
| Excel (.xlsx, .xls) | Sheet parsing via `exceljs` | Structured JSON |
| Word (.docx, .doc) | Text extraction via `mammoth` | Plain text |
| Images | Base64 encoding | Base64 string (for LLM vision) |
| Text/JSON/XML/CSV | UTF-8 decoding | Plain text |
| Other binary | Base64 encoding | Base64 string |
## Error Handling
All actions return consistent error information through the `ActionResultSimple` interface:
| Result Code | Description |
|------------|-------------|
| `SUCCESS` | Operation completed successfully |
| `MISSING_FORM_ID` | Required FormID parameter was not provided |
| `MISSING_API_TOKEN` | No API credentials could be resolved |
| `MISSING_CONTEXT_USER` | Context user is required for credential lookup |
| `ERROR` | General error (check Message for details) |
Each provider automatically handles platform-specific HTTP errors:
- **401** - Invalid or expired API token
- **403** - Insufficient permissions / scope
- **404** - Form or response not found
- **429** - Rate limit exceeded (automatic retry with exponential backoff)
## Rate Limiting
All providers include built-in rate limit handling with automatic retry:
- Respects `Retry-After` headers when provided
- Falls back to 60-second wait when no header is present
- Adds 100ms delays between pagination requests to avoid hitting limits
- JotForm: Regional endpoints (US, EU, HIPAA) for data residency compliance
## Dependencies
| Package | Purpose |
|---------|---------|
| `@memberjunction/actions` | Action engine and `BaseAction` class |
| `@memberjunction/actions-base` | `ActionParam` types and base interfaces |
| `@memberjunction/core` | `RunView`, `Metadata`, `UserInfo`, logging |
| `@memberjunction/core-entities` | `CompanyIntegrationEntity` for credential lookup |
| `@memberjunction/global` | `@RegisterClass` decorator |
| `axios` | HTTP client for all provider API calls |
| `exceljs` | Excel file parsing in `FileContentProcessor` |
| `mammoth` | Word document text extraction |
| `pdf-parse` | PDF text extraction |
## Related Packages
- [@memberjunction/actions](../../Engine/README.md) - Action execution engine
- [@memberjunction/actions-base](../../Base/README.md) - Base classes and interfaces
- [@memberjunction/core-actions](../../CoreActions/README.md) - Pre-built core actions
- [@memberjunction/actions-bizapps-accounting](../Accounting/README.md) - Accounting integrations
- [@memberjunction/actions-bizapps-lms](../LMS/README.md) - LMS integrations
- [@memberjunction/actions-bizapps-crm](../CRM/README.md) - CRM integrations
## Adding a New Provider
To integrate a new form/survey platform:
1. Create provider directory: `src/providers/{platform}/`
2. Implement a provider base class extending `BaseFormBuilderAction` with:
- `formPlatform` and `integrationName` properties
- Axios instance with authentication and rate limit interceptors
- API client methods for the provider's endpoints
- Response normalization to the shared `FormResponse` interface
- Provider-specific error handling
3. Create individual action classes in `src/providers/{platform}/actions/`
4. Export all classes from `src/providers/{platform}/index.ts`
5. Add the provider export to `src/index.ts`