Repository navigation
platform sdk 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.
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:
-
AccessPermissionLevelis read-only; mutate throughSetAccessPermissionLevel(int). Toggle inheritance from the parent group throughSetInheritAccessPermissionLevel(bool). -
Cardholder.AccessRulesis the inverse ofAccessRule.Cardholdersand is read-only. Manage rule membership through the rule (or by adding the cardholder to a group the rule covers), not through this collection. -
LastAccessPointreturns aCardholderAccessPointInfowith 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.
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.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): expiredaysdays after first use. -
ExpireWhenNotUsedInDays(int days): expire afterdaysdays 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).
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) andInheritAccessPermissionLevel(read-only). Mutate throughSetAccessPermissionLevel(int level)andSetInheritAccessPermissionLevel(bool inherits). -
EmailAddress(read+write): an address used for group notifications. -
IsVisitorGroup(read+write): whentrue, 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. InspectIsDenyingRulebefore interpreting a returned rule as a grant. -
Parents,Children(read-only): nested-group navigation.
For partition assignment:
-
InsertIntoPartition(Guid partition)andRemoveFromPartition(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)andRemoveCardholderGroupFromPartition(Guid partition, bool applyToChildren)apply to the group, optionally cascading to nested children.
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.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).
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.
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.
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.
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.
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.
- 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.
-
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";
-
Get the credential builder from
Engine.EntityManagerand configure the credential format.ICredentialBuilder builder = engine.EntityManager.GetCredentialBuilder(); var format = new KeypadCredentialFormat("1234"); builder .SetName("Jane Doe PIN") .SetFormat(format) .SetCardholder(cardholder);
-
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();
-
Verify the credential is attached to the cardholder.
Console.WriteLine($"Cardholder credentials: {cardholder.Credentials.Count}"); Console.WriteLine($" Type: {credential.Type}, status: {credential.Status.State}");
-
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.
- About access control: The access control hub.
- Mobile credentials: Mobile credential issuance and lifecycle.
- Door templates: Reusable door configuration projected across many doors.
- Visitors: Active and archived visitor lifecycle.
- Overview
- Connecting to Security Center
- SDK certificates
- Referencing SDK assemblies
- ASP.NET hosting
- SDK compatibility
- Migrating to modern .NET
- Entities
- Entity cache
- Entity loading, cache, and query options
- Detecting entity changes
- Entity change event reference
- Transactions
- Security Center events
- Actions
- Security Desk
- Custom events
- Event-to-actions
- Managing macros
- ReportManager
- ReportManager query reference
- Access control raw events
- DownloadAllRelatedData and StrictResults
- Privileges
- Partitions
- Entity write restrictions
- About custom fields
- About video
- About cameras
- Enrolling a video unit
- Ingesting third-party video
- Archiver and auxiliary archiver roles
- Archive transfer
- About access control
- About cardholders and credentials
- About doors, areas, elevators, and access points
- About access rules and schedules
- About access control units and interface modules
- Enrolling an access control unit
- Door templates
- Visitors
- Mobile credentials
- About threat levels
- About alarms
- About incidents
- Maps
- Logging
- Overview
- Localization
- Certificates
- Lifecycle
- Threading
- State management
- Configuration
- Restricted configuration
- Events
- Queries
- Request manager
- Database
- Entity ownership
- Engine extensions
- Entity mappings
- Server management
- Custom privileges
- Custom entity types
- Resolving non-SDK assemblies
- Deploying plugins
- Modern .NET support
- Overview
- Localization
- Certificates
- Creating modules
- Tasks
- Pages
- Components
- Tile extensions
- Services
- Contextual actions
- Custom actions
- Options extensions
- Configuration pages
- Monitors
- Shared components
- Commands
- Extending events
- Map extensions
- Timeline providers
- Image extractors
- Credential encoders
- Credential readers
- Cardholder fields extractors
- Badge printers
- Content builders
- Dashboard widgets
- Incidents
- Logon providers
- Pinnable content builders
- Custom report pages
- Overview
- Getting started
- Media file formats
- MediaPlayer
- VideoSourceFilter
- MediaExporter
- MediaFile
- G64 converters
- FileCryptingManager
- PlaybackSequenceQuerier
- PlaybackStreamReader
- OverlayFactory
- PtzCoordinatesManager
- AudioTransmitter
- AudioRecorder
- AnalogMonitorController
- Camera blocking
- Overview
- Getting started
- Referencing entities
- Entity operations
- About access control in the Web SDK
- About video in the Web SDK
- Users and user groups
- Partitions
- Custom fields
- Custom card formats
- Actions
- Events and alarms
- Incidents
- Reports
- Tasks
- Macros
- Custom entity types
- System endpoints
- Performance guide
- Reference
- Endpoints
- Constants
- Enumerations
- Under the hood
- Troubleshooting