--- title: Fields and relationships | Lightfield description: Learn how fields, relationships, and their value types work in the Lightfield API. --- Fields and relationships are the core data model for CRM objects in Lightfield. **Fields** store data on an object (e.g. a name, email, or deal value), while **relationships** link objects together (e.g. an opportunity’s account or contacts). When creating or updating objects via the API (or through the SDKs), you pass field values in the `fields` object. Each key in that object corresponds to a field on the object type. ## Field keys Field and relationship keys use a `$` prefix to distinguish system-defined entries from custom ones: - **System fields and relationships**: Prefixed with `$`, e.g. `$name`, `$stage`, `$account`. - **Custom fields and relationships**: No prefix, e.g. `priority`, `partners`. ``` { "fields": { "$name": { "valueType": "TEXT", "value": "Acme Corp" }, "$stage": { "valueType": "SINGLE_SELECT", "value": "opt_..." }, "priority": { "valueType": "NUMBER", "value": 1 } } } ``` ## Discovering definitions Use the definitions endpoint to discover all fields and relationships available on an object type: ``` GET /v1/{objectType}/definitions ``` For example: Terminal window ``` curl https://api.lightfield.app/v1/opportunities/definitions \ -H "Authorization: Bearer $API_KEY" \ -H "Lightfield-Version: 2026-03-01" ``` The response includes the full schema for both fields and relationships: ``` { "objectType": "opportunity", "fieldDefinitions": { "$name": { "id": "ad_...", "slug": "name", "label": "Name", "description": null, "valueType": "TEXT", "system": true, "typeConfiguration": {} }, "$stage": { "id": "ad_...", "slug": "stage", "label": "Stage", "description": "What stage is the opportunity in?", "valueType": "SINGLE_SELECT", "system": true, "typeConfiguration": { "options": [ { "id": "opt_...", "label": "Prospecting", "description": null }, { "id": "opt_...", "label": "Closed Won", "description": null } ] } } }, "relationshipDefinitions": { "$account": { "id": null, "slug": "account", "label": "Account", "description": null, "system": true, "cardinality": "HAS_ONE", "objectType": "account" } } } ``` This is especially useful for resolving select option IDs to their labels, discovering which keys will appear in record responses, and finding the relationship definition slugs you can use to [filter by relationships](/using-the-api/list-endpoints#filtering-by-relationships/index.md). ### Supported object types | Object type | Path | | ------------- | ------------------------------- | | Accounts | `/v1/accounts/definitions` | | Contacts | `/v1/contacts/definitions` | | Opportunities | `/v1/opportunities/definitions` | ### Field definition object | Property | Type | Description | | ------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------- | | `id` | `string \| null` | Unique identifier (prefixed with `ad_`), or `null` for synthesized fields. | | `slug` | `string` | Internal slug for the field. | | `label` | `string` | Human-readable label. | | `description` | `string \| null` | Optional description. | | `valueType` | `string` | The field’s value type. See [field value types](#field-value-types). | | `system` | `boolean` | `true` for built-in fields, `false` for custom fields. | | `readOnly` | `boolean` | Present and `true` for fields that are not writable via the API (e.g. AI-generated summaries). Absent on writable fields. | | `typeConfiguration` | `object` | Type-specific configuration. See [type configuration](#type-configuration). | ### Relationship definition object | Property | Type | Description | | ------------- | ---------------- | --------------------------------------------------------------------------------- | | `id` | `string \| null` | Unique identifier (prefixed with `rd_`), or `null` for synthesized relationships. | | `slug` | `string` | Internal slug for the relationship. | | `label` | `string` | Human-readable label. | | `description` | `string \| null` | Optional description. | | `system` | `boolean` | `true` for built-in relationships, `false` for custom relationships. | | `cardinality` | `string` | `HAS_ONE` or `HAS_MANY`. | | `objectType` | `string` | The related object type (e.g. `account`, `contact`, `user`). | ## Writing relationships For creates, relationship values are entity IDs or arrays of entity IDs. For updates, use `add`, `remove`, or `replace` as allowed by the relationship’s cardinality. The limit applies to the whole request, not each relationship. A request can include up to 25 entity IDs after duplicate IDs within each relationship are removed. It can also add or remove up to 25 links. An ID counts even if the link already exists. A replacement can count as both a removal and an addition. Requests over either limit return `422` with the code `relationship_write_limit_exceeded`. ## Field value history Use a field history endpoint to retrieve the recorded values of one attribute-backed field over time. ### Supported history endpoints | Object type | Endpoint | | -------------- | ---------------------------------------------------------------- | | Accounts | `/v1/accounts/{id}/fields/{fieldKey}/history` | | Contacts | `/v1/contacts/{id}/fields/{fieldKey}/history` | | Opportunities | `/v1/opportunities/{id}/fields/{fieldKey}/history` | | Custom objects | `/v1/objects/{entitySlug}/values/{id}/fields/{fieldKey}/history` | Use a `$`-prefixed key for a system field, such as `$stage`, and a bare slug for a custom field. Results are returned newest first, with consecutive identical values collapsed. Responses default to 20 entries and accept a `limit` of up to 100. Use `nextCursor` as the `after` parameter to retrieve older pages. For example: Terminal window ``` curl 'https://api.lightfield.app/v1/opportunities/opp_.../fields/$stage/history?limit=20' \ -H "Authorization: Bearer $API_KEY" \ -H "Lightfield-Version: 2026-03-01" ``` The response includes the recorded value, its display value, and when it was recorded: ``` { "data": [ { "value": "opt_abc123", "valueType": "SINGLE_SELECT", "displayValue": "Closed Won", "recordedAt": "2026-08-01T17:30:00.000Z", "isCreate": false }, { "value": "opt_def456", "valueType": "SINGLE_SELECT", "displayValue": "Qualified", "recordedAt": "2026-06-20T09:15:00.000Z", "isCreate": true } ], "hasMore": false, "nextCursor": null } ``` History is available only for attribute-backed fields. Column-backed system fields and relationships are not tracked by these endpoints. Entries include the recorded value and time, but not the actor or source that made the change. See the API reference for [accounts](/api/resources/account/methods/fieldHistory/index.md), [contacts](/api/resources/contact/methods/fieldHistory/index.md), [opportunities](/api/resources/opportunity/methods/fieldHistory/index.md), and [custom objects](/api/resources/object/methods/fieldHistory/index.md). ## Field value types Every field has a `valueType` that determines the shape of its value: | Type | Value format | Description | | ------------------- | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `TEXT` | `string` | Plain text. | | `NUMBER` | `number` | A number with up to 2 decimal places, within safe integer range. | | `CHECKBOX` | `boolean \| null` | A true/false value. | | `CURRENCY` | `number \| null` | A numeric amount. The currency code (e.g. `USD`) is set in the field’s type configuration. | | `DATETIME` | `string \| null` | An ISO 8601 datetime string with timezone offset (e.g. `"2026-03-16T12:00:00+00:00"`). Stored as UTC. | | `EMAIL` | `string \| string[] \| null` | Email address data (RFC 5322). Most `EMAIL` fields return arrays, but on the meetings resource `meeting.$organizerEmail` returns a single string. Fields such as `meeting.$attendeeEmails` still return arrays. | | `TELEPHONE` | `string[]` | An array of phone numbers. See [telephone format](#telephone-format) below. | | `URL` | `string \| string[] \| null` | A URL or array of URLs. | | `ADDRESS` | `object \| null` | A structured address object. See [address format](#address-format) below. | | `FULL_NAME` | `object` | An object with `firstName` and `lastName` properties. | | `SOCIAL_HANDLE` | `string \| null` | A social media handle or profile URL. See [social handle format](#social-handle-format) below. | | `SINGLE_SELECT` | `string \| null` | The `id` of a select option (prefixed with `opt_`). | | `MULTI_SELECT` | `string[]` | An array of select option `id`s. | | `READONLY_MARKDOWN` | `string` | Read-only markdown content, typically AI-generated. Not writable via the API. | Cardinality is field-specific for some value types. Most `EMAIL` fields return arrays, but on the meetings resource (`POST /v1/meetings`, `GET /v1/meetings`, `GET /v1/meetings/{id}`, and `POST /v1/meetings/{id}`), `meeting.$organizerEmail` returns a single email string. `meeting.$attendeeEmails` still returns an array of email strings. `MULTI_SELECT` is always an array, and fields like `SINGLE_SELECT`, `CHECKBOX`, and `NUMBER` accept a single value or `null`. ## Address format Address values are objects with the following properties, all optional. For convenience, pass a single `null` value to unset all properties: | Property | Type | Description | | ------------ | -------- | ------------------------------------------------------------------------------------ | | `street` | `string` | Street address line 1. | | `street2` | `string` | Street address line 2 (apartment, suite, etc.). | | `city` | `string` | City name. | | `state` | `string` | State or region. | | `postalCode` | `string` | Postal or ZIP code. | | `country` | `string` | ISO 3166-1 alpha-2 country code (e.g. `"US"`, `"GB"`). Must be exactly 2 characters. | | `latitude` | `number` | Latitude coordinate. | | `longitude` | `number` | Longitude coordinate. | ``` { "street": "123 Main St", "city": "San Francisco", "state": "CA", "postalCode": "94105", "country": "US" } ``` ## Telephone format Phone numbers are validated and normalized on input: - **With `+` prefix**: Validated as an international number in E.164 format (e.g. `"+12025551234"`). - **Without `+` prefix**: Validated as a US number first, then falls back to accepting 7–15 digit local numbers. - **Extensions**: Supported via `;ext=` notation (e.g. `"+12025551234;ext=100"`). Various input formats like `x100`, `ext:100`, and `#100` are automatically normalized to the `;ext=` format. ``` ["+12025551234", "+442071234567;ext=100"] ``` ## Social handle format `SOCIAL_HANDLE` fields store a social media profile URL. Each social handle field has a `handleService` in its type configuration that specifies the platform: `TWITTER`, `LINKEDIN`, `FACEBOOK`, or `INSTAGRAM`. Values must be a valid profile URL for the configured platform: | Platform | Accepted URL formats | | ----------- | ----------------------------------------------------------------------- | | `TWITTER` | `https://twitter.com/{username}`, `https://x.com/{username}` | | `LINKEDIN` | `https://linkedin.com/in/{slug}`, `https://linkedin.com/company/{slug}` | | `FACEBOOK` | `https://facebook.com/{username}`, `https://fb.com/{username}` | | `INSTAGRAM` | `https://www.instagram.com/{username}` | Regional subdomains (e.g. `https://uk.linkedin.com/in/...`) and query parameters are accepted. ``` "https://x.com/lightfield" "https://www.linkedin.com/in/jane-doe" "https://www.instagram.com/lightfield" ``` Pass `null` to clear a social handle field. ## Select options `SINGLE_SELECT` and `MULTI_SELECT` fields reference predefined options by their `id`. Use the [definitions endpoint](#discovering-definitions) to discover the available options for a field. Each option has the following shape: | Property | Type | Description | | ------------- | ---------------- | ---------------------------------------- | | `id` | `string` | Unique identifier, prefixed with `opt_`. | | `label` | `string` | Display label. | | `description` | `string \| null` | Optional description. | When setting a `SINGLE_SELECT` value, pass the option’s `id` as a string. For `MULTI_SELECT`, pass an array of option `id`s. ``` { "fields": { "$stage": "opt_abc123", "tags": ["opt_def456", "opt_ghi789"] } } ``` Select options are org-specific. Always read them from the definitions endpoint rather than hardcoding option IDs. ## Type configuration Some field types include additional configuration via `typeConfiguration` on the field definition. You can read these from the [definitions endpoint](#discovering-definitions). | Type | Configuration | Description | | --------------- | -------------------------- | --------------------------------------------------------------------------------------------------- | | `CURRENCY` | `currency` | ISO 4217 currency code (e.g. `"USD"`). | | `EMAIL` | `unique`, `multipleValues` | Whether values must be unique across objects, and whether the field supports multiple email values. | | `TELEPHONE` | `unique`, `multipleValues` | Same as email — uniqueness and multiple value support. | | `URL` | `unique`, `multipleValues` | Same as email — uniqueness and multiple value support. | | `SOCIAL_HANDLE` | `handleService` | Platform hint: `TWITTER`, `LINKEDIN`, `FACEBOOK`, or `INSTAGRAM`. | | `SINGLE_SELECT` | `options` | The list of available select options. | | `MULTI_SELECT` | `options` | The list of available select options. | Types not listed above (such as `TEXT`, `NUMBER`, `DATETIME`, `CHECKBOX`, `ADDRESS`, `FULL_NAME`, and `READONLY_MARKDOWN`) return an empty `typeConfiguration: {}`. ## System vs custom fields Fields and relationships can be either **system** or **custom**: - **System** entries are built-in, prefixed with `$`, and cannot be deleted. They are present on every object of their entity type (e.g. `$name` on accounts, `$account` on opportunities). - **Custom** entries are user-defined and can be created, updated, and removed through the API or the Lightfield UI. Both system and custom entries follow the same value type rules described above. ## ID prefixes Lightfield uses prefixed IDs to distinguish between entity types: | Entity | Prefix | Example | | ----------------------- | ------ | ------------ | | Account | `acc_` | `acc_abc123` | | Contact | `con_` | `con_abc123` | | Opportunity | `opp_` | `opp_abc123` | | Member | `mem_` | `mem_abc123` | | Field definition | `ad_` | `ad_abc123` | | Relationship definition | `rd_` | `rd_abc123` | | Select option | `opt_` | `opt_abc123` | | Attribute value | `av_` | `av_abc123` | | Relationship value | `rv_` | `rv_abc123` |