Custom Routes
Custom routes let you register HTTP endpoints scoped to your workspace. Use them to receive webhooks from external systems, expose custom APIs, or serve data to frontend integrations.
Registering a route
Use sdk.registerRoute() inside your module's register function.
Basic example
import { ConvoticModule } from "@convotic/block-sdk";
const module: ConvoticModule = {
name: "erp-webhook",
version: "1.0.0",
register(sdk) {
sdk.registerRoute({
method: "POST",
path: "/webhook/erp",
handler: async (req, ctx) => {
const payload = req.body;
// Look up the contact by external ID
const contact = await ctx.contacts.findByExternalId(
payload.customer_id
);
if (contact) {
// Update the contact with order data from the ERP
await ctx.contacts.update(contact.id, {
attributes: {
last_erp_order_id: payload.order_id,
last_erp_order_status: payload.status,
},
});
}
return { status: 200, body: { ok: true } };
},
});
},
};
export default module;
The route will be available at:
POST https://api.convotic.com/v1/modules/erp-webhook/webhook/erp
Route handler signature
sdk.registerRoute({
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE",
path: string,
middleware?: Middleware[],
handler: (req: RouteRequest, ctx: ConvoticContext) => Promise<RouteResponse>,
});
RouteRequest
| Property | Type | Description |
|---|---|---|
body | any | Parsed request body (JSON). |
query | Record<string, string> | Query string parameters. |
params | Record<string, string> | URL path parameters. |
headers | Record<string, string> | Request headers. |
ConvoticContext
| Property | Description |
|---|---|
ctx.contacts | Contact CRUD operations. |
ctx.conversations | Conversation CRUD operations. |
ctx.messages | Send messages to contacts. |
ctx.workflows | Trigger workflows programmatically. |
ctx.storage | Key-value storage scoped to the module. |
ctx.logger | Structured logger. |
RouteResponse
{
status: number;
body?: any;
headers?: Record<string, string>;
}
Path parameters
Define dynamic path segments with :param syntax:
sdk.registerRoute({
method: "GET",
path: "/orders/:orderId",
handler: async (req, ctx) => {
const { orderId } = req.params;
const order = await ctx.storage.get(`order:${orderId}`);
if (!order) {
return { status: 404, body: { error: "Order not found" } };
}
return { status: 200, body: order };
},
});
Authentication
By default, custom routes require a valid X-API-Key header (the same key used for the Convotic API). To accept unauthenticated requests (e.g., for external webhooks), set auth: false:
sdk.registerRoute({
method: "POST",
path: "/webhook/external",
auth: false,
handler: async (req, ctx) => {
// Verify the webhook signature yourself
const isValid = verifySignature(req.headers, req.body);
if (!isValid) {
return { status: 401, body: { error: "Invalid signature" } };
}
// Process the event
await ctx.workflows.trigger("wf_abc123", {
payload: req.body,
});
return { status: 200, body: { ok: true } };
},
});
警告
When disabling authentication, always implement your own verification logic to prevent unauthorized access.