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

# Practice studio

## Worked practice-studio samples

The endpoint list below documents every operation's parameters; this
overlay walks a roleplay session the way a coaching integration actually
uses it: **list the built-in scenarios → drive the simulated customer's
next turn → grade the finished session**. Success envelopes are `{ data, meta }`, error envelopes
`{ error, meta }` — see [How to read a
worked sample](/guides/using-orbit-samples). The instructor persona lives
in the scenario (persona, situation, objective, grading rubric); the
roleplay client keeps the transcript across calls, and the grade summary
moves to per-criterion feedback the LLM judge returns.

Every response carries `meta.request_id`. Quote the request id when a
rubric score disagrees with a session replay — support can pair the
scenario id, the score, and the request id from the turn index.

### 1. List built-in practice scenarios

`GET /api/v1/practice-studio/scenarios` returns the built-in
AI-simulated-customer roleplay scenarios: persona, situation,
objective, and grading rubric. Use this to pick a starting scenario for
your coaching loop before you drive turns.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X GET \
    "https://api.orbit.devotel.io/api/v1/practice-studio/scenarios" \
    -H "X-API-Key: dv_test_sk_YOUR_KEY"
  ```

  ```typescript Node.js theme={null}
  const res = await fetch(
    "https://api.orbit.devotel.io/api/v1/practice-studio/scenarios",
    {
      headers: { "X-API-Key": process.env.ORBIT_API_KEY! },
    },
  );
  console.log(await res.json());
  ```
</CodeGroup>

```json 200 theme={null}
{
  "data": {
    "scenarios": [
      {
        "id": "scn_refund_abuse",
        "persona": "frustrated customer",
        "situation": "they report a charged refund that never cleared",
        "objective": "de-escalate, pull the ledger, resolve",
        "criteria": ["empathy", "resolution", "policy"]
      }
    ]
  },
  "meta": {
    "request_id": "req_prs_scenarios",
    "timestamp": "2026-08-26T12:00:00.000Z"
  }
}
```

### 2. Drive the simulated customer's next turn

`POST /api/v1/practice-studio/turn` returns the next customer message
and session signals — mood, satisfaction, resolved, ended. The AI stays
in character as the customer and never coaches the trainee, so the
transcript is the only surface of truth you need to keep.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://api.orbit.devotel.io/api/v1/practice-studio/turn" \
    -H "X-API-Key: dv_test_sk_YOUR_KEY" \
    -H "Content-Type: application/json" \
    -d '{
    "scenario_id": "scn_refund_abuse",
    "transcript": [
      { "speaker": "customer", "text": "My refund never landed. I want it today." },
      { "speaker": "agent", "text": "I can check the ledger for you right now." }
    ]
  }'
  ```

  ```typescript Node.js theme={null}
  const res = await fetch(
    "https://api.orbit.devotel.io/api/v1/practice-studio/turn",
    {
      method: "POST",
      headers: {
        "X-API-Key": process.env.ORBIT_API_KEY!,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        scenario_id: "scn_refund_abuse",
        transcript: [
          { speaker: "customer", text: "My refund never landed. I want it today." },
          { speaker: "agent", text: "I can check the ledger for you right now." },
        ],
      }),
    },
  );
  console.log(await res.json());
  ```
</CodeGroup>

```json 200 theme={null}
{
  "data": {
    "next_message": "You said that yesterday. Where's the money?",
    "signals": {
      "mood": "irritated",
      "satisfaction": 0.32,
      "resolved": false,
      "ended": false
    }
  },
  "meta": {
    "request_id": "req_prs_turn",
    "timestamp": "2026-08-26T12:01:00.000Z"
  }
}
```

### 3. Grade the finished session

`POST /api/v1/practice-studio/score` takes the full transcript and an
LLM judges the trainee against the scenario rubric — weighted overall
score, per-criterion feedback, strengths, and concrete improvements.
Call it when `signals.ended` flips to `true`.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://api.orbit.devotel.io/api/v1/practice-studio/score" \
    -H "X-API-Key: dv_test_sk_YOUR_KEY" \
    -H "Content-Type: application/json" \
    -d '{
    "scenario_id": "scn_refund_abuse",
    "transcript": [
      { "speaker": "customer", "text": "My refund never landed." },
      { "speaker": "agent", "text": "I can check the ledger for you right now." },
      { "speaker": "customer", "text": "… thank you." }
    ]
  }'
  ```

  ```typescript Node.js theme={null}
  const res = await fetch(
    "https://api.orbit.devotel.io/api/v1/practice-studio/score",
    {
      method: "POST",
      headers: {
        "X-API-Key": process.env.ORBIT_API_KEY!,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        scenario_id: "scn_refund_abuse",
        transcript: [
          { speaker: "customer", text: "My refund never landed." },
          { speaker: "agent", text: "I can check the ledger for you right now." },
          { speaker: "customer", text: "… thank you." },
        ],
      }),
    },
  );
  console.log(await res.json());
  ```
</CodeGroup>

```json 200 theme={null}
{
  "data": {
    "overall_score": 78,
    "criteria": [
      { "name": "empathy", "weight": 0.3, "score": 0.76 },
      { "name": "resolution", "weight": 0.5, "score": 0.82 },
      { "name": "policy", "weight": 0.2, "score": 0.66 }
    ],
    "strengths": ["acknowledged the frustration before solving"],
    "improvements": [
      "state the refund-off reversal in plain numbers before you close"
    ]
  },
  "meta": {
    "request_id": "req_prs_score",
    "timestamp": "2026-08-26T12:02:00.000Z"
  }
}
```

### 4. Errors

Errors follow the `{ error, meta }` envelope. The failure every client
hits:

**422 — missing scenario.** A `scenario_id` or inline `scenario` was not
supplied:

```json 422 theme={null}
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "scenario_id or an inline scenario is required.",
    "status": 422
  },
  "meta": {
    "request_id": "req_prs_err",
    "timestamp": "2026-08-26T12:03:00.000Z"
  }
}
```
