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 act_. 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 act_
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 (agt_ 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.

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, type, 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. When combined with external_id (idempotent creation), the memory is only created the first time — 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 act_ 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

API keys are automatically scoped to a single project — project_id is inferred from the key and must not be supplied in the request body. JWT callers must supply project_id explicitly for write operations.

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:act_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 \
--type customer

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 --type customer

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"
}
}
}
]
}