Skip to main content

Policies

The Policies page is where administrators configure system-wide behavioral rules. The current release covers data retention, dispatch behavior, outbound integration delivery, alert notification delivery, commission API limits, security audit configuration, access scope behavior, and what workflows do after a restart; future releases will add file attachment rules and other administrator-configurable behavior under the same framework.

Every policy edit is recorded as a PolicyChanged entry in the Security Events log, including the old and new configuration. This satisfies SOC 2 change-management requirements and means an auditor can reconstruct exactly what changed, when, and by whom.

Access​

The Policies page is the Policies tab of the System page (/system/policies). It requires the Manage Policies permission, which is granted to the Admin role by default. Operator and Contributor roles do not see the tab.

The list can be filtered by Category and Status (Enabled, Disabled, or Always on) in addition to the search box. Click a policy's row to open its detail page, which shows the policy's description in its Overview section, its configuration, and, for retention policies, the Background Job card described in Triggering a run on demand below. Enable/Disable is in the detail page's Actions menu, and is also available from the list's row actions menu for policies that support it.

How policies work​

Each policy is identified by a Category (e.g., Retention) and a Key (e.g., TagRead, Telemetry). The configuration values are stored as JSON internally; the UI presents typed forms so administrators do not edit JSON directly.

Two important behaviors:

  • Disabling a policy sets its enforcement to off. The capture-mode check on the ingest path treats a disabled policy the same as a missing policy: default behavior continues. Disable, edit, then re-enable when you want to stage a change without it taking effect immediately.
  • Out-of-the-box defaults are seeded automatically on first boot. New categories ship with conservative defaults so the system runs sensibly with no admin action.

Some policies are always-on: the disable toggle is hidden and the policy editor shows an explanatory banner instead. Always-on policies govern infrastructure behavior that must remain running regardless of configuration intent (stopping email delivery, for example, is done by setting Mail:Provider=None in the Admin Console, not by disabling the dispatch policy). See Dispatch policies below for the current always-on policies.

Policies at a glance​

The Policies tab shows this one-line summary for each policy; open a policy for its full description and settings.

CategoryKeyWhat it controls
RetentionTagReadHow long raw tag reads and their rollups are kept, and whether raw capture is on
RetentionTelemetryHow long raw telemetry events and their rollups are kept
RetentionScanEventHow long scan events are kept at each level of detail
RetentionWorkflowExecutionHow long workflow runs, their payloads and rollups are kept
RetentionMovementRuleExecutionHow long movement-rule runs and their payloads are kept
RetentionOperationalRollupsHow long the 15-minute operational rollups are kept
RetentionInventorySnapshotHow long hourly inventory level snapshots are kept
RetentionRefreshTokenHow long expired or revoked sign-in tokens are kept
RetentionDispatchOutboxHow long sent and failed email deliveries are kept
RetentionIntegrationOutboxHow long sent and failed integration deliveries are kept
RetentionCommissionHow long commission batches and their items are kept
RetentionAlertInstanceHow long resolved alerts are kept
RetentionAuditHow long Change History and security events are kept
RetentionClosedInventoryHow long closed-out items keep their custom fields and history
DispatchEmailRetries, backoff and timeout for outbound email
IntegrationOutboxRetries, timeouts, concurrency and size caps for outbound integrations
IntegrationThrottlePer-host concurrency and circuit breaking for outbound HTTP
AlertingDeliveryGrouping, re-notification and rate caps for alert notifications
AlertingSystemHealthThresholds for the disk, memory, CPU and database size alerts
AlertingDevicesWhen a reader counts as offline or an antenna as disconnected
CommissionLimitsMaximum rows per synchronous commission request
SecurityAuthAuditWhich authentication events reach the security log
SecurityReaderCertificateValidationRequire a valid TLS certificate from every reader
ScopingSiteCatalogLet sites own their own catalog definitions
ScopingCrossScopeTransferWhat happens when inventory moves into another scope's location
WorkflowsCatchUpRun workflows the service did not get to before a restart

Retention policies​

Retention policies tell the system how long to keep historical data before it is automatically deleted. The current retention policies are TagRead, Telemetry, ScanEvent, WorkflowExecution, MovementRuleExecution, OperationalRollups, InventorySnapshot, RefreshToken, DispatchOutbox, Commission, IntegrationOutbox, AlertInstance, Audit, and ClosedInventory. All are enforced by the Retention background job, which runs nightly on a configured cron schedule and can also be triggered on demand from the Background Jobs page. The job processes every active retention policy in a single pass and records every run, including the row counts affected per layer, in the run history.

All time-series and audit tables are now under the retention framework. The legacy System Settings → Data Cleanup tool has been removed; the retention engine is the single source of automated deletion across the database.

Payload truncation​

Several execution policies use a two-step lifecycle: truncation followed later by deletion. Truncation nulls the large JSON payload columns on a row (the trigger snapshot, per-tag lists, action input/output context, and similar bulk data) while leaving the row itself intact. Aggregate counters, status, duration, which workflow or rule ran, and foreign-key linkage all survive truncation.

This matters because payload data has two very different useful lives:

  • Short useful life (days to weeks). The trigger snapshot is most valuable in the immediate debugging window. If a workflow misfired or skipped tags, you want the full context. After that window closes, the payload adds storage cost without operational value.
  • Long useful life (months to a year). The row-level audit trail (this workflow ran at 14:23:07, it matched 47 tags, it completed in 0.4 s, it succeeded) is what answers compliance questions and feeds dashboards. You do not need the trigger snapshot to answer those questions.

Truncation gets both: cheap storage for the long audit window, full fidelity in the short debugging window. On the run's detail page, truncation operations show an orange truncate chip; deletion shows the default delete chip. The Total Deleted counter in the run results counts only row deletions; truncated rows are still present and do not count toward the VACUUM threshold.

Storage planning​

Use the profiles below to estimate the database footprint for a deployment before you provision a disk. The numbers assume defaults across all retention policies (TagRead 7 d, ScanEvent reads 7 / metrics 30 / detail 90 / rollups 365, Workflow payload 14 / detail 90 / rollups 365, Movement-rule payload 30 / detail 90).

Workload variables

VariableWhat it capturesExample
ReadersActive fixed readers feeding the system10
Active hoursHours per day readers are scanning8
Scans/reader-hourDiscrete ScanEvent rows produced per reader per hour50
Tags/scanAverage distinct tags seen in a single scan25
Workflows × actionsWorkflows triggered per scan, multiplied by actions per workflow1 × 3

