Custom Connector Setup
This guide explains how to build a custom connector that receives webhooks from any provider, validates signatures, normalizes payloads, and forwards them to Adspire.AI using the Node SDK.
Architecture overview
A connector is a stateless HTTP service that:
- Receives provider webhooks.
- Verifies the signature using the provider’s secret.
- Normalizes the payload and enriches it with
_meta. - Sends a
WebhookEventto Adspire using the Node SDK.
Prerequisites
- Node.js 18+ (or Docker)
- A dedicated connector user in Adspire (email + password) or a static JWT
- Webhook secrets from the provider
Connector skeleton (Node.js)
Use any framework (Express, Fastify, Nest). The key pieces are raw-body parsing for signature verification and a forwarding step.
import express from "express";
import crypto from "crypto";
const app = express();
app.post("/webhooks/provider", express.raw({ type: "application/json" }), async (req, res) => {
const rawBody = req.body as Buffer;
const signature = req.header("x-provider-signature");
if (!verifySignature(rawBody, signature, process.env.PROVIDER_WEBHOOK_SECRET)) {
return res.status(401).send("Invalid signature");
}
const payload = JSON.parse(rawBody.toString("utf8"));
await forwardEvent({
source: "provider",
topic: "topic",
payload,
requestId: crypto.randomUUID(),
});
return res.sendStatus(200);
});
app.listen(process.env.PORT ?? 3000);
Signature verification (provider-agnostic)
Most providers sign the raw request body using HMAC and a shared secret. Always verify using the raw bytes, not a parsed JSON body.
const expected = crypto
.createHmac("sha256", secret)
.update(rawBody)
.digest("base64");
const valid = crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature ?? "")
);
Authentication
Connectors can authenticate in two ways:
-
Connector user login (recommended)
- Use
CONNECTOR_API_EMAIL+CONNECTOR_API_PASSWORD - Authenticate via the Adspire.AI API and cache the access token (see the Auth docs)
- Use
-
Static JWT (optional)
- Provide
CONNECTOR_API_TOKEN - Used as-is for all requests
- Provide
SDK usage patterns
Option A: static JWT
import { createWebhookEvent } from "@adspireai/adspire-node-sdk";
import { client } from "@adspireai/adspire-node-sdk/dist/client.gen";
client.setConfig({
baseUrl: process.env.API_URL,
headers: {
Accept: "application/json",
Authorization: `Bearer ${process.env.CONNECTOR_API_TOKEN}`,
},
});
await createWebhookEvent({ client, body: { event_type: "provider.topic", payload } });
Option B: login and cache JWT
Use the Auth endpoints to obtain an access token and cache it for subsequent requests.
Dockerize your connector
Example Dockerfile:
FROM node:20-alpine
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm install
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["node", "dist/index.js"]
Run multiple connectors in one Docker Compose
You can run multiple connector containers in a single docker-compose.yml, each with its own port and secrets.
version: "3.9"
services:
connector-shopify:
build: ./connector-shopify
env_file: .env
environment:
PORT: "3001"
PROVIDER: "shopify"
ports:
- "3001:3001"
connector-woocommerce:
build: ./connector-woocommerce
env_file: .env
environment:
PORT: "3002"
PROVIDER: "woocommerce"
ports:
- "3002:3002"
SDK forwarding
Use the Node SDK to forward events with a provider-specific event_type and a normalized payload (optionally enriched with _meta).
Register provider webhooks
Each provider should be configured to call your connector endpoint. Examples:
- Shopify:
/webhooks/shopify - WooCommerce:
/webhooks/woocommerce - Custom provider:
/webhooks/<provider>
Connector API endpoints
See the API reference for managing connectors and installations.
Operational considerations
- Add health checks appropriate for your hosting environment.
- Use resilient delivery patterns (retries, buffering, and replay) appropriate for your runtime and provider requirements.
Troubleshooting
- 401/403: invalid JWT or connector user credentials
- 502: backend unreachable or returned non-2xx