Status: PR-Ready Concept
Author: Integration Design Task
Date: 2026-02-06
Connect is a first-party integrations platform for the Rust NYC Talk Submission System. It provides a unified, secure interface for external tools to access talk data, receive real-time notifications, and export schedulesโenabling organizers to build custom workflows without modifying the core system.
| Field | Description |
|---|---|
key_id | Public identifier (prefix: rnc_) |
key_hash | bcrypt hash of secret portion |
name | Human-readable label |
scopes | Permission array: ["submissions:read", "webhooks:manage", "exports:create"] |
created_by | Discord user ID of creator |
expires_at | Optional expiration timestamp |
last_used_at | Audit timestamp |
Key format: rnc_live_<24-char-random> (production) / rnc_test_<24-char-random> (test mode)
Schema addition (backend/index.ts, new table):
CREATE TABLE IF NOT EXISTS api_keys_1 (
id INTEGER PRIMARY KEY AUTOINCREMENT,
key_id TEXT UNIQUE NOT NULL,
key_hash TEXT NOT NULL,
name TEXT NOT NULL,
scopes TEXT NOT NULL, -- JSON array
created_by TEXT NOT NULL,
expires_at DATETIME,
last_used_at DATETIME,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
| Field | Description |
|---|---|
id | Auto-increment ID |
url | HTTPS endpoint (must be TLS) |
events | Array: ["submission.created", "submission.updated", "talk.scheduled"] |
secret | HMAC-SHA256 signing secret |
api_key_id | Owning API key |
active | Boolean toggle |
failure_count | Consecutive failures (disable at 10) |
Webhook payload structure:
{ "event": "submission.created", "timestamp": "2026-02-06T13:57:40Z", "data": { /* TalkSubmission object */ }, "signature": "sha256=..." }
Schema addition:
CREATE TABLE IF NOT EXISTS webhooks_1 (
id INTEGER PRIMARY KEY AUTOINCREMENT,
url TEXT NOT NULL,
events TEXT NOT NULL, -- JSON array
secret TEXT NOT NULL,
api_key_id INTEGER REFERENCES api_keys_1(id),
active BOOLEAN DEFAULT true,
failure_count INTEGER DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
All endpoints require Authorization: Bearer rnc_live_... header.
| Endpoint | Scope | Description |
|---|---|---|
GET /api/v1/submissions | submissions:read | List all submissions with pagination |
GET /api/v1/submissions/:id | submissions:read | Get single submission |
GET /api/v1/schedule | submissions:read | Get scheduled talks with dates |
| Endpoint | Scope | Format |
|---|---|---|
GET /api/v1/exports/submissions.csv | exports:create | CSV download |
GET /api/v1/exports/submissions.json | exports:create | JSON array |
GET /api/v1/exports/schedule.ics | exports:create | iCalendar feed |
CSV columns: id,speaker_name,talk_context,is_on_behalf,submitter_name,discord_channel_id,created_at,scheduled_date
| Endpoint | Scope | Description |
|---|---|---|
POST /api/v1/webhooks | webhooks:manage | Register new webhook |
GET /api/v1/webhooks | webhooks:manage | List webhooks |
DELETE /api/v1/webhooks/:id | webhooks:manage | Remove webhook |
POST /api/v1/webhooks/:id/test | webhooks:manage | Send test event |
// Zapier/Make webhook handler
app.post("/api/webhooks/notion-sync", async (c) => {
const payload = await c.req.json();
if (payload.event === "submission.created") {
await notionClient.pages.create({
parent: { database_id: NOTION_DB_ID },
properties: {
"Speaker": { title: [{ text: { content: payload.data.speaker_name }}] },
"Context": { rich_text: [{ text: { content: payload.data.talk_context }}] },
"Discord": { url: payload.data.discord_invite_link },
"Status": { select: { name: "New" } }
}
});
}
});
# Cron job or manual trigger curl -H "Authorization: Bearer rnc_live_..." \ "https://rustnyc-talks.val.run/api/v1/exports/submissions.csv" \ | google-sheets-append --spreadsheet-id=... --sheet="Submissions"
<!-- Subscribe to iCal feed --> <a href="webcal://rustnyc-talks.val.run/api/v1/exports/schedule.ics?key=rnc_live_..."> Add to Calendar </a>
// Webhook endpoint receives events
async function handleWebhook(event: WebhookPayload) {
if (event.event === "talk.scheduled") {
await slack.chat.postMessage({
channel: "#rust-nyc-talks",
text: `๐
"${event.data.speaker_name}" scheduled for ${event.data.scheduled_date}!`,
unfurl_links: false
});
}
}
Authorization: Bearer headerkey_hashexpires_at < NOW()X-Signature: sha256=HMAC(secret, rawBody)X-Timestamp for replay protection (5-min window)| Scope | Limit |
|---|---|
| Per API key | 100 req/min |
| Webhook deliveries | 10 req/sec per endpoint |
| Export endpoints | 10 req/hour |
CREATE TABLE IF NOT EXISTS api_audit_log_1 (
id INTEGER PRIMARY KEY AUTOINCREMENT,
api_key_id INTEGER,
endpoint TEXT NOT NULL,
method TEXT NOT NULL,
status_code INTEGER,
ip_address TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
| Metric | Target (90 days) | Measurement |
|---|---|---|
| API keys created | 10+ | COUNT in api_keys_1 |
| Webhooks registered | 5+ | COUNT in webhooks_1 |
| Export downloads/week | 20+ | Audit log COUNT |
| Webhook delivery success rate | >99% | Failures / Total |
| P95 API latency | <200ms | Timing middleware |
| Integration partners mentioned | 2+ | Manual tracking |
backend/middleware/auth.ts)GET /api/v1/submissions with API key authscheduled_date column)backend/index.ts:70-90)File: shared/types.ts (add after line 24)
// --- Integrations Platform Types ---
export type ApiKeyScope = "submissions:read" | "webhooks:manage" | "exports:create";
export interface ApiKey {
id: number;
key_id: string; // e.g., "rnc_live_abc123..."
name: string;
scopes: ApiKeyScope[];
created_by: string;
expires_at?: string;
last_used_at?: string;
created_at: string;
}
export type WebhookEvent = "submission.created" | "submission.updated" | "talk.scheduled";
export interface Webhook {
id: number;
url: string;
events: WebhookEvent[];
api_key_id: number;
active: boolean;
failure_count: number;
created_at: string;
}
export interface WebhookPayload<T = TalkSubmission> {
event: WebhookEvent;
timestamp: string;
data: T;
signature: string;
}
export interface ExportOptions {
format: "csv" | "json" | "ics";
since?: string; // ISO date filter
status?: "pending" | "scheduled" | "all";
}
Rust NYC Launches "Connect" โ Open API for Community Tool Integrations
Organizers can now sync talks to Notion, export to Sheets, and receive real-time Slack notifications
"We used to manually copy submissions into Notion every week. Now they appear automatically within seconds."
https://rustnyc-talks.val.run/docs/apiJoin Rust NYC Discord to request an API key and start building integrations today.
scheduled_date column to talk_submissions_3 (required for calendar feed)/docs/api page with OpenAPI spec or Markdown