Skip to content
Dashboard

Create or update a contact

POST/v2/contacts

Creates a new contact or updates an existing one.

If a contact with the given email or userId already exists, it will be updated. Otherwise, a new contact will be created.

At least one of email or userId must be provided for identification.

Request Body

FieldTypeRequiredDescription
emailstring or nullOne of email/userIdContact email address. Send null with userId to remove the current email.
userIdstring or nullOne of email/userIdExternal user ID. Send null with email to remove it.
namestring or nullNoContact display name. null resets it to the system fallback.
profilePicturestring or nullNoProfile picture URL. null removes it.
companiesarrayNoReplacement company list. [] removes all memberships.
customFieldsobjectNoValues merge by key. A key with null is removed.
subscribedToChangelogbooleanNoWhether subscribed to changelog
localestring or nullNoContact locale/language. null removes it.
phonestring or nullNoContact phone number. null removes it.
rolesarrayNoReplacement role list. [] removes all roles.
userHashstringNoHMAC hash for identity verification
createdAtstringNoWhen the contact was created (ISO 8601)

Orbit Field Update Semantics

  • Omit a field to keep its current value.
  • Send a non-null scalar value to add or replace that value.
  • Send null for email, userId, name, profilePicture, phone, or locale to clear or reset it.
  • A contact must keep at least one identifier. Removing email requires userId. Removing userId requires email.
  • Send an array to replace the stored list. Send [] to clear companies or roles.
  • customFields is merged by key. Omitted keys stay unchanged. A key with null is removed.
  • createdAt and subscribedToChangelog do not accept null. Use false to turn off changelog subscription.
  • Empty strings are not deletion values for typed contact fields.

Company Object

Each company in the companies array can have:

  • id (required) - External company ID from your system
  • name (required) - Company name
  • monthlySpend - Monthly spend/revenue
  • customFields - Custom field values
  • industry - Industry
  • website - Company website URL
  • plan - Current plan/subscription
  • companySize - Number of employees
  • createdAt - When the company was created

Response

Returns the created or updated contact object.

  • 201 Created - A new contact was created
  • 200 OK - An existing contact was updated

Example Request

{
  "email": "john@example.com",
  "name": "John Doe",
  "userId": "usr_12345",
  "companies": [
    {
      "id": "company_123",
      "name": "Acme Inc",
      "monthlySpend": 500,
      "plan": "enterprise"
    }
  ],
  "customFields": {
    "plan": "pro",
    "signupSource": "website"
  },
  "subscribedToChangelog": true
}

Example: Remove an Email

{
  "userId": "usr_12345",
  "email": null
}

Example Response

{
  "object": "contact",
  "id": "676f0f6765bdaa7d7d760f88",
  "email": "john@example.com",
  "name": "John Doe",
  "userId": "usr_12345",
  "type": "customer",
  "companies": [...],
  "customFields": {...},
  ...
}

Version Availability

This endpoint is available in API version 2026-01-01.nova and newer. The Orbit field update rules, including null removal and empty-array replacement, require API version 2026-08-19.orbit or newer.

Header ParametersExpand Collapse
"Featurebase-Version": optional "2026-08-19.orbit" or "2026-01-01.nova" or "2025-12-12.clover"
One of the following:
"2026-08-19.orbit"
"2026-01-01.nova"
"2025-12-12.clover"
Body ParametersJSONExpand Collapse
companies: optional array of object { id, name, companyHash, 7 more }

Replacement list of companies. Pass an empty array to remove all company memberships.

id: string

External company ID from your system

minLength1
maxLength500
name: string

Company name

minLength1
maxLength500
companyHash: optional string

HMAC-SHA256 hash of company ID for identity verification

maxLength256
companySize: optional number

Number of employees in the company

minimum0
createdAt: optional string

When the company was created (ISO 8601)

customFields: optional map[string or number or boolean or array of string or number or boolean]

Custom field values on the company. Values can be string, number, boolean, null, or array of primitives.

One of the following:
string
number
boolean
array of string or number or boolean
One of the following:
string
number
boolean
industry: optional string

Industry the company operates in

maxLength500
monthlySpend: optional number

Monthly spend/revenue from this company

minimum0
plan: optional string

Current plan/subscription name

maxLength500
website: optional string

Company website URL

maxLength500
createdAt: optional string

When the contact was created in your system (ISO 8601). This field cannot be null.

formatdate-time
customFields: optional map[string or number or boolean or array of string or number or boolean]

Fields are merged by key. Omitted keys are preserved. A null value removes that custom field key.

