Selda HTTP API & Webhooks
All Selda HTTP routes are defined in convex/http.ts and served from the Convex
deployment’s .convex.site host:
| Environment | Base 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.
| Route | Method | Auth | Purpose |
|---|---|---|---|
/stripe-webhook | POST | Stripe signature (stripe-signature header, verified in stripeActions.handleStripeWebhook) | Subscription / checkout / billing events. |
/clerk-webhook | POST | Svix signature (CLERK_WEBHOOK_SECRET) | Sync Clerk users / orgs / memberships into Convex. |
/api/mailgun/webhook | POST | Mailgun HMAC-SHA256 signature | Inbound email (legacy path). |
/api/mailgun/wildcard-webhook | POST | Mailgun HMAC-SHA256 signature | Inbound email for wildcard subdomains (same handler). |
/mailgun/inbound | POST | Mailgun HMAC-SHA256 signature | Inbound email (preferred path). |
/mailgun/tracking | POST | Mailgun HMAC-SHA256 signature required | Delivery / open / click / bounce event webhooks. |
/unipile/webhook | POST | Provider event payload (no signature) | LinkedIn / email events via Unipile (account connected, message received). |
/slack-support-events | POST | Slack 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.
| Route | Method | Purpose |
|---|---|---|
/google-calendar-callback | GET | Google Calendar OAuth callback (seldaIntegrations). |
/google-analytics-callback | GET | Google 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.
| Route | Method | Purpose |
|---|---|---|
/track/pixel.gif | GET | Tracking pixel (tracking/pixel.ts). |
/track/script.js | GET | Tracking script (tracking/pixel.ts). |
/api/events | POST / OPTIONS | Event ingestion from the browser script (public/s.js). |
/api/track-time | POST / OPTIONS | Time-on-page beacon from preview//for pages (payload capped at 4 KB). |
/t/... | GET | Tracking-link redirect: logs the click, then 302 to the original URL (tracking/httpRedirect.ts). |
/p/... | GET / POST | Personalized page render + CTA/time tracking (personalizedPages/serve.ts). |
Admin / internal
Require the SELDA_INTERNAL_API_KEY shared secret. Not for client use.
| Route | Method | Auth | Purpose |
|---|---|---|---|
/admin/waitlist-campaign | POST | x-selda-internal-key header must equal SELDA_INTERNAL_API_KEY (else 401) | Seed + schedule a waitlist email campaign. |
/waitlist/welcome | POST | Internal (waitlist welcome action) | Trigger a waitlist welcome email. |
Public app actions
| Route | Method | Auth | Purpose |
|---|---|---|---|
/ambassador-apply | POST / OPTIONS | Public (CORS-open, called from selda.ai/ambassador) | Submit an ambassador application → Slack alert. |
/vault/upload | POST | Authenticated (Clerk identity required; 401 otherwise) | Raw file upload into the Selda Vault. |
MCP API
| Route | Method | Auth | Purpose |
|---|---|---|---|
/mcp/query | POST / OPTIONS | API key Bearer token (read scope) | MCP read operations. |
/mcp/mutate | POST / OPTIONS | API key Bearer token (write scope) | MCP write operations. |
/mcp/run | POST / OPTIONS | API 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
adminscope, 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
| File | Routes |
|---|---|
convex/http.ts | Router, 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 |