REST API
Every action in every app is an endpoint with its own method and path, and it returns JSON. The same operations your agent sees over MCP, for your scripts.
Introduction
All endpoints live under one base URL, https://cotlet.dev/api/v1: the app slug, then the endpoint's path. The method says what it does: GET reads, POST creates or acts, PUT and PATCH change, DELETE removes.
GET https://cotlet.dev/api/v1/skool/groups/{group}/members
POST https://cotlet.dev/api/v1/skool/groups/{group}/posts
PATCH https://cotlet.dev/api/v1/skool/groups/{group}/posts/{post_id}
DELETE https://cotlet.dev/api/v1/skool/groups/{group}/posts/{post_id} Pick an app in the sidebar to see its endpoints, their inputs and an example response for each one.
Authentication
Send your API key as a bearer token on every call. X-API-Key works too, for tools that cannot set an Authorization header.
Authorization: Bearer COTLET_API_KEY
Content-Type: application/json Making a call
Path parameters such as {group} go in the URL. The other inputs go in the query string for GET and DELETE, and in a JSON object body for POST, PUT and PATCH.
curl "https://cotlet.dev/api/v1/skool/groups/yourgroup/members?role=admin" \
-H "Authorization: Bearer $COTLET_API_KEY" - Inputs are validated against the endpoint JSON Schema before anything runs.
- Unknown fields are rejected, so a typo never runs silently.
- A known path with the wrong method returns
405with anAllowheader.
Responses
A successful call returns 200 and the endpoint output as JSON, exactly as shown in its example response.
{
"members": [
{
"member_id": "m_8c41d2",
"name": "Ana Lopez",
"role": "member",
"joined_at": "2026-10-01T18:21:00Z"
}
],
"next_cursor": "eyJvZmZzZXQiOjUwfQ"
} | Header | Meaning |
|---|---|
X-Request-Id | ID of the call. Quote it when you contact support. |
X-Credits-Used | Credits this call used. Failed calls use none. |
Link | Docs of the app, as rel="describedby". |
Pagination
List endpoints return up to limit items and a next_cursor. Pass it as cursor to get the next page; null means you have everything.
GET https://cotlet.dev/api/v1/skool/groups/yourgroup/members?cursor=eyJvZmZzZXQiOjUwfQ Confirmation
Endpoints marked Confirm reach other people: they publish, message or email. By default each call waits for your approval before it runs.
{
"call_id": "0f3c2a91-6b7e-4d1a-9c55-2e8f1b7d4a60",
"status": "pending",
"app": "skool",
"tool": "send_message",
"message": "Send message in Skool reaches other people, so it waits for the account owner's approval...",
"approval_url": "https://cotlet.dev/app/activity?tab=approvals",
"status_url": "https://cotlet.dev/api/v1/calls/0f3c2a91-6b7e-4d1a-9c55-2e8f1b7d4a60",
"expires_at": "2026-10-18T09:12:00+00:00"
} Approve it at the approval_url, then read the outcome with GET on the status_url. Without a decision the call lapses at expires_at and nothing is sent. To run on a schedule without you, switch the endpoint to "Run without approval" in Activity, Approval rules.
Errors
Errors return JSON with an error code, a message written for your agent, and a link when you need to act.
{
"error": "access_not_granted",
"message": "This API key has no access to Skool.",
"request_access_url": "https://cotlet.dev/signup?app=skool",
"docs_url": "https://cotlet.dev/integrations/skool-api-mcp-server"
} | Status | Code | What happened | What to do |
|---|---|---|---|
| 400 | invalid_input | The inputs do not match the endpoint schema. | Fix the fields named in details. |
| 402 | insufficient_credits | Your credits ran out. | Buy a credit pack in the dashboard; calls and your Cloud Browser resume after the purchase. |
| 401 | api_key_required | No API key on the call. | Send Authorization: Bearer. |
| 401 | invalid_api_key | The key is unknown, expired or revoked. | Create a new key in the dashboard. |
| 403 | access_not_granted | The key has no access to this app. | Request the app with the link in the error. |
| 404 | unknown_app, unknown_endpoint | No such app, or no endpoint at this path. | The error lists the valid routes. |
| 405 | method_not_allowed | The path is an endpoint, but not with this method. | Use a method from the Allow header. |
| 403 | AUTH_REQUIRED | The app is not signed in to in your Cloud Browser, or not enabled for agents. | Open the browser_url, sign in and press Enable for agents. |
| 403 | AUTH_EXPIRED | The app signed you out. | Open the browser_url and sign in again. |
| 403 | CAPTCHA_REQUIRED, MFA_REQUIRED | The app asked for a check only you can pass. | Finish it in your Cloud Browser at the browser_url. |
| 403 | SCOPE_DENIED | The action is outside what you allowed. | Switch the app to read and write at the browser_url, or create a new key with a wider scope in the dashboard. |
| 403 | APP_PERMISSION_DENIED | Your account in the app lacks the role for this, e.g. it is not a group admin. | Use a group or item your account manages. Nothing was charged. |
| 403 | BROWSER_SUSPENDED | We suspended your Cloud Browser. | Contact us at the support_url. |
| 429 | rate_limited | Too many requests. | Wait for Retry-After seconds. |
| 403 | TARGET_BLOCKED | The app refused the account. | Nothing was charged; tell the user. |
We never sign in for you, solve a CAPTCHA or skip two-factor checks. When the app asks for one, the error carries a browser_url to your Cloud Browser (also sent as connect_url, the older name).
Reporting a problem
Send bugs, errors and missing features straight to us with your key. Same fields as the MCP tool cotlet_report_issue (MCP docs).
POST https://cotlet.dev/api/v1/reports
Authorization: Bearer COTLET_API_KEY
{
"kind": "error",
"title": "approve_membership_request fails with TARGET_BLOCKED",
"description": "Three calls in a row since 14:00, the user can approve members by hand.",
"app": "skool",
"tool": "approve_membership_request",
"call_id": "0f3c2a91-6b7e-4d1a-9c55-2e8f1b7d4a60"
} kind is one of bug, error, feature_request, missing_feature or other. The answer is 201 with a report_id.
Drive your Cloud Browser
Your scripts can drive your Cloud Browser over Chrome DevTools. You have one browser per app, signed in to that app only. Ask for a session of an app with your key, then connect any CDP client to cdp_url. Same as the MCP tool cotlet_browser_connect (MCP docs).
POST https://cotlet.dev/api/v1/browser/cdp-sessions
Authorization: Bearer COTLET_API_KEY
Content-Type: application/json
{"app": "skool"} The answer is 201 with cdp_url, expires_at and credits_per_minute. Every started minute of an open connection is charged up front; 402 means no credits left.
Rate limits
| What | Limit |
|---|---|
| Calls to endpoints | 60 a minute per key |
| Listing apps, endpoints and schemas | 120 a minute per IP |
| MCP messages | 120 a minute per key |
| Problem reports | 20 an hour per account |
Over the limit you get 429 with a Retry-After header. Apps have limits of their own; we pace calls so your account stays within them.
Discovery
These endpoints need no key. Agents use them to find the right endpoint and its schema before the first call.
| Endpoint | Returns |
|---|---|
GET /api/v1/apps | Every app with its endpoint count and links. |
GET /api/v1/apps/{app} | One app: what it is, how it connects, its endpoints with their method and path. |
GET /api/v1/apps/{app}/endpoints/{endpoint} | One endpoint by name: full JSON Schema of the input and an example response. |
curl https://cotlet.dev/api/v1/apps