Lead capture API
Post a lead from your own page into Monody: the endpoint, the body, the answer and the errors.
Your own landing page can post a lead straight into your workspace. This call needs no API key: the form's origin allowlist and its bot check protect it.
The endpoint
POST https://<your-workspace>.monody.app/api/v1/leads The body
JSON. Only formId is required; send what your form asks for.
| Field | Type | What it holds |
|---|---|---|
formId Required | uuid | The form this lead came through. |
firstName, lastName | string | Up to 120 characters each. |
email | string | Stored lower-cased. |
phone | string | As typed. 604-555-0123 is stored as +16045550123; a number outside North America needs its country code. |
consent | object | { marketingEmail, marketingSms }, both booleans. |
child | object | { firstName, age }, a first name required. Lands on the lead's Children. |
age | number | The person's own age, 0 to 120. |
utm | object | The utm_ parameters your page received, as strings. |
tags | string[] | Up to 20, such as a program the parent ticked. |
locationId | uuid | One of your live locations. Without it the lead goes to the form's own location. |
turnstileToken | string | When the form's bot protection is on. |
honeypot | string | A hidden field your page leaves empty. A submission that fills it is answered and dropped. |
| Field | Type | What it holds | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| formId | uuid | The form this lead came through. | formId Required uuid The form this lead came through. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| firstName, lastName | string | Up to 120 characters each. | firstName, lastName string Up to 120 characters each. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| string | Stored lower-cased. | email string Stored lower-cased. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| phone | string | As typed. 604-555-0123 is stored as +16045550123; a number outside North America needs its country code. | phone string As typed. 604-555-0123 is stored as +16045550123; a number outside North America needs its country code. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| consent | object | { marketingEmail, marketingSms }, both booleans. | consent object { marketingEmail, marketingSms }, both booleans. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| child | object | { firstName, age }, a first name required. Lands on the lead's Children. | child object { firstName, age }, a first name required. Lands on the lead's Children. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| age | number | The person's own age, 0 to 120. | age number The person's own age, 0 to 120. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| utm | object | The utm_ parameters your page received, as strings. | utm object The utm_ parameters your page received, as strings. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tags | string[] | Up to 20, such as a program the parent ticked. | tags string[] Up to 20, such as a program the parent ticked. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| locationId | uuid | One of your live locations. Without it the lead goes to the form's own location. | locationId uuid One of your live locations. Without it the lead goes to the form's own location. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| turnstileToken | string | When the form's bot protection is on. | turnstileToken string When the form's bot protection is on. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| honeypot | string | A hidden field your page leaves empty. A submission that fills it is answered and dropped. | honeypot string A hidden field your page leaves empty. A submission that fills it is answered and dropped. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
await fetch("https://summit.monody.app/api/v1/leads", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
formId: "3f6c2a1e-8b4d-4c1a-9f0e-2d7b5a9c1e44",
firstName: "Leila",
lastName: "Farouk",
email: "leila.f@example.com",
phone: "604-555-0123",
child: { firstName: "Omar", age: 9 },
consent: { marketingEmail: true, marketingSms: false },
}),
});The answer
201 Created with the lead's id. The lead lands in New, assigned to a rep with the call clock started, and when messaging is set up the workspace's instant reply goes out on the channels the person agreed to.
{
"ok": true,
"leadId": "b7e1c0d2-5a3f-4e8b-9c61-0f2d4a7e9b13",
"emailVerificationRequired": false
}emailVerificationRequired is true when the form asks the person to confirm their email and an email was sent. A lead posted from your own page lands with the source Landing page; one from Monody's hosted page as Web form, so Insights separates the two.
The same person again, when the form checks for duplicates, is not a second lead. The same email, or the same phone under the same first name, answers 200 with the lead they already have, and the enquiry is added to that lead's timeline:
{ "ok": true, "leadId": "b7e1c0d2-5a3f-4e8b-9c61-0f2d4a7e9b13", "existing": true }Errors
Every refusal has one shape. The message is written for the person filling in the form: show it beside the field it names.
{ "error": { "code": "validation_failed", "field": "email",
"message": "That doesn't look like an email address. Check it for typos." } }| Code | Status | When |
|---|---|---|
validation_failed | 400 | A field is wrong; field names it. Also an unknown locationId, and, when the form checks them, a disposable address or an email domain that does not exist. |
invalid_json | 400 | The body could not be read as JSON. |
bot_check_failed | 400 | The form's bot check did not pass. |
origin_not_allowed | 403 | Your site is not on the form's allowlist. |
form_not_found | 404 | No form with that id. |
tenant_not_found | 404 | No workspace at that address. |
rate_limited | 429 | More than ten submissions in an hour from one address. |
server_error | 500 | The lead could not be saved; try again. |
tenant_inactive | 503 | The workspace is not taking submissions. |
| Code | Status | When | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| validation_failed | 400 | A field is wrong; field names it. Also an unknown locationId, and, when the form checks them, a disposable address or an email domain that does not exist. | validation_failed 400 A field is wrong; field names it. Also an unknown locationId, and, when the form checks them, a disposable address or an email domain that does not exist. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| invalid_json | 400 | The body could not be read as JSON. | invalid_json 400 The body could not be read as JSON. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bot_check_failed | 400 | The form's bot check did not pass. | bot_check_failed 400 The form's bot check did not pass. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| origin_not_allowed | 403 | Your site is not on the form's allowlist. | origin_not_allowed 403 Your site is not on the form's allowlist. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| form_not_found | 404 | No form with that id. | form_not_found 404 No form with that id. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tenant_not_found | 404 | No workspace at that address. | tenant_not_found 404 No workspace at that address. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| rate_limited | 429 | More than ten submissions in an hour from one address. | rate_limited 429 More than ten submissions in an hour from one address. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| server_error | 500 | The lead could not be saved; try again. | server_error 500 The lead could not be saved; try again. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tenant_inactive | 503 | The workspace is not taking submissions. | tenant_inactive 503 The workspace is not taking submissions. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Read the form first
The form's own details are public: the offer, the centre's name, street and phone, the fields, and the consent wording with its version. Build your page from them so its words never drift from what Monody records.
GET https://<your-workspace>.monody.app/api/v1/lead-forms/<form id> The answer also names the endpoint to post to, whether the bot check is on and its site key, and the form's success message. It needs no key.
Webhooks
A lead posted to this endpoint does not send a webhook today: lead.created goes out for a lead created with an API key (see API keys and scopes). Reading leads back also needs a key, with the leads:read scope.
A question these pages do not answer? Write to support@monody.app.