SynapSynapDocs
Hub REST API

Entities

The Entities operations of the Hub REST API (17 operations).

For BuildersOpen-source podSynap CloudGenerated from Synap-core/backend@c31e261ab (main)

List entities for a user

GET
/users/{userId}/entities

Authorization

bearerAuth
headerAuthorizationBearer <API key>

Pod API key (Bearer) issued via /api/hub/setup/agent or pod admin tooling. Browser/web clients may use the X-Session-Token header instead. See GET /api/hub/auth/status to introspect the calling key.

Path Parameters

userId*string

The caller's own user id, or me.

Query Parameters

profileSlug?string
type?string

Deprecated alias for profileSlug.

workspaceId?string
projectId?string

Project lens: only the project and what is filed into it.

facetSlug?string

Only entities carrying a live facet of this role (e.g. client).

limit?string

Response Body

Array of entities

application/json
  1. response
[index: integer]?
Example Request
curl -X GET 'https://pod.example.com/api/hub/users/string/entities'
Example Response
[  {    "property1": null,    "property2": null  }]
GET
/entities/without-project/counts

Authorization

bearerAuth
headerAuthorizationBearer <API key>

Pod API key (Bearer) issued via /api/hub/setup/agent or pod admin tooling. Browser/web clients may use the X-Session-Token header instead. See GET /api/hub/auth/status to introspect the calling key.

Response Body

Counts by kind

application/json
  1. response
total*integer
byProfile*array<>
Example Request
curl -X GET 'https://pod.example.com/api/hub/entities/without-project/counts'
Example Response
{  "total": 0,  "byProfile": [    {      "profileSlug": "string",      "count": 0    }  ]}

Search/list entities

GET
/entities

Authorization

bearerAuth
headerAuthorizationBearer <API key>

Pod API key (Bearer) issued via /api/hub/setup/agent or pod admin tooling. Browser/web clients may use the X-Session-Token header instead. See GET /api/hub/auth/status to introspect the calling key.

Query Parameters

q?string

Free-text search query. Refused (403 GRANT_DOOR_UNSUPPORTED) for a scoped key: search is not bounded by a grant. List without q instead.

profileSlug?string
workspaceId?string
projectId?string

Project lens — narrows results to a single project's data (the project entity + everything that belongs_to it). Pure-narrowing; cannot widen access past the user floor.

withoutProject?string

true = only the things filed into NO project (the 'Not in a project' list). 400 with projectId or q. Counts by kind: GET /entities/without-project/counts.

Value in"true""false"
limit?string
offset?string

Zero-based offset for cursor-free pagination (default 0).

sort?string

e.g. updatedAt:desc.

scope?string

pod = pod-wide entities only (null workspaceId); workspace = single workspace (requires workspaceId); all = merge across all accessible workspaces.

Value in"pod""workspace""all"
includePodWide?string

When a workspaceId is supplied, include pod-wide (null workspaceId) globals in the result. Defaults to true so a workspace lens returns 'that workspace + pod-wide globals' — the behavior agents and the CRM expect. Set false to return only the exact workspace's rows.

facetSlug?string

Kind + Facets filter — only return entities carrying a live facet of this role-profile slug (e.g. investor).

facetProfileId?string

Same as facetSlug but by profile id. Wins if both are set.

createdAfter?string

Creation-date window, INCLUSIVE lower bound (ISO-8601). Together with createdBefore forms the half-open interval [createdAfter, createdBefore), so consecutive windows tile. Rejected with 400 when combined with q — see createdBefore.

createdBefore?string

Creation-date window, EXCLUSIVE upper bound (ISO-8601). NOT combinable with q: the q branch is a relevance-ranked top-N served by Typesense, and a date window is an exhaustive question, so silently truncating one to the other would under-report. Ask for the window without q.

Response Body

Array of entities

application/json
  1. response
[index: integer]?
Example Request
curl -X GET 'https://pod.example.com/api/hub/entities'
Example Response
[  {    "id": "string",    "profileSlug": "string",    "type": "string",    "title": "string",    "preview": "string",    "description": "string",    "properties": {      "property1": null,      "property2": null    },    "systemData": {      "property1": null,      "property2": null    },    "userId": "string",    "workspaceId": "string",    "documentId": "string",    "version": 0,    "createdAt": "string",    "updatedAt": "string"  }]

Create an entity

POST
/entities

Authorization

bearerAuth
headerAuthorizationBearer <API key>

Pod API key (Bearer) issued via /api/hub/setup/agent or pod admin tooling. Browser/web clients may use the X-Session-Token header instead. See GET /api/hub/auth/status to introspect the calling key.

