Menu

API reference · Web Forms

Forms

web_forms · forms · 12 operations

GET/v1/web-forms

List web forms

web_forms.forms.list

ReadIdempotentSince 2026-11-01

Lists the organization's web forms (HTML forms embedded on websites that email every submission and can feed the CRM), newest first, paginated by cursor (limit up to 200). enabled=true returns only published forms (accepting submissions), false only unpublished ones; the page is completed up to limit (reading up to 500 forms per call). Each row carries the notification recipients, allowed origins, anti-spam settings, CRM link, counters, created_by_name, field_count and auto_responder_enabled; the fields and the auto-responder come with web_forms.forms.get. Use web_forms.submissions.list for the responses. Requires view access to Web Forms.

Scope
web_forms:read
Permission
View the module
MCP tool
web_forms_forms_listOn the full surface; on the compact one, mb_execute_operation with id: "web_forms.forms.list".

Declared cost: up to 701 reads and 0 writes per call (they count against the daily quota).

Parameters

limitintegerqueryoptional

How many items this page brings at most (see the maximum and the default in the schema).

Default: 50

cursorstringqueryoptional

The next_cursor of the previous page, exactly as it came (only for the same list and filters); without it, the first page.

enabledbooleanqueryoptional

true = only published forms; false = only unpublished ones. Filtered on the page read.

Response

dataarray<object>
has_moreboolean
next_cursorstring

Errors

Example

curl "https://api.mb-suite.com/v1/web-forms" \
  -H "Authorization: Bearer $MB_API_KEY"

Try it with your test API key

With a test API key the request always goes to your practice organization: example data you can reset.

curl "https://api.mb-suite.com/v1/web-forms" \
  -H "Authorization: Bearer $MB_TEST_KEY"
GET/v1/web-forms/{form_id}

Get one web form

web_forms.forms.get

ReadIdempotentSince 2026-11-01

Returns one web form: its fields (name, label, type, required, crm_template), the notification email (from_name, to_emails, subject, conditional recipient_routing), allowed_origins, the anti-spam settings (honeypot_field_name, recaptcha_enabled, rate_limits), thank_you_url, the auto-responder, the CRM link (linked_source_id), its counters (stats) and who created it (created_by_id, created_by_name). A visitor's page submits with POST https://mb-suite.com/api/v1/public/forms/submit?formId=<id> and a JSON body {"formId": <id>, "<field name>": <value>, …}, sent from one of allowed_origins. Not found (web_form_not_found) if the id does not exist in this organization. Requires view access to Web Forms.

Scope
web_forms:read
Permission
View the module
MCP tool
web_forms_forms_getOn the full surface; on the compact one, mb_execute_operation with id: "web_forms.forms.get".

Declared cost: up to 2 reads and 0 writes per call (they count against the daily quota).

Parameters

form_idstringpathrequired

The form's id (from web_forms.forms.list, or the form_id that web_forms.forms.create returns).

Response

idstring
org_idstring
namestring
enabledboolean
fieldsarray<object>
honeypot_field_namestring
from_namestring
to_emailsarray<string>
subjectstring
allowed_originsarray<string>
rate_limitsobject
statsobject
created_atnumber
updated_atnumber
descriptionstring
ordernumber
from_emailstring
sender_idstring | null
reply_to_fieldstring | null
recipient_routingobject | null
turnstile_enabledboolean
turnstile_site_keystring | null
recaptcha_enabledboolean
recaptcha_min_scorenumber
thank_you_urlstring
email_brandingstring
crm_enabledboolean
linked_source_idstring | null
auto_responderobject | null
created_by_idstring
created_by_namestring | null

Display name of created_by_id (null if it is not a person of the organization).

Errors

Example

curl "https://api.mb-suite.com/v1/web-forms/{form_id}" \
  -H "Authorization: Bearer $MB_API_KEY"

Try it with your test API key

With a test API key the request always goes to your practice organization: example data you can reset.

curl "https://api.mb-suite.com/v1/web-forms/{form_id}" \
  -H "Authorization: Bearer $MB_TEST_KEY"
POST/v1/web-forms

Create a web form

web_forms.forms.create

WriteSince 2026-11-01

