For the complete documentation index, see llms.txt. This page is also available as Markdown.

Quickstart C — Build forms via the API

Create, structure and publish a form entirely from your backend.

If your users build their forms inside your product, you have two options: embed InsurGrid's builder, or drive the API yourself and put your own interface on top. This walkthrough is the second.

By the end you'll have created a form, given it questions, and published it — without opening a dashboard. About 20 minutes.

What you'll need

  • An InsurGrid account with Forms access enabled.

  • An API key belonging to an agency admin (where to get one), kept on your server.

No webhook endpoint or tunnel needed here — this is API-only. To collect the submissions as well, do Quickstart B once your form is live.

Environment
Base URL

Staging

https://phoenix-staging.insurgrid.com/api/v1/forms

Production

https://phoenix.insurgrid.com/api/v1/forms

Only the host differs — see Environments.


1. Create a draft

Every form belongs to one of your users. externalUserId says which, it's required, and it decides who can see the form from then on.

const res = await fetch("https://phoenix-staging.insurgrid.com/api/v1/forms", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.INSURGRID_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "Apartment Intake — Acme Agency",
    externalUserId: "user-8841",
    insurance_line: "commercial",
  }),
});

const form = await res.json();
console.log(form.id, form.public_id, form.status);   // "draft"
Python

Forms are always created as drafts — you can't create one already live.

Because we left steps out, the form comes with a working default: a welcome step, a contact info step already collecting name, email and phone, and a confirmation step. That contact step matters more than it looks — see step 2.

Field
Required
Notes

externalUserId

Yes

Whose form this is. Missing → 422 external_user_required.

name

Yes

Up to 255 characters.

slug

No

e.g. home-intake. Generated from name if omitted, with a short random suffix.

insurance_line

No

personal (default) or commercial.

steps

No

The pages and questions. Omit for the default above.

redirect_url

No

Where to send the customer after they submit.

branding

No

Logo and colours.

Starting from something

Rather than an empty form:

⚠️ line is required on acord-catalog. Without it you get 422 validation_error, and an unrecognised value is a 400 invalid_request. Use personal or commercial.

2. Give it questions

A form is a list of steps — the pages someone moves through — each holding questions.

On a step, type, name, segment and position are all required — segment is the one people miss, since it has no default. On a question, type, label and position are required. The full field tables are on the Forms API page.

Step types: welcome, contact_info, custom, confirmation, complete

Question types: short_text, long_text, email, number, date, dropdown, single_choice, multi_choice, file_upload, address, data_table

Send it with PATCH:

⚠️ steps replaces, it doesn't merge. Send the complete list — anything you leave out is deleted.

Don't lose the contact block

That replacement includes the default contact info step you got in step 1 — and with it, the tags that populate the contact object on every submission webhook.

InsurGrid fills contact.name, contact.email and contact.phone from questions carrying a field_mapping, not from a question's type or label. Replace steps without tagging your own questions and every webhook from then on arrives with:

Nothing errors, and the answers are still in answers[] — only contact goes empty, which is easy to miss until something downstream quietly stops working.

So tag them:

Or keep a contact_info step in your own structure and let it do the job.

The shape you send is exactly what GET /forms/{id} returns, so the quickest way to learn it is to build one form the way you want it in the embedded builder, fetch it, and work from that. Forms built in the InsurGrid dashboard won't do — this API can't see them.

externalUserId can't be changed after creation — it isn't a field on the update call. On PATCH, unknown fields are rejected, so a misspelled key is a 422.

3. Publish it

A PUT, not a POST. The form becomes active and its shareUrl — null until now — is filled in with the address people visit to fill it in.

Later, PUT /{id}/archive takes it out of use and PUT /{id}/reactivate brings it back.

⚠️ Publishing replaces the live form in place. Forms aren't versioned, so if you need to keep an older structure, copy it first with POST /forms/{id}/duplicate.

4. Put it in front of people

Your form is live at its shareUrl. Share it and start receiving submissions — that's Quickstart B.

Carrier-connected forms, which log into a carrier to pull declaration pages, can't be created here — the request is rejected with 422 carrier_flow_disabled. Build those in the dashboard.


Next steps