# Teachable API

> Every Teachable endpoint on Cotlet: inputs, JSON Schema, cURL, Python and Node.js examples and an example response for each call.

Source: https://cotlet.dev/docs/api/teachable

Docs sections: [Get started](https://cotlet.dev/docs.md) · [REST API](https://cotlet.dev/docs/api.md) · [MCP](https://cotlet.dev/docs/mcp.md) · [SDKs & CLI](https://cotlet.dev/docs/sdks.md) · [Integrations](https://cotlet.dev/docs/integrations.md)

15 endpoints for Teachable. Each one is a POST with a JSON body, called with your API key.

- Base URL: `https://cotlet.dev/api/v1/teachable`
- Connects with: Your own Teachable school owner account
- Integration page: https://cotlet.dev/integrations/teachable-api-mcp-server

## Endpoints

| Endpoint | Access | Description |
|---|---|---|
| `list_courses` | read | Courses in your school with name, published state and URL. |
| `get_curriculum` | read | Sections and lessons of a course, in order, with published and public preview flags. |
| `get_lesson` | read | Lesson content as blocks: text as Markdown, plus the type of each video, PDF, audio, quiz and other block. |
| `list_students` | read | Students with name, email and join date, or the students of one course. |
| `list_comments` | read | Lesson comments across your school or one course, with author, lesson, status and unread flag. |
| `list_coupons` | read | School and product coupons with code, discount, uses, expiry and status. |
| `create_lesson` | write | New unpublished lesson at the end of a section, with text from Markdown. |
| `update_lesson` | write | Rename a lesson or replace the text of its Text & Image block. Other blocks stay as they are. |
| `publish_lesson` | write | Publish a lesson so enrolled students see it, or unpublish it. |
| `create_coupon` | write | Coupon code for all products, one product type or one pricing plan. |
| `archive_coupon` | write | Unpublish a coupon so the code stops working. Archived codes can be used again. |
| `enroll_student` | write | Enroll a student in a course for free, adding them to your school first if the email is new. |
| `send_email` | write | Send an email from your school to chosen users, the students of a course or all users, as on the Emails, Compose page. |
| `reply_to_comment` | write | Reply to a lesson comment as the school owner. The reply is public to the course. |
| `moderate_comment` | write | Approve or deny a comment held in Pending Approval. |

## List courses

`POST https://cotlet.dev/api/v1/teachable/list_courses`

Access: read.

Returns every course in your school, in the order the Courses page shows them. Use course_id from each item in the other tools. Set published to see only the courses students can enroll in.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `published` | boolean | no | Only published courses. Default: all. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/teachable/list_courses \
  -H "Authorization: Bearer $COTLET_API_KEY"
```

**Python**

```python
result = client.call("teachable", "list_courses")
```

**Node.js**

```js
const result = await cotlet.call('teachable', 'list_courses');
```

**CLI**

```bash
cotlet call teachable list_courses
```

### Example response

```json
{
  "courses": [
    {
      "course_id": "1204512",
      "name": "Python for Beginners",
      "published": true,
      "url": "https://yourschool.teachable.com/p/python-for-beginners"
    },
    {
      "course_id": "1260077",
      "name": "Team Onboarding",
      "published": false,
      "url": "https://yourschool.teachable.com/p/team-onboarding"
    }
  ]
}
```

## Get curriculum

`POST https://cotlet.dev/api/v1/teachable/get_curriculum`

Access: read.

Returns the Curriculum tab of one course: sections in order, and the lessons inside each section in order. Each lesson has a published flag and a public preview flag. Use lesson_id with get_lesson, update_lesson and publish_lesson.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `course_id` | string | yes | Course ID from list_courses. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/teachable/get_curriculum \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"course_id":"course_8c41d2"}'
```

**Python**

```python
result = client.call("teachable", "get_curriculum", {"course_id":"course_8c41d2"})
```

**Node.js**

```js
const result = await cotlet.call('teachable', 'get_curriculum', {"course_id":"course_8c41d2"});
```

**CLI**

```bash
cotlet call teachable get_curriculum --data '{"course_id":"course_8c41d2"}'
```

### Example response

```json
{
  "course_id": "1204512",
  "sections": [
    {
      "section_id": "9120331",
      "name": "Basics",
      "lessons": [
        {
          "lesson_id": "35120871",
          "name": "Lesson 1: Welcome",
          "published": true,
          "public_preview": true
        },
        {
          "lesson_id": "35120874",
          "name": "Lesson 3: Functions",
          "published": true,
          "public_preview": false
        }
      ]
    }
  ]
}
```

## Get lesson

`POST https://cotlet.dev/api/v1/teachable/get_lesson`

