Skip to main content

Connector Registration and HTTP Contract

All connectors follow the same registration and approval process. Developers register connectors through the Adspire partner portal. While a connector definition is pending review, it is visible in the registering developer's workspace; after Adspire approves it, it becomes available to users generally.

This guide describes the HTTP contract for a connector service. A connector may publish events, synchronize data, receive provider webhooks, or expose actions; these are implementation patterns, not separate connector registration types. See Connector Development Patterns for an overview.

Public base URL and routes​

Register a stable HTTPS base URL for the connector, for example https://connector.example.com. Adspire appends the paths below to this URL. Do not register a URL that includes a route path.

RouteRequiredCallerPurpose
GET /healthzYesAdspireReadiness/connectivity check. Return a 2xx response when the service can accept requests.
POST /installYesAdspireStart an installation and return its opaque hash; for OAuth, also return auth_url.
POST /uninstallYesAdspireRevoke provider credentials and remove connector-owned data for an installation.
POST /actions/queryIf the manifest declares a read_only_query actionAdspireExecute the declared read-only query and return JSON results.
Provider OAuth callback route(s)If OAuth is usedProvider/browserComplete authorization and report the installation result to Adspire.
Provider webhook route(s)If webhooks are usedProviderVerify and process provider events. Use the exact route configured with the provider.

All Adspire-to-connector requests, including health checks and GET requests, are authenticated. Provider callbacks and webhooks use the provider's own OAuth/signature verification, not Adspire request authentication. The connector must make its callback and webhook URLs publicly reachable over HTTPS; the lifecycle routes must also be reachable by Adspire.

/metrics is useful operationally but is not part of the connector contract. Protect it or restrict it at the network layer if enabled.

Publishing a product catalog​

For connectors using the brand graph events API, brand.offerings.updated represents a complete replacement of the brand's product catalog. Fetch every provider page and send the entire catalog in one event; never send separate pages or chunks as independent updates. Send an explicit empty product_catalog array when the store has no products, but do not publish a partial catalog after a failed or invalid provider response.

The current event schema requires service_offerings and projects arrays; the catalog consumer ignores these fields and preserves existing services, projects and sub-brands. Use the other graph update paths to change those fields. Use the installation's brand API key server-side. A successful response means the event was published, not that graph processing has completed.

The full event must fit the deployed HTTP and Kafka message limits. Surface oversize and publication failures for retry or operator action; do not silently split the event. Overlapping syncs are not ordered and can overwrite newer catalogs. The catalog is brand-wide rather than store-scoped, so different stores connected to the same brand can overwrite one another.

Authenticate Adspire requests​

Partner connectors receive a per-definition signing secret during connector registration. Adspire does not send its internal API key to partner connectors. Verify the following headers on every request from Adspire:

  • X-Adspire-Timestamp: Unix timestamp in seconds.
  • X-Adspire-Signature: lowercase hexadecimal HMAC-SHA256.

The signature is calculated over the exact request bytes:

HMAC-SHA256(signing_secret, timestamp + "." + raw_request_body)

For GET /healthz, the body is empty, so the signed message is timestamp + ".". Reject missing or malformed headers, timestamps more than five minutes from the current time, and invalid signatures. Compare signatures in constant time. Capture the raw body before JSON parsing; parsing and re-serializing JSON can change the bytes and invalidate verification.

Example verification helper in Node.js:

import crypto from "node:crypto";

function isAuthenticAdspireRequest(
timestamp: string | undefined,
signature: string | undefined,
rawBody: Buffer,
signingSecret: string,
): boolean {
if (!timestamp || !/^\d+$/.test(timestamp) || !signature || !/^[a-f0-9]{64}$/i.test(signature)) {
return false;
}
if (Math.abs(Math.floor(Date.now() / 1000) - Number(timestamp)) > 300) {
return false;
}

const expected = crypto
.createHmac("sha256", signingSecret)
.update(timestamp)
.update(".")
.update(rawBody)
.digest();
const received = Buffer.from(signature, "hex");
return received.length === expected.length && crypto.timingSafeEqual(received, expected);
}

Keep the signing secret server-side. Never include it in the install response, browser redirects, logs, or provider requests. This guide describes the partner connector contract; use only the signing secret issued for your connector definition.

Start an installation​

Adspire calls POST /install with JSON. The common fields are:

FieldMeaning
brand_id or workspace_idThe tenant scope selected for this connector. Brand-scoped connectors receive brand_id; workspace-scoped connectors receive workspace_id.
brand_api_keyPer-installation Adspire API key. Keep it with the installation and use it only for the authorized Adspire API operations.
adspire_return_urlBrowser return destination after authorization. Treat it as a runtime value; do not hard-code an Adspire frontend URL.
adspire_complete_urlAdspire callback URL for reporting installation success or failure. Treat it as a runtime value; do not hard-code the endpoint.
Configured store fieldThe manifest's store_field (store_url, shop, or none) determines the provider/store identifier, if any.
Declared install fieldsOptional values from the connector's install_fields, forwarded from installation config.