Creates a web form: name, 1 to 40 fields (name, label, type text|email|phone|textarea|number, required), the internal recipients of the notification email (to_emails, up to 10), its sender name and subject ({{field_name}} placeholders allowed), the origins allowed to submit (allowed_origins, scheme://host without path) and optionally a thank-you URL, conditional routing of recipients, an auto-responder (needs a verified Branding domain and sender), rate limits and CRM availability. It is created unpublished by default (enabled=false): publish it with web_forms.forms.publish (or create it with enabled=true) and from then on it accepts submissions at POST https://mb-suite.com/api/v1/public/forms/submit and emails each one to to_emails. reCAPTCHA is off by default through the API; turn it on only if the page sends a g-recaptcha-response token. The CRM link is made from the CRM (a lead source of type MB Suite Form) or with web_forms.forms.link_crm, which can also create that source (create_source). Errors: branding_not_verified / sender_not_found (a sender_id or auto-responder sender not usable), routing_field_reserved. Returns the new form_id and the form. Requires edit access to Web Forms.

Instructions for agents

Tell the person the form_id, whether it is published (enabled) and how the page submits: POST https://mb-suite.com/api/v1/public/forms/submit?formId=<form_id> with {"formId", one key per field name}, from one of the allowed origins. Never invent recipients: use the ones the person gave.

Scope
web_forms:write
Permission
Edit in the module
MCP tool
web_forms_forms_createOn the full surface; on the compact one, mb_execute_operation with id: "web_forms.forms.create".
Events it can emit
web_forms.form.published

Declared cost: up to 0 reads and 1 writes per call (they count against the daily quota).

Parameters

namestringbodyrequired

Internal name of the form (visitors never see it).

fieldsarray<object>bodyrequired

The form's fields (1 to 40), in the order the page shows them.

from_namestringbodyrequired

Sender display name of the notification email.

to_emailsarray<string (email)>bodyrequired

Internal recipients of the notification email sent for every submission.

subjectstringbodyrequired

Subject of the notification email; {{field_name}} is replaced by the submitted value.

allowed_originsarray<string (uri)>bodyrequired

Origins allowed to submit (scheme://host[:port], no path), e.g. https://www.example.com.

descriptionstringbodyoptional

Internal description.

enabledbooleanbodyoptional

false (default) = draft; true = published (it accepts submissions right away and each one emails to_emails). Publish later with web_forms.forms.publish.

Default: false

honeypot_field_namestringbodyoptional

Name of the hidden anti-bot field; the page must render it empty and hidden. Keep the default unless the page already uses it.

Default: "_qzt_field"

sender_idstring | nullbodyoptional

Id of a Branding sender (verified email domain) for the notification email; null = form@mb-suite.com.

Default: null

reply_to_fieldstring | nullbodyoptional

Name of the email field whose value becomes the Reply-To of the notification.

Default: null

recipient_routingobject | nullbodyoptional

Conditional recipients: a field of the form picks who gets the notification; null or absent = always to_emails.

turnstile_enabledbooleanbodyoptional

Cloudflare Turnstile gate (needs turnstile_site_key and the widget on the page).

Default: false

turnstile_site_keystring | nullbodyoptional

The Turnstile site key of the page.

Default: null

recaptcha_enabledbooleanbodyoptional

reCAPTCHA Enterprise gate. Turn it on only if the page sends a g-recaptcha-response token (the snippet from the screen does): with it on, submissions without a token are silently dropped.

Default: false

recaptcha_min_scorenumberbodyoptional

Minimum reCAPTCHA score (0 to 1) to accept a submission; without it, the platform default.

thank_you_urlstringbodyoptional

Absolute http(s) URL the visitor is sent to after submitting; without it the page shows its inline success message.

rate_limitsobjectbodyoptional

Anti-abuse limits. Default: per_ip_per_minute 5, per_ip_per_hour 50, per_form_per_day 1000.

auto_responderobject | nullbodyoptional

The auto-reply to the visitor; null removes it.

Default: null

email_brandingenumbodyoptional

mb_suite (default), org or none: the frame of the notification email.

Values: mb_suite, org, none · Default: "mb_suite"

crm_enabledbooleanbodyoptional

Makes the form available as a CRM lead source (the link itself is made from the CRM or with web_forms.forms.link_crm).

Default: false

Response

form_idstring
formobject

Errors

Example

curl -X POST "https://api.mb-suite.com/v1/web-forms" \
  -H "Authorization: Bearer $MB_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "name": "NAME",
  "fields": [],
  "from_name": "FROM_NAME",
  "to_emails": [
    "ana@example.com"
  ],
  "subject": "SUBJECT",
  "allowed_origins": [
    "ALLOWED_ORIGIN"
  ]
}'

Try it with your test API key

With a test API key the request always goes to your practice organization: example data you can reset.

curl -X POST "https://api.mb-suite.com/v1/web-forms" \
  -H "Authorization: Bearer $MB_TEST_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "name": "NAME",
  "fields": [],
  "from_name": "FROM_NAME",
  "to_emails": [
    "ana@example.com"
  ],
  "subject": "SUBJECT",
  "allowed_origins": [
    "ALLOWED_ORIGIN"
  ]
}'
PATCH/v1/web-forms/{form_id}

