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
Go to the Integrations page in your Enterpret instance
Click +New Integration, and search for "Webhook"
Choose the type of webhook integration based on your needs:
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
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
Endpoint URL: https://api.enterpret.com/webhook/custom/all
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 like1716460800000Must 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
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 returnsErrCodeMissingRequiredField
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
SURVEY Type
Use for structured question-and-answer feedback like NPS, CSAT, and product feedback surveys.
Required fields:
surveyResponse: Contains an array of responsesFor every response that has
answerorselectedOptions, the question field must be non-empty — otherwise the record returnsErrCodeMissingRequiredFieldResponses with neither
answernorselectedOptionsare 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
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 messageAt least one message must have non-empty text — messages with empty or whitespace-only text are filtered out after ingestion
Each message needs
textandactorfieldsActor 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
FORUM_CONVERSATION_THREAD Type
Required field: conversation object with msgs array (same structure as CONVERSATION).
Key difference from CONVERSATION:
The first message in
msgsis the original post/thread starterAll 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
AUDIO_RECORDING Type
Use for voice recordings from call centers, sales calls, or other audio feedback.
Required fields:
audioRecording: Contains theaudioURLfieldaudioURLmust 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, ortext/plainare 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
Metadata Structure
All metadata follows this format:
{
"metadata": {
"metadata": {
"field_name": {
"array": {
"type_indicator": [
"value1",
"value2"
]
}
}
}
}
}
Supported Metadata TypesStrings: 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:
Contact your Customer Success Manager (CSM) or Enterpret Support
Share your integration name or API key
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 |
| String | A detailed error message explaining the issue |
| String | An error code identifier |
| String | A unique ID to reference when contacting support |
Common Error Responses
Error Code | HTTP Status | Is Retriable? | Description |
| 500 | Yes | Server-side issue - retry with exponential backoff |
| 400 | No | Issue with request format or data |
| 400 | No | Required field is missing from the payload |
| 400 | No | Field format doesn't match expected schema |
| 400 | No | Too many records in single request (max 100) |
| 413 | No | Request payload exceeds 200KB limit |
| 429 | Yes | Too many requests - implement backoff |
| 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?
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=fileuploadinstead ofsource=webhookin 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
textin 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
textwill be flagged.What to do? Ensure every message has non-empty
textbefore sending. Use the Webhook Payload Validator to confirm before sending bulk data.
Survey responses with
answerorselectedOptionsbut an emptyquestionare rejected.How to identify? The API returns
ErrCodeMissingRequiredFieldwith a message pointing to the survey response index. Inspect your payload for any response object whereanswerorselectedOptionsis populated butquestionis empty.What to do? Ensure every survey response object has a non-empty
questionwheneveranswerorselectedOptionsis provided.
If the problem persists, please reach out to support.
Implementation Examples
Example: Sending a Batch of Reviews
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:
cURL Examples Notebook:
Enterpret Custom Webhook Example_curl.ipynb
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:
Script - Script_for_sending_records_to_enterpret.py
Sample Payload - sample_payload.json
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
Email: [email protected]
Dashboard: Use the chat widget in your dashboard


