# Designing a persistent case entity schema

> <!-- Audience: Intermediate — Automation Developers, Business Architects -->

<!-- Audience: Intermediate — Automation Developers, Business Architects -->

## Overview

The **case entity** **[Coming Soon]** is the central, persistent data model that every stage, task, and rule reads from and writes to throughout a case's lifetime. A well-designed entity schema separates **input fields** (data provided at case creation) from **computed fields** (data produced by tasks during processing) and assigns **clear field ownership** so that no two tasks write to the same field. Follow this guide to define a schema that prevents data collisions during write-back and supports reliable stage rules.

## Prerequisites

- Access to **Studio Web**.
- A defined business process with identified stages and tasks.
- Familiarity with the [core concepts of Maestro Case](introduction-to-maestro-case.md#core-concepts).
- A Data Fabric instance available in your UiPath environment (recommended for native entity creation).

## Step 1: understanding the out-of-the-box data objects

Every case project automatically creates three data objects:

| Object | Purpose |
|---|---|
| **Case Entity** **[Coming Soon]** | Holds all structured business data that stages, tasks, and rules read from and write to. |
| **Case Documents** | Stores attachments and files associated with the case (receipts, photos, contracts). |
| **Case Comments** | Stores notes, annotations, and communications added by case workers throughout the lifecycle. |

All three share an immutable `caseID` system field that is auto-generated at case creation. This field ties all case data together and cannot be changed.

Focus your schema design effort on the **Case Entity** — it is the single source of truth for all case processing logic.

## Step 2: choosing where the entity lives

Before defining fields, decide how to source the entity. Select the option that best fits your data ownership model:

| Source | Description | When to Use |
|---|---|---|
| **Native in Data Fabric** (recommended) | Create the entity as a native business entity in Data Fabric and link it to your case. | New processes where you own the data model. |
| **Virtual Data Object (VDO) in Data Fabric** | Register an external source as a VDO in Data Fabric and link the VDO to the case. | Entity data lives in an external system (CRM, ERP) and you want to reference it without duplicating. |
| **Case trigger payload** | Pass existing data in the case creation trigger (for example, an API connector). The payload fields become case fields available across all stages. | Lightweight integrations where you hydrate the case at creation time. |

## Step 3: identifying and categorize your fields

### Mapping out the full case lifecycle

List every stage and every task in your case plan. For each task, identify:

- What data it **needs to read** (inputs).
- What data it **produces** (outputs).

### Classifying fields into two categories

Separate every field in your schema into one of two groups:

| Category | Definition | Characteristics | Examples |
|---|---|---|---|
| **Input fields** | Data provided when the case is created, either by a trigger payload, a form submission, or an external system. | Populated at creation. Typically read-only after hydration. Required for initial routing and task execution. | `policyNumber`, `claimantName`, `dateOfLoss`, `lossDescription` |
| **Computed fields** | Data produced by tasks during case processing. These fields start empty and are written back as tasks complete. | Empty at creation. Written by a specific task. Consumed by downstream tasks and rules. | `validationResult`, `damageEstimate`, `adjusterDecision`, `paymentReference` |

### Marking input fields as read-only

Input fields such as `employeeId`, `policyNumber`, or `reportId` should never be overwritten by tasks. Document these fields as read-only in your schema to prevent accidental modification during processing.

## Step 4: establishing field ownership

Field ownership is the most critical principle for preventing data collisions. Each computed field in the entity must be written by **exactly one task**.

### Assigning one writer per field

For every computed field, designate the single task responsible for writing to it. If two tasks write to the same field, the last writer wins and previous data is lost.

### Annotating ownership in your schema

Use a `writtenBy` annotation (or equivalent comment) to document which task owns each computed field. While the platform does not enforce this annotation at runtime, it serves as a design contract that prevents collisions during development.

### Using namespaced fields to avoid ambiguity

When multiple tasks produce similar types of output, namespace your fields to keep them distinct. For example:

- Use `photoAnalysis` for the output of an image analysis agent task.
- Use `fieldInspection` for the output of a human field inspection task.
- Avoid a generic `analysisResult` field that multiple tasks might contend for.

## Step 5: defining the schema

### Creating the entity in your chosen source

Navigate to the Case Entity designer in Studio Web. Create a new entity with a descriptive name that matches your business domain (for example, `AutoInsuranceClaim` or `ExpenseReport`).

### Adding input fields first

Define all input fields with their types, required flags, and any default values. Set `required: true` for fields that must be present at case creation.

### Adding computed fields with ownership annotations

Define all computed fields with their types, set `required: false` (these fields are empty at creation), and annotate each with the task that writes to it.

### Reviewing a reference schema

The following example demonstrates the input-versus-computed pattern with field ownership for an auto insurance claims case:

```json
{
  "entityName": "AutoInsuranceClaim",
  "fields": {
    // --- Input fields (populated by trigger, read-only after creation) ---
    "claimId":            { "type": "string",  "required": true,  "generated": true },
    "policyNumber":       { "type": "string",  "required": true },
    "claimantName":       { "type": "string",  "required": true },
    "claimantEmail":      { "type": "string",  "required": true },
    "dateOfLoss":         { "type": "date",    "required": true },
    "lossDescription":    { "type": "string",  "required": true },
    "vehicleInfo":        { "type": "object",  "required": true },
    "photos":             { "type": "array",   "items": "url" },
    "policeReportNumber": { "type": "string",  "required": false },

    // --- Computed fields (written by tasks during processing) ---
    "policyValid":        { "type": "boolean", "writtenBy": "Validate Policy" },
    "extractedDetails":   { "type": "object",  "writtenBy": "Extract Details" },
    "photoAnalysis":      { "type": "object",  "writtenBy": "Analyze Photos" },
    "fieldInspection":    { "type": "object",  "writtenBy": "Field Inspection" },
    "policeReport":       { "type": "object",  "writtenBy": "Retrieve Police Report" },
    "damageEstimate":     { "type": "decimal", "writtenBy": "Estimate Damage" },
    "adjusterDecision":   { "type": "string",  "enum": ["approve", "deny", "investigate_more"] },
    "payoutAmount":       { "type": "decimal", "writtenBy": "Calculate Payout" },
    "paymentReference":   { "type": "string",  "writtenBy": "Issue Payment" }
  }
}
```

## Step 6: wiring input and output mappings to tasks

After defining the schema, connect it to tasks through input and output mappings.

### Configuring input mappings

For each task, select only the Case Entity fields the task needs to read. This controls what data the task can see.

Example — **Validate Policy** task input mapping:

```json
"input": {
  "policyNumber": "caseEntity.policyNumber"
}
```

### Configuring output mappings

For each task, map the task's result to the specific Case Entity field that the task owns. This is the write-back mechanism.

Example — **Validate Policy** task output mapping:

```json
"output": {
  "caseEntity.policyValid": "taskOutput.policyValid"
}
```

### Verifying that output mappings respect field ownership

Cross-check every task's output mapping against your schema annotations. Confirm that no two tasks write to the same Case Entity field.

## Step 7: validating the schema against rules

Stage rules (entry, complete, exit, re-entry) evaluate against Case Entity fields. Verify that:

- Every field referenced in a rule's **IF** clause is present in the schema.
- The field is written by a task that completes **before** the rule evaluates.
- The field type matches the operator used in the rule (for example, do not use a string comparison on a decimal field).

Example — an Exit rule on an Intake stage depends on the `policyValid` field:

```
WHEN PolicyCheckCompleted event arrives
  IF caseEntity.policyValid == false
```

Confirm that the **Validate Policy** task writes `policyValid` and completes within the Intake stage before this Exit rule evaluates.

## Expected outcome

After completing these steps, you have a case entity schema that:

- Clearly separates **input fields** (populated at case creation) from **computed fields** (written by tasks during processing).
- Assigns **explicit field ownership** so that exactly one task writes to each computed field.
- Uses **namespaced field names** to prevent ambiguity and collisions.
- Supports reliable **write-back** from tasks to the entity, ensuring downstream rules and tasks consume accurate, non-conflicting data.
- Documents the data contract between the case plan and its tasks through `writtenBy` annotations.

## Code snippet

The following is a second reference schema for an expense report use case, demonstrating the same input-versus-computed pattern:

```json
{
  "entityName": "ExpenseReport",
  "fields": {
    // --- Input fields (populated at case creation) ---
    "reportId":          { "type": "string",  "required": true,  "generated": true },
    "employeeId":        { "type": "string",  "required": true },
    "employeeName":      { "type": "string",  "required": true },
    "department":        { "type": "string",  "required": true },
    "totalAmount":       { "type": "decimal", "required": true },
    "currency":          { "type": "string",  "default": "USD" },
    "lineItems":         { "type": "array",   "items": "ExpenseLineItem" },

    // --- Computed fields (written by tasks during processing) ---
    "validationResult":  { "type": "object",  "required": false, "writtenBy": "Validate Receipts" },
    "categories":        { "type": "array",   "required": false, "writtenBy": "Categorize Expenses" },
    "anomalyFlags":      { "type": "array",   "required": false, "writtenBy": "Flag Anomalies" },
    "managerDecision":   { "type": "string",  "enum": ["approved", "rejected", "needs_info"] },
    "financeDecision":   { "type": "string",  "enum": ["approved", "rejected", "hold"] },
    "paymentRef":        { "type": "string",  "required": false, "writtenBy": "Process Payment" }
  }
}
```

## Troubleshooting

| Problem | Cause | Resolution |
|---|---|---|
| A computed field contains unexpected or stale data. | Multiple tasks write to the same field. The last writer overwrites the previous value. | Audit your output mappings. Assign each field to exactly one task and use namespaced field names. |
| A rule never evaluates to `true`. | The field referenced in the rule's IF clause is not yet written by the time the rule evaluates. | Verify that the task responsible for writing the field completes within the current or a preceding stage. |
| A variable does not appear in the entity selector. | The field is not defined in the deployed version of the case entity schema. | Add the field to the schema, republish, and redeploy the case plan. |
| Input fields are being overwritten during processing. | A task output mapping targets an input field. | Remove the output mapping that targets the input field. Document input fields as read-only in your schema. |

## Limitations

- Native case-entity support in Data Fabric is not yet available. Use one of the three sourcing options described in [Step 2](#step-2-choosing-where-the-entity-lives).
- The `writtenBy` annotation is a design-time documentation convention. The platform does not enforce single-writer constraints at runtime. Developers must verify field ownership through review.
- Case user roles and access support (restricting which personas can edit specific entity fields) is not yet available.

## Next steps

- [How to model primary and secondary stages](how-to-model-primary-secondary-stages.md) — Learn how to configure stage rules that consume your entity fields.
- [Input and output mapping reference](maestro-case-management-component-dictionary.md) — Detailed reference for wiring entity fields to task parameters.
- [Maestro Case tutorial: Property insurance claims](building-an-insurance-claims-case-in-30-minutes.md) — End-to-end tutorial that applies these schema design principles to a complete case plan.
