Guides/Agent webhook callback pattern

Agent webhook callback pattern

Agent creates a form, registers a webhook to its own endpoint, and reacts when humans submit.

Updated March 2026

The webhook callback pattern lets an AI agent set up a form, point a webhook at its own server, and take action the moment a human submits data. The agent doesn't poll. It doesn't wait. It gets an HTTP POST with the submission payload and decides what to do next -- update a page, send an email, create a dashboard entry, or trigger another workflow. This is the standard pattern for agents that run a persistent server process. If your agent can receive HTTP requests, this is the fastest path from human input to agent action.

1. Create a form for human input

The agent creates a form with the fields it needs. This could be a feedback form, an approval request, a content submission -- anything where the agent needs structured input from a human.

const SUTRENA_KEY = process.env.SUTRENA_API_KEY;

const formRes = await fetch("https://sutrena.com/api/forms", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${SUTRENA_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "Agent Approval Queue",
    fields: [
      { name: "item", label: "Item to approve", type: "text", required: true },
      { name: "decision", label: "Decision", type: "select", options: ["Approve", "Reject", "Needs revision"] },
      { name: "notes", label: "Notes", type: "textarea" },
    ],
  }),
});
const form = await formRes.json();
console.log("Form URL:", form.data.hostedFormUrl);
console.log("Form ID:", form.data.formId);

2. Register a webhook pointing to your agent

The agent creates a webhook that fires on every submission to its form. The webhook URL is the agent's own HTTP endpoint. When a human submits the form, Sutrena sends a POST request to this URL with the full submission payload.

const webhookRes = await fetch("https://sutrena.com/api/webhooks", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${SUTRENA_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    url: "https://my-agent.example.com/webhook/approval",
    formIds: [form.data.formId],
    description: "Agent approval callback",
  }),
});
const webhook = await webhookRes.json();
// Save the signing secret to verify payloads later
const signingSecret = webhook.data.signingSecret;

3. Handle the webhook in your agent

When a human submits the form, your agent receives an HTTP POST with the submission data. Parse the payload and take action -- update a page, send a notification, trigger a downstream workflow, or anything else your agent needs to do.

// Node.js / Express example
import express from "express";
import crypto from "crypto";

const app = express();
app.use(express.json());

app.post("/webhook/approval", async (req, res) => {
  // Verify the webhook signature (recommended)
  const signature = req.headers["x-webhook-signature"];
  const expected = crypto
    .createHmac("sha256", signingSecret)
    .update(JSON.stringify(req.body))
    .digest("hex");

  if (signature !== expected) {
    return res.status(401).json({ error: "Invalid signature" });
  }

  // Extract submission data
  const { payload } = req.body;
  const { item, decision, notes } = payload;

  console.log(`Decision on "${item}": ${decision}`);

  if (decision === "Approve") {
    // Agent takes action: update a page, call another API, etc.
    await fetch(`https://sutrena.com/api/pages/${pageId}`, {
      method: "PUT",
      headers: {
        "Authorization": `Bearer ${SUTRENA_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        html: `<h1>Approved: ${item}</h1><p>${notes || "No notes."}</p>`,
      }),
    });
  }

  res.status(200).json({ received: true });
});

app.listen(3000);

4. Python Flask alternative

The same pattern works in Python. The agent receives the webhook POST and acts on it.

from flask import Flask, request, jsonify
import hmac, hashlib, json, os, requests

app = Flask(__name__)
SIGNING_SECRET = os.environ["WEBHOOK_SIGNING_SECRET"]
SUTRENA_KEY = os.environ["SUTRENA_API_KEY"]

@app.route("/webhook/approval", methods=["POST"])
def handle_approval():
    # Verify signature
    signature = request.headers.get("x-webhook-signature", "")
    expected = hmac.new(
        SIGNING_SECRET.encode(),
        request.data,
        hashlib.sha256,
    ).hexdigest()

    if not hmac.compare_digest(signature, expected):
        return jsonify(error="Invalid signature"), 401

    data = request.json
    decision = data["payload"]["decision"]
    item = data["payload"]["item"]

    if decision == "Approve":
        requests.put(
            f"https://sutrena.com/api/pages/{page_id}",
            headers={"Authorization": f"Bearer {SUTRENA_KEY}"},
            json={"html": f"<h1>Approved: {item}</h1>"},
        )

    return jsonify(received=True)

FAQ

What does the webhook payload look like?

The POST body contains `{ formId, submissionId, payload: { ...field values }, submittedAt }`. The payload object has keys matching your form field names. File fields contain the CDN URL of the uploaded file.

What if my agent can't receive HTTP requests?

Use the polling pattern instead. See the "Poll for new form submissions" guide. Poll GET /api/forms/:id/submissions with a `from` timestamp to get new submissions since the last check.

Can I use platform-specific webhook templates?

Yes. Set `template: "slack"`, `"discord"`, `"telegram"`, `"teams"`, or `"google-chat"` to auto-format the payload for those platforms. For your own agent endpoint, use the default template which sends raw JSON.

How do I verify webhook signatures?

Compute HMAC-SHA256 of the raw request body using the signing secret (returned when you create the webhook). Compare it to the `x-webhook-signature` header. This ensures the payload came from Sutrena.

What is Sutrena?

Sutrena is the web runtime for AI agents. Forms, Pages, Dashboards, Analytics, Webhooks, Automations, Emails — all through 75 MCP tools and one REST API. Your agent creates web artifacts, humans interact with them, and your agent gets the data back. Use any one feature or all of them together.

Pages

Deploy HTML instantly

Forms

Collect structured data

Dashboards

Visualize with 7 widget types

Analytics

Privacy-first, no cookies

Webhooks

Slack, Discord, Telegram

Get started in two API calls

1. Get a trial key (no auth, no signup)

curl -X POST https://sutrena.com/api/trial

2. Create anything — a page, form, dashboard, or analytics site

# Create a form with a dashboard
curl -X POST https://sutrena.com/api/forms \
  -H "Authorization: Bearer st_trial_xxx" \
  -H "Content-Type: application/json" \
  -d '{"workflowId": "waitlist", "createDashboard": true}'

# Or deploy a page
curl -X POST https://sutrena.com/api/pages \
  -H "Authorization: Bearer st_trial_xxx" \
  -H "Content-Type: application/json" \
  -d '{"slug": "index", "title": "My Site", "html": "<h1>Live</h1>"}'

Ready to build?

Get a trial API key instantly with no signup, or create an account for the full experience.

Agent webhook callback pattern — receive form submissions in real-time | Sutrena | Sutrena