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
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.

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-actor_external_id 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). 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.

Per-Actor Memory

An actor has no memory field. Retrieval scope for a generation comes from the agent's knowledge_config and nothing else, so the platform never read a link stored on the actor — keep the actor→memory mapping in your application and pass it per call.

Create one Memory per end user, keyed however your application already keys them (the actor's external_id is the natural choice), then name it in the generate body:

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

memory_ids and memory_tags are unioned with the agent's stored config, so a per-actor memory extends the agent's shared scope rather than replacing it. If you would rather not keep a mapping table, tag the memory (tags) or name it after the external_id and look it up with GET /memories.

Memory data outlives the actor record: deleting an actor deletes nothing in any memory.

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 filters by project_id, external_id (exact match — use it to resolve an external identifier to an actor_ ID), and name (partial, case-insensitive), with limit/offset pagination in a { data, total, limit, offset } envelope.

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, managed via the tags field or the tag sub-endpoints, and matched by IAM conditions (soat:ResourceTag/<key>). Actors use the actor resource type in SRNs (srn:proj_ABC:actor:actor_123). See IAM — Tags and SRNs.

Examples

Create an actor

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

The same call is an idempotent upsert when external_id is set — 201 on first contact, 200 with the existing actor thereafter (see external_id and Idempotent Creation). For policy examples scoping access to actors (including tag conditions), see IAM — Examples.

Get an actor

soat get-actor --actor-id actor_123