Create a contact
create (or match and update) a contact.
POST
https://api.centerfy.ai/webhooks/inbound/contacts Headers
| Header | Required | Value | Description |
|---|---|---|---|
x-api-key | Yes | your sub-account API key (cfy_…) | Authenticates the request; or use Authorization: Bearer <key>. |
Content-Type | Yes | application/json | JSON request body. |
X-Webhook-Source | No | your-system-name | Optional loop-prevention label; the outbound trigger won’t echo this change back to the same source. |
Body parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
name | string | No | — | Full name; if omitted it is derived from first_name + last_name, then email, then phone. |
first_name | string | No | — | Used with last_name to build name when name is not given. |
last_name | string | No | — | Used with first_name to build name when name is not given. |
email | string | No | — | Lowercased on save; at least one of email or a valid phone is required (400 ‘contacts_email_or_phone_required’ otherwise). Also used to match an existing contact. |
phone | string | No | — | Must be E.164 (e.g. +14155550123); non-conforming values are silently dropped (treated as no phone). At least one of email or a valid phone is required. Also used to match an existing contact. |
address | string | No | — | Optional postal/street address. |
source | string | No | — | Lead source label; defaults to ‘inbound_webhook’ when omitted. Only applied when a new contact is created. |
custom_field_values | object | No | — | Custom field values keyed by field key (see GET /webhooks/inbound/custom-fields). Keys must be active fields; values must match the field type (number, boolean, YYYY-MM-DD date, a select option, or a string array for multi-select). Read-only GoHighLevel fields are rejected. |
company_id | string (uuid) | null | No | — | Company to link the contact to; must be in this sub-account. null clears it. |
assigned_user_id | string (uuid) | null | No | — | Assign the contact to an active member of this sub-account. null unassigns. |
status | string | No | — | Contact status label (max 100 characters; cannot be empty). |
Request body
{
"name": "Jane Doe",
"first_name": "Jane",
"last_name": "Doe",
"email": "[email protected]",
"phone": "+14155550123",
"address": "123 Market St, San Francisco, CA",
"source": "website_form"
}Example
curl -X POST "https://api.centerfy.ai/webhooks/inbound/contacts" \
-H "x-api-key: $CENTERFY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Jane Doe",
"first_name": "Jane",
"last_name": "Doe",
"email": "[email protected]",
"phone": "+14155550123",
"address": "123 Market St, San Francisco, CA",
"source": "website_form"
}' import { CenterfyClient } from "@centerfy/sdk";
const centerfy = new CenterfyClient({ apiKey: process.env.CENTERFY_API_KEY! });
const result = await centerfy.contacts.create({
"name": "Jane Doe",
"first_name": "Jane",
"last_name": "Doe",
"email": "[email protected]",
"phone": "+14155550123",
"address": "123 Market St, San Francisco, CA",
"source": "website_form"
});
console.log(result); centerfy contacts create '{"name":"Jane Doe","first_name":"Jane","last_name":"Doe","email":"[email protected]","phone":"+14155550123","address":"123 Market St, San Francisco, CA","source":"website_form"}' MCP tool: centerfy_contacts_create (see MCP server)
Response
{
"status": "success",
"created": true,
"contact_id": "3f1c2a9e-7b4d-4c8a-9e2f-1a2b3c4d5e6f"
}Errors
400— if neither a usable email nor a valid E.164 phone is provided.401— if unauthenticated.
Notes
Match-and-update behavior: an existing contact matched by email (then phone) is updated in place and returns 200 with created:false; a new contact returns 201 with created:true. Tags are not accepted here — use POST /webhooks/inbound/contacts/:id/tags. The extended fields (custom_field_values, company_id, assigned_user_id, status) return 400 when invalid — e.g. a company or user outside this sub-account. If the contact saves but its custom field values fail to save, the response is 500 with contact_id.