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.
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"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.
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:
⚠️
lineis required onacord-catalog. Without it you get422 validation_error, and an unrecognised value is a400 invalid_request. Usepersonalorcommercial.
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:
⚠️
stepsreplaces, 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
Forms API — the full field, error and rate-limit reference
Embed the form builder — let your users build visually instead
