{"site":{"name":"Koji","description":"AI-native customer research platform that helps teams conduct, analyze, and synthesize customer interviews at scale.","url":"https://www.koji.so","contentTypes":["blog","documentation"],"lastUpdated":"2026-05-18T13:30:44.226Z"},"content":[{"type":"documentation","id":"9b42f662-976d-40ec-b4b4-496f40cb2a29","slug":"webhook-setup","title":"Webhook Setup","url":"https://www.koji.so/docs/webhook-setup","summary":"Webhooks deliver real-time notifications when interviews complete and analysis is ready. Payloads include interview metadata, quality scores, and structured answer summaries. Supports signature verification via HMAC-SHA256 and automatic retry with exponential backoff.","content":"# Webhook Setup\n\nWebhooks let you receive real-time notifications from Koji when key events happen in your project — most importantly, when an interview completes and its analysis is ready. Instead of polling the API, you provide a URL and Koji sends the data to you.\n\n---\n\n## Why Use Webhooks\n\nPolling the API to check if an interview's analysis is done works, but it has drawbacks: unnecessary requests, delayed detection, and wasted resources. Webhooks solve all of these by pushing data to your server the moment it is available.\n\nCommon use cases for webhooks include:\n\n- **Syncing results to your database** as soon as analysis completes\n- **Triggering downstream workflows** like sending a thank-you email to respondents\n- **Updating dashboards** in real time as new interviews come in\n- **Alerting your team** when a high-priority interview finishes\n- **Aggregating structured answers** from scale, choice, and ranking questions across interviews\n\n---\n\n## Configuring a Webhook\n\nSet up webhooks from your project settings:\n\n1. Open your project in Koji.\n2. Navigate to **Settings > Integrations > Webhooks**.\n3. Click **Add Webhook Endpoint**.\n4. Enter your endpoint URL. This must be an HTTPS URL that accepts POST requests.\n5. Select the events you want to receive.\n6. Click **Save**.\n\nKoji sends a verification request to your endpoint when you save it. Your server must respond with a `200` status code to confirm it is ready to receive events.\n\n---\n\n## Supported Events\n\n| Event | Trigger | Description |\n|---|---|---|\n| `interview.completed` | Interview is marked complete | Fires when the complete endpoint is called or the interview ends naturally |\n| `interview.analysis_ready` | Analysis finishes processing | Fires when the automatic analysis pipeline finishes and results are available |\n| `interview.quality_scored` | Quality score is assigned | Fires when the quality gate has evaluated the interview |\n\nYou can subscribe to one or more events per webhook endpoint. Most integrations subscribe to `interview.analysis_ready` since that is when actionable data is available.\n\n---\n\n## Webhook Payload\n\nKoji sends a POST request to your endpoint with a JSON body:\n\n```json\n{\n  \"event\": \"interview.analysis_ready\",\n  \"timestamp\": \"2025-01-15T10:26:15Z\",\n  \"project_id\": \"a1b2c3d4-e5f6-7890-abcd-ef1234567890\",\n  \"data\": {\n    \"interview_id\": \"f8e7d6c5-b4a3-2109-8765-432109876543\",\n    \"status\": \"completed\",\n    \"analysis_status\": \"completed\",\n    \"quality_score\": 4.2,\n    \"respondent\": {\n      \"id\": \"r_abc123def456\",\n      \"display_name\": \"Jane Doe\",\n      \"external_id\": \"user_12345\"\n    },\n    \"stats\": {\n      \"duration_seconds\": 1530,\n      \"total_messages\": 37\n    },\n    \"metadata\": {\n      \"segment\": \"power_users\",\n      \"source\": \"onboarding_flow\"\n    },\n    \"structured_answers_summary\": {\n      \"has_structured_answers\": true,\n      \"question_count\": 3,\n      \"question_types\": [\"scale\", \"single_choice\", \"yes_no\"]\n    }\n  }\n}\n```\n\nThe payload includes enough information to identify the interview and decide if you need to fetch full results. The `structured_answers_summary` field indicates whether the interview collected structured data and how many questions were answered.\n\nTo retrieve the complete transcript, analysis, and full structured answers, call `GET https://koji.so/api/v1/interviews/:interview_id` using your API key.\n\n### Structured Answer Data\n\nWhen you fetch the full interview after receiving a webhook, the analysis includes a `structured_answers` array. Each structured answer contains:\n\n- `questionId` and `questionText` — identifies which question was answered\n- `questionType` — scale, single_choice, multiple_choice, ranking, or yes_no\n- `structuredValue` — the typed response (number for scale, string for single choice, string array for multiple choice or ranking, boolean for yes/no)\n- `qualitativeAnswer` — any additional context the respondent provided\n- `confidence` — how confident the analysis is in the extracted answer\n- `followUpInsights` — insights from follow-up probing\n\nThis enables programmatic aggregation of quantitative data alongside qualitative insights across all interviews in your project.\n\n---\n\n## Verifying Webhook Signatures\n\nEvery webhook request includes a signature header that lets you verify the request genuinely came from Koji:\n\n```\nX-Koji-Signature: sha256=abc123...\n```\n\nTo verify the signature:\n\n1. Retrieve the raw request body as a string (before any JSON parsing).\n2. Compute an HMAC-SHA256 of the body using your webhook secret as the key.\n3. Compare the computed hash with the value in the `X-Koji-Signature` header.\n\n```javascript\nconst crypto = require('crypto');\n\nfunction verifyWebhookSignature(rawBody, signature, secret) {\n  const expected = 'sha256=' + crypto\n    .createHmac('sha256', secret)\n    .update(rawBody)\n    .digest('hex');\n  return crypto.timingSafeEqual(\n    Buffer.from(signature),\n    Buffer.from(expected)\n  );\n}\n```\n\nYour webhook secret is displayed once when you create the webhook endpoint. Store it securely.\n\nAlways verify signatures before processing webhook data. Without verification, an attacker could send fake events to your endpoint.\n\n---\n\n## Responding to Webhooks\n\nYour endpoint must return a `2xx` status code within 10 seconds to acknowledge receipt. If Koji does not receive a successful response, it retries the delivery.\n\n### Retry Policy\n\n- **Immediate retry** after the first failure.\n- **Exponential backoff** for subsequent retries: 1 minute, 5 minutes, 30 minutes, 2 hours.\n- **Maximum retries**: 5 attempts over approximately 2.5 hours.\n- After all retries are exhausted, the event is marked as failed. You can see failed deliveries in the webhook logs on your Integrations page.\n\n### Best Practices for Handling Webhooks\n\n- **Return 200 immediately.** Do your heavy processing asynchronously after acknowledging receipt. If your processing takes more than 10 seconds, the webhook times out.\n- **Be idempotent.** Koji may send the same event more than once (for example, if your server returned 200 but the connection dropped before Koji received the response). Use the `interview_id` and event type to deduplicate.\n- **Log everything.** Store raw webhook payloads so you can debug issues later.\n- **Monitor failures.** Check the webhook logs in your Integrations page regularly.\n\n---\n\n## Testing Webhooks\n\nThe Integrations page includes a **Send Test Event** button for each webhook endpoint. This sends a sample payload to your URL so you can verify your handler works correctly before real interviews generate events.\n\nDuring development, tools like ngrok or similar tunneling services let you expose a local server to receive webhook deliveries.\n\n---\n\n## Troubleshooting\n\nIf your webhook is not receiving events:\n\n- **Check the endpoint URL** is correct and accessible from the internet.\n- **Verify HTTPS.** Koji only delivers webhooks to HTTPS URLs.\n- **Check your firewall** allows incoming requests from Koji's IP ranges.\n- **Review the delivery logs** in Settings > Integrations > Webhooks for error details.\n- **Confirm event subscription.** Make sure you are subscribed to the events you expect.\n\n---\n\n## Next Steps\n\n- [Complete interviews to trigger webhook events](/docs/completing-interviews-via-api)\n- [Set up your API key for fetching full results](/docs/api-authentication)\n- [Learn about structured questions](/docs/structured-questions-guide)\n- [Explore the headless API overview](/docs/headless-api-overview)","category":"API Reference","lastModified":"2026-05-15T03:23:48.575624+00:00","metaTitle":"Webhook Setup — Koji Docs","metaDescription":"Configure webhooks to receive real-time notifications when Koji interviews complete and analysis results are ready.","keywords":["webhooks","webhook setup","real-time notifications","interview events","webhook signature"],"aiSummary":"Webhooks deliver real-time notifications when interviews complete and analysis is ready. Payloads include interview metadata, quality scores, and structured answer summaries. Supports signature verification via HMAC-SHA256 and automatic retry with exponential backoff.","aiPrerequisites":["api-authentication","completing-interviews-via-api"],"aiLearningOutcomes":["Configure webhook endpoints","Verify webhook signatures","Handle retries and idempotency","Debug webhook delivery issues"],"aiDifficulty":"intermediate","aiEstimatedTime":"7 min read"}],"pagination":{"total":1,"returned":1,"offset":0}}