Skip to main content

Actors

The Actors module represents entities — people, bots, or other participants — that interact within a project. A common use case is storing external contacts such as WhatsApp numbers, where external_id holds the phone number and correlates the actor with a record in the external system.

Overview

An Actor belongs to a project and has a display name, an optional external_id, and optional links to an Agent or Chat. Actors are identified by a public id prefixed with actor_. The internal database primary key is never returned.

See the Permissions Reference for the IAM action strings for this module.

The module covers:

  • Identity — display name and external correlation via external_id
  • Idempotent creationPOST /actors with external_id uses find-or-create semantics
  • Agent/Chat linking — an Actor can be bound to an Agent or a Chat for AI interactions
  • Instructions — per-actor system prompt overrides composed into generate calls
  • Tags — key-value metadata enabling attribute-based access control via IAM conditions

Data Model

FieldTypeRequiredDescription
idstringPublic identifier prefixed with actor_
project_idstringPublic ID of the owning project (proj_ prefix)
namestringYesDisplay name of the actor
external_idstringNoExternal identifier (e.g. WhatsApp phone number). Unique per project; null is never unique
instructionsstring | nullNoPersona-specific instructions composed into the effective system prompt for generate calls
agent_idstring | nullNoPublic ID of the linked Agent (agent_ prefix). Mutually exclusive with chat_id
chat_idstring | nullNoPublic ID of the linked Chat (chat_ prefix). Mutually exclusive with agent_id
memory_idstring | nullNoPublic ID of the linked Memory container (mem_ prefix). Stores persistent facts for this actor
tagsobjectNoKey-value string pairs used for ABAC conditions (see Tags)
created_atstringISO 8601 creation timestamp
updated_atstringISO 8601 last-updated timestamp

Key Concepts

external_id and Idempotent Creation

external_id is a free-form string for correlating an Actor with a record in an external system (e.g. a WhatsApp phone number, a CRM contact ID). It is enforced unique per project at the database level — two actors in the same project cannot share the same external_id. Across different projects the same value is allowed.

null / absent external_id is never considered a duplicate — PostgreSQL NULL semantics are preserved.

:::warning Choose this value knowing it egresses

external_id is not internal-only. Whenever a generation runs in a session bound to this actor, the value is auto-populated into tool_context and transmitted as the X-Soat-Context-ActorExternalId request header to every http and mcp tool the agent calls — including endpoints you do not control.

If the tool set includes third-party endpoints, prefer an opaque internal identifier here (and correlate to the phone number or email on your own side) rather than storing the PII directly. See the Tool Context reference.

:::

When external_id is supplied to POST /actors, the endpoint uses find-or-create semantics:

  • If no actor with that external_id exists in the project, a new actor is created and 201 Created is returned.
  • If an actor with that external_id already exists, the existing actor is returned as-is with 200 OK. None of the other request fields (name, instructions, etc.) are applied to the existing actor.

This makes actor creation safe to call repeatedly from event-driven pipelines (e.g. a new inbound WhatsApp message) without risk of duplicate actors or errors.

POST /api/v1/actors
Content-Type: application/json

{
"project_id": "proj_V1StGXR8Z5jdHi6B",
"name": "Alice",
"external_id": "+15551234567"
}
  • First call → 201 Created with the new actor.
  • Subsequent calls with the same external_id200 OK with the existing actor.

When external_id is not supplied, POST /actors always creates a new actor and returns 201 Created.

Agent and Chat Linking

An Actor can be linked to either an Agent or a Chat — not both simultaneously. These links control which AI backend handles generate calls initiated by or for the actor.

  • Set agent_id to link the actor to a specific Agent.
  • Set chat_id to link the actor to a specific Chat.
  • Pass null in a PATCH /actors/:id request to unlink either field.
  • Supplying both agent_id and chat_id in the same request returns 400 Bad Request.

Memory Linking and Auto-Creation

An Actor can be linked to a Memory container via memory_id. The memory container stores persistent facts about the actor (e.g. preferences, conversation history summaries) that can be injected into AI generation calls.

