Features
Integrations
OpsDash connects to external tools through API keys, outbound webhooks, and Gmail sync. The Lead Capture API accepts inbound data from any source without requiring an SDK.
API keys
Plate: Full Loop (platform:api-access). Manage at Workspace Admin → Integrations → API Keys (admin/owner only).
- Keys are stored as SHA-256 hashes — the raw key is shown only once at creation
- Each key is org-scoped and tracks
usage_countandlast_used_at - Keys can be revoked at any time (takes effect immediately)
Creating a key: Workspace Admin → Integrations → API Keys → Generate New API Key → copy it (not shown again). Send it in an Authorization: Bearer {KEY} or X-Api-Key: {KEY} header.
Using the key:
curl -X POST https://your-app.com/api/lead-capture \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Jane Doe",
"email": "jane@example.com",
"company": "Acme Corp",
"source": "zapier"
}'Lead Capture API (inbound)
Plate: Sales (platform:lead-capture-api). Endpoint: POST /api/lead-capture
{
name: string; // Required
email: string; // Required
company?: string;
phone?: string;
source?: string; // zapier, make, n8n, resend, etc.
notes?: string;
}Response: { "success": true, "leadId": "uuid" }
| Status | Meaning |
|---|---|
| 401 | Missing or invalid API key |
| 400 | Validation error in request body |
| 429 | Monthly API quota exceeded |
| 500 | Server error |
Zapier / n8n / Make: Use a Webhooks/HTTP Request node — POST to the endpoint with Authorization: Bearer YOUR_KEY and the JSON body above.
Outbound webhooks
Plate: Full Loop (platform:webhooks). Manage at Workspace Admin → Integrations → Webhooks (admin/owner only).
Events sent: lead created, lead converted, deal stage changed, deal won, deal lost, contact created.
Payload format:
{
"event": "deal.won",
"org_id": "uuid",
"timestamp": "2026-03-24T12:00:00Z",
"data": { "deal_id": "uuid", "title": "...", "value": 15000 }
}Signature verification: When a webhook secret is set, OpsDash adds an X-OpsDash-Signature header:
const expected = `sha256=${createHmac('sha256', secret).update(rawBody).digest('hex')}`;
const isValid = timingSafeEqual(Buffer.from(expected), Buffer.from(received));Gmail integration
Plate: Sales (crm:email-integration). Manage at Settings → Account → Email & Calendar.
Syncs emails from a connected Gmail account into CRM Activities, giving the team a complete interaction history without manual logging.
Setup flow:
- Settings → Account → Email & Calendar → Connect Gmail
- Authorize OpsDash. Gmail scopes:
gmail.readonly,gmail.send,userinfo.email - After authorization, click Sync Now or let the cron sync automatically
What gets synced: Emails to/from CRM contact email addresses are matched and linked. Each matched email creates an activity of type email. Emails already synced are not re-imported (tracked by Gmail messageId).
| Item | Detail |
|---|---|
| OAuth scopes | gmail.readonly + gmail.send + userinfo.email |
| Server actions | getGmailAuthUrl, saveGmailTokens (email-integrations-gmail.ts); syncGmailToActivities, syncEmailIntegration (email-integrations.ts) |
| Callback | /api/integrations/gmail/callback |
| Cron | GET /api/cron/email-sync (every 15 min, Gmail + Outlook) |
| Token storage | Supabase Vault (migration 00181); row holds access_secret_id / refresh_secret_id |
| Migration | 00019_email_calendar_integrations.sql (email_calendar_integrations table); 00181 moves OAuth tokens into Vault |