Access: read.

Returns the blocks of one lesson in the order the lesson editor shows them. Text & Image blocks come back as Markdown with a block_id. Other blocks (video, PDF viewer, audio, resource, quiz and so on) come back with their type and title only.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `course_id` | string | yes | Course ID from list_courses. |
| `lesson_id` | string | yes | Lesson ID from get_curriculum. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/teachable/get_lesson \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"course_id":"course_8c41d2","lesson_id":"lesson_8c41d2"}'
```

**Python**

```python
result = client.call("teachable", "get_lesson", {"course_id":"course_8c41d2","lesson_id":"lesson_8c41d2"})
```

**Node.js**

```js
const result = await cotlet.call('teachable', 'get_lesson', {"course_id":"course_8c41d2","lesson_id":"lesson_8c41d2"});
```

**CLI**

```bash
cotlet call teachable get_lesson --data '{"course_id":"course_8c41d2","lesson_id":"lesson_8c41d2"}'
```

### Example response

```json
{
  "lesson_id": "35120874",
  "name": "Lesson 3: Functions",
  "published": true,
  "blocks": [
    {
      "block_id": "b_5521",
      "type": "text_and_image",
      "body_markdown": "# Functions\n\nA function groups steps you want to reuse."
    },
    {
      "block_id": "b_5522",
      "type": "video",
      "title": "Functions in 6 minutes"
    }
  ]
}
```

## List students

`POST https://cotlet.dev/api/v1/teachable/list_students`

Access: read.

Lists students of your school, newest first. Pass course_id to list only the students enrolled in one course. Pages hold up to 100 students; pass next_cursor back as cursor for the next page. Use the emails with send_email when you need part of a course.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `course_id` | string | no | Only students enrolled in this course. |
| `limit` | integer | no | 1 to 100. Default: 50. |
| `cursor` | string | no | Next page cursor from the previous call. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/teachable/list_students \
  -H "Authorization: Bearer $COTLET_API_KEY"
```

**Python**

```python
result = client.call("teachable", "list_students")
```

**Node.js**

```js
const result = await cotlet.call('teachable', 'list_students');
```

**CLI**

```bash
cotlet call teachable list_students
```

### Example response

```json
{
  "students": [
    {
      "user_id": "70418822",
      "name": "Maria Kowal",
      "email": "maria.kowal@example.com",
      "joined_at": "2026-09-28T10:14:00Z"
    }
  ],
  "next_cursor": null
}
```

## List comments

`POST https://cotlet.dev/api/v1/teachable/list_comments`

Access: read.

Lists comments the way Site, Comments does, newest first. Status pending returns the Pending Approval queue, which exists only when Comment moderation is on; denied returns comments you denied, which stay in your admin. Use comment_id with moderate_comment and reply_to_comment.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `course_id` | string | no | Only comments in this course. Default: all courses. |
| `status` | enum | no | all, pending or denied. Default: all. |
| `unread_only` | boolean | no | Only comments you have not marked as read. |
| `limit` | integer | no | 1 to 100. Default: 50. |
| `cursor` | string | no | Next page cursor from the previous call. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/teachable/list_comments \
  -H "Authorization: Bearer $COTLET_API_KEY"
```

**Python**

```python
result = client.call("teachable", "list_comments")
```

**Node.js**

```js
const result = await cotlet.call('teachable', 'list_comments');
```

**CLI**

```bash
cotlet call teachable list_comments
```

### Example response

```json
{
  "comments": [
    {
      "comment_id": "88120431",
      "course_id": "1204512",
      "lesson": "Lesson 3: Functions",
      "author": "Maria Kowal",
      "body": "Can a function return two values?",
      "status": "pending",
      "unread": true,
      "created_at": "2026-10-09T14:32:00Z"
    }
  ],
  "next_cursor": null
}
```

## List coupons

`POST https://cotlet.dev/api/v1/teachable/list_coupons`

Access: read.

Returns the coupons from the Coupons page: school coupons (all products or one product type) and product coupons (one pricing plan). Archived coupons are left out unless you ask for them. Check the list before create_coupon so a code is not used twice.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `include_archived` | boolean | no | Also list archived coupons. Default: false. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/teachable/list_coupons \
  -H "Authorization: Bearer $COTLET_API_KEY"
