The cases namespace provides API methods for getting, adding, updating
or deleting cases from a Testmo project. The following endpoints allow single
and bulk operations
-
GET
projects/{project_id}/cases -
POST
projects/{project_id}/cases -
PATCH
projects/{project_id}/cases -
DELETE
projects/{project_id}/cases -
GET
projects/{project_id}/cases/{case_id}/result-history -
GET
projects/{project_id}/templates -
GET
projects/{project_id}/repos -
GET
projects/{project_id}/tags
GET /projects/{project_id}/cases
Returns a list of cases from the specified project.
This method uses pagination so you might need to request additional pages to retrieve all results.
project_id id required
ID of the project.
Request
page integer
Number of page to return (default: first page).
per_page integer
Maximum number of cases to return (supported: 15,
25, 50, 100; default:
100).
order string
Sort order (supported: asc, desc; default:
asc).
sort string
Sort field for the list of cases (supported:
repository_cases:created_at,
repository_cases:name,
repository_cases:has_automation_status; default:
repository_cases:created_at).
name string
Filter cases by name (partial match, max 255 characters).
folder_id string
Comma-separated list of folder IDs to filter by. Use with
recursive=true to include cases from all sub-folders.
recursive boolean
When true, returns cases from all sub-folders of the
specified folder_id (default: false).
template_id string
Comma-separated list of template IDs to filter by.
state_id string
Comma-separated list of state IDs to filter by.
status_id string
Comma-separated list of status IDs to filter by.
priority string
Comma-separated list of priority IDs to filter by.
tags string
Comma-separated list of tag names or IDs to filter by. Use with
tag_match to control matching behaviour.
tag_match string
Controls how multiple tags values are matched (supported:
any, all; default: any).
created_after string
Limit results to cases created after this date/time (ISO 8601, UTC;
e.g. 2025-06-01T00:00:00Z or 2025-06-01).
created_before string
Limit results to cases created before this date/time (ISO 8601, UTC;
e.g. 2025-06-30T23:59:59Z or 2025-06-30).
updated_from string
Limit results to cases updated on or after this date/time (ISO 8601,
UTC; e.g. 2025-06-01T00:00:00Z or 2025-06-01).
updated_to string
Limit results to cases updated on or before this date/time (ISO 8601,
UTC; e.g. 2025-06-30T23:59:59Z or 2025-06-30).
updated_by string
Comma-separated list of user IDs to filter by last-updated user.
has_automation boolean
Filter cases with (true) or without (false)
automation links.
has_automation_status boolean
Filter cases with (true) or without (false)
an automation status.
expands string
Comma-separated list of expands to return.
{
"page": {
"type": "integer",
"format": "int64",
"description": "Number of page to return (default: first page)."
},
"per_page": {
"type": "integer",
"format": "int64",
"enum": [15, 25, 50, 100],
"description": "Maximum number of items to return per page (default: 100)."
},
"order": {
"type": "string",
"enum": ["asc", "desc"],
"default": "asc",
"description": "Sort order (ascending or descending)."
},
"sort": {
"type": "string",
"enum": [
"repository_cases:created_at",
"repository_cases:name",
"repository_cases:has_automation_status",
"repository_cases:state_id",
"repository_cases:status_id",
"repository_cases:template_id",
"repository_cases:updated_at"
],
"default": "repository_cases:created_at",
"description": "Sort field for the list of cases."
},
"name": {
"type": "string",
"description": "Filter cases by name (partial match, max 255 characters)."
},
"folder_id": {
"type": "string",
"description": "Comma-separated list of folder IDs to filter by."
},
"recursive": {
"type": "boolean",
"description": "When true, returns cases from all sub-folders of the specified folder_id."
},
"template_id": {
"type": "string",
"description": "Comma-separated list of template IDs to filter by."
},
"state_id": {
"type": "string",
"description": "Comma-separated list of state IDs to filter by."
},
"status_id": {
"type": "string",
"description": "Comma-separated list of status IDs to filter by."
},
"priority": {
"type": "string",
"description": "Comma-separated list of priority IDs to filter by."
},
"tags": {
"type": "string",
"description": "Comma-separated list of tag names or IDs to filter by."
},
"tag_match": {
"type": "string",
"enum": ["any", "all"],
"default": "any",
"description": "Controls how multiple tags values are matched."
},
"created_after": {
"type": "string",
"format": "date-time",
"description": "Limit results to cases created after this date/time (ISO 8601, UTC)."
},
"created_before": {
"type": "string",
"format": "date-time",
"description": "Limit results to cases created before this date/time (ISO 8601, UTC)."
},
"updated_from": {
"type": "string",
"format": "date-time",
"description": "Limit results to cases updated on or after this date/time (ISO 8601, UTC)."
},
"updated_to": {
"type": "string",
"format": "date-time",
"description": "Limit results to cases updated on or before this date/time (ISO 8601, UTC)."
},
"updated_by": {
"type": "string",
"description": "Comma-separated list of user IDs to filter by last-updated user."
},
"has_automation": {
"type": "boolean",
"description": "Filter cases with (true) or without (false) automation links."
},
"has_automation_status": {
"type": "boolean",
"description": "Filter cases with (true) or without (false) automation status."
},
"expands": {
"type": "string",
"description": "Comma-separated list of expands to return (automation_links, comments, folders, history, tags, templates, users)."
}
}
This method supports the following expands so you can automatically include additional information for referenced objects:
-
automation_links -
comments -
folders -
history -
tags -
templates -
users
Response
GET /api/v1/projects/1/cases
200 OK
{
"page": 1,
"prev_page": null,
"next_page": 2,
"last_page": 31,
"per_page": 100,
"total": 3042,
"result": [
{
"id": 35802,
"estimate": null,
"folder_id": 1,
"forecast": null,
"has_automation": false,
"has_automation_status": false,
"key": 35802,
"name": "Verify login with valid credentials",
"project_id": 1,
"repo_id": 1,
"state_id": 4,
"status_id": null,
"status_at": null,
"template_id": 2,
"created_at": "2025-09-22T08:36:02.572541Z",
"created_by": 1,
"updated_at": "2025-09-22T08:44:19.455334Z",
"updated_by": 1,
"custom_priority": { "id": 2, "name": "High" },
"custom_components": [
{ "id": 5, "name": "Backend" },
{ "id": 7, "name": "API" }
],
"custom_is_regression": true,
"custom_steps": [
{
"column_name": "custom_steps",
"id": 1,
"case_id": 35802,
"text1": "<p>Navigate to login page</p>",
"text2": null,
"text3": "<p>Login page is displayed</p>",
"text4": null,
"display_order": 1
}
]
},
..
]
}
Custom fields are returned according to their type:
-
Dropdown fields return a single object with the selected
option's
idandname(e.g.custom_priorityabove). -
Multi-select fields return an array of objects, each with
the
idandnameof a selected option (e.g.custom_componentsabove). -
Checkbox fields return a plain boolean (e.g.
custom_is_regressionabove).
Examples
// Filter by name (partial match)
GET /api/v1/projects/1/cases?name=login
// Filter by folder and include all sub-folders
GET /api/v1/projects/1/cases?folder_id=10&recursive=true
// Filter by template
GET /api/v1/projects/1/cases?template_id=3
// Filter by tags (cases matching any of the listed tags)
GET /api/v1/projects/1/cases?tags=smoke,regression
// Filter by tags (cases matching all listed tags)
GET /api/v1/projects/1/cases?tags=smoke,regression&tag_match=all
// Filter by priority
GET /api/v1/projects/1/cases?priority=1,2
// Filter by date range (cases created in June 2025)
GET /api/v1/projects/1/cases?created_after=2025-06-01&created_before=2025-06-30
// Filter by last updated date range
GET /api/v1/projects/1/cases?updated_from=2025-09-01&updated_to=2025-09-30
// Include tags and automation links
GET /api/v1/projects/1/cases?expands=tags,automation_links
// Paginate through results
GET /api/v1/projects/1/cases?page=2&per_page=50
Status codes
200 401 403 404422
429 (details)
POST /projects/{project_id}/cases
Creates one or more test cases (minimum 1, maximum 100 per request).
Where case details aren't provided, default settings from the administration area will be used. If a folder isn't specified, one will be created automatically.
Custom fields are specified with the custom_ prefix. Fields that
are not defined in the case's template will return a 422 validation
error. Restricted custom fields require appropriate user permissions (403 Forbidden
if user lacks permission).
If you supply a custom field's content key without the
custom_ prefix (e.g. description when the template
defines custom_description), the request returns a
422 error with a hint suggesting the prefixed name — for example:
Unknown field 'description'. Did you mean 'custom_description'?.
System fields such as name, folder_id,
state_id, status_id, priority,
estimate and template_id are unaffected and never trigger
this hint.
project_id id required
ID of the project.
Request
cases array required
Array of case objects to create.
name string required
The name of the test case.
folder_id integer
A valid folder ID to which the case should be added. A folder will
be created automatically if not specified.
template_id integer
A valid template ID to apply to the test case. Uses the project default
if not specified.
state_id integer
A valid state ID to apply to the test case.
estimate integer
Estimated execution duration in seconds.
automation_links array
Array of automation links to apply to the test case (automation_case_id,
name, automation_source_id).
issues array
Array of issues to link to the test case (display_id,
integration_id, connection_project_id for
GitHub and GitLab issues).
tags array
Array of tag strings to apply to the test case.
custom_* various
Any custom fields defined for the case's template, specified with
the custom_ prefix (e.g. custom_priority,
custom_steps). Fields not defined in the template will
return a 422 error.
{
"cases": {
"type": "array",
"description": "Array of test case objects to create (minimum 1, maximum 100).",
"minItems": 1,
"maxItems": 100,
"items": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Name of the test case (required)."
},
"folder_id": {
"type": "integer",
"format": "int64",
"nullable": true,
"description": "Folder ID. A folder is created automatically if not provided."
},
"template_id": {
"type": "integer",
"format": "int64",
"description": "Template ID. Uses project default if not specified."
},
"state_id": {
"type": "integer",
"format": "int64",
"description": "State ID to apply to the case."
},
"estimate": {
"type": "integer",
"format": "int64",
"description": "Estimated execution duration in seconds."
},
"automation_links": {
"type": "array",
"description": "Array of automation link objects (automation_case_id, name, automation_source_id)."
},
"issues": {
"type": "array",
"description": "Array of issue objects (display_id, integration_id, connection_project_id)."
},
"tags": {
"type": "array",
"items": { "type": "string" },
"description": "Array of tag strings."
}
},
"required": ["name"]
}
}
}
Example
POST /api/v1/projects/1/cases
{
"cases": [
{
"name": "Verify user login",
"folder_id": 10,
"template_id": 2,
"state_id": 2,
"estimate": 120,
"custom_priority": 1,
"custom_steps": [
{
"text1": "<p>Navigate to login page</p>",
"text3": "<p>Login page is displayed</p>"
},
{
"text1": "<p>Enter valid credentials and click Login</p>",
"text3": "<p>User is authenticated and redirected to dashboard</p>"
}
],
"tags": ["smoke", "authentication"],
"issues": [
{
"display_id": "ABC-123",
"integration_id": 3
}
]
}
]
}
201 Created
{
"result": [
{
"id": 35812,
"estimate": 120,
"folder_id": 10,
"forecast": 120,
"has_automation": false,
"has_automation_status": false,
"key": 35812,
"name": "Verify user login",
"project_id": 1,
"repo_id": 1,
"state_id": 2,
"status_id": null,
"status_at": null,
"template_id": 2,
"created_at": "2025-09-24T08:26:09.366217Z",
"created_by": 1,
"updated_at": null,
"updated_by": null,
"custom_priority": { "id": 1, "name": "Critical" },
"custom_steps": [
{
"column_name": "custom_steps",
"id": 4,
"case_id": 35812,
"text1": "<p>Navigate to login page</p>",
"text2": null,
"text3": "<p>Login page is displayed</p>",
"text4": null,
"display_order": 1
}
]
}
]
}
Status codes
201 400 401 403404
422 429 (details)
PATCH /projects/{project_id}/cases
Bulk updates up to 100 cases (minimum 1, maximum 100).
When updating cases with different templates, custom fields must exist in ALL
templates or a 422 error will be returned.
As with case creation, custom fields must use the custom_ prefix.
Supplying a bare content key (e.g. description instead of
custom_description) returns a 422 error with a hint
suggesting the prefixed name — for example:
Unknown field 'description'. Did you mean 'custom_description'?.
System fields (name, folder_id,
state_id, status_id, priority,
estimate, template_id, tags,
automation_links) are exempt.
project_id id required
ID of the project.
Request
ids array required
Array of case IDs to update (minimum 1, maximum 100).
name string
The name to apply to the test cases.
folder_id integer
A valid folder ID to move the cases to.
state_id integer
A valid state ID to apply to the test cases.
status_id integer
A valid status ID to apply to the test cases.
estimate integer
Estimated execution duration in seconds.
automation_links array
Array of automation links to apply (automation_case_id,
name, automation_source_id).
issues array
Array of issues to link (display_id,
integration_id, connection_project_id for
GitHub and GitLab issues).
tags array
Array of tag strings to apply to the test cases.
custom_* various
Any custom field specified with the custom_ prefix.
When updating cases with different templates, the field must exist
in all templates.
{
"ids": {
"type": "array",
"description": "Array of case IDs to update (minimum 1, maximum 100).",
"minItems": 1,
"maxItems": 100,
"items": { "type": "integer", "format": "int64" }
},
"name": {
"type": "string",
"description": "New name for the test cases."
},
"folder_id": {
"type": "integer",
"format": "int64",
"description": "New folder ID."
},
"state_id": {
"type": "integer",
"format": "int64",
"description": "New state ID to apply."
},
"status_id": {
"type": "integer",
"format": "int64",
"description": "New status ID to apply."
},
"estimate": {
"type": "integer",
"format": "int64",
"description": "New estimated execution duration in seconds."
},
"automation_links": {
"type": "array",
"description": "Array of automation link objects."
},
"issues": {
"type": "array",
"description": "Array of issue objects."
},
"tags": {
"type": "array",
"items": { "type": "string" },
"description": "Array of tag strings."
}
}
Example
PATCH /api/v1/projects/1/cases
{
"ids": [1, 2, 3, 4, 5],
"folder_id": 1,
"state_id": 1,
"estimate": 200,
"custom_priority": 1,
"tags": ["authentication"],
"automation_links": [1, 2, 3]
}
200 OK
{
"result": [
{
"id": 1,
"estimate": 200,
"folder_id": 1,
"forecast": 200,
"has_automation": false,
"has_automation_status": false,
"key": 1,
"name": "Verify login with valid credentials",
"project_id": 1,
"repo_id": 1,
"state_id": 1,
"status_id": 2,
"status_at": "2025-09-10T14:16:32.619735Z",
"template_id": 3,
"created_at": "2025-09-19T14:13:40.616240Z",
"created_by": 1,
"updated_at": "2025-09-24T08:48:14.365222Z",
"updated_by": 1,
"custom_priority": { "id": 1, "name": "Critical" },
"custom_description": null,
"custom_expected": null
},
..
]
}
Status codes
200 400 401 403404
422 429 (details)
DELETE /projects/{project_id}/cases
Deletes up to 100 cases (minimum 1, maximum 100).
project_id id required
ID of the project.
Request
ids array required
Array of case IDs to delete (minimum 1, maximum 100).
{
"ids": {
"type": "array",
"description": "Array of case IDs to delete (minimum 1, maximum 100).",
"minItems": 1,
"maxItems": 100,
"items": { "type": "integer", "format": "int64" }
}
}
Returns a 204 no content response.
Example
DELETE /api/v1/projects/1/cases
{
"ids": [1, 2, 3, 4, 5]
}
204 No Content
Status codes
204 401 403 404429
(details)
GET /projects/{project_id}/cases/{case_id}/result-history
Returns a paginated list of test results recorded against the specified case across all runs.
This method uses pagination so you might need to request additional pages to retrieve all results. Results are always returned in descending order by date (most recent first).
project_id id required
ID of the project.
case_id id required
ID of the test case.
Request
page integer
Number of page to return (default: first page).
per_page integer
Maximum number of results to return (supported: 15,
25, 50, 100; default:
100).
{
"page": {
"type": "integer",
"format": "int64",
"description": "Number of page to return (default: first page)."
},
"per_page": {
"type": "integer",
"format": "int64",
"enum": [15, 25, 50, 100],
"description": "Maximum number of items to return per page (default: 100)."
}
}
Response
GET /api/v1/projects/1/cases/35802/result-history
200 OK
{
"page": 1,
"prev_page": null,
"next_page": null,
"last_page": 1,
"per_page": 100,
"total": 3,
"result": [
{
"id": 1042,
"project_id": 1,
"run_id": 87,
"test_id": 4201,
"case_id": 35802,
"status_id": 2,
"note": "Passed after fixing environment config",
"elapsed": 45,
"assignee_id": 3,
"created_at": "2025-09-24T09:14:22.000000Z",
"created_by": 3,
"updated_at": "2025-09-24T09:14:22.000000Z",
"updated_by": 3
},
..
]
}
Examples
// Get result history for a case
GET /api/v1/projects/1/cases/35802/result-history
// Paginate through results
GET /api/v1/projects/1/cases/35802/result-history?page=2&per_page=50
Status codes
200 401 403 404429
(details)
GET /projects/{project_id}/templates
Returns the list of templates available for test cases in the specified project, including each template's field definitions. Use this endpoint to discover available templates and their fields before creating or filtering cases.
project_id id required
ID of the project.
Response
GET /api/v1/projects/1/templates
200 OK
{
"result": [
{
"id": 1,
"name": "Default Template",
"is_default": true,
"fields": [
{
"id": 1,
"name": "Priority",
"type": 8,
"options": [
{ "id": 1, "value": "Critical" },
{ "id": 2, "value": "High" },
{ "id": 3, "value": "Medium" },
{ "id": 4, "value": "Low" }
]
}
]
},
{
"id": 2,
"name": "Steps Template",
"is_default": false,
"fields": [
{
"id": 1,
"name": "Priority",
"type": 8,
"options": [
{ "id": 1, "value": "Critical" },
{ "id": 2, "value": "High" },
{ "id": 3, "value": "Medium" },
{ "id": 4, "value": "Low" }
]
},
{
"id": 2,
"name": "Steps",
"type": 10
}
]
},
..
]
}
The type field in each field definition indicates the field type:
-
1— String -
2— Text -
3— Integer -
4— Float -
5— Date -
6— URL -
7— Checkbox (returns a boolean in case responses) -
8— Dropdown (includesoptionsarray; returns a single{ id, name }object in case responses) -
9— Multi-select (includesoptionsarray; returns an array of{ id, name }objects in case responses) -
10— Steps
Status codes
200 401 403 404429
(details)
GET /projects/{project_id}/repos
Returns the list of test case repositories for the specified project. Each project has a default (master) repository. This endpoint can be used to retrieve repository IDs for use in other API calls.
project_id id required
ID of the project.
Response
GET /api/v1/projects/1/repos
200 OK
{
"result": [
{
"id": 1,
"name": "Main Repository",
"is_default": true
}
]
}
Status codes
200 401 403 404429
(details)
GET /projects/{project_id}/tags
Returns the list of tags defined in the specified project, along with a usage
count for each tag. Use this endpoint to discover available tag names and IDs
for use with the tags filter on the cases endpoint.
project_id id required
ID of the project.
Response
GET /api/v1/projects/1/tags
200 OK
{
"result": [
{
"id": 1,
"name": "smoke",
"usage_count": 42
},
{
"id": 2,
"name": "regression",
"usage_count": 118
},
{
"id": 3,
"name": "authentication",
"usage_count": 27
},
..
]
}
Status codes
200 401 403 404429
(details)