Skip to main content

Webhook Integration

Written by Team Enterpret

Introduction

Enterpret's Custom Webhook integration lets you programmatically send customer feedback to Enterpret for analysis. This method is ideal for connecting feedback sources not supported by native integrations—making it easy to consolidate all your feedback in Enterpret.

When to Use a Custom Webhook

Use this integration when you're working with:

  • Review platforms that are not natively supported by Enterpret

  • Forum feedback that requires custom collection

  • Chatbot conversations from platforms without direct integrations

  • Survey tools specific to your industry

  • Internal feedback systems built for your organization

  • Feedback extracted from data warehouses (e.g., Redshift, BigQuery)

Note: Before proceeding, visit our native integrations page to see if your feedback source is already supported.

💡 Tip: Before sending bulk data, validate your payload with the Webhook Payload Validator. Paste your JSON, preview how records will appear in the dashboard, and generate a ready-to-use cURL command. No API key required.

Prerequisites

  • API access to your feedback source

  • Engineering resources to implement the webhook connection

  • Enterpret account with administrator access

Not an engineer? This guide is designed for developers and contains technical instructions. Please share it with your engineering team for implementation.

Getting Started

Create an API Key

  1. Go to the Integrations page in your Enterpret instance

  2. Click +New Integration, and search for "Webhook"

  3. Choose the type of webhook integration based on your needs:

    • Feedback Integration – Send customer feedback into Enterpret

    • User Integration – Send user data to enrich feedback analysis

    • Account Integration – Sync account (company) data for account-level insights

  4. Provide a descriptive Display Name for your integration

    • This name will appear as the source for records throughout Enterpret

    • Add a detailed description to improve summary and prediction quality

  5. Once configured, you'll find your API key at the top of the integration page

    • This key should be included in all API requests to authenticate


Webhook Endpoint & Authentication

Authentication: Include your API key in every request header

api-key: your-api-key-here

Hard Limits (Enforced):

  • Maximum: 2000 requests per minute (RPM)

  • Maximum payload size: 200KB per request

  • Recommended: Stay below 100 records per batch

Data Structure Overview

Webhook payloads must follow a structured JSON format. Enterpret accepts data in JSON format with a specific structure based on the feedback type.

Supported Feedback Types

We support four primary types of feedback:

  • REVIEW - Simple product/service reviews

  • CONVERSATION - Multi-message interactions between users and agents

  • SURVEY - Question-and-answer format feedback (NPS, CSAT, product feedback forms)

  • AUDIO_RECORDING - Voice recordings with optional transcripts

  • FORUM_CONVERSATION_THREAD - Forum/community posts with threaded replies (e.g., Facebook groups, Reddit, Discourse). The first message represents the original post, and subsequent messages are comments/replies.

Core Fields

All records require these essential fields:

{
"records": [
{
"id": "unique-identifier",
"fileID": "source-identifier",
"type": "REVIEW",
"createdAt": 1768913221,
"metadata": {
"metadata": {
"custom_field": {
"array": {
"s": ["value1", "value2"]
}
}
}
}
}
]
}

createdAt must follow following rules:

  • Must be a Unix epoch integer in seconds — not a date string like "2024-05-23", not milliseconds like 1716460800000

  • Must not be before year 2000 — timestamps before Jan 1, 2000 are rejected as likely misconfigured

  • Must not be in the future — timestamps ahead of current time are rejected

  • Note: Timestamps that appear to be in milliseconds (13 digits, e.g. 1716460800000) are automatically converted to seconds on ingestion. Sending Unix seconds directly is still the recommended format to avoid ambiguity.

Detailed Guides by Feedback Type

REVIEW Type

The simplest feedback format, ideal for app store reviews, product reviews, etc.

Required fields:

  • text: The content of the review — must be non-empty; an empty or missing text returns ErrCodeMissingRequiredField

Example:

{
"type": "REVIEW",
"createdAt": 1716460800,
"text": "This product works exactly as described. Very happy with my purchase!",
"metadata": {
"rating": {
"array": {
"n": [5]
}
},
"platform": {
"array": {
"s": ["iOS App Store"]
}
}
}
}

SURVEY Type

Use for structured question-and-answer feedback like NPS, CSAT, and product feedback surveys.