One of the following:
string
number
boolean
array of string or number or boolean
One of the following:
string
number
boolean
email: optional string

Contact email address. Omit to preserve it, or pass null with userId to remove the stored email.

maxLength500
formatemail
locale: optional string

Contact locale/language preference. Pass null to remove it.

minLength1
maxLength10
name: optional string

Contact display name. Pass null to reset it to the system fallback.

minLength1
maxLength500
phone: optional string

Contact phone number. Pass null to remove it.

minLength1
maxLength500
profilePicture: optional string

Profile picture URL. Pass null to remove the stored profile picture.

formaturi
roles: optional array of string

Replacement list of role (User Tag) names. Pass an empty array to remove all roles.

subscribedToChangelog: optional boolean

Whether the contact is subscribed to changelog updates

userHash: optional string

HMAC-SHA256 hash of userId or email for identity verification

maxLength256
userId: optional string

External user ID from your system. Pass null with email to remove the stored user ID.

minLength1
maxLength500

Create or update a contact

curl https://do.featurebase.app/v2/contacts \
    -H 'Content-Type: application/json' \
    -H "Authorization: Bearer $FEATUREBASE_API_KEY" \
    -d '{
          "createdAt": "2024-01-15T10:30:00Z",
          "customFields": {
            "plan": "pro",
            "signupSource": "website",
            "accountType": "string"
          },
          "email": "john@example.com",
          "locale": "en",
          "name": "John Doe",
          "phone": "+1234567890",
          "profilePicture": "https://example.com/avatar.png",
          "roles": [
            "app",
            "role_vip",
            "role_beta",
            "Internal User"
          ],
          "subscribedToChangelog": true,
          "userHash": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6",
          "userId": "usr_12345"
        }'
{
  "id": "676f0f6765bdaa7d7d760f88",
  "name": "John Steezy",
  "object": "contact",
  "type": "customer",
  "commentsCreated": 0,
  "companies": [
    {
      "id": "507f1f77bcf86cd799439011",
      "companyId": "comp_12345",
      "companySize": 250,
      "createdAt": "2025-01-01T12:00:00.000Z",
      "industry": "Technology",
      "lastActivity": "2025-01-15T00:00:00.000Z",
      "linkedUsers": 15,
      "monthlySpend": 5000,
      "name": "Acme Inc",
      "object": "company",
      "plan": "enterprise",
      "updatedAt": "2025-01-10T15:30:00.000Z",
      "website": "https://acme.com",
      "customFields": {
        "location": "bar",
        "priority": "bar"
      }
    }
  ],
  "customFields": {
    "foo": "bar"
  },
  "description": "",
  "email": "john@example.com",
  "lastActivity": "2025-01-03T21:42:30.181Z",
  "locale": "en",
  "manuallyOptedOutFromChangelog": false,
  "organizationId": "5febde12dc56d60012d47db6",
  "postsCreated": 0,
  "profilePicture": "https://fb-usercontent.fra1.cdn.digitaloceanspaces.com/anon_23.png",
  "roles": [
    "string"
  ],
  "subscribedToChangelog": true,
  "userId": "676f0f673dbb299c8a4f3057",
  "verified": true
}
Returns Examples
{
  "id": "676f0f6765bdaa7d7d760f88",
  "name": "John Steezy",
  "object": "contact",
  "type": "customer",
  "commentsCreated": 0,
  "companies": [
    {
      "id": "507f1f77bcf86cd799439011",
      "companyId": "comp_12345",
      "companySize": 250,
      "createdAt": "2025-01-01T12:00:00.000Z",
      "industry": "Technology",
      "lastActivity": "2025-01-15T00:00:00.000Z",
      "linkedUsers": 15,
      "monthlySpend": 5000,
      "name": "Acme Inc",
      "object": "company",
      "plan": "enterprise",
      "updatedAt": "2025-01-10T15:30:00.000Z",
      "website": "https://acme.com",
      "customFields": {
        "location": "bar",
        "priority": "bar"
      }
    }
  ],
  "customFields": {
    "foo": "bar"
  },
  "description": "",
  "email": "john@example.com",
  "lastActivity": "2025-01-03T21:42:30.181Z",
  "locale": "en",
  "manuallyOptedOutFromChangelog": false,
  "organizationId": "5febde12dc56d60012d47db6",
  "postsCreated": 0,
  "profilePicture": "https://fb-usercontent.fra1.cdn.digitaloceanspaces.com/anon_23.png",
  "roles": [
    "string"
  ],
  "subscribedToChangelog": true,
  "userId": "676f0f673dbb299c8a4f3057",
  "verified": true
}