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

User#

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#

FieldTypeDescription
idUUIDUnique identifier
app_idUUIDApplication this user belongs to
config.namestringUser’s display name
config.system_userbooleanWhether this is a system/bot user
created_atdatetimeWhen 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#

FieldTypeDescription
idUUIDUnique identifier
user_idUUIDParent User
platform_connection_idUUIDPlatform connection
external_idstringPlatform-specific identifier
config.namestringPlatform-specific display name
config.profile_picture_urlstringProfile picture URL
created_atdatetimeWhen created

External IDs by Platform#

PlatformExternal ID FormatExample
WhatsAppPhone number (E.164)+14155551234
InstagramInstagram Scoped User ID123456789012345
TelegramTelegram User ID123456789
SlackSlack User IDU0123456789
DiscordDiscord User ID123456789012345678
SMSPhone number (E.164)+14155551234
EmailEmail addressuser@example.com

Auto-Creation#

Platform users and users are automatically created when:

  1. Inbound message - A new person messages you
  2. Outbound message - You message a new recipient
  3. 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"
        }
      }
    ]
}

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

Learn more about Link Codes →

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

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 message
  • sent_to - Who received the message

Best Practices#

1. Don’t Create Users Prematurely#

Let Outeract auto-create users from messages. This ensures proper linking.

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.

  • Events - How users relate to events
  • Edges - User relationships in the graph
  • Link Codes - Cross-platform identity linking