Host Integration
Mount the Hitl internal API on your HTTP framework so workflows can POST to /.well-known/hitl/v1.
new Hitl() exposes three mount styles:
| Method | Use when |
|---|---|
hitl.routeHandlers.POST | Next.js App Router, frameworks with Request/Response route exports |
hitl.fetch(req) | Edge runtimes, custom routers, manual fetch handlers |
hitl.handler(req, res) | Node IncomingMessage / ServerResponse (Express, raw http) |
Also mount Chat SDK webhooks separately when using chat adapters.
Next.js
App Router catch-all under .well-known:
app/.well-known/hitl/v1/[[...path]]/route.ts
import { hitl } from "@/lib/hitl";
export const { POST } = hitl.routeHandlers;Express
import express from "express";
import { hitl } from "./lib/hitl";
const app = express();
app.post("/.well-known/hitl/v1/*", (req, res) => {
void hitl.handler(req, res);
});Or adapt hitl.fetch if you prefer middleware that builds a Request:
app.post("/.well-known/hitl/v1/*", async (req, res) => {
const url = `${req.protocol}://${req.get("host")}${req.originalUrl}`;
const body = req.method !== "GET" ? JSON.stringify(req.body) : undefined;
const response = await hitl.fetch(
new Request(url, { method: req.method, headers: req.headers as HeadersInit, body }),
);
res.status(response.status);
response.headers.forEach((v, k) => res.setHeader(k, v));
res.send(await response.text());
});Hono
import { Hono } from "hono";
import { hitl } from "./lib/hitl";
const app = new Hono();
app.post("/.well-known/hitl/v1/*", (c) => hitl.fetch(c.req.raw));Fastify
import Fastify from "fastify";
import { hitl } from "./lib/hitl";
const app = Fastify();
app.post("/.well-known/hitl/v1/*", async (req, reply) => {
const url = `${req.protocol}://${req.hostname}${req.url}`;
const response = await hitl.fetch(
new Request(url, { method: "POST", headers: req.headers as HeadersInit, body: JSON.stringify(req.body) }),
);
reply.status(response.status);
for (const [k, v] of response.headers) reply.header(k, v);
reply.send(await response.text());
});NestJS
import { Controller, Post, Req, Res } from "@nestjs/common";
import type { Request, Response } from "express";
import { hitl } from "./hitl";
@Controller(".well-known/hitl/v1")
export class HitlController {
@Post("*")
handle(@Req() req: Request, @Res() res: Response) {
return hitl.handler(req, res);
}
}Nitro
server/routes/.well-known/hitl/v1/[...path].ts
import { hitl } from "../../lib/hitl";
export default defineEventHandler((event) => hitl.fetch(toWebRequest(event)));Nuxt
Use a Nitro server route (same as Nitro above) under server/routes/.well-known/hitl/v1/[...path].ts.
Astro
src/pages/.well-known/hitl/v1/[...path].ts
import type { APIRoute } from "astro";
import { hitl } from "../../../lib/hitl";
export const POST: APIRoute = ({ request }) => hitl.fetch(request);SvelteKit
// src/routes/.well-known/hitl/v1/[...path]/+server.ts
import { hitl } from "$lib/hitl";
import type { RequestHandler } from "./$types";
export const POST: RequestHandler = ({ request }) => hitl.fetch(request);Vite / raw Node
For Vite SSR or a standalone Node server:
import { createServer } from "node:http";
import { hitl } from "./lib/hitl";
createServer((req, res) => {
if (req.method === "POST" && req.url?.startsWith("/.well-known/hitl/v1")) {
void hitl.handler(req, res);
return;
}
res.statusCode = 404;
res.end();
}).listen(3000);Security
Set HITL_SECRET (or pass secret to new Hitl()) in production. Workflows must send Authorization: Bearer <secret> on internal API calls when configured.