```

**Python**

```python
result = client.call("teachable", "list_coupons")
```

**Node.js**

```js
const result = await cotlet.call('teachable', 'list_coupons');
```

**CLI**

```bash
cotlet call teachable list_coupons
```

### Example response

```json
{
  "coupons": [
    {
      "coupon_id": "3917702",
      "name": "Summer sale",
      "code": "SUMMER20",
      "discount": "20%",
      "applies_to": "all_products",
      "expires_at": "2026-09-01",
      "quantity": 500,
      "used": 212,
      "status": "archived"
    }
  ]
}
```

## Create lesson

`POST https://cotlet.dev/api/v1/teachable/create_lesson`

Access: write.

Adds a lesson to a section, like New lesson in the Curriculum tab. New lessons start unpublished, so no student sees it until publish_lesson runs. That is why create_lesson runs without asking first.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `course_id` | string | yes | Course ID from list_courses. |
| `section_id` | string | yes | Section ID from get_curriculum. |
| `title` | string | yes | Lesson name. |
| `body_markdown` | string | no | Text for a Text & Image block. Images by URL. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/teachable/create_lesson \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"course_id":"course_8c41d2","section_id":"section_8c41d2","title":"title"}'
```

**Python**

```python
result = client.call("teachable", "create_lesson", {"course_id":"course_8c41d2","section_id":"section_8c41d2","title":"title"})
```

**Node.js**

```js
const result = await cotlet.call('teachable', 'create_lesson', {"course_id":"course_8c41d2","section_id":"section_8c41d2","title":"title"});
```

**CLI**

```bash
cotlet call teachable create_lesson --data '{"course_id":"course_8c41d2","section_id":"section_8c41d2","title":"title"}'
```

### Example response

```json
{
  "lesson_id": "35120990",
  "name": "Lesson 4: Loops",
  "section_id": "9120331",
  "published": false
}
```

## Update lesson

`POST https://cotlet.dev/api/v1/teachable/update_lesson`

Access: write. Confirm: waits for the user's OK before it runs.

Renames a lesson and replaces the text of one Text & Image block; video, PDF, audio and quiz blocks are not touched. Teachable saves edits to a published lesson live at once, so students see the change right away. By default you confirm each call; you can switch that off for this tool.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `course_id` | string | yes | Course ID from list_courses. |
| `lesson_id` | string | yes | Lesson ID from get_curriculum. |
| `title` | string | no | New lesson name. |
| `body_markdown` | string | no | New text for the Text & Image block. |
| `block_id` | string | no | Text & Image block ID from get_lesson. Default: the first one. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/teachable/update_lesson \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"course_id":"1204512","lesson_id":"35120874","title":"Lesson 3: Functions","body_markdown":"# Functions\n\nA function groups steps you want to reuse."}'
```

**Python**

```python
result = client.call("teachable", "update_lesson", {"course_id":"1204512","lesson_id":"35120874","title":"Lesson 3: Functions","body_markdown":"# Functions\n\nA function groups steps you want to reuse."})
```

**Node.js**

```js
const result = await cotlet.call('teachable', 'update_lesson', {"course_id":"1204512","lesson_id":"35120874","title":"Lesson 3: Functions","body_markdown":"# Functions\n\nA function groups steps you want to reuse."});
```

**CLI**

```bash
cotlet call teachable update_lesson --data '{"course_id":"1204512","lesson_id":"35120874","title":"Lesson 3: Functions","body_markdown":"# Functions\n\nA function groups steps you want to reuse."}'
```

### Example response

```json
{
  "lesson_id": "35120874",
  "name": "Lesson 3: Functions",
  "updated_block_id": "b_5521",
  "published": true,
  "updated_at": "2026-10-11T09:20:00+02:00"
}
```

## Publish lesson

`POST https://cotlet.dev/api/v1/teachable/publish_lesson`

Access: write. Confirm: waits for the user's OK before it runs.

