Forms API
Manage lead-capture forms, read submissions, and pull form analytics through the Goliath developer API
About
Programmatic access to lead-capture (embed) forms: create and update forms, read submissions as they come in, and pull the analytics behind the app’s form dashboards — conversion funnels, traffic sources, engagement time, and lead routing.
All operations go through the developer API gateway — one endpoint, called by
operationId. If you haven’t set up a key yet, start with the
Developer API Overview.
Operations
Forms and submissions (READ scope)
| operationId | What it does |
|---|---|
listForms |
List the organization’s lead-capture forms — name, slug, status, lifetime submission count, public URL. |
getForm |
Fetch one form by id — the single-page or multi-step fieldConfig, submitButtonLabel, styleConfig, auto-apply tags/lists, dedupe settings, notification toggles, and assignee pool. Read before replacing fields, pages, or styling. The persisted response does not include the form’s workflow-trigger wiring. |
listFormSubmissions |
List submissions (leads), newest first, optionally filtered to one form. Each row carries submittedData and the contactId it created/matched. |
getFormSubmission |
Fetch one submission by id — its submittedData and linked contactId. |
Form analytics (READ scope)
| operationId | What it does |
|---|---|
getFormAnalytics |
Conversion funnel for one form over a date range — views, starts, submits, conversion rates, median seconds-to-submit, plus a per-day time series. Date ranges over 90 days are rejected. |
getFormTrafficSources |
Where a form’s traffic and submissions come from — breakdowns by traffic source, device class, and location. |
listFormActivity |
Recent per-session activity (cursor-paginated) — session status, submit/abandon times, traffic source, device, matched contact, assigned agent. |
getFormActiveTime |
Daily active-engagement time — session count and active-ms percentiles (p50/p90), split by submitted vs abandoned. |
listFormActivityLocations |
Approximate (~1 km-rounded) geo coordinates of recent sessions, for plotting a lead map. |
getFormRoutingDistribution |
Lead-assignment distribution for a form’s round-robin pool — lead count per assigned user. |
Writing forms (WRITE scope)
| operationId | What it does |
|---|---|
createForm |
Create a form. Requires name and fieldConfig, either a flat field array or a multi-step { fields, steps, showStepProgress } document. Optional submitButtonLabel and styleConfig control rendered copy and appearance. New forms start DRAFT; publish via updateForm(status: ACTIVE). Also configurable: SMS and marketing consent capture (collectSmsConsent, showMarketingConsent and their text fields), legal text and policy links/content (termsAndConditionsText, privacyPolicyUrl/privacyPolicyContent, termsOfServiceUrl/termsOfServiceContent), AI-employee triggers (triggerAgentIds), the lead-routing pool (assignmentConfig), submission notifications (enableEmailNotification, enableSmsNotification, enableSlackNotification with slackChannelId/slackChannelName and slackMessageTemplate), plus source and gtmContainerId. |
updateForm |
Update a form by id (patch semantics). fieldConfig replaces the entire fields-and-steps document, and styleConfig replaces the whole style object; read, merge, and send the full replacement. Activate/deactivate with status (ACTIVE | INACTIVE). Also configurable: SMS and marketing consent capture (collectSmsConsent, showMarketingConsent and their text fields), legal text and policy links/content (termsAndConditionsText, privacyPolicyUrl/privacyPolicyContent, termsOfServiceUrl/termsOfServiceContent), AI-employee triggers (triggerAgentIds), the lead-routing pool (assignmentConfig), submission notifications (enableEmailNotification, enableSmsNotification, enableSlackNotification with slackChannelId/slackChannelName and slackMessageTemplate), plus source and gtmContainerId. |
deleteForm |
Hard-delete a form (irreversible, cascading) — permanently removes ALL its submissions, events, and viewer/lead history. |
createFormContactLink |
A link to an ACTIVE form for one contact, opening prefilled with their details; their answers land on that contact. Use url in an email, shortUrl in a text. |
duplicateForm |
Copy a form, optionally under a new name. The copy carries the form’s fields, styling and settings but none of its submissions or analytics. |
Calendar locations
A calendar field’s calendarConfig.locationType is CUSTOM (the default),
VIDEO_CALL, or PHONE_CALL. CUSTOM uses defaultLocation, such as a
street address or pasted meeting link. VIDEO_CALL uses the join link created
on the host’s calendar: Google Meet, or Microsoft Teams when calendarProvider is
MICROSOFT. PHONE_CALL creates no meeting link: the host calls the number
the booker entered. That mode requires a field with type: "phone",
mapping: "phone", and required: true, or the write is refused.
Always supply the rest of calendarConfig; without it, the calendar field
renders no booking picker.
Routing calendar bookings
Set assignmentConfig to { assignees, teamIds, participantUserIds }.
All three lists are required when supplied and replace the whole pool; omit
the object to preserve it. Read getForm and merge before changing one list.
Each assignee has a same-organization userId; on a calendar form it also
needs calendarConfig: { isFixed, checkExternalCalendars }.
Non-fixed assignees rotate in array order by least recent booking. Fixed assignees always attend; if all are fixed, the first is the host. Calendar forms require a non-empty pool and a calendar configuration for every assignee before activation. Some organizations also require the pinned Google/Microsoft calendar provider to be connected on first activation; connect any refused account in the app and retry. A form without a calendar can activate with an empty pool.
Starting workflows on submission
createForm and updateForm accept input.triggerWorkflowGroupIds, the
complete set of CONTACT workflow group ids to start for each submission’s
contact. Omit the field to leave the wiring untouched; send [] to clear it.
Newly added groups must belong to the organization and be neither archived
nor hidden.
The active version is resolved at submission time, so publishing a new
version does not require rewiring the form. Enrollment follows the workflow’s
enrollment policy; it does not re-evaluate its trigger predicate.
The persisted getForm, createForm, and updateForm responses do not echo
this wiring. Keep the intended full set in your integration when replacing
it. Workflows with no eligible active version are skipped, and an enrollment
failure does not fail the form submission; a successful submission alone
does not confirm a workflow ran.
Example
Pull this month’s conversion funnel for one form:
curl -X POST https://server.goliathdata.com/api/v1/call \
-H "Authorization: Bearer gsk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "operationId": "getFormAnalytics", "variables": { "formId": "FORM_ID", "startDate": "2026-07-01", "endDate": "2026-07-31" } }'
Full variable schemas, response shapes, and worked examples for every operation are available from the discovery endpoint.