Required fields:

  • surveyResponse: Contains an array of responses

  • For every response that has answer or selectedOptions, the question field must be non-empty — otherwise the record returns ErrCodeMissingRequiredField

  • Responses with neither answer nor selectedOptions are filtered out safely and do not need a question

Example:

{
"records": [
{
"id": "survey-456",
"fileID": "nps-survey",
"type": "SURVEY",
"createdAt": 1768913221,
"surveyResponse": {
"responses": [
{
"question": "How likely are you to recommend our product to a colleague?",
"answer": [
"9"
]
},
{
"question": "What is the primary reason for your score?",
"answer": "The product is intuitive and saves me time on daily tasks."
},
{
"question": "Which features do you use most often?",
"selectedOptions": [
"Dashboard",
"Reports",
"API"
]
}
]
},
"metadata": {
"metadata": {
"nps_score": {
"array": {
"n": [
9
]
}
},
"user_segment": {
"array": {
"s": [
"Enterprise"
]
}
}
}
}
}
]
}

CONVERSATION Type

Ideal for support tickets, chat transcripts, or any back-and-forth communication.

Required fields:

  • conversation: Contains an array of messages. Must contain at least one message

  • At least one message must have non-empty text — messages with empty or whitespace-only text are filtered out after ingestion

  • Each message needs text and actor fields

  • Actor must be one of: "user", "agent", or "bot" (any other value rejects the record)

Example:

{
"records": [
{
"id": "01JY05BQ6EV1RPSXJ61K0BV0YA",
"type": "CONVERSATION",
"fileID": "proto_chat",
"createdAt": 1768913221,
"conversation": {
"msgs": [
{
"actor": "bot",
"text": "Hello boom504!"
},
{
"actor": "bot",
"text": "Hello boom504!"
}
]
},
"metadata": {
"metadata": {
"src_account_id": {
"array": {
"s": [
"TybFPBMTLnY96T5nQ"
]
}
},
"website": {
"array": {
"s": [
"SportsBet"
]
}
},
"primary_product": {
"array": {
"s": [
"Cross Sell"
]
}
},
"tech": {
"array": {
"s": [
"Web"
]
}
},
"channel": {
"array": {
"s": [
"Desktop"
]
}
},
"value_segment": {
"array": {
"s": [
"High Value 1"
]
}
},
"active_segment": {
"array": {
"s": [
"Active High"
]
}
},
"sportsbook_ccf": {
"array": {
"s": [
"CCF 0.1"
]
}
},
"vip": {
"array": {
"s": [
"1"
]
}
},
"scrubbed": {
"array": {
"s": [
"0"
]
}
},
"first_deposit_date": {
"array": {
"s": [
"2022-11-13T00:00:00.000Z"
]
}
},
"last_active_date": {
"array": {
"s": [
"2025-06-18T00:00:00.000Z"
]
}
},
"bot_id": {
"array": {
"s": [
"01J0685Q1AYVRTHSKRSA289MKC"
]
}
},
"bot_name": {
"array": {
"s": [
"Sportsbet.io"
]
}
},
"language": {
"array": {
"s": [
"English"
]
}
}
}
}
}
]
}

FORUM_CONVERSATION_THREAD Type

Required field: conversation object with msgs array (same structure as CONVERSATION).

Key difference from CONVERSATION:

  • The first message in msgs is the original post/thread starter

  • All subsequent messages are comments/replies to that post

  • All actors should use "user" (no "agent" or "bot" distinction in forum threads)

Example payload:

{
"records": [
{
"id": "forum-thread-001",
"fileID": "community-posts",
"type": "FORUM_CONVERSATION_THREAD",
"createdAt": 1774600094,
"conversation": {
"msgs": [
{
"actor": "user",
"text": "Has anyone tried the new API v2? I'm seeing intermittent timeouts."
},
{
"actor": "user",
"text": "Yes, I noticed the same issue. Switching to the batch endpoint fixed it for me."
},
{
"actor": "user",
"text": "Thanks! That worked. Would be great if this was documented better."
}
]
},
"metadata": {
"source_platform": { "s": ["community_forum"] },
"thread_topic": { "s": ["API v2 timeouts"] }
}
}
]
}

AUDIO_RECORDING Type

Use for voice recordings from call centers, sales calls, or other audio feedback.