Update a web form

web_forms.forms.update

WriteSince 2026-11-01

Updates one or more settings of a web form (updates object): name, description, enabled, fields, honeypot_field_name, from_name, sender_id, to_emails, subject, reply_to_field, recipient_routing (null removes it), allowed_origins, recaptcha_enabled, recaptcha_min_score (null = global default), thank_you_url (null removes it), rate_limits, auto_responder, email_branding, crm_enabled. Only what is sent changes; fields replaces the whole list. crm_enabled cannot be turned off while the form is linked to a CRM lead source (crm_enabled_required_while_linked: unlink it first with web_forms.forms.unlink_crm). To only publish or unpublish prefer web_forms.forms.publish / unpublish. Returns the updated form. Requires edit access to Web Forms.

Scope
web_forms:write
Permission
Edit in the module
MCP tool
web_forms_forms_updateOn the full surface; on the compact one, mb_execute_operation with id: "web_forms.forms.update".
Events it can emit
web_forms.form.published

Declared cost: up to 2 reads and 1 writes per call (they count against the daily quota).

Parameters

form_idstringpathrequired

The form's id (from web_forms.forms.list, or the form_id that web_forms.forms.create returns).

updatesobjectbodyrequired

The fields to change.

Response

formobject

Errors

Example

curl -X PATCH "https://api.mb-suite.com/v1/web-forms/{form_id}" \
  -H "Authorization: Bearer $MB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "updates": {}
}'

Try it with your test API key

With a test API key the request always goes to your practice organization: example data you can reset.

curl -X PATCH "https://api.mb-suite.com/v1/web-forms/{form_id}" \
  -H "Authorization: Bearer $MB_TEST_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "updates": {}
}'
POST/v1/web-forms/{form_id}/publish

Publish a web form

web_forms.forms.publish

WriteIdempotentSince 2026-11-01

Publishes a web form (enabled=true): from now on the public endpoint accepts its submissions, emails them and, when linked, sends them to the CRM. Idempotent: on a form that is already published nothing changes (changed=false). Returns the form. Requires edit access to Web Forms.

Scope
web_forms:write
Permission
Edit in the module
MCP tool
web_forms_forms_publishOn the full surface; on the compact one, mb_execute_operation with id: "web_forms.forms.publish".
Events it can emit
web_forms.form.published

Declared cost: up to 2 reads and 1 writes per call (they count against the daily quota).

Parameters

form_idstringpathrequired

The form's id (from web_forms.forms.list, or the form_id that web_forms.forms.create returns).

Response

formobject
changedboolean

Errors

Example

curl -X POST "https://api.mb-suite.com/v1/web-forms/{form_id}/publish" \
  -H "Authorization: Bearer $MB_API_KEY"

Try it with your test API key

With a test API key the request always goes to your practice organization: example data you can reset.

curl -X POST "https://api.mb-suite.com/v1/web-forms/{form_id}/publish" \
  -H "Authorization: Bearer $MB_TEST_KEY"
POST/v1/web-forms/{form_id}/unpublish

Unpublish a web form

web_forms.forms.unpublish

WriteIdempotentSince 2026-11-01

Unpublishes a web form (enabled=false): the public endpoint answers its submissions with a generic 404 and stores nothing, while the form, its responses and its CRM link are kept. Use it to pause a form instead of deleting it. Idempotent: on a form that is already unpublished nothing changes (changed=false). Requires edit access to Web Forms.

Scope
web_forms:write
Permission
Edit in the module
MCP tool
web_forms_forms_unpublishOn the full surface; on the compact one, mb_execute_operation with id: "web_forms.forms.unpublish".

Declared cost: up to 2 reads and 1 writes per call (they count against the daily quota).

Parameters