Does what Publish and Unpublish do in the lesson editor. Unpublished lessons are hidden from students, and a section whose lessons are all unpublished is hidden too. The course itself must be published and enrollable on its Information tab. By default you confirm each call; you can switch that off for this tool.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `course_id` | string | yes | Course ID from list_courses. |
| `lesson_id` | string | yes | Lesson ID from get_curriculum. |
| `published` | boolean | no | false unpublishes. Default: true. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/teachable/publish_lesson \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"course_id":"course_8c41d2","lesson_id":"lesson_8c41d2"}'
```

**Python**

```python
result = client.call("teachable", "publish_lesson", {"course_id":"course_8c41d2","lesson_id":"lesson_8c41d2"})
```

**Node.js**

```js
const result = await cotlet.call('teachable', 'publish_lesson', {"course_id":"course_8c41d2","lesson_id":"lesson_8c41d2"});
```

**CLI**

```bash
cotlet call teachable publish_lesson --data '{"course_id":"course_8c41d2","lesson_id":"lesson_8c41d2"}'
```

### Example response

```json
{
  "lesson_id": "35120874",
  "published": true
}
```

## Create coupon

`POST https://cotlet.dev/api/v1/teachable/create_coupon`

Access: write. Confirm: waits for the user's OK before it runs.

Creates a coupon on the Coupons page. The code holds letters and numbers only. Quantity is the number available, up to 10,000. Teachable does not sell a paid product below $1.00 USD. The code takes money off your sales. By default you confirm each call; you can switch that off for this tool.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `name` | string | yes | Internal name, for your own list. |
| `code` | string | yes | Letters and numbers only. |
| `discount_type` | enum | yes | percent or amount. |
| `discount` | integer | yes | Percent off, or the fixed amount off. |
| `applies_to` | enum | no | all_products, courses, coaching, digital_downloads, memberships, bundles or pricing_plan. Default: all_products. |
| `pricing_plan_id` | string | no | Required when applies_to is pricing_plan. |
| `expires_at` | date | no | Last day the code works. |
| `quantity` | integer | no | Number available, 1 to 10000. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/teachable/create_coupon \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Black Friday 2026","code":"BLACKFRIDAY","discount_type":"percent","discount":30,"applies_to":"courses","expires_at":"2026-12-01","quantity":100}'
```

**Python**

```python
result = client.call("teachable", "create_coupon", {"name":"Black Friday 2026","code":"BLACKFRIDAY","discount_type":"percent","discount":30,"applies_to":"courses","expires_at":"2026-12-01","quantity":100})
```

**Node.js**

```js
const result = await cotlet.call('teachable', 'create_coupon', {"name":"Black Friday 2026","code":"BLACKFRIDAY","discount_type":"percent","discount":30,"applies_to":"courses","expires_at":"2026-12-01","quantity":100});
```

**CLI**

```bash
cotlet call teachable create_coupon --data '{"name":"Black Friday 2026","code":"BLACKFRIDAY","discount_type":"percent","discount":30,"applies_to":"courses","expires_at":"2026-12-01","quantity":100}'
```

### Example response

```json
{
  "coupon_id": "3918274",
  "name": "Black Friday 2026",
  "code": "BLACKFRIDAY",
  "discount": "30%",
  "applies_to": "courses",
  "expires_at": "2026-12-01",
  "quantity": 100,
  "used": 0,
  "status": "active"
}
```

## Archive coupon

`POST https://cotlet.dev/api/v1/teachable/archive_coupon`

Access: write. Confirm: waits for the user's OK before it runs.

Archives a coupon, which is how Teachable unpublishes one. Buyers who have the code can no longer use it. An archived code can be used for a new coupon later. By default you confirm each call; you can switch that off for this tool.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `coupon_id` | string | yes | Coupon ID from list_coupons. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/teachable/archive_coupon \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"coupon_id":"coupon_8c41d2"}'
```

**Python**

```python
result = client.call("teachable", "archive_coupon", {"coupon_id":"coupon_8c41d2"})
```

**Node.js**

```js
const result = await cotlet.call('teachable', 'archive_coupon', {"coupon_id":"coupon_8c41d2"});
```

**CLI**

```bash
cotlet call teachable archive_coupon --data '{"coupon_id":"coupon_8c41d2"}'
```

### Example response

```json
{
  "coupon_id": "3917702",
  "code": "SUMMER20",
  "status": "archived"
}
```

## Enroll student

`POST https://cotlet.dev/api/v1/teachable/enroll_student`

Access: write. Confirm: waits for the user's OK before it runs.

