## Create a contact External contacts are Project Blue's native contact store for accounts **without a connected CRM**. They render in the Project Blue app's contact list and details panel. You can create them explicitly with the endpoints below, or implicitly by passing `firstName`, `lastName`, `email`, and `customFields` on `/send-api-message` — those fields persist on the auto-created contact for the recipient. > **For accounts without a connected CRM** > > If HighLevel or HubSpot is connected, contacts live in your CRM instead (see [CRM integration](https://api.tryprojectblue.com/#crm-integration)). External contacts apply to external/API-source accounts. `POST /create-external-contact` Required: `phone` only (plus `Authorization: Bearer `). Optional: `firstName`, `lastName`, `email`, `customFields`. `customFields` is a JSON object of key/value pairs, max 10,000 characters when stringified. Omit it entirely if you have none. Same phone for the same API key is an upsert (fills blank names, overwrites email, shallow-merges `customFields`). > **Upserts do not behave like updates** > > When the phone number already exists this endpoint merges rather than replaces, and the rules differ per field:`firstName` and `lastName` are only filled in when the stored value is empty. Sending a new name for a contact that already has one is silently discarded.`email` overwrites whenever you send a non-empty value.`customFields` is **shallow-merged** here — the opposite of [update](https://api.tryprojectblue.com/#update-contact), which replaces the whole object. Sending `{}` changes nothing.The response is the contact as it was _before_ the merge, so a discarded name is not visible in it. Read the contact back if you need to confirm. The same merge runs when `/send-api-message` auto-creates a contact. ### Request body - `phone` (string, required) — The only required field besides Authorization. E.164 like +12345678901. - `firstName` (string) — The contact's first name. On upsert, filled in only when the stored value is blank. - `lastName` (string) — The contact's last name. On upsert, filled in only when the stored value is blank. - `email` (string) — The contact's email address. On upsert, overwrites the stored email. - `customFields` (object) — JSON object of key/value pairs. Max 10,000 characters when stringified. Omit it entirely if you have none. On upsert, shallow-merged into the stored object. ### The contact object All three endpoints return the same object. Create and update return it as `{ contact }`; list returns `{ contacts, pagination }`. There is no `status` envelope. - `id` (string, required) — Opaque contact id. Pass it back as contactId when updating; do not parse it. - `firstName` (string | null, required) — The contact's first name, or null if never set. - `lastName` (string | null, required) — The contact's last name, or null if never set. - `phoneNumber` (string, required) — The contact's number in E.164. Note the asymmetry: requests take phone, responses return phoneNumber. - `email` (string | null, required) — The contact's email address, or null if never set. - `customFields` (object | null, required) — Whatever JSON metadata you last stored. Replaced wholesale on update, never merged. - `note` (string | null, required) — Free-form note stored on the contact. - `createdAt` (string (ISO-8601), required) — When the contact was first created. - `updatedAt` (string (ISO-8601), required) — When the contact was last modified. **Request — Body** Required: phone only. Optional: firstName, lastName, email, customFields. ```javascript { "phone": "+15551234567", // required, E.164 like +12345678901 "firstName": "Jane", // optional "lastName": "Doe", // optional "email": "jane@example.com", // optional "customFields": { // optional object; omit if unused "shopifyCustomerId": "123", "shopifyOrderId": "456" } } ``` **Request — Minimal** Phone plus Authorization: Bearer is enough. ```json { "phone": "+15551234567" } ``` **Request — cURL** ```bash curl -X POST https://api.tryprojectblue.com/create-external-contact \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "phone": "+15551234567", "firstName": "Jane", "lastName": "Doe", "email": "jane@example.com", "customFields": { "shopifyCustomerId": "123", "shopifyOrderId": "456" } }' ``` **Response — 200** Wrapped as { contact }. List returns { contacts, pagination }. ```json { "contact": { "id": "string", "firstName": "string | null", "lastName": "string | null", "phoneNumber": "+15551234567", "email": "string | null", "customFields": { } | null, "note": "string | null", "createdAt": "ISO-8601", "updatedAt": "ISO-8601" } } ```