CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-langfuse--langfuse-java

Java client for the Langfuse API providing access to observability and analytics features for LLM applications

Overview
Eval results
Files

scim.mddocs/

SCIM User Provisioning

The SCIM API provides SCIM 2.0-compliant user provisioning for automated organization user management. All operations require an organization-scoped API key.

Capabilities

ScimClient

Client for SCIM 2.0 user provisioning operations.

/**
 * Get SCIM Service Provider Configuration
 * Requires organization-scoped API key
 *
 * @param requestOptions Optional request configuration
 */
ServiceProviderConfig getServiceProviderConfig();
ServiceProviderConfig getServiceProviderConfig(RequestOptions requestOptions);

/**
 * Get SCIM Resource Types
 * Requires organization-scoped API key
 *
 * @param requestOptions Optional request configuration
 */
ResourceTypesResponse getResourceTypes();
ResourceTypesResponse getResourceTypes(RequestOptions requestOptions);

/**
 * Get SCIM Schemas
 * Requires organization-scoped API key
 *
 * @param requestOptions Optional request configuration
 */
SchemasResponse getSchemas();
SchemasResponse getSchemas(RequestOptions requestOptions);

/**
 * List users in the organization
 * Requires organization-scoped API key
 *
 * @param request Optional filters (filter query, startIndex, count)
 * @param requestOptions Optional request configuration
 */
ScimUsersListResponse listUsers();
ScimUsersListResponse listUsers(ListUsersRequest request);
ScimUsersListResponse listUsers(ListUsersRequest request, RequestOptions requestOptions);

/**
 * Create a new user in the organization
 * Requires organization-scoped API key
 *
 * @param request User definition
 * @param requestOptions Optional request configuration
 */
ScimUser createUser(CreateUserRequest request);
ScimUser createUser(CreateUserRequest request, RequestOptions requestOptions);

/**
 * Get a specific user by ID
 * Requires organization-scoped API key
 *
 * @param userId User ID
 * @param requestOptions Optional request configuration
 */
ScimUser getUser(String userId);
ScimUser getUser(String userId, RequestOptions requestOptions);

/**
 * Remove a user from the organization
 * Note: Does not delete the user entity, only removes from organization
 * Requires organization-scoped API key
 *
 * @param userId User ID
 * @param requestOptions Optional request configuration
 */
EmptyResponse deleteUser(String userId);
EmptyResponse deleteUser(String userId, RequestOptions requestOptions);

Usage Examples:

import com.langfuse.client.LangfuseClient;
import com.langfuse.client.resources.scim.types.*;
import java.util.List;

// Use organization-scoped API key
LangfuseClient client = LangfuseClient.builder()
    .url("https://cloud.langfuse.com")
    .credentials("org-pk-...", "org-sk-...")
    .build();

// Get service provider config
ServiceProviderConfig config = client.scim().getServiceProviderConfig();
System.out.println("Documentation: " + config.getDocumentationUri());

// List all users
ScimUsersListResponse users = client.scim().listUsers();
for (ScimUser user : users.getResources()) {
    System.out.println(user.getUserName() + " - " + user.getDisplayName());
}

// Filter users
ListUsersRequest filterRequest = ListUsersRequest.builder()
    .filter("userName eq \"user@example.com\"")
    .build();

ScimUsersListResponse filtered = client.scim().listUsers(filterRequest);

// Create a user
CreateUserRequest userRequest = CreateUserRequest.builder()
    .userName("newuser@example.com")
    .emails(List.of(
        ScimEmail.builder()
            .value("newuser@example.com")
            .type("work")
            .primary(true)
            .build()
    ))
    .name(ScimName.builder()
        .givenName("John")
        .familyName("Doe")
        .formatted("John Doe")
        .build())
    .displayName("John Doe")
    .active(true)
    .build();

ScimUser newUser = client.scim().createUser(userRequest);
System.out.println("Created user: " + newUser.getId());

// Get a specific user
ScimUser user = client.scim().getUser(newUser.getId());

// Remove user from organization
EmptyResponse deleteResp = client.scim().deleteUser(newUser.getId());

Request Types

ListUsersRequest

/**
 * Request parameters for listing users
 */
public final class ListUsersRequest {
    Optional<String> getFilter();      // SCIM filter query
    Optional<Integer> getStartIndex(); // 1-based index (default: 1)
    Optional<Integer> getCount();      // Items per page (default: 100)

    static Builder builder();
}

CreateUserRequest

/**
 * Request for creating a user
 */
public final class CreateUserRequest {
    String getUserName();                 // Username (usually email)
    List<ScimEmail> getEmails();          // Email addresses
    Optional<ScimName> getName();         // Name components
    Optional<String> getDisplayName();    // Display name
    Optional<Boolean> getActive();        // Active status

    static Builder builder();
}

