9.3 Common SailPoint API Objects and Methods

Key Takeaways

  • SailPointContext provides getObjectByName, getObjectById, getObject, search, countObjects, saveObject, removeObject, commitTransaction, decache, runRule, and sendEmailNotification.

  • context.search with a property list runs a projection query that returns Object[] rows instead of full objects, which is far more efficient for large result sets.

  • Filter builds criteria (eq, ne, like, in, and, or, isnull), and QueryOptions carries filters, ordering, distinct, and result limits.

  • Changes made through saveObject are not permanent until commitTransaction is called, and decache frees memory in long loops.

  • Access changes should go through a ProvisioningPlan (AccountRequest plus AttributeRequest) run by the Provisioner or a workflow, not direct edits to Link objects.

Last updated: September 2026

Common SailPoint API Objects and Methods

Objective 4.7 asks you to know the common SailPoint API objects and methods and how to leverage them. The full API is in the javadocs that ship with every installation, under identityiq_home/doc/javadoc. Exam items focus on a small, high-use set.

SailPointContext: The Gateway

Every rule receives context, a sailpoint.api.SailPointContext:

MethodUse
getObjectByName(Class, name) / getObjectById(Class, id) / getObject(Class, idOrName)Fetch one object
getObjects(Class, QueryOptions)Fetch a List (fine for small sets)
search(Class, QueryOptions)Stream matches as an Iterator of objects
search(Class, QueryOptions, List props)Projection query: an Iterator of Object[] rows holding only the listed properties
countObjects(Class, QueryOptions)Count without loading
saveObject(obj) / removeObject(obj)Stage a save or delete
commitTransaction() / rollbackTransaction()Make staged changes permanent, or undo them
decache() / decache(obj)Clear loaded objects from the Hibernate session to free memory
runRule(rule, Map args)Run another rule
sendEmailNotification(EmailTemplate, EmailOptions)Send an email from code
encrypt(String) / decrypt(String)Use the IdentityIQ keystore

Querying With Filter and QueryOptions

import sailpoint.object.*;
import java.util.*;

QueryOptions qo = new QueryOptions();
qo.addFilter(Filter.and(
    Filter.eq("department", "Finance"),
    Filter.eq("inactive", false)));
qo.addOrdering("lastname", true);
qo.setResultLimit(500);

List props = Arrays.asList(new String[] {"name", "email", "manager.name"});
Iterator it = context.search(Identity.class, qo, props);
while (it.hasNext()) {
    Object[] row = (Object[]) it.next();
    String name = (String) row[0];
    // ... row[1] is the email, row[2] the manager's name
}
  • Filter static methods include eq, ne, like (with a match mode), in, and, or, not, isnull, notnull, and ignoreCase. Filter.compile("department == \"Finance\"") parses a filter string, the same syntax the Advanced Search "view/edit filter source" link shows.
  • QueryOptions carries filters plus addOrdering, setDistinct, setResultLimit, and setFirstRow. The documentation's own form examples build a distinct, ordered projection query to fill a drop-down.
  • Dot notation reaches related objects: manager.name, application.name, identity.department.
  • Only searchable attributes (extended columns, section 2.3) can be used in database filters. Non-searchable attributes are stored in XML and cannot be queried efficiently.

The Core Object Classes

ClassRepresentsFrequently used methods
IdentityAn identity cubegetName, getDisplayName, getFirstname, getEmail, getManager, getAttribute(name), setAttribute, getLinks, getAssignedRoles, getDetectedRoles, isInactive, getCapabilities, getWorkgroups
LinkAn accountgetNativeIdentity, getApplication, getApplicationName, getAttribute, getIdentity, getDisplayName
ApplicationAn application definitiongetName, getConnector, getAttributeValue, getSchema("account"), isAuthoritative
BundleA rolegetName, getType, getRequirements, getPermits, getInheritance, getProfiles
ManagedAttributeAn entitlement or account group in the cataloggetApplication, getAttribute, getValue, getDisplayName, getOwner, isRequestable
ResourceObjectAccount or group data from a connector during aggregationgetIdentity, getAttribute, put
CustomA key/value configuration objectget(key), put(key, value) for lookup tables used by rules

Changing Access: ProvisioningPlan

Do not edit Link attributes directly to grant access. Describe the change as a ProvisioningPlan and let IdentityIQ provision it, audit it, and keep the identity cube consistent:

