GetUserForCallHandler

What It Does

When CallCore receives a call segment from the phone system, it needs to link the call's handler (a PhoneCallHandler__c record) to a Salesforce User. This link is what enables the timeline to show only calls handled by users the running user is permitted to see, and what drives the "own call" permission checks.

The default implementation matches the handler to a User by email address. If the email sent by the phone system matches exactly one active Salesforce User, that user is linked. If there is no match or the match is ambiguous, the handler is left unlinked.

Implement GetUserForCallHandler.IStrategy to change how this matching works — for example, to match by extension number, by a custom external ID field on User, or to integrate with a directory service.

Interface

global interface IStrategy {
    void tryGet(Context context);
}

Context

Input Fields

All input fields are global final — they are set when the call segment arrives and cannot be changed by the strategy.

Field Type Description
email String Email address of the call handler as reported by the phone system. May be null or blank.
displayName String Cleaned/normalised display name. May be null or blank.
rawDisplayName String Display name exactly as received from the phone system. May be null or blank.
extensionNumber Integer Extension number as reported by the phone system. May be null.
callcoreWorkspaceSubdomain String The CallCore workspace subdomain the event originated from. Useful for multi-tenant setups.

Write Methods

Call exactly one of these before returning:

Method Description
found(Id userId) Records the Salesforce User ID that was matched. userId must not be null.
notFound() Records that no match was found. The handler remains unlinked.

If you call neither method, the result is the same as notFound().

Discriminator

This strategy has no SObject discriminator. There is one global implementation per org. Use default as the discriminator value.

Default Behaviour

Matches by email address. Queries User WHERE IsActive = true AND Email = :email. If exactly one user is found, calls context.found(userId). If zero or more than one user is found, calls nothing (effectively notFound()).

Example: Match by Extension Number

If your phone system identifies users by extension and you store extensions on a custom field PhoneExtension__c on the User object:

public class GetUserForCallHandlerByExtension
    implements callcoreio.GetUserForCallHandler.IStrategy {

    public void tryGet(callcoreio.GetUserForCallHandler.Context context) {
        if (context.extensionNumber == null) {
            context.notFound();
            return;
        }

        String ext = String.valueOf(context.extensionNumber);

        List<User> users = [
            SELECT Id
            FROM User
            WHERE IsActive = true
              AND PhoneExtension__c = :ext
            LIMIT 2
        ];

        if (users.size() == 1) {
            context.found(users[0].Id);
        } else {
            context.notFound();
        }
    }
}

Register with:

  • Strategy Type: GetUserForCallHandler
  • Discriminator: default
  • Apex Class Name: GetUserForCallHandlerByExtension

Example: Try Extension First, Fall Back to Email

public class GetUserForCallHandlerByExtensionOrEmail
    implements callcoreio.GetUserForCallHandler.IStrategy {

    public void tryGet(callcoreio.GetUserForCallHandler.Context context) {
        if (context.extensionNumber != null) {
            String ext = String.valueOf(context.extensionNumber);
            List<User> users = [
                SELECT Id FROM User
                WHERE IsActive = true AND PhoneExtension__c = :ext
                LIMIT 2
            ];
            if (users.size() == 1) {
                context.found(users[0].Id);
                return;
            }
        }

        if (!String.isBlank(context.email)) {
            String email = context.email.trim();
            List<User> users = [
                SELECT Id FROM User
                WHERE IsActive = true AND Email = :email
                LIMIT 2
            ];
            if (users.size() == 1) {
                context.found(users[0].Id);
                return;
            }
        }

        context.notFound();
    }
}

Notes

  • Always query with LIMIT 2 when checking for uniqueness. This lets you distinguish "no match" from "ambiguous match" without scanning the full User table.
  • Avoid linking a handler to a user when the match is ambiguous — an incorrect link will cause calls to appear for the wrong user. Calling notFound() is safer than guessing.
  • This strategy is invoked once per handler per sync event, not on every timeline load. The result is stored on PhoneCallHandler__c.User__c. If you change your matching logic, existing handlers already linked to users are not automatically re-evaluated.
  • The callcoreWorkspaceSubdomain field is useful if your org handles calls from multiple CallCore workspaces with different user populations.