{
  "openapi": "3.1.0",
  "info": {
    "title": "CloserGym Public API",
    "version": "1.0.0",
    "summary": "Read only facts about CloserGym: plans, prices, product tools and published articles.",
    "description": "CloserGym is an AI sales roleplay and coaching platform for high ticket closers and appointment setters.\n\nThis API returns what the CloserGym website says about itself, as JSON: the plans and what they cost, the team plans, the price of extra credits, what the product and its tools do, and the published articles.\n\nIt is read only and needs no key and no sign in. Answers may be cached for 5 minutes. Each address may send 60 requests a minute.\n\nNot offered: There is no free plan. There is no sandbox or test environment for developers. There is no API key to sign up for. The public API needs none. There is no API for member accounts, scorecards or call recordings. The app uses a private one that only works for a signed in member in the CloserGym app. A roleplay cannot be started through an API. Practice happens in the app, by voice, signed in.\n\nThe same data is available as tools from the MCP server at https://closergym.com/mcp.",
    "termsOfService": "https://closergym.com/terms",
    "contact": {
      "name": "CloserGym Support",
      "url": "https://closergym.com/contact",
      "email": "support@closergym.com"
    }
  },
  "servers": [
    {
      "url": "https://closergym.com",
      "description": "CloserGym"
    }
  ],
  "externalDocs": {
    "description": "CloserGym developer page",
    "url": "https://closergym.com/developers"
  },
  "security": [],
  "tags": [
    {
      "name": "About this API",
      "description": "What the API offers and where its description lives."
    },
    {
      "name": "Plans and prices",
      "description": "What CloserGym costs. Every number comes from the same place the pricing page reads."
    },
    {
      "name": "Product",
      "description": "What CloserGym is and what its tools do."
    },
    {
      "name": "Articles",
      "description": "The Sales Playbook, the blog and the comparisons, as Markdown."
    }
  ],
  "paths": {
    "/api/public": {
      "get": {
        "operationId": "getApiIndex",
        "summary": "List what the public API offers",
        "description": "Returns the address of every endpoint in this API, the OpenAPI document, the MCP server and the developer page. Start here when you do not know the API yet.",
        "tags": [
          "About this API"
        ],
        "security": [],
        "parameters": [],
        "responses": {
          "200": {
            "description": "List what the public API offers.",
            "headers": {
              "Cache-Control": {
                "description": "How long the answer may be kept.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiIndex"
                },
                "examples": {
                  "example": {
                    "summary": "GET /api/public",
                    "value": {
                      "name": "CloserGym Public API",
                      "version": "1.0.0",
                      "description": "Read only facts about CloserGym: plans, prices, product tools and published articles.",
                      "authentication": "None. No key, no sign in.",
                      "endpoints": [
                        {
                          "operation_id": "getApiIndex",
                          "method": "GET",
                          "url": "https://closergym.com/api/public",
                          "summary": "List what the public API offers"
                        },
                        {
                          "operation_id": "getPlans",
                          "method": "GET",
                          "url": "https://closergym.com/api/public/plans",
                          "summary": "Get the individual plans and prices"
                        },
                        {
                          "operation_id": "getTeamPlans",
                          "method": "GET",
                          "url": "https://closergym.com/api/public/team-plans",
                          "summary": "Get the team plans and prices"
                        },
                        {
                          "operation_id": "getCreditPrices",
                          "method": "GET",
                          "url": "https://closergym.com/api/public/credits",
                          "summary": "Get the price of extra credits"
                        },
                        {
                          "operation_id": "getProductFacts",
                          "method": "GET",
                          "url": "https://closergym.com/api/public/product",
                          "summary": "Get what CloserGym is and what its tools do"
                        },
                        {
                          "operation_id": "listArticles",
                          "method": "GET",
                          "url": "https://closergym.com/api/public/articles",
                          "summary": "List the published articles"
                        },
                        {
                          "operation_id": "getArticle",
                          "method": "GET",
                          "url": "https://closergym.com/api/public/articles/{section}/{slug}",
                          "summary": "Get one article in full"
                        }
                      ],
                      "openapi_url": "https://closergym.com/openapi.json",
                      "mcp_url": "https://closergym.com/mcp",
                      "docs_url": "https://closergym.com/developers",
                      "llms_txt_url": "https://closergym.com/llms.txt",
                      "not_offered": [
                        "There is no free plan.",
                        "There is no sandbox or test environment for developers.",
                        "There is no API key to sign up for. The public API needs none.",
                        "There is no API for member accounts, scorecards or call recordings. The app uses a private one that only works for a signed in member in the CloserGym app.",
                        "A roleplay cannot be started through an API. Practice happens in the app, by voice, signed in."
                      ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait and try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "example": {
                    "summary": "Too many requests",
                    "value": {
                      "error": "Too many requests from this address.",
                      "code": "RATE_LIMITED",
                      "message": "Too many requests from this address.",
                      "hint": "Wait, then try again. The RateLimit headers say how long."
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/public/plans": {
      "get": {
        "operationId": "getPlans",
        "summary": "Get the individual plans and prices",
        "description": "Returns the plans one person can buy (Standard, Pro, Elite): the weekly, monthly and annual price of each in US dollars, how many roleplays and call analyses each includes, and the free trial on Pro. Use it to answer what CloserGym costs or which plan fits. There is no free plan.",
        "tags": [
          "Plans and prices"
        ],
        "security": [],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Get the individual plans and prices.",
            "headers": {
              "Cache-Control": {
                "description": "How long the answer may be kept.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Plans"
                },
                "examples": {
                  "example": {
                    "summary": "GET /api/public/plans",
                    "value": {
                      "currency": "USD",
                      "prices_are": "Website list prices in whole US dollars.",
                      "free_plan": false,
                      "free_trial": {
                        "plan": "pro",
                        "days": 7,
                        "roleplays": 5,
                        "call_analyses": 2,
                        "billing_periods": [
                          "weekly",
                          "monthly"
                        ]
                      },
                      "plans": [
                        {
                          "id": "standard",
                          "name": "Standard",
                          "description": "Perfect for consistent practice.",
                          "prices": {
                            "weekly": 17,
                            "monthly": 47,
                            "annual": 497
                          },
                          "includes": {
                            "per_month": {
                              "roleplays": 10,
                              "call_analyses": 5
                            },
                            "per_week_on_weekly_billing": {
                              "roleplays": 3,
                              "call_analyses": 2
                            }
                          },
                          "free_trial": null,
                          "url": "https://closergym.com/pricing"
                        },
                        {
                          "id": "pro",
                          "name": "Pro",
                          "description": "For serious closers wanting detailed feedback.",
                          "prices": {
                            "weekly": 27,
                            "monthly": 97,
                            "annual": 997
                          },
                          "includes": {
                            "per_month": {
                              "roleplays": 25,
                              "call_analyses": 10
                            },
                            "per_week_on_weekly_billing": {
                              "roleplays": 6,
                              "call_analyses": 3
                            }
                          },
                          "free_trial": {
                            "plan": "pro",
                            "days": 7,
                            "roleplays": 5,
                            "call_analyses": 2,
                            "billing_periods": [
                              "weekly",
                              "monthly"
                            ]
                          },
                          "url": "https://closergym.com/pricing"
                        },
                        {
                          "id": "elite",
                          "name": "Elite",
                          "description": "The ultimate psychological sales training.",
                          "prices": {
                            "weekly": 47,
                            "monthly": 147,
                            "annual": 1597
                          },
                          "includes": {
                            "per_month": {
                              "roleplays": 40,
                              "call_analyses": 20
                            },
                            "per_week_on_weekly_billing": {
                              "roleplays": 10,
                              "call_analyses": 5
                            }
                          },
                          "free_trial": null,
                          "url": "https://closergym.com/pricing"
                        }
                      ],
                      "notes": [
                        "There is no free plan. Pro has a 7 day free trial on weekly and monthly billing with 5 roleplays and 2 call analyses.",
                        "Monthly and annual billing include the monthly counts each month. Weekly billing includes the weekly counts each week.",
                        "Objection Sparring and Script Trainer sessions use the same roleplay count.",
                        "Plans are bought on the website, signed in. Nothing can be bought through this API."
                      ],
                      "url": "https://closergym.com/pricing"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait and try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "example": {
                    "summary": "Too many requests",
                    "value": {
                      "error": "Too many requests from this address.",
                      "code": "RATE_LIMITED",
                      "message": "Too many requests from this address.",
                      "hint": "Wait, then try again. The RateLimit headers say how long."
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/public/team-plans": {
      "get": {
        "operationId": "getTeamPlans",
        "summary": "Get the team plans and prices",
        "description": "Returns the plans a sales team can buy (Growth Team, Scale Team): the monthly and annual price for each seat in US dollars, what each rep gets every month, the smallest and largest team size, and where larger teams ask for Enterprise pricing.",
        "tags": [
          "Plans and prices"
        ],
        "security": [],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Get the team plans and prices.",
            "headers": {
              "Cache-Control": {
                "description": "How long the answer may be kept.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TeamPlans"
                },
                "examples": {
                  "example": {
                    "summary": "GET /api/public/team-plans",
                    "value": {
                      "currency": "USD",
                      "prices_are": "Website list prices in whole US dollars, for each seat.",
                      "min_seats": 5,
                      "max_seats": 49,
                      "enterprise": {
                        "from_seats": 50,
                        "pricing": "Quoted by the CloserGym team.",
                        "contact_url": "https://closergym.com/enterprise-contact"
                      },
                      "guarantee": "Run your team through 20 scored calls each in the first 30 days. If your team's average call score hasn't improved, we'll refund your first month.",
                      "plans": [
                        {
                          "id": "pro_team",
                          "name": "Growth Team",
                          "description": "For growing sales teams.",
                          "price_per_seat": {
                            "monthly": 147,
                            "annual": 1497
                          },
                          "includes_per_rep_per_month": {
                            "roleplays": 25,
                            "call_analyses": 10
                          },
                          "manager_dashboard": true,
                          "url": "https://closergym.com/pricing"
                        },
                        {
                          "id": "elite_team",
                          "name": "Scale Team",
                          "description": "For high-ticket scale closers.",
                          "price_per_seat": {
                            "monthly": 197,
                            "annual": 1997
                          },
                          "includes_per_rep_per_month": {
                            "roleplays": 40,
                            "call_analyses": 20
                          },
                          "manager_dashboard": true,
                          "url": "https://closergym.com/pricing"
                        }
                      ],
                      "url": "https://closergym.com/pricing"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait and try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "example": {
                    "summary": "Too many requests",
                    "value": {
                      "error": "Too many requests from this address.",
                      "code": "RATE_LIMITED",
                      "message": "Too many requests from this address.",
                      "hint": "Wait, then try again. The RateLimit headers say how long."
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/public/credits": {
      "get": {
        "operationId": "getCreditPrices",
        "summary": "Get the price of extra credits",
        "description": "Returns what one extra roleplay credit and one extra call analysis credit cost in US dollars. Credits are for members whose plan runs out before it renews.",
        "tags": [
          "Plans and prices"
        ],
        "security": [],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Get the price of extra credits.",
            "headers": {
              "Cache-Control": {
                "description": "How long the answer may be kept.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Credits"
                },
                "examples": {
                  "example": {
                    "summary": "GET /api/public/credits",
                    "value": {
                      "currency": "USD",
                      "credits": [
                        {
                          "type": "roleplay",
                          "price": 4.97,
                          "buys": "One extra roleplay session."
                        },
                        {
                          "type": "call_analysis",
                          "price": 2,
                          "buys": "One extra call analysis."
                        }
                      ],
                      "notes": [
                        "Credits are extra sessions a member buys, signed in, when a plan runs out before it renews.",
                        "The price is the same on every plan."
                      ],
                      "url": "https://closergym.com/pricing"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait and try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "example": {
                    "summary": "Too many requests",
                    "value": {
                      "error": "Too many requests from this address.",
                      "code": "RATE_LIMITED",
                      "message": "Too many requests from this address.",
                      "hint": "Wait, then try again. The RateLimit headers say how long."
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/public/product": {
      "get": {
        "operationId": "getProductFacts",
        "summary": "Get what CloserGym is and what its tools do",
        "description": "Returns a one sentence summary of CloserGym, its four tools (Live Roleplay, Objection Sparring, AI Script Trainer, Call Analysis), who it is for, how it compares, how to reach support, and the plain facts the site publishes about itself as questions and answers. Use it to describe CloserGym accurately or to decide whether it fits a task.",
        "tags": [
          "Product"
        ],
        "security": [],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Get what CloserGym is and what its tools do.",
            "headers": {
              "Cache-Control": {
                "description": "How long the answer may be kept.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Product"
                },
                "examples": {
                  "example": {
                    "summary": "Shortened here to the first two facts. The live answer holds every fact.",
                    "value": {
                      "name": "CloserGym",
                      "url": "https://closergym.com",
                      "summary": "CloserGym is an AI sales roleplay and coaching platform where high-ticket closers and appointment setters practice live voice sales calls against AI prospects and get a scored breakdown of every call.",
                      "founder": {
                        "name": "Lane Wright",
                        "title": "Founder",
                        "url": "https://closergym.com/about"
                      },
                      "support_email": "support@closergym.com",
                      "contact_url": "https://closergym.com/contact",
                      "community_url": "https://discord.gg/RVeWuKrRNt",
                      "ai_buyers": [
                        "Sarah",
                        "Mike"
                      ],
                      "tools": [
                        {
                          "id": "live-roleplay",
                          "name": "Live Roleplay",
                          "summary": "Live Roleplay is a real-time voice sales call against an AI prospect named Sarah or Mike. Closers run full discovery and closing calls of up to 45 minutes. Setters practice outbound cold calls, inbound booking calls, and triage calls of up to 15 minutes. The prospect interrupts, stalls, and objects the way a real buyer does.",
                          "url": "https://closergym.com/product/live-roleplay"
                        },
                        {
                          "id": "objection-sparring",
                          "name": "Objection Sparring",
                          "summary": "Objection Sparring is a rapid drill where the AI fires common objections, such as price, timing, and needing to talk to a spouse, so a rep can practice responses on repeat. It is included from the Standard plan, with the hardest difficulty on Pro and above.",
                          "url": "https://closergym.com/product/objection-sparring"
                        },
                        {
                          "id": "ai-script-trainer",
                          "name": "AI Script Trainer",
                          "summary": "The AI Script Trainer lets a rep or team load their own script, offer, and objection framework, then practice against an AI prospect built around that exact offer. It is included on Pro, Elite, and team plans.",
                          "url": "https://closergym.com/product/ai-script-trainer"
                        },
                        {
                          "id": "call-analysis",
                          "name": "Call Analysis",
                          "summary": "Call Analysis grades a recording of a real sales call. It scores the call, identifies where it was won or lost, and gives specific coaching. It is included from the Standard plan with a monthly allowance.",
                          "url": "https://closergym.com/product/call-analysis"
                        }
                      ],
                      "facts": [
                        {
                          "id": "what-it-is",
                          "heading": "What CloserGym is",
                          "items": [
                            {
                              "question": "What is CloserGym?",
                              "answer": "CloserGym is an AI sales roleplay and coaching platform where high-ticket closers and appointment setters practice live voice sales calls against AI prospects and get a scored breakdown of every call."
                            },
                            {
                              "question": "Who is CloserGym for?",
                              "answer": "CloserGym is for high-ticket closers, appointment setters, and the sales managers who train them, typically selling offers priced from about $3,000 to $100,000 or more. It works for an individual rep training alone and for a sales team."
                            }
                          ]
                        }
                      ],
                      "not_offered": [
                        "There is no free plan.",
                        "There is no sandbox or test environment for developers.",
                        "There is no API key to sign up for. The public API needs none.",
                        "There is no API for member accounts, scorecards or call recordings. The app uses a private one that only works for a signed in member in the CloserGym app.",
                        "A roleplay cannot be started through an API. Practice happens in the app, by voice, signed in."
                      ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait and try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "example": {
                    "summary": "Too many requests",
                    "value": {
                      "error": "Too many requests from this address.",
                      "code": "RATE_LIMITED",
                      "message": "Too many requests from this address.",
                      "hint": "Wait, then try again. The RateLimit headers say how long."
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/public/articles": {
      "get": {
        "operationId": "listArticles",
        "summary": "List the published articles",
        "description": "Returns the title, description, author, date and address of every article CloserGym has published: the Sales Playbook, the blog and the comparisons with other tools. Pass a section to get one part only. Use getArticle to read one in full.",
        "tags": [
          "Articles"
        ],
        "security": [],
        "parameters": [
          {
            "name": "section",
            "in": "query",
            "required": false,
            "description": "Which part of the site: playbook, blog, comparisons. Leave it out for all of them.",
            "schema": {
              "type": "string",
              "enum": [
                "playbook",
                "blog",
                "comparisons"
              ]
            },
            "example": "comparisons"
          }
        ],
        "responses": {
          "200": {
            "description": "List the published articles.",
            "headers": {
              "Cache-Control": {
                "description": "How long the answer may be kept.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ArticleList"
                },
                "examples": {
                  "example": {
                    "summary": "GET /api/public/articles?section=comparisons",
                    "value": {
                      "count": 6,
                      "sections": [
                        {
                          "id": "comparisons",
                          "name": "Comparisons",
                          "url": "https://closergym.com/playbook/comparisons",
                          "count": 6
                        }
                      ],
                      "articles": [
                        {
                          "section": "comparisons",
                          "slug": "kendo-ai",
                          "title": "CloserGym vs Kendo AI",
                          "description": "CloserGym vs Kendo AI compared on pricing, features, and who each is built for. See which AI sales training platform is right for high-ticket closers.",
                          "author": "Lane Wright",
                          "date": "2026-06-19",
                          "url": "https://closergym.com/playbook/comparisons/kendo-ai",
                          "api_url": "https://closergym.com/api/public/articles/comparisons/kendo-ai"
                        },
                        {
                          "section": "comparisons",
                          "slug": "closer-coach-ai",
                          "title": "Closer Coach AI vs CloserGym: Which Is Better?",
                          "description": "Closer Coach AI vs. CloserGym: Side-by-side comparison. Discover key feature differences, pricing, and which AI tool is best for high-ticket closing.",
                          "author": "Lane Wright",
                          "date": "2026-06-19",
                          "url": "https://closergym.com/playbook/comparisons/closer-coach-ai",
                          "api_url": "https://closergym.com/api/public/articles/comparisons/closer-coach-ai"
                        },
                        {
                          "section": "comparisons",
                          "slug": "gong",
                          "title": "CloserGym vs Gong",
                          "description": "CloserGym vs Gong: why these tools serve completely different purposes. Gong analyzes calls after. CloserGym trains you before. See the full comparison.",
                          "author": "Lane Wright",
                          "date": "2026-06-19",
                          "url": "https://closergym.com/playbook/comparisons/gong",
                          "api_url": "https://closergym.com/api/public/articles/comparisons/gong"
                        },
                        {
                          "section": "comparisons",
                          "slug": "hyperbound",
                          "title": "CloserGym vs Hyperbound",
                          "description": "CloserGym vs Hyperbound compared on pricing, setup time, and target user. See which AI sales roleplay platform is better for individual high-ticket closers.",
                          "author": "Lane Wright",
                          "date": "2026-06-19",
                          "url": "https://closergym.com/playbook/comparisons/hyperbound",
                          "api_url": "https://closergym.com/api/public/articles/comparisons/hyperbound"
                        },
                        {
                          "section": "comparisons",
                          "slug": "pitch-monster",
                          "title": "CloserGym vs Pitch Monster",
                          "description": "CloserGym vs Pitch Monster: pricing, features, and who each is built for. Pitch Monster starts at $4,800/yr. CloserGym starts at $47/mo. Full breakdown.",
                          "author": "Lane Wright",
                          "date": "2026-06-19",
                          "url": "https://closergym.com/playbook/comparisons/pitch-monster",
                          "api_url": "https://closergym.com/api/public/articles/comparisons/pitch-monster"
                        },
                        {
                          "section": "comparisons",
                          "slug": "second-nature-ai",
                          "title": "CloserGym vs Second Nature AI",
                          "description": "CloserGym vs Second Nature AI: enterprise vs individual, custom quote vs transparent pricing. See which AI sales training platform fits your needs.",
                          "author": "Lane Wright",
                          "date": "2026-06-19",
                          "url": "https://closergym.com/playbook/comparisons/second-nature-ai",
                          "api_url": "https://closergym.com/api/public/articles/comparisons/second-nature-ai"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "A parameter is missing or not allowed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "example": {
                    "summary": "A parameter is not one of the allowed values",
                    "value": {
                      "error": "section must be one of: playbook, blog, comparisons.",
                      "code": "INVALID_SECTION",
                      "message": "section must be one of: playbook, blog, comparisons.",
                      "hint": "Use one of: playbook, blog, comparisons."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait and try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "example": {
                    "summary": "Too many requests",
                    "value": {
                      "error": "Too many requests from this address.",
                      "code": "RATE_LIMITED",
                      "message": "Too many requests from this address.",
                      "hint": "Wait, then try again. The RateLimit headers say how long."
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/public/articles/{section}/{slug}": {
      "get": {
        "operationId": "getArticle",
        "summary": "Get one article in full",
        "description": "Returns one published article with its full text as Markdown. Take the section and the slug from listArticles.",
        "tags": [
          "Articles"
        ],
        "security": [],
        "parameters": [
          {
            "name": "section",
            "in": "path",
            "required": true,
            "description": "Which part of the site: playbook, blog, comparisons.",
            "schema": {
              "type": "string",
              "enum": [
                "playbook",
                "blog",
                "comparisons"
              ]
            },
            "example": "playbook"
          },
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "The last part of the article address, as listArticles returns it in slug.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]*$",
              "maxLength": 120
            },
            "example": "what-is-closer-gym"
          }
        ],
        "responses": {
          "200": {
            "description": "Get one article in full.",
            "headers": {
              "Cache-Control": {
                "description": "How long the answer may be kept.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Article"
                },
                "examples": {
                  "example": {
                    "summary": "The article text is shortened here. The live answer holds all of it.",
                    "value": {
                      "section": "playbook",
                      "slug": "what-is-closer-gym",
                      "title": "What is CloserGym? AI Sales Training",
                      "description": "CloserGym is an AI sales training platform for high-ticket closers. Practice realistic roleplay calls 24/7, master objection handling, and close more deals.",
                      "author": "Lane Wright",
                      "date": "2026-06-24",
                      "url": "https://closergym.com/playbook/foundation/what-is-closer-gym",
                      "api_url": "https://closergym.com/api/public/articles/playbook/what-is-closer-gym",
                      "content_markdown": "CloserGym is an on-demand AI sales training platform where high-ticket closers and appointment setters practice live voice roleplay calls against realistic AI buyers. Instead of rehearsing your presentation or testing diagnostic qualification frameworks on real human prospects, you enter a private digital gym where conversational mistakes cost zero lost revenue. You plug in your headset, select yo ..."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "A parameter is missing or not allowed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "example": {
                    "summary": "A parameter is not one of the allowed values",
                    "value": {
                      "error": "section must be one of: playbook, blog, comparisons.",
                      "code": "INVALID_SECTION",
                      "message": "section must be one of: playbook, blog, comparisons.",
                      "hint": "Use one of: playbook, blog, comparisons."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Nothing exists at that address.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "example": {
                    "summary": "No article at that address",
                    "value": {
                      "error": "No article with that section and slug.",
                      "code": "ARTICLE_NOT_FOUND",
                      "message": "No article with that section and slug.",
                      "hint": "List the articles at https://closergym.com/api/public/articles and use a section and slug from there."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait and try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "example": {
                    "summary": "Too many requests",
                    "value": {
                      "error": "Too many requests from this address.",
                      "code": "RATE_LIMITED",
                      "message": "Too many requests from this address.",
                      "hint": "Wait, then try again. The RateLimit headers say how long."
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Error": {
        "type": "object",
        "description": "An error. Every error from this API has these four fields.",
        "required": [
          "error",
          "code",
          "message",
          "hint"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "What went wrong. The same text as message."
          },
          "code": {
            "type": "string",
            "description": "A stable code for this kind of error. It does not change when the wording does.",
            "examples": [
              "NOT_FOUND"
            ]
          },
          "message": {
            "type": "string",
            "description": "What went wrong, in plain words."
          },
          "hint": {
            "type": "string",
            "description": "What to do about it."
          }
        },
        "additionalProperties": false
      },
      "Allowance": {
        "type": "object",
        "description": "How many sessions a plan includes in one period.",
        "required": [
          "roleplays",
          "call_analyses"
        ],
        "properties": {
          "roleplays": {
            "type": "integer",
            "minimum": 0,
            "description": "AI roleplay sessions. Objection Sparring and Script Trainer sessions use the same count."
          },
          "call_analyses": {
            "type": "integer",
            "minimum": 0,
            "description": "Recorded calls graded by Call Analysis."
          }
        },
        "additionalProperties": false
      },
      "FreeTrial": {
        "type": "object",
        "description": "The free trial. Only the Pro plan has one.",
        "required": [
          "plan",
          "days",
          "roleplays",
          "call_analyses",
          "billing_periods"
        ],
        "properties": {
          "plan": {
            "type": "string",
            "description": "The id of the plan the trial is on."
          },
          "days": {
            "type": "integer",
            "minimum": 0,
            "description": "How long the trial lasts, in days."
          },
          "roleplays": {
            "type": "integer",
            "minimum": 0,
            "description": "Roleplays included in the trial."
          },
          "call_analyses": {
            "type": "integer",
            "minimum": 0,
            "description": "Call analyses included in the trial."
          },
          "billing_periods": {
            "type": "array",
            "description": "The billing periods the trial is offered on.",
            "items": {
              "type": "string",
              "enum": [
                "weekly",
                "monthly",
                "annual"
              ]
            }
          }
        },
        "additionalProperties": false
      },
      "Plan": {
        "type": "object",
        "description": "One plan a person can buy.",
        "required": [
          "id",
          "name",
          "description",
          "prices",
          "includes",
          "free_trial",
          "url"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "The plan id.",
            "examples": [
              "pro"
            ]
          },
          "name": {
            "type": "string",
            "description": "The plan name as the pricing page shows it."
          },
          "description": {
            "type": "string",
            "description": "Who the plan is for."
          },
          "prices": {
            "type": "object",
            "description": "The list price in US dollars for each billing period.",
            "required": [
              "weekly",
              "monthly",
              "annual"
            ],
            "properties": {
              "weekly": {
                "type": "number",
                "minimum": 0,
                "description": "Price for one week."
              },
              "monthly": {
                "type": "number",
                "minimum": 0,
                "description": "Price for one month."
              },
              "annual": {
                "type": "number",
                "minimum": 0,
                "description": "Price for one year, paid once."
              }
            },
            "additionalProperties": false
          },
          "includes": {
            "type": "object",
            "description": "What the plan includes.",
            "required": [
              "per_month",
              "per_week_on_weekly_billing"
            ],
            "properties": {
              "per_month": {
                "$ref": "#/components/schemas/Allowance",
                "description": "Included each month on monthly and annual billing."
              },
              "per_week_on_weekly_billing": {
                "$ref": "#/components/schemas/Allowance",
                "description": "Included each week on weekly billing."
              }
            },
            "additionalProperties": false
          },
          "free_trial": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/FreeTrial"
              },
              {
                "type": "null"
              }
            ],
            "description": "The free trial on this plan, or null when it has none."
          },
          "url": {
            "type": "string",
            "description": "Where to buy it.",
            "format": "uri"
          }
        },
        "additionalProperties": false
      },
      "Plans": {
        "type": "object",
        "description": "The plans one person can buy.",
        "required": [
          "currency",
          "prices_are",
          "free_plan",
          "free_trial",
          "plans",
          "notes",
          "url"
        ],
        "properties": {
          "currency": {
            "type": "string",
            "description": "The currency of every price. Always USD."
          },
          "prices_are": {
            "type": "string",
            "description": "What the prices are."
          },
          "free_plan": {
            "type": "boolean",
            "const": false,
            "description": "Always false. CloserGym has no free plan."
          },
          "free_trial": {
            "$ref": "#/components/schemas/FreeTrial"
          },
          "plans": {
            "type": "array",
            "description": "The plans, cheapest first.",
            "items": {
              "$ref": "#/components/schemas/Plan"
            }
          },
          "notes": {
            "type": "array",
            "description": "Things to know before quoting a plan.",
            "items": {
              "type": "string"
            }
          },
          "url": {
            "type": "string",
            "description": "The pricing page.",
            "format": "uri"
          }
        },
        "additionalProperties": false
      },
      "TeamPlan": {
        "type": "object",
        "description": "One plan a sales team can buy.",
        "required": [
          "id",
          "name",
          "description",
          "price_per_seat",
          "includes_per_rep_per_month",
          "manager_dashboard",
          "url"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "The plan id.",
            "examples": [
              "pro_team"
            ]
          },
          "name": {
            "type": "string",
            "description": "The plan name as the pricing page shows it."
          },
          "description": {
            "type": "string",
            "description": "Who the plan is for."
          },
          "price_per_seat": {
            "type": "object",
            "description": "The list price in US dollars for one seat.",
            "required": [
              "monthly",
              "annual"
            ],
            "properties": {
              "monthly": {
                "type": "number",
                "minimum": 0,
                "description": "Price for one seat for one month."
              },
              "annual": {
                "type": "number",
                "minimum": 0,
                "description": "Price for one seat for one year, paid once."
              }
            },
            "additionalProperties": false
          },
          "includes_per_rep_per_month": {
            "$ref": "#/components/schemas/Allowance"
          },
          "manager_dashboard": {
            "type": "boolean",
            "description": "Whether the plan includes the manager dashboard."
          },
          "url": {
            "type": "string",
            "description": "Where to buy it.",
            "format": "uri"
          }
        },
        "additionalProperties": false
      },
      "TeamPlans": {
        "type": "object",
        "description": "The plans a sales team can buy.",
        "required": [
          "currency",
          "prices_are",
          "min_seats",
          "max_seats",
          "enterprise",
          "guarantee",
          "plans",
          "url"
        ],
        "properties": {
          "currency": {
            "type": "string",
            "description": "The currency of every price. Always USD."
          },
          "prices_are": {
            "type": "string",
            "description": "What the prices are."
          },
          "min_seats": {
            "type": "integer",
            "minimum": 0,
            "description": "The smallest number of seats a team plan can have."
          },
          "max_seats": {
            "type": "integer",
            "minimum": 0,
            "description": "The largest number of seats a team plan can have."
          },
          "enterprise": {
            "type": "object",
            "description": "What a larger team does.",
            "required": [
              "from_seats",
              "pricing",
              "contact_url"
            ],
            "properties": {
              "from_seats": {
                "type": "integer",
                "minimum": 0,
                "description": "Teams with this many seats or more use Enterprise pricing."
              },
              "pricing": {
                "type": "string",
                "description": "How Enterprise is priced."
              },
              "contact_url": {
                "type": "string",
                "description": "Where to ask.",
                "format": "uri"
              }
            },
            "additionalProperties": false
          },
          "guarantee": {
            "type": "string",
            "description": "The team guarantee, as the site states it."
          },
          "plans": {
            "type": "array",
            "description": "The team plans, cheapest first.",
            "items": {
              "$ref": "#/components/schemas/TeamPlan"
            }
          },
          "url": {
            "type": "string",
            "description": "The pricing page.",
            "format": "uri"
          }
        },
        "additionalProperties": false
      },
      "Credit": {
        "type": "object",
        "description": "The price of one extra credit.",
        "required": [
          "type",
          "price",
          "buys"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "roleplay",
              "call_analysis"
            ],
            "description": "What the credit is for."
          },
          "price": {
            "type": "number",
            "minimum": 0,
            "description": "The price of one credit in US dollars."
          },
          "buys": {
            "type": "string",
            "description": "What one credit buys."
          }
        },
        "additionalProperties": false
      },
      "Credits": {
        "type": "object",
        "description": "What extra credits cost.",
        "required": [
          "currency",
          "credits",
          "notes",
          "url"
        ],
        "properties": {
          "currency": {
            "type": "string",
            "description": "The currency of every price. Always USD."
          },
          "credits": {
            "type": "array",
            "description": "One entry for each kind of credit.",
            "items": {
              "$ref": "#/components/schemas/Credit"
            }
          },
          "notes": {
            "type": "array",
            "description": "Things to know about credits.",
            "items": {
              "type": "string"
            }
          },
          "url": {
            "type": "string",
            "description": "The pricing page.",
            "format": "uri"
          }
        },
        "additionalProperties": false
      },
      "Tool": {
        "type": "object",
        "description": "One of the tools inside CloserGym.",
        "required": [
          "id",
          "name",
          "summary",
          "url"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "The tool id.",
            "examples": [
              "live-roleplay"
            ]
          },
          "name": {
            "type": "string",
            "description": "The tool name."
          },
          "summary": {
            "type": "string",
            "description": "What the tool does."
          },
          "url": {
            "type": "string",
            "description": "The page about it.",
            "format": "uri"
          }
        },
        "additionalProperties": false
      },
      "FactSection": {
        "type": "object",
        "description": "A group of facts about CloserGym.",
        "required": [
          "id",
          "heading",
          "items"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "The id of the group."
          },
          "heading": {
            "type": "string",
            "description": "The heading of the group."
          },
          "items": {
            "type": "array",
            "description": "The facts, each as a question and its answer.",
            "items": {
              "type": "object",
              "description": "One fact.",
              "required": [
                "question",
                "answer"
              ],
              "properties": {
                "question": {
                  "type": "string",
                  "description": "The question."
                },
                "answer": {
                  "type": "string",
                  "description": "The answer. It stands on its own and is accurate to quote."
                }
              },
              "additionalProperties": false
            }
          }
        },
        "additionalProperties": false
      },
      "Product": {
        "type": "object",
        "description": "What CloserGym is.",
        "required": [
          "name",
          "url",
          "summary",
          "founder",
          "support_email",
          "contact_url",
          "community_url",
          "ai_buyers",
          "tools",
          "facts",
          "not_offered"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "The product name."
          },
          "url": {
            "type": "string",
            "description": "The website.",
            "format": "uri"
          },
          "summary": {
            "type": "string",
            "description": "CloserGym in one sentence."
          },
          "founder": {
            "type": "object",
            "description": "Who founded it.",
            "required": [
              "name",
              "title",
              "url"
            ],
            "properties": {
              "name": {
                "type": "string",
                "description": "Name."
              },
              "title": {
                "type": "string",
                "description": "Title."
              },
              "url": {
                "type": "string",
                "description": "The page about the founder.",
                "format": "uri"
              }
            },
            "additionalProperties": false
          },
          "support_email": {
            "type": "string",
            "description": "Where to email support.",
            "format": "email"
          },
          "contact_url": {
            "type": "string",
            "description": "The contact page.",
            "format": "uri"
          },
          "community_url": {
            "type": "string",
            "description": "The CloserGym community.",
            "format": "uri"
          },
          "ai_buyers": {
            "type": "array",
            "description": "The names of the AI buyers a rep practices against.",
            "items": {
              "type": "string"
            }
          },
          "tools": {
            "type": "array",
            "description": "The tools inside CloserGym.",
            "items": {
              "$ref": "#/components/schemas/Tool"
            }
          },
          "facts": {
            "type": "array",
            "description": "The facts the site publishes about itself.",
            "items": {
              "$ref": "#/components/schemas/FactSection"
            }
          },
          "not_offered": {
            "type": "array",
            "description": "What CloserGym does not offer, so nobody is told it does.",
            "items": {
              "type": "string"
            }
          }
        },
        "additionalProperties": false
      },
      "ArticleSummary": {
        "type": "object",
        "description": "One published article, without its text.",
        "required": [
          "section",
          "slug",
          "title",
          "description",
          "author",
          "date",
          "url",
          "api_url"
        ],
        "properties": {
          "section": {
            "type": "string",
            "enum": [
              "playbook",
              "blog",
              "comparisons"
            ],
            "description": "Which part of the site it is in."
          },
          "slug": {
            "type": "string",
            "description": "The last part of its address."
          },
          "title": {
            "type": "string",
            "description": "The title."
          },
          "description": {
            "type": "string",
            "description": "What it is about."
          },
          "author": {
            "type": "string",
            "description": "Who wrote it."
          },
          "date": {
            "type": "string",
            "description": "The date on the article, as YYYY-MM-DD."
          },
          "url": {
            "type": "string",
            "description": "The article on the website.",
            "format": "uri"
          },
          "api_url": {
            "type": "string",
            "description": "The address that returns the article in full.",
            "format": "uri"
          }
        },
        "additionalProperties": false
      },
      "ArticleList": {
        "type": "object",
        "description": "The published articles.",
        "required": [
          "count",
          "sections",
          "articles"
        ],
        "properties": {
          "count": {
            "type": "integer",
            "minimum": 0,
            "description": "How many articles are in the list."
          },
          "sections": {
            "type": "array",
            "description": "The sections in the list.",
            "items": {
              "type": "object",
              "description": "One section.",
              "required": [
                "id",
                "name",
                "url",
                "count"
              ],
              "properties": {
                "id": {
                  "type": "string",
                  "enum": [
                    "playbook",
                    "blog",
                    "comparisons"
                  ],
                  "description": "The section id."
                },
                "name": {
                  "type": "string",
                  "description": "The section name."
                },
                "url": {
                  "type": "string",
                  "description": "The section on the website.",
                  "format": "uri"
                },
                "count": {
                  "type": "integer",
                  "minimum": 0,
                  "description": "How many articles it has."
                }
              },
              "additionalProperties": false
            }
          },
          "articles": {
            "type": "array",
            "description": "The articles.",
            "items": {
              "$ref": "#/components/schemas/ArticleSummary"
            }
          }
        },
        "additionalProperties": false
      },
      "Article": {
        "type": "object",
        "description": "One published article in full.",
        "required": [
          "section",
          "slug",
          "title",
          "description",
          "author",
          "date",
          "url",
          "api_url",
          "content_markdown"
        ],
        "properties": {
          "section": {
            "type": "string",
            "enum": [
              "playbook",
              "blog",
              "comparisons"
            ],
            "description": "Which part of the site it is in."
          },
          "slug": {
            "type": "string",
            "description": "The last part of its address."
          },
          "title": {
            "type": "string",
            "description": "The title."
          },
          "description": {
            "type": "string",
            "description": "What it is about."
          },
          "author": {
            "type": "string",
            "description": "Who wrote it."
          },
          "date": {
            "type": "string",
            "description": "The date on the article, as YYYY-MM-DD."
          },
          "url": {
            "type": "string",
            "description": "The article on the website.",
            "format": "uri"
          },
          "api_url": {
            "type": "string",
            "description": "The address of this answer.",
            "format": "uri"
          },
          "content_markdown": {
            "type": "string",
            "description": "The whole article as Markdown."
          }
        },
        "additionalProperties": false
      },
      "ApiIndex": {
        "type": "object",
        "description": "What this API offers.",
        "required": [
          "name",
          "version",
          "description",
          "authentication",
          "endpoints",
          "openapi_url",
          "mcp_url",
          "docs_url",
          "llms_txt_url",
          "not_offered"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "The name of the API."
          },
          "version": {
            "type": "string",
            "description": "The version of the API."
          },
          "description": {
            "type": "string",
            "description": "What the API is for."
          },
          "authentication": {
            "type": "string",
            "description": "How to authenticate. None is needed."
          },
          "endpoints": {
            "type": "array",
            "description": "Every endpoint.",
            "items": {
              "type": "object",
              "description": "One endpoint.",
              "required": [
                "operation_id",
                "method",
                "url",
                "summary"
              ],
              "properties": {
                "operation_id": {
                  "type": "string",
                  "description": "The operationId in the OpenAPI document."
                },
                "method": {
                  "type": "string",
                  "enum": [
                    "GET"
                  ],
                  "description": "The HTTP method."
                },
                "url": {
                  "type": "string",
                  "description": "The address. Braces mark a part to fill in."
                },
                "summary": {
                  "type": "string",
                  "description": "What it returns."
                }
              },
              "additionalProperties": false
            }
          },
          "openapi_url": {
            "type": "string",
            "description": "The OpenAPI document.",
            "format": "uri"
          },
          "mcp_url": {
            "type": "string",
            "description": "The MCP server.",
            "format": "uri"
          },
          "docs_url": {
            "type": "string",
            "description": "The developer page.",
            "format": "uri"
          },
          "llms_txt_url": {
            "type": "string",
            "description": "The guide for AI agents.",
            "format": "uri"
          },
          "not_offered": {
            "type": "array",
            "description": "What CloserGym does not offer.",
            "items": {
              "type": "string"
            }
          }
        },
        "additionalProperties": false
      }
    },
    "securitySchemes": {
      "memberSession": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "Members only. The ID token a member gets when they sign in to CloserGym, sent by the CloserGym app to the private API it uses. It is not offered to outside developers, cannot be requested, and has no scopes. No operation in this document uses it: every operation here needs no credentials."
      }
    }
  }
}