Profiles

ProfileWorkloadPer scan (full payload)Per reader, steady state10 readers25 readers
Light8 hrs, 10 scans/hr, 5 tags/scan, 1 wf × 2 actions~50 KB~150 MB~1.5 GB~4 GB
Typical8 hrs, 50 scans/hr, 25 tags/scan, 1 wf × 3 actions~170 KB~2.5 GB~25 GB~62 GB
Heavy24 hrs, 100 scans/hr, 50 tags/scan, 2 wfs × 5 actions~440 KB~25 GB~250 GB~625 GB

Steady-state numbers fold in payload truncation at the 14-day mark. The bulk of every workflow and movement-rule execution row gets nulled out long before the row itself ages out at 90 days, which is the single biggest reason a Typical deployment lives in gigabytes rather than tens of gigabytes.

Sizing formula

Per scan event (full payload, KB):
Workflow data = Workflows × (10 + 2 × Tags + 22 × Actions)
Movement-rule data = 5 + 0.5 × Tags
Scan event stack = 1.1 × Tags
Tag read events = 0.5 × Tags

Daily growth (full payload) = Per-scan KB × Scans/hr × Active hrs × Readers
Steady-state at defaults ≈ ~17 days of full growth, in round numbers

The "~17 days" approximation reflects that most rows live in their full-payload form for 14 days, then shrink dramatically. Workflow payload columns are by far the largest line item; they dominate the per-scan KB number for any deployment with non-trivial workflow logic.

Truncation savings (per row)

TableFullTruncatedReduction
WorkflowExecution~62 KB~3 KB95%
WorkflowActionExecution~22 KB~2 KB91%
MovementRuleExecution~19 KB~1 KB95%

These ratios are why a Typical deployment with 50 scans/reader-hour stays in the low gigabytes per reader instead of climbing into double digits: payload truncation removes 90%+ of the row weight by the time the audit window opens up.

Worked example. Five readers, 8 active hours/day, 50 scans/reader-hour, 25 tags/scan, 1 workflow × 3 actions:

  • Per-scan KB ≈ (10 + 50 + 66) + (5 + 12.5) + 27.5 + 12.5 ≈ 184 KB
  • Daily full-payload growth ≈ 184 KB × 50 × 8 × 5 ≈ 360 MB/day
  • Steady state ≈ 360 MB × 17 ≈ 6 GB
  • Provision 12 GB to leave headroom for rollups, indexes, and bursts

Caveats. These estimates cover the high-volume tables driven by ScanEvent ingest and workflow execution. The remaining retention-managed tables (operational rollups for device connectivity, inventory transaction, and API ingest, plus inventory snapshots) are bounded by their dimension cardinality (devices, transaction types, locations, items) rather than scan volume, and add a smaller, mostly fixed overhead on top. SQL Server allocates more page-level overhead than SQLite, so figure another 30–50% on top of the numbers above when planning a SQL Server disk. Index storage is roughly 20% of table storage and is included in the per-row numbers.

TagRead retention​

Controls raw TagReadEvent rows and the hourly TagRead rollup.

SettingDescriptionDefault
Capture modeFullCapture persists every raw read. Disabled skips raw row writes; rollups and scan processing continue uninterrupted.FullCapture
Store raw payload per readKeeps the reader's original JSON for each stored read, shown in the Raw JSON section of the tag read detail. The parsed columns already hold every field the event carries, so this is only needed when a support case wants the exact payload. It costs about 360 bytes per read; on a reader reporting every read, that was a third of all tag-read storage.Off
Raw retention (days)How long raw TagReadEvent rows are kept. Older rows are deleted by the next retention run.7
Rollup retention (days)How long the hourly rollup rows are kept. Must be ≥ raw retention.365
Capture override until (UTC)Forces full capture on regardless of mode until this timestamp. Use for time-bounded support investigations. Auto-expires.(none)

When to disable raw capture. High-traffic deployments that do not need raw read history for troubleshooting can switch capture to Disabled and reclaim significant storage. The hourly rollup continues to provide trending data (read counts, ingest event counts, antenna distribution) and scan processing is unaffected.

When to use the override. A support case asks you to look at raw reads for a specific reader. Set the override timestamp 4-8 hours in the future, run the troubleshooting, and the override expires automatically without any further action. While the override is active, reads also keep their raw payload, whatever Store raw payload per read is set to, so you do not have to toggle two settings for one investigation.

When Capture mode is Disabled, the Ingest History and the Ingest data tabs on device and inventory item detail pages show a warning that new raw reads are not being stored, with a link back to this page.

Telemetry retention​

Controls raw TelemetryEvent rows and the 15-minute telemetry rollup.

SettingDescriptionDefault
Raw retention (days)How long raw TelemetryEvent rows are kept. Older rows are deleted by the next retention run.7
Rollup retention (days)How long the 15-minute rollup rows are kept. Must be ≥ raw retention.365

Telemetry has no Disabled mode. Volume does not justify it, and the heartbeat path drives connectivity tracking and live device-state updates.

ScanEvent retention​

Controls the family of tables produced by every scan: the ScanEvent itself, its per-tag rows, the raw individual reads that fed it, the per-antenna RF metrics, and the 15-minute scan rollups. Different sub-tables have very different storage profiles and very different long-term value, so the policy exposes four independent retention horizons rather than one knob.

SettingTables affectedDescriptionDefault
Reads retention (days)ScanEventReadsRaw individual antenna reads. High volume; useful for a few days for motion analysis and reader diagnostics, then mostly dead weight.7
Tag metrics retention (days)ScanEventTagMetricsPer-antenna RF and curve statistics. Useful for system tuning and read signature captures; value drops off past a month. Must be ≥ Reads.30
Detail retention (days)ScanEvent, ScanEventTag, ScanEventMetricsThe "this passage happened, here's what we saw" record. Drives audit, dashboards, and the scan history view. Must be ≥ Tag Metrics.90
Rollup retention (days)ScanEventRollups15-minute pre-aggregated counts; tiny per row, kept long for year-over-year dashboard data. Must be ≥ Detail.365

The validation rule Reads ≤ Tag Metrics ≤ Detail ≤ Rollup ensures shorter-retention layers always have a parent that survives at least as long. When a ScanEvent is deleted at the Detail horizon, any surviving children (Reads, Tag Metrics, Tags, Metrics) cascade-delete with it; the explicit per-layer retention only matters for shrinking children while their parent still exists.

