Create a contact

create (or match and update) a contact.

POST https://api.centerfy.ai/webhooks/inbound/contacts

Headers

HeaderRequiredValueDescription
x-api-keyYesyour sub-account API key (cfy_…)Authenticates the request; or use Authorization: Bearer <key>.
Content-TypeYesapplication/jsonJSON request body.
X-Webhook-SourceNoyour-system-nameOptional loop-prevention label; the outbound trigger won’t echo this change back to the same source.

Body parameters

NameTypeRequiredDefaultDescription
namestringNo—Full name; if omitted it is derived from first_name + last_name, then email, then phone.
first_namestringNo—Used with last_name to build name when name is not given.
last_namestringNo—Used with first_name to build name when name is not given.
emailstringNo—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.
phonestringNo—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.
addressstringNo—Optional postal/street address.
sourcestringNo—Lead source label; defaults to ‘inbound_webhook’ when omitted. Only applied when a new contact is created.
custom_field_valuesobjectNo—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_idstring (uuid) | nullNo—Company to link the contact to; must be in this sub-account. null clears it.
assigned_user_idstring (uuid) | nullNo—Assign the contact to an active member of this sub-account. null unassigns.
statusstringNo—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.

© 2026 Centerfy AI. All rights reserved.