Connector Development Patterns
Connectors can publish events, synchronize data from an external API, receive provider webhooks, or expose actions. These are implementation patterns, not different registration paths or connector types. All connectors are registered by developers through the Adspire partner portal; pending definitions are visible in the developer's workspace and become available to users generally after Adspire approval.
Follow Connector Registration and HTTP Contract for registration, review, installation, and service requirements. This guide introduces common implementation patterns and gives a webhook-to-event example.
Choose an integration pattern
- Event producer: send events to Adspire when relevant changes happen in an external system.
- Webhook receiver: receive provider webhooks, verify their signatures, normalize events, and forward them to Adspire.
- Polling or scheduled sync: periodically fetch changes from a provider API, track a cursor or checkpoint, and publish new or updated records as events.
- Adspire installation and actions: after the connector definition is approved and available to users, let users install it through Adspire, retain provider credentials securely per installation, and optionally expose declared actions or queries.
Patterns can be combined. For example, an integration may perform an initial sync after installation and then use webhooks for ongoing updates. Use the Adspire installation lifecycle when installation state, per-install credentials, or Adspire-dispatched actions are needed; follow the signed-request contract rather than treating lifecycle calls as ordinary provider webhooks.
Webhook-to-event example
The following pattern applies when a provider sends webhook notifications. A connector that polls an API or publishes events from another source can use the same event-forwarding step without implementing an inbound webhook route.
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);
Verify the provider signature
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 ?? "")
);
Authenticate requests to Adspire
For integrations that call Adspire APIs directly, use a server-side credential with the least privilege needed:
-
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
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 a connector with Docker Compose
For local development, a connector can be run with Docker Compose. Keep credentials in an untracked environment file or a secrets manager; do not commit them.
services:
connector:
build: .
env_file: .env
environment:
PORT: "3000"
ports:
- "3000:3000"
Publish events
Use the Node SDK to forward events with a provider-specific event_type and a normalized payload (optionally enriched with _meta). For polling or scheduled syncs, checkpoint progress so retries do not lose updates or repeatedly publish the entire dataset. Make processing idempotent where possible.
Receive provider webhooks
Configure the provider to call the webhook route implemented by your connector. For example:
POST /webhooks/<provider>
Adspire installation lifecycle and actions
If users install the connector through Adspire or Adspire needs to invoke connector actions, use the connector HTTP contract. It describes install and uninstall lifecycle handling, signed requests, OAuth completion, and read-only query actions.
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 Adspire credentials or provider signature
- 502: backend unreachable or returned non-2xx