Capture data is kept. Events collected by a read signature capture are exempt from the Reads, Tag Metrics and Detail horizons, along with their raw reads, tags and per-antenna metrics, because building a signature reads them (the raw reads let it use any transit window). The exemption covers finished captures, and active captures that started within the Detail horizon; an active capture left running longer than that is treated as abandoned and its events age out normally, as do a discarded capture's. Captures are small (tens of events each), so the exemption does not grow with scan volume.

When to shorten Tag Metrics. This is the single biggest line item under typical workloads; it scales as (tags × antennas) per scan. Customers tight on storage who do not use read signatures can drop this to 7–14 days without affecting day-to-day operation.

When to shorten Reads. If you do not use raw read data for reader troubleshooting or motion analysis, dropping Reads to 1–3 days reclaims space quickly. The information needed for scan summaries, signature checks, and audit lives in ScanEventTag (the Detail layer), not in ScanEventReads.

When to lengthen Detail. Customers using scan history for compliance audit beyond a quarter can raise Detail to 180 or 365 days. Storage cost is dominated by the child tables, not the ScanEvent row itself, so a longer Detail horizon costs less than it looks like at first glance, provided Tag Metrics is set conservatively.

Pre-existing data after upgrade. The Timestamp column on ScanEventTagMetrics is populated for all new rows. Rows that existed before the upgrade have Timestamp = null and are skipped by explicit Tag Metrics retention; they are cleaned up via parent ScanEvent cascade once they age past the Detail horizon (90 days by default). No action required.

WorkflowExecution retention​

Controls the workflow execution table family using three tiers. The Payload tier truncates (does not delete) large JSON columns; the Detail and Rollup tiers delete rows.

SettingTables affectedOperationDescriptionDefault
Payload retention (days)WorkflowExecution, WorkflowActionExecutionTruncateNulls the trigger snapshot (SourceEventJson, FilteredTagEpcs) and per-action context columns (InputContextJson, OutputDataJson, ResultData, Message). Aggregate counters, status, duration, and FK linkage survive.14
Detail retention (days)WorkflowExecution, WorkflowActionExecutionDeleteDeletes workflow execution rows. Action execution rows cascade-delete with their parent.90
Rollup retention (days)WorkflowExecutionRollupDeleteDeletes 15-minute aggregate rows used by dashboards.365

Validation enforces Payload ≤ Detail ≤ Rollup. The form rejects values that violate this order with an inline message before saving.

Defaults rationale. Fourteen days covers the typical debugging window for a workflow that behaved unexpectedly. Ninety days gives a full quarter of row-level execution history, enough to answer questions like "how many times did this workflow fire in Q1?" Rollup stays at 365 days to align with other rollup policies and preserve year-over-year dashboard data.

When to lengthen Payload. If your operations team routinely investigates trigger context more than two weeks after the fact, extend Payload to 30–45 days. The payload columns are the largest cost driver on this table, so consider storage capacity before extending significantly.

When to shorten Detail. High-volume deployments where workflows fire many thousands of times per day accumulate rows quickly. If dashboard data (from the Rollup tier) is sufficient for your needs, 30–45 days of Detail still covers most compliance windows.

MovementRuleExecution retention​

Controls movement-rule execution rows using two tiers. There is no rollup table for movement-rule executions.

SettingTables affectedOperationDescriptionDefault
Payload retention (days)MovementRuleExecutionTruncateNulls the per-tag results JSON (ResultsJson). The aggregate pass/fail counts and trigger result survive.30
Detail retention (days)MovementRuleExecutionDeleteDeletes movement-rule execution rows.90

Validation enforces Payload ≤ Detail. The form rejects values that violate this order.

Defaults rationale. Thirty days for Payload covers a typical month of per-tag result debugging, long enough to investigate a movement-rule misfire without the overhead of keeping full JSON indefinitely. Ninety days of Detail matches the workflow execution Detail default so both execution histories cover the same compliance window.

When to lengthen Payload. If you routinely audit which specific tags triggered a movement-rule transition more than a month after the event, extend Payload. The per-tag results JSON is the most storage-intensive part of the movement-rule execution row.

OperationalRollups retention​

Single retention horizon covering four 15-minute operational rollup tables. They share one knob because they have the same shape (single BucketTime column, no payload, no parent/child cascade) and the same role: low-cardinality counters kept for dashboards.

SettingTables affectedDescriptionDefault
Retention (days)DeviceConnectivityRollups, InventoryTransactionRollups, ApiIngestRollups, IntegrationDeliveryRollupsRows older than the cutoff are deleted by the next retention run. Covers heartbeat presence/gap stats per device, transaction counts per (TransactionType, Location), request count / throughput / error / latency per (Device, Endpoint), and outbound integration delivery counts, attempts, and timing per connection profile.365

Defaults rationale. One year is the smallest window that supports year-over-year dashboard comparisons. ApiIngestRollups is typically the row-volume driver (its (device × endpoint) cardinality grows fastest in heavy-ingest deployments), so the default balances trend coverage against storage. Lengthen if your operations team relies on multi-year capacity planning data; shorten if row count becomes a concern.

IntegrationDeliveryRollups is what lets Integration Outbox delivery counts and timing survive well past the much shorter IntegrationOutbox retention window that governs the request and response payloads themselves: the payloads age out in weeks, but the record that deliveries happened, and roughly how many, stays for a full year by default.

Future direction. If a per-table retention horizon is needed later (e.g., keep DeviceConnectivityRollups longer than ApiIngestRollups), this policy can be expanded into multiple horizons under the same key. The four-tier ScanEvent policy is the precedent.

InventorySnapshot retention​

Controls hourly InventoryLevelSnapshot rows: point-in-time inventory level captures used for trend and year-over-year analysis ("what was on hand at this location on this date last year?").

SettingTables affectedDescriptionDefault
Retention (days)InventoryLevelSnapshotsRows older than the cutoff are deleted by the next retention run.1825 (5 years)

Defaults rationale. Hourly granularity keeps row volume modest (24 buckets/day per (Location, Item) pair), so multi-year retention is cheap. Inventory-level history has high business value for YoY analysis, so the default is intentionally long.

Future direction. This policy is expected to be folded into a future InventoryTransaction retention policy that will govern transaction rows and snapshot history together.

Commission retention​

