Integrations
Connect Youth Workbench ↔ EDORA Cases
YWB is the opportunity engine. EDORA is the case engine. Together they turn workshops, apprenticeships, and mentor matches into guided tasks, milestones, and outcomes — with dignity and consent first.
Quick Start
1) Configure env vars
In EDORA (Vercel env):
EDORA_WEBHOOK_SECRET
,YWB_API_BASE=https://youthworkbench.net/api
, and yourADMIN_TOKEN
(for protected writes).2) Set your EDORA webhook URL in YWB
https://edoracases.com/api/integrations/ywb/webhook
(adjust for your domain). YWB will sign events with HMAC-SHA256.3) Pull live opportunities
EDORA reads
/api/opportunities
from YWB to surface fits inside case workflows.4) Test the flow
Send a sample webhook (below). Verify the event appears on your case timeline.
Endpoints
YWB → EDORA (Webhook)
EDORA receives signed events at:POST /api/integrations/ywb/webhook
- Header:
X-YWB-Signature: sha256=HEX
- Replay protection:
X-YWB-Timestamp
(5 min window) - Content-Type:
application/json
// Example payload (opportunity.published)
{
"id": "evt_123",
"type": "opportunity.published",
"createdAt": "2025-01-01T18:22:00Z",
"data": {
"opportunityId": "opp_AR_00042",
"title": "Apprenticeship: Entry Web Dev",
"state": "AR",
"paid": true,
"deadline": "2025-02-15",
"applyUrl": "https://youthworkbench.net/opportunities/opp_AR_00042"
}
}
EDORA → YWB (Read)
Pull current opportunities and filter in-app:
GET https://youthworkbench.net/api/opportunities?state=AR&paid=true
Response (trimmed):
[
{
"id": "opp_AR_00042",
"title": "Apprenticeship: Entry Web Dev",
"orgName": "Arkansas Beta Lab",
"state": "AR",
"paid": true,
"type": "Apprenticeship",
"summary": "Four-week on-ramp with mentor match and stipend.",
"applyUrl": "https://youthworkbench.net/opportunities/opp_AR_00042",
"deadline": "2025-02-15",
"createdAt": "2025-01-01T17:20:00Z",
"updatedAt": "2025-01-01T17:20:00Z"
}
]
Verifying Webhook Signatures
EDORA should verify the HMAC using EDORA_WEBHOOK_SECRET
and the raw request body (no JSON reformatting).
// app/api/integrations/ywb/webhook/verify.ts (example)
import crypto from "node:crypto";
export function verifySignature(rawBody: string, signatureHeader: string | null, secret: string) {
if (!signatureHeader) return false;
const expected = crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
const provided = signatureHeader.replace(/^sha256=/, "");
// constant-time compare
const a = Buffer.from(expected, "hex");
const b = Buffer.from(provided, "hex");
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
Local test (send a sample event):
curl -X POST "http://localhost:3000/api/integrations/ywb/webhook" \
-H "Content-Type: application/json" \
-H "X-YWB-Timestamp: $(date -u +%s)" \
-H "X-YWB-Signature: sha256=$(printf '%s' '{"type":"opportunity.published","data":{"opportunityId":"opp_AR_00042"}}' | openssl dgst -sha256 -hmac "$EDORA_WEBHOOK_SECRET" -binary | xxd -p -c 256)" \
-d '{"type":"opportunity.published","data":{"opportunityId":"opp_AR_00042"}}'
Consent & Privacy
- Only minimal fields flow from YWB → EDORA (no PII without explicit consent).
- Audit headers on every event:
X-YWB-EventId
,X-YWB-Signature
,X-YWB-Timestamp
. - Right to be forgotten: requests remove references in EDORA and stop future syncs.
Need a hand?
We’ll pair your environment variables, set the webhook, and test the flow end-to-end.