Submit a form
Submit a form on a lead's behalf, exactly like a visitor would. The contact is matched by email, then phone, or created (lead source form). The submission fires the form's "form submitted" workflows, which can send messages and place AI calls that cost money, and sends the form's notification email if one is set.
POST
https://api.centerfy.ai/webhooks/inbound/forms/:id/submit Headers
| Header | Required | Value | Description |
|---|---|---|---|
x-api-key | Yes | your sub-account API key (cfy_…) | Authenticates the request; or use Authorization: Bearer <key>. |
X-Webhook-Source | No | my-crm | Your system’s label. A contact created here is stamped with it so outbound webhooks with the same source don’t echo the change back to you. |
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string (uuid) | Yes | The form id. |
Body parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
data | object | Yes | — | Answers keyed by field label (field ids also work). Required fields must be present and non-empty. Consent fields take true, under the field’s label or “GDPR Consent” / “A2P-10DLC Consent”. |
contact | object | No | — | Overrides the contact details read from the form’s typed fields. |
contact.email | string | No | — | Matched case-insensitively. |
contact.phone | string | No | — | E.164, e.g. +14155550123. Other formats are ignored. |
contact.name | string | No | — | Full name for a newly created contact. |
contact.first_name | string | No | — | First name for a newly created contact. |
contact.last_name | string | No | — | Last name for a newly created contact. |
Request body
{
"data": {
"Full name": "Jane Doe",
"Email": "[email protected]",
"Phone": "+14155550123"
}
}Example
curl -X POST "https://api.centerfy.ai/webhooks/inbound/forms/{id}/submit" \
-H "x-api-key: $CENTERFY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"data": {
"Full name": "Jane Doe",
"Email": "[email protected]",
"Phone": "+14155550123"
}
}' import { CenterfyClient } from "@centerfy/sdk";
const centerfy = new CenterfyClient({ apiKey: process.env.CENTERFY_API_KEY! });
const result = await centerfy.forms.submit("<formId>", {
"data": {
"Full name": "Jane Doe",
"Email": "[email protected]",
"Phone": "+14155550123"
}
});
console.log(result); centerfy forms submit <formId> '{"data":{"Full name":"Jane Doe","Email":"[email protected]","Phone":"+14155550123"}}' MCP tool: centerfy_forms_submit (see MCP server)
Response
{
"status": "success",
"submission": {
"id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"form_id": "2b3c4d5e-6f7a-4b8c-9d0e-1f2a3b4c5d6e",
"contact_id": "3f1c2a9e-7b4d-4c8a-9e2f-1a2b3c4d5e6f",
"data": {
"Full name": "Jane Doe",
"Email": "[email protected]",
"Phone": "+14155550123"
},
"metadata": {
"ip": "",
"userAgent": "curl/8.0",
"referrerUrl": "",
"pageUrl": "",
"submittedAt": "2026-10-05T15:00:00.000Z",
"via": "public_api"
},
"status": "complete",
"source": "platform",
"created_at": "2026-10-05T15:00:00.000Z"
},
"contact_id": "3f1c2a9e-7b4d-4c8a-9e2f-1a2b3c4d5e6f",
"contact_created": true,
"notification_sent": false
}Errors
400— if data is not an object, or with missing_fields when required fields are missing.404— if the form is not in this sub-account.409— if the form is not active, or is a survey (surveys can’t be submitted through this endpoint).
Notes
Returns 201. An existing contact is not updated. With no email or phone the submission is saved without a contact (contact_id null). Answers to fields linked to custom fields are saved on the contact.