Controls how long commission batch records (the "print-run manifest") are kept. A CommissionBatch row represents a single commission API call: the job header, its status, and the per-row results. When a batch ages out, its CommissionBatchItem rows cascade-delete with it. The inventory created by a commission call is never touched by this policy; only the historical record of the print run itself ages out.

SettingTables affectedDescriptionDefault
Retention (days)CommissionBatch, CommissionBatchItemBatches older than the cutoff (by CreatedAt) are deleted by the next retention run, along with their items. Allowed range: 1–3650 days.90

Defaults rationale. Ninety days covers a typical label-printing reconciliation and audit window. Each batch holds one item row per label, so a high-volume printer (thousands of labels a day) accumulates manifest rows quickly: the shorter default keeps the footprint in check. Lengthen it if you need a longer audit history. The inventory the batches created is unaffected either way.

When to lengthen. If your compliance or audit process requires tracing a specific print run for longer than a year, such as for serialized medical devices or regulated assets, extend the window to match your retention obligation.

When to shorten. If your integration tracks commission history on its own side (in your ERP or WMS), you may not need the full year. Reducing this horizon frees a small amount of storage without affecting live inventory.

Audit retention​

Controls how long the two audit logs are kept, by when each event happened: Change History (the operational audit log: who changed which configuration, and what changed) and Security Events (sign-ins, key and permission changes). The two windows are independent because the logs carry different compliance weight: the operational log is small and kept for years, while security events can run high volume under a misconfigured or retrying reader, so they default shorter.

SettingTables affectedDescriptionDefaultAllowed range
Change History retention (days)OperationalAuditEventsHow long Change History rows are kept, from when the change happened.2555 (7 years)365-3650
Security event retention (days)SecurityEventsHow long security events are kept, from when the event happened.36590-3650

Defaults rationale. Change History is the record of how your configuration changed over time, and that stays useful for years, so it defaults to seven years; the 365-day minimum is one SOC 2 Type II observation window. The table is small: each entry is roughly 1-2 KB, so even a busy site making 100 configuration changes a day only adds about 70 MB a year. One year for security events balances forensic recall against the volume a misconfigured or retrying reader can generate on the credential and authentication event types.

Upgrade behavior. Before this policy existed, nothing ever purged security events or Change History; both logs simply grew forever. On an installation upgrading from an earlier version that already had security events at the time this policy was first seeded, the security event window starts at the maximum, 3650 days, so the upgrade does not delete any pre-existing history. An administrator can shorten the window afterward. A fresh installation, with no security events yet at seed time, gets the normal 365-day default. Change History always seeds at the 2555-day default; there is no equivalent upgrade case because Change History did not exist before this policy shipped.

When to shorten either window. Raising either window costs disk, and shortening a window permanently deletes older events on the next retention run. Lower the security event window if your deployment is internet-exposed and generates high sign-in volume you don't need to keep for a full year. Leave Change History at its default unless storage is genuinely tight: the table's footprint is small enough that shortening it rarely buys meaningful space.

Disabling Audit retention. Setting Enabled = Off stops the engine from deleting rows from both logs, the same as other retention policies. Use this only temporarily, for example while preserving evidence for an investigation. Re-enable the policy when finished; the next run catches up to the configured windows.

ClosedInventory retention​

Controls how long inventory items that have been closed out (shipped, consumed, decommissioned) keep their detail. Both windows are measured from when each item closed, not from when it was created, and only items that are still Closed are affected: an item returned to inventory drops out of this policy automatically. Nothing recorded after the cutoff is removed: a transaction is only trimmed or deleted once its own date is past the window, and editing a closed item's custom fields restarts the clock for that item's values. See Inventory Item Lifecycle for how items close.

This policy ships disabled. Closed items are the record a recall or an audit reaches back for, so trimming them is a decision an administrator makes on purpose. Nothing is removed until you enable it.

SettingTables affectedDescriptionDefault
Custom fields retention (days)InventoryItems, InventoryTransactions, CustomFieldSearchableValuesClears the custom field values on the item and on its transactions, and removes the item's custom field search entries. The item, its tags, and its transaction history remain. Must not exceed the history window. Allowed range: 1-3650 days.730
History retention (days)InventoryTransactionsDeletes the item's transactions, which removes its location history. Must be at least the custom fields window. Allowed range: 1-3650 days.730

The item and its tags are never deleted. A tag ID is never freed for reuse. If a shipped item comes back years later and its tag is read, it still resolves to its own record, and placing it at a location returns it to inventory as usual. The only thing lost is the history from before the purge: its Location History tab says the history was removed by retention. The Closed counts on the dashboard and the Items page continue to include these items.

What else changes when history is removed. Transaction count charts and inventory level trends are kept in their own summary tables, governed by the OperationalRollups and InventorySnapshot policies, so they keep their history. Views that list or count individual transactions do lose the removed rows: the Transactions page, the usage count shown for a transaction type, and the recent-movement figures on Item and Item Type pages (those look back 14 days, so they are only affected by a history window shorter than that). A location whose only transaction history belonged to purged items can also be deleted afterwards, since nothing references it anymore.

Choosing the windows. Set History retention to at least your longest recall, warranty, or regulatory lookback. Both windows default to the same 730 days because custom fields often hold exactly what a recall needs (lot, batch, serial numbers), so by default they last as long as the history. Shorten the custom fields window only when the values are bulky and you do not need them after the fact.

Disabling a retention policy​

Setting Enabled = Off on a retention policy stops the retention engine from deleting that policy's rows. The capture path is unaffected: raw rows continue to accumulate at full speed. Use this only when you need to pause deletion temporarily (for example, while gathering evidence for a support case). Re-enable the policy and the next run will catch up to the configured retention window.

Triggering a run on demand​

Each retention policy detail page shows a Background job card with the current schedule, last run outcome, and next scheduled run, but no per-policy Run Now button: the retention job enforces every enabled retention policy in a single pass, so a button scoped to one policy would misrepresent what actually runs. To trigger a run manually, go to the Background Jobs page and use its Run job menu. That manual run goes through the same code path as the scheduled run: same envelope, same skip-if-active behavior, same history record.

Dispatch policies​

Dispatch policies control how outbound messages (email, and future channels) are retried when a provider call fails. All dispatch policies are always-on: the disable toggle is hidden. To stop email delivery entirely, set Mail:Provider=None in the Admin Console rather than adjusting the policy.

Dispatch / Email​

