TalentSprout
API Reference

Webhooks

Receive real-time HTTP POST notifications when interviews are completed. Build powerful integrations and automate your hiring workflow.

Quick Start

1

Configure your webhook URL

Add a global URL in Settings → Global Interview Settings or set a per-interview override in the Settings tab.

2

Handle incoming requests

Your endpoint should return a 2xx status code within 10 seconds.

Events

interview.completed
Interview finished successfully (passed completion checks)
interview.abandoned
Interview ended early or failed completion checks

Webhooks are sent for all session types. Preview sessions include test: true so you can easily filter them: if (payload.test) return;

Payload

response.json
{
  "event": "interview.completed",
  "apiVersion": "2026-01-30",
  "timestamp": "2024-12-27T00:25:50.431Z",
  "data": {
    "interview": {
      "id": "c04f53d2",
      "title": "Senior Software Engineer",
      "companyName": "Acme Corp",
      "dashboardUrl": "https://talentsprout.ai/dashboard/interviews/c04f53d2"
    },
    "candidate": {
      "id": "6907d2602dab93276cdedb2a",
      "firstName": "John",
      "lastName": "Doe",
      "fullName": "John Doe",
      "email": "john.doe@example.com",
      "phone": "+1234567890",
      "externalUserId": "your_user_12345",
      "profileUrl": "https://talentsprout.ai/dashboard/interviews/c04f53d2/candidates/6907d2602dab93276cdedb2a",
      "shareUrl": "https://talentsprout.ai/share/interviews/c04f53d2/candidates/6907d2602dab93276cdedb2a"
    },
    "session": {
      "id": "694f268cd70bb7447a4e4d4e",
      "token": "3f54f82d...",
      "type": "candidate",
      "status": "completed",
      "startedAt": "2024-12-27T00:21:36.836Z",
      "completedAt": "2024-12-27T00:25:46.490Z",
      "durationMinutes": 4.16,
      "recordingUrl": "https://talentsprout.ai/media/recording/3f54f82d...",
      "thumbnailUrl": "https://talentsprout.ai/media/thumbnail/3f54f82d...",
      "resumeUrl": null
    },
    "validation": {
      "passed": true,
      "reason": "completed",
      "checkedAt": "2024-12-27T00:25:50.431Z"
    },
    "scores": {
      "overall": 85,
      "interview": 85,
      "resume": null
    },
    "evaluation": {
      "performanceSummary": "Strong candidate with excellent communication...",
      "performance": {
        "communicationSkills": { "name": "Communication Skills", "score": 88, "notes": "..." },
        "domainExpertise": { "name": "Domain Expertise", "score": 85, "notes": "..." },
        "problemSolving": { "name": "Problem Solving", "score": 82, "notes": "..." },
        "culturalFit": { "name": "Cultural Fit", "score": 90, "notes": "..." },
        "professionalism": { "name": "Professionalism", "score": 87, "notes": "..." }
      },
      "summary": {
        "overview": "Candidate brings strong technical background...",
        "background": ["5 years experience", "Previous tech lead role"],
        "strengths": ["Strong communication", "Technical expertise"],
        "concerns": ["Limited experience with specific stack"],
        "tags": ["senior", "leadership"]
      }
    },
    "resumeEvaluation": null,
    "transcript": [
      { "timestamp": 1735263696836, "isAgent": true, "text": "Hello! Welcome..." },
      { "timestamp": 1735263702000, "isAgent": false, "text": "Thank you..." }
    ]
  }
}

Field Reference

