POST /api/v1/message-event
GHL webhook handler for inbound and outbound messages
Message Event
Processes GHL message webhooks — handles deduplication, contact lookup, conversation management, sleep mode, channel exclusion, and AI response triggering.
Volume: ~41M runs/week | Auth: Location token (via SubAccount lookup)
Request
POST /api/v1/message-event
Content-Type: application/jsonBody Parameters
| Field | Type | Required | Description |
|---|---|---|---|
locationId | string | ✅ | GHL location ID |
messageType | string | ✅ | Channel type (SMS, Email, etc.) |
messageId | string | GHL message ID (used for dedup) | |
contactId | string | GHL contact ID | |
conversationId | string | GHL conversation ID | |
body | string | Message body text | |
direction | string | inbound or outbound | |
type | string | Message type (Custom normalizes to SMS) | |
source | string | Message source | |
dateAdded | string | ISO timestamp | |
status | string | Message status | |
attachments | array | File attachments | |
appId | string | GHL app ID | |
threadId | string | Thread ID | |
webhookId | string | Webhook ID |
Response
All responses return HTTP 200 (webhook best practice).
Missing messageType
{ "messageType": false }Location not found
{ "status": "location doesnt exist" }Archived account
{ "status": "Archived account" }Duplicate message
{ "messageId": false }AI response triggered
{ "status": "ai response pending" }Sleep mode active
{ "direction": "inbound", "sleep": true }Channel excluded
{ "status": "channel excluded" }AI off (contact tag)
{ "status": "ai_off" }Success
{ "status": "sent to platform" }Test with cURL
curl -X POST http://localhost:3000/api/v1/message-event \
-H "Content-Type: application/json" \
-d '{
"locationId": "your_location_id",
"messageType": "SMS",
"messageId": "msg_test_001",
"contactId": "contact_123",
"conversationId": "conv_456",
"body": "Hello, I need help",
"direction": "inbound"
}'Pipeline Flow
- Validate
messageTypeexists - Look up SubAccount by
locationId(Prisma) - Check archive status and access token
- Dedup check by
ghlMessageId(Prisma) - Create message record (Prisma)
- Fetch contact from GHL API
- Parallel: active tags, conversation upsert, ingestion
- Link message to conversation
- Assistant lookup → sleep check → AI run decision
- If AI enabled: add tag → mark replying → trigger
chatRun