Send website inquiries to CRM
Request and response reference for submitting website forms.
Send website inquiries to CRM
This is the reference for the developer wiring a website form into a Workspace
CRM inbox. A submission creates an Inquiry that Owners and Managers review,
respond to, and manage in Backoffice.
Before using this, someone in the Workspace needs to create a key and set up
inquiry types.
See Connect your website to CRM for that.
Base path:
https://backoffice.mindfulltechnologies.com
Submission modes
The endpoint accepts two credential modes. Use exactly one per request.
1. Browser form submissions
Send the public formKey in the JSON body. This is safe in website JavaScript
because the backend validates the form key, the Workspace CRM capability, and the
allowed origins configured for that key. Send the browser Origin header.
POST /api/public/v1/inquiries
Content-Type: application/json
Origin: https://hotel.example.com
X-External-Submission-Id: contact-form-2026-06-26-001
{
"formKey": "mf_form_public_abc123",
"typeSlug": "general",
"contact": {
"name": "Anu Mathew",
"email": "anu@example.com",
"phone": "+91 99999 99999"
},
"subject": "Question about rooms",
"message": "Do you have parking available?",
"additionalDetails": {
"page": "/contact",
"campaign": "monsoon-offer"
}
}
2. Server-to-server submissions
Send the secret API key as a bearer token and omit formKey from the body. Do
not expose this key in browser JavaScript.
POST /api/public/v1/inquiries
Content-Type: application/json
Authorization: Bearer mf_live_secret_value
X-External-Submission-Id: room-form-2026-08-12-anu
{
"typeSlug": "rooms",
"contact": {
"name": "Anu Mathew",
"email": "anu@example.com",
"phone": "+91 99999 99999"
},
"message": "We would like two rooms if available.",
"startsAt": "2026-08-12",
"endsAt": "2026-08-15",
"arrivalTime": "18:30",
"guestCount": 4,
"additionalDetails": {
"sourceSystem": "wordpress",
"roomType": "Deluxe"
}
}
Request fields
| Field | Required | Notes |
|---|---|---|
formKey | Browser form submissions only | Required when no bearer API key is sent. Omit for server-to-server submissions. |
typeId | Optional | Inquiry type id. Omit to use the Workspace default type. |
typeSlug | Optional | Inquiry type slug, such as rooms or events. Omit to use the default. |
contact.name | Optional | Customer or guest name. Up to 120 characters. |
contact.email | Required unless phone is present | Must be a valid email if provided, up to 254 characters. Used for matching. |
contact.phone | Required unless email is present | Stored on the CRM contact. Up to 32 characters, spaces and brackets included. |
contact.company | Optional | Useful for business accounts. Up to 120 characters. |
subject | Optional | Short summary shown in the inquiry list. Up to 500 characters. |
message | Optional | Customer message. Up to 4000 characters. |
startsAt | Optional | ISO date such as 2026-08-12. |
endsAt | Optional | ISO date such as 2026-08-15. |
arrivalTime | Optional | HH:mm format, 24 hour. |
guestCount | Optional | Positive integer. |
additionalDetails | Optional | Object of custom form fields, up to 8 KB as JSON. Do not send secrets. |
A value over its limit is refused with 400 and VALIDATION_FAILED, and the
message names the field and its limit, such as Name must be 120 characters or fewer. Nothing is filed, so correct the value and send it again.
Inquiry type slugs come from Settings then Inquiry types in Backoffice. A
submission that sends neither typeId nor typeSlug is filed under the
Workspace default type, General.
Retries and email notification matching
Send the X-External-Submission-Id header so a retry does not create a duplicate
inquiry for the same Workspace:
X-External-Submission-Id: canoeville-contact-a1b2c3d4e5f678901234567890abcdef
Use a unique, stable value per submission.
If your website also sends a notification email about the same submission to a
connected CRM email channel, put an X-External-Submission-Id header with the
identical value on that email. The email then attaches to the inquiry this API
created instead of arriving as a second, separate inquiry.
Responses
A new inquiry returns 201.
{
"inquiry": {
"id": "inquiry_123",
"workspaceId": "workspace_123",
"type": {
"id": "type_rooms",
"name": "Rooms",
"slug": "rooms",
"isDefault": false
},
"status": "NEW",
"source": "WEBSITE_FORM",
"sourceName": "Hotel website",
"subject": null,
"message": "We would like two rooms if available.",
"preferredStartAt": "2026-08-12T00:00:00.000Z",
"preferredEndAt": "2026-08-15T00:00:00.000Z",
"arrivalTime": "18:30",
"guestCount": 4,
"receivedAt": "2026-06-26T10:00:00.000Z",
"updatedAt": "2026-06-26T10:00:00.000Z",
"contact": {
"id": "contact_123",
"name": "Anu Mathew",
"email": "anu@example.com",
"phone": "+91 99999 99999"
},
"assignedMember": null
}
}
A retry with an already-used X-External-Submission-Id returns 200 with
idempotent: true. Treat it as success.
{
"inquiry": {
"id": "inquiry_123"
},
"idempotent": true
}
If the notification email reached CRM before this request did, the existing email
inquiry is filled in with the form's details rather than duplicated. That returns
200 with upgraded: true. Treat it as success too.
{
"inquiry": {
"id": "inquiry_123"
},
"upgraded": true
}
Errors
Errors return a JSON object with an error holding a code and a message. The
message is safe to log; do not show it to your website visitors, since it
describes the integration rather than what they did.
{
"error": {
"code": "VALIDATION_FAILED",
"message": "Contact email is invalid"
}
}
| Status | Meaning |
|---|---|
400 | A field is missing or malformed. |
401 | The form key or API key is missing, unknown, or disabled. |
403 | The Origin is not on the key's allowed list, or CRM is off for the Workspace. |
429 | Too many submissions. Wait before retrying; see the limits below. |
500 | Unexpected server error. Safe to retry with the same submission id. |
Limits
The intake accepts up to 10 submissions a minute from one address per
Workspace, and up to 1,000 a day for the Workspace as a whole. A submission over
either limit is answered 429 with the code INQUIRY_RATE_LIMITED and is not
stored, so a client that retries should wait at least a minute before sending
the same submission id again. A real website form does not reach these; a
script that does is the reason they exist.
Versioning
This endpoint is versioned under /v1. New fields may appear in responses, so
ignore fields you do not recognise. Breaking changes will use a new version path.