KakaoTalk Chatbot Webhook Verification Fails: Fix FastAPI Endpoint for Kakao OpenBuilder

If you've set up a FastAPI server for your KakaoTalk chatbot and Kakao OpenBuilder keeps rejecting your webhook during the verification step, you're not alone. This is one of the most common stumbling blocks when building Kakao chatbots — the verification endpoint has a very specific response format, and even a small deviation causes it to fail silently. This post walks you through exactly what format Kakao expects, how to implement it correctly in FastAPI, and how to debug when things still go wrong.

Table of Contents

🔍 How Kakao Webhook Verification Works

When you register a webhook URL in Kakao OpenBuilder, the platform sends a GET request to your endpoint to verify it's alive and responding correctly. This is different from the actual chatbot message flow, which uses POST requests.

The verification flow looks like this:

sequenceDiagram participant KakaoOpenBuilder as "Kakao OpenBuilder" participant YourServer as "Your FastAPI Server" KakaoOpenBuilder->>YourServer: GET /webhook YourServer-->>KakaoOpenBuilder: 200 OK + {"version":"2.0","data":{}} KakaoOpenBuilder->>KakaoOpenBuilder: "Verification Passed" Note over KakaoOpenBuilder,YourServer: After verification, real messages use POST

The key insight is that Kakao's verification is a simple GET request, and your server must respond with a specific JSON body — not just an HTTP 200 status. Many developers return a plain 200 OK with no body or the wrong body structure, which causes the verification to fail even though the server is technically reachable.

📋 The Exact Response Format Kakao Expects

Kakao OpenBuilder's webhook verification expects your GET endpoint to return a JSON response with the following structure:

{
  "version": "2.0",
  "data": {}
}

A few critical rules:

  • The HTTP status code must be 200.
  • The Content-Type header must be application/json.
  • The version field must be the string "2.0" — not a number.
  • The data field must be present, even if it's an empty object {}.

Some older tutorials show returning just {"status": "OK"} or an empty body — these will fail with the current Kakao OpenBuilder API. Always use the structure above.

⚡ FastAPI Implementation

Here's how to implement the verification endpoint in FastAPI. The GET route handles verification, and the POST route handles actual chatbot messages.

from fastapi import FastAPI
from fastapi.responses import JSONResponse

app = FastAPI()

# Kakao OpenBuilder sends a GET request to verify the webhook endpoint
@app.get("/webhook")
async def verify_webhook():
    # Return the exact format Kakao expects for verification
    return JSONResponse(
        content={
            "version": "2.0",
            "data": {}
        },
        status_code=200
    )

# Actual chatbot messages arrive via POST
@app.post("/webhook")
async def handle_message(request: dict):
    # Placeholder: process the incoming Kakao message
    return JSONResponse(
        content={
            "version": "2.0",
            "template": {
                "outputs": [
                    {
                        "simpleText": {
                            "text": "Hello from FastAPI!"
                        }
                    }
                ]
            }
        },
        status_code=200
    )

Notice that both the GET and POST routes live on the same /webhook path. FastAPI handles routing by HTTP method automatically, so there's no conflict.

🐛 Common Mistakes and Fixes

Mistake 1: Missing the data field

Returning {"version": "2.0"} without the data key will cause verification to fail. Always include "data": {}.

Mistake 2: Wrong Content-Type

If you return a plain string response or use Response instead of JSONResponse, the Content-Type may not be set to application/json. FastAPI's JSONResponse sets this automatically.

Mistake 3: Only a POST route defined

Kakao's verification uses GET. If you only defined a POST route, the verification request gets a 405 Method Not Allowed response. Make sure you have a GET handler on the same path.

Mistake 4: SSL certificate issues

Kakao OpenBuilder requires your webhook URL to use HTTPS with a valid SSL certificate. Self-signed certificates will be rejected. Use a service like Let's Encrypt or a platform like Railway, Render, or ngrok for testing.

Mistake 5: Firewall blocking Kakao IPs

If your server is behind a firewall, make sure port 443 (HTTPS) is open to incoming connections from Kakao's servers.

🌐 Testing Locally with ngrok