FieldDescription
eventEvent type: interview.completed or interview.abandoned
apiVersionAPI version
timestampISO 8601 timestamp
testPresent (true) for preview sessions; omitted for production
data.interviewInterview details & dashboard URL
data.candidateCandidate info, profile URL & share URL
data.candidate.externalUserIdYour external user ID (from Smart Links)
data.candidate.shareUrlPublic shareable link (no auth required)
data.sessionSession timing, recording & thumbnail URLs
data.session.idUnique session identifier
data.session.tokenSession token (truncated in examples)
data.session.typeSession type: candidate or preview
data.session.statusSession status: completed or abandoned
data.session.startedAtISO 8601 timestamp when interview started
data.session.completedAtISO 8601 timestamp when interview ended
data.session.durationMinutesInterview duration in minutes
data.session.recordingUrlInterview recording (null for preview sessions)
data.session.thumbnailUrlVideo thumbnail (null for preview sessions)
data.session.resumeUrlUploaded resume URL (when provided)
data.validationCompletion validation details
data.validation.passedWhether interview completed successfully
data.validation.reasonReason: completed, too_short, no_evaluation, etc.
data.validation.checkedAtISO 8601 timestamp of validation check
data.scoresOverall, interview & resume scores
data.evaluationPerformance metrics & summaries
data.resumeEvaluationResume evaluation (when resume uploaded)
data.transcriptFull conversation transcript
data.transcript[].timestampUnix timestamp in milliseconds
data.transcript[].isAgenttrue = AI interviewer, false = candidate
data.transcript[].textMessage content

Note: Optional fields like resumeEvaluation, language, and customScores are included only when applicable.

Request Headers

HeaderValue
Content-Typeapplication/json
User-AgentTalentSprout-Webhooks/1.0
X-TalentSprout-Eventinterview.completed or interview.abandoned
X-TalentSprout-Signaturet=1672531200,v1=... (when enabled)

Security

Optionally verify that webhooks are sent from TalentSprout using HMAC-SHA256 signatures. Enable webhook authentication in Settings → Global Interview Settings. When disabled, webhooks are still sent but without the signature header.

Getting Your Signing Secret

  • • Your secret (starting with whsec_) is shown once when you enable authentication
  • • Copy it immediately and store it securely (e.g., environment variable)
  • • If lost, regenerate a new one — this invalidates the previous secret

Signature Format

When enabled, webhooks include the X-TalentSprout-Signature header:

t=1672531200,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
  • t — Unix timestamp when the webhook was generated
  • v1 — HMAC-SHA256 signature of {timestamp}.{payload}

Verification Example

Node.js
const crypto = require('crypto');

function verifyWebhookSignature(payload, header, secret) {
  const parts = header.split(',');
  const timestamp = parts[0].split('=')[1];
  const signature = parts[1].split('=')[1];
  
  // Reject timestamps older than 5 minutes (replay protection)
  const age = Math.floor(Date.now() / 1000) - parseInt(timestamp);
  if (age > 300) {
    throw new Error('Timestamp too old');
  }
  
  // Compute expected signature
  const signedPayload = `${timestamp}.${payload}`;
  const expected = crypto
    .createHmac('sha256', secret)
    .update(signedPayload)
    .digest('hex');
  
  // Compare signatures (timing-safe)
  if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
    throw new Error('Invalid signature');
  }
  
  return true;
}

// Usage in Express
app.post('/webhook', express.raw({type: 'application/json'}), (req, res) => {
  const signature = req.headers['x-talentsprout-signature'];
  
  if (signature) {
    try {
      verifyWebhookSignature(
        req.body.toString(),
        signature,
        process.env.TALENTSPROUT_WEBHOOK_SECRET
      );
    } catch (err) {
      return res.status(400).send(err.message);
    }
  }
  
  // Process the webhook
  const event = JSON.parse(req.body);
  console.log('Received:', event.event);
  
  res.status(200).send('OK');
});

Important: You must use the raw request body for signature verification. Parsing the JSON before verification will change the payload and cause signature mismatch.

Best Practices

Respond quickly

Return a 2xx status code within 10 seconds

Process async

Handle heavy operations asynchronously

Deduplicate

Use session.id to handle potential retries

Use HTTPS

Only secure endpoints are supported

Testing

Use the Send Test button in Settings → Global Interview Settings to verify your endpoint is reachable. Test events include test: true in the payload so you can identify and filter them.

To inspect the full payload structure, use webhook.site to generate a temporary endpoint.

Preview mode interviews trigger webhooks with test: true. Use these to test your integration without affecting billing. Filter production events with if (payload.test) return;

Note: Preview mode interviews are not recorded. recordingUrl and thumbnailUrl will be null for these sessions.

Need help with your integration?

Our team is here to help you get set up.

Contact Support
    Webhooks - TalentSprout API