import sailpoint.object.ProvisioningPlan;
import sailpoint.object.ProvisioningPlan.AccountRequest;
import sailpoint.object.ProvisioningPlan.AttributeRequest;
import sailpoint.api.Provisioner;

ProvisioningPlan plan = new ProvisioningPlan();
plan.setIdentity(identity);
AccountRequest acct = new AccountRequest(AccountRequest.Operation.Modify,
        "Active Directory", null, link.getNativeIdentity());
acct.add(new AttributeRequest("memberOf", ProvisioningPlan.Operation.Add,
        "CN=Finance,OU=Groups,DC=acme,DC=com"));
plan.add(acct);
Provisioner p = new Provisioner(context);
p.execute(plan);

AccountRequest operations include Create, Modify, Delete, Enable, Disable, Unlock, and Lock. AttributeRequest operations are Add, Remove, and Set. In a workflow, pass the plan to LCM Provisioning or a provisioning subprocess instead, so that approvals and policy checks still apply.

Helper Classes

  • sailpoint.api.IdentityService – for example, getLinks(identity, application) finds an identity's accounts on one application.
  • sailpoint.api.ObjectUtil – utilities such as convertIdsToNames(context, Application.class, ids), used in the documentation's report examples, plus locking helpers for safely updating identities.
  • sailpoint.tools.Util – null-safe helpers such as isNullOrEmpty, isNotNullOrEmpty, otos (object to string), otob (to boolean), and CSV/list conversions.

Two Everyday Recipes

Lookup tables in a Custom object. Instead of hard-coding a department-to-cost-center map in five rules, store it once:

import sailpoint.object.Custom;
Custom map = context.getObjectByName(Custom.class, "ACME Cost Centers");
String cc = (map != null) ? (String) map.get(department) : null;

Sending an email from code. The documentation's Test Email Sending rule shows the pattern: load an EmailTemplate by name, take a copy with deepCopy(context), put template arguments in a Map, wrap the recipient and arguments in EmailOptions, then call context.sendEmailNotification(template, options). With email redirection on (section 3.4), this is a safe way to test templates.

Patterns That Separate Good Code From Outages

  1. Iterate, do not load everything. Use search (ideally a projection) instead of getObjects for large sets.
  2. Commit, then decache, in loops. Call commitTransaction() after saves and decache() every few hundred objects, so the session does not grow until memory runs out.
  3. Never commit inside certain rules unless required. Aggregation, correlation, and customization rules run inside IdentityIQ's own transaction. Return data and let the engine save it.
  4. Handle nulls. Attributes and managers may be missing, so use Util helpers.
  5. Log at debug, not println. The documentation suggests println only for temporary debugging, removed before production. Prefer a named logger (section 14.1).
Test Your Knowledge

A rule must list the names and emails of 200,000 identities without loading full Identity objects into memory. Which call is most appropriate?

A

context.getObjects(Identity.class, qo)

B

context.search(Identity.class, qo, Arrays.asList("name","email")) and read Object[] rows

C

context.getObjectByName(Identity.class, "*")

D

context.countObjects(Identity.class, qo)

Test Your Knowledge

A custom task updates an attribute on thousands of identities with context.saveObject(). After it finishes, none of the changes are visible. What is missing?

A

A call to context.decache() before each save

B

Running the task with partitioning enabled

C

Marking the attribute Searchable

D

A call to context.commitTransaction() so staged saves become permanent

Test Your Knowledge

What is the recommended way for custom code to add a user to an Active Directory group?

A

Build a ProvisioningPlan with an AccountRequest (Modify) and an AttributeRequest (Add) and run it through the Provisioner or a provisioning workflow.

B

Set the memberOf attribute on the Link object and call saveObject.

C

Edit the Application object's schema to include the group.

D

Insert a row into spt_link with SQL.

Test Your Knowledge

Which QueryOptions and Filter combination returns active Finance identities ordered by last name?

A

QueryOptions.setDistinct(true) with Filter.like("Finance")

B

Filter.or(Filter.eq("department","Finance"), Filter.eq("inactive", false)) with setResultLimit(0)

C

Filter.and(Filter.eq("department","Finance"), Filter.eq("inactive", false)) plus addOrdering("lastname", true)

D

Filter.compile("Finance") with setFirstRow(1)

Sections you finish are checked off in the contents.