You can't register localhost as a webhook URL in Kakao OpenBuilder. Use ngrok to expose your local FastAPI server over HTTPS for testing.

# Step 1: Install ngrok from https://ngrok.com and authenticate
# Step 2: Start your FastAPI server
uvicorn main:app --host 0.0.0.0 --port 8000

# Step 3: In a separate terminal, start ngrok
ngrok http 8000

ngrok will give you a public HTTPS URL like https://abc123.ngrok.io. Register https://abc123.ngrok.io/webhook in Kakao OpenBuilder. Remember that free ngrok URLs change every time you restart ngrok, so you'll need to update the URL in OpenBuilder each session.

✅ Full Working Example

Here's a complete, production-ready FastAPI app that handles both Kakao webhook verification and basic message responses. You can copy this directly and run it.

🔽 [Click to expand] Full FastAPI Kakao Chatbot Server
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
import uvicorn

app = FastAPI(title="KakaoTalk Chatbot Server")


# --- Webhook Verification (GET) ---
# Kakao OpenBuilder calls this endpoint to verify the webhook is live
@app.get("/webhook")
async def verify_webhook():
    """
    Kakao OpenBuilder verification endpoint.
    Must return HTTP 200 with this exact JSON structure.
    """
    return JSONResponse(
        content={
            "version": "2.0",
            "data": {}
        },
        status_code=200
    )


# --- Message Handler (POST) ---
# Kakao sends user messages to this endpoint
@app.post("/webhook")
async def handle_kakao_message(request: Request):
    """
    Handles incoming messages from KakaoTalk users.
    Parses the request body and returns a simple text response.
    """
    body = await request.json()

    # Extract the user's utterance from the Kakao request payload
    user_utterance = body.get("userRequest", {}).get("utterance", "")

    # Build a simple text response in Kakao's SkillResponse format
    response_text = f"You said: {user_utterance}"

    return JSONResponse(
        content={
            "version": "2.0",
            "template": {
                "outputs": [
                    {
                        "simpleText": {
                            "text": response_text
                        }
                    }
                ]
            }
        },
        status_code=200
    )


# --- Health Check ---
@app.get("/health")
async def health_check():
    """Simple health check endpoint."""
    return {"status": "ok"}


if __name__ == "__main__":
    # Run the server on port 8000
    uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)

To run this, install the dependencies first:

pip install fastapi uvicorn
python main.py
flowchart TD A["User sends message in KakaoTalk"] --> B["Kakao Platform"] B --> C["POST /webhook to your FastAPI server"] C --> D["Parse userRequest.utterance"] D --> E["Build SkillResponse JSON"] E --> F["Return 200 + response body"] F --> B B --> G["Display reply to user"]

🔧 Debugging Checklist

If verification still fails after implementing the correct format, go through this checklist:

  • Check the response body: Use curl -X GET https://your-url/webhook and confirm the response is exactly {"version": "2.0", "data": {}}.
  • Check Content-Type: Run curl -I https://your-url/webhook and verify content-type: application/json is in the headers.
  • Check HTTP status: The response must be 200, not 201 or 204.
  • Check HTTPS: The URL must start with https://. HTTP URLs are rejected.
  • Check the path: Make sure the path you registered in OpenBuilder exactly matches your FastAPI route (e.g., /webhook vs /webhook/ — trailing slashes matter).
  • Check server logs: Look at your FastAPI logs to confirm the GET request is actually reaching your server. If there are no logs, the request is being blocked before it hits your app.

To summarize: Kakao OpenBuilder's webhook verification requires a GET endpoint that returns HTTP 200 with a JSON body of {"version": "2.0", "data": {}} and a Content-Type: application/json header. FastAPI's JSONResponse handles all of this automatically. The most common causes of failure are a missing data field, no GET route defined, or an invalid SSL certificate — fix those three things first and verification will pass.

Comments

Popular posts from this blog

OpenAI vs Gemini API in 2026: Pricing, Rate Limits & Response Quality for Your Chatbot

System, User, and Assistant Roles in the OpenAI Chat API Explained

Discord Slash Command Not Appearing in Server: How to Fix It Fast (2026)