Skip to content

platform sdk cardholders and credentials

Andre Lafleur edited this page Oct 8, 2026 · 15 revisions

About cardholders and credentials

A cardholder is a person known to the access control system. A credential is an authentication token, such as a card, PIN, license plate, or mobile credential, that the cardholder presents to a reader. A cardholder group is a logical grouping of cardholders for access policy and partition assignment. The Platform SDK exposes all three as entities.

The Cardholder entity

A Cardholder carries identification, contact information, an embedded picture, a status object, and references to credentials and access rules.

Cardholder cardholder = engine.GetEntity<Cardholder>(cardholderGuid);

Console.WriteLine(
    $"{cardholder.FirstName} {cardholder.LastName} ({cardholder.EmailAddress})");
Console.WriteLine($"Status: {cardholder.Status.State}");
Console.WriteLine(
    $"Last access point: {cardholder.LastAccessPoint?.AccessPointGuid}");

A few non-obvious behaviors:

  • AccessPermissionLevel is read-only; mutate through SetAccessPermissionLevel(int). Toggle inheritance from the parent group through SetInheritAccessPermissionLevel(bool).
  • Cardholder.AccessRules is the inverse of AccessRule.Cardholders and is read-only. Manage rule membership through the rule (or by adding the cardholder to a group the rule covers), not through this collection.
  • LastAccessPoint returns a CardholderAccessPointInfo with the access-point Guid (AccessPointGuid), the timestamp (AccessPointDate), and a granted/denied flag (AccessGranted).
  • cardholder.IsMemberOf(CardholderGroup) checks group membership (including transitively through nested groups).
  • cardholder.CanSetPicture() reports whether the picture can be assigned; it depends on entity restrictions.

The Visitor entity derives from Cardholder. A cardholder configuration query can therefore return visitor entities. Use the inherited Cardholder members for shared data. Retrieve the entity as Visitor when you need visitor-specific members such as Arrival, CheckinDate, VisitDate, Departure, Escort, or Checkout.

Filtering visitors from cardholder queries

Use CardholderConfigurationQuery.VisitorsOnly to control which records a cardholder configuration query returns.

VisitorsOnly Returned records
false Cardholders only, excluding visitors
true Visitors only
null Cardholders and visitors

After the engine is connected, set VisitorsOnly before you execute the query:

CardholderConfigurationQuery query =
    (CardholderConfigurationQuery)engine.ReportManager
        .CreateReportQuery(ReportType.CardholderConfiguration);

query.VisitorsOnly = false;

QueryCompletedEventArgs result = await Task.Factory.FromAsync(
    query.BeginQuery,
    query.EndQuery,
    null);

int resultCount = result.Data.Rows.Count;

Use VisitorQuery when you need active or archived visitor records, visitor-specific fields, or visitor lifecycle operations.

Cardholder lifecycle: the Status object

Cardholder.Status returns a CardholderAccessStatus. The status is itself a small API for managing the cardholder's activation and expiration:

// Activate now and set an expiration date.
cardholder.Status.Activate(
    activationDate: DateTime.UtcNow,
    expirationDate: DateTime.UtcNow.AddYears(1));

// Or, expire the credential the first time it is used or after N days of inactivity.
cardholder.Status.ExpireWhenNotUsedInDays(30);

The supported lifecycle methods on the status object:

  • Activate(): activate immediately, no expiration set.
  • Activate(DateTime activationDate): schedule activation for a specific date.
  • Activate(DateTime activationDate, DateTime expirationDate): bound the activation window.
  • Deactivate(): deactivate immediately.
  • Deactivate(DateTime expirationDate): deactivate at a future date.
  • ExpireOnFirstUseInDays(int days): expire days days after first use.
  • ExpireWhenNotUsedInDays(int days): expire after days days without use.
  • SetExpirationToNever(): remove any expiration.

Read-only status properties: ActivationDate, ActivationType, ExpirationDate, ExpirationType, ExpirationDuration, State (a CardholderState value such as Active, Inactive, or Expired).

Cardholder groups

