Integrations API

Send a rich context handoff to create a Build for one of your users, and receive signed webhook events as they progress. A partner is configured, not coded.

Overview

The platform is multi-tenant and generic. You authenticate with an API key + HMAC signature, POST a context handoff to create a Build, and subscribe to webhook events. Examples use the placeholder slug {partner_slug}.

Quickstart

  1. Get your API key and signing secret from the Edirae integrations team.
  2. Register a webhook URL and subscribe to events.
  3. POST a signed handoff to /builds/create.
  4. Redirect your user to the returned redirect_url.
  5. Handle webhook events at your endpoint.

Authentication

Every authenticated request requires three headers:

  • Authorization: Bearer {api_key}
  • X-Partner-Signature: {hmac_sha256(body, signing_secret)}
  • X-Partner-Timestamp: {unix_ts} — within 5 minutes of server time
curl
# Sign the request body with your signing secret (HMAC-SHA256)
BODY='{"build_type":"interview_build", ... }'
TS=$(date +%s)
SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$EDIRAE_SIGNING_SECRET" | awk '{print $2}')

curl -X POST https://api.edirae.com/api/v1/integrations/{partner_slug}/builds/create \
  -H "Authorization: Bearer $EDIRAE_API_KEY" \
  -H "X-Partner-Signature: $SIG" \
  -H "X-Partner-Timestamp: $TS" \
  -H "Content-Type: application/json" \
  -d "$BODY"
Node.js
import crypto from "crypto";

const body = JSON.stringify(payload);
const ts = Math.floor(Date.now() / 1000).toString();
const signature = crypto
  .createHmac("sha256", process.env.EDIRAE_SIGNING_SECRET)
  .update(body)
  .digest("hex");

await fetch(`https://api.edirae.com/api/v1/integrations/${slug}/builds/create`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.EDIRAE_API_KEY}`,
    "X-Partner-Signature": signature,
    "X-Partner-Timestamp": ts,
    "Content-Type": "application/json",
  },
  body,
});
Python
import hmac, hashlib, time, json, requests

body = json.dumps(payload)
ts = str(int(time.time()))
signature = hmac.new(
    SIGNING_SECRET.encode(), body.encode(), hashlib.sha256
).hexdigest()

requests.post(
    f"https://api.edirae.com/api/v1/integrations/{slug}/builds/create",
    data=body,
    headers={
        "Authorization": f"Bearer {API_KEY}",
        "X-Partner-Signature": signature,
        "X-Partner-Timestamp": ts,
        "Content-Type": "application/json",
    },
)
PHP
<?php
$body = json_encode($payload);
$ts = (string) time();
$signature = hash_hmac('sha256', $body, $signingSecret);

$ch = curl_init("https://api.edirae.com/api/v1/integrations/{$slug}/builds/create");
curl_setopt_array($ch, [
  CURLOPT_POST => true,
  CURLOPT_POSTFIELDS => $body,
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => [
    "Authorization: Bearer {$apiKey}",
    "X-Partner-Signature: {$signature}",
    "X-Partner-Timestamp: {$ts}",
    "Content-Type: application/json",
  ],
]);
$response = curl_exec($ch);

Create a Build

POST /api/v1/integrations/{partner_slug}/builds/create

Request body
{
  "build_type": "interview_build",
  "referral_token": "partner_user_xxx",
  "applicant": {
    "full_name": "Jordan Rivera",
    "email": "jordan@example.com",
    "consent": { "basics": true, "cv": true, "cover_letter": false }
  },
  "build_context": {
    "company_name": "Acme",
    "role": "Senior Backend Engineer",
    "jd_text": "...",
    "interview_date": "2026-07-15"
  },
  "candidate_context": { "cv_text": "..." },
  "metadata": { "your_application_id": "..." }
}
Response
201 Created
{
  "success": true,
  "data": {
    "build_id": "8b1c…",
    "user_id": 4021,
    "build_type": "interview_build",
    "redirect_url": "https://app.edirae.com/build/8b1c…?handoff_token=…",
    "ready_at": "2026-06-20T10:00:30Z"
  }
}

The redirect_url includes a short-lived token that logs the user straight into their new Build. Unconsented sensitive fields are dropped server-side; the handoff still succeeds.

Webhooks

Events are POSTed to your webhook URL, signed with your signing secret in the X-Edirae-Signature header (sha256=…). Respond 200 within 5 seconds. Failed deliveries retry at 1m, 5m, 30m, 2h, 12h and are abandoned after 5 attempts. Recover missed events via GET /events?since={unix_ts}.

Verify (Node.js)
import crypto from "crypto";

function verify(rawBody, header, signingSecret) {
  const expected = "sha256=" + crypto
    .createHmac("sha256", signingSecret)
    .update(rawBody)
    .digest("hex");
  return crypto.timingSafeEqual(Buffer.from(header), Buffer.from(expected));
}

// Express: use the RAW body, not the parsed JSON.
app.post("/webhooks/edirae", express.raw({ type: "*/*" }), (req, res) => {
  if (!verify(req.body, req.header("X-Edirae-Signature"), SECRET)) {
    return res.sendStatus(401);
  }
  const event = JSON.parse(req.body.toString());
  res.sendStatus(200);
});
Sample payload
{
  "event": "build.created",
  "delivery_id": 91823,
  "created_at": "2026-06-20T10:00:30Z",
  "data": { "build_id": "8b1c…", "build_type": "interview_build" }
}

Event reference

EventWhen it fires
build.createdA handoff successfully generated a Build.
build.first_engagementThe user opened the Build for the first time.
readiness.crossed_threshold(interview) readiness score crossed 25/50/75/90.
mock_session.completed(interview) a mock interview round finished.
interview.24h_warning(interview) 24h before the interview date.
interview.past(interview) the interview date has passed.
outcome.recorded(interview) a post-mortem outcome was submitted.
build.completed(learning) mastery was reached.
consent.revokedThe user revoked one or more shared data types.
data.deletedData sourced from your integration was deleted.

Build types

Each handoff names a build_type with its own build_context schema:

  • interview_build — company, role, JD, optional CV/cover letter, interview date.
  • learning_build — a topic, optional description, category and difficulty.

Need a different build type? Ask the integrations team to register one — no API changes for you.

Rate limits & errors

  • POST /builds/create: 60 / minute
  • GET endpoints: 600 / minute
  • On breach: 429 Too Many Requests with a Retry-After header.

Errors return { "success": false, "message": "…", "error": "code" }. Common codes: invalid_api_key, invalid_signature, insufficient_scope, partner_inactive, rate_limited.