Response Types

ServiceProviderConfig

/**
 * SCIM Service Provider Configuration
 */
public final class ServiceProviderConfig {
    String getDocumentationUri();
    ScimFeatureSupport getPatch();
    BulkConfig getBulk();
    FilterConfig getFilter();
    ScimFeatureSupport getChangePassword();
    ScimFeatureSupport getSort();
    ScimFeatureSupport getEtag();
    List<AuthenticationScheme> getAuthenticationSchemes();

    static Builder builder();
}

ResourceTypesResponse

/**
 * SCIM Resource Types
 */
public final class ResourceTypesResponse {
    List<ResourceType> getResources();

    static Builder builder();
}

SchemasResponse

/**
 * SCIM Schemas
 */
public final class SchemasResponse {
    List<SchemaResource> getResources();

    static Builder builder();
}

ScimUsersListResponse

/**
 * Paginated list of SCIM users
 */
public final class ScimUsersListResponse {
    List<ScimUser> getResources();
    int getTotalResults();
    int getStartIndex();
    int getItemsPerPage();

    static Builder builder();
}

ScimUser

/**
 * SCIM user resource
 */
public final class ScimUser {
    String getId();
    String getUserName();
    List<ScimEmail> getEmails();
    Optional<ScimName> getName();
    Optional<String> getDisplayName();
    Optional<Boolean> getActive();
    Optional<UserMeta> getMeta();

    static Builder builder();
}

EmptyResponse

/**
 * Empty response for delete operations
 */
public final class EmptyResponse {
    static Builder builder();
}

Supporting Types

ScimEmail

/**
 * Email address with type and primary flag
 */
public final class ScimEmail {
    String getValue();              // Email address
    Optional<String> getType();     // "work", "home", etc.
    Optional<Boolean> getPrimary(); // Primary email flag

    static Builder builder();
}

ScimName

/**
 * User name components
 */
public final class ScimName {
    Optional<String> getFormatted();    // Full formatted name
    Optional<String> getFamilyName();   // Last name
    Optional<String> getGivenName();    // First name

    static Builder builder();
}

UserMeta

/**
 * User metadata
 */
public final class UserMeta {
    String getResourceType();
    Optional<String> getCreated();       // ISO 8601 timestamp
    Optional<String> getLastModified();  // ISO 8601 timestamp

    static Builder builder();
}

ScimFeatureSupport

/**
 * Feature support indicator
 */
public final class ScimFeatureSupport {
    boolean getSupported();

    static Builder builder();
}

BulkConfig

/**
 * Bulk operation configuration
 */
public final class BulkConfig {
    boolean getSupported();
    int getMaxOperations();
    int getMaxPayloadSize();

    static Builder builder();
}

FilterConfig

/**
 * Filter configuration
 */
public final class FilterConfig {
    boolean getSupported();
    int getMaxResults();

    static Builder builder();
}

AuthenticationScheme

/**
 * Authentication scheme details
 */
public final class AuthenticationScheme {
    String getType();
    String getName();
    String getDescription();
    Optional<String> getDocumentationUri();

    static Builder builder();
}

ResourceType

/**
 * SCIM resource type definition
 */
public final class ResourceType {
    String getId();
    String getName();
    String getDescription();
    String getEndpoint();
    String getSchema();
    Optional<List<SchemaExtension>> getSchemaExtensions();

    static Builder builder();
}

SchemaResource

/**
 * SCIM schema resource definition
 */
public final class SchemaResource {
    String getId();
    String getName();
    String getDescription();
    Object getAttributes();  // Schema attributes definition

    static Builder builder();
}

Complete SCIM Provisioning Example

import com.langfuse.client.LangfuseClient;
import com.langfuse.client.resources.scim.types.*;
import java.util.List;

