EDORA

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. 1) Configure env vars

    In EDORA (Vercel env): EDORA_WEBHOOK_SECRET, YWB_API_BASE=https://youthworkbench.net/api, and your ADMIN_TOKEN (for protected writes).

  2. 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. 3) Pull live opportunities

    EDORA reads /api/opportunities from YWB to surface fits inside case workflows.

  4. 4) Test the flow

    Send a sample webhook (below). Verify the event appears on your case timeline.

Youth Workbench
Opportunities • Mentors • Tracks
➡️
EDORA
Cases • Tasks • Milestones
➡️
Outcomes
Placements • Credits • Growth
JSON over HTTPS. Webhooks signed with HMAC-SHA256. Consent-first field selection (no PII without explicit opt-in).

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.