form_idstringpathrequired

The form's id (from web_forms.forms.list, or the form_id that web_forms.forms.create returns).

Response

formobject
changedboolean

Errors

Example

curl -X POST "https://api.mb-suite.com/v1/web-forms/{form_id}/unpublish" \
  -H "Authorization: Bearer $MB_API_KEY"

Try it with your test API key

With a test API key the request always goes to your practice organization: example data you can reset.

curl -X POST "https://api.mb-suite.com/v1/web-forms/{form_id}/unpublish" \
  -H "Authorization: Bearer $MB_TEST_KEY"
POST/v1/web-forms/{form_id}/duplicate

Duplicate a web form

web_forms.forms.duplicate

WriteSince 2026-11-01

Copies a web form with all its settings into a new one named '<name> (copy)' (the suffix in the caller's language: '(copia)', '(copy)' or '(cópia)'), unpublished, without the CRM link (a lead source points to a single form) and with its counters at zero; responses are not copied. Returns the new form_id and the form. Requires edit access to Web Forms.

Scope
web_forms:write
Permission
Edit in the module
MCP tool
web_forms_forms_duplicateOn the full surface; on the compact one, mb_execute_operation with id: "web_forms.forms.duplicate".

Declared cost: up to 1 reads and 1 writes per call (they count against the daily quota).

Parameters

form_idstringpathrequired

The form's id (from web_forms.forms.list, or the form_id that web_forms.forms.create returns).

Response

form_idstring
formobject

Errors

Example

curl -X POST "https://api.mb-suite.com/v1/web-forms/{form_id}/duplicate" \
  -H "Authorization: Bearer $MB_API_KEY"

Try it with your test API key

With a test API key the request always goes to your practice organization: example data you can reset.

curl -X POST "https://api.mb-suite.com/v1/web-forms/{form_id}/duplicate" \
  -H "Authorization: Bearer $MB_TEST_KEY"
DELETE/v1/web-forms/{form_id}

Delete a web form and its responses

web_forms.forms.delete

IrreversibleSince 2026-11-01

Deletes a web form together with ALL its responses, and removes its link with the CRM lead source (the source stays active; leads already created stay in the CRM). The form's configuration goes to the 30-day trash and owners and admins can restore it (it comes back unlinked from the CRM and with its counters at zero); the responses are deleted for good and do not come back: export them first with web_forms.submissions.list if they are needed. To pause a form use web_forms.forms.unpublish instead. Outside the screen it always requires a dry run (which says how many responses will be lost) and a confirm_token. Requires edit access to Web Forms.

Needs confirmation: first dry_run: true (preview and confirm_token, valid 10 minutes and for one run), then the same body with confirm_token. In a few operations only with certain data: the description says when. Why and how: Dry run and confirm_token.

Scope
web_forms:write and “Allow deletions” (web_forms:delete in OAuth)
Permission
Edit in the module
MCP tool
web_forms_forms_deleteOn the full surface; on the compact one, mb_execute_operation with id: "web_forms.forms.delete".

Declared cost: up to 503 reads and 503 writes per call (they count against the daily quota).

Parameters

form_idstringpathrequired

The form's id (from web_forms.forms.list, or the form_id that web_forms.forms.create returns).

Response

oktrue
submissions_deletednumber
trash_item_idstring

The trash item this deletion left: the item_id of platform.trash.restore (POST /trash/{item_id}/restore). Missing if nothing went to the trash.

trash_item_idsarray<string>

Only when the deletion left more than one copy: all of them, the first being trash_item_id.

trash_expires_atnumber

When the copy leaves the trash for good (epoch ms): 30 days after the deletion.

Errors

Example

curl -X DELETE "https://api.mb-suite.com/v1/web-forms/{form_id}" \
  -H "Authorization: Bearer $MB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "dry_run": true
}'

Try it with your test API key

With a test API key the request always goes to your practice organization: example data you can reset.

curl -X DELETE "https://api.mb-suite.com/v1/web-forms/{form_id}" \
  -H "Authorization: Bearer $MB_TEST_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "dry_run": true
}'
POST/v1/web-forms/{form_id}/test-notification

Send a test notification email

web_forms.forms.send_test_notification

WriteSince 2026-11-01