Controls retry behavior for outbound email dispatches. After a provider failure, the consumer re-queues the outbox row as Pending with the next retry scheduled according to the backoff curve. Once Max attempts is exhausted, the row is dead-lettered. Dead-lettered rows remain in the Dispatch Outbox and can be manually re-queued.

SettingDescriptionDefault
Max attemptsTotal provider call attempts (including the first) before dead-lettering. Set to 1 to disable retries.5
BackoffHow the wait time between retries grows: Exponential (doubles each attempt) or Linear (constant wait).Exponential
Initial delay (seconds)Wait time before the first retry. Anchors the backoff curve.30
Max delay (seconds)Cap on the computed delay so exponential backoff does not extend into multi-day territory.3600 (1 hour)
Send timeout (seconds)Hard upper bound on a single provider call. Timeouts count as retryable failures and decrement the attempt budget.30

Defaults rationale. Five attempts with exponential backoff starting at 30 seconds covers roughly 1 hour of retry window before dead-lettering, enough to recover from most transient provider outages without holding the row in limbo for days. Increase Max attempts or Max delay for environments with less reliable connectivity to the provider.

When to tune. If your provider has frequent short-lived outages (1–5 minutes), a lower Initial delay (10–15 seconds) and more attempts catches more recoverable failures. If your provider has slow-response characteristics, raise Send timeout beyond the default 30 seconds to avoid premature timeouts eating into your attempt budget.

DispatchOutbox retention​

Controls how long Email Delivery rows (the dispatch outbox) are kept after reaching a terminal state. The policy has two independent windows because sent rows and failed/dead-lettered rows have different operational lifetimes: sent rows are routine delivery confirmation that doesn't need a long window, while failed rows are what administrators reach for during incident triage.

Pending and Sending rows represent live delivery state and are never deleted by retention.

SettingTables affectedDescriptionDefault
Sent retention (days)DispatchOutbox where Status=SentHow long to keep successfully delivered rows.30
Failed / dead-lettered retention (days)DispatchOutbox where Status=Failed or DeadLetteredHow long to keep failed and dead-lettered rows.90

Defaults rationale. Thirty days of sent history covers most compliance review windows for delivery confirmation. Ninety days for failed rows matches the workflow and movement-rule execution detail defaults and gives a full quarter of triage evidence.

Disabling DispatchOutbox retention. Setting Enabled = Off on this policy stops the engine from deleting outbox rows. Use this only temporarily, for example while preserving evidence for a delivery incident. Re-enable the policy when the investigation is complete.

Integration policies​

Integration policies control outbound HTTP behavior on two different scopes. Integration / Outbox governs how requests queued by the HTTP Integration (durable) workflow action are delivered and retained. Integration / Throttle governs the per-host concurrency limit and circuit breaker shared by every outbound call Tagnet makes, queued deliveries, synchronous Send HTTP Request actions, and connection-profile token requests alike. See the Integration Outbox guide for the concepts behind delivery: what "queued" means, how retries and dead-lettering work, and the idempotency header sent to receivers.

Integration / Outbox​

Controls delivery behavior: the retry budget and backoff curve, the per-attempt timeout, how many deliveries send concurrently, request and response size caps, the idempotency header name, and the thresholds that raise the outbox's operational alerts. This policy is always-on: the disable toggle is hidden, because the consumer falls back to these defaults when no policy row exists, so disabling it would silently swap admin-tuned values for defaults rather than turning anything off.

SettingDescriptionDefault
Max attemptsTotal delivery attempts, including the first, before the delivery is dead-lettered.15
BackoffHow the wait time between retries grows: Exponential (doubles each attempt) or Linear (constant wait).Exponential
First retry delay (seconds)Wait time before the first retry. Anchors the backoff curve.30
Longest wait between attempts (minutes)Cap on the computed delay so exponential backoff doesn't drift into tomorrow.60
Per-attempt timeout (seconds)Hard upper bound on a single delivery attempt. Timeouts count as retryable failures.30
Deliveries sent at onceHow many deliveries the consumer sends concurrently. Applied when Tagnet restarts, unlike every other setting here.1
Largest request body (KB)A request over this is rejected when queued, not truncated: a cut-down body could not be resent faithfully.256
Response kept for diagnosis (KB)Longer responses are stored truncated and marked.32
Idempotency headerHeader name carrying each delivery's unique key, so the receiving system can recognize a resend. Leave blank only if the endpoint rejects unknown headers.Idempotency-Key
Consecutive failures before an endpoint counts as unavailableFailed attempts to one connection in a row before the Endpoint unavailable alert raises. Any answered response resets the count.5
Queue age before deliveries count as backing up (minutes)Age of the oldest never-attempted delivery before the Queue backing up alert raises. Retry backoff cannot trip it.15
Wait before the consumer counts as stalled (minutes)How long deliveries sit queued with no worker sending them before the Consumer stalled alert raises.5
Days before unhandled dead letters raise an alertHow long dead-lettered deliveries sit unhandled before the Dead letters aging alert raises. Keep it well inside the failed-delivery retention window below.7

Defaults rationale. Fifteen attempts on the default curve rides out roughly 8 hours of an endpoint being unavailable before dead-lettering, deliberately much longer than the Dispatch / Email budget of about an hour (see Dispatch policies above). A short retry window is fine for a transient email-provider blip; it is useless for an ERP down for a routine overnight maintenance window, which would otherwise dead-letter an entire night's worth of events.

Deliveries sent at once above the per-host limit only helps when deliveries target several different systems. Concurrent requests to the same host are capped by the Integration / Throttle policy below (5 by default), the same shared limit that governs the synchronous Send HTTP Request action. Raising Deliveries sent at once past that limit does nothing for a deployment where every delivery goes to one endpoint; the extra workers just wait for a slot.

Deliveries sent at once needs a restart. Every other setting on this policy takes effect on the next delivery. The worker count is read once when Tagnet starts, so changing it and saving leaves the running workers as they were until Tagnet is restarted. The Integration Outbox diagnostics page says so when the two disagree, showing both the number the policy asks for and the number actually running.

When to tune. Raise Max attempts or Longest wait between attempts for a receiving system with longer planned outages than the default budget covers. Lower Per-attempt timeout if your endpoint responds quickly and you'd rather fail fast and retry than tie up a delivery slot on a slow connection.

Integration / Throttle​

