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}");

Update Tags

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);
}

Updating Metadata

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