---
title: "Orangescrum Partner API Reference | Orangescrum"
description: "Reference for all 59 Orangescrum Partner API endpoints: authentication, AES payload encryption, response format, rate limits, projects, and tasks."
canonical: https://www.orangescrum.com/developer/api
---

# Orangescrum Partner API Reference | Orangescrum

> For the complete documentation index, see [llms.txt](https://www.orangescrum.com/llms.txt).

Developer docs

Getting started

-   [Overview](/developer)
-   [Get API credentials](/developer#credentials)
-   [Make your first call](/developer#first-call)

Partner API

-   [Introduction](/developer/api)
-   [Authentication](/developer/api#authentication)
-   [Encrypting requests](/developer/api#encryption)
-   [Responses and errors](/developer/api#responses)
-   [Rate limits](/developer/api#rate-limits)
-   [Endpoint reference](/developer/api#reference)

MCP server

-   [Overview](/mcp)
-   [Connect a client](/mcp#connect)
-   [Tool reference](/mcp#tools)

More

-   [Self-Hosted edition](/self-hosted)
-   [Community Edition](/open-source/free-download)
-   [Talk to us](/contact-sales)

[Home](/)[Developer](/developer)API reference

# Partner API reference

59 endpoints across 13 resource groups. Every call is a `POST`, authenticated with an API key, carrying an encrypted JSON payload. If you have not made a call yet, start with the [quick start](/developer#first-call).

## Conventions[](#conventions)

All endpoints live under a single base path:

```
https://<your-orangescrum-host>/api/v1/partner
```

Cloud customers get their host with their credentials. Self-hosted customers use their own.

-   **Everything is POST.** Reads use POST too, because the request body is encrypted and a query string cannot carry that.
-   **One body field.** The JSON body is always `{ "encrypted_data": "..." }`. Your real parameters live inside the encrypted blob.
-   **Scope comes from the key.** You never pass a company or user ID. The API key is bound to a company and a user, and that is what the call can see.
-   **Dates** are `YYYY-MM-DD` unless a field says otherwise.

Parameter tables describe the decrypted payload

Every parameter listed in this reference goes inside the JSON you encrypt, not in the outer request body and not in the URL.

## Authentication[](#authentication)

Send your API key in the `X-API-KEY` header on every request. It is the only header we require beyond `Content-Type`.

```
X-API-KEY: your_api_key
Content-Type: application/json
```

The key is checked against active, unexpired credentials. A missing, unknown, deactivated, or expired key returns `401`. Your API secret is never sent: it is only used to encrypt and decrypt the payload, which is what proves the request really came from you.

Keep the secret server side

Anyone holding the secret can read and forge your payloads. Never ship it in a browser bundle, a mobile app, or a public repository. If it leaks, ask us to rotate it.

## Encrypting requests[](#encryption)

Payloads are encrypted with **AES-256-CBC**. The exact recipe is:

1.  Serialise your parameters as JSON. An endpoint with no parameters takes `{}`.
2.  Derive the key: `sha256(secret)` as **raw bytes**, giving 32 bytes. Do not hex encode it first.
3.  Generate a fresh random 16 byte IV for every request. Never reuse one.
4.  Encrypt the JSON with that key and IV.
5.  Prepend the IV to the ciphertext, then base64 encode the whole thing.
6.  Send the result as `encrypted_data`.

```
import crypto from "node:crypto";

/**
 * Build the "encrypted_data" value.
 *   key        = raw sha256(secret)          -> 32 bytes for AES-256
 *   iv         = 16 random bytes
 *   ciphertext = AES-256-CBC(JSON payload)
 *   result     = base64(iv + ciphertext)
 */
export function encryptPayload(payload, secret) {
  const key = crypto.createHash("sha256").update(secret).digest();
  const iv = crypto.randomBytes(16);
  const cipher = crypto.createCipheriv("aes-256-cbc", key, iv);
  const enc = Buffer.concat([
    cipher.update(JSON.stringify(payload), "utf8"),
    cipher.final(),
  ]);
  return Buffer.concat([iv, enc]).toString("base64");
}

export async function call(endpoint, payload) {
  const res = await fetch("https://<your-orangescrum-host>/api/v1/partner" + endpoint, {
    method: "POST",
    headers: {
      "X-API-KEY": process.env.OS_API_KEY,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      encrypted_data: encryptPayload(payload, process.env.OS_API_SECRET),
    }),
  });
  return res.json();
}

// Example
const projects = await call("/projects/list", {});
```

The same recipe in four languages. Reuse the helper for every call.

Test your encryption before anything else

Call `/api/v1/partner/validate` first. It decrypts your payload and echoes it back, so if you get your own object returned, your key and your encryption are both correct. Debugging that against a real endpoint is much harder.

## Responses and errors[](#responses)

Responses are JSON with a consistent envelope. `success` tells you what happened, `message` is human readable, and `data` carries the result when there is one.

```
{
  "success": true,
  "message": "Projects retrieved successfully",
  "data": [
    {
      "id": 1042,
      "name": "Website Redesign",
      "short_name": "WEB",
      "status": "Started",
      "priority": "High",
      "start_date": "2026-07-01",
      "end_date": "2026-09-30"
    }
  ]
}
```

Status codes follow normal HTTP meanings:

| Code | What it means |

| `200` | The request worked. Read data from the response body. |
| `201` | Something was created. The new record is in the response body. |
| `400` | The payload was rejected. Check errors for the field that failed. |
| `401` | The API key is missing, wrong, inactive, expired, or the payload could not be decrypted. |
| `404` | The record you asked for does not exist, or your key cannot see it. |
| `422` | Validation failed on one or more fields. |
| `429` | You went over a rate limit. Back off and retry. |
| `500` | Something broke on our side. Retry, and tell us if it persists. |

A 401 can mean decryption failed

If your key is valid but the payload cannot be decrypted, the response is still `401`. When a key that worked yesterday starts returning 401, check your encryption before assuming the key was revoked.

## Rate limits[](#rate-limits)

120 requests per minute and 5000 per day, counted per API key and IP. Going over returns `429`.

Back off and retry rather than hammering. If you are backfilling a warehouse or syncing a large account and these limits are the wrong shape for that, tell us what you are doing and we will look at raising them.

## Endpoint reference[](#reference)

Every endpoint below is a `POST` to the base path plus the path shown. Parameters go inside the encrypted payload.

### Validate1[#](#group-validate)

A single endpoint to check that your API key and your encryption setup are correct.

POST`/validate`Validate request

POST`/api/v1/partner/validate`[](#ep-validate)

Checks the API key, decrypts the payload, and returns the decrypted payload back to you. The payload can contain anything, so there are no required fields.

This endpoint takes no parameters. Send an empty object as the payload.

### Projects5[#](#group-projects)

Read, create, update, and search projects in the connected Orangescrum account.

POST`/projects/list`List projects

POST`/api/v1/partner/projects/list`[](#ep-projects-list)

Returns every project the API key has access to.

| Name | Type | Required | Description |

| `filters` | array | No | Reserved filter object. It is accepted and validated, but the list is not narrowed by it. Use the search endpoint to filter. |

POST`/projects/create`Create a project

POST`/api/v1/partner/projects/create`[](#ep-projects-create)

Creates a new project and returns it.

| Name | Type | Required | Description |

| `name` | string | Yes | Project name. Max 255 characters. |
| `short_name` | string | No | Short code for the project. Max 50 characters. |
| `description` | string | No | Project description. |
| `project_type` | integer or string | No | Project type. Pass the numeric type ID, or a type name to match or create. |
| `status` | string | No | One of: Started, Hold, Stack, Completed. |
| `priority` | string | No | One of: High, Medium, Low. |
| `start_date` | string (date) | No | Project start date. |
| `end_date` | string (date) | No | Project end date. Must be on or after start\_date. |
| `estimated_hours` | number | No | Estimated hours. Cannot be negative. |
| `status_group_id` | integer | No | ID of the status group to use. |

POST`/projects/detail`Get a project

POST`/api/v1/partner/projects/detail`[](#ep-projects-detail)

Returns one project by its unique ID.

| Name | Type | Required | Description |

| `project_id` | string | Yes | Unique ID of the project. |

POST`/projects/update`Update a project

POST`/api/v1/partner/projects/update`[](#ep-projects-update)

Updates the fields you send on an existing project. Fields you leave out are not changed.

| Name | Type | Required | Description |

| `project_id` | string | Yes | Unique ID of the project. |
| `name` | string | No | Project name. Max 255 characters. |
| `short_name` | string | No | Short code for the project. Max 50 characters. |
| `description` | string | No | Project description. |
| `project_type` | integer | No | Numeric project type ID. |
| `status` | string | No | One of: Started, Hold, Stack, Completed. |
| `priority` | string | No | One of: High, Medium, Low. |
| `isactive` | boolean | No | Whether the project is active. |
| `start_date` | string (date) | No | Project start date. |
| `end_date` | string (date) | No | Project end date. Must be on or after start\_date. |
| `estimated_hours` | number | No | Estimated hours. Cannot be negative. |

POST`/projects/search`Search projects

POST`/api/v1/partner/projects/search`[](#ep-projects-search)

Searches projects by text and returns a paginated list.

| Name | Type | Required | Description |

| `q` | string | No | Search text. Max 255 characters. |
| `page` | integer | No | Page number. Starts at 1. |
| `per_page` | integer | No | Results per page. Between 1 and 100. |

### Tasks5[#](#group-tasks)

Read, create, update, and search tasks inside a project.

POST`/tasks/list`List tasks

POST`/api/v1/partner/tasks/list`[](#ep-tasks-list)

Returns a paginated list of tasks, with optional filters.

| Name | Type | Required | Description |

| `filters` | array | No | Object holding the filter fields below. |
| `filters.project_id` | string or integer | No | Unique ID of the project, or its numeric ID. |
| `filters.status` | string | No | One of: Open, Closed. |
| `filters.priority` | string | No | One of: high, medium, low. |
| `filters.assign_to` | integer | No | User ID the task is assigned to. |
| `filters.type_id` | integer | No | Task type ID. |
| `filters.case_no` | integer | No | Task number within the project. |
| `filters.q` | string | No | Search text. |
| `per_page` | integer | No | Results per page. Between 1 and 100. |
| `page` | integer | No | Page number. Starts at 1. |

POST`/tasks/create`Create a task

POST`/api/v1/partner/tasks/create`[](#ep-tasks-create)

Creates a task in a project and returns it.

| Name | Type | Required | Description |

| `project_id` | string | Yes | Unique ID of the project. |
| `title` | string | Yes | Task title. Max 255 characters. |
| `message` | string | No | Task description. |
| `type_id` | integer | Yes | Task type ID. |
| `priority` | string | Yes | One of: high, medium, low. |
| `assign_to` | integer | No | User ID to assign the task to. |
| `estimated_hours` | number | No | Estimated hours. Cannot be negative. |
| `gantt_start_date` | string (date) | No | Task start date. |
| `due_date` | string (date) | No | Task due date. |
| `completed` | integer | No | Progress state. One of: 0, 1, 2, 3, 4, 5, 6. |
| `legend` | integer | No | Legend or label ID. |
| `custom_status` | string | No | One of: New, In Progress, Resolved, Closed. |
| `parent_task_id` | integer | No | ID of the parent task, for a subtask. |
| `story_point` | number | No | Story points. Cannot be negative. |
| `epic_id` | integer | No | ID of the epic to link the task to. |
| `milestone_id` | integer | No | ID of the milestone to link the task to. |
| `is_recurring` | boolean | No | Whether the task repeats. |

POST`/tasks/detail`Get a task

POST`/api/v1/partner/tasks/detail`[](#ep-tasks-detail)

Returns one task by its unique ID.

| Name | Type | Required | Description |

| `task_id` | string | Yes | Unique ID of the task. |

POST`/tasks/update`Update a task

POST`/api/v1/partner/tasks/update`[](#ep-tasks-update)

Updates the fields you send on an existing task. Fields you leave out are not changed.

| Name | Type | Required | Description |

| `task_id` | string | Yes | Unique ID of the task. |
| `title` | string | No | Task title. Max 255 characters. |
| `message` | string | No | Task description. |
| `type_id` | integer | No | Task type ID. |
| `priority` | string | No | One of: high, medium, low. |
| `assign_to` | integer | No | User ID to assign the task to. |
| `estimated_hours` | number | No | Estimated hours. Cannot be negative. |
| `gantt_start_date` | string (date) | No | Task start date. |
| `due_date` | string (date) | No | Task due date. |
| `completed` | integer | No | Progress state. One of: 0, 1, 2, 3, 4, 5, 6. |
| `legend` | integer | No | Legend or label ID. |
| `custom_status` | string | No | One of: New, In Progress, Resolved, Closed. |
| `status` | string | No | One of: Open, Closed. |

POST`/tasks/search`Search tasks

POST`/api/v1/partner/tasks/search`[](#ep-tasks-search)

Searches tasks by text and returns a paginated list.

| Name | Type | Required | Description |

| `q` | string | No | Search text. Max 255 characters. |
| `page` | integer | No | Page number. Starts at 1. |
| `per_page` | integer | No | Results per page. Between 1 and 100. |
| `project_id` | string | No | Unique ID of a project, to limit the search to that project. |

### Timelogs5[#](#group-timelogs)

Read and record time entries against projects and tasks.

POST`/timelogs/list`List timelogs

POST`/api/v1/partner/timelogs/list`[](#ep-timelogs-list)

Returns a paginated list of time entries, with optional filters.

| Name | Type | Required | Description |

| `per_page` | integer | No | Results per page. Between 1 and 100. |
| `page` | integer | No | Page number. Starts at 1. |
| `filters` | array | No | Object holding the filter fields below. |
| `filters.project_id` | string | No | Unique ID of the project. |
| `filters.task_id` | string | No | Unique ID of the task. |
| `filters.from` | string (date) | No | Start of the date range. |
| `filters.to` | string (date) | No | End of the date range. Must be on or after filters.from. |
| `filters.is_billable` | boolean | No | Only billable or non billable entries. |
| `filters.timesheet_flag` | boolean | No | Only entries on a timesheet. |
| `filters.q` | string | No | Search text. Max 255 characters. |

POST`/timelogs/search`Search timelogs

POST`/api/v1/partner/timelogs/search`[](#ep-timelogs-search)

Searches time entries and returns a paginated list. Takes the same filters as the list endpoint.

| Name | Type | Required | Description |

| `per_page` | integer | No | Results per page. Between 1 and 100. |
| `page` | integer | No | Page number. Starts at 1. |
| `filters` | array | No | Object holding the filter fields below. |
| `filters.project_id` | string | No | Unique ID of the project. |
| `filters.task_id` | string | No | Unique ID of the task. |
| `filters.from` | string (date) | No | Start of the date range. |
| `filters.to` | string (date) | No | End of the date range. Must be on or after filters.from. |
| `filters.is_billable` | boolean | No | Only billable or non billable entries. |
| `filters.timesheet_flag` | boolean | No | Only entries on a timesheet. |
| `filters.q` | string | No | Search text. Max 255 characters. |

POST`/timelogs/detail`Get a timelog

POST`/api/v1/partner/timelogs/detail`[](#ep-timelogs-detail)

Returns one time entry by its unique ID.

| Name | Type | Required | Description |

| `timelog_id` | string | Yes | Unique ID of the time entry. |

POST`/timelogs/create`Create a timelog

POST`/api/v1/partner/timelogs/create`[](#ep-timelogs-create)

Records a new time entry against a project, and optionally against a task.

| Name | Type | Required | Description |

| `project_id` | string | Yes | Unique ID of the project. |
| `task_id` | string | No | Unique ID of the task. |
| `task_date` | string (date) | Yes | Date the work was done. |
| `start_datetime` | string (date) | No | Full start date and time. |
| `end_datetime` | string (date) | No | Full end date and time. Must be after start\_datetime. |
| `start_time` | string (date) | No | Start time in HH:MM:SS format. Required when start\_datetime is not sent. |
| `end_time` | string (date) | No | End time in HH:MM:SS format. Required when end\_datetime is not sent. |
| `total_hours` | number | No | Total hours logged. Cannot be negative. |
| `break_time` | number | No | Break time in hours. Cannot be negative. |
| `description` | string | Yes | What the time was spent on. Max 1000 characters. |
| `is_from_timer` | boolean | No | Whether the entry came from a running timer. |
| `is_billable` | boolean | No | Whether the time is billable. |
| `timesheet_flag` | boolean | No | Whether the entry belongs to a timesheet. |
| `task_status` | integer | No | Status of the task at the time of logging. |
| `pending_status` | integer | No | Approval status of the entry. |
| `approver_id` | integer | No | User ID of the approver. |
| `ip` | string | No | IP address the entry was logged from. Must be a valid IP. |

POST`/timelogs/update`Update a timelog

POST`/api/v1/partner/timelogs/update`[](#ep-timelogs-update)

Updates the fields you send on an existing time entry. Fields you leave out are not changed.

| Name | Type | Required | Description |

| `timelog_id` | string | Yes | Unique ID of the time entry. |
| `project_id` | string | No | Unique ID of the project. |
| `task_id` | string | No | Unique ID of the task. |
| `task_date` | string (date) | No | Date the work was done. |
| `start_datetime` | string (date) | No | Full start date and time. |
| `end_datetime` | string (date) | No | Full end date and time. Must be after start\_datetime. |
| `start_time` | string (date) | No | Start time in HH:MM:SS format. |
| `end_time` | string (date) | No | End time in HH:MM:SS format. |
| `total_hours` | number | No | Total hours logged. Cannot be negative. |
| `break_time` | number | No | Break time in hours. Cannot be negative. |
| `description` | string | No | What the time was spent on. Max 1000 characters. |
| `is_from_timer` | boolean | No | Whether the entry came from a running timer. |
| `is_billable` | boolean | No | Whether the time is billable. |
| `timesheet_flag` | boolean | No | Whether the entry belongs to a timesheet. |
| `task_status` | integer | No | Status of the task at the time of logging. |
| `pending_status` | integer | No | Approval status of the entry. |
| `approver_id` | integer | No | User ID of the approver. |
| `ip` | string | No | IP address the entry was logged from. Must be a valid IP. |

### Users2[#](#group-users)

Read the people in the connected Orangescrum account. These endpoints are read only.

POST`/users/list`List users

POST`/api/v1/partner/users/list`[](#ep-users-list)

Returns a paginated list of users, with optional filters.

| Name | Type | Required | Description |

| `filters` | array | No | Object holding the filter fields below. |
| `filters.search` | string | No | Search by name or email. |
| `filters.project_id` | string | No | Unique ID of a project, to return only its members. |
| `page` | integer | No | Page number. Starts at 1. |
| `per_page` | integer | No | Results per page. Between 1 and 100. |

POST`/users/detail`Get a user

POST`/api/v1/partner/users/detail`[](#ep-users-detail)

Returns one user by their unique ID.

| Name | Type | Required | Description |

| `uniq_id` | string | Yes | Unique ID of the user. |

### Test cases5[#](#group-test-cases)

Manage test cases, the individual checks that belong to a project and optionally to a test scenario.

POST`/test-cases/list`List test cases

POST`/api/v1/partner/test-cases/list`[](#ep-test-cases-list)

Returns a paginated list of test cases, with optional filters.

| Name | Type | Required | Description |

| `project_id` | string | No | Unique ID of the project. |
| `scenario_id` | string | No | Unique ID of the test scenario. |
| `status` | string | No | Test case status. Max 50 characters. |
| `priority` | string | No | Test case priority. Max 100 characters. |
| `q` | string | No | Search text. Max 255 characters. |
| `include_archived` | boolean | No | Include archived test cases. |
| `page` | integer | No | Page number. Starts at 1. |
| `per_page` | integer | No | Results per page. Between 1 and 100. |

POST`/test-cases/create`Create a test case

POST`/api/v1/partner/test-cases/create`[](#ep-test-cases-create)

Creates a test case in a project and returns it.

| Name | Type | Required | Description |

| `project_id` | string | Yes | Unique ID of the project. |
| `name` | string | Yes | Test case name. Max 255 characters. |
| `description` | string | No | Test case description. Max 255 characters. |
| `type` | string | No | Test case type. Max 50 characters. |
| `priority` | string | No | Test case priority. Max 100 characters. |
| `severity` | string | No | Test case severity. Max 100 characters. |
| `behaviour` | string | No | Expected behaviour. Max 100 characters. |
| `automation_status` | string | No | Automation status. Max 100 characters. |
| `pre_condition` | string | No | What must be true before the test runs. |
| `post_condition` | string | No | What should be true after the test runs. |
| `expected_result` | string | No | Expected result. Max 255 characters. |
| `status` | string | No | Test case status. Max 50 characters. |
| `version` | string | No | Version label. Max 50 characters. |
| `scenario_id` | string | No | Unique ID of the test scenario to link to. |
| `epic_id` | integer | No | ID of the epic to link to. |
| `feature_id` | integer | No | ID of the feature to link to. |
| `story_id` | integer | No | ID of the story to link to. |

POST`/test-cases/detail`Get a test case

POST`/api/v1/partner/test-cases/detail`[](#ep-test-cases-detail)

Returns one test case by its unique ID.

| Name | Type | Required | Description |

| `test_case_id` | string | Yes | Unique ID of the test case. |

POST`/test-cases/update`Update a test case

POST`/api/v1/partner/test-cases/update`[](#ep-test-cases-update)

Updates the fields you send on an existing test case. Fields you leave out are not changed.

| Name | Type | Required | Description |

| `test_case_id` | string | Yes | Unique ID of the test case. |
| `name` | string | No | Test case name. Max 255 characters. |
| `description` | string | No | Test case description. Max 255 characters. |
| `type` | string | No | Test case type. Max 50 characters. |
| `priority` | string | No | Test case priority. Max 100 characters. |
| `severity` | string | No | Test case severity. Max 100 characters. |
| `behaviour` | string | No | Expected behaviour. Max 100 characters. |
| `automation_status` | string | No | Automation status. Max 100 characters. |
| `pre_condition` | string | No | What must be true before the test runs. |
| `post_condition` | string | No | What should be true after the test runs. |
| `expected_result` | string | No | Expected result. Max 255 characters. |
| `status` | string | No | Test case status. Max 50 characters. |
| `version` | string | No | Version label. Max 50 characters. |
| `scenario_id` | string | No | Unique ID of the test scenario to link to. |
| `epic_id` | integer | No | ID of the epic to link to. |
| `feature_id` | integer | No | ID of the feature to link to. |
| `story_id` | integer | No | ID of the story to link to. |

POST`/test-cases/delete`Delete a test case

POST`/api/v1/partner/test-cases/delete`[](#ep-test-cases-delete)

Archives a test case, or removes it permanently when hard\_delete is true.

| Name | Type | Required | Description |

| `test_case_id` | string | Yes | Unique ID of the test case. |
| `hard_delete` | boolean | No | Set to true to delete permanently instead of archiving. |

### Test scenarios5[#](#group-test-scenarios)

Manage test scenarios, the groupings that test cases belong to.

POST`/test-scenarios/list`List test scenarios

POST`/api/v1/partner/test-scenarios/list`[](#ep-test-scenarios-list)

Returns a paginated list of test scenarios, with optional filters.

| Name | Type | Required | Description |

| `project_id` | string | No | Unique ID of the project. |
| `status` | string | No | Scenario status. Max 50 characters. |
| `type` | string | No | Scenario type. Max 100 characters. |
| `q` | string | No | Search text. Max 255 characters. |
| `include_archived` | boolean | No | Include archived scenarios. |
| `page` | integer | No | Page number. Starts at 1. |
| `per_page` | integer | No | Results per page. Between 1 and 100. |

POST`/test-scenarios/create`Create a test scenario

POST`/api/v1/partner/test-scenarios/create`[](#ep-test-scenarios-create)

Creates a test scenario in a project and returns it.

| Name | Type | Required | Description |

| `project_id` | string | Yes | Unique ID of the project. |
| `name` | string | Yes | Scenario name. Max 255 characters. |
| `description` | string | No | Scenario description. Max 255 characters. |
| `type` | string | No | Scenario type. Max 100 characters. |
| `coverage_percentage` | number | No | Coverage percentage. Between 0 and 100. |
| `test_strategy_id` | integer | No | ID of the test strategy. |
| `status` | string | No | Scenario status. Max 50 characters. |
| `version` | string | No | Version label. Max 50 characters. |

POST`/test-scenarios/detail`Get a test scenario

POST`/api/v1/partner/test-scenarios/detail`[](#ep-test-scenarios-detail)

Returns one test scenario by its unique ID.

| Name | Type | Required | Description |

| `scenario_id` | string | Yes | Unique ID of the test scenario. |

POST`/test-scenarios/update`Update a test scenario

POST`/api/v1/partner/test-scenarios/update`[](#ep-test-scenarios-update)

Updates the fields you send on an existing test scenario. Fields you leave out are not changed.

| Name | Type | Required | Description |

| `scenario_id` | string | Yes | Unique ID of the test scenario. |
| `name` | string | No | Scenario name. Max 255 characters. |
| `description` | string | No | Scenario description. Max 255 characters. |
| `type` | string | No | Scenario type. Max 100 characters. |
| `coverage_percentage` | number | No | Coverage percentage. Between 0 and 100. |
| `test_strategy_id` | integer | No | ID of the test strategy. |
| `status` | string | No | Scenario status. Max 50 characters. |
| `version` | string | No | Version label. Max 50 characters. |

POST`/test-scenarios/delete`Delete a test scenario

POST`/api/v1/partner/test-scenarios/delete`[](#ep-test-scenarios-delete)

Archives a test scenario, or removes it permanently when hard\_delete is true.

| Name | Type | Required | Description |

| `scenario_id` | string | Yes | Unique ID of the test scenario. |
| `hard_delete` | boolean | No | Set to true to delete permanently instead of archiving. |

### Test steps5[#](#group-test-steps)

Manage the ordered steps inside a test case.

POST`/test-steps/list`List test steps

POST`/api/v1/partner/test-steps/list`[](#ep-test-steps-list)

Returns the steps of one test case, in order and paginated.

| Name | Type | Required | Description |

| `test_case_id` | string | Yes | Unique ID of the test case. |
| `include_archived` | boolean | No | Include archived steps. |
| `page` | integer | No | Page number. Starts at 1. |
| `per_page` | integer | No | Results per page. Between 1 and 100. |

POST`/test-steps/create`Create a test step

POST`/api/v1/partner/test-steps/create`[](#ep-test-steps-create)

Adds a step to a test case. Send a name, a description, or both.

| Name | Type | Required | Description |

| `test_case_id` | string | Yes | Unique ID of the test case. |
| `name` | string | No | Step name. Max 255 characters. |
| `description` | string | No | Step description. Max 255 characters. Required when name is not sent. |
| `expected_result` | string | No | Expected result. Max 255 characters. |
| `outcome` | string | No | Actual outcome. Max 255 characters. |
| `status` | string | No | Step status. Max 50 characters. |
| `version` | string | No | Version label. Max 50 characters. |

POST`/test-steps/update`Update a test step

POST`/api/v1/partner/test-steps/update`[](#ep-test-steps-update)

Updates the fields you send on an existing step. Fields you leave out are not changed.

| Name | Type | Required | Description |

| `test_case_id` | string | Yes | Unique ID of the test case. |
| `step_id` | string | Yes | Unique ID of the step. |
| `name` | string | No | Step name. Max 255 characters. |
| `description` | string | No | Step description. Max 255 characters. |
| `expected_result` | string | No | Expected result. Max 255 characters. |
| `outcome` | string | No | Actual outcome. Max 255 characters. |
| `status` | string | No | Step status. Max 50 characters. |
| `version` | string | No | Version label. Max 50 characters. |

POST`/test-steps/reorder`Reorder test steps

POST`/api/v1/partner/test-steps/reorder`[](#ep-test-steps-reorder)

Sets the order of the steps in a test case. Send the step IDs in the order you want.

| Name | Type | Required | Description |

| `test_case_id` | string | Yes | Unique ID of the test case. |
| `step_ids` | array | Yes | Step IDs in the new order. At least one is required. |
| `step_ids.*` | string | Yes | Each entry is the unique ID of a step. |

POST`/test-steps/delete`Delete a test step

POST`/api/v1/partner/test-steps/delete`[](#ep-test-steps-delete)

Archives a step, or removes it permanently when hard\_delete is true.

| Name | Type | Required | Description |

| `test_case_id` | string | Yes | Unique ID of the test case. |
| `step_id` | string | Yes | Unique ID of the step. |
| `hard_delete` | boolean | No | Set to true to delete permanently instead of archiving. |

### Checklist configuration1[#](#group-checklist-config)

Read how the checklist framework is set up for the account.

POST`/checklists/config`Get checklist configuration

POST`/api/v1/partner/checklists/config`[](#ep-checklists-config)

Returns the checklist settings for the account, optionally narrowed to one work item type.

| Name | Type | Required | Description |

| `work_item_type` | string | No | One of: epic, feature, story, task, defect, test\_scenario, test\_case, test\_defect, project, program. |

### Checklist groups4[#](#group-checklist-groups)

Manage the account level catalogue of checklist groups.

POST`/checklists/groups/list`List checklist groups

POST`/api/v1/partner/checklists/groups/list`[](#ep-checklists-groups-list)

Returns the checklist groups in the account catalogue.

| Name | Type | Required | Description |

| `active_only` | boolean | No | Return only active groups. |

POST`/checklists/groups/create`Create a checklist group

POST`/api/v1/partner/checklists/groups/create`[](#ep-checklists-groups-create)

Adds a new checklist group to the account catalogue.

| Name | Type | Required | Description |

| `name` | string | Yes | Group name. Max 255 characters. |
| `color` | string | No | Colour code for the group. Max 16 characters. |
| `is_active` | boolean | No | Whether the group is active. |

POST`/checklists/groups/update`Update a checklist group

POST`/api/v1/partner/checklists/groups/update`[](#ep-checklists-groups-update)

Updates the fields you send on an existing checklist group.

| Name | Type | Required | Description |

| `group_id` | integer | Yes | ID of the checklist group. |
| `name` | string | No | Group name. Max 255 characters. |
| `color` | string | No | Colour code for the group. Max 16 characters. |
| `is_active` | boolean | No | Whether the group is active. |

POST`/checklists/groups/delete`Delete a checklist group

POST`/api/v1/partner/checklists/groups/delete`[](#ep-checklists-groups-delete)

Removes a checklist group from the account catalogue.

| Name | Type | Required | Description |

| `group_id` | integer | Yes | ID of the checklist group. |

### Checklist templates4[#](#group-checklist-templates)

Manage reusable checklist templates. A template belongs to a checklist group and holds a list of items.

POST`/checklists/templates/list`List checklist templates

POST`/api/v1/partner/checklists/templates/list`[](#ep-checklists-templates-list)

Returns every checklist template in the account. This endpoint takes no parameters.

This endpoint takes no parameters. Send an empty object as the payload.

POST`/checklists/templates/create`Create a checklist template

POST`/api/v1/partner/checklists/templates/create`[](#ep-checklists-templates-create)

Adds a new checklist template, with its items, to a checklist group.

| Name | Type | Required | Description |

| `checklist_group_id` | integer | Yes | ID of the checklist group. |
| `name` | string | Yes | Template name. Max 255 characters. |
| `auto_attach` | boolean | No | Attach this template automatically when a matching work item is created. |
| `is_active` | boolean | No | Whether the template is active. |
| `items` | array | No | The checklist items in the template. |
| `items.*.label` | string | No | Text of each item. Max 2000 characters. Required when items is sent. |

POST`/checklists/templates/update`Update a checklist template

POST`/api/v1/partner/checklists/templates/update`[](#ep-checklists-templates-update)

Updates the fields you send on an existing checklist template.

| Name | Type | Required | Description |

| `template_id` | integer | Yes | ID of the checklist template. |
| `checklist_group_id` | integer | No | ID of the checklist group. |
| `name` | string | No | Template name. Max 255 characters. |
| `auto_attach` | boolean | No | Attach this template automatically when a matching work item is created. |
| `is_active` | boolean | No | Whether the template is active. |
| `items` | array | No | The checklist items in the template. |
| `items.*.label` | string | No | Text of each item. Max 2000 characters. Required when items is sent. |

POST`/checklists/templates/delete`Delete a checklist template

POST`/api/v1/partner/checklists/templates/delete`[](#ep-checklists-templates-delete)

Removes a checklist template from the account.

| Name | Type | Required | Description |

| `template_id` | integer | Yes | ID of the checklist template. |

### Work item checklists11[#](#group-work-item-checklists)

Read and edit the checklist attached to a single work item, such as a task, story, or defect. Groups and items here live on the work item itself, not in the account catalogue.

POST`/work-items/checklist/detail`Get a work item checklist

POST`/api/v1/partner/work-items/checklist/detail`[](#ep-work-items-checklist-detail)

Returns the checklist groups, items, and completion counts for one work item.

| Name | Type | Required | Description |

| `work_item_type` | string | Yes | One of: epic, feature, story, task, defect, test\_scenario, test\_case, test\_defect, project, program. |
| `work_item_id` | integer | Yes | Numeric ID of the work item. |

POST`/work-items/checklist/items/add`Add a checklist item

POST`/api/v1/partner/work-items/checklist/items/add`[](#ep-work-items-checklist-items-add)

Adds one item to a checklist group on a work item.

| Name | Type | Required | Description |

| `work_item_type` | string | Yes | One of: epic, feature, story, task, defect, test\_scenario, test\_case, test\_defect, project, program. |
| `work_item_id` | integer | Yes | Numeric ID of the work item. |
| `work_item_group_id` | integer | Yes | ID of the checklist group on the work item. |
| `label` | string | Yes | Text of the item. Max 2000 characters. |

POST`/work-items/checklist/items/update`Update a checklist item

POST`/api/v1/partner/work-items/checklist/items/update`[](#ep-work-items-checklist-items-update)

Changes the text of one checklist item.

| Name | Type | Required | Description |

| `item_id` | integer | Yes | ID of the checklist item. |
| `label` | string | Yes | New text of the item. Max 2000 characters. |

POST`/work-items/checklist/items/delete`Delete a checklist item

POST`/api/v1/partner/work-items/checklist/items/delete`[](#ep-work-items-checklist-items-delete)

Removes one checklist item from a work item.

| Name | Type | Required | Description |

| `item_id` | integer | Yes | ID of the checklist item. |

POST`/work-items/checklist/items/complete`Complete a checklist item

POST`/api/v1/partner/work-items/checklist/items/complete`[](#ep-work-items-checklist-items-complete)

Marks a checklist item as done, or reopens it.

| Name | Type | Required | Description |

| `item_id` | integer | Yes | ID of the checklist item. |
| `is_completed` | boolean | No | True to mark the item done, false to reopen it. |

POST`/work-items/checklist/groups/add`Add a checklist group

POST`/api/v1/partner/work-items/checklist/groups/add`[](#ep-work-items-checklist-groups-add)

Copies a group from the account catalogue onto a work item.

| Name | Type | Required | Description |

| `work_item_type` | string | Yes | One of: epic, feature, story, task, defect, test\_scenario, test\_case, test\_defect, project, program. |
| `work_item_id` | integer | Yes | Numeric ID of the work item. |
| `source_group_id` | integer | Yes | ID of the catalogue checklist group to copy. |

POST`/work-items/checklist/groups/create-custom`Create a custom checklist group

POST`/api/v1/partner/work-items/checklist/groups/create-custom`[](#ep-work-items-checklist-groups-create-custom)

Creates a group that exists only on this work item, not in the account catalogue.

| Name | Type | Required | Description |

| `work_item_type` | string | Yes | One of: epic, feature, story, task, defect, test\_scenario, test\_case, test\_defect, project, program. |
| `work_item_id` | integer | Yes | Numeric ID of the work item. |
| `name` | string | Yes | Group name. Max 255 characters. |

POST`/work-items/checklist/groups/rename`Rename a checklist group

POST`/api/v1/partner/work-items/checklist/groups/rename`[](#ep-work-items-checklist-groups-rename)

Changes the name of a checklist group on a work item.

| Name | Type | Required | Description |

| `work_item_group_id` | integer | Yes | ID of the checklist group on the work item. |
| `name` | string | Yes | New group name. Max 255 characters. |

POST`/work-items/checklist/groups/delete`Delete a checklist group

POST`/api/v1/partner/work-items/checklist/groups/delete`[](#ep-work-items-checklist-groups-delete)

Removes a checklist group, and its items, from a work item.

| Name | Type | Required | Description |

| `work_item_group_id` | integer | Yes | ID of the checklist group on the work item. |

POST`/work-items/checklist/groups/reorder`Reorder checklist groups

POST`/api/v1/partner/work-items/checklist/groups/reorder`[](#ep-work-items-checklist-groups-reorder)

Sets the order of the checklist groups on a work item.

| Name | Type | Required | Description |

| `work_item_type` | string | Yes | One of: epic, feature, story, task, defect, test\_scenario, test\_case, test\_defect, project, program. |
| `work_item_id` | integer | Yes | Numeric ID of the work item. |
| `group_ids` | array | Yes | Group IDs in the new order. At least one is required. |
| `group_ids.*` | integer | Yes | Each entry is the ID of a checklist group. |

POST`/work-items/checklist/groups/add-from-template`Add a checklist group from a template

POST`/api/v1/partner/work-items/checklist/groups/add-from-template`[](#ep-work-items-checklist-groups-add-from-template)

Adds a group to a work item using a checklist template, including all of its items.

| Name | Type | Required | Description |

| `work_item_type` | string | Yes | One of: epic, feature, story, task, defect, test\_scenario, test\_case, test\_defect, project, program. |
| `work_item_id` | integer | Yes | Numeric ID of the work item. |
| `template_id` | integer | Yes | ID of the checklist template to apply. |

### Defects6[#](#group-defects)

Track defects raised against a project, and link them to test cases, test steps, or tasks.

POST`/defects/list`List defects

POST`/api/v1/partner/defects/list`[](#ep-defects-list)

Returns a paginated list of defects, with optional filters.

| Name | Type | Required | Description |

| `project_id` | string | No | Unique ID of the project. |
| `status` | string | No | Defect status. Max 50 characters. |
| `priority` | string | No | Defect priority. Max 50 characters. |
| `severity` | string | No | Defect severity. Max 50 characters. |
| `test_case_id` | string | No | Unique ID of a linked test case. |
| `q` | string | No | Search text. Max 255 characters. |
| `page` | integer | No | Page number. Starts at 1. |
| `per_page` | integer | No | Results per page. Between 1 and 100. |

POST`/defects/create`Create a defect

POST`/api/v1/partner/defects/create`[](#ep-defects-create)

Raises a new defect in a project and returns it.

| Name | Type | Required | Description |

| `project_id` | string | Yes | Unique ID of the project. |
| `title` | string | Yes | Defect title. Max 255 characters. |
| `description` | string | No | Defect description. Max 255 characters. |
| `type` | string | No | Defect type. Max 50 characters. |
| `priority` | string | No | Defect priority. Max 50 characters. |
| `severity` | string | No | Defect severity. Max 50 characters. |
| `status` | string | No | Defect status. Max 50 characters. |
| `test_case_id` | string | No | Unique ID of the test case to link to. |
| `test_step_id` | string | No | Unique ID of the test step to link to. |
| `task_id` | string | No | Unique ID of the task to link to. |
| `epic_id` | integer | No | ID of the epic to link to. |
| `feature_id` | integer | No | ID of the feature to link to. |

POST`/defects/detail`Get a defect

POST`/api/v1/partner/defects/detail`[](#ep-defects-detail)

Returns one defect by its unique ID.

| Name | Type | Required | Description |

| `defect_id` | string | Yes | Unique ID of the defect. |

POST`/defects/update`Update a defect

POST`/api/v1/partner/defects/update`[](#ep-defects-update)

Updates the fields you send on an existing defect. Fields you leave out are not changed.

| Name | Type | Required | Description |

| `defect_id` | string | Yes | Unique ID of the defect. |
| `title` | string | No | Defect title. Max 255 characters. |
| `description` | string | No | Defect description. Max 255 characters. |
| `type` | string | No | Defect type. Max 50 characters. |
| `priority` | string | No | Defect priority. Max 50 characters. |
| `severity` | string | No | Defect severity. Max 50 characters. |
| `status` | string | No | Defect status. Max 50 characters. |
| `epic_id` | integer | No | ID of the epic to link to. |
| `feature_id` | integer | No | ID of the feature to link to. |

POST`/defects/link`Link a defect

POST`/api/v1/partner/defects/link`[](#ep-defects-link)

Links a defect to a test case, a test step, or a task. Send at least one of the three.

| Name | Type | Required | Description |

| `defect_id` | string | Yes | Unique ID of the defect. |
| `test_case_id` | string | No | Unique ID of the test case. Required when test\_step\_id and task\_id are both missing. |
| `test_step_id` | string | No | Unique ID of the test step. |
| `task_id` | string | No | Unique ID of the task. |

POST`/defects/status`Set defect status

POST`/api/v1/partner/defects/status`[](#ep-defects-status)

Changes the status of a defect.

| Name | Type | Required | Description |

| `defect_id` | string | Yes | Unique ID of the defect. |
| `status` | string | Yes | New defect status. Max 50 characters. |

## Something wrong here?[](#help)

If an endpoint behaves differently from what this page says, the page is probably out of date and we want to know. Email [support@orangescrum.com](mailto:support@orangescrum.com) with the endpoint and what you saw.

Looking to let an AI assistant work in Orangescrum instead of writing an integration? See the [MCP server](/mcp).
