Skip to main content
RapidDev - Software Development Agency
flutterflow-integrationsFlutterFlow API Call

Teachable

Connect FlutterFlow to Teachable using the API Calls panel to build a branded mobile storefront on top of your Teachable school. Create an API Group against Teachable's REST API v1, add endpoints for courses, enrollments, and orders, then proxy the API key through a Firebase Cloud Function — the key exposes all school revenue and student PII, so it must never sit in Dart.

What you'll learn

  • How to generate a Teachable API key on a paid plan and store it securely
  • How to create a 'Teachable' API Group in FlutterFlow with resource-based endpoints
  • How to proxy the API key through a Firebase Cloud Function to keep it off the client
  • How to bind course catalog data to a GridView with pagination support
  • How to map a FlutterFlow auth user to a Teachable student for enrollment checks
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate13 min read40 minutesEducationLast updated July 2026RapidDev Engineering Team
TL;DR

Connect FlutterFlow to Teachable using the API Calls panel to build a branded mobile storefront on top of your Teachable school. Create an API Group against Teachable's REST API v1, add endpoints for courses, enrollments, and orders, then proxy the API key through a Firebase Cloud Function — the key exposes all school revenue and student PII, so it must never sit in Dart.

Quick facts about this guide
FactValue
ToolTeachable
CategoryEducation
MethodFlutterFlow API Call
DifficultyIntermediate
Time required40 minutes
Last updatedJuly 2026

Building a Branded Mobile Teachable App with FlutterFlow

Teachable's REST API is refreshingly conventional compared to other LMS platforms. Each resource lives at its own endpoint — `/v1/courses` returns the course catalog, `/v1/users` returns student records, `/v1/enrollments` shows who is enrolled in what, and `/v1/orders` surfaces purchase history. All calls use a single API key in the Authorization header. This predictable structure makes FlutterFlow's API Group panel the ideal tool: one group with a shared base URL and auth header, one call per resource, and JSON Paths to bind the responses to your widgets.

The business case is compelling: course creators want a custom-branded mobile experience that feels like their own app, not a generic Teachable subdomain. With FlutterFlow and the Teachable API you can build exactly that — a course catalog in a branded GridView, a student portal showing enrolled courses and completion progress, and a revenue summary for the creator's dashboard.

One critical rule governs the architecture: read in the app, write server-side. The Teachable API key has access to all revenue data and every student's personal information. Embedding it in a Flutter app compiled for iOS and Android would expose it to anyone who decompiles the binary. Additionally, enrollment and purchase transactions should never originate from client code — use a Firebase Cloud Function or Supabase Edge Function as the intermediary. Teachable's API access is also plan-gated: verify that your Teachable subscription tier includes API access before building. Note that Teachable's API uses pagination (`page` and `per` query parameters); skipping these silently truncates responses to just the first page.

Integration method

FlutterFlow API Call

Teachable exposes a conventional resource-based REST API at `https://developers.teachable.com/v1`. Each resource has its own endpoint: `/v1/courses`, `/v1/users`, `/v1/enrollments`, `/v1/orders`. Authentication is a single API key sent as an Authorization header. In FlutterFlow, you create one API Group for the Teachable base URL, then add one API Call per resource. Because the API key reveals all school revenue and student data, it must be proxied through a Firebase Cloud Function rather than placed directly in any API Call header.