Request Body

application/json
  1. body
userId?string

Owner of the entity. Defaults to the authenticated user.

agentUserId?string

Agent user that performed the write. Used for proposal authorship.

workspaceId?string

Target workspace. Falls back to profile.entityScope and the user's first accessible workspace.

projectId?string

File the new entity into a project (its entity id). Stamps a belongs_to_project edge at materialization, so project-scoped recall (ask with projectId) finds it. Orthogonal to workspaceId.

profileSlug?string

Profile slug. Provide profileSlug, profileId, or the deprecated type alias.

profileId?string

Profile UUID. Resolved to the profile's slug server-side; takes precedence over profileSlug/type.

Formatuuid
type?string

Deprecated alias for profileSlug. Prefer profileSlug.

title*string
description?string
properties?
content?string

Long-form markdown body. When set, a linked, versioned document is created (MinIO-stored) instead of inlining into properties.

reasoning?string

The proposing agent's rationale for this action, surfaced in the proposal inbox.

source?string

Origin signal for downstream attribution.

Value in"intelligence""agent""openwebui-pipeline""extension""cli""n8n""raycast"
sourceMessageId?string

Optional message ID that triggered this write (for event chain causality).

sessionId?string

Optional session ID to link proposals created by this write to an active focus session.

facets?array<>

Kind + Facets: role-profiles to attach to the new entity in the same call (each via the governed attachFacet door). Applied only when the entity materializes. slug is canonical; profileSlug is accepted as an alias for parity with the /structure and tRPC facet shapes.

forceCreate?boolean

Bypass the weak same-name create gate when a same-profile entity with this title already exists. Prefer reusing the existing id. Does not bypass strong-signal auto-merge (email/phone/url).

externalId?string

Strong cross-source identity anchor for this subject, as provider:id (e.g. discord:123456789012345678). Registered as an external_id identity signal so a later create with the SAME value auto-resolves to the same entity (dedup) instead of minting a duplicate — the strong atom that the weak email/phone/url auto-extraction cannot express for opaque connector ids. Opaque and case-sensitive; the provider prefix namespaces the id.

expectedLabel?string

The declared session-output slot this entity fulfils, exactly as declared on the session's expected outputs. Left unset when the create doesn't fulfil a specific declared slot.

Response Body

Created entity (with effectiveWorkspaceId echo)

application/json
  1. response
status*string

e.g. 'created' or 'proposed'.

message?string
id?string
proposalId?string|null
proposedEntityId?string|null
workspaceId?string|null
effectiveWorkspaceId*|

Workspace the entity actually landed in. May be null for pod-wide profiles.

proposalType?string

Present when status is "proposed". "join" for a workspace-join gate, else "." (e.g. "entity.create").

reviewUrl?string

Present when status is "proposed". Absolute URL to review the proposal in the Studio.

deferred?true

Workspace-join gate only (proposalType "join"): this write was KEPT on the join request and is replayed for you once the join is approved — do not resend it.

Value intrue
ref?string

With deferred: the kept write's ref on the join proposal.

facets?array<>

Kind + Facets: per-role attach outcome for any facets passed in the request (status attached/proposed/error).

writeReceipt?
Example Request
curl -X POST 'https://pod.example.com/api/hub/entities' \  -H 'Content-Type: application/json' \  -d '{  "title": "string"}'
Example Response
{  "status": "string",  "message": "string",  "id": "string",  "proposalId": "string",  "proposedEntityId": "string",  "workspaceId": "string",  "effectiveWorkspaceId": "string",  "proposalType": "string",  "reviewUrl": "string",  "deferred": true,  "ref": "string",  "facets": [    {      "slug": "string",      "status": "string",      "facetId": "string",      "proposalId": "string",      "error": "string"    }  ],  "writeReceipt": {    "state": "pending",    "proposalId": "string",    "reviewUrl": "string",    "entityId": "string",    "proposedEntityId": "string",    "profileSlug": "string",    "effectiveWorkspaceId": "string",    "projectId": "string",    "source": "string",    "facets": [      {        "slug": "string",        "outcome": "string",        "facetId": "string",        "proposalId": "string",        "error": "string"      }    ],    "warnings": [      "string"    ],    "properties": {      "unmodeled": [        {          "key": "string",          "didYouMean": "string"        }      ]    }  }}
GET
/entities/{id}/connections

Authorization

bearerAuth
headerAuthorizationBearer <API key>

