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
- Get your API key and signing secret from the Edirae integrations team.
- Register a webhook URL and subscribe to events.
- POST a signed handoff to
/builds/create. - Redirect your user to the returned
redirect_url. - 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
# 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"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,
});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
$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
{
"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": "..." }
}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}.
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);
});{
"event": "build.created",
"delivery_id": 91823,
"created_at": "2026-06-20T10:00:30Z",
"data": { "build_id": "8b1c…", "build_type": "interview_build" }
}Event reference
| Event | When it fires |
|---|---|
| build.created | A handoff successfully generated a Build. |
| build.first_engagement | The 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.revoked | The user revoked one or more shared data types. |
| data.deleted | Data 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.
Consent & privacy
Send per-data-type consent flags in applicant.consent. Without consent for a sensitive field, it is dropped (the handoff still succeeds). Users can revoke any data type anytime, which fires consent.revoked. Mock answers, gate reasoning and mentor transcripts never leave Edirae.
Rate limits & errors
POST /builds/create: 60 / minute- GET endpoints: 600 / minute
- On breach:
429 Too Many Requestswith aRetry-Afterheader.
Errors return { "success": false, "message": "…", "error": "code" }. Common codes: invalid_api_key, invalid_signature, insufficient_scope, partner_inactive, rate_limited.