GoodInbound

Forms API

Use these anonymous endpoints to run GoodInbound forms from your own UI. No API key: never put one in visitor-side code.

Get form config#

GET /api/v1/forms/:projectSlug/:formSlug

Returns the form's pages, fields, validation rules, and visibility conditions, plus a loadToken. Send the token back as _token on your first call so Lead Screen can measure how long the form took to fill in.

curl "https://goodinbound.com/api/v1/forms/acme/contact"
{
  "form": {
    "id": "form_x1y2z3",
    "name": "Contact Form",
    "steps": [
      {
        "id": "step_1",
        "sortOrder": 0,
        "fields": [
          { "id": "full_name", "type": "text", "label": "Full Name", "required": true },
          { "id": "email", "type": "email", "label": "Email", "required": true }
        ]
      }
    ]
  },
  "loadToken": "1791102815820.k3J…"
}

Save a step#

POST /api/v1/forms/:projectSlug/:formSlug/:stepIndex

Validate and save one page of a multi-step form. stepIndex is the page's position in the builder (conditional pages don't renumber). The first call returns a session; send it on later calls. Pages can be saved in any order, and each call checks that page's required fields and formats. Saving pages is free; only the submit counts as usage.

Property Type Required Description
fields object Yes Field ID → value. Multi-select values can be arrays.
session string No From the first call. Omit it on the first call.
_token string No The loadToken from the form config, on the first call.
curl -X POST "https://goodinbound.com/api/v1/forms/acme/contact/0" \
  -H "Content-Type: application/json" \
  -d '{ "fields": { "full_name": "Jane Smith", "email": "jane@example.com" } }'
{ "session": "resp_a1b2c3d4" }

To attach a file, send the call as multipart/form-data with the file under its field ID and the other values as plain fields. Uploaded files are private; download them with a project API key at GET /api/v1/forms/:projectSlug/:formSlug/responses/:responseId/files/:valueId.

Submit a form#

POST /api/v1/forms/:projectSlug/:formSlug

Finish the form and get the Lead Screen label back. With a session, it merges any final fields with the saved pages; without one, it submits a one-page form in a single call. Send Accept: application/json. The submit returns 422 when a required field on any page that applies is empty, listing each field and its page. Each submit is one usage.

curl -X POST "https://goodinbound.com/api/v1/forms/acme/contact" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{ "session": "resp_a1b2c3d4", "fields": { "message": "What does annual pricing look like?" } }'
{
  "session": "resp_a1b2c3d4",
  "status": "screened",
  "label": "lead",
  "label_name": "Real lead",
  "reason": "Asks about pricing or plans",
  "confidence": 0.94,
  "action": "allow",
  "checks": {
    "behavior": { "result": "skipped", "summary": "No behavior data for this submission" },
    "network": { "result": "pass", "summary": "Residential, London, GB" },
    "identity": { "result": "pass", "summary": "Work email, active domain" },
    "context": { "result": "pass", "summary": "Asks about pricing or plans" }
  }
}

Missing required field (422)

{ "error": "validation_failed", "fields": { "email": { "message": "Please enter your email", "step": 0 } } }

Native HTML form#

Point a plain browser form at the submit URL. Without an Accept: application/json header, GoodInbound shows a hosted thank-you page or redirects, as configured in the form builder. Fields the builder doesn't define are kept with the response.

<form action="https://goodinbound.com/api/v1/forms/acme/contact" method="post" enctype="multipart/form-data">
  <input type="text" name="full_name" required />
  <input type="email" name="email" required />
  <input type="file" name="resume" />
  <textarea name="message"></textarea>
  <button type="submit">Send</button>
</form>

Use your form's field IDs as the input name values; the form builder and its generated API prompt show the exact IDs. Include enctype="multipart/form-data" when the form has file inputs.

Other visitor endpoints#

Supporting anonymous routes used by public links, widgets, and analytics.

Method Path Description
GET /api/v1/event-types/:projectSlug/:eventSlug Get public event type configuration.
POST /api/v1/t Record an anonymous visitor analytics event.
GET /api/public/resolve/:projectSlug/:slug Resolve a share-link slug to a form or event type.
GET /api/uploads/:key Download a public upload by object key.

Public upload URLs contain unguessable object keys; don't treat them as authorization for sensitive private response files.