> ## Documentation Index
> Fetch the complete documentation index at: https://trysalty.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Schema

> Declare custom attributes on any object type.

The schema endpoints let agents extend Salty's data model without leaving the API. See [Concepts → Schema engine](/docs/concepts/schema-engine) for how validation works.

## Endpoints

| Method   | Path                                   | Purpose                                            |
| -------- | -------------------------------------- | -------------------------------------------------- |
| `GET`    | `/schema`                              | Full workspace schema, grouped by object type      |
| `GET`    | `/schema/:object_type`                 | Attributes defined for one object type             |
| `POST`   | `/schema/:object_type/attributes`      | Add an attribute definition                        |
| `PATCH`  | `/schema/:object_type/attributes/:key` | Modify (metadata + append enums + toggle required) |
| `DELETE` | `/schema/:object_type/attributes/:key` | Soft-delete (sets `deprecated_at`)                 |

`:object_type` is one of `person`, `company`, `deal`, `note`, `task`, `activity`, or a custom object's UUID (not its slug).

## Add an attribute

```bash theme={null}
curl -X POST $SALTY_API/schema/person/attributes \
  -H "Authorization: Bearer $SALTY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "attribute_key": "lifecycle_stage",
    "display_name": "Lifecycle Stage",
    "data_type": "enum",
    "enum_values": ["lead", "customer", "churned"],
    "is_required": false,
    "default_value": "lead"
  }'
```

Response:

```json theme={null}
{
  "id": "2a48bdd8-61e6-4ad1-bf8b-8d58ba6e1666",
  "object_type": "person",
  "attribute_key": "lifecycle_stage",
  "display_name": "Lifecycle Stage",
  "data_type": "enum",
  "is_required": false,
  "default_value": "lead",
  "enum_values": ["lead", "customer", "churned"],
  "reference_object_type": null,
  "deprecated": false,
  "deprecated_at": null,
  "created_at": "2026-05-24T02:45:46.461Z"
}
```

## Constraints by `data_type`

* `enum` requires non-empty `enum_values`. Otherwise → `400 enum_values_required`.
* `reference` requires `reference_object_type` ∈ `{person, company, deal}`. Otherwise → `400 reference_object_type_required`.
* All keys must match `^[a-z][a-z0-9_]*$` (lowercase snake\_case, max 64 chars).
* Duplicate `(workspace_id, object_type, attribute_key)` → `409 attribute_exists`.

## Modify (PATCH)

```bash theme={null}
curl -X PATCH $SALTY_API/schema/person/attributes/lifecycle_stage \
  -H "Authorization: Bearer $SALTY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "display_name": "Lifecycle",
    "enum_values_append": ["prospect", "qualified"],
    "is_required": true
  }'
```

What you can edit:

* `display_name`
* `default_value`
* `is_required` (both directions; doesn't backfill existing rows)
* `enum_values_append` — array of new values to add. Duplicates are deduped server-side.

What you **can't** edit:

* `data_type` — frozen after creation. Changing it would require a data migration.
* `enum_values` — only appendable in v1. Removing values silently invalidates existing rows; the v1.1 endpoint will require explicit migration semantics.

## Deprecate (DELETE)

```bash theme={null}
curl -X DELETE $SALTY_API/schema/person/attributes/lead_source \
  -H "Authorization: Bearer $SALTY_API_KEY"
# 204 No Content
```

Sets `deprecated_at = now()`. Existing data with the key stays readable. New writes of the key return `400 attribute_deprecated`. The attribute still shows up in `GET /schema` with `"deprecated": true`.

## Read

```bash theme={null}
curl $SALTY_API/schema -H "Authorization: Bearer $SALTY_API_KEY"
```

```json theme={null}
{
  "objects": {
    "person": [ /* ... */ ],
    "company": [],
    "deal": [],
    "note": [],
    "task": [],
    "activity": []
  }
}
```
