This guide explains how to create properties through the API and what happens
when multiple objects use a property with the same name.

## Why this matters

Properties define the schema for your objects: field types, validation rules,
select options, and display metadata. In Caraer, a property is **not owned by a
single object**. The same property definition can be attached to many objects,
so you can reuse common fields like `email`, `firstname`, or `status` without
duplicating configuration.

## How properties relate to objects

Each property has:

- **Shared definition** — applies everywhere the property is used: `name`,
`label`, `type`, `format`, `rules`, `options`, `indexed`, and related
settings.
- **Per-object settings** — configured separately for each object the property
is attached to: `group`, `hidden`, `index`, and archive state (`deletedAt`,
`deletedBy`).


When you fetch properties for an object, the API returns the combined view:
shared definition plus that object's settings.

## Endpoint map

All property routes are scoped to an object:

- `POST /api/v2/objects/{objectUuid}/properties` — create (or attach) a property
- `PUT /api/v2/objects/{objectUuid}/properties/{propertyUuid}` — update
- `GET /api/v2/objects/{objectUuid}/properties/{propertyUuid}` — fetch one
- `POST /api/v2/objects/{objectUuid}/properties/index` — list with pagination
- `PUT /api/v2/objects/{objectUuid}/properties/updateIndices` — reorder fields
- `DELETE /api/v2/objects/{objectUuid}/properties/{propertyUuid}` — archive
for this object
- `POST /api/v2/objects/{objectUuid}/properties/{propertyUuid}/restore` —
restore an archived property on this object
- `DELETE /api/v2/objects/{objectUuid}/properties/{propertyUuid}/permanent` —
permanently remove an archived property from this object (and delete the
property entirely if no other object uses it)
- `GET /api/v2/objects/{objectUuid}/properties/formats` — list available formats
- `GET /api/v2/objects/{objectUuid}/properties/calculation-types` — calculation
types per property type


Write operations require the `tools.objects.schemas.write` scope. Delete and
restore require `tools.objects.schemas.delete`.

`{objectUuid}` accepts either the object UUID or its internal name (for example
`candidate`).

## Step 1: Create a property on an object


```bash
curl -X POST "https://your-company.caraer.com/api/v2/objects/candidate/properties" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "email",
    "label": "Email address",
    "description": "Primary contact email",
    "type": "string",
    "format": "email",
    "rules": ["required"],
    "group": "Contact details",
    "hidden": false,
    "indexed": true
  }'
```

Required fields:

| Field | Notes |
|  --- | --- |
| `name` | Lowercase, letters/numbers/underscores only. Stable internal key. |
| `label` | Human-readable label shown in the UI. |
| `type` | One of `string`, `number`, `date`, `single-select`, `multi-select`, `file`, `tag`, `structure`, `linked-property`, `checkbox`, `range`. |
| `format` | Must match the chosen type (for example `email` or `single-line` for `string`). Use `GET .../properties/formats` for the full list. |
| `rules` | Array of validation rule strings (for example `required`, `unique`). |


Common optional fields:

- `description` — max 255 characters
- `options` — required for select types; each option needs a unique `name` and `label`
- `group` — form/view grouping for this object
- `hidden` — hide on forms for this object (default `false`)
- `indexed` — enable faster filtering on this property (default `false`)
- `formatSettings` — format-specific configuration (for example linked-property targets)


### Visibility and behavior flags

These fields control how a property is exposed and how clients should treat it.
They are part of the **shared property definition**, so they apply everywhere
the property is attached.

| Field | Default | What it does |
|  --- | --- | --- |
| `nonPublic` | `false` | Restricts access for **App/API token** authentication. Even when a token has record read or write scopes for the object, it cannot read or write this property. Use for internal-only fields that integrations should not touch. |
| `editable` | `true` | Controls whether the **property schema** can be changed. When `false`, the property definition is locked — updates through `PUT .../properties/{propertyUuid}` are not allowed. This does not affect whether record values can be written. |
| `immutable` | `false` | Marks the property as **platform-managed**. Record values for this field cannot be edited through the records API — Caraer sets and maintains them through system logic or relations. Automatically enabled for `linked-property` fields. |
| `webpagePublic` | `false` | Allows the property value to be included when a record is returned through **public webpage** endpoints. Properties without this flag are omitted from public webpage responses. |
| `embeddable` | `false` | Includes the property value when Caraer builds a **semantic search embedding** for the record after create or update. Useful on text-like fields you want discoverable through AI-powered search. |
| `icon` | `null` | Optional icon name (Font Awesome style, for example `envelope` or `user`) shown in forms, views, and filters in the Caraer UI. Does not affect record API behavior. |


> **Note:** `editable` and `immutable` apply at different levels. `editable`
locks the property **schema** (type, label, rules, and so on). `immutable`
locks **record values** for that field. A property can be schema-editable
but still have immutable record values, or vice versa.


Example with flags set:


```bash
curl -X POST "https://your-company.caraer.com/api/v2/objects/candidate/properties" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "internal_notes",
    "label": "Internal notes",
    "type": "string",
    "format": "multi-line",
    "rules": [],
    "nonPublic": true,
    "editable": true,
    "webpagePublic": false,
    "embeddable": true,
    "icon": "note-sticky"
  }'
```

What happens on create:

