The Put Request method builds a custom HTTP PUT request to send to the Keep API server. This provides low-level control over the request when the standard put methods don’t meet your specific requirements.
The Put Request functionality allows you to construct custom PUT requests with full control over:
Use the Put Request method when you need:
using Feenics.Keep.WebApi.Wrapper;
using Flurl.Http;
var client = new Client("https://keep.feenics.com");
await client.OpenAsync("username", "password", "my-app");
// Access the underlying Flurl client to build a custom PUT request
var flurlClient = client.FlurlClient;
// Build and send a custom PUT request
var response = await flurlClient
.Request("api/v2/custom-endpoint")
.WithHeader("X-Custom-Header", "CustomValue")
.PutJsonAsync(new
{
PropertyA = "Value1",
PropertyB = "Value2"
});
// Check the response
if (response.StatusCode == 200)
{
Console.WriteLine("Custom PUT request successful");
}
using Feenics.Keep.WebApi.Wrapper;
using Flurl.Http;
var client = new Client("https://keep.feenics.com");
await client.OpenAsync("username", "password", "my-app");
// The wrapper automatically includes authentication
// Build a PUT request to a specific resource
var response = await client.FlurlClient
.Request($"api/f/{instanceId}/CustomResource/{resourceId}")
.WithOAuthBearerToken(client.TokenResponse.AccessToken)
.PutJsonAsync(new
{
Status = "Active",
Configuration = new { Setting1 = true, Setting2 = "enabled" }
});
using Feenics.Keep.WebApi.Wrapper;
using Flurl.Http;
using System.Text.Json;
// Build PUT request and get typed response
var response = await client.FlurlClient
.Request($"api/f/{instanceId}/Object/{objectId}")
.PutJsonAsync(updatedObject);
// Read response as JSON
var jsonResponse = await response.GetJsonAsync<dynamic>();
Console.WriteLine($"Updated object key: {jsonResponse.Key}");
// Or read as string for debugging
var stringResponse = await response.GetStringAsync();
Console.WriteLine($"Raw response: {stringResponse}");
💡 Prefer Standard Methods: Use
PutAsync,PutItemAsync, orPutWithParamsAsyncwhen possible—they handle authentication and error handling automatically
⚠️ Authentication Required: When building custom requests, ensure you include the OAuth bearer token from
TokenResponse
💡 Error Handling: Custom requests require manual error handling—check status codes and handle exceptions
Example in C#
// Returns: nothing
await client.Put(T value, String uriSegments);
Put methods update existing objects in the acre Access Control API. They send the complete object with modified properties back to the server.
| Method | Description |
|---|---|
Put<T>(T obj) |
Update an object synchronously |
PutAsync<T>(T obj) |
Update an object asynchronously |
PutItem(BaseInfo obj) |
Update a BaseInfo object |
PutWithParams<T>(T obj, params) |
Update with additional parameters |
// Get a person, modify, and save
var person = await client.GetPeopleAsync(instance, 0, 1).First();
person.Surname = "NewSurname";
person.Department = "Engineering";
var updated = await client.UpdatePersonAsync(person);
Console.WriteLine($"Updated: {updated.CommonName}");
// Update controller settings
var controller = await client.GetControllerByMacAddressAsync(root, mac);
controller.CommonName = "Main Entrance Controller";
await client.UpdateAsync(controller);
┌─────────────────────────────────────────────────────────────────┐
│ Update Workflow │
├─────────────────────────────────────────────────────────────────┤
│ 1. GET - Retrieve current object │
│ 2. MODIFY - Change properties in memory │
│ 3. PUT - Send complete object back to API │
│ 4. RECEIVE - Get updated object with new timestamps │
└─────────────────────────────────────────────────────────────────┘
Send Complete Objects: PUT operations require the complete object. Partial updates are not supported - always retrieve the current object before modifying.
Optimistic Concurrency: The API uses
ModifiedOntimestamps. If the object was modified by another user since you retrieved it, the update may fail.
try
{
var updated = await client.UpdatePersonAsync(person);
}
catch (FailedOutcomeException ex) when (ex.HttpStatus == HttpStatusCode.Conflict)
{
// Object was modified by another user - refresh and retry
Console.WriteLine("Conflict detected - please refresh and try again");
}
catch (FailedOutcomeException ex) when (ex.HttpStatus == HttpStatusCode.NotFound)
{
// Object no longer exists
Console.WriteLine("Object was deleted");
}
Example in C#
// Returns: T
var item = await client.PutItem<T>(Object value, String uriSegments);
The PutItemAsync method updates a single property or nested item within an existing Keep object. This is useful when you need to modify a specific field without updating the entire object.
public async Task<T> PutItemAsync<T>(string href, object item) where T : class
The PutItemAsync method allows for targeted updates to specific properties or sub-objects within a Keep entity. This is more efficient than full object updates when:
| Parameter | Type | Description |
|---|---|---|
| href | string |
The href (URL) of the specific property or item to update |
| item | object |
The new value to set for the specified property or item |
| Type | Description |
|---|---|
Task<T> |
The updated object or property value |
using Feenics.Keep.WebApi.Wrapper;
var client = new Client("https://keep.feenics.com");
await client.OpenAsync("username", "password", "my-app");
// Example: Update a specific property of a cardholder
var cardholder = await client.SearchOneAsync<CardholderInfo>(
instanceId,
"$filter=FirstName eq 'John'"
);
// Update just the phone number using the property href
string phoneHref = $"{cardholder.Href}/PhoneNumber";
var updatedValue = await client.PutItemAsync<string>(phoneHref, "555-123-4567");
Console.WriteLine($"Updated phone number: {updatedValue}");
using Feenics.Keep.WebApi.Wrapper;
// Update a nested configuration object
var reader = await client.GetByKeyAsync<ReaderInfo>(readerId);
// Update the LED settings for the reader
string ledSettingsHref = $"{reader.Href}/LedSettings";
var newLedSettings = new LedSettingsInfo
{
IdleLed = LedColor.Green,
GrantedLed = LedColor.Blue,
DeniedLed = LedColor.Red
};
var updatedSettings = await client.PutItemAsync<LedSettingsInfo>(
ledSettingsHref,
newLedSettings
);
using Feenics.Keep.WebApi.Wrapper;
// Update a specific item in a collection
var accessLevel = await client.GetByKeyAsync<AccessLevelInfo>(accessLevelId);
// Update a specific schedule assignment by index
string scheduleHref = $"{accessLevel.Href}/Schedules/0";
var newScheduleAssignment = new ScheduleAssignmentInfo
{
ScheduleKey = newScheduleId,
Priority = 1
};
await client.PutItemAsync<ScheduleAssignmentInfo>(scheduleHref, newScheduleAssignment);
💡 Efficiency: PutItemAsync is more efficient than full object updates for single property changes
⚠️ Valid Href: Ensure the href points to a valid, updatable property
💡 Type Safety: The generic type parameter should match the expected property type
Example in C#
// Returns: nothing
await client.PutWithParams(T value, Object parameters, String uriSegments);
The PutWithParamsAsync method updates an existing Keep object while including additional query parameters. This is useful when updates require flags or options to modify the update behavior.
public async Task<T> PutWithParamsAsync<T>(T item, string queryParams) where T : class
The PutWithParamsAsync method extends the standard put operation by allowing query parameters to be included with the update request. This enables:
| Parameter | Type | Description |
|---|---|---|
| item | T |
The object with updated values to send to the server |
| queryParams | string |
Additional query parameters to include with the request |
| Type | Description |
|---|---|
Task<T> |
The updated object returned from the server |
using Feenics.Keep.WebApi.Wrapper;
var client = new Client("https://keep.feenics.com");
await client.OpenAsync("username", "password", "my-app");
// Get a cardholder to update
var cardholder = await client.GetByKeyAsync<CardholderInfo>(cardholderId);
// Modify the cardholder
cardholder.LastName = "Smith-Johnson";
cardholder.Department = "Engineering";
// Update with additional parameters
var updatedCardholder = await client.PutWithParamsAsync(
cardholder,
"notifyOnUpdate=true&validateBeforeSave=true"
);
Console.WriteLine($"Updated: {updatedCardholder.FirstName} {updatedCardholder.LastName}");
using Feenics.Keep.WebApi.Wrapper;
// Update an access level with cascade options
var accessLevel = await client.GetByKeyAsync<AccessLevelInfo>(accessLevelId);
// Modify access level properties
accessLevel.CommonName = "Updated Employee Access";
accessLevel.Priority = 5;
// Update with cascade to refresh all cardholder assignments
var updated = await client.PutWithParamsAsync(
accessLevel,
"cascadeToCardholders=true"
);
using Feenics.Keep.WebApi.Wrapper;
// Update with server-side validation
var door = await client.GetByKeyAsync<DoorInfo>(doorId);
door.CommonName = "Main Entrance - Lobby";
door.StrikeTime = 5;
try
{
// Request validation before committing the update
var updatedDoor = await client.PutWithParamsAsync(
door,
"validate=true&dryRun=false"
);
Console.WriteLine("Door updated successfully with validation");
}
catch (Exception ex)
{
Console.WriteLine($"Validation failed: {ex.Message}");
}
| Parameter | Description |
|---|---|
validate=true |
Validate the object before saving |
notify=true |
Send notifications about the update |
cascade=true |
Apply changes to related objects |
force=true |
Override certain validation checks |
💡 Note: Available parameters depend on the object type and server configuration
💡 Object Must Have Href: The item must have a valid Href property from a previous get operation
⚠️ Parameter Format: Query parameters should not include the leading
?
💡 Server Support: Not all parameters are supported for all object types