Pod API key (Bearer) issued via /api/hub/setup/agent or pod admin tooling. Browser/web clients may use the X-Session-Token header instead. See GET /api/hub/auth/status to introspect the calling key.

Path Parameters

id*string
Match^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
Formatuuid

Query Parameters

userId?string
workspaceId?string
limit?string

Response Body

Relations list with summary counts

application/json
  1. response
[key: string]?unknown
Example Request
curl -X GET 'https://pod.example.com/api/hub/entities/497f6eca-6276-4993-bfeb-53cbbbba6f08/connections'
Example Response
{  "property1": null,  "property2": null}

Fetch a single entity

GET
/entities/{id}

Authorization

bearerAuth
headerAuthorizationBearer <API key>

Pod API key (Bearer) issued via /api/hub/setup/agent or pod admin tooling. Browser/web clients may use the X-Session-Token header instead. See GET /api/hub/auth/status to introspect the calling key.

Path Parameters

id*string
Match^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
Formatuuid

Query Parameters

userId?string

Response Body

Canonical entity wire record

application/json
  1. response
id*string
profileSlug*string
type*string
title?string|null
preview?string|null
description?string|null
properties*
systemData?
userId*string
workspaceId?string|null
documentId?string|null
version?number
createdAt?string|
updatedAt?string|
Example Request
curl -X GET 'https://pod.example.com/api/hub/entities/497f6eca-6276-4993-bfeb-53cbbbba6f08'
Example Response
{  "id": "string",  "profileSlug": "string",  "type": "string",  "title": "string",  "preview": "string",  "description": "string",  "properties": {    "property1": null,    "property2": null  },  "systemData": {    "property1": null,    "property2": null  },  "userId": "string",  "workspaceId": "string",  "documentId": "string",  "version": 0,  "createdAt": "string",  "updatedAt": "string"}

Hybrid semantic recall

POST
/entities/recall

Authorization

bearerAuth
headerAuthorizationBearer <API key>

Pod API key (Bearer) issued via /api/hub/setup/agent or pod admin tooling. Browser/web clients may use the X-Session-Token header instead. See GET /api/hub/auth/status to introspect the calling key.

Request Body

application/json
  1. body
query*string
Length1 <= length
profileSlug?string
workspaceId?string
limit?integer
Range1 <= value <= 100

Response Body

Merged entity results

application/json
  1. response
entities*array<>
source*string
Value in"hybrid""typesense"
Example Request
curl -X POST 'https://pod.example.com/api/hub/entities/recall' \  -H 'Content-Type: application/json' \  -d '{  "query": "string"}'
Example Response
{  "entities": [    {      "property1": null,      "property2": null    }  ],  "source": "hybrid"}
POST
/entities/retrieve

Authorization

bearerAuth
headerAuthorizationBearer <API key>

Pod API key (Bearer) issued via /api/hub/setup/agent or pod admin tooling. Browser/web clients may use the X-Session-Token header instead. See GET /api/hub/auth/status to introspect the calling key.

Request Body

application/json
  1. body
query*string
Length1 <= length
workspaceId?string
limit?integer
Range1 <= value <= 100

Response Body

Ranked entities + retrieval understanding

application/json
  1. response
entities*array<>
understanding*
source*string
Value in"hybrid""typesense"
verdict*string
Value in"confident""ambiguous""empty"
Example Request
curl -X POST 'https://pod.example.com/api/hub/entities/retrieve' \  -H 'Content-Type: application/json' \  -d '{  "query": "string"}'
Example Response
{  "entities": [    {      "property1": null,      "property2": null    }  ],  "understanding": {    "property1": null,    "property2": null  },  "source": "hybrid",  "verdict": "confident"}
POST
/entities/{entityId}/attachments

Authorization

bearerAuth
headerAuthorizationBearer <API key>

Pod API key (Bearer) issued via /api/hub/setup/agent or pod admin tooling. Browser/web clients may use the X-Session-Token header instead. See GET /api/hub/auth/status to introspect the calling key.

Path Parameters

entityId*string
Match^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
Formatuuid

Request Body

application/json
  1. body
userId?string
workspaceId?string
filename?string
Length1 <= length
mimeType?string
Length1 <= length
contentBase64?string
Length1 <= length
fileEntityId?string

Response Body

Created file entity + link result

application/json
  1. response
[key: string]?unknown
Example Request
curl -X POST 'https://pod.example.com/api/hub/entities/497f6eca-6276-4993-bfeb-53cbbbba6f08/attachments' \  -H 'Content-Type: application/json' \  -d '{}'
Example Response
{  "property1": null,  "property2": null}
POST
/entities/batch-update