Controls per-host limits on outbound HTTP: how many requests to one external system are allowed to run at once, and how many consecutive failures take that system out of rotation before further requests are rejected without being attempted. Unlike Integration / Outbox above, this policy is not scoped to durable deliveries. It applies per destination host to every outbound call Tagnet makes: queued deliveries, synchronous Send HTTP Request actions, and the token requests that authenticate a connection profile, so a workflow hammering a host also constrains the deliveries queued behind it. This policy is always-on: the disable toggle is hidden, for the same reason as Integration / Outbox, because callers fall back to these defaults when no policy row exists, so disabling it would silently swap admin-tuned values for defaults rather than turning throttling off.

SettingDescriptionDefault
Requests at once to one systemRequests allowed to be in flight to one host at the same time. Anything beyond this waits for a slot.5
Consecutive failures before requests are held backConsecutive failed requests to one host before its circuit opens and further requests to that host are rejected immediately, without being attempted. Any answered response resets the count. Only connection failures, timeouts, and 5xx/408/429 responses count toward it; a 404 or 401 says something about the request, not the host.5
Cooldown before trying againHow long an open circuit rejects requests before letting a single probe through. A failed probe re-opens it for another cooldown.30 seconds

Defaults rationale. Five concurrent requests per host is generous enough that a healthy destination with several queued deliveries and workflow actions in flight isn't artificially slowed down, while still bounding how hard Tagnet is allowed to push any one system. Five consecutive failures tolerates a brief blip without giving up too fast: a lower threshold trips on the sort of isolated network hiccup every deployment sees occasionally, and the cost is not one delayed request, it's every workflow and queued delivery targeting that host waiting out the cooldown.

When to tune. Raise Requests at once to one system for a destination that can genuinely take more concurrent load; lower it for one that enforces its own rate limit. Raise Consecutive failures before requests are held back for a flaky network where brief failures are routine and you'd rather keep trying than pause. Raise Cooldown before trying again for a system with longer typical outages, so Tagnet isn't repeatedly probing a host you already know is still down.

Who gets notified when a system stops responding is a separate concern, set by the alerting thresholds on Integration / Outbox above and by Alert Routing. This policy only decides when Tagnet stops calling the system, not who hears about it.

IntegrationOutbox retention​

Controls how long Integration Outbox delivery rows are kept after reaching a terminal state. Like DispatchOutbox retention, the policy has two independent windows: sent rows are the high-volume, routine case, while failed and dead-lettered rows are what an administrator reaches for during troubleshooting.

Deliveries still in progress (not yet Sent, Failed, or DeadLettered) are never deleted by retention.

SettingTables affectedDescriptionDefault
Sent retention (days)IntegrationOutbox where Status=SentHow long to keep successfully delivered rows.30
Failed / dead-lettered / abandoned retention (days)IntegrationOutbox where Status=Failed, DeadLettered, or AbandonedHow long to keep failed, dead-lettered, and abandoned rows.90

Defaults rationale. Thirty days of sent history is routine delivery confirmation with little forensic value past that point. Ninety days for failed and dead-lettered rows matches the Dispatch Outbox default and gives a full quarter of evidence for troubleshooting a failing integration. Deliveries carry their full request body, so this policy is usually the largest driver of table growth for this feature; shortening the sent window is the first lever to pull if storage is a concern.

Disabling IntegrationOutbox retention. Setting Enabled = Off stops the engine from deleting delivery rows. Use this only temporarily, for example while preserving evidence for a delivery incident under investigation. Re-enable the policy when finished.

Alerting policies​

Alerting policies control how alert notifications are delivered and how long resolved alerts are kept. The thresholds that decide when the Integration Outbox raises its alerts live on the Integration / Outbox policy above; this section governs what happens after an alert exists.

Alerting / Delivery​

Controls the pacing of alert notifications: how long a new alert is held before anyone is notified, how often an unhandled alert re-notifies, and the flood control. This policy is always-on: the notifier falls back to these defaults when no policy row exists, so disabling it would silently swap admin-tuned values for defaults rather than turning notifications off. See Noise controls for how these interact with per-rule overrides.

SettingDescriptionDefault
Grouping window (minutes)How long a brand-new alert is held before the first notification, so a condition that fires and immediately clears never reaches anyone. Routing rules can override this per rule; when several rules match, the shortest window wins.2
Repeat interval (hours)How often an alert that stays Active and unacknowledged re-notifies its audience. Acknowledging the alert stops repeats. Routing rules can override this per rule.4
Max notifications per type per hourFlood control: how many distinct alerts of one type can notify within an hour. Past the cap, further alerts of that type collapse into a single digest notification per hour.10
Notifier poll interval (seconds)How often the delivery service scans for alerts that are due to send.30

Defaults rationale. Two minutes of grouping filters out self-healing blips without meaningfully delaying real problems. Four hours of repeat is a reminder cadence, not a siren: the alert is already visible in the inbox, and acknowledge exists precisely to silence repeats once someone owns the problem. The hourly cap counts distinct alerts rather than sends, because a flood of distinct alerts is how a bad night manifests; the digest keeps the audience informed without burying them.

When to tune. Raise the grouping window if flapping conditions in your environment routinely take longer than two minutes to settle. Shorten the repeat interval for environments with strict response-time expectations, or lengthen it if repeats feel naggy. Lower the per-type cap if a noisy integration endpoint floods the inbox faster than the default catches.

Alerting / SystemHealth​

Thresholds for the system health alerts: disk space, memory, CPU, database quota, and how often the monitor samples. Background job failures, the request queue, and dead-lettered email deliveries are condition-based, with nothing to tune. Like the other alerting policies, this one is always-on: the monitor falls back to these defaults when no policy row exists.

SettingDescriptionDefault
Free space warning threshold (GB)Free space on the database volume below this raises the storage alert at Warning.10
Free space critical threshold (GB)Below this, the open storage alert escalates to Critical. Must be below the warning threshold.2
Memory warning threshold (%)The Tagnet process's own memory use as a share of what it can use (the container's limit on hosted instances, machine memory on-prem). Must hold across consecutive samples to raise.85
Memory critical threshold (%)Above this, the open memory alert escalates to Critical. Must be above the warning threshold.95
CPU sustained threshold (%)Process CPU across all cores; raises only when held over several consecutive samples, so bursts never page anyone.90
Database size quota (GB)The cap the database-size alert measures against, warning at 80% and Critical at 95% of it. Zero means auto-detect: the provider's own cap when it reports one (Azure SQL), otherwise no size alert.0 (auto)
Sample interval (minutes)How often the monitor samples every condition.5

