GoliathDevelopersGet an API key
Guides / Forms API

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.