> ## Documentation Index
> Fetch the complete documentation index at: https://docs.usefini.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Create test case

> Save a captured conversation or scripted scenario as a regression case.

Save a captured conversation or scripted scenario as a regression case.

Choose exactly one conversation or scenario. Cases may be saved without criteria, but cannot run until they have at least one local or shared criterion. Copies require the same agent and source conversation; they copy saved setup, including recovered mocks, and do not copy runs.

## Headers

<ParamField header="Authorization" type="string" required>
  Bearer token containing your workspace API key. The key needs `write` scope.
</ParamField>

## Body parameters

<ParamField body="botId" type="string" required>
  Agent UUID. The agent must belong to the workspace.
</ParamField>

<ParamField body="sourceType" type="enum" required>
  `conversation` or `scenario`.
</ParamField>

<ParamField body="conversationId" type="string">
  Required for conversation cases. Must be absent for scenarios.
</ParamField>

<ParamField body="name" type="string" required>
  Nonblank name, up to 200 characters.
</ParamField>

<ParamField body="criteria" type="array">
  Local criterion definitions, up to 50. Defaults to an empty array; shared group checks can supply run criteria.
</ParamField>

<ParamField body="criteriaGroupIds" type="array">
  Shared group UUIDs, up to 50. Overrides the single-group compatibility field.
</ParamField>

<ParamField body="criteriaGroupId" type="string">
  Single shared group UUID. Prefer criteriaGroupIds for new integrations.
</ParamField>

<ParamField body="attributeOverrides" type="object">
  Attribute values for this test only, up to 100 KB. Reserved top-level names `__proto__`, `constructor` and `prototype` are rejected.
</ParamField>

