---
title: SAIF.NetCore.Clients.UC4 business rules
summary: Enumerated business rules with per-rule trigger, enforcement, and outcomes.
status: draft
owner: SAIF Framework Platform Team
last_reviewed: 2026-10-02
library: SAIF.NetCore.Clients.UC4
reference_type: package
---

# SAIF.NetCore.Clients.UC4 business rules

This page captures business rules enforced by this library.

## BR-UC4-01 Required settings must exist at construction

| Field | Value |
| --- | --- |
| **Statement** | `uc4coreapi:url` and `uc4coreapi:schedulejobroute` must both exist in the Environment API section `uc4coreapi`, or construction of `UC4Client` fails. |
| **Trigger** | `UC4Client` constructor runs (DI resolution or direct `new`). |
| **Enforcement** | `UC4Client..ctor` calls `EnsureSettingExists` for each key, which calls `IEnvironmentClient.HasSetting`; a `false` result throws `NotSupportedException`. |
| **Outcome** | Construction aborts with `NotSupportedException` if either key is absent; the client cannot be used in that environment. |
| **Source** | src/SAIF.NetCore.Clients.UC4/SAIF.NetCore.Clients.UC4/SAIF.NetCore.Clients.UC4/Services/UC4Client.cs |

---

## BR-UC4-02 Settings existence is validated independently of value content

