# Teachable MCP tools

> Every Teachable MCP tool on the Cotlet server: tool names, input schemas, a tools/call example and an example result for each tool.

Source: https://cotlet.dev/docs/mcp/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 tools for Teachable on the Cotlet MCP server. Your agent picks them from tools/list.

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

## Endpoints

| Endpoint | Access | Description |
|---|---|---|
| `teachable_list_courses` | read | Courses in your school with name, published state and URL. |
| `teachable_get_curriculum` | read | Sections and lessons of a course, in order, with published and public preview flags. |
| `teachable_get_lesson` | read | Lesson content as blocks: text as Markdown, plus the type of each video, PDF, audio, quiz and other block. |
| `teachable_list_students` | read | Students with name, email and join date, or the students of one course. |
| `teachable_list_comments` | read | Lesson comments across your school or one course, with author, lesson, status and unread flag. |
| `teachable_list_coupons` | read | School and product coupons with code, discount, uses, expiry and status. |
| `teachable_create_lesson` | write | New unpublished lesson at the end of a section, with text from Markdown. |
| `teachable_update_lesson` | write | Rename a lesson or replace the text of its Text & Image block. Other blocks stay as they are. |
| `teachable_publish_lesson` | write | Publish a lesson so enrolled students see it, or unpublish it. |
| `teachable_create_coupon` | write | Coupon code for all products, one product type or one pricing plan. |
| `teachable_archive_coupon` | write | Unpublish a coupon so the code stops working. Archived codes can be used again. |
| `teachable_enroll_student` | write | Enroll a student in a course for free, adding them to your school first if the email is new. |
| `teachable_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. |
| `teachable_reply_to_comment` | write | Reply to a lesson comment as the school owner. The reply is public to the course. |
| `teachable_moderate_comment` | write | Approve or deny a comment held in Pending Approval. |

## List courses

MCP tool: `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. |

### tools/call

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "teachable_list_courses",
    "arguments": {}
  }
}
```

### Example response

```json
{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "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"
      }
    ]
  },
  "isError": false
}
```

## Get curriculum

MCP tool: `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. |

### tools/call

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "teachable_get_curriculum",
    "arguments": {
      "course_id": "course_8c41d2"
    }
  }
}
```

### Example response

```json
{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "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
          }
        ]
      }
    ]
  },
  "isError": false
}
```

## Get lesson

MCP tool: `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. |

### tools/call

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "teachable_get_lesson",
    "arguments": {
      "course_id": "course_8c41d2",
      "lesson_id": "lesson_8c41d2"
    }
  }
}
```

### Example response

```json
{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "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"
      }
    ]
  },
  "isError": false
}
```

## List students

MCP tool: `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. |

### tools/call

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "teachable_list_students",
    "arguments": {}
  }
}
```

### Example response

```json
{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "students": [
      {
        "user_id": "70418822",
        "name": "Maria Kowal",
        "email": "maria.kowal@example.com",
        "joined_at": "2026-09-28T10:14:00Z"
      }
    ],
    "next_cursor": null
  },
  "isError": false
}
```

## List comments

MCP tool: `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. |

### tools/call

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "teachable_list_comments",
    "arguments": {}
  }
}
```

### Example response

```json
{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "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
  },
  "isError": false
}
```

## List coupons

MCP tool: `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. |

### tools/call

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "teachable_list_coupons",
    "arguments": {}
  }
}
```

### Example response

```json
{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "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"
      }
    ]
  },
  "isError": false
}
```

## Create lesson

MCP tool: `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. |

### tools/call

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "teachable_create_lesson",
    "arguments": {
      "course_id": "course_8c41d2",
      "section_id": "section_8c41d2",
      "title": "title"
    }
  }
}
```

### Example response

```json
{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "lesson_id": "35120990",
    "name": "Lesson 4: Loops",
    "section_id": "9120331",
    "published": false
  },
  "isError": false
}
```

## Update lesson

MCP tool: `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. |

### tools/call

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "teachable_update_lesson",
    "arguments": {
      "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
{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "lesson_id": "35120874",
    "name": "Lesson 3: Functions",
    "updated_block_id": "b_5521",
    "published": true,
    "updated_at": "2026-10-11T09:20:00+02:00"
  },
  "isError": false
}
```

## Publish lesson

MCP tool: `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. |

### tools/call

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "teachable_publish_lesson",
    "arguments": {
      "course_id": "course_8c41d2",
      "lesson_id": "lesson_8c41d2"
    }
  }
}
```

### Example response

```json
{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "lesson_id": "35120874",
    "published": true
  },
  "isError": false
}
```

## Create coupon

MCP tool: `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. |

### tools/call

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "teachable_create_coupon",
    "arguments": {
      "name": "Black Friday 2026",
      "code": "BLACKFRIDAY",
      "discount_type": "percent",
      "discount": 30,
      "applies_to": "courses",
      "expires_at": "2026-12-01",
      "quantity": 100
    }
  }
}
```

### Example response

```json
{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "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"
  },
  "isError": false
}
```

## Archive coupon

MCP tool: `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. |

### tools/call

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "teachable_archive_coupon",
    "arguments": {
      "coupon_id": "coupon_8c41d2"
    }
  }
}
```

### Example response

```json
{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "coupon_id": "3917702",
    "code": "SUMMER20",
    "status": "archived"
  },
  "isError": false
}
```

## Enroll student

MCP tool: `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. |

### tools/call

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "teachable_enroll_student",
    "arguments": {
      "email": "jane@example.com",
      "course_id": "course_8c41d2"
    }
  }
}
```

### Example response

```json
{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "user_id": "70418901",
    "email": "maria.kowal@example.com",
    "course_id": "1204512",
    "status": "enrolled",
    "user_created": true,
    "confirmation_email_sent": false
  },
  "isError": false
}
```

## Send email

MCP tool: `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. |

### tools/call

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "teachable_send_email",
    "arguments": {
      "to": "users",
      "subject": "subject",
      "body_markdown": "body markdown"
    }
  }
}
```

### Example response

```json
{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "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"
  },
  "isError": false
}
```

## Reply to comment

MCP tool: `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. |

### tools/call

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "teachable_reply_to_comment",
    "arguments": {
      "comment_id": "comment_8c41d2",
      "body": "body"
    }
  }
}
```

### Example response

```json
{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "reply_id": "88120502",
    "comment_id": "88120431",
    "status": "published"
  },
  "isError": false
}
```

## Moderate comment

MCP tool: `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. |

### tools/call

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "teachable_moderate_comment",
    "arguments": {
      "comment_id": "88120431",
      "decision": "approve"
    }
  }
}
```

### Example response

```json
{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "comment_id": "88120431",
    "status": "approved"
  },
  "isError": false
}
```