A CardholderGroup collects cardholders for access policy and partition assignment. Groups can nest: each group has Parents and Children collections, and access permission levels can be inherited from a parent group.

CardholderGroup group = engine.GetEntity<CardholderGroup>(groupGuid);

group.AddCardholderIntoGroup(cardholderGuid);
// ...
group.RemoveCardholderFromGroup(cardholderGuid);

Properties:

  • AccessPermissionLevel (read-only) and InheritAccessPermissionLevel (read-only). Mutate through SetAccessPermissionLevel(int level) and SetInheritAccessPermissionLevel(bool inherits).
  • EmailAddress (read+write): an address used for group notifications.
  • IsVisitorGroup (read+write): when true, the group is treated as a visitor group; visitor records are created as children of this group.
  • AccessRules (read-only): rules related to members of this group, including grant and deny rules. Inspect IsDenyingRule before interpreting a returned rule as a grant.
  • Parents, Children (read-only): nested-group navigation.

For partition assignment:

  • InsertIntoPartition(Guid partition) and RemoveFromPartition(Guid partition) can also move the group's members into or out of the partition, not only the group itself, depending on the system's automatic cardholder-move setting.
  • InsertCardholderGroupIntoPartition(Guid partition, bool applyToChildren) and RemoveCardholderGroupFromPartition(Guid partition, bool applyToChildren) apply to the group, optionally cascading to nested children.

The Credential entity

A Credential is the token a cardholder presents to a reader. A credential is assigned to at most one cardholder at a time, or it can be unassigned (CardholderGuid is Guid.Empty). It has a CredentialFormat describing how its data is encoded, and carries its own status object for activation and expiration.

Credential credential = engine.GetEntity<Credential>(credentialGuid);

Console.WriteLine($"Credential: {credential.Name}");
Console.WriteLine($"Cardholder: {credential.CardholderGuid}");
Console.WriteLine($"Format: {credential.Format?.Name}");
Console.WriteLine($"Status: {credential.Status.State}");

A CredentialType is a coarse classification: None, Keypad, UndecodedWiegand, LicensePlate, CardRequest, or RandomKeypadRequest. None is the initial state before a format is assigned; most regular card credentials carry UndecodedWiegand once formatted, and the Format describes the actual card.

Credential.HasAccess(Door.DoorSide, DateTime) and Credential.HasAccess(Floor, DateTime) return an AccessResult reporting whether the credential's configured access rights grant access at a door side or elevator floor at a given moment. Use them to validate rule setup, credential status, and schedule coverage without involving the hardware. The result reflects configuration, not the cardholder's current position in the system: where antipassback is enforced, Granted means the configuration allows access, not that the reader will grant it on the next presentation.

AccessResult reports one of four outcomes:

Value Meaning
Granted The configuration allows access at that moment.
Denied No configured rule grants access at that moment.
Error Something in the configuration actively blocks access, such as a deny rule covering the cardholder or a credential that is not active.
Inconclusive The evaluation could not reach a conclusion.

Treat Error as a denial with a configured cause, not as a failed call.

Use ChangeCredentialFormat(Guid formatGuid) to decode an existing undecoded Wiegand credential into a matching configured format; it returns false if the credential's data isn't undecoded Wiegand or no matching format is found. Use SetCredentialFormat(string formatName, byte[] rawData) only on a credential that doesn't already have a format; it returns false if the credential is already formatted.

Credential lifecycle: the Status object

Credential.Status returns a CredentialAccessStatus that mirrors the cardholder status object, with two additional methods specific to the credential lifecycle:

  • Lost() marks the credential as lost.
  • Stolen() marks the credential as stolen.

The other methods match the cardholder status: Activate(), Activate(DateTime), Activate(DateTime, DateTime), Deactivate(), Deactivate(DateTime), ExpireOnFirstUseInDays(int), ExpireWhenNotUsedInDays(int), SetExpirationToNever().

// Mark the credential as lost.
credential.Status.Lost();

// Or activate it with a one-year window.
credential.Status.Activate(
    activationDate: DateTime.UtcNow,
    expirationDate: DateTime.UtcNow.AddYears(1));

