ff_token cookie, or the same JWT sent as Authorization: Bearer <token>. Integrations belong to a form, and each form has at most one integration of each type.
Integration configs are encrypted at rest with AES-256-GCM. Secrets (apiKey, webhook secret, Google refreshToken) are masked as •••••••• whenever a config is returned.
List a form’s integrations
Create an integration
EMAIL, WEBHOOK, GOOGLE_SHEETS. Posting a type the form already has replaces that integration’s config. Returns 201 with { "integration": { "id", "type", "name", "enabled", "createdAt" } }.
EMAIL config
WEBHOOK config
GOOGLE_SHEETS config
Google Sheets is connected through Google OAuth from the form’s Integrations tab, not with this endpoint. See Google Sheets.Get an integration
{ "integration": { "_id", "type", "name", "enabled", "config", "createdAt", "updatedAt" } } with secrets masked.
Update an integration
name, enabled, config.
config replaces the stored config, so send the complete object. To keep an existing webhook secret without resending it, pass "secret": "__KEEP_EXISTING__".
Delete an integration
{ "success": true }.
Test an integration
Test User, test@example.com, and a few more fields). No submission is created.
400 with { "error": "Test failed", "message": "<provider error>" }.
Integration status
type, name, and enabled. For Google Sheets it also returns connectedEmail, spreadsheetId, spreadsheetUrl, and sheetName.
Per-submission success or failure is in the integration logs.