Manual linking — supply memory_id in the POST /actors or PATCH /actors/:id request body:

{ "memory_id": "mem_V1StGXR8Z5jdHi6B" }

Auto-creation — set auto_create_memory: true in the POST /actors body to automatically create a new memory container named after the actor and link it:

{
"project_id": "proj_ABC",
"name": "Alice",
"external_id": "+15551234567",
"auto_create_memory": true
}

The response will include memory_id pointing to the newly created memory.

auto_create_memory is create-if-missing, not create-always: it only creates a memory when no memory_id is supplied in the same request. If you pass an explicit memory_id, that memory is linked and auto_create_memory has no effect. When combined with external_id (idempotent creation), the memory is created at most once — repeat calls return the existing actor with its existing memory_id unchanged.

Deleting an actor does not delete its linked memory. Memory data outlives the actor record and may contain valuable information.

Once you have the actor's memory_id, use it in generation calls:

{
"knowledge_config": {
"memory_ids": ["mem_V1StGXR8Z5jdHi6B"],
"write_memory_id": "mem_V1StGXR8Z5jdHi6B"
}
}

To unlink a memory from an actor without deleting it, PATCH the actor with "memory_id": null.

Instructions

instructions is a free-form string injected into the system prompt when an AI generation is scoped to this actor. Use it to encode persona-specific context (tone, name, constraints) that should be consistent across all interactions with the actor.

Pass null to PATCH /actors/:id to clear the instructions.

Filtering

GET /actors supports the following query parameters for filtering:

ParameterDescription
project_idLimit results to a specific project (required for JWT callers in most cases)
external_idExact match — use to resolve an external identifier to an actor_ ID
namePartial, case-insensitive match against the actor's display name
limitMaximum number of results to return (default: 50)
offsetNumber of results to skip for pagination (default: 0)

The response envelope is:

{
"data": [
/* ActorRecord[] */
],
"total": 42,
"limit": 50,
"offset": 0
}

Project Scope

Project-scoped API keys make project_id optional: omit it and the request defaults to the key's project, supply a matching one and it is accepted, and supply a different project's id and the request is rejected with 403. JWT callers must supply project_id explicitly for write operations. See Implicit project id for the full rules.

Tags

Tags are key-value string pairs attached to an actor. They enable attribute-based access control (ABAC) via IAM condition keys (see IAM).

{
"tags": {
"channel": "whatsapp",
"tier": "premium"
}
}

Tags can be managed via the dedicated tag sub-endpoints:

MethodEndpointDescription
GET/api/v1/actors/:id/tagsReturn the actor's current tags
PUT/api/v1/actors/:id/tagsReplace all tags (any tags not in the body are removed)
PATCH/api/v1/actors/:id/tagsMerge tags (existing tags not in the body are kept)

All tag endpoints require actors:UpdateActor permission.

SOAT Resource Names

Actors use the actor resource type in SRNs:

soat:<project_id>:actor:<actor_id>

Example: soat:proj_ABC:actor:actor_123

Use SRN patterns in policy resource fields to scope permissions to specific actors or all actors in a project:

{
"effect": "Allow",
"action": ["actors:GetActor", "actors:ListActors"],
"resource": ["soat:proj_ABC:actor:*"]
}

Examples

Create an actor

soat create-actor \
--project-id proj_ABC \
--name Alice \
--external-id +15551234567

Idempotent actor upsert

Safe to call on every inbound message — creates the actor on first contact, returns the existing record thereafter:

soat create-actor --name Bob --external-id +15559876543

Allow a user to manage all actors in a project

{
"statement": [
{
"effect": "Allow",
"action": ["actors:*"],
"resource": ["soat:proj_ABC:actor:*"]
}
]
}

Restrict access to actors tagged with a specific channel

{
"statement": [
{
"effect": "Allow",
"action": ["actors:GetActor", "actors:ListActors"],
"resource": ["soat:proj_ABC:actor:*"],
"condition": {
"StringEquals": {
"soat:ResourceTag/channel": "whatsapp"
}
}
}
]
}