Read-only status properties: ActivationDate, ActivationType, ExpirationDate, ExpirationType, ExpirationDuration, State (a CredentialState value).

Credential formats and badge requests

CredentialFormat describes the credential's binary format: name, bit length, format ID, whether it is a custom format, and the raw bytes. format.ExtractCredentialFormatModel() returns a structured ICredentialFormatModel representation.

A CredentialBadgeRequest captures the operator's request for a credential to be issued (typically printed on a badge) before the physical credential exists. It carries a Reason, a BadgeTemplate Guid, an ActivationPeriod (Never, AfterEnrollment, or SpecificDate) with optional ActivationDate, and SendEmailWhenReady/EmailUserGuid for notification. Credential.GetCredentialRequest() returns the request attached to a credential, if any. Credential.CancelCredentialRequest() cancels a pending request.

Finding a credential by card number or format

To determine whether a card is already enrolled, or to locate the credential for a known card, query with CredentialConfigurationQuery and add a CredentialFormat describing the card to the query's UniqueIds collection. The query returns only credentials whose card data matches. Do not use it with KeypadCredentialFormat: the query does not filter on the PIN, so it returns every keypad credential in the system regardless of the value you pass.

public async Task<Credential> FindCredentialAsync(
    Engine engine, CredentialFormat format)
{
    var query = (CredentialConfigurationQuery)engine.ReportManager
        .CreateReportQuery(ReportType.CredentialConfiguration);
    query.UniqueIds.Add(format);

    QueryCompletedEventArgs results = await Task.Factory.FromAsync(
        query.BeginQuery, query.EndQuery, null);

    return results.Data.Rows
        .OfType<DataRow>()
        .Select(row => engine.GetEntity<Credential>(
            row.Field<Guid>(nameof(Guid))))
        .FirstOrDefault();
}

Standard Wiegand formats take their identifying values in the constructor. The CSN value is a long because card serial numbers extend beyond the int range.

Credential standard = await FindCredentialAsync(
    engine, new WiegandStandardCredentialFormat(facility: 12, cardId: 4567));

Credential csn = await FindCredentialAsync(
    engine, new WiegandCsn32CredentialFormat(3405691582));

WiegandH10306CredentialFormat and WiegandH10304CredentialFormat take (int facility, int cardId) like WiegandStandardCredentialFormat. WiegandCorporate1000CredentialFormat and Wiegand48BitCorporate1000CredentialFormat take (int companyId, int cardId). WiegandH10302CredentialFormat takes a single long cardId, like WiegandCsn32CredentialFormat. For each format's accepted argument ranges, see Built-in numeric credential ranges.

A custom card format is matched by its format ID and a dictionary that maps field names to values. Retrieve the format defined in the system to obtain its format ID; the field names come from the format definition.

var systemConfiguration = (SystemConfiguration)engine.GetEntity(
    SystemConfiguration.SystemConfigurationGuid);
var customFormat = systemConfiguration.CustomCardFormatService
    .GetCustomCardFormat("H2005386");

var valuesByFieldName = new Dictionary<string, string>
{
    ["Facility Number"] = "2",
    ["Account Number"] = "20",
};

Credential custom = await FindCredentialAsync(
    engine, new CustomCredentialFormat(customFormat.FormatID, valuesByFieldName));

Query for the credential before creating one to avoid duplicates.

Building a credential

ICredentialBuilder is the fluent builder for new credentials. Obtain one through Engine.EntityManager.GetCredentialBuilder(), then chain:

  • SetCardholder(Cardholder cardholder): attach the credential to a cardholder.
  • SetFormat(CredentialFormat credentialFormat): assign a format directly.
  • SetFormatFromModel(ICredentialFormatModel credentialFormatModel): assign a format derived from a structured model (useful when constructing custom formats).
  • SetCredentialRequest(CredentialBadgeRequest credentialBadgeRequest): attach a badge request.

Card data must be unique in the system. Building a credential whose card data matches an existing credential fails with an SdkException whose ErrorCode is SdkError.TransactionFailed, and the credential is not created. Test ErrorCode, because the message text is not stable. To avoid the failure during bulk imports, find the credential before creating it.