| Field | Value |
| --- | --- |
| **Statement** | An empty string value for `uc4coreapi:url` or `uc4coreapi:schedulejobroute` passes the existence check and construction succeeds; the value is not validated for shape or non-emptiness. |
| **Trigger** | Construction reads the setting value after the existence check has passed. |
| **Enforcement** | `UC4Client..ctor` reads `GetSettingValue`, which returns `ConfigValue?.Value ?? ""` with no further validation. |
| **Outcome** | An empty `ServiceUrl` or `ScheduleJobEndpoint` is accepted at construction and fails later, during `ScheduleJob`, with `UriFormatException` or a malformed route. |
| **Source** | src/SAIF.NetCore.Clients.UC4/SAIF.NetCore.Clients.UC4/SAIF.NetCore.Clients.UC4/Services/UC4Client.cs (call site); src/SAIF.NetCore.EnvironmentClient/SAIF.NetCore.EnvironmentClient/SAIF.NetCore.EnvironmentClient/BaseEnvironmentClient.cs (declares `GetSettingValue`'s `ConfigValue?.Value ?? ""` behavior) |

---

## BR-UC4-03 Job scheduling URL is built by direct string concatenation

| Field | Value |
| --- | --- |
| **Statement** | The request URL for scheduling a job is `ScheduleJobEndpoint + jobname`, built by direct string concatenation with no URL-encoding of `jobname`. |
| **Trigger** | `ScheduleJob(appname, jobname)` is called. |
| **Enforcement** | `UC4Client.ScheduleJob` builds `$"{ScheduleJobEndpoint}{jobname}"` and passes it to `IHttpJsonClient.PostAsync`. |
| **Outcome** | A `jobname` containing URL-reserved characters (`/`, `?`, `#`, spaces) can route to an unintended endpoint or be rejected by the server*. |
| **Source** | src/SAIF.NetCore.Clients.UC4/SAIF.NetCore.Clients.UC4/SAIF.NetCore.Clients.UC4/Services/UC4Client.cs |

---

## BR-UC4-04 Caller identity is sent via fixed and caller-supplied headers

| Field | Value |
| --- | --- |
| **Statement** | Every scheduling request carries `Client-Name` = the running machine name, `Application-ID` = the caller-supplied `appname`, and `Client-User` = the fixed string `"SAIF.NetCore.Clients.UC4"`. |
| **Trigger** | `Init` runs on the first `ScheduleJob` call, calling `SetClientInfo`; headers are attached on every subsequent POST. |
| **Enforcement** | `UC4Client.Init` calls `IHttpJsonClient.SetClientInfo(ServiceUrl, ClientName, Environment.MachineName, appName)`; `HttpJsonClient.CreateRequest` attaches the headers from the stored `ClientInfo`. |
| **Outcome** | The UC4 API receives a consistent, caller-identifiable set of headers for every request from a given client instance. |
| **Source** | src/SAIF.NetCore.Clients.UC4/SAIF.NetCore.Clients.UC4/SAIF.NetCore.Clients.UC4/Services/UC4Client.cs (call site); src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/HttpJsonClient/HttpJsonClient.cs (declares `SetClientInfo` and `CreateRequest`, which attach the headers) |

---

## BR-UC4-05 Application ID cannot be null

| Field | Value |
| --- | --- |
| **Statement** | `appname` cannot be null; a null value throws `ArgumentNullException` during initialization, inherited from the shared `ClientInfo` construction logic in `SAIF.NetCore.HttpClients`. |
| **Trigger** | `Init(appName)` constructs a `ClientInfo` with the supplied `appName`. |
| **Enforcement** | `ClientInfo`'s constructor (in `SAIF.NetCore.HttpClients`) throws `ArgumentNullException` when any constructor argument, including `applicationId`, is null. |
| **Outcome** | A null `appname` on the first `ScheduleJob` call throws before any HTTP request is attempted; `IsConfigured` remains `false` so the next call retries `Init`. |
| **Source** | src/SAIF.NetCore.Clients.UC4/SAIF.NetCore.Clients.UC4/SAIF.NetCore.Clients.UC4/Services/UC4Client.cs (call site); src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/ClientInfo.cs (declares the constructor that throws `ArgumentNullException`) |

---

## BR-UC4-06 Client identity is fixed on first use

| Field | Value |
| --- | --- |
| **Statement** | The `appname` supplied on the first `ScheduleJob` call becomes the fixed `Application-ID` for every subsequent call on that same `UC4Client` instance; later calls with a different `appname` do not change it. |
| **Trigger** | A second or later `ScheduleJob` call passes a different `appname` than the first call. |
| **Enforcement** | `UC4Client.ScheduleJob` only calls `Init` when `IsConfigured == false`; once `true`, `appname` on subsequent calls is accepted by the method signature but never applied. |
| **Outcome** | Consumers that reuse a single injected `UC4Client` across multiple logical applications will silently misreport `Application-ID` after the first call*. |
| **Source** | src/SAIF.NetCore.Clients.UC4/SAIF.NetCore.Clients.UC4/SAIF.NetCore.Clients.UC4/Services/UC4Client.cs |

---

## BR-UC4-07 IsConfigured has a public setter that bypasses initialization

| Field | Value |
| --- | --- |
| **Statement** | `IsConfigured` has a public setter; external code can set it to `true` before any `ScheduleJob` call, skipping `Init` and the underlying `SetClientInfo` call entirely. |
| **Trigger** | External code sets `client.IsConfigured = true` directly. |
| **Enforcement** | `UC4Client.IsConfigured { get; set; }` has no guard against external mutation; `ScheduleJob` trusts the flag unconditionally. |
| **Outcome** | The subsequent POST fails with `ClientNotInitializedException` from `HttpJsonClient.ValidateInitialization`, unless the injected `IHttpJsonClient` was separately initialized elsewhere. |
| **Source** | src/SAIF.NetCore.Clients.UC4/SAIF.NetCore.Clients.UC4/SAIF.NetCore.Clients.UC4/Services/UC4Client.cs (call site); src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/HttpJsonClient/HttpJsonClient.cs (declares `ValidateInitialization` and `ClientNotInitializedException`) |

---

## BR-UC4-08 Scheduling requests retry automatically on specific transient status codes

| Field | Value |
| --- | --- |
| **Statement** | A scheduling POST that returns status 401, 502, or 503 is retried automatically, up to 3 times, with a fixed delay between attempts. |
| **Trigger** | The UC4 API responds with one of the retryable status codes. |
| **Enforcement** | `HttpJsonClient.ExecuteAsync` (in `SAIF.NetCore.HttpClients`) checks the response status against `RetryOnStatusCodes` (default `{401, 502, 503}`) and retries up to `retries` (3) times with `RetryDelayInMs` (default 1000 ms) between attempts. |
| **Outcome** | Whether a retry after a 502/503 that the UC4 API actually processed can submit a duplicate job depends on whether the UC4 Core API's schedule endpoint is idempotent, which is unknown**. |
| **Source** | src/SAIF.NetCore.Clients.UC4/SAIF.NetCore.Clients.UC4/SAIF.NetCore.Clients.UC4/Services/UC4Client.cs (call site); src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/HttpJsonClient/HttpJsonClient.cs (declares `ExecuteAsync`'s retry loop); src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/HttpClientOptions.cs (declares the `RetryOnStatusCodes`/`RetryDelayInMs` defaults) |

---

## BR-UC4-09 Scheduling failures are returned, not thrown

| Field | Value |
| --- | --- |
| **Statement** | A non-success HTTP response from the UC4 API is returned to the caller as `IHttpRequestResult.IsSuccessful = false`; it is never raised as a thrown exception from `ScheduleJob` itself. |
| **Trigger** | The UC4 API responds with a non-success status after all retries are exhausted. |
| **Enforcement** | `HttpJsonClient.ExecuteAsync` builds an `HttpRequestResult` with `IsSuccessful` set from the response status and returns it; it does not throw for HTTP-level failures. |
| **Outcome** | Consumers that do not check `IsSuccessful` will silently treat a failed scheduling attempt as successful. |
| **Source** | src/SAIF.NetCore.Clients.UC4/SAIF.NetCore.Clients.UC4/SAIF.NetCore.Clients.UC4/Services/UC4Client.cs (call site); src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/HttpJsonClient/HttpJsonClient.cs (declares `ExecuteAsync`'s `HttpRequestResult` construction) |

---

## BR-UC4-10 Scheduled jobs start with an empty JSON object

| Field | Value |
| --- | --- |
| **Statement** | Every scheduling POST carries an empty JSON object (`{}`), with no job parameters, schedule time, or metadata passed through this API surface. |
| **Trigger** | `ScheduleJob` builds the POST request. |
| **Enforcement** | `UC4Client.ScheduleJob` calls `IHttpJsonClient.PostAsync($"{ScheduleJobEndpoint}{jobname}", new { })`, which RestSharp serializes to the JSON object `{}`; `jobname` is only present in the URL path, never in the body. |
| **Outcome** | UC4 jobs triggered through this client must be pre-configured on the UC4/Automic side with all parameters; this client can only start them by name. |
| **Source** | src/SAIF.NetCore.Clients.UC4/SAIF.NetCore.Clients.UC4/SAIF.NetCore.Clients.UC4/Services/UC4Client.cs |

## Related documentation
- [Index](index.md)
- [Overview](overview.md)
- [Architecture](architecture.md)
- [Dependencies](dependencies.md)
- [Configuration](configuration.md)
- [Operations and processes](operations-processes.md)
- [API reference](api/index.md)

> **Note:** Statements marked with * were inferred from code structure, naming conventions, or
> contextual evidence where direct confirmation was not available in source artifacts. Statements
> marked with ** could not be confirmed; treat as unknown until verified.

## Sources
- `src/SAIF.NetCore.Clients.UC4/SAIF.NetCore.Clients.UC4/SAIF.NetCore.Clients.UC4/SAIF.NetCore.Clients.UC4.csproj`
- `src/SAIF.NetCore.Clients.UC4/SAIF.NetCore.Clients.UC4/SAIF.NetCore.Clients.UC4/Constants.cs`
- `src/SAIF.NetCore.Clients.UC4/SAIF.NetCore.Clients.UC4/SAIF.NetCore.Clients.UC4/IUC4Client.cs`
- `src/SAIF.NetCore.Clients.UC4/SAIF.NetCore.Clients.UC4/SAIF.NetCore.Clients.UC4/Services/UC4Client.cs`
- `src/SAIF.NetCore.EnvironmentClient/SAIF.NetCore.EnvironmentClient/SAIF.NetCore.EnvironmentClient/BaseEnvironmentClient.cs` — declares the `HasSetting`/`GetSettingValue` behavior enforced by [BR-UC4-01](#br-uc4-01-required-settings-must-exist-at-construction)/[BR-UC4-02](#br-uc4-02-settings-existence-is-validated-independently-of-value-content).
- `src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/HttpJsonClient/HttpJsonClient.cs` — declares `SetClientInfo`, `ValidateInitialization`, and `ExecuteAsync`'s retry/result behavior enforced by [BR-UC4-04](#br-uc4-04-caller-identity-is-sent-via-fixed-and-caller-supplied-headers), [BR-UC4-07](#br-uc4-07-isconfigured-has-a-public-setter-that-bypasses-initialization), [BR-UC4-08](#br-uc4-08-scheduling-requests-retry-automatically-on-specific-transient-status-codes), [BR-UC4-09](#br-uc4-09-scheduling-failures-are-returned%2C-not-thrown).
- `src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/ClientInfo.cs` — declares the constructor behavior enforced by [BR-UC4-05](#br-uc4-05-application-id-cannot-be-null).
- `src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/HttpClientOptions.cs` — declares the retry defaults referenced by [BR-UC4-08](#br-uc4-08-scheduling-requests-retry-automatically-on-specific-transient-status-codes).