1. Caraer looks up an existing property by **name**.
2. If one exists, the same property UUID is reused. If not, a new property is
created.
3. The property is attached to the object (or restored if it was previously
archived on that object).
4. Per-object settings (`group`, `hidden`, `index`) are saved for this object.
5. If `indexed` is `true`, the property is indexed for faster queries on this
object.
6. The property may be added to the object's default view (when it has fewer
than 10 visible columns).


## How sharing works when names match

Property names are **globally unique** within your company. There is one
property definition per name, not one per object.

### Attaching an existing property to another object

Create the same property name on a second object:


```bash
curl -X POST "https://your-company.caraer.com/api/v2/objects/vacancy/properties" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "email",
    "label": "Email address",
    "type": "string",
    "format": "email",
    "rules": [],
    "group": "Recruiter contact"
  }'
```

Because `email` already exists, Caraer:

- Reuses the same property UUID
- Attaches it to `vacancy`
- Saves object-specific `group`, `hidden`, and `index` for `vacancy`


Both objects now expose an `email` field, backed by the same property
definition.

### What is shared vs per-object

| Concern | Shared across objects | Per object |
|  --- | --- | --- |
| `name` | Yes — one name globally | — |
| `type`, `format` | Yes | — |
| `rules`, `options` | Yes | — |
| `label`, `description` | Yes | — |
| `indexed` | Yes | — |
| `nonPublic`, `editable`, `immutable`, `webpagePublic`, `embeddable`, `icon` | Yes | — |
| `group`, `hidden`, `index` | — | Yes |
| Archive (soft delete) | — | Yes (per object) |


Practical implication: updating `options` on a shared select property affects
every object that uses it. Changing `group` or `hidden` only affects the object
you update.

### Type and format must match

If a property name already exists, the new create request must use the **same
`type` and `format`**. Otherwise validation fails with an error like:

> A property with the same name already exists — change the type to … and
format to …


You cannot create `email` as a `string`/`email` on one object and as a
`single-select` on another.

Reserved names (`name`, `externalUuid`, `createdAt`, `updatedAt`, `uuid`) cannot
be created manually.

## Step 2: Update a property

Updates go through the property UUID:


```bash
curl -X PUT "https://your-company.caraer.com/api/v2/objects/candidate/properties/PROPERTY_UUID" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "email",
    "label": "Work email",
    "type": "string",
    "format": "email",
    "rules": ["required", "unique"]
  }'
```

Immutable after creation:

- `name`
- `type`


`format` is validated on create; treat it as fixed once records exist.

When you change shared fields (label, rules, options), all objects using that
property see the change. Send object-specific fields like `group` or `hidden` in
the same request to update only that object's settings.

## Step 3: Remove a property from an object

Archiving removes the property from one object's schema only:


```bash
curl -X DELETE "https://your-company.caraer.com/api/v2/objects/candidate/properties/PROPERTY_UUID" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```

- The property definition remains if other objects still use it.
- Existing values on `candidate` records are not automatically cleared; the
field is removed from the schema for that object.
- Restore with `POST .../properties/{propertyUuid}/restore`.


Permanent delete removes the archived property from this object. If no other
object uses the property, it is deleted entirely and values are removed from
records on all affected objects.

System properties (`name`, `label`, `createdAt`, `updatedAt`, `deletedAt`,
`uuid`) cannot be deleted.

## Property types and formats (quick reference)

| Type | Example formats |
|  --- | --- |
| `string` | `single-line`, `multi-line`, `email`, `phone`, `url` |
| `number` | `number`, `currency` |
| `date` | `date` |
| `single-select` | `single-select` |
| `multi-select` | `multi-select` |
| `checkbox` | `single-checkbox` |
| `file` | `file` |
| `tag` | `tag` |
| `structure` | `structure` |
| `linked-property` | `linked-property` (read-only; value comes from a related record) |
| `range` | `number-range`, `currency-range` |


Use `GET /api/v2/objects/{objectUuid}/properties/formats` for the authoritative
list in your environment.

## Design guidelines

- **Reuse intentionally.** Shared names mean shared validation, options, and
labels. Use the same name when fields truly represent the same concept
(`email`, `firstname`). Use distinct names when semantics differ (`status_candidate`
vs `status_vacancy`).
- **Plan names early.** `name` and `type` are immutable; renaming effectively
means migrating data to a new property.
- **Index for query paths.** Set `indexed: true` only on properties you filter
or sort on frequently.
- **Scope deletes carefully.** Archiving a shared property on one object does
not affect others; permanently deleting it while other objects still use it
is blocked.
- **Align integrations on `name`.** Record APIs address values by property
name, not UUID. Shared names behave the same across objects, but values live
on each record independently.


## Common pitfalls

- Creating the same name with a different type/format on another object.
- Expecting different select options per object while reusing one property name.
- Assuming archive on one object removes the property everywhere.
- Trying to update a property schema when `editable` is `false`.
- Sending values for `immutable` or `linked-property` fields on record create/update.
- Setting `nonPublic: false` and expecting App/API tokens to read sensitive fields without explicit property scopes.
- Removing select options that are still used in existing records.


## Related reading

- [Caraer data model: Object, Property, Relation](/blog/2026-03-25-caraer-data-model-object-property-relation)
- [Save records](/blog/2026-03-25-save-records)
- [How to submit values by property type and format](/blog/2026-03-25-save-records#how-to-submit-values-by-property-type-and-format)
- [Fetch records and filtering guide](/blog/2026-03-25-fetch-records-and-filtering-guide)