Sends the form's notification email with dummy values to its configured recipients (to_emails, or the routing defaults), through Amazon SES, so the recipients and the look of the email can be checked. Stores nothing. Returns sent=false with the error (for example, no valid recipients) instead of failing; the attempt is audited either way. At most one test per form and person every 30 seconds (cooldown_active, 429). Sends a real email: run it only when the person asks to test the notification. Outside the app it always runs in two steps: dry_run=true says to whom it goes (and, for a test submission, whether it creates a CRM lead and where the auto-reply goes) with the same checks and a confirm_token (10 minutes); then call it again with the confirm_token. Requires edit access to Web Forms.

Needs confirmation: first dry_run: true (preview and confirm_token, valid 10 minutes and for one run), then the same body with confirm_token. In a few operations only with certain data: the description says when. Why and how: Dry run and confirm_token.

Scope
web_forms:write
Permission
Edit in the module
MCP tool
web_forms_forms_send_test_notificationOn the full surface; on the compact one, mb_execute_operation with id: "web_forms.forms.send_test_notification".

Declared cost: up to 1 reads and 0 writes per call (they count against the daily quota). Touches an external platform (ses): it counts against the external operations quota.

Parameters

form_idstringpathrequired

The form's id (from web_forms.forms.list, or the form_id that web_forms.forms.create returns).

Response

sentboolean
message_idstring | null
errorstring | null
recipientsarray<string>

Errors

Example

curl -X POST "https://api.mb-suite.com/v1/web-forms/{form_id}/test-notification" \
  -H "Authorization: Bearer $MB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "dry_run": true
}'

Try it with your test API key

With a test API key the request always goes to your practice organization: example data you can reset. In the practice organization this operation is not simulated (it reaches another platform): it answers 409 sandbox_external_unavailable.

POST/v1/web-forms/{form_id}/test-submit

Send a test submission through the whole pipeline

web_forms.forms.test_submit

WriteSince 2026-11-01

Runs a test submission of the form with the given values ({name, value} pairs, one per field) through the real pipeline, skipping only the origin, rate-limit, honeypot and CAPTCHA checks: it validates the values against the form's fields (validation_failed with details.fields), stores a response marked as a test (origin __test__), sends the notification email to the form's recipients through Amazon SES, creates a REAL lead in the CRM when the form is linked to a lead source, and sends the auto-responder (if on) to the caller's own email with a [TEST] subject prefix. It does not fire web_forms.submission.received. At most one test per form and person every 30 seconds (cooldown_active). Returns what happened at each step. Run it only when the person asks to test the form. Outside the app it always runs in two steps: dry_run=true says to whom it goes (and, for a test submission, whether it creates a CRM lead and where the auto-reply goes) with the same checks and a confirm_token (10 minutes); then call it again with the confirm_token. Requires edit access to Web Forms.

Instructions for agents

Tell the person the test response is stored marked as a test and, when lead_created is true, that a real lead was created in the CRM (they may want to delete it).

Needs confirmation: first dry_run: true (preview and confirm_token, valid 10 minutes and for one run), then the same body with confirm_token. In a few operations only with certain data: the description says when. Why and how: Dry run and confirm_token.

Scope
web_forms:write
Permission
Edit in the module
MCP tool
web_forms_forms_test_submitOn the full surface; on the compact one, mb_execute_operation with id: "web_forms.forms.test_submit".

Declared cost: up to 3 reads and 4 writes per call (they count against the daily quota). Touches an external platform (ses): it counts against the external operations quota.

Parameters

form_idstringpathrequired

The form's id (from web_forms.forms.list, or the form_id that web_forms.forms.create returns).

valuesarray<object>bodyrequired

The submitted values as {name, value} pairs, one per form field, exactly as a visitor's page would send them.

Response

submission_idstring
email_sentboolean
email_errorstring | null
notification_recipientsarray<string>
notification_routed_valuestring | null
auto_responder_sentboolean
auto_responder_errorstring | null
auto_responder_skip_reasonstring | null
auto_responder_recipientstring | null
lead_createdboolean
lead_idstring | null
lead_typeenum | null

Values: deal, prospect

person_idstring | null

Errors

Example

curl -X POST "https://api.mb-suite.com/v1/web-forms/{form_id}/test-submit" \
  -H "Authorization: Bearer $MB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "values": [],
  "dry_run": true
}'

Try it with your test API key

With a test API key the request always goes to your practice organization: example data you can reset. In the practice organization this operation is not simulated (it reaches another platform): it answers 409 sandbox_external_unavailable.