Required fields:

  • audioRecording: Contains the audioURL field

  • audioURL must satisfy all of the following:

    • should be publicly accessible or have a long-lived signed URL - no authentication, IP allow-listing, or bearer tokens.

    • Supported formats: mp3, mp4, wav, flac, ogg, amr, webm, m4a

    • Must return a 2xx response within 10 seconds. Enterpret fetches the URL on ingestion to validate accessibility.

    • Must return an audio content type. URLs that return text/html, application/json, application/xml, or text/plain are rejected.

    • Pre-signed S3 URLs must not expire before Enterpret fetches them.

    • Optional: Include a transcript to improve analysis or use your own transcription

With Automatic Transcription:

{
"records": [
{
"id": "call-abc123",
"fileID": "sales-calls",
"type": "AUDIO_RECORDING",
"createdAt": 1768913221,
"audioRecording": {
"audioURL": "https://storage.example.com/calls/customer-feedback-call.mp3"
},
"metadata": {
"metadata": {
"call_duration_seconds": {
"array": {
"n": [
352
]
}
},
"call_type": {
"array": {
"s": [
"Sales Demo"
]
}
}
}
}
}
]
}

With Provided Transcript:

{
"records": [
{
"id": "call-xyz789",
"fileID": "support-calls",
"type": "AUDIO_RECORDING",
"createdAt": 1768913221,
"audioRecording": {
"audioURL": "https://storage.example.com/calls/support-call.mp3",
"transcript": {
"conversation": {
"units": [
{
"actor": "agent",
"actorID": "support-rep-42",
"text": "Thank you for calling customer support. How can I help you today?",
"metadata": {
"startTime": 1000,
"endTime": 5000
}
},
{
"actor": "user",
"actorID": "caller",
"text": "Hi, I'm having an issue with logging into my account.",
"metadata": {
"startTime": 5500,
"endTime": 9000
}
}
]
}
}
},
"metadata": {
"metadata": {
"issue_category": {
"array": {
"s": [
"Authentication"
]
}
}
}
}
}
]
}

Working with Metadata

Metadata provides crucial context about your feedback. Enterpret uses this data for filtering, analysis, and visualization.

Metadata Structure

All metadata follows this format:

{
"metadata": {
"metadata": {
"field_name": {
"array": {
"type_indicator": [
"value1",
"value2"
]
}
}
}
}
}
Supported Metadata Types

Strings: Use "s" as the type indicator for strings

"user_tier": { "array": { "s": ["Premium"] } }

Numbers: Use "n" as the type indicator for number [int, float]

"rating": { "array": { "n": [4.5] } }

Booleans: Use "b" as the type indicator for bool

"is_paying_customer": { "array": { "b": [true] } }

Multiple Values: Arrays can contain multiple values

"tags": { "array": { "s": ["bug", "ui", "mobile"] } }

metadata is required on every record. If a record has no metadata to send, include an empty object: "metadata": {}. Omitting the metadata field entirely returns ErrCodeMissingRequiredField.

Important: Each metadata field can only use one type. Mixing types (e.g., strings and numbers) in the same field is not supported and returns ErrCodeInvalidFieldFormat. If you send a single value we will treat it as a single value like a key-value pair.

Example — mixed types are rejected:

- ✅ Correct: `"rating": { "array": { "n": [5] } }` — single type (`n` for numbers)
- ❌ Wrong: `"rating": { "array": { "n": [5], "s": ["five"] } }` — mixed types in one field, rejected

Data Mutability & Updating Records

What is Data Mutability?

Data mutability defines how Enterpret handles incoming records that share the same id.

When enabled
When enabled by support, sending a record with an existing id will replace the original record with the new payload — it does not merge or append. This is useful when you need to:

  • Update feedback content (for example, when a user edits a review in the source system)

  • Correct mistakes in previously ingested records

Important: Because updates replace the full record, do NOT use mutability to append new messages to a conversation. To add a new message to an existing conversation, resend the entire conversation.msgs array (old + new messages together) under the same id, keeping the record under the 200KB per-record limit.

When disabled (default)
Records with duplicate ids are automatically skipped to prevent duplication. This is ideal for one-time or historical data imports where records should remain immutable.

How to Check if Data Mutability Is Enabled

Data mutability is configured per integration and must be enabled by Enterpret support.

To check whether it’s enabled for your integration:

  1. Contact your Customer Success Manager (CSM) or Enterpret Support

  2. Share your integration name or API key

  3. The team will confirm your current data mutability setting