Return a successful JSON response with a connector-generated opaque hash that uniquely identifies the pending installation. For OAuth, include the provider authorization URL:

{
"hash": "connector-generated-install-id",
"auth_url": "https://provider.example/oauth/authorize?..."
}

After a user starts an installation, Adspire records that installation as pending and redirects the user to auth_url. This installation state is separate from the connector definition's review status. Do not persist usable provider credentials until authorization succeeds. Bind the state to the install, validate it on callback, and retain the install ID and callback URLs securely through the OAuth round trip.

For a non-OAuth installation, return a hash and report completion after setup succeeds. Report failure if setup cannot be completed. Return appropriate 4xx responses for invalid install input; avoid returning a success response without a usable hash.

Report installation completion​

After setup succeeds or fails, send a JSON POST to the exact adspire_complete_url received in the install request. Sign this outgoing request with the connector's per-definition signing secret using the same timestamp-and-raw-body algorithm and header names described above.

Success:

{
"hash": "connector-generated-install-id",
"status": "active",
"store_url": "https://store.example"
}

Failure:

{
"hash": "connector-generated-install-id",
"status": "failed"
}

store_url is optional, as is a metadata object. Retry transient network and server errors with bounded backoff; the completion report is idempotent. Return the user's browser to the supplied adspire_return_url after OAuth, with an appropriate success/failure status. Do not send provider access tokens or secrets in the browser redirect.

Uninstall​

Adspire calls POST /uninstall with the installation identifier and available tenant context:

{
"install_id": "connector-generated-install-id",
"brand_id": 123
}

Workspace-scoped installations receive workspace_id instead of brand_id. Treat uninstall as idempotent: revoke or delete the provider credentials and connector-owned data for that install, and return a 2xx JSON response such as {"uninstalled": 1}. Adspire owns its installation record and status; the connector should only clean up its own data.

Read-only queries​

If the connector declares read_only_query actions in ui_actions, implement POST /actions/query. Adspire sends:

{
"action_id": "provider.list_resources",
"install_id": "connector-generated-install-id",
"brand_id": 123,
"workspace_id": null,
"config": {},
"context": {
"query": "optional search text",
"page_size": 25,
"page_token": "optional cursor"
}
}

Tenant fields may be null when they do not apply. Validate the action ID against the connector's declared actions, load credentials by install_id, enforce the action's read-only scope, and validate context before calling the provider. Return the query result as JSON. For resource pickers, describe item fields, filters, pagination, and navigation/context keys in manifest metadata rather than adding provider-specific frontend code. Do not expose a general provider API proxy.

Adspire also exposes a user-facing query endpoint; the connector itself only implements /actions/query. Adspire enforces installation access and action eligibility before forwarding the request.

Provider callbacks and webhooks​

OAuth callback paths are connector-owned and must match the redirect URI registered with the provider. Verify the provider's callback signature/state and use only values carried in validated state for install routing. The callback completes the provider code exchange, stores credentials securely, and calls the supplied adspire_complete_url.

Webhook paths are also connector-owned. Configure the provider to call the public HTTPS URL, verify signatures against the original request bytes, validate payloads, and make retries/deduplication safe. Acknowledge successful deliveries promptly. If processing cannot complete synchronously, persist work durably before acknowledging it.

Keep provider credentials in connector-owned storage, encrypted at rest where persisted. Do not put credentials in the Adspire manifest, install response, logs, webhook payloads, or browser URLs. Use the per-install brand_api_key only for the intended Adspire operations and never reuse it across installations.

Manifest and registration​

Register the connector with:

  • A unique lowercase slug, display name, and HTTPS base_url.
  • scope: brand or workspace.
  • store_field: store_url, shop, or none.
  • auth_type, capabilities, events, and declared install_fields.
  • ui_actions for each action the connector exposes, including its kind and any resource-picker metadata.

The base_url must be allowed by Adspire's connector URL policy. Use manifest metadata to describe configuration and capabilities; do not add provider-specific routes or frontend components to Adspire for a marketplace integration.

Operations and failure behavior​

  • Serve readiness at GET /healthz; do not include credentials or internal diagnostics in its response.
  • Use stable HTTPS URLs in production and configure reverse proxies to preserve the external scheme and any required path prefix.
  • Set bounded request and provider timeouts. Adspire expects connector calls to complete promptly; move lengthy syncs to durable background work.
  • Use structured logs, but redact signing secrets, provider credentials, and per-install API keys.
  • Make installs, callbacks, webhooks, completion reports, queries, and uninstalls safe to retry where applicable.
  • Return non-2xx responses for failed requests instead of success-shaped error payloads. Do not expose stack traces or provider secrets in responses.