<ParamField body="scenario" type="object">
  Required scripted input when sourceType is scenario. See [Case input](/en/api-reference/test-suite#case-input).
</ParamField>

<ParamField body="actionResponses" type="array">
  Captured-conversation response overrides, up to 200. See [Recorded Action overrides](/en/api-reference/test-suite#recorded-action-overrides).
</ParamField>

<ParamField body="artifacts" type="array">
  Up to 100 manual references: `kind` is `rule` or `article`, and `id` is a UUID. Rules must be assigned to the agent; articles must be live workspace articles.
</ParamField>

<ParamField body="collectionId" type="string">
  Collection UUID to add the new case to.
</ParamField>

<ParamField body="duplicateOfCaseId" type="string">
  Conversation case UUID to copy from its saved snapshot. Requires `duplicateSourceUpdatedAt`.
</ParamField>

<ParamField body="duplicateSourceUpdatedAt" type="string">
  ISO timestamp from the source case. Requires `duplicateOfCaseId`; stale copies return `409`.
</ParamField>

## Response

Returns the case detail, effective and local criteria, setup, artifacts and history. A new scenario intentionally has empty arrays for Action-response overrides and run history.

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
    --url 'https://api-prod.usefini.com/v2/test-suite/cases/public' \
    --header 'Authorization: Bearer fini_your_api_key' \
    --header 'Content-Type: application/json' \
    --data '{
    "botId": "00000000-0000-4000-8000-000000000001",
    "name": "Refund policy",
    "sourceType": "scenario",
    "scenario": {
      "turns": [
        {
          "message": "Am I eligible for a refund?",
          "actions": [
            {
              "ruleId": "00000000-0000-4000-8000-000000000005",
              "nodeId": "lookup",
              "toolId": "00000000-0000-4000-8000-000000000006",
              "success": true,
              "output": {
                "eligible": true
              }
            }
          ]
        }
      ],
      "userAttributes": {
        "plan": "standard"
      }
    },
    "criteria": [
      {
        "name": "No handoff",
        "type": "deterministic",
        "condition": {
          "scope": "SCALAR",
          "predicate": {
            "left": {
              "path": "escalated",
              "dataType": "boolean"
            },
            "operator": "==",
            "right": {
              "value": false
            }
          }
        },
        "blocking": true
      }
    ],
    "attributeOverrides": {
      "plan": "standard"
    },
    "artifacts": [
      {
        "kind": "rule",
        "id": "00000000-0000-4000-8000-000000000005"
      }
    ],
    "criteriaGroupIds": [
      "00000000-0000-4000-8000-000000000003"
    ]
  }'
  ```

  ```javascript Node.js theme={null}
  const body = {
    "botId": "00000000-0000-4000-8000-000000000001",
    "name": "Refund policy",
    "sourceType": "scenario",
    "scenario": {
      "turns": [
        {
          "message": "Am I eligible for a refund?",
          "actions": [
            {
              "ruleId": "00000000-0000-4000-8000-000000000005",
              "nodeId": "lookup",
              "toolId": "00000000-0000-4000-8000-000000000006",
              "success": true,
              "output": {
                "eligible": true
              }
            }
          ]
        }
      ],
      "userAttributes": {
        "plan": "standard"
      }
    },
    "criteria": [
      {
        "name": "No handoff",
        "type": "deterministic",
        "condition": {
          "scope": "SCALAR",
          "predicate": {
            "left": {
              "path": "escalated",
              "dataType": "boolean"
            },
            "operator": "==",
            "right": {
              "value": false
            }
          }
        },
        "blocking": true
      }
    ],
    "attributeOverrides": {
      "plan": "standard"
    },
    "artifacts": [
      {
        "kind": "rule",
        "id": "00000000-0000-4000-8000-000000000005"
      }
    ],
    "criteriaGroupIds": [
      "00000000-0000-4000-8000-000000000003"
    ]
  };

  const response = await fetch("https://api-prod.usefini.com/v2/test-suite/cases/public", {
    method: "POST",
    headers: {
      Authorization: "Bearer fini_your_api_key",
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
  });
  if (!response.ok) throw new Error(await response.text());
  const result = await response.json();
  ```

  ```python Python theme={null}
  import requests
  import json

  body = json.loads('''
  {
    "botId": "00000000-0000-4000-8000-000000000001",
    "name": "Refund policy",
    "sourceType": "scenario",
    "scenario": {
      "turns": [
        {
          "message": "Am I eligible for a refund?",
          "actions": [
            {
              "ruleId": "00000000-0000-4000-8000-000000000005",
              "nodeId": "lookup",
              "toolId": "00000000-0000-4000-8000-000000000006",
              "success": true,
              "output": {
                "eligible": true
              }
            }
          ]
        }
      ],
      "userAttributes": {
        "plan": "standard"
      }
    },
    "criteria": [
      {
        "name": "No handoff",
        "type": "deterministic",
        "condition": {
          "scope": "SCALAR",
          "predicate": {
            "left": {
              "path": "escalated",
              "dataType": "boolean"
            },
            "operator": "==",
            "right": {
              "value": false
            }
          }
        },
        "blocking": true
      }
    ],
    "attributeOverrides": {
      "plan": "standard"
    },
    "artifacts": [
      {
        "kind": "rule",
        "id": "00000000-0000-4000-8000-000000000005"
      }
    ],
    "criteriaGroupIds": [
      "00000000-0000-4000-8000-000000000003"
    ]
  }
  ''')

  response = requests.request(
      "POST",
      "https://api-prod.usefini.com/v2/test-suite/cases/public",
      headers={"Authorization": "Bearer fini_your_api_key"},
      json=body,
  )
  response.raise_for_status()
  result = response.json()
  ```
</RequestExample>

<ResponseExample>
  ```json 201 Created theme={null}
  {
    "id": "00000000-0000-4000-8000-000000000002",
    "companyId": "00000000-0000-4000-8000-000000000011",
    "botId": "00000000-0000-4000-8000-000000000001",
    "name": "Refund policy",
    "sourceType": "scenario",
    "sourceInteractionId": null,
    "criteria": [
      {
        "id": "2995012b-d9bf-58e9-c6c7-672205196681",
        "companyId": "00000000-0000-4000-8000-000000000011",
        "defaultCriterionId": null,
        "name": "No handoff",
        "type": "deterministic",
        "condition": {
          "scope": "SCALAR",
          "predicate": {"left": {"path": "escalated", "dataType": "boolean"}, "operator": "==", "right": {"value": false}}
        },
        "blocking": true,
        "judgePrompt": null,
        "passPrompt": null,
        "failPrompt": null,
        "isActive": true,
        "createdAt": "2026-10-12T10:00:00.000Z",
        "updatedAt": "2026-10-12T10:00:00.000Z"
      },
      {
        "id": "00000000-0000-4000-8000-000000000012",
        "companyId": "00000000-0000-4000-8000-000000000011",
        "defaultCriterionId": null,
        "name": "No handoff",
        "type": "deterministic",
        "condition": {
          "scope": "SCALAR",
          "predicate": {"left": {"path": "escalated", "dataType": "boolean"}, "operator": "==", "right": {"value": false}}
        },
        "blocking": true,
        "judgePrompt": null,
        "passPrompt": null,
        "failPrompt": null,
        "isActive": true,
        "createdAt": "2026-10-12T10:00:00.000Z",
        "updatedAt": "2026-10-12T10:00:00.000Z"
      }
    ],
    "artifacts": [{"kind": "rule", "id": "00000000-0000-4000-8000-000000000005", "name": "Refund workflow", "origin": "manual"}],
    "criteriaGroupId": "00000000-0000-4000-8000-000000000003",
    "criteriaGroupIds": ["00000000-0000-4000-8000-000000000003"],
    "criteriaGroup": {
      "id": "00000000-0000-4000-8000-000000000003",
      "companyId": "00000000-0000-4000-8000-000000000011",
      "botId": "00000000-0000-4000-8000-000000000001",
      "name": "Refund checks",
      "criteria": [
        {
          "id": "00000000-0000-4000-8000-000000000012",
          "companyId": "00000000-0000-4000-8000-000000000011",
          "defaultCriterionId": null,
          "name": "No handoff",
          "type": "deterministic",
          "condition": {
            "scope": "SCALAR",
            "predicate": {"left": {"path": "escalated", "dataType": "boolean"}, "operator": "==", "right": {"value": false}}
          },
          "blocking": true,
          "judgePrompt": null,
          "passPrompt": null,
          "failPrompt": null,
          "isActive": true,
          "createdAt": "2026-10-12T10:00:00.000Z",
          "updatedAt": "2026-10-12T10:00:00.000Z"
        }
      ],
      "revision": 1,
      "createdAt": "2026-10-12T10:00:00.000Z",
      "updatedAt": "2026-10-12T10:00:00.000Z"
    },
    "criteriaGroups": [
      {
        "id": "00000000-0000-4000-8000-000000000003",
        "companyId": "00000000-0000-4000-8000-000000000011",
        "botId": "00000000-0000-4000-8000-000000000001",
        "name": "Refund checks",
        "criteria": [
          {
            "id": "00000000-0000-4000-8000-000000000012",
            "companyId": "00000000-0000-4000-8000-000000000011",
            "defaultCriterionId": null,
            "name": "No handoff",
            "type": "deterministic",
            "condition": {
              "scope": "SCALAR",
              "predicate": {"left": {"path": "escalated", "dataType": "boolean"}, "operator": "==", "right": {"value": false}}
            },
            "blocking": true,
            "judgePrompt": null,
            "passPrompt": null,
            "failPrompt": null,
            "isActive": true,
            "createdAt": "2026-10-12T10:00:00.000Z",
            "updatedAt": "2026-10-12T10:00:00.000Z"
          }
        ],
        "revision": 1,
        "createdAt": "2026-10-12T10:00:00.000Z",
        "updatedAt": "2026-10-12T10:00:00.000Z"
      }
    ],
    "criterionOrigins": {
      "2995012b-d9bf-58e9-c6c7-672205196681": {"groupId": "00000000-0000-4000-8000-000000000003", "groupName": "Refund checks"}
    },
    "localCriteria": [
      {
        "id": "00000000-0000-4000-8000-000000000012",
        "companyId": "00000000-0000-4000-8000-000000000011",
        "defaultCriterionId": null,
        "name": "No handoff",
        "type": "deterministic",
        "condition": {
          "scope": "SCALAR",
          "predicate": {"left": {"path": "escalated", "dataType": "boolean"}, "operator": "==", "right": {"value": false}}
        },
        "blocking": true,
        "judgePrompt": null,
        "passPrompt": null,
        "failPrompt": null,
        "isActive": true,
        "createdAt": "2026-10-12T10:00:00.000Z",
        "updatedAt": "2026-10-12T10:00:00.000Z"
      }
    ],
    "createdAt": "2026-10-12T10:00:00.000Z",
    "updatedAt": "2026-10-12T10:00:00.000Z",
    "messages": [{"id": "00000000-0000-4000-8000-000000000010", "role": "user", "content": "Am I eligible for a refund?"}],
    "conversationName": "Refund policy",
    "attributeOverrides": {"plan": "standard"},
    "history": [],
    "latestIndividualRun": null,
    "actionResponses": [],
    "turns": [
      {
        "message": "Am I eligible for a refund?",
        "actions": [
          {
            "ruleId": "00000000-0000-4000-8000-000000000005",
            "nodeId": "lookup",
            "toolId": "00000000-0000-4000-8000-000000000006",
            "success": true,
            "output": {"eligible": true}
          }
        ]
      }
    ],
    "userAttributes": {"plan": "standard"},
    "createdBy": null,
    "deletedAt": null
  }
  ```
</ResponseExample>


## Related topics

- [Test Suite](/en/testing/test-suite.md)
- [Overview](/en/api-reference/test-suite.md)
- [Quickstart](/en/quickstart.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.