---
id: public-api.cases
title: Cases API
summary: Read canonical project Cases and submit supported imports without confusing current project content with frozen benchmark membership.
kind: api
product_area: public_api
status: stable
updated: 2026-10-31
canonical: /docs/public-api/cases
---

# Cases API

## Definition

Under the organization/project prefix, current routes include:

```http
GET /cases
GET /cases/{caseId}
POST /cases/imports
GET /cases/imports/{importId}
PUT /cases/{caseId}/reference-result
POST /cases/reference-results
```

Paths above are suffixes of `/v1/organizations/{organizationKey}/projects/{projectId}`.

## Read behavior

Case lists use `data.columns` and `data.rows`; detail uses `result`. Case IDs and dataset-record IDs remain their issued identities. Canonical content, schema version, derived search text, compatibility metadata and latest reference-output context are distinct fields.

These project reads do not define membership of a fixed Benchmark Version. Use the benchmark `case-manifest` for exact frozen Cases used by an evaluation.

## Reference responses

A Case reference-result operation is different from an external candidate Run. Reference-result replacement controls and outcomes do not authorize overwriting accepted benchmark Trial responses.

## Input contract

Canonical Case content uses its declared input container and material blocks. Project Input Schema constrains future Case authoring. This router does not establish the old proposed customer `/input-schema` or `/cases/{caseId}/versions` endpoints.

## List and retrieve Cases

### Example: Read Cases

```sh
curl --fail-with-body \
  --header "Authorization: Bearer ${TEAMMATELY_API_KEY}" \
  "${PROJECT_URL}/cases?limit=10&offset=0"

# CASE_ID is a case_id returned in data.rows.
curl --fail-with-body \
  --header "Authorization: Bearer ${TEAMMATELY_API_KEY}" \
  "${PROJECT_URL}/cases/${CASE_ID}"
```

```python
import os
from urllib.parse import quote, urlencode
from urllib.request import HTTPRedirectHandler, Request, build_opener

class NoRedirect(HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, headers, newurl):
        return None

opener = build_opener(NoRedirect())
api_key = os.environ["TEAMMATELY_API_KEY"]

url = os.environ["PROJECT_URL"] + "/cases"
url += "?" + urlencode({"limit": "10", "offset": "0"})
headers = {"Authorization": "Bearer " + api_key}
request = Request(url, headers=headers, method="GET")
with opener.open(request) as response:
    print(response.read().decode("utf-8"))

url = os.environ["PROJECT_URL"] + "/cases/" + quote(os.environ["CASE_ID"], safe="")
headers = {"Authorization": "Bearer " + api_key}
request = Request(url, headers=headers, method="GET")
with opener.open(request) as response:
    print(response.read().decode("utf-8"))
```

```typescript
const apiKey = process.env.TEAMMATELY_API_KEY!;

{
  const url = new URL(process.env.PROJECT_URL! + "/cases");
  url.searchParams.set("limit", "10");
  url.searchParams.set("offset", "0");
  const response = await fetch(url, {
    method: "GET",
    redirect: "error",
    headers: {
      Authorization: `Bearer ${apiKey}`,
    },
  });
  if (!response.ok) throw new Error(`HTTP ${response.status}`);
  console.log(await response.text());
}

{
  const url = new URL(process.env.PROJECT_URL! + "/cases/" + encodeURIComponent(process.env.CASE_ID!));
  const response = await fetch(url, {
    method: "GET",
    redirect: "error",
    headers: {
      Authorization: `Bearer ${apiKey}`,
    },
  });
  if (!response.ok) throw new Error(`HTTP ${response.status}`);
  console.log(await response.text());
}
```

```go
package main

import (
    "context"
    "fmt"
    "io"
    "log"
    "net/http"
    "net/url"
    "os"
)

func main() {
    if err := run(context.Background()); err != nil { log.Fatal(err) }
}

func run(ctx context.Context) error {
    client := &http.Client{
        CheckRedirect: func(req *http.Request, via []*http.Request) error {
            return http.ErrUseLastResponse
        },
    }
    {
        target := os.Getenv("PROJECT_URL") + "/cases"
        query := url.Values{}
        query.Set("limit", "10")
        query.Set("offset", "0")
        target += "?" + query.Encode()
        request, err := http.NewRequestWithContext(ctx, "GET", target, nil)
        if err != nil { return err }
        request.Header.Set("Authorization", "Bearer " + os.Getenv("TEAMMATELY_API_KEY"))
        response, err := client.Do(request)
        if err != nil { return err }
        payloadBytes, readErr := io.ReadAll(response.Body)
        response.Body.Close()
        if readErr != nil { return readErr }
        fmt.Println(string(payloadBytes))
        if response.StatusCode < 200 || response.StatusCode >= 300 { return fmt.Errorf("HTTP %d", response.StatusCode) }
    }
    {
        target := os.Getenv("PROJECT_URL") + "/cases/" + url.PathEscape(os.Getenv("CASE_ID"))
        request, err := http.NewRequestWithContext(ctx, "GET", target, nil)
        if err != nil { return err }
        request.Header.Set("Authorization", "Bearer " + os.Getenv("TEAMMATELY_API_KEY"))
        response, err := client.Do(request)
        if err != nil { return err }
        payloadBytes, readErr := io.ReadAll(response.Body)
        response.Body.Close()
        if readErr != nil { return readErr }
        fmt.Println(string(payloadBytes))
        if response.StatusCode < 200 || response.StatusCode >= 300 { return fmt.Errorf("HTTP %d", response.StatusCode) }
    }
    return nil
}
```

Configure `PROJECT_URL` with the [quickstart](/docs/public-api/quickstart). The list defaults to 10 rows, permits `limit` from 1 through 1,000 and uses nonnegative `offset`. Conditional reads use the returned ETag with `If-None-Match`.

## Write a reference response

`PUT /cases/{caseId}/reference-result` accepts `output` and optional normalized `trajectory`. It returns `201` for a created reference or `200` for a replacement/unchanged reference, plus an ETag. `If-Match` supplies a conditional precondition.

The batch operation requires `Idempotency-Key` and returns per-Case outcomes. See `ReferenceResultWrite` and `ReferenceResultBatchWrite` in the [OpenAPI](https://api.teammately.ai/v1/openapi.yaml) for replacement controls and batch fields.
## Related reference pages

{% related-card-grid title="Related reference pages" %}
- [Case Serialization](/docs/public-api/case-schema)
- [Import Cases](/docs/public-api/case-imports)
- [Submit External Evaluation Outputs](/docs/public-api/evaluation-outputs)
{% /related-card-grid %}
