Docs/Webhook Integration

Webhook Integration

Receive content from Entro AI agents on any platform via HTTP webhooks.

Overview

Entro's Webhook integration lets your AI agents push content to any external platform via standard HTTP requests. Unlike platform-specific integrations (WordPress, Notion, etc.), webhooks give you full control over how and where content is delivered.

Common use cases:

  • Publish blog posts to a custom CMS or static site generator
  • Send notifications to internal tools (Slack alternatives, Discord, etc.)
  • Push data to a headless e-commerce platform
  • Trigger CI/CD pipelines or automation workflows
  • Sync content to any REST API

How It Works

The flow is straightforward:

  1. You register a webhook endpoint in Entro's Settings → Integrations page.
  2. When your AI agent needs to publish content, it sends an HTTP request to your endpoint with a JSON payload.
  3. Your server receives the request, validates the authentication, and processes the content however you need.
┌─────────────┐     HTTP POST/PUT/PATCH     ┌──────────────────┐
│  Entro Agent │  ───────────────────────►   │  Your Server     │
│              │     + Auth Header           │  (webhook receiver)│
│  "Publish    │     + JSON Body             │                  │
│   this post" │                             │  Process content  │
└─────────────┘                              └──────────────────┘

Setting Up in Entro

  1. Go to Settings → Integrations in your Entro dashboard.
  2. Find the Webhook card and click Add Webhook.
  3. Fill in the form:
    • URL — Your server's endpoint (e.g. https://yoursite.com/api/entro-webhook)
    • Label — A friendly name (e.g. “My Blog”)
    • Method — POST (default), PUT, or PATCH
    • Authentication — Choose Header, Query Parameter, or None
    • Token / API Key — Your secret for authenticating requests
  4. Click Add. Entro will verify your endpoint is reachable before saving.

You can add multiple webhook endpoints — your agents can publish to different platforms simultaneously.

Building Your Receiver Endpoint

Your webhook receiver is a standard HTTP endpoint that accepts JSON. Here's what it needs to do:

  1. Accept the configured HTTP method (POST, PUT, or PATCH)
  2. Validate authentication (check the token/key)
  3. Parse the JSON body and process the content
  4. Return a response (2xx for success, 4xx/5xx for errors)

Request Format

Headers

Every request includes Content-Type: application/json. Depending on your authentication configuration:

// Header authentication (most common)
POST /api/entro-webhook HTTP/1.1
Host: yoursite.com
Content-Type: application/json
Authorization: Bearer your-secret-token

// Query parameter authentication
POST /api/entro-webhook?token=your-secret-token HTTP/1.1
Host: yoursite.com
Content-Type: application/json

// No authentication
POST /api/entro-webhook HTTP/1.1
Host: yoursite.com
Content-Type: application/json

Request Body

The JSON body structure depends on what your agent sends. Entro agents are flexible — you can instruct them to format the payload to match your API. A typical content-publishing payload looks like:

{
  "title": "How to Build a REST API",
  "content": "<h2>Introduction</h2><p>In this guide...</p>",
  "status": "draft",
  "excerpt": "A step-by-step guide to building REST APIs.",
  "categories": ["tutorials", "backend"],
  "tags": ["api", "rest", "nodejs"],
  "author": "Entro Agent",
  "metadata": {
    "seo_title": "How to Build a REST API | My Blog",
    "seo_description": "Learn how to build a REST API...",
    "featured_image": "https://example.com/images/api-guide.jpg"
  }
}

Tip: You can customize what your agent sends by describing the expected payload format in the agent's system prompt. For example: “When publishing to the webhook, always include title, content, status, and categories fields.”

Authentication

Entro supports three authentication methods:

1. Header Authentication (Recommended)

The token is sent in an HTTP header. You choose the header name when configuring the webhook (defaults to Authorization).

// Default: Authorization header
Authorization: Bearer your-secret-token

// Custom header (e.g. X-API-Key)
X-API-Key: your-secret-token

2. Query Parameter Authentication

The token is appended to the URL as a query parameter named token.

POST /api/entro-webhook?token=your-secret-token

3. No Authentication

No token is sent. Only use this for internal endpoints on private networks. Not recommended for production.

Expected Response

Your endpoint should return a JSON response. The agent reads the response to confirm success or handle errors:

// Success (200 OK)
{
  "success": true,
  "id": "post-123",
  "url": "https://yoursite.com/blog/how-to-build-a-rest-api"
}

// Error (400 Bad Request)
{
  "error": "Missing required field: title"
}

// Error (401 Unauthorized)
{
  "error": "Invalid authentication token"
}

Code Examples

Node.js (Express)

const express = require("express");
const app = express();

app.use(express.json());

const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET;

app.post("/api/entro-webhook", (req, res) => {
  // 1. Validate authentication
  const authHeader = req.headers["authorization"];
  if (!authHeader || authHeader !== `Bearer ${WEBHOOK_SECRET}`) {
    return res.status(401).json({ error: "Unauthorized" });
  }

  // 2. Process the content
  const { title, content, status, categories } = req.body;

  if (!title || !content) {
    return res.status(400).json({ error: "title and content are required" });
  }

  // 3. Save to your database, CMS, or file system
  console.log("Received:", { title, status, categories });
  // await db.posts.create({ title, content, status, categories });

  // 4. Return success
  res.json({
    success: true,
    id: "post-" + Date.now(),
    message: "Content received successfully",
  });
});

