Express
Mount the proxy as standard Express middleware. Mount webhook verification before JSON parsing so the adapter receives the original request bytes.
Create the integration
import { createIncld, defineActions } from "@incld/client/express"
export const incld = createIncld({
apiKey: process.env.INCLD_SECRET_KEY!,
webhookSecret: process.env.INCLD_WEBHOOK_SECRET!,
actions: defineActions({ sync_contacts: { run: runContactSync } }),
async resolveContext(_request, req) {
if (!req.user) return null
if (!req.organization) return null
return {
user: { id: req.user.id },
organization: { id: req.organization.id },
roles: req.user.roles,
}
},
async authorize({ context, operation }) {
return permissions.allow(context.user.id, operation)
},
})
Mount proxy and webhook middleware
import express from "express"
import { incld } from "./incld"
const app = express()
// Mount before the global JSON parser so the signature sees exact bytes.
app.post(
"/api/incld/webhook",
express.raw({ type: "application/json" }),
incld.webhook,
)
app.use(express.json())
app.use("/api/incld/v1", requireSession, incld.routes)
Middleware order matters
If a JSON parser consumes and reserializes the webhook body first, the HMAC will not match. The proxy may use parsed JSON; the webhook must retain the raw bytes.
Error handling
The middleware sends the adapter response status, headers, and body. Unexpected adapter exceptions are passed to Express
next(error)
when available. Keep your standard final error middleware after the @incld routes.
Synchronize actions on deploy
// scripts/sync-incld-actions.ts
import { incld } from "../src/incld"
await incld.syncActions()