Authorization

bearerAuth
headerAuthorizationBearer <API key>

Pod API key (Bearer) issued via /api/hub/setup/agent or pod admin tooling. Browser/web clients may use the X-Session-Token header instead. See GET /api/hub/auth/status to introspect the calling key.

Request Body

application/json
  1. body
userId?string
agentUserId?string
Formatuuid
workspaceId?string
Formatuuid
sessionId?string
Formatuuid
items*array<>
Items1 <= items <= 500
idempotencyKey?string
validate?boolean
reasoning?string

Response Body

Per-item receipt: applied / proposed (one pack) / denied / skipped

application/json
  1. response
[key: string]?unknown
Example Request
curl -X POST 'https://pod.example.com/api/hub/entities/batch-update' \  -H 'Content-Type: application/json' \  -d '{  "items": [    {      "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",      "patch": {        "property1": null,        "property2": null      }    }  ]}'
Example Response
{  "property1": null,  "property2": null}

Update an entity

PATCH
/entities/{entityId}

Authorization

bearerAuth
headerAuthorizationBearer <API key>

Pod API key (Bearer) issued via /api/hub/setup/agent or pod admin tooling. Browser/web clients may use the X-Session-Token header instead. See GET /api/hub/auth/status to introspect the calling key.

Path Parameters

entityId*string
Match^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
Formatuuid

Request Body

application/json
  1. body
userId?string
agentUserId?string
workspaceId?string|null
title?string
preview?string
metadata?
deleteProperties?array<string>
reasoning?string

The proposing agent's rationale for this update, surfaced in the proposal inbox.

sourceMessageId?string
sessionId?string
expectedStatus?|

Compare-and-set on the status: the status you SAW. If it moved meanwhile the call answers 409 with the current status and writes nothing.

Response Body

Updated entity

application/json
  1. response
status*string
message?string
proposalId?string|null
summary?string
reasoning?string
reviewPath?string
reviewUrl?string
deferred?true

Workspace-join gate only (proposalType "join"): this write was KEPT on the join request and is replayed for you once the join is approved — do not resend it.

Value intrue
ref?string

With deferred: the kept write's ref on the join proposal.

Example Request
curl -X PATCH 'https://pod.example.com/api/hub/entities/497f6eca-6276-4993-bfeb-53cbbbba6f08' \  -H 'Content-Type: application/json' \  -d '{}'
Example Response
{  "status": "string",  "message": "string",  "proposalId": "string",  "summary": "string",  "reasoning": "string",  "reviewPath": "string",  "reviewUrl": "string",  "deferred": true,  "ref": "string"}

Delete an entity

DELETE
/entities/{entityId}

Authorization

bearerAuth
headerAuthorizationBearer <API key>

Pod API key (Bearer) issued via /api/hub/setup/agent or pod admin tooling. Browser/web clients may use the X-Session-Token header instead. See GET /api/hub/auth/status to introspect the calling key.

Path Parameters

entityId*string
Match^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
Formatuuid

Query Parameters

workspaceId?string
userId?string
reasoning?string
agentUserId?string

Response Body

Deletion result (completed or proposed)

application/json
  1. response
[key: string]?unknown
Example Request
curl -X DELETE 'https://pod.example.com/api/hub/entities/497f6eca-6276-4993-bfeb-53cbbbba6f08'
Example Response
{  "property1": null,  "property2": null}
GET
/entities/{entityId}/facets

Authorization

bearerAuth
headerAuthorizationBearer <API key>

Pod API key (Bearer) issued via /api/hub/setup/agent or pod admin tooling. Browser/web clients may use the X-Session-Token header instead. See GET /api/hub/auth/status to introspect the calling key.

Path Parameters

entityId*string
Match^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
Formatuuid

Response Body

Effective facets

application/json
  1. response
facets*array<>
Example Request
curl -X GET 'https://pod.example.com/api/hub/entities/497f6eca-6276-4993-bfeb-53cbbbba6f08/facets'
Example Response
{  "facets": [    {      "property1": null,      "property2": null    }  ]}
POST
/entities/{entityId}/facets

Authorization

bearerAuth
headerAuthorizationBearer <API key>

Pod API key (Bearer) issued via /api/hub/setup/agent or pod admin tooling. Browser/web clients may use the X-Session-Token header instead. See GET /api/hub/auth/status to introspect the calling key.

Path Parameters

entityId*string
Match^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
Formatuuid

Request Body

application/json
  1. body
