# Profiles

> The Profiles operations of the Hub REST API (6 operations).

URL: https://docs.synap.live/docs/reference/hub-api/profiles · Generated from Synap-core/backend@c31e261ab (main)

Paths are relative to `/api/hub` on a pod.

## GET /profiles

List entity profiles

Returns profiles visible to the user in the given workspace (system + workspace-scoped + extended).

Parameters:

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `userId` | query | string | yes |  |
| `workspaceId` | query | string | yes |  |
| `detail` | query | "full" |  | Pass `detail=full` to receive the complete profile row including renderer/hint columns. Omit for the lightweight digest (default). |

Responses:

- `200`: Array of profiles. Default: lightweight digest (id, slug, displayName, entityScope, scope, description, icon, profileKind, applicableKinds). `scope` = visibility (who can use the type); `entityScope` = placement (where its entities live). Pass ?detail=full for the complete row.
- `400`: Missing required query param
- `403`: Forbidden
- `500`: Internal error

## POST /profiles

Define a kind or a role

Defines an entity kind (`profileKind: 'kind'`, default) or an attachable role (`profileKind: 'role'`), optionally with `fields`. Slug-idempotent. Governed: an agent caller gets `status: 'proposed'` (fields deferred until approval) — that is success, not an error. Same door as MCP synap_define_kind / synap_define_role.

Request body (`application/json`): CreateProfileRequest

| Field | Type | Required | Description |
|---|---|---|---|
| `userId` | string | yes |  |
| `workspaceId` | string | yes |  |
| `slug` | string | yes | Stable lower-kebab profile identifier. |
| `displayName` | string | yes |  |
| `description` | string |  |  |
| `defaultValues` | object |  |  |
| `parentProfileId` | string |  | Parent profile to inherit properties from. |
| `uiHints` | object |  |  |
| `reasoning` | string |  |  |
| `agentUserId` | string |  |  |
| `sourceMessageId` | string |  |  |
| `profileKind` | "kind" \| "role" |  | 'kind' (default) = a primary entity type; 'role' = an attachable facet type (client, sponsor…). |
| `applicableKinds` | array<string> |  | For a role: base-kind slugs it attaches to. Omitted → company, person. |
| `roleCategory` | string |  | For a role: category key clustering related roles. |
| `entityScope` | "pod" \| "workspace" |  | Where entities of this type live. Omit to let the pod decide (kind → pod, role → workspace). |
| `icon` | string |  |  |
| `fields` | array<object> |  | Field definitions ({ slug, valueType, displayName?, required?, defaultValue?, constraints?, uiHints?, displayOrder?, overlay? }). Added once the profile exists; deferred while it is proposed. Each field reports its own result. |

Responses:

- `200`: Created profile
- `400`: Bad request
- `403`: Forbidden
- `500`: Internal error

## GET /property-defs

List property definitions

Returns property defs visible to the workspace (global + profile-base + this workspace's overlays).

Parameters:

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `userId` | query | string | yes |  |
| `workspaceId` | query | string | yes |  |
| `profileId` | query | string |  | Filter to defs attached to a specific profile. |

Responses:

- `200`: Array of property definitions
- `400`: Missing required query param
- `403`: Forbidden
- `500`: Internal error

## POST /property-defs

Create a property definition

Adds a property def. Set `overlay: true` to create a workspace-scoped overlay invisible to other workspaces using the same profile.

Request body (`application/json`): CreatePropertyDefRequest

| Field | Type | Required | Description |
|---|---|---|---|
| `userId` | string | yes |  |
| `workspaceId` | string | yes |  |
| `profileId` | string |  |  |
| `slug` | string | yes |  |
| `valueType` | string | yes | Logical type — e.g. text, number, date, boolean, select, multi_select, relation. |
| `constraints` | object |  |  |
| `uiHints` | object |  |  |
| `agentUserId` | string |  |  |
| `sourceMessageId` | string |  |  |
| `overlay` | boolean |  | When true, create a workspace-scoped overlay def invisible to other workspaces using the same profile. Defaults to false (base def). |

Responses:

- `200`: Created property def
- `400`: Bad request
- `403`: Forbidden
- `500`: Internal error

## PATCH /property-defs/{id}

Edit an existing property definition

Changes a stored def's slug / valueType / constraints / uiHints. This is the ONE edit door — `POST /property-defs` is slug-idempotent and never converges, so it reports `status: "unchanged"` and points here. Governed: an agent caller gets `status: 'proposed'` (that is success, not an error), because narrowing a def re-interprets every existing row of every profile that links it.

Request body (`application/json`): UpdatePropertyDefRequest

| Field | Type | Required | Description |
|---|---|---|---|
| `userId` | string | yes |  |
| `workspaceId` | string | yes |  |
| `slug` | string |  | Rename. Normalised to kebab-case by the one slugifier. |
| `valueType` | string |  |  |
| `constraints` | object |  |  |
| `uiHints` | object |  |  |
| `agentUserId` | string |  |  |
| `sourceMessageId` | string |  |  |
| `reasoning` | string |  |  |

Responses:

- `200`: Updated property def
- `400`: Bad request
- `403`: Forbidden
- `404`: Property def not found
- `500`: Internal error

## GET /profiles/{slug}/renderers

Get the effective renderer(s) for a profile

Returns the RendererTarget resolved for the given profile in the given workspace. Resolution order: workspace overlay → profile system default → hardcoded fallback. Omit `contentKind` to receive all profile renderer kinds in one round trip.

Parameters:

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `userId` | query | string | yes |  |
| `workspaceId` | query | string | yes |  |
| `contentKind` | query | "entity-detail" \| "entity-card" \| "entity-profile" \| "collection" |  |  |
| `slot` | query | "list" \| "detail" \| "dashboard" |  | Deprecated alias: list → entity-profile, detail → entity-detail, dashboard → collection. |

Responses:

- `200`: ContentKind-keyed renderer map. Unrequested kinds are null when `contentKind` is supplied.
- `400`: Missing required query param
- `403`: Forbidden
- `500`: Internal error