Note: Data mutability is disabled by default for new webhook integrations and must be explicitly enabled.

How to Update Existing Records

Once data mutability is enabled, you can update a record by sending the same id with the updated fields.

Example: Updating a Review

{
"records": [
{
"id": "review-123",
"fileID": "appstore-reviews",
"type": "REVIEW",
"createdAt": 1768913221,
"text": "Updated review text after customer edited their review",
"metadata": {
"metadata": {
"rating": { "array": { "n": [5] } },
"updated_at": { "array": { "n": [1716500000] } }
}
}
}
]
}

Example: Adding Metadata to an Existing Record

{
"records": [
{
"id": "review-123",
"fileID": "appstore-reviews",
"type": "REVIEW",
"createdAt": 1716460800,
"text": "Original review text",
"metadata": {
"metadata": {
"rating": { "array": { "n": [5] } },
"user_tier": { "array": { "s": ["Premium"] } },
"lifetime_value": { "array": { "n": [50000] } }
}
}
}
]
}

Important Considerations

ID Consistency: Always use the same `id` format when updating records. The ID must match exactly (case-sensitive) because it's used to generate a deterministic UUID for record identification.

Metadata Updates: When updating metadata, the entire metadata attribute is replaced, not merged. If you want to add a new metadata field while keeping existing ones, you must include all existing metadata fields in your update request along with the new field.

Conversation messages: The conversation.msgs array is replaced on every update, not appended to. To add message, resend the full message history under the same id.

Error Handling

Our API provides detailed error messages to help you troubleshoot issues with your webhook implementation.

Error Response Structure

When you encounter an error, you'll receive a JSON response with:

Field

Type

Description

msg

String

A detailed error message explaining the issue

code

String

An error code identifier

referenceID

String

A unique ID to reference when contacting support

Common Error Responses

Error Code

HTTP Status

Is Retriable?

Description

ErrCodeInternalError

500

Yes

Server-side issue - retry with exponential backoff

ErrCodeInvalidRequestArgument

400

No

Issue with request format or data

ErrCodeMissingRequiredField

400

No

Required field is missing from the payload

ErrCodeInvalidFieldFormat

400

No

Field format doesn't match expected schema

ErrCodeExceededBatchLimit

400

No

Too many records in single request (max 100)

ErrCodePayloadTooLarge

413

No

Request payload exceeds 200KB limit

ErrCodeRateLimitExceeded

429

Yes

Too many requests - implement backoff

ErrCodeInvalidAPIKey

401

No

API key is invalid or missing

Example Error Response

{
"msg": "Missing required field: 'id'. Each record must have a unique identifier.",
"code": "ErrCodeMissingRequiredField",
"referenceID": "8B5OQA"
}

Troubleshooting Tips

Authentication Issues: Verify your API key is correct and the integration is enabled

Format Problems: Ensure your JSON is valid and follows the required schema

Missing Fields: Check that all required fields are present for your feedback type

Invalid Values: Verify field values meet the requirements (e.g., actor types)

Large Payloads: Consider breaking very large requests into smaller batches

Why isn't my data showing up?

If the API returns 200 OK but records don’t appear in your dashboard, the three most common causes are:

  • Legacy webhooks (created before August 2025) run through a lenient sanitiser that can drop malformed records silently.

    • How to identify? Check the integration’s creation date on the Integrations page. If it was created before August 2025, or if records appear under source=fileupload instead of source=webhook in the dashboard source filter, it’s a legacy webhook.

    • What to do? Recreate the integration to use the current validation path and the dedicated Webhook source.

  • Empty or whitespace-only text in conversation messages is filtered out by the sanitiser, so a payload can return 200 OK and yield zero visible messages.

    • How to identify? The API returns 200 OK but records don’t appear in the dashboard, or appear with no visible message content. Paste the payload into the Webhook Payload Validator — messages with empty text will be flagged.

    • What to do? Ensure every message has non-empty text before sending. Use the Webhook Payload Validator to confirm before sending bulk data.

  • Survey responses with answer or selectedOptions but an empty question are rejected.

    • How to identify? The API returns ErrCodeMissingRequiredField with a message pointing to the survey response index. Inspect your payload for any response object where answer or selectedOptions is populated but question is empty.

    • What to do? Ensure every survey response object has a non-empty question whenever answer or selectedOptions is provided.