Does what manual enrollment does in the Users menu: the student gets access to the course with no payment. A new email is added to your school first, because adding a student does not enroll them. Teachable sends no confirmation email for manual enrollments, and plan limits such as access duration or enrollment caps do not apply. By default you confirm each call; you can switch that off for this tool.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `email` | string | yes | Student email. |
| `course_id` | string | yes | Course ID from list_courses. |
| `name` | string | no | Full name, used when the student is new. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/teachable/enroll_student \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email":"jane@example.com","course_id":"course_8c41d2"}'
```

**Python**

```python
result = client.call("teachable", "enroll_student", {"email":"jane@example.com","course_id":"course_8c41d2"})
```

**Node.js**

```js
const result = await cotlet.call('teachable', 'enroll_student', {"email":"jane@example.com","course_id":"course_8c41d2"});
```

**CLI**

```bash
cotlet call teachable enroll_student --data '{"email":"jane@example.com","course_id":"course_8c41d2"}'
```

### Example response

```json
{
  "user_id": "70418901",
  "email": "maria.kowal@example.com",
  "course_id": "1204512",
  "status": "enrolled",
  "user_created": true,
  "confirmation_email_sent": false
}
```

## Send email

`POST https://cotlet.dev/api/v1/teachable/send_email`

Access: write. Confirm: waits for the user's OK before it runs.

Sends a custom email from your school. Students who opted out of instructional and promotional emails in their profile do not receive it. To reach part of a course, take the emails from list_students and pass them in user_emails. By default you confirm each call; you can switch that off for this tool.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `to` | enum | yes | users, course or all_users. |
| `user_emails` | string[] | no | Recipients when to is users. |
| `course_id` | string | no | Recipients when to is course. |
| `subject` | string | yes | Email subject. |
| `body_markdown` | string | yes | Email body. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/teachable/send_email \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"to":"users","subject":"subject","body_markdown":"body markdown"}'
```

**Python**

```python
result = client.call("teachable", "send_email", {"to":"users","subject":"subject","body_markdown":"body markdown"})
```

**Node.js**

```js
const result = await cotlet.call('teachable', 'send_email', {"to":"users","subject":"subject","body_markdown":"body markdown"});
```

**CLI**

```bash
cotlet call teachable send_email --data '{"to":"users","subject":"subject","body_markdown":"body markdown"}'
```

### Example response

```json
{
  "email_id": "em_4471",
  "to": "course",
  "course_id": "1204512",
  "subject": "Live Q&A moved to Thursday",
  "status": "sent",
  "sent_at": "2026-10-11T09:30:00+02:00"
}
```

## Reply to comment

`POST https://cotlet.dev/api/v1/teachable/reply_to_comment`

Access: write. Confirm: waits for the user's OK before it runs.

Posts a reply under one comment, as the Reply button does in your comment admin. Comments are public and cannot be made private, so the whole course sees the reply. By default you confirm each call; you can switch that off for this tool.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `comment_id` | string | yes | Comment ID from list_comments. |
| `body` | string | yes | Reply text. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/teachable/reply_to_comment \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"comment_id":"comment_8c41d2","body":"body"}'
```

**Python**

```python
result = client.call("teachable", "reply_to_comment", {"comment_id":"comment_8c41d2","body":"body"})
```

**Node.js**

```js
const result = await cotlet.call('teachable', 'reply_to_comment', {"comment_id":"comment_8c41d2","body":"body"});
```

**CLI**

```bash
cotlet call teachable reply_to_comment --data '{"comment_id":"comment_8c41d2","body":"body"}'
```

### Example response

```json
{
  "reply_id": "88120502",
  "comment_id": "88120431",
  "status": "published"
}
```

## Moderate comment

`POST https://cotlet.dev/api/v1/teachable/moderate_comment`

Access: write. Confirm: waits for the user's OK before it runs.

Does what Approve and Deny do on a pending comment. An approved comment appears on the lesson. A denied comment stays in your comment admin with a Denied tag and is hidden from students. Pending Approval exists only when Comment moderation is on. By default you confirm each call; you can switch that off for this tool.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `comment_id` | string | yes | Comment ID from list_comments. |
| `decision` | enum | yes | approve or deny. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/teachable/moderate_comment \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"comment_id":"88120431","decision":"approve"}'
```

**Python**

```python
result = client.call("teachable", "moderate_comment", {"comment_id":"88120431","decision":"approve"})
```

**Node.js**

```js
const result = await cotlet.call('teachable', 'moderate_comment', {"comment_id":"88120431","decision":"approve"});
```

**CLI**

```bash
cotlet call teachable moderate_comment --data '{"comment_id":"88120431","decision":"approve"}'
```

### Example response

```json
{
  "comment_id": "88120431",
  "status": "approved"
}
```
