ReferenceHTTP API & webhooks

Selda HTTP API & Webhooks

All Selda HTTP routes are defined in convex/http.ts and served from the Convex deployment’s .convex.site host:

EnvironmentBase URL
Production (brave-buzzard-349)https://brave-buzzard-349.eu-west-1.convex.site
Staging (accurate-okapi-584)the staging deployment’s .convex.site host
Local dev (nautical-pika-194)the dev deployment’s .convex.site host

Most of these endpoints are not meant to be called by application clients. They are server-to-server webhooks, OAuth callbacks, or public assets used by the tracking script. They are documented here for operational reference. The browser app talks to Convex over the normal query/mutation/action channel, not these routes.


Inbound webhooks (server-to-server)

These are called by external services. Each verifies a signature or shared secret; none should be called by a client.

RouteMethodAuthPurpose
/stripe-webhookPOSTStripe signature (stripe-signature header, verified in stripeActions.handleStripeWebhook)Subscription / checkout / billing events.
/clerk-webhookPOSTSvix signature (CLERK_WEBHOOK_SECRET)Sync Clerk users / orgs / memberships into Convex.
/api/mailgun/webhookPOSTMailgun HMAC-SHA256 signatureInbound email (legacy path).
/api/mailgun/wildcard-webhookPOSTMailgun HMAC-SHA256 signatureInbound email for wildcard subdomains (same handler).
/mailgun/inboundPOSTMailgun HMAC-SHA256 signatureInbound email (preferred path).
/mailgun/trackingPOSTMailgun HMAC-SHA256 signature requiredDelivery / open / click / bounce event webhooks.
/unipile/webhookPOSTProvider event payload (no signature)LinkedIn / email events via Unipile (account connected, message received).
/slack-support-eventsPOSTSlack signing secret (SLACK_SIGNING_SECRET)Support thread replies in Slack → in-app inbox.

Mailgun signature hardening

Both Mailgun handlers (convex/mailgunWebhookHttp.ts) verify an HMAC-SHA256 signature over timestamp + token using MAILGUN_WEBHOOK_SIGNING_KEY (falling back to MAILGUN_API_KEY), with a constant-time comparison. When signature fields are present and invalid, the request is rejected with 403. This closes the prior gap where unsigned inbound mail / tracking events could be spoofed. Inbound mail carrying the X-Selda-Warmup header is treated as closed-loop warmup: it triggers an auto-reply but is not persisted to the Sales Inbox.


OAuth callbacks (GET)

Redirect targets registered with the provider; reached via the user’s browser during an OAuth grant, not called directly.

RouteMethodPurpose
/google-calendar-callbackGETGoogle Calendar OAuth callback (seldaIntegrations).
/google-analytics-callbackGETGoogle Analytics OAuth callback (seldaIntegrations).

Tracking (public)

Public endpoints used by the Selda tracking pixel/script served from track.selda.ai. CORS-open because they run on customer domains. No auth, so they ingest anonymous/lead events only.

RouteMethodPurpose
/track/pixel.gifGETTracking pixel (tracking/pixel.ts).
/track/script.jsGETTracking script (tracking/pixel.ts).
/api/eventsPOST / OPTIONSEvent ingestion from the browser script (public/s.js).
/api/track-timePOST / OPTIONSTime-on-page beacon from preview//for pages (payload capped at 4 KB).
/t/...GETTracking-link redirect: logs the click, then 302 to the original URL (tracking/httpRedirect.ts).
/p/...GET / POSTPersonalized page render + CTA/time tracking (personalizedPages/serve.ts).

Admin / internal

Require the SELDA_INTERNAL_API_KEY shared secret. Not for client use.

RouteMethodAuthPurpose
/admin/waitlist-campaignPOSTx-selda-internal-key header must equal SELDA_INTERNAL_API_KEY (else 401)Seed + schedule a waitlist email campaign.
/waitlist/welcomePOSTInternal (waitlist welcome action)Trigger a waitlist welcome email.

Public app actions

RouteMethodAuthPurpose
/ambassador-applyPOST / OPTIONSPublic (CORS-open, called from selda.ai/ambassador)Submit an ambassador application → Slack alert.
/vault/uploadPOSTAuthenticated (Clerk identity required; 401 otherwise)Raw file upload into the Selda Vault.

MCP API

RouteMethodAuthPurpose
/mcp/queryPOST / OPTIONSAPI key Bearer token (read scope)MCP read operations.
/mcp/mutatePOST / OPTIONSAPI key Bearer token (write scope)MCP write operations.
/mcp/runPOST / OPTIONSAPI key Bearer token (pipeline scope)MCP pipeline / engine actions.

These are the external-AI access layer. Auth, scopes, org isolation, and the tool set are documented in mcp.md.


Security notes

  • Mailgun inbound + tracking now require a valid HMAC signature and reject spoofed/invalid requests with 403.
  • Cross-org / console access on the function layer was locked down: MCP keys cannot self-grant the admin scope, and every org-scoped function re-checks ownership server-side. See the internal security notes.
  • Never log or print secret values (*_API_KEY, *_WEBHOOK_SECRET, signing keys, sk_live_… keys). They live only in the Convex Dashboard environment.

Source files

FileRoutes
convex/http.tsRouter, all routes above
convex/stripeWebhookHttp.ts/stripe-webhook
convex/clerkWebhookHttp.ts/clerk-webhook
convex/mailgunWebhookHttp.ts/api/mailgun/webhook, /api/mailgun/wildcard-webhook, /mailgun/inbound, /mailgun/tracking
convex/linkedin/webhookHttp.ts/unipile/webhook
convex/supportSlackHttp.ts/slack-support-events
convex/seldaIntegrations.ts/google-calendar-callback, /google-analytics-callback
convex/trackingHttp.ts/api/events, /api/track-time
convex/tracking/pixel.ts/track/pixel.gif, /track/script.js
convex/tracking/httpRedirect.ts/t/...
convex/personalizedPages/serve.ts/p/...
convex/admin/waitlistCampaignHttp.ts/admin/waitlist-campaign
convex/waitlistActions.ts/waitlist/welcome
convex/ambassadorHttp.ts/ambassador-apply
convex/vault/httpActions.ts/vault/upload
convex/mcpApi.ts/mcp/query, /mcp/mutate, /mcp/run