public class ScimProvisioningExample {
    public static void main(String[] args) {
        // Use organization-scoped API key
        LangfuseClient client = LangfuseClient.builder()
            .url("https://cloud.langfuse.com")
            .credentials("org-pk-...", "org-sk-...")
            .build();

        // 1. Get SCIM service provider config
        ServiceProviderConfig config = client.scim().getServiceProviderConfig();
        System.out.println("SCIM documentation: " + config.getDocumentationUri());
        System.out.println("Patch supported: " + config.getPatch().getSupported());
        System.out.println("Bulk supported: " + config.getBulk().getSupported());

        // 2. List existing users
        ScimUsersListResponse users = client.scim().listUsers();
        System.out.println("\nExisting users: " + users.getTotalResults());

        for (ScimUser user : users.getResources()) {
            System.out.println("  " + user.getUserName() +
                             " - Active: " + user.getActive().orElse(true));
        }

        // 3. Create new users from HR system
        String[] newEmployees = {
            "alice@example.com",
            "bob@example.com",
            "charlie@example.com"
        };

        for (String email : newEmployees) {
            String[] parts = email.split("@")[0].split("\\.");
            String firstName = capitalize(parts[0]);
            String lastName = parts.length > 1 ? capitalize(parts[1]) : "";

            CreateUserRequest userRequest = CreateUserRequest.builder()
                .userName(email)
                .emails(List.of(
                    ScimEmail.builder()
                        .value(email)
                        .type("work")
                        .primary(true)
                        .build()
                ))
                .name(ScimName.builder()
                    .givenName(firstName)
                    .familyName(lastName)
                    .formatted(firstName + (lastName.isEmpty() ? "" : " " + lastName))
                    .build())
                .displayName(firstName + (lastName.isEmpty() ? "" : " " + lastName))
                .active(true)
                .build();

            try {
                ScimUser created = client.scim().createUser(userRequest);
                System.out.println("Created user: " + created.getUserName() +
                                 " (ID: " + created.getId() + ")");
            } catch (Exception e) {
                System.err.println("Failed to create " + email + ": " + e.getMessage());
            }
        }

        // 4. Filter for specific user
        ListUsersRequest filterRequest = ListUsersRequest.builder()
            .filter("userName eq \"alice@example.com\"")
            .build();

        ScimUsersListResponse filtered = client.scim().listUsers(filterRequest);
        if (!filtered.getResources().isEmpty()) {
            ScimUser alice = filtered.getResources().get(0);
            System.out.println("\nFound user: " + alice.getDisplayName());
        }

        // 5. Paginate through large user list
        int startIndex = 1;
        int pageSize = 50;
        boolean hasMore = true;

        while (hasMore) {
            ListUsersRequest pageRequest = ListUsersRequest.builder()
                .startIndex(startIndex)
                .count(pageSize)
                .build();

            ScimUsersListResponse page = client.scim().listUsers(pageRequest);

            System.out.println("\nPage starting at " + startIndex +
                             ": " + page.getResources().size() + " users");

            for (ScimUser user : page.getResources()) {
                System.out.println("  " + user.getUserName());
            }

            hasMore = startIndex + pageSize <= page.getTotalResults();
            startIndex += pageSize;
        }

        // 6. Deactivate users (remove from organization)
        // Note: This doesn't delete the user, just removes from organization
        ListUsersRequest inactiveRequest = ListUsersRequest.builder()
            .filter("active eq false")
            .build();

        ScimUsersListResponse inactive = client.scim().listUsers(inactiveRequest);

        for (ScimUser user : inactive.getResources()) {
            client.scim().deleteUser(user.getId());
            System.out.println("Removed user from organization: " + user.getUserName());
        }
    }

    private static String capitalize(String str) {
        if (str == null || str.isEmpty()) return str;
        return str.substring(0, 1).toUpperCase() + str.substring(1).toLowerCase();
    }
}

SCIM Filter Examples

Filter by Username

ListUsersRequest request = ListUsersRequest.builder()
    .filter("userName eq \"user@example.com\"")
    .build();

Filter by Active Status

ListUsersRequest request = ListUsersRequest.builder()
    .filter("active eq true")
    .build();

Filter by Email

ListUsersRequest request = ListUsersRequest.builder()
    .filter("emails.value eq \"user@example.com\"")
    .build();

Complex Filters

// AND operator
String filter = "active eq true and userName co \"@example.com\"";

// OR operator
String filter = "userName eq \"user1@example.com\" or userName eq \"user2@example.com\"";

ListUsersRequest request = ListUsersRequest.builder()
    .filter(filter)
    .build();

Best Practices

  1. Organization Keys Required: All SCIM operations require organization-scoped API keys
  2. Pagination: Use pagination for large user lists
  3. Filter Queries: Use SCIM filter syntax for efficient queries
  4. Bulk Operations: Check bulk support in service provider config
  5. Error Handling: Handle duplicate username errors gracefully
  6. Active Status: Track user active status instead of deleting
  7. Email Uniqueness: Usernames and primary emails must be unique
  8. Integration Testing: Test SCIM flows in development environment first

SCIM 2.0 Compliance

Langfuse implements SCIM 2.0 core specification:

  • Service Provider Configuration endpoint
  • Resource Types endpoint
  • Schemas endpoint
  • User resource CRUD operations
  • Filtering support
  • Pagination support

Related Documentation

  • Projects and Organizations - Organization management
  • Client Configuration - API key configuration

Install with Tessl CLI

npx tessl i tessl/maven-com-langfuse--langfuse-java

docs

client-configuration.md

comments-annotations.md

common-types.md

datasets.md

exceptions.md

health.md

index.md

ingestion.md

media.md

metrics.md

models.md

pagination.md

projects-organizations.md

prompts.md

scim.md

scores.md

sessions.md

traces-observations.md

tile.json