Defaults rationale. Ten gigabytes of headroom gives an administrator days-to-weeks of warning on typical ingest volumes before growth becomes an emergency; two gigabytes is the point where cleanup must happen now. The memory and CPU defaults sit high enough that only genuine pressure crosses them, and both use consecutive-sample holds plus a resolve margin so a machine hovering at a threshold neither pages repeatedly nor raise/resolve-cycles. A single-tenant on-prem install is exactly the environment where nobody is watching a resource graph, which is why this policy exists.

When to tune. Size the storage warning threshold to your ingest rate: a heavy scan-event volume grows the database faster and deserves a larger buffer. Set an explicit Database size quota on-prem when your organization caps database growth by convention; hosted Azure SQL instances are covered automatically by their own quota. Lower the CPU threshold only if the host is shared with other workloads that suffer before Tagnet does.

Alerting / Devices​

Thresholds for the device alerts: when a reader is reported offline, when an antenna port is reported disconnected, and how often the monitor checks. The thresholds apply to every reader. Like the other alerting policies, this one is always-on: the monitor falls back to these defaults when no policy row exists or the policy is disabled, so reader alerting cannot be switched off by accident. To stop watching one reader, deactivate it.

SettingDescriptionDefault
Offline after (minutes)How long an active reader may go without checking in before it is reported offline. Between 2 minutes and 7 days.10
Antenna disconnected after (minutes)How long a connected antenna port must report disconnected before it is reported. Between 2 minutes and 1 hour.5
Check interval (minutes)How often the monitor checks the readers.1

Defaults rationale. Zebra readers send a heartbeat every minute by default, so ten minutes of silence is several missed heartbeats in a row: long enough to ride out a reboot or a brief network drop, short enough that a dead reader is reported well before anyone notices inventory has stopped moving. Five minutes for an antenna is several heartbeats in a row reporting the port down, which a loose connector or a knocked cable will easily exceed and a momentary glitch will not.

When to tune. Keep Offline after above your readers' heartbeat interval, or every reader is reported between heartbeats. Readers at remote sites on cellular or satellite links drop off for short spells as a matter of course; if they cause repeated offline alerts that clear on their own, raise the threshold. A facility with readers on wired networks can lower it to hear sooner. Every reader shares the one threshold for now, so choose the value that suits the readers whose silence would cost the most.

AlertInstance retention​

Controls how long Resolved alerts are kept, measured from when they resolved. Active and Acknowledged alerts are never deleted by retention, no matter how old: an alert nobody has dealt with is exactly the row that must not quietly disappear.

SettingTables affectedDescriptionDefault
Resolved retention (days)AlertInstances where State=ResolvedHow long to keep resolved alerts after resolution.90

Defaults rationale. Ninety days matches the failed-delivery and execution-history defaults and gives a full quarter of incident history. This policy is what keeps a flapping condition, one that fails and recovers every few minutes, from growing the alerts table forever with old closed rows.

Security policies​

Security policies control how Tagnet captures security-relevant events and signals. They live under the Security category on the Policies page.

AuthAudit policy​

The Security / AuthAudit policy controls whether UserLoginFailed events with reason user_not_found are written to the Security Events log. This reason fires when someone attempts to sign in with an email address that does not exist in Tagnet.

This policy ships enabled on every new installation. The only control is the Enable / Disable button on the policy detail page; there are no additional configuration fields.

SettingBehavior
EnabledAll failed sign-in reasons are logged, including user_not_found. Complete visibility into unknown-email login attempts, credential stuffing, and reconnaissance.
Disableduser_not_found events are suppressed. All other failure reasons (bad_password, account_disabled, account_locked) continue to fire regardless of this setting.

When to disable: On internet-exposed hosted deployments, bot traffic can generate thousands of user_not_found events daily using attacker-controlled email addresses. If this volume obscures legitimate audit signal, disable the policy. On on-premises deployments with a controlled login surface, the user_not_found events are useful signal, so leave the policy enabled.

Note: Disabling this policy does not affect events for known users. Failed logins with reasons bad_password, account_disabled, or account_locked always fire, because those involve a real user account.

Reader certificate validation policy​

The Security category's ReaderCertificateValidation policy, listed under that key on the Policies page, controls whether Tagnet requires a valid TLS certificate on every connection it makes to a reader, whether that's an admin login, a configuration deploy, or any other outbound call to the reader's own API.

This policy ships disabled on every new installation. RFID readers, out of the box, present a self-signed certificate on a private network; requiring validation by default would have disconnected every existing installation on upgrade.

SettingBehavior
Disabled (default)Tagnet accepts whatever TLS certificate the reader presents, self-signed or otherwise.
EnabledTagnet refuses a reader connection whose certificate does not validate against a trusted chain.

When to enable: if your organization has issued your readers TLS certificates that chain up to a trust root Tagnet recognizes. Enabling this before that's true will disconnect every reader, so confirm the reader certificates are in place first.

When enabled and a reader's certificate fails validation, the connection attempt fails with a message explaining that the certificate was refused and that this is a policy setting, not a bug: reissue the reader a trusted certificate, or turn this policy back off under Policies.

Changes to this policy take effect immediately on the next connection attempt; no restart is required.

Commission policies​

Commission policies control operational limits and behavior for the synchronous commission (print) API. They live under the Commission category on the Policies page.

Commission / Limits​

Controls the maximum number of rows that a single commission API call may contain. This limit applies to both the POST /api/v1/commission submit endpoint and the POST /api/v1/commission/validate dry-run endpoint.

The effective cap is the lesser of the configured MaxBatchSize and a code-enforced hard ceiling of 2000. Even if MaxBatchSize is set above 2000, the system never accepts a batch larger than 2000 rows. A batch that exceeds the effective cap is rejected immediately with HTTP 400 before any inventory is created or any sequence generator is advanced.

SettingDescriptionDefault
MaxBatchSizeMaximum rows per commission request. The system applies min(MaxBatchSize, 2000) as the actual cap.500

Why this limit exists. Commission calls are synchronous: the API waits for all rows to be processed before responding. A very large batch in a single synchronous request increases request latency, holds a database transaction open longer, and makes partial-failure recovery harder for the calling system. The limit keeps individual calls fast and predictable; larger jobs should be chunked into multiple requests, each with its own batch key for idempotency.

When to raise MaxBatchSize. If your print jobs regularly exceed 500 items per call and your Tagnet instance handles the load well (short response times, no timeouts), you can raise this value up to 2000. Test with a representative batch before changing production workflows.