Prerequisites

  • A Teachable school on a paid plan that includes API access (verify your current plan's API eligibility)
  • A Teachable API key generated from Settings → API Keys in your Teachable admin panel
  • A Firebase project with Cloud Functions enabled (or a Supabase project for Edge Functions)
  • A FlutterFlow project with Firebase Auth configured for student login
  • Basic familiarity with FlutterFlow's API Calls panel

Step-by-step guide

1

Generate a Teachable API key and secure it in a Cloud Function

Log in to your Teachable admin panel and go to Settings → API Keys. Click New API Key, give it a descriptive name like 'FlutterFlow App', and click Create Key. Copy the key immediately — Teachable only shows it once. Do not paste it anywhere in FlutterFlow. Instead, open the Firebase Console, go to Functions → Configuration (or use the Firebase CLI), and set the key as a secret: `firebase functions:config:set teachable.key='sk_YOUR_KEY_HERE'`. This stores the key in Firebase's encrypted config, inaccessible to your app's client code. Deploy a simple Cloud Function called `teachableProxy` that accepts a resource path and query parameters from FlutterFlow, injects the Authorization header with your API key server-side, proxies the request to `https://developers.teachable.com/v1/{path}`, and returns the JSON response. Require Firebase Auth on the Cloud Function so that only logged-in users can trigger the proxy — this adds a second layer of protection beyond the Teachable key itself. The API key exposes every student's email, name, and purchase history plus all your revenue figures; treat it like a password to your bank account.

index.js
1// Firebase Cloud Function: functions/index.js
2const functions = require('firebase-functions');
3const axios = require('axios');
4const cors = require('cors')({ origin: true });
5
6exports.teachableProxy = functions.https.onRequest((req, res) => {
7 cors(req, res, async () => {
8 const apiKey = functions.config().teachable.key;
9 const { path, ...queryParams } = req.query;
10
11 if (!path) {
12 return res.status(400).json({ error: 'Missing path parameter' });
13 }
14
15 try {
16 const response = await axios.get(
17 `https://developers.teachable.com/v1/${path}`,
18 {
19 headers: { apiKey: apiKey },
20 params: queryParams,
21 }
22 );
23 return res.json(response.data);
24 } catch (err) {
25 const status = err.response ? err.response.status : 500;
26 return res.status(status).json({ error: err.message });
27 }
28 });
29});
30

Pro tip: Teachable uses the header name `apiKey` (not `Authorization: Bearer`) for its REST API v1. Confirm the exact header name in Teachable's developer documentation before deploying.

Expected result: The `teachableProxy` Cloud Function is deployed and returns Teachable API data when called with a valid path query parameter.

2

Create the 'Teachable' API Group in FlutterFlow

In FlutterFlow, click API Calls in the left navigation panel. Click + Add → Create API Group. Name the group 'Teachable'. Set the Base URL to your Firebase Cloud Function's HTTPS endpoint, such as `https://us-central1-YOUR_PROJECT.cloudfunctions.net/teachableProxy`. This means your FlutterFlow API Calls will be sending requests to your Cloud Function, which then securely calls Teachable with the real API key. No auth header is needed at the API Group level if your Cloud Function is a public HTTP endpoint that you secure via its own logic. However, if you want an extra layer of verification, add a shared `x-flutter-auth` custom header with a shared secret — but this is optional. Click the Variables tab on the API Group and add a group-level variable `path` of type String to hold the Teachable resource path (e.g., `courses`, `users`, `enrollments`). This variable will be passed as a query parameter to your Cloud Function. Save the API Group. You now have a single, reusable gateway to all Teachable resources.

Pro tip: Setting the base URL to your Cloud Function URL means ALL Teachable API traffic flows through one secure server-side entry point. You can swap or rotate the Teachable API key in Firebase config without touching FlutterFlow at all.

Expected result: The 'Teachable' API Group is saved in the API Calls panel with the Cloud Function URL as the base URL.

3

Add API Calls for courses, enrollments, and orders

Inside the 'Teachable' API Group, add individual API Calls for each resource you need. Click + Add → Create API Call. For the course catalog: name the call 'GetCourses', set Method to GET. Add query parameters `path` = `courses`, `page` = `1`, and `per` = `20`. The `page` and `per` parameters are critical — Teachable's API paginates results and will silently return only the first page if you omit them, making it look like you have fewer courses than you do. In the Response & Test tab, click Run Test (once your Cloud Function is deployed and the path is set). Paste a sample Teachable courses JSON response and click Generate JSON Paths. You'll get paths like `$.courses[*].id`, `$.courses[*].name`, `$.courses[*].image_url`, `$.courses[*].description`, `$.courses[*].price`. These are the paths you'll bind to your GridView tile widgets. Repeat for enrollments: create 'GetEnrollments' with `path` = `enrollments` and add a `user_id` variable. Create 'GetOrders' with `path` = `orders` and `date_start`/`date_end` variables for the creator's revenue dashboard. Each call inherits the base URL from the group, so you only need to specify the unique parameters per resource.

typescript
1// GetCourses API Call config
2{
3 "name": "GetCourses",
4 "method": "GET",
5 "endpoint": "",
6 "query_params": {
7 "path": "courses",
8 "page": "{{ page }}",
9 "per": "20"
10 },
11 "variables": [
12 { "name": "page", "type": "integer", "default": "1" }
13 ]
14}
15
16// JSON Paths after generating from response:
17// Course ID: $.courses[*].id
18// Course name: $.courses[*].name
19// Course image URL: $.courses[*].image_url
20// Course description: $.courses[*].heading
21// Course price: $.courses[*].price
22// Total count: $.meta.total
23

Pro tip: Bind `$.meta.total` to a Text widget and use it to calculate whether to show a 'Load More' button. Compare total count against `page * per` to know if more pages exist.

Expected result: GetCourses, GetEnrollments, and GetOrders API Calls are configured inside the Teachable group with JSON Paths auto-generated from real Teachable responses.

4

Build the course catalog GridView with pagination

On your canvas, add a GridView widget for the course catalog. In the right panel, set its Backend Query to 'API Call → GetCourses' from the Teachable group. For the initial page, pass `1` as the `page` variable. Inside the GridView, add a Card widget with a NetworkImage (bound to `$.courses[*].image_url`), a Text for the course name (bound to `$.courses[*].name`), a Text for the description (bound to `$.courses[*].heading`), and a Text for the price (bound to `$.courses[*].price`). For pagination, add a 'Load More' button below the GridView. Set its Action to call GetCourses again with `page + 1`, then append the returned courses to an app state variable (a list of JSON objects) that the GridView reads from instead of directly from the backend query. This 'infinite scroll' pattern keeps the UI responsive and avoids loading all pages upfront. For the 'Enroll Now' action on each card, add a Launch URL action that deep-links to `https://YOUR_SCHOOL.teachable.com/p/COURSE_SLUG` — replace the slug with the course's `permalink` field from the API response. Enrollment and payment happen on Teachable's secure checkout, not inside your FlutterFlow app. This is intentional: never recreate a payment flow in a mobile app when the platform already provides one.

Pro tip: Store the full course list in a Page State or App State variable as a JSON list. When 'Load More' is tapped, append the new page's courses to the existing list rather than replacing it.

Expected result: A branded GridView displays Teachable courses with images and pricing. A 'Load More' button fetches additional pages. 'Enroll Now' opens the Teachable checkout in a browser.

5

Map FlutterFlow auth user to a Teachable student and gate content

For the student enrollment portal, you need to match the logged-in FlutterFlow user to their Teachable record. The cleanest approach: when a student logs in with Firebase Auth, your Cloud Function's proxy can look up the Teachable user by their email address using `GET /v1/users?email=STUDENT_EMAIL`. Store the returned Teachable `user_id` in Firestore under the Firebase UID for fast retrieval on subsequent sessions. With the Teachable user ID stored in Firestore, your FlutterFlow app can call GetEnrollments with that user ID to fetch their enrolled courses. Gate premium content behind an enrollment check: before showing a 'Continue Learning' button, verify that the course ID appears in the user's enrollments list. Use a conditional visibility rule — show the 'Continue Learning' button only when the course ID matches an enrollment, otherwise show the 'Enroll Now' (deep-link to checkout) button. For the creator's revenue dashboard (visible only to an admin user), add a role check: store an `isAdmin` boolean in Firestore for the creator's Firebase UID and check it on the dashboard page using a conditional widget. The GetOrders call populates the revenue chart; use FlutterFlow's Chart widget with the orders data bound to the chart series.

Pro tip: Never call GetOrders from a student-facing screen. Gate the revenue dashboard page with a Firestore role check (`isAdmin == true`) so only the creator can see financial data.

Expected result: Logged-in students see only their enrolled courses on the enrollment dashboard. The creator's revenue page is hidden from students via role-gating.

Common use cases

Branded mobile course catalog for a creator's school

A Flutter app with the creator's colors, logo, and course thumbnails pulled from Teachable's `/v1/courses` endpoint. Students browse available courses in a GridView, tap for details, and are redirected to Teachable's checkout via a deep link — the purchase happens on Teachable, not in the app.

FlutterFlow Prompt

Build a branded mobile app for my Teachable school that shows all published courses in a grid with thumbnails, a short description, and price. Tapping a course opens a detail screen and shows a 'Enroll Now' button that deep-links to the Teachable checkout page.

Copy this prompt to try it in FlutterFlow

Student enrollment dashboard with progress tracking

An authenticated student portal where logged-in users can see only the courses they are enrolled in via `/v1/enrollments`, view their completion percentage, and quickly jump back to where they left off on the Teachable web player via a deep link.

FlutterFlow Prompt

Create a student dashboard FlutterFlow app where users log in with their email, see their enrolled Teachable courses, and can see how many lessons they have completed in each course. A 'Continue Learning' button should open the course on teachable.com.

Copy this prompt to try it in FlutterFlow

Creator revenue and enrollment analytics app

A private dashboard for the course creator showing recent orders from `/v1/orders` and enrollment counts from `/v1/enrollments` in a Recharts-style bar chart, so the creator can monitor sales without logging into the Teachable admin panel.

FlutterFlow Prompt

Build a private FlutterFlow dashboard for me, the course creator, showing daily revenue from Teachable orders, new enrollments this week, and my top 3 selling courses — all pulled live from the Teachable API.

Copy this prompt to try it in FlutterFlow

Troubleshooting

401 or 403 response from the Teachable API even with a valid API key

Cause: API access is gated by Teachable's subscription plan. A free or Basic school may return 403 on all API calls. Alternatively, the API key was passed using the wrong header name.

Solution: Verify your Teachable plan includes API access. Check Teachable's developer documentation for the exact header name (historically `apiKey`, not `Authorization: Bearer`). Generate a new API key if the existing one was created before a plan upgrade.

Only the first 20 (or fewer) courses appear, even though the school has more

Cause: The API call is missing the `page` and `per` query parameters, so Teachable returns only the first default page.

Solution: Add `page=1` and `per=20` (or up to 50) as query parameters on the GetCourses API Call. Implement pagination by checking `$.meta.total` to determine if more pages exist, then incrementing the `page` variable when 'Load More' is tapped.

XMLHttpRequest error in FlutterFlow web preview but works on device

Cause: The Firebase Cloud Function is not returning CORS headers, so the browser blocks the response. Native iOS/Android builds bypass CORS enforcement.

Solution: Add the `cors` npm package to your Cloud Function and wrap the handler. In the index.js, use `const cors = require('cors')({ origin: true })` and wrap the request handler: `cors(req, res, () => { /* proxy logic */ })`.

typescript
1const cors = require('cors')({ origin: true });
2exports.teachableProxy = functions.https.onRequest((req, res) => {
3 cors(req, res, async () => { /* proxy logic */ });
4});

Teachable webhooks for enrollment.created events are not being received

Cause: FlutterFlow apps cannot receive inbound webhooks — they run on users' devices with no public HTTPS endpoint.

Solution: Configure a Firebase Cloud Function as the webhook target URL in Teachable's webhook settings. The function receives the event, updates Firestore with the new enrollment, and FlutterFlow reads from Firestore. This is the correct architecture for event-driven updates.

Best practices

  • Never place the Teachable API key in FlutterFlow's API Calls headers, App Values, or any Dart code — it exposes all school revenue and student PII.
  • Always use pagination parameters (`page` and `per`) on API calls to Teachable endpoints that return lists — the default truncation can make data look incomplete.
  • Use deep links to Teachable's hosted checkout for enrollment and payment — never rebuild a payment flow in FlutterFlow.
  • Verify your Teachable plan includes API access before building; test with a real API key against the live API early in the project.
  • Store the logged-in user's Teachable `user_id` in Firestore after the first lookup so subsequent enrollment checks skip the user lookup step.
  • Gate the revenue and orders views behind a Firestore role check so only the course creator can see financial data.
  • Use conditional visibility to switch between 'Enroll Now' (unenrolled) and 'Continue Learning' (enrolled) CTAs based on the enrollments response.
  • Set up Teachable webhooks pointing to a Cloud Function to keep enrollment data fresh in Firestore rather than polling the API on every app launch.

Alternatives

Frequently asked questions

Does Teachable's API work on all plan tiers?

No. Teachable's REST API access is limited to specific paid plans. Free and lower-tier schools may receive a 403 error on all API calls. Check your current Teachable plan's feature list for API access, or contact Teachable support to confirm. Plan and pricing details change periodically — verify on teachable.com.

Can FlutterFlow receive Teachable webhook events like enrollment.created?

No. FlutterFlow apps run on users' devices and have no public HTTPS endpoint to receive inbound webhooks. You need a Firebase Cloud Function or Supabase Edge Function as the webhook target. Configure the webhook URL in Teachable to point to your Cloud Function, which then updates Firestore, which FlutterFlow reads via its native Firestore integration.

Can I process Teachable enrollments and payments directly inside the FlutterFlow app?

You should not. Enrollment and payment writes should always go through a server-side function, not client code. For payment, Teachable's hosted checkout is the secure path — deep-link to it from the app. For programmatic enrollment (if your plan supports it), use a Cloud Function that calls the Teachable enrollment API with the API key stored server-side.

How do I show a student's course completion percentage from Teachable?

The `/v1/enrollments` endpoint includes completion data per enrollment record. After fetching a student's enrollments via GetEnrollments (with their Teachable user ID), parse the `percent_complete` field from the enrollment JSON. Bind this to FlutterFlow's CircularProgressIndicator or LinearProgressBar widget using the enrollment JSON Path.

What happens if a student changes their email — will the Firestore user mapping break?

Yes, if a student's Firebase Auth email changes and their Teachable account uses a different email, the stored Teachable user_id may become stale. Implement a re-sync mechanism: on each login, verify the stored Teachable user_id still matches by calling a lookup in your Cloud Function, and update the Firestore record if needed.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Integrations are where projects stall

We wire FlutterFlow integrations like this one every week — auth, webhooks, edge cases included. Fixed-price builds from $13K, delivered in 6–10 weeks.

Talk to an integration engineer

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.