Skip to main content

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:

  1. Receives provider webhooks.
  2. Verifies the signature using the provider’s secret.
  3. Normalizes the payload and enriches it with _meta.
  4. Sends a WebhookEvent to 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:

  1. 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)
  2. Static JWT (optional)

    • Provide CONNECTOR_API_TOKEN
    • Used as-is for all requests

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