When to lower MaxBatchSize. On resource-constrained deployments or when multiple systems commission concurrently, a lower cap (100–250) reduces the per-request database pressure.

Note: This policy has no Enabled/Disabled toggle. The cap is always in effect; setting MaxBatchSize to a very large number does not disable it, because the 2000 hard ceiling applies regardless.

Scoping policies​

Scoping policies control how access scopes govern your catalog. They live under the Scoping category on the Policies page.

Scoping / Site Catalog​

Controls whether catalog definitions (items, item types, location types, device types, transaction types, custom fields, and sequence generators) can be owned by individual site scopes in addition to the shared system scope.

This policy ships disabled on every new installation. There are no additional configuration fields; the only control is the Enable / Disable button on the policy detail page.

SettingBehavior
Disabled (default)New definitions are created in the shared system scope with no prompt. The scope field is hidden on definition forms.
EnabledDefinition create/edit forms show an Availability choice instead of a plain scope field: "Shared across {your organization}" (the default) or "{Site} only." The Items page also gains an Availability filter.

When to enable: multi-site deployments where individual sites need to maintain catalog entries that don't apply everywhere, for example, a site-specific consumable that only one plant stocks, or a regional item type. Single-site deployments and multi-site deployments with a fully shared catalog should leave this disabled; it keeps the scope question invisible and routes everything to the shared catalog automatically.

Disabling after site-scoped definitions exist. Turning this policy off does not touch any definitions already scoped to a site; it only hides the Availability affordance going forward. Existing site-scoped catalog entries stay exactly as they are.

Note: This policy gates UI affordances and defaults only, never data validity. A site-scoped definition created while the policy was enabled remains fully valid and usable if the policy is later disabled.

Scoping / Cross-Scope Transfer​

Decides what happens when an inventory item is moved to a location that belongs to a different scope than the item. Same-scope moves, including a departure to a site's own transit location, never consult this policy, and neither do moves involving the shared scope or a close-out. The decision is made inside the move itself, so it covers every path that moves inventory: the UI, the REST API, movement rules, and the Move Inventory workflow action.

This policy is always on; the only setting is the mode, which ships as Reject.

ModeBehavior
Reject (default)The item does not move. A workflow reports the tag as skipped, with the scope pair in the message, and the scan batch succeeds. A manual move gets a warning, and the REST move endpoint returns 409 Conflict with the reason. A Cross-scope movement rejected alert (in the Inventory category) is raised for the scope whose read point saw the item, one open alert per origin-and-destination scope pair: ten packs from one site rejected at another in the same scan fold into a single alert whose occurrence count says how many, so the routed audience gets one notification rather than ten. Route it with Alert Routing to notify the people who should know.
TransferThe item moves and the destination scope becomes its owner, in the same database transaction as the movement. The transaction records both scopes and shows a Transferred badge in the item's transaction history; that record is the audit trail, and no alert is raised. Transfer is refused, with the reason recorded and the rejection alert raised, when the item's catalog definition or a custom field it carries a value for belongs to a third scope rather than the destination or the shared catalog.

When to use Transfer: deployments where stock travels between scopes as a matter of course, such as yard networks where packs leave one site through a portal and arrive at another. Leave Reject for isolated scopes, where another scope's item appearing at a read point is itself the thing to investigate.

To be told about transfers, add an action tag condition on Scope Transferred in the Movement group: a Send Notification or HTTP Integration action after the Move Inventory action then fires only for tags that changed owner.

Every transaction is stamped with the item's scope at the time it was written, so after a transfer the origin scope keeps the history from its own tenure, the destination scope sees the history from the transfer onward, and the transfer itself is visible to both.

Workflow policies​

Workflow policies control how scan event processing behaves around the Tagnet service itself. They live under the Workflows category on the Policies page.

Workflows / CatchUp​

Decides what happens to scan events whose workflows had not run when the service stopped. Workflow processing is queued in memory, so an upgrade, a restart, or a recovery restart can interrupt events that were waiting in the queue, were cut off mid-run, or were saved as the service stopped (see When the Service Stops). At startup, before Tagnet accepts new reads, this policy decides whether those events run.

This policy ships enabled. The Enabled toggle is the on/off switch.

StateBehavior
Enabled (default)Events no older than the maximum age are run, oldest first. Older events are kept and marked Skipped.
DisabledNone of them run. All are kept and marked Skipped.
SettingDescriptionDefault
Maximum age (minutes)How old an event may be and still be run. Measured from when the scan event was saved. Allowed range: 1-1440.15

What to expect:

  • Finished work is not repeated. Workflows that already finished for an event are not run again. A workflow cut off part-way has no run record, so it runs again from the start. An action that ran before the cut, such as an HTTP call, can repeat.
  • A full queue drops events. Events that do not fit in the workflow queue at startup (200 per read point, 1,000 overall) are marked Dropped. See Queue Processing for how the queue is organized.
  • The outcome is visible. Each event's Status shows Processed, Skipped, or Dropped (see Scan Event Status), and the Workflows Diagnostics tab shows a summary after a startup that found cut-off events.

When to leave it on. Anywhere every read must be acted on: receiving, a dock door or portal that moves inventory, a consumables cabinet that decrements stock. A restart then delays the work instead of losing it. Raise the maximum age if your maintenance windows run longer than 15 minutes and the work is still worth doing afterward.

When to turn it off. Where only current reads matter: real-time location display, or a warning light that should reflect what is in the read zone now. Running a quarter-hour-old event would act on news that is no longer true.

Note: This policy covers only events Tagnet had already saved. Reads a reader sends while Tagnet is stopped are refused, and a reader that buffers and retries sends them after the restart. Tagnet processes those as new reads and runs workflows normally, so catch-up does not apply to them and Tagnet does not yet filter stale buffered reads. If only current reads matter at a site, also turn off retry buffering on the reader. See Reads while the service is stopped.

Editing a policy​

  1. Open the Policies tab of the System page.
  2. Click the policy row to open its detail page.
  3. Click Edit to open the configuration form.
  4. Adjust values and click Save. The new configuration takes effect immediately on the next call that reads the policy.

Validation runs before save: invalid values (e.g., raw retention greater than rollup retention) are rejected with an inline message and no change is persisted.

Audit trail​

Every save, enable, and disable writes a SecurityEvent with EventType = PolicyChanged. The Details field contains the action (Created/Updated/Enabled/Disabled), the old configuration JSON, and the new configuration JSON. View the trail on the Security Events page.