Users & Platform Users#
Outeract uses a two-level user model to handle identity across multiple messaging platforms. Understanding this model is key to building effective multi-platform experiences.
The Two-Level Model#
flowchart TB
subgraph User["User (Abstract Identity)"]
PU1["Platform User<br/>(WhatsApp)<br/>+1234567890"]
PU2["Platform User<br/>(Telegram)<br/>@john_doe"]
PU3["Platform User<br/>(Slack)<br/>U12345678"]
endUser#
The abstract identity that represents a person across all platforms. A User can have multiple Platform Users attached.
Platform User#
The concrete identity on a specific platform. Contains the platform-specific identifier (external_id) and is linked to a Platform Connection.
User Model#
| Field | Type | Description |
|---|---|---|
id | UUID | Unique identifier |
app_id | UUID | Application this user belongs to |
config.name | string | User’s display name |
config.system_user | boolean | Whether this is a system/bot user |
created_at | datetime | When the user was created |
System Users#
System users represent your application or bot in conversations. They’re automatically created for each platform connection and used as the “sender” for outbound messages.
query {
users(configFilters: [{ field: "system_user", value: "true" }]) {
edges {
node {
id
name
identities {
externalId
platformConnection {
id
platformName
}
}
}
}
}
}Platform User Model#
| Field | Type | Description |
|---|---|---|
id | UUID | Unique identifier |
user_id | UUID | Parent User |
platform_connection_id | UUID | Platform connection |
external_id | string | Platform-specific identifier |
config.name | string | Platform-specific display name |
config.profile_picture_url | string | Profile picture URL |
created_at | datetime | When created |
External IDs by Platform#
| Platform | External ID Format | Example |
|---|---|---|
| Phone number (E.164) | +14155551234 | |
| Instagram Scoped User ID | 123456789012345 | |
| Telegram | Telegram User ID | 123456789 |
| Slack | Slack User ID | U0123456789 |
| Discord | Discord User ID | 123456789012345678 |
| SMS | Phone number (E.164) | +14155551234 |
| Email address | user@example.com |
Auto-Creation#
Platform users and users are automatically created when:
- Inbound message - A new person messages you
- Outbound message - You message a new recipient
- Link code activation - A user links their identity
flowchart TB
A["Incoming WhatsApp message<br/>from +14155551234"] --> B{"Check: Does Identity exist?<br/>external_id='+14155551234'<br/>platform_connection_id=whatsapp_conn"}
B -->|Exists| C["Use existing<br/>Identity"]
B -->|Not Found| D["Create new User<br/>Create new Identity"]Querying Users#
Get All Users#
query {
users(first: 20) {
edges {
node {
id
name
isSystemUser
identities {
id
externalId
platformConnection {
platformName
}
}
}
}
pageInfo {
hasNextPage
endCursor
}
}
}Find User by Platform Identity#
Look up the identity (the identities query returns a flat list) and read its
parent user:
query {
identities(externalIds: ["+14155551234"]) {
id
externalId
user {
id
name
}
}
}Filter by Config Fields#
Use configFilters to filter users by any config_data field. Each filter is a { field, value } pair that performs an exact match against the JSON-extracted text. Multiple filters are combined with AND.
# Only system users
query {
users(configFilters: [{ field: "system_user", value: "true" }]) {
edges { node { id name } }
}
}
# Combine with search
query {
users(
search: "John"
configFilters: [{ field: "system_user", value: "false" }]
) {
edges { node { id name } }
}
}Boolean config values like system_user are stored as JSON true/false, so use "true" or "false" as the string value.
Get Identities for a User#
The identities query returns a flat list. Filter by userId (or
externalIds / identityType):
query {
identities(userId: "user_abc123") {
id
externalId
name
platformConnection {
id
platformName
}
}
}Creating Users#
Create a User Manually#
mutation {
createUser(
name: "John Doe"
) {
id
name
}
}Create a Platform User#
mutation {
createIdentity(
userId: "user_abc123"
platformConnectionId: "pc_xyz789"
externalId: "+14155551234"
name: "John's WhatsApp"
) {
id
externalId
user {
id
name
}
}
}Linking Users Across Platforms#
When you have a User with multiple Platform Users, you can message them on any platform:
# User has both WhatsApp and Slack identities
query {
users(userIds: ["user_abc123"]) {
edges {
node {
id
name
identities {
id
externalId
platformConnection {
id
platformName
}
}
}
}
}
}
# The matching user node
{
"id": "user_abc123",
"name": "John Doe",
"identities": [
{
"id": "pu_whatsapp",
"externalId": "+14155551234",
"platformConnection": {
"id": "pc_whatsapp",
"platformName": "WHATSAPP"
}
},
{
"id": "pu_slack",
"externalId": "U12345678",
"platformConnection": {
"id": "pc_slack",
"platformName": "SLACK"
}
}
]
}Using Link Codes#
Link codes allow users to connect their identities across platforms:
flowchart LR
A["User messages<br/>your bot on WhatsApp"] --> B["You generate<br/>a link code"]
B --> C["User enters the code<br/>in your Slack bot"]
C --> D["Outeract links both<br/>identities to one User"]Merging Users#
If you discover two Users are actually the same person, transfer all identities
from the duplicate onto the User you want to keep with transferIdentities
(this is the mutation behind the console’s Merge Users dialog):
mutation {
transferIdentities(
sourceUserId: "user_to_merge"
targetUserId: "user_to_keep"
deleteSourceUser: true
deleteSourceConversations: true
) {
id
identities {
id
externalId
}
}
}This:
- Moves all identities from source to target
- Re-assigns edges (sent_by, sent_to) to target
- Deletes the source User (when
deleteSourceUser: true) - Creates a
user.mergedevent
User in Events#
Messages link to their sender and recipient via sent_by / sent_to edges,
which target identities. Read the edge, then resolve the identity:
query {
event(id: "evt_abc123") {
id
eventType
edges {
edgeType # "sent_by" / "sent_to"
targetNodeType # "identity"
targetNodeId # resolve with identities(identityIds:)
}
}
}Typical edges:
sent_by- Who sent the messagesent_to- Who received the message
Best Practices#
1. Don’t Create Users Prematurely#
Let Outeract auto-create users from messages. This ensures proper linking.
2. Use Link Codes for Cross-Platform Identity#
Don’t manually merge users. Use link codes for user-initiated linking.
3. Store Your Own User IDs#
Map Outeract User IDs to your internal user database.
4. Handle Platform User Changes#
Users might change phone numbers or usernames. Track changes via events.
5. Respect Privacy#
Users on different platforms may not want their identities linked. Always get consent.
Related Concepts#
- Events - How users relate to events
- Edges - User relationships in the graph
- Link Codes - Cross-platform identity linking