Put Request

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.

Description

The Put Request functionality allows you to construct custom PUT requests with full control over:

  • Request URL: Specify the exact endpoint to target
  • Headers: Add custom headers to the request
  • Body Content: Control the exact payload sent to the server
  • Query Parameters: Include any query string parameters needed

When to Use

Use the Put Request method when you need:

  • Custom headers that aren’t set by standard methods
  • Non-standard URL patterns not covered by wrapper methods
  • Raw body content that doesn’t map to wrapper object types
  • Integration with custom Keep API extensions

Usage Example

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

Custom Request with Authentication

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

Handling Response Content

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

Important Notes

💡 Prefer Standard Methods: Use PutAsync, PutItemAsync, or PutWithParamsAsync when 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

Best Practices

  1. Use Wrapper Methods First: Only use custom requests when standard methods are insufficient
  2. Include Authentication: Always include the bearer token for authenticated endpoints
  3. Handle Errors: Implement proper error handling for network and API errors
  4. Log Requests: Consider logging custom requests for debugging

Put

Example in C#

// Returns: nothing
await client.Put(T value, String uriSegments);

title: “Put Methods”

Put methods update existing objects in the acre Access Control API. They send the complete object with modified properties back to the server.


Methods

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

Basic Usage

C# Wrapper

// 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 Pattern

┌─────────────────────────────────────────────────────────────────┐
│                      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            │
└─────────────────────────────────────────────────────────────────┘

Important Notes

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 ModifiedOn timestamps. If the object was modified by another user since you retrieved it, the update may fail.


Error Handling

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

Put Item

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.

Method Signature

public async Task<T> PutItemAsync<T>(string href, object item) where T : class

Description

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:

  • Updating Single Properties: Change one field without sending the entire object
  • Nested Object Updates: Modify a sub-object within a larger entity
  • Partial Updates: Reduce network overhead by sending minimal data

Parameters

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

Return Value

Type Description
Task<T> The updated object or property value

Usage Example

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

Nested Object Update Example

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

Collection Item Update Example

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

Important Notes

💡 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

Best Practices

  1. Use for Single Properties: Ideal when changing one or two fields
  2. Construct Valid Hrefs: Use the base object href plus property path
  3. Handle Errors: Not all properties may be individually updatable

Put With Params

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.

Method Signature

public async Task<T> PutWithParamsAsync<T>(T item, string queryParams) where T : class

Description

The PutWithParamsAsync method extends the standard put operation by allowing query parameters to be included with the update request. This enables:

  • Conditional Updates: Apply updates only under certain conditions
  • Cascade Options: Control how related objects are affected
  • Update Flags: Pass behavioral flags to the server
  • Custom Headers: Include additional parameters for specialized update behavior

Parameters

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

Return Value

Type Description
Task<T> The updated object returned from the server

Usage Example

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

Cascade Update Example

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

Validation Example

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

Common Query Parameters

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

Important Notes

💡 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

Best Practices

  1. Check Documentation: Verify supported parameters for each object type
  2. Use Sparingly: Only add parameters when needed; use standard Put for simple updates
  3. Handle Errors: Additional parameters may cause validation failures
  • Put - Standard object update
  • PutItem - Update a single property
  • Put Request - Build custom put requests