Security Center does not enforce this uniqueness for PIN or license plate credentials. Two credentials can have the same PIN, or the same license plate.

A newly built credential is active immediately and never expires. To set the activation and expiration dates, call Status.Activate(activationDate, expirationDate) on the credential that Build() returns.

Prefer the builder over Engine.CreateEntity for credentials. CreateEntity persists the credential immediately without a format, and at most one format-less credential can exist in the system: while one exists, every call that commits a credential without format data, including CreateEntity itself, fails with the same exception. When you use CreateEntity, assign Format inside a transaction, so the credential and its format are committed together.

You can assign Credential.Format only once. To enroll a replacement card, create a new credential for that card instead of assigning a new format to the existing credential.

Creating a credential in a custom card format

Construct the CustomCredentialFormat with the format ID and a dictionary of field values, then pass it to the builder. The constructor validates the field names and value ranges against the format definition, accepts decimal, hexadecimal, and UTF-8 field values as strings, and rejects unknown names or out-of-range values with descriptive exceptions.

var systemConfiguration = (SystemConfiguration)engine.GetEntity(
    SystemConfiguration.SystemConfigurationGuid);
var customFormat = systemConfiguration.CustomCardFormatService
    .GetCustomCardFormat("H2005386");

var format = new CustomCredentialFormat(
    customFormat.FormatID,
    new Dictionary<string, string>
    {
        ["Facility Number"] = "2",
        ["Account Number"] = "20",
    });

Credential credential = engine.EntityManager.GetCredentialBuilder()
    .SetName("Custom format card")
    .SetFormat(format)
    .Build();

To change the values on an existing CustomCredentialFormat instance, call SetValues. The values are positional: supply one value per non-fixed field, in the order given by UnfixedFields. RetrieveFieldFormats() returns each field's value representation.

Warning

When any field of the format is hexadecimal or UTF-8, the long overload of SetValues sets no values and only logs a warning. Use the string overload, which supports every representation.

format.SetValues("2", "20");

For mobile credentials specifically, the issuance workflow uses dedicated SDK members; see Mobile credentials.

Creating a cardholder with credentials

The end-to-end workflow to enroll a new person into the access control system: create the cardholder record, build a credential, and attach the credential to the cardholder. The result is a cardholder visible in Security Center with an active credential.

Prerequisites

  • You must be logged on with privileges to create cardholders and credentials.
  • For card-based credentials, the credential format must already exist in the system, or you must construct one.
  • For credential requests (a badge to be printed later), have the badge template guid available.

Procedure

  1. Create the cardholder entity and set the identification fields.

    var cardholder = (Cardholder)engine.CreateEntity("Jane Doe", EntityType.Cardholder);
    cardholder.FirstName = "Jane";
    cardholder.LastName = "Doe";
    cardholder.EmailAddress = "jane.doe@example.com";
  2. Get the credential builder from Engine.EntityManager and configure the credential format.

    ICredentialBuilder builder = engine.EntityManager.GetCredentialBuilder();
    var format = new KeypadCredentialFormat("1234");
    
    builder
        .SetName("Jane Doe PIN")
        .SetFormat(format)
        .SetCardholder(cardholder);
  3. Build the credential. The builder validates that a name is set and that the credential has either a format or a request, then persists the credential.

    Credential credential = builder.Build();
  4. Verify the credential is attached to the cardholder.

    Console.WriteLine($"Cardholder credentials: {cardholder.Credentials.Count}");
    Console.WriteLine($"  Type: {credential.Type}, status: {credential.Status.State}");
  5. Attach the cardholder (or the cardholder group it belongs to) to one or more access rules to grant access. See Creating an access rule for the rule-side workflow.

To create a credential in request mode (a badge to be printed later), call SetCredentialRequest(CredentialBadgeRequest) instead of SetFormat. Request mode requires a cardholder, the cardholder cannot already have a credential in request mode, and the request must specify a Reason.

See also

Platform SDK

Plugin SDK

Workspace SDK

Media SDK

Macro SDK

Web SDK

Synergis RIO

Media Gateway

Genetec Web Player

Clone this wiki locally