Modify a Person
Overview
Modifying person records is a common operation for updating contact information, changing access levels, or managing card assignments. This guide covers the different modification patterns and when to use each approach.
Modification Patterns
| Operation |
Method |
Update Required? |
| Add address |
AddAddressAsync |
No (auto-appends) |
| Remove address |
RemoveAddressAsync |
No (auto-removes) |
| Change name/properties |
Modify object + UpdatePersonAsync |
Yes |
| Add card assignment |
AddCardAssignmentAsync(person, cardInfo) |
No |
| Remove card |
RemoveCardAssignmentAsync |
No |
| Change access level |
AssignConnectedObjectAsync |
No |
| Update metadata |
Modify object + UpdatePersonAsync |
Yes |
| Add tags |
Modify object + UpdatePersonAsync |
Yes |
Finding a Person to Modify
By Current User
// Get the current API user as a person
var user = await client.GetCurrentUserAsync();
var person = await client.GetPersonByUserAsync(user);
By CommonName
var person = (await client.SearchAsync(currentInstance, "{\"_t\":\"Person\", \"CommonName\":\"JohnDoe\"}"))
.OfType<PersonInfo>().FirstOrDefault();
By Key
var person = (await client.SearchAsync(currentInstance, $"{{\"_t\":\"Person\", \"_id\":\"{personKey}\"}}"))
.OfType<PersonInfo>().FirstOrDefault();
By Moniker (External ID)
var results = await client.SearchAsync(currentInstance,
"{\"_t\":\"Person\",\"Monikers.Namespace\":\"HR_System\",\"Monikers.Nickname\":\"EMP001\"}");
var person = results.OfType<PersonInfo>().FirstOrDefault();
Modifying Basic Properties
Change Name
// Get the person
var person = (await client.SearchAsync(currentInstance, "{\"_t\":\"Person\", \"CommonName\":\"JohnDoe\"}"))
.OfType<PersonInfo>().FirstOrDefault();
// Modify properties
person.GivenName = "Jonathan";
person.CommonName = "JonathanDoe";
// Save changes (required for property changes)
await client.UpdatePersonAsync(person);
Console.WriteLine($"Updated person to: {person.CommonName}");
Tags must be lowercase strings with no spaces.
var person = (await client.SearchAsync(currentInstance, "{\"_t\":\"Person\", \"CommonName\":\"JohnDoe\"}"))
.OfType<PersonInfo>().FirstOrDefault();
// Add tags (preserve existing) - tags must be lowercase with no spaces
var existingTags = person.Tags?.ToList() ?? new List<string>();
existingTags.Add("vip");
existingTags.Add("parking-a");
person.Tags = existingTags.ToArray();
await client.UpdatePersonAsync(person);
Update Notes
var person = (await client.SearchAsync(currentInstance, "{\"_t\":\"Person\", \"CommonName\":\"JohnDoe\"}"))
.OfType<PersonInfo>().FirstOrDefault();
// Add a new note
var notes = person.Notes?.ToList() ?? new List<NoteInfo>();
notes.Add(new NoteInfo
{
CreatedOn = DateTime.UtcNow,
NoteText = "Annual security training completed",
User = new ObjectLinkItem
{
CommonName = currentUser.CommonName,
LinkedObjectKey = currentUser.Key,
Relation = "CreatedBy"
}
});
person.Notes = notes.ToArray();
await client.UpdatePersonAsync(person);
Managing Addresses
Add Address (No Update Required)
var person = (await client.SearchAsync(currentInstance, "{\"_t\":\"Person\", \"CommonName\":\"JohnDoe\"}"))
.OfType<PersonInfo>().FirstOrDefault();
// Add phone - automatically appends to person
var phone = await client.AddAddressAsync(person, new PhoneInfo
{
Type = "Mobile",
Number = "555-123-4567"
});
// Add email
var email = await client.AddAddressAsync(person, new EmailAddressInfo
{
Type = "Work",
MailTo = "john.doe@newcompany.com"
});
Console.WriteLine("Addresses added successfully");
Update Existing Address
var person = (await client.SearchAsync(currentInstance, "{\"_t\":\"Person\", \"CommonName\":\"JohnDoe\"}"))
.OfType<PersonInfo>().FirstOrDefault();
// Find the work email
var workEmail = person.Addresses?
.OfType<EmailAddressInfo>()
.FirstOrDefault(e => e.Type == "Work");
if (workEmail != null)
{
workEmail.MailTo = "jonathan.doe@company.com";
await client.UpdateAddressAsync(person, workEmail);
}
Remove Address
var person = (await client.SearchAsync(currentInstance, "{\"_t\":\"Person\", \"CommonName\":\"JohnDoe\"}"))
.OfType<PersonInfo>().FirstOrDefault();
// Find and remove old phone number
var oldPhone = person.Addresses?
.OfType<PhoneInfo>()
.FirstOrDefault(p => p.Number == "555-OLD-NUMBER");
if (oldPhone != null)
{
await client.RemoveAddressAsync(person, oldPhone);
}
Managing Access Levels
Assign Access Level
var person = (await client.SearchAsync(currentInstance, "{\"_t\":\"Person\", \"CommonName\":\"JohnDoe\"}"))
.OfType<PersonInfo>().FirstOrDefault();
var accessLevel = (await client.SearchAsync(currentInstance, "{\"_t\":\"AccessLevel\", \"CommonName\":\"Building-A Access\"}"))
.OfType<AccessLevelInfo>().FirstOrDefault();
await client.AssignConnectedObjectAsync(person, accessLevel, "AccessLevel", false);
Replace All Access Levels
var person = (await client.SearchAsync(currentInstance, "{\"_t\":\"Person\", \"CommonName\":\"JohnDoe\"}"))
.OfType<PersonInfo>().FirstOrDefault();
// Get new access levels
var newLevels = new[]
{
(await client.SearchAsync(currentInstance, "{\"_t\":\"AccessLevel\", \"CommonName\":\"Main Entrance\"}")).OfType<AccessLevelInfo>().FirstOrDefault(),
(await client.SearchAsync(currentInstance, "{\"_t\":\"AccessLevel\", \"CommonName\":\"Parking Garage\"}")).OfType<AccessLevelInfo>().FirstOrDefault()
};
// Clear existing and add new
foreach (var level in newLevels)
{
await client.AssignConnectedObjectAsync(person, level, "AccessLevel", false);
}
Remove Access Level
var person = (await client.SearchAsync(currentInstance, "{\"_t\":\"Person\", \"CommonName\":\"JohnDoe\"}"))
.OfType<PersonInfo>().FirstOrDefault();
var accessLevel = (await client.SearchAsync(currentInstance, "{\"_t\":\"AccessLevel\", \"CommonName\":\"Temporary Access\"}"))
.OfType<AccessLevelInfo>().FirstOrDefault();
await client.RemoveAccessLevelFromPersonAsync(person, accessLevel);
Managing Card Assignments
Add New Card
var person = (await client.SearchAsync(currentInstance, "{\"_t\":\"Person\", \"CommonName\":\"JohnDoe\"}"))
.OfType<PersonInfo>().FirstOrDefault();
await client.AddCardAssignmentAsync(person, new CardAssignmentInfo
{
DisplayCardNumber = "NEW12345",
EncodedCardNumber = 12345,
ActiveOn = DateTime.UtcNow,
ExpiresOn = DateTime.UtcNow.AddYears(1)
});
Disable Card
var person = (await client.SearchAsync(currentInstance, "{\"_t\":\"Person\", \"CommonName\":\"JohnDoe\"}"))
.OfType<PersonInfo>().FirstOrDefault();
// Find the card to disable
var card = person.CardAssignments?.FirstOrDefault(c => c.DisplayCardNumber == "12345");
if (card != null)
{
card.IsDisabled = true;
await client.UpdateCardAssignmentAsync(person, card);
}
Extend Card Expiration
var person = (await client.SearchAsync(currentInstance, "{\"_t\":\"Person\", \"CommonName\":\"JohnDoe\"}"))
.OfType<PersonInfo>().FirstOrDefault();
var card = person.CardAssignments?.FirstOrDefault(c => c.DisplayCardNumber == "12345");
if (card != null)
{
card.ExpiresOn = DateTime.UtcNow.AddYears(1); // Extend by 1 year
await client.UpdateCardAssignmentAsync(person, card);
}
var person = (await client.SearchAsync(currentInstance, "{\"_t\":\"Person\", \"CommonName\":\"JohnDoe\"}"))
.OfType<PersonInfo>().FirstOrDefault();
// Update or add metadata
var metadata = person.Metadata?.ToList() ?? new List<MetadataItem>();
// Find existing HR data or create new
var hrData = metadata.FirstOrDefault(m => m.Application == "HR_System");
if (hrData != null)
{
hrData.Values = JsonConvert.SerializeObject(new
{
department = "Engineering",
costCenter = "CC-2000", // Updated value
manager = "Jane Smith"
});
}
else
{
metadata.Add(new MetadataItem
{
Application = "HR_System",
Values = JsonConvert.SerializeObject(new { department = "Engineering" })
});
}
person.Metadata = metadata.ToArray();
await client.UpdatePersonAsync(person);
cURL Examples
Get Person
curl -X GET \
"https://api.us.acresecurity.cloud/api/f/INSTANCE_KEY/people/PERSON_KEY" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Update Person Properties
curl -X PUT \
"https://api.us.acresecurity.cloud/api/f/INSTANCE_KEY/people/PERSON_KEY" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"$type": "Feenics.Keep.WebApi.Model.PersonInfo, Feenics.Keep.WebApi.Model",
"Key": "PERSON_KEY",
"CommonName": "JonathanDoe",
"GivenName": "Jonathan",
"Surname": "Doe"
}'
Add Address
curl -X POST \
"https://api.us.acresecurity.cloud/api/f/INSTANCE_KEY/people/PERSON_KEY/addresses" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"$type": "Feenics.Keep.WebApi.Model.PhoneInfo, Feenics.Keep.WebApi.Model",
"Number": "555-123-4567",
"Type": "Mobile"
}'
Best Practices
| Practice |
Recommendation |
| Fetch First |
Always get the latest version before modifying |
| Minimal Updates |
Only change necessary fields to avoid overwriting |
| Use Specific Methods |
Use AddAddressAsync instead of modifying Addresses array |
| Audit Trail |
Add notes documenting significant changes |
| Validate Data |
Validate input before updating (email format, phone format) |
| Error Handling |
Handle concurrent modification conflicts |
Troubleshooting
| Issue |
Cause |
Solution |
| Changes not saved |
Forgot UpdatePersonAsync |
Call update after property changes |
| Overwrote data |
Stale object reference |
Fetch fresh copy before modifying |
| Address not added |
Wrong method |
Use AddAddressAsync instead of modifying array |
| Permission denied |
Insufficient rights |
User needs People Update permission |
| Conflict error |
Concurrent modification |
Re-fetch and re-apply changes |