If the problem persists, please reach out to support.

Implementation Examples

Example: Sending a Batch of Reviews

import requests
import json
import time

# Configuration
api_key = "your-api-key"
endpoint = "https://api.enterpret.com/webhook/custom/all"

# Sample data: creating 3 sample reviews
reviews = [
{
"id": f"review-{i}",
"fileID": "product-reviews",
"type": "REVIEW",
"createdAt": int(time.time()),
"text": f"Sample review text {i}",
"metadata": {
"metadata": {
"rating": {
"array": {
"n": [4]
}
}
}
}
}
for i in range(1, 4)
]

# Prepare payload
payload = {
"records": reviews
}

# Send request
headers = {
"api-key": api_key,
"Content-Type": "application/json"
}

response = requests.post(
endpoint,
headers=headers,
data=json.dumps(payload)
)

# Handle response
if response.status_code == 200:
print("Successfully sent reviews to Enterpret")
else:
print(f"Error: {response.status_code}")
print(response.text)
Example: Extracting Data from Your Database

Interactive Code Examples

We provide downloadable notebooks with executable code examples to accelerate your integration development:

Best for: Initial testing, debugging, and non-Python environments

This notebook contains command-line examples that work in any environment. Use it when you need to:

  • Test the API immediately without writing code

  • Debug integration issues quickly

  • Work in non-Python development environments

  • Validate your API key and endpoint connectivity

What's included:

  • Working cURL commands for all feedback types

  • Error handling examples

  • Authentication testing

  • Quick validation scripts

Download cURL Examples Notebook:

Python Implementation Script

Best for: Production integrations and Python development teams

This script provides production-ready Python classes with type hints for building robust webhook integrations. Use it when you need:

  • Structured, maintainable code for your integration

  • Type safety and IDE support

  • Reusable classes for different feedback types

  • A foundation for scaling your webhook implementation

What's included:

  • Complete Python class definitions

  • Type-safe implementations

  • Error handling and retry logic

Download Python Implementation Script and Sample Payload:

python3 Script_for_sending_records_to_enterpret.py --payload sample_payload.json  

Best Practices

Data Placement Guide: Feedback Content vs Metadata

Place data in the correct fields so Enterpret can analyze feedback and use metadata for filtering and context.

Feedback content fields (what customers said)

Use these for the actual customer feedback text:

  • text (REVIEW type) — the review text

  • conversation.msgs[].text (CONVERSATION type) — message content

  • surveyResponse.responses[].answer (SURVEY type) — open-ended answers

  • surveyResponse.responses[].selectedOptions (SURVEY type) — selected options

Metadata fields (context about the feedback)

Use metadata for structured, contextual information:

  • Ratings, scores (NPS, CSAT, star ratings)

  • Demographics (country, region, language)

  • Customer attributes (tier, segment, account ID)

  • Transactional data (order ID, product ID, ticket ID)

  • Timestamps and statuses

  • Platform/device information

Data Quality

  • Include rich metadata - The more context you provide, the better Enterpret can analyze your feedback

  • Maintain consistent IDs - Use stable, unique identifiers for your records

  • Provide accurate timestamps - This helps with trend analysis over time

  • Handle special characters properly in your JSON strings

  • Set appropriate fileType values to organize your feedback sources

  • Right position for feedback vs metadta: Please keep the information like customer rating, nps score, country etc in the metadta and customer text feedback in the actual Conversati like text in review, survery wquestion ans.

Performance

  • Batch requests: Send multiple records per request instead of one at a time (up to 100 records per batch).

  • Retry on transient failures: Implement retry logic for 5xx errors using exponential backoff.

  • Use compression: Enable request compression for larger payloads when possible.

  • Schedule regular syncs: Run periodic syncs to keep your data up to date in Enterpret.

Security

  • Protect your API key - Treat it like a password

  • Use HTTPS for all requests to ensure encryption

  • Audit data before sending to ensure no sensitive information is included

  • Consider IP restrictions for your API access if available

Getting Help

If you encounter any issues implementing your webhook integration:

  • Check error messages for specific guidance on what needs to be fixed

  • Review this documentation to ensure your implementation follows the requirements

  • Contact Enterpret Support with your referenceID if you need additional assistance

Support Channels:

  • Help Center: helpcenter.enterpret.com

  • Dashboard: Use the chat widget in your dashboard


Did this answer your question?