userId?string
profileSlug?string
profileId?string
Formatuuid
workspaceId?|
Formatuuid
contextEntityId?|
Formatuuid
status?string
properties?
agentUserId?string
Formatuuid
reasoning?string

Response Body

Attach result (or proposal envelope)

application/json
  1. response
[key: string]?unknown
Example Request
curl -X POST 'https://pod.example.com/api/hub/entities/497f6eca-6276-4993-bfeb-53cbbbba6f08/facets' \  -H 'Content-Type: application/json' \  -d '{}'
Example Response
{  "property1": null,  "property2": null}
PATCH
/facets/{facetId}

Authorization

bearerAuth
headerAuthorizationBearer <API key>

Pod API key (Bearer) issued via /api/hub/setup/agent or pod admin tooling. Browser/web clients may use the X-Session-Token header instead. See GET /api/hub/auth/status to introspect the calling key.

Path Parameters

facetId*string
Match^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
Formatuuid

Request Body

application/json
  1. body
userId?string
status?string
properties?
workspaceId?|
Formatuuid
agentUserId?string
Formatuuid
reasoning?string
expectedStatus?|

Compare-and-set on the role's status: the status you SAW. If it moved meanwhile the call answers 409 with the current status and writes nothing.

Response Body

Update result (or proposal envelope)

application/json
  1. response
[key: string]?unknown
Example Request
curl -X PATCH 'https://pod.example.com/api/hub/facets/497f6eca-6276-4993-bfeb-53cbbbba6f08' \  -H 'Content-Type: application/json' \  -d '{}'
Example Response
{  "property1": null,  "property2": null}
DELETE
/facets/{facetId}

Authorization

bearerAuth
headerAuthorizationBearer <API key>

Pod API key (Bearer) issued via /api/hub/setup/agent or pod admin tooling. Browser/web clients may use the X-Session-Token header instead. See GET /api/hub/auth/status to introspect the calling key.

Path Parameters

facetId*string
Match^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
Formatuuid

Query Parameters

userId?string
agentUserId?string
reasoning?string

Response Body

Detach result (or proposal envelope)

application/json
  1. response
[key: string]?unknown
Example Request
curl -X DELETE 'https://pod.example.com/api/hub/facets/497f6eca-6276-4993-bfeb-53cbbbba6f08'
Example Response
{  "property1": null,  "property2": null}
POST
/identity/resolve

Authorization

bearerAuth
headerAuthorizationBearer <API key>

Pod API key (Bearer) issued via /api/hub/setup/agent or pod admin tooling. Browser/web clients may use the X-Session-Token header instead. See GET /api/hub/auth/status to introspect the calling key.

Request Body

application/json
  1. body
kindSlug?string

The entity kind (profile slug) being created. A weak (same-name) match must be this kind to become match:'weak'; cross-kind rows still ride in candidates.

title?string

Name/title to weak-match. Blank → strong-signal lookup only.

workspaceId?string
Formatuuid
signals?

Explicit strong identity atoms.

properties?

Draft property bag — strong signals are also extracted from it (merged with signals).

Response Body

Identity resolution result

application/json
  1. response
match*string
Value in"strong""weak""none"
entityId?string
entityTitle?string|null
entityKind?string
candidates*array<>
crossKindCandidates*array<>

Same-title entities of a DIFFERENT kind. ADVISORY: match stays "none" because a title alone never auto-merges — but do NOT treat "none" as "safe to create" when this is non-empty. Prefer proposing a link to one of these over minting a duplicate.

pendingCandidates?array<>

Strong-signal matches in YOUR OWN pending capture proposals — a duplicate already in-flight but not yet committed (so match is still "none"). ADVISORY: carries a proposalId, NEVER an entityId — do NOT link to it (it can still be rejected). Wait for review or revise, rather than filing a second copy.

Example Request
curl -X POST 'https://pod.example.com/api/hub/identity/resolve' \  -H 'Content-Type: application/json' \  -d '{}'
Example Response
{  "match": "strong",  "entityId": "string",  "entityTitle": "string",  "entityKind": "string",  "candidates": [    {      "entityId": "string",      "title": "string",      "kind": "string",      "score": 0    }  ],  "crossKindCandidates": [    {      "entityId": "string",      "title": "string",      "kind": "string"    }  ],  "pendingCandidates": [    {      "proposalId": "string",      "proposalType": "string",      "summary": "string",      "entityRef": "string",      "entityTitle": "string",      "profileSlug": "string",      "matchedSignals": [        {          "type": "string",          "value": "string"        }      ]    }  ]}