Attribute Types — Read & Write Reference
Per-type code snippets for reading and writing every Protrak attribute type. For the general concept of accessor methods and why to use them, see Instance Accessor Methods.
Three contexts where attribute code appears:
- Pre-trigger — mutate the incoming
instancein-place using typed setters (SetTextAttributeValue,SetPicklistAttributeValue, etc.)- Post-trigger / UpdateInstance — build a new
InstanceviaInstanceBuilder.ForUpdateand callUpdateInstanceAsync- CreateInstance — build a new
InstanceviaInstanceBuilder.ForCreateand callCreateInstanceAsyncAll typed setter methods are available on any
Instanceobject. UseInstanceBuilderwhen constructing a payload for service calls.
Quick Reference Table
| Attribute Type | Enum | Property | Getter | Typed Setter |
|---|---|---|---|---|
| Text | AttributeType.Text | TextValue | GetTextAttributeValue | SetTextAttributeValue(name, string) |
| Rich Text | AttributeType.RichText | TextValue | GetTextAttributeValue | SetTextAttributeValue(name, string) |
| Numeric | AttributeType.Numeric | NumericValue | GetNumericAttributeValue | SetNumericAttributeValue(name, double?) |
| Currency | AttributeType.Currency | NumericValue | GetNumericAttributeValue | SetNumericAttributeValue(name, double?) |
| Date | AttributeType.Date | DateValue | GetDateAttributeValue | SetDateAttributeValue(name, DateTime?) |
| DateTime | AttributeType.DateTime | DateValue | GetDateAttributeValue | SetDateTimeAttributeValue(name, DateTime?) |
| Boolean | AttributeType.Boolean | BooleanValue | GetBooleanAttributeValue | SetBooleanAttributeValue(name, bool?) |
| Picklist | AttributeType.Picklist | ArrayValue | GetPicklistAttributeValue | SetPicklistAttributeValue(name, params string[]) |
| User | AttributeType.User | UserValues | GetUserAttributeValue | SetUserAttributeValue(name, params UserValue[]) |
| Reference | AttributeType.Reference | ReferenceValues | GetReferenceAttributeValue | SetReferenceAttributeValue(name, params ReferenceValue[]) |
| Attachment | AttributeType.Attachment | FileValue | GetAttachmentAttributeValue | SetAttachmentAttributeValue(name, FileValue) |
Text
Property: TextValue (string)
Enum: AttributeType.Text (also AttributeType.RichText for rich text fields — same property)
// Read
string title = instance.GetTextAttributeValue("Title");
// Returns null if the attribute is absent or has no value.
// Null check before use
if (!string.IsNullOrEmpty(title)) { ... }
// Set (Pre-trigger)
instance.SetTextAttributeValue("Title", "New Value");
// Clear / set to empty
instance.SetTextAttributeValue("Title", string.Empty);
// Build Attribute for CreateInstance
new Attribute { Name = "Title", Type = AttributeType.Text, TextValue = "New Value" }
Numeric / Currency
Property: NumericValue (double?)
Enum: AttributeType.Numeric or AttributeType.Currency — both use the same property
// Read
double? amount = instance.GetNumericAttributeValue("Amount");
// Null check and safe use
if (amount.HasValue)
{
double value = amount.Value; // double
int asInt = (int)amount.Value; // cast to int if needed
}
// Arithmetic (safe pattern)
double current = instance.GetNumericAttributeValue("Score") ?? 0;
double updated = current + 10;
// Set
instance.SetNumericAttributeValue("Amount", 42.5);
// Currency
instance.SetNumericAttributeValue("Price", 199.99);
// Increment existing value
var count = instance.GetNumericAttributeValue("Count") ?? 0;
instance.SetNumericAttributeValue("Count", count + 1);
// Set nullable (e.g. clearing or passing a computed nullable value)
instance.SetNumericAttributeValue("Score", (double?)null);
// Build Attribute for CreateInstance
new Attribute { Name = "Amount", Type = AttributeType.Numeric, NumericValue = 42.5 }
Date and DateTime
Property: DateValue (DateTime?)
Enum: AttributeType.Date or AttributeType.DateTime — both use the same property, but use the matching setter to store the correct enum
Storage: Always UTC. Convert to tenant timezone before displaying or comparing.
// Read
DateTime? scheduledDate = instance.GetDateAttributeValue("ScheduledStartDate");
// Null check
if (scheduledDate.HasValue) { ... }
// Compare dates (UTC)
bool isOverdue = scheduledDate.HasValue && scheduledDate.Value.Date < DateTime.UtcNow.Date;
// Compare old vs new value in PreUpdate (fetch persisted instance first)
var stored = await InstanceService.GetInstanceAsync(instance.Id, new[] { "ScheduledStartDate" });
var oldDate = stored.GetDateAttributeValue("ScheduledStartDate");
var newDate = instance.GetDateAttributeValue("ScheduledStartDate");
bool dateChanged = newDate.HasValue && newDate != oldDate;
// Set a Date-type attribute (AttributeType.Date)
instance.SetDateAttributeValue("ScheduledStartDate", DateTime.UtcNow);
// Set a DateTime-type attribute (AttributeType.DateTime)
instance.SetDateTimeAttributeValue("LastModifiedAt", DateTime.UtcNow);
// Build Attribute for CreateInstance
new Attribute { Name = "StartDate", Type = AttributeType.Date, DateValue = DateTime.UtcNow }
// Convert UTC to tenant timezone for display
var settings = HomeService.GetTenantSettings();
var tz = TimeZoneInfo.FindSystemTimeZoneById(settings.DateTimeFormat.TimeZoneId);
var dateFormat = settings.DateTimeFormat.DateFormat;
var localDate = TimeZoneInfo.ConvertTimeFromUtc(
DateTime.SpecifyKind(scheduledDate.Value, DateTimeKind.Utc), tz);
string formatted = localDate.ToString(dateFormat); // e.g. "28/02/2024"
Boolean
Property: BooleanValue (bool?)
Enum: AttributeType.Boolean
// Read
bool? isActive = instance.GetBooleanAttributeValue("IsActive");
// Null check and use
if (isActive == true) { /* explicitly true */ }
if (isActive == false) { /* explicitly false */ }
if (!isActive.HasValue){ /* not set */ }
// Safe default
bool active = instance.GetBooleanAttributeValue("IsActive") ?? false;
// Set
instance.SetBooleanAttributeValue("IsActive", true);
instance.SetBooleanAttributeValue("IsArchived", false);
// Set nullable (e.g. copying from a getter result)
bool? sourced = other.GetBooleanAttributeValue("IsActive");
instance.SetBooleanAttributeValue("IsActive", sourced);
// Build Attribute for CreateInstance
new Attribute { Name = "IsActive", Type = AttributeType.Boolean, BooleanValue = true }
Picklist
Property: ArrayValue (string[])
Enum: AttributeType.Picklist
Applies to both single-select and multi-select picklist attributes — the only difference is how many values are in the array.
// Read — returns string[] or null
string[] selected = instance.GetPicklistAttributeValue("Status");
// Single-select: get the selected option
string status = instance.GetPicklistAttributeValue("Status")?.FirstOrDefault();
// Returns null if nothing is selected.
// Multi-select: get all selected options
string[] regions = instance.GetPicklistAttributeValue("Regions") ?? Array.Empty<string>();
// Check if a specific value is selected
bool isApproved = instance.GetPicklistAttributeValue("Status")?.Contains("Approved") ?? false;
// Null check before comparing
var priority = instance.GetPicklistAttributeValue("Priority")?.FirstOrDefault();
if (priority == "High") { ... }
// Set — single-select
instance.SetPicklistAttributeValue("Status", "Approved");
// Set — multi-select
instance.SetPicklistAttributeValue("Regions", "North", "East");
// Clear a picklist
instance.SetPicklistAttributeValue("Status"); // no values = clear
// Build Attribute for CreateInstance
new Attribute { Name = "Status", Type = AttributeType.Picklist, ArrayValue = new[] { "Draft" } }
User
Property: UserValues (UserValue[])
Enum: AttributeType.User
Applies to both single-user and multi-user attributes.
// Read — returns UserValue[] or null
UserValue[] assignees = instance.GetUserAttributeValue("Assignee");
// Single-user: get the assigned user
UserValue assignee = instance.GetUserAttributeValue("Assignee")?.FirstOrDefault();
// Returns null if no user is assigned.
// Access user properties
if (assignee != null)
{
Guid userId = assignee.UserId;
string userName = assignee.UserName;
string userEmail = assignee.UserEmail;
}
// Multi-user: iterate
var owners = instance.GetUserAttributeValue("Owners") ?? Array.Empty<UserValue>();
foreach (var owner in owners) { ... }
// Build a UserValue for setting (always copy all three fields)
var recipient = new UserValue
{
UserId = existingUser.UserId,
UserName = existingUser.UserName,
UserEmail = existingUser.UserEmail
};
// Set — single user
instance.SetUserAttributeValue("Assignee",
new UserValue { UserId = userId, UserName = userName, UserEmail = email });
// Set — multiple users
instance.SetUserAttributeValue("Owners",
new UserValue { UserId = user1.UserId, UserName = user1.UserName, UserEmail = user1.UserEmail },
new UserValue { UserId = user2.UserId, UserName = user2.UserName, UserEmail = user2.UserEmail });
// Copy a User attribute value from one instance to another
var sourceUsers = sourceInstance.GetUserAttributeValue("TeamLead");
if (sourceUsers != null)
instance.SetUserAttributeValue("TeamLead", sourceUsers);
// Build Attribute for CreateInstance
new Attribute
{
Name = "Assignee",
Type = AttributeType.User,
UserValues = new[] { new UserValue { UserId = userId, UserName = name, UserEmail = email } }
}
Reference
Property: ReferenceValues (ReferenceValue[])
Enum: AttributeType.Reference
// Read — returns ReferenceValue[] or null
ReferenceValue[] refs = instance.GetReferenceAttributeValue("ProjectRef");
// Single reference: get linked instance ID
Guid? linkedId = instance.GetReferenceAttributeValue("ProjectRef")?.FirstOrDefault()?.Id;
// Get linked instance name (populated when the instance was fetched with this attribute)
string linkedName = instance.GetReferenceAttributeValue("ProjectRef")?.FirstOrDefault()?.Name;
// Null check before using
var projectRef = instance.GetReferenceAttributeValue("ProjectRef");
if (projectRef == null || projectRef.Length == 0)
throw new Exception("Project is required.");
var projectId = projectRef[0].Id;
// Multi-reference: get all linked IDs
var linkedIds = instance.GetReferenceAttributeValue("Participants")
?.Select(r => r.Id)
.ToArray() ?? Array.Empty<Guid>();
// Set — single reference (only Id is required; Name is optional metadata)
instance.SetReferenceAttributeValue("ProjectRef", new ReferenceValue { Id = projectId });
// Set — multiple references
instance.SetReferenceAttributeValue("Participants",
new ReferenceValue { Id = id1 },
new ReferenceValue { Id = id2 });
// Build Attribute for CreateInstance
new Attribute
{
Name = "ProjectRef",
Type = AttributeType.Reference,
ReferenceValues = new[] { new ReferenceValue { Id = projectId } }
}
Attachment
Property: FileValue (FileValue)
Enum: AttributeType.Attachment
// Read — returns FileValue or null
FileValue file = instance.GetAttachmentAttributeValue("ReportDocument");
if (file != null)
{
Guid fileId = file.FileId;
string fileName = file.FileName;
}
// Check if an attachment is present
bool hasAttachment = instance.GetAttachmentAttributeValue("ReportDocument") != null;
// Set (using a known FileId, e.g. after uploading via IFileService)
instance.SetAttachmentAttributeValue("ReportDocument",
new FileValue { FileId = uploadedFileId, FileName = "report.pdf" });
// InstanceBuilder also supports attachment values
var payload = InstanceBuilder.ForUpdate(instanceId, "Report")
.SetAttachmentAttributeValue("ReportDocument", new FileValue { FileId = fileId, FileName = "report.pdf" })
.Build();
// Build Attribute for CreateInstance
new Attribute
{
Name = "ReportDocument",
Type = AttributeType.Attachment,
FileValue = new FileValue { FileId = uploadedFileId, FileName = "report.pdf" }
}
// Upload a file and set the attribute (using IFileService)
using var stream = new MemoryStream(fileBytes);
await FileService.UploadFileContentAsync(fileId, fileName, "application/pdf", stream);
instance.SetAttachmentAttributeValue("ReportDocument",
new FileValue { FileId = fileId, FileName = fileName });
Standard / System Attributes
System attributes are available as direct properties on Instance, or via attribute accessors for the ones stored as attributes.
// Direct Instance properties
Guid id = instance.Id;
string name = instance.Name;
string typeName = instance.InstanceTypeName;
DateTime? created = instance.Created; // UTC
DateTime? modified = instance.Modified; // UTC
string stateName = instance.State.Name;
string stateDisplay = instance.State.DisplayName;
// Attribute-based system attributes (request by name when calling GetInstanceAsync)
string trackingId = instance.GetTextAttributeValue("TrackingId");
string description = instance.GetTextAttributeValue("Description");
UserValue[] creator = instance.GetUserAttributeValue("Creator");
UserValue[] modifier = instance.GetUserAttributeValue("Modifier");
// Example: fetch instance with standard attributes
var instance = await InstanceService.GetInstanceAsync(instanceId,
new[] { "TrackingId", "Creator", "Description" });
string creatorName = instance.GetUserAttributeValue("Creator")?.FirstOrDefault()?.UserName;
RelatedInstance Attributes
RelatedInstance (returned by GetRelatedInstancesAsync) has the same typed getter methods as Instance. Use them just like you would on an Instance.
For relation-level attributes (attributes on the relation itself, not the related instance), use the AttributeArrayExtensions extension methods on RelationAttributes.
using Prorigo.Protrak.API.Contracts.Extensions;
var related = await InstanceService.GetRelatedInstancesAsync(parentId, query);
foreach (var item in related.Items)
{
// RelatedInstance-specific metadata
Guid relatedId = item.RelatedInstanceId;
Guid relationId = item.RelationId; // use for DeleteRelation
string stateName = item.State?.Name;
// Read instance attributes — same typed getters as Instance
string name = item.GetTextAttributeValue("Name");
string status = item.GetPicklistAttributeValue("Status")?.FirstOrDefault();
DateTime? due = item.GetDateAttributeValue("DueDate");
var assignee = item.GetUserAttributeValue("Assignee")?.FirstOrDefault();
Guid? parentId = item.GetReferenceAttributeValue("ParentRef")?.FirstOrDefault()?.Id;
bool? active = item.GetBooleanAttributeValue("IsActive");
// Check attribute presence
bool hasNotes = item.HasAttribute("Notes");
// Read relation-level attributes (via AttributeArrayExtensions)
string role = item.RelationAttributes.GetPicklistAttributeValue("RoleInProject")?.FirstOrDefault();
double? allocPct = item.RelationAttributes.GetNumericAttributeValue("AllocationPercentage");
}
Always include the required attribute names in
RelatedInstanceQuery.Attributes— only requested attributes are returned.
Relation Attributes
Relations can have their own attributes (configured on the relation type). Access them via AttributeArrayExtensions extension methods on Relation.RelationAttributes (same helper used for RelatedInstance.RelationAttributes).
using Prorigo.Protrak.API.Contracts.Extensions;
// In a PostConnect trigger
public async Task RunAsync(Guid relationId)
{
var relation = await RelationService.GetRelationAsync(relationId);
// Read relation attributes via AttributeArrayExtensions
double? percentage = relation.RelationAttributes.GetNumericAttributeValue("AllocationPercentage");
DateTime? start = relation.RelationAttributes.GetDateAttributeValue("StartDate");
string role = relation.RelationAttributes.GetPicklistAttributeValue("RoleInProject")?.FirstOrDefault();
// Relation metadata
Guid sourceId = relation.SourceInstanceId;
Guid destinationId = relation.DestinationInstanceId;
string relType = relation.RelationTypeName;
}