🔗 Introduction
Connected Sources is MoEngage's flexible webhook integration framework that allows you to stream real-time data from any external platform directly into MoEngage. Integrate CRMs, e-commerce platforms, marketing tools, or custom applications with a standardized approach.
📊 How It Works
Connect any external system using three simple steps:
Webhook POST
Your system sends JSON payload to MoEngage endpoint
Transform
Mapper extracts and validates data
Import
Data flows into events & profiles
✨ Key Capabilities
Stream user actions as they happen
Update user attributes automatically
Handle batches in single call
Transform complex JSON structures
Combine separate date/time fields
Auto-import custom fields
💼 Use Cases
Stream form submissions from Unbounce, Typeform, HubSpot for instant follow-ups
Track subscriptions from Stripe, Chargebee (created, failed, expiring)
Monitor Zendesk/Intercom interactions for satisfaction tracking
Capture orders, carts, views from custom platforms
Send events from proprietary systems without native SDKs
🚀 Build Your Integration
Follow these five steps to go from sample payload to live data flowing into MoEngage.
Prepare: Prerequisites & Sample Payloads
Before building your mapper, make sure you have everything ready.
You will need:
- Webhook capability in your system (HTTP POST)
- Authentication support (Basic Auth or custom headers)
- Valid JSON webhook payloads
- 2-3 sample payloads from your system
- MoEngage credentials (Workspace ID, Data API Key from Settings → APIs)
⚡ Rate Limits
Recommended maximum: 200 requests/second
For higher throughput, contact your Solutions Engineering team with: expected volume, use case description, peak/average patterns, and data center location.
💡 Validate early: Paste your sample payloads into JSONLint to confirm they are valid JSON before proceeding.
Create: Build Your Mapper Configuration
The mapper is a JSON configuration that tells MoEngage how to transform your incoming webhook data into events or user profiles. This is the core of your integration.
📋 Choose Your Mapping Type
Two sync types available:
-
mapping_type: "events"- Track events AND create/update user profiles -
mapping_type: "users"- Create/Update user profiles ONLY (no events)
{
"partner_name": "integration_name",
"mapping_type": "events",
"mappings": {
"customer_id": "$.user_id",
"event_name": "$.event_type",
"user_time": "$.timestamp",
"platform": "web",
"app_version": "1.0.0",
"attr": {
"custom_field": "$.field_name"
},
"user_attributes": {
"u_em": "$.email"
}
},
"batching": false,
"payload_validations": {}
}
{
"partner_name": "integration_name",
"mapping_type": "users",
"mappings": {
"customer_id": "$.user_id",
"user_attributes": {
"u_em": "$.email",
"u_fn": "$.first_name",
"u_ln": "$.last_name"
}
},
"batching": false,
"payload_validations": {}
}
🔄 Field Requirements by Mapping Type
| Field | For "events" | For "users" |
|---|---|---|
| customer_id | Required - Unique user identifier | Required - Unique user identifier |
| event_name | Required - Event name | Not used |
| user_time | Required - Event timestamp | Not used |
| platform | Required - "web", "android", "ios", "api" | Not used |
| app_version | Required - Static version string | Not used |
| user_attributes | Optional - Update user profile | Optional - Update user profile |
| attr | Optional - Event attributes | Not used |
📍 JSONPath Quick Reference
Use JSONPath expressions to map fields from your payload to MoEngage fields. Here's how to read them:
| Payload | JSONPath | Result |
|---|---|---|
{"email": "user@example.com"} |
$.email |
user@example.com |
{"user":{"email": "test@ex.com"}} |
$.user.email |
test@ex.com |
{"items": [{"name": "A"}]} |
$.items[0].name |
A |
{"events": [{"id": 1}, {"id": 2}]} |
$.events[*].id |
Iterates all (batching) |
📌 Complete Field Reference
| Field | Type | Description |
|---|---|---|
partner_name |
String | Unique lowercase identifier (e.g., "typeform") |
mapping_type |
String | "events" OR "users" |
customer_id |
JSONPath | 🔑 Path to unique user ID (email, phone, custom ID) - links events to profiles |
event_name |
JSONPath/String | Event name path or static string. Required for "events" type only |
user_time |
JSONPath/"#MERGE" | Timestamp path (epoch ms preferred) or "#MERGE" for split date/time. Required for "events" only |
platform |
String | "web", "android", "ios", or "api". Required for "events" type only |
app_version |
String | Version string (default: "1.0.0"). Required for "events" type only |
user_attributes |
Object | User profile attributes: u_em (email), u_fn (first name), u_ln (last name), u_mb (mobile) |
attr |
Object | Event attributes (key-value pairs). Optional for "events" only |
batching |
Boolean |
true for array payloads, false for single events |
📋 Starter Template
Copy this template and replace the JSONPath expressions with paths from your actual payload:
{
"partner_name": "your_integration_name",
"mapping_type": "events",
"mappings": {
"customer_id": "$.user_id",
"event_name": "$.event_type",
"user_time": "$.timestamp",
"platform": "web",
"app_version": "1.0.0",
"attr": {
"custom_field": "$.field_name"
},
"user_attributes": {
"u_em": "$.email",
"u_fn": "$.first_name"
}
},
"batching": false,
"payload_validations": {
"$.event_type": "string"
}
}
💡 See It in Action: Real-World Examples
Click each example to see the payload and its corresponding mapper configuration side by side.
Scenario: Lead form sends contact info on submission
{
"email": "john@example.com",
"first_name": "John",
"last_name": "Doe",
"phone": "+14155551234",
"form_name": "Contact Us",
"submitted_at": 1704067200000,
"utm_source": "google"
}
{
"partner_name": "contact_form",
"mapping_type": "events",
"mappings": {
"customer_id": "$.email",
"event_name": "Form Submitted",
"user_time": "$.submitted_at",
"platform": "web",
"app_version": "1.0.0",
"attr": {
"form_name": "$.form_name",
"utm_source": "$.utm_source"
},
"user_attributes": {
"u_em": "$.email",
"u_fn": "$.first_name",
"u_ln": "$.last_name"
}
},
"batching": false,
"payload_validations": {}
}
✅ Key Points: Email as unique ID • Static event name • Epoch timestamp • Updates user profile
Scenario: System sends multiple orders in single webhook
{
"orders": [
{
"order_id": "ORD-001",
"customer_email": "alice@ex.com",
"order_total": 99.99,
"currency": "USD",
"order_date": 1704067200000
},
{
"order_id": "ORD-002",
"customer_email": "bob@ex.com",
"order_total": 149.50,
"currency": "USD",
"order_date": 1704070800000
}
]
}
{
"partner_name": "ecommerce",
"mapping_type": "events",
"mappings": {
"customer_id": "$.orders[*].customer_email",
"event_name": "Order Completed",
"user_time": "$.orders[*].order_date",
"platform": "web",
"app_version": "1.0.0",
"attr": {
"order_id": "$.orders[*].order_id",
"order_total": "$.orders[*].order_total",
"currency": "$.orders[*].currency"
},
"user_attributes": {
"u_em": "$.orders[*].customer_email"
}
},
"batching": true,
"payload_validations": {}
}
✅ Key Points: batching=true for arrays • [*] wildcard iterates • Creates separate event per order
Scenario: Separate date and time fields need combining
{
"user_email": "user@example.com",
"event_type": "Webinar Registration",
"webinar_name": "Product Demo",
"date_registered": "2024-10-25",
"time_registered": "14:30:00"
}
{
"partner_name": "webinar",
"mapping_type": "events",
"mappings": {
"customer_id": "$.user_email",
"event_name": "$.event_type",
"user_time": "#MERGE",
"platform": "web",
"app_version": "1.0.0",
"attr": { "webinar_name": "$.webinar_name" },
"#MERGE": {
"user_time": { "date": "$.date_registered", "time": "$.time_registered" }
},
"formats": { "user_time": "yyyy-MM-dd HH:mm:ss" }
},
"batching": false,
"payload_validations": {}
}
✅ Key Points: user_time="#MERGE" signals merging • #MERGE section defines fields • formats specifies pattern
Scenario: CRM sends profile updates without event tracking
{
"email": "sarah@example.com",
"first_name": "Sarah",
"last_name": "Williams",
"phone": "+14155559999",
"subscription_tier": "Premium",
"account_status": "Active"
}
{
"partner_name": "crm_sync",
"mapping_type": "users",
"mappings": {
"customer_id": "$.email",
"user_attributes": {
"u_em": "$.email", "u_fn": "$.first_name", "u_ln": "$.last_name",
"u_mb": "$.phone", "subscription_tier": "$.subscription_tier",
"account_status": "$.account_status"
}
},
"batching": false,
"payload_validations": {}
}
✅ Key Points: mapping_type="users" • No event created • No event_name/user_time/platform needed
Scenario: Track pre-login behavior, merge after identification
{
"session_id": "anon_abc123xyz",
"event": "Product Viewed",
"product_id": "PROD-001",
"product_name": "Wireless Headphones",
"timestamp": 1704067200000
}
{
"partner_name": "ecommerce_anon",
"mapping_type": "events",
"mappings": {
"anon_id": "$.session_id",
"event_name": "$.event",
"user_time": "$.timestamp",
"platform": "web",
"app_version": "1.0.0",
"attr": { "product_id": "$.product_id", "product_name": "$.product_name" }
},
"batching": false,
"payload_validations": {}
}
🔄 Merging Flow: After login, send event with both customer_id and anon_id. MoEngage auto-merges anonymous history to identified profile.
💡 Pro Tip: Using create_all
The create_all attribute allows you to iterate
through all keys within a specified object from your payload.
This is highly recommended when dealing with objects whose
keys are dynamic, frequently changing, or constantly expanding.
Instead of mapping every new field manually, create_all
grabs them all at once!
Scenario: Syncing user profiles from a CRM where
custom fields are constantly added to a user_attributes
object.
{
"customer_id": "CUST-8890",
"first_name": "Alex",
"last_name": "Taylor",
"email": "alex.t@example.com",
"phone": "+1234567890",
"user_attributes": {
"modified_time": "25-10-2024 14:30:00",
"lead_score": 85,
"industry": "Software",
"lifecycle_stage": "MQL"
}
}
{
"partner_name": "zoho-crm",
"mapping_type": "users",
"mappings": {
"customer_id": "$.customer_id",
"app_version": "1.0.0",
"platform": "web",
"user_attributes": {
"u_fn": "$.first_name",
"u_ln": "$.last_name",
"u_em": "$.email",
"u_mb": "$.phone",
"create_all": "$.user_attributes"
},
"update_existing_only": false,
"user_time": "$.user_attributes.modified_time",
"formats": {
"user_time": "dd-MM-yyyy HH:mm:ss"
}
},
"batching": false,
"payload_validations": {}
}
✅ Key Points: Standard fields are mapped
directly (e.g., u_fn) • create_all
automatically pulls in lead_score,
industry, and lifecycle_stage without
explicit mapping.
Scenario: A CRM sends an event where both the user properties and the event details contain dynamic, unpredictable keys.
{
"customer_id": "CUST-8890",
"event_name": "Deal Advanced",
"user_attributes": {
"account_manager": "Sarah Jenkins",
"region": "EMEA"
},
"event_attributes": {
"modified_time": "26-10-2024 09:15:00",
"deal_value": 50000,
"previous_stage": "Demo",
"new_stage": "Negotiation"
}
}
{
"partner_name": "zoho-crm",
"mapping_type": "events",
"mappings": {
"customer_id": "$.customer_id",
"app_version": "1.0.0",
"platform": "web",
"user_attributes": {
"create_all": "$.user_attributes"
},
"attr": {
"create_all": "$.event_attributes"
},
"event_name": "$.event_name",
"user_time": "$.event_attributes.modified_time",
"formats": {
"user_time": "dd-MM-yyyy HH:mm:ss"
}
},
"batching": false,
"payload_validations": {}
}
✅ Key Points: create_all works
inside both user_attributes and event attr
objects simultaneously • Perfect for heavily customized CRM
payloads.
💡 Timestamps: Use epoch milliseconds (integer) when possible — no format specification needed! If your payload uses string timestamps, specify the format in the formats section.
| Format String | Example |
|---|---|
yyyy-MM-dd |
2024-10-25 |
yyyy/MM/dd |
2024/10/25 |
dd/MM/yyyy |
25/10/2024 |
dd-MM-yyyy |
25-10-2024 |
yyyy-MM-dd HH:mm:ss |
2024-10-25 14:30:00 |
dd-MM-yyyy HH:mm:ss |
25-10-2024 14:30:00 |
dd/MM/yyyy HH:mm:ss |
25/10/2024 14:30:00 |
yyyy/MM/dd HH:mm:ss |
2024/10/25 14:30:00 |
yyyy-MM-dd'T'HH:mm:ssZ |
2024-10-25T14:30:00+0530 |
yyyy-MM-dd HH:mm:ss.SSSSSS |
2024-10-25 14:30:00.123456 |
yyyy-MM-dd'T'HH:mm:ss.SSSSSS'Z' |
2024-10-25T14:30:00.123456Z |
Also supported: dd/MM/yyyy and dd-MM-yyyy variants with T, Z, and microsecond suffixes.
Use the Visual Mapper Generator to build your JSON configuration with a guided interface.
Submit: Send Your Mapper to MoEngage
Once your mapper JSON is ready, email support@moengage.com with:
- Completed mapper JSON (attached)
- Sample webhook payload(s)
- Integration details: partner name, Workspace ID, Data Center, use case
Configure: Add the Webhook URL
MoEngage will provide you a unique webhook URL. Add it to your source system with Basic Auth credentials (Workspace ID as username, Data API Key as password).
For events:
https://api-0X.moengage.com/v1/partner/<partner_name>/events/?configName=<config_name>
For users:
https://api-0X.moengage.com/v1/partner/<partner_name>/users/?configName=<config_name>
Test: Verify Your Data Flow
Use Postman to send your sample payload to the webhook URL. Verify a
200 OK response, save the
request_id,
then check MoEngage Dashboard → Analytics → Events (allow 1-2 minutes
for processing).
✅ Understanding API Responses
Different response codes indicate different outcomes:
Status: Your request was successfully received and queued for processing.
{
"request_id": "uhdvtjxa",
"name": "integration_name",
"description": "Successfully submitted for processing"
}
💡 Next Steps: Save the
request_id.
Your data should appear in MoEngage within 1-2 minutes.
Cause: A field defined as mandatory in your mapper is missing from the payload.
{
"error": {
"code": "e_9004",
"message": "Required field 'Request.data.actions.action' is missing in the payload",
"target": "payload_validation",
"details": [{ "code": "e_9004", "target": "payload_validation", "message": "Required field 'Request.data.actions.action' is missing in the payload" }]
}
}
🔧 How to Fix: Verify JSONPath expressions match your payload structure and ensure all required fields are present.
Cause: Your JSON payload is malformed or doesn't match the expected structure.
{
"error": {
"code": "e_9004",
"message": "The provided payload is invalid",
"target": "payload_validation",
"details": [{ "code": "e_9004", "target": "payload_validation", "message": "The provided payload is invalid" }]
}
}
🔧 How to Fix: Validate JSON using JSONLint and check for missing braces, brackets, or commas.
Cause: Invalid Basic Auth credentials or missing Authorization header.
{
"status": "fail",
"error": { "message": "Invalid authorization format. Expected Basic authentication", "type": "Authentication required", "request_id": "70e2fd" }
}
🔧 How to Fix: In Postman, select Basic Auth and enter your Workspace ID as username and Data API Key as password.
If you encounter an error response not covered above, contact MoEngage Solutions Engineering with:
- Complete error response (JSON)
- Your mapper configuration (JSON)
- Sample payload you were testing (JSON)
- Expected behavior vs actual behavior
🔧 Common Issues
Verify 200 OK response • Check customer_id path
• Confirm timestamp format • Wait 2-3 min • Contact MoEngage
with request_id
Test JSONPath at jsonpath.com • Verify payload structure • Check field name typos (case-sensitive)
Match format string exactly • Use epoch ms when possible • Include timezone for ISO format
Set batching=true • Use [*] in all
paths • Check array location in payload
Verify path points to object • Ensure key-value pairs • Check nesting (works one level deep)
🛠️ Helpful Tools
❓ FAQ
Yes. Include both user_attributes and attr in your mapper. Profile updates and event creation happen simultaneously.
A new profile is created automatically. If customer_id already exists, the event associates with the existing profile.
Yes. Contact MoEngage with the updated JSON. They'll deploy a new version with a new config_name. Your old URL continues working with the previous mapper.
Maximum 1MB recommended. For bulk requests, batch 100-500 events per call for optimal performance.
Use JSONPath notation: $.orders[0].items[0].name for specific items or $.orders[*].items[*].name for batching. Complex nesting may require flattening first.
🆘 Support
Need Help?
- 💬 General Questions: Contact your MoEngage Customer Success Manager
-
🔧 Technical Support: Email support@moengage.com with:
- Mapper JSON (attached)
- Sample webhook payload
- Request ID from response
- Issue description
- 🎨 Mapper Help: Share your payload & requirements - our team will assist