app.listen(3000, () => console.log("Webhook receiver running on :3000"));

Python (Flask)

from flask import Flask, request, jsonify
import os

app = Flask(__name__)
WEBHOOK_SECRET = os.environ.get("WEBHOOK_SECRET")

@app.route("/api/entro-webhook", methods=["POST"])
def entro_webhook():
    # 1. Validate authentication
    auth = request.headers.get("Authorization", "")
    if auth != f"Bearer {WEBHOOK_SECRET}":
        return jsonify({"error": "Unauthorized"}), 401

    # 2. Process the content
    data = request.get_json()
    title = data.get("title")
    content = data.get("content")

    if not title or not content:
        return jsonify({"error": "title and content are required"}), 400

    # 3. Save to your database
    print(f"Received: {title}")
    # db.posts.create(title=title, content=content, ...)

    # 4. Return success
    return jsonify({
        "success": True,
        "message": "Content received successfully",
    })

if __name__ == "__main__":
    app.run(port=3000)

PHP

<?php
// api/entro-webhook.php

$WEBHOOK_SECRET = getenv("WEBHOOK_SECRET");

// 1. Validate authentication
$headers = getallheaders();
$auth = $headers["Authorization"] ?? "";

if ($auth !== "Bearer " . $WEBHOOK_SECRET) {
    http_response_code(401);
    echo json_encode(["error" => "Unauthorized"]);
    exit;
}

// 2. Parse the JSON body
$body = json_decode(file_get_contents("php://input"), true);
$title = $body["title"] ?? null;
$content = $body["content"] ?? null;

if (!$title || !$content) {
    http_response_code(400);
    echo json_encode(["error" => "title and content are required"]);
    exit;
}

// 3. Save to your database
// $pdo->prepare("INSERT INTO posts ...")->execute([...]);

// 4. Return success
http_response_code(200);
echo json_encode([
    "success" => true,
    "message" => "Content received successfully",
]);

Next.js (App Router)

// app/api/entro-webhook/route.ts
import { NextRequest, NextResponse } from "next/server";

const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET!;

export async function POST(req: NextRequest) {
  // 1. Validate authentication
  const auth = req.headers.get("authorization");
  if (auth !== `Bearer ${WEBHOOK_SECRET}`) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  // 2. Parse the body
  const { title, content, status } = await req.json();

  if (!title || !content) {
    return NextResponse.json(
      { error: "title and content are required" },
      { status: 400 }
    );
  }

  // 3. Save to your database
  // await prisma.post.create({ data: { title, content, status } });

  // 4. Return success
  return NextResponse.json({
    success: true,
    message: "Content received successfully",
  });
}

Testing Your Webhook

Using webhook.site

Before building your own endpoint, you can test the integration using webhook.site:

  1. Go to webhook.site and copy your unique URL
  2. Add it as a webhook endpoint in Entro (Settings → Integrations)
  3. Ask your agent to publish something — you'll see the request appear in real-time

Using curl

Test your own endpoint locally:

curl -X POST http://localhost:3000/api/entro-webhook \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer your-secret-token" \
  -d '{
    "title": "Test Post",
    "content": "<p>Hello from Entro!</p>",
    "status": "draft"
  }'

Security Best Practices

  • Always use HTTPS in production — tokens are sent in headers/URLs and must be encrypted in transit
  • Use Header authentication over query parameters — query strings can appear in server logs
  • Generate strong tokens — use at least 32 characters with mixed case, numbers, and symbols
  • Validate the request body — never trust incoming data; always sanitize HTML content before storing
  • Rate-limit your endpoint — protect against abuse with reasonable rate limits
  • Return minimal error details — don't expose internal error messages or stack traces

Troubleshooting

“Could not reach” error when adding the webhook

Entro verifies your endpoint is reachable when you add it. Make sure:

  • Your server is running and publicly accessible
  • The URL is correct (check for typos)
  • Your firewall allows inbound HTTP requests
  • If using a local development server, expose it with a tunneling service like ngrok

401 / 403 errors

  • Double-check that the token in Entro matches the one your server expects
  • Verify you're checking the correct header name (e.g. Authorization vs X-API-Key)
  • Make sure your auth check includes the Bearer prefix if your token format requires it

Empty or malformed body

  • Ensure your server parses JSON bodies (e.g. express.json() in Express, request.get_json() in Flask)
  • Check that Content-Type: application/json is being sent (Entro always sends this)

Agent doesn't send the right fields

Customize the payload by adding instructions to your agent's system prompt:

When publishing content via webhook, always send a JSON body with:
- "title": the post title (string)
- "content": the full HTML content (string)
- "status": "draft" or "publish" (string)
- "categories": array of category slugs
- "featured_image": URL to the featured image (if available)

Need Help?

If you run into issues with the Webhook integration, reach out through our Telegram community or email us at support@entro.work.