CallRail Webhooks
Connect CallRail to MoveRight with two webhook endpoints. The precall webhook fires when a call rings in — creating a lead and logging the call before your agent picks up. The postcall webhook fires after the call ends — updating attribution data and appending call details to the job timeline.
Overview
Precall webhook
POST /api/callrail/precall Postcall webhook
POST /api/callrail/postcall
MoveRight receives real-time call data from CallRail via two webhook endpoints. Both endpoints accept application/x-www-form-urlencoded bodies (the default CallRail format) and resolve the MoveRight zone from the request context.
Together, the two webhooks provide a complete call-to-lead pipeline:
- 1 A caller dials your CallRail tracking number
- 2 CallRail fires the <strong>precall webhook</strong> before the phone rings at your office
- 3 MoveRight matches the caller to an existing job (by phone number) or creates a new lead
- 4 An "Inbound Call" comment appears on the job timeline in real time
- 5 After the call ends, CallRail fires the <strong>postcall webhook</strong> with full attribution data (recording URL, UTM params, duration, transcription)
- 6 MoveRight appends the call details and marketing source data to the job
id field is stored as an externalMessageID on the inbound call comment. If CallRail sends the same call event twice (e.g., retries), MoveRight deduplicates by this ID rather than creating a duplicate comment thread.
Setup in CallRail
In your CallRail account, create two webhooks:
- 1
Go to Settings → Integrations → Webhooks
Create a new webhook for each event type.
- 2
Precall webhook
Set the URL to <code>https://moveright.app/api/callrail/precall?zone=YOUR_ZONE_ID</code> and select the "Before Call Connected" trigger. This fires when a call starts ringing, before anyone picks up.
- 3
Postcall webhook
Set the URL to <code>https://moveright.app/api/callrail/postcall?zone=YOUR_ZONE_ID</code> and select the "Call Ended" trigger. This fires after the call completes, with full attribution data.
- 4
Enable CallRail in MoveRight
In your MoveRight zone settings, set <code>callrail.enabled</code> to <code>true</code>. See <a href="#zone-config" class="text-brand-green hover:underline">Zone Configuration</a> for all available settings.
zone query parameter is required. CallRail does not send zone information in the webhook body — it must be embedded in the URL you configure in CallRail. Use the MoveRight zone ID for the franchise location that owns the tracking number.
Precall Webhook
Endpoint
POST /api/callrail/precall?zone=ZONE_ID Content-Type
application/x-www-form-urlencoded
The precall webhook fires before the phone rings at your office. It is designed to be fast — MoveRight returns 200 OK immediately and processes everything asynchronously so CallRail never times out.
When a call comes in, MoveRight:
- 1 Looks up the caller by phone number to find an existing job
- 2 If no job exists and <code>callrail.createJobInPrecall</code> is enabled, creates a new lead job using the caller's data
- 3 If a job already exists, updates its marketing source/origin data (without overwriting existing values)
- 4 Adds an "Inbound Call" comment to the job with the CallRail call ID for thread tracking
Precall Responses
| Status | Condition | Description |
|---|---|---|
| 200 | Missing phone | Returned immediately. Processing happens asynchronously after the response is sent. |
| 400 | Missing phone | The <code>customer_phone_number</code> field is missing or empty. |
Postcall Webhook
Endpoint
POST /api/callrail/postcall?zone=ZONE_ID Content-Type
application/x-www-form-urlencoded The postcall webhook fires after the call ends and includes the full set of call data: recording URL, duration, transcription, UTM attribution, and more.
When MoveRight receives a postcall webhook:
- 1 Looks up the caller by phone number to find the existing job
- 2 If no job is found, returns <code>"NO JOB"</code> and exits
- 3 If CallRail is not enabled for the zone, returns <code>"CALLRAIL NOT ENABLED IN JOB ZONE"</code>
- 4 If <code>callrail.postCallUpdateSource</code> is enabled, parses UTM/attribution data and updates the job's source (without overwriting existing values)
- 5 Appends a comment to the existing precall thread (or creates a new one) with the call details, tracking number, and source data
Postcall Responses
| Status | Body | Condition |
|---|---|---|
| 200 | (empty) | Call processed successfully |
| 200 | NO JOB | No existing job found for the caller's phone number |
| 200 | CALLRAIL NOT ENABLED… | The <code>callrail.enabled</code> zone config is false |
| 400 | (empty) | The <code>customer_phone_number</code> field is missing or empty |
Request Fields
Both webhooks accept the same payload — the standard CallRail webhook body. Only customer_phone_number is required. The full field list is documented below for reference; you don't need to send all fields.
Required fields
| Field | Required | Description |
|---|---|---|
| customer_phone_number | Yes | The caller's phone number in E.164 format (e.g., <code>+17035550123</code>). Used to match against existing customers in MoveRight and as the primary phone number for new leads. |
| id | Recommended | CallRail's unique call identifier. Stored as <code>externalMessageID</code> for deduplication — if CallRail retries the same webhook, MoveRight updates the existing record rather than creating a duplicate. |
| direction | Recommended | <code>"inbound"</code> or <code>"outbound"</code>. Stored on the call thread for filtering. |
Caller identification
| Field | Description |
|---|---|
| customer_name | Caller name from caller ID. MoveRight uses AI to parse this into given/family name for new leads. Handles formats like "Last, First" and filters out "Wireless Caller" / "Unavailable". |
| customer_city | Caller's city, inferred from area code or geolocation. |
| customer_state | Caller's state or province code (e.g., "OH", "ON"). |
| customer_country | Two-letter country code (e.g., "US", "CA"). |
| source_name | Human-readable tracking source name (e.g., "Google Ads", "Bing"). Used as the referral source when creating a new lead. |
| business_phone_number | The tracking number the caller dialed (E.164 format). Stored in call metadata. |
Call details
| Field | Description |
|---|---|
| call_type | Type of call: <code>"abandonded"</code>, <code>"answered"</code>, <code>"in_progress"</code>, <code>"missed"</code>, <code>"outbound"</code>, <code>"voicemail"</code>, or <code>"voicemail_transcription"</code>. |
| answered | Whether the call was answered (<code>boolean</code>). |
| duration | Call duration in seconds (as a string). |
| recording | URL to the call recording audio file. |
| transcription | Full call transcription text. |
| voicemail | Whether the call ended in voicemail (<code>boolean</code>). |
| note | Any notes attached to the call in CallRail. |
| tags / detail_tags | Array of tag names applied to the call. |
| agent_email | Email of the MoveRight agent who answered, if known. |
| spam | Whether CallRail flagged the call as spam (<code>boolean</code>). |
| first_call | Whether this is the caller's first call to this company (<code>boolean</code>). |
| total_calls | Total number of calls from this customer (number). |
| start_time | ISO 8601 timestamp when the call started. |
| created_at | ISO 8601 timestamp when the CallRail record was created. |
Marketing attribution
These fields are captured by CallRail from the caller's web session and mapped to MoveRight job source data. See Attribution Mapping for how each field is used.
| Field | Description |
|---|---|
| utm_source | UTM source parameter (e.g., "google", "bing"). |
| utm_medium | UTM medium parameter (e.g., "cpc", "organic"). |
| utm_campaign | UTM campaign name. Fallback: <code>campaign</code> or <code>milestones.last_touch.campaign</code>. |
| utm_term | UTM term/keyword. Fallback: <code>keywords</code> or <code>milestones.last_touch.keywords</code>. |
| utm_content | UTM content parameter. |
| gclid | Google Click ID for Google Ads attribution. |
| fbclid | Facebook Click ID. |
| landing_page_url | The page URL the caller was on when they dialed. |
| referrer_domain | The domain of the referring site (e.g., "google.com"). |
| source | Raw source identifier from CallRail. |
| medium | Marketing medium (e.g., "cpc", "organic"). |
| keywords | Keywords associated with the call. |
| campaign | Campaign name tied to the call. |
| callsource | CallRail source label — often the ad network or extension name. |
AI and sentiment
| Field | Description |
|---|---|
| call_summary | AI-generated summary of the call. |
| conversational_transcript | Full conversational transcript with speaker labels. |
| sentiment | Auto-scored sentiment label. |
| speaker_percent | Object mapping speaker labels to percentage of time spoken. |
| call_highlights | Key moments identified in the call. |
| keywords_spotted | Keywords spotted during transcription. |
Company and tracking
| Field | Description |
|---|---|
| company_id | Numeric ID of the company within CallRail. |
| company_name | Name of the company. |
| company_time_zone | Company timezone in IANA format (e.g., "America/New_York"). |
| tracking_phone_number | The tracking number dialed (E.164 format). |
| formatted_tracking_phone_number | Tracking number formatted for display. |
| formatted_customer_phone_number | Customer number formatted for display. |
| lead_status | Lead quality status: "good_lead", "not_a_lead", etc. |
| value | Assigned monetary value of the call. |
| timeline_url | URL to view the lead's timeline in CallRail. |
callercity, callercountry, callername, callernum, callerstate, callsource, datetime, destinationnum, ip, kissmetrics_id, landingpage, referrer, referrermedium, score, tag, trackingnum, timestamp, and resource_id variants). These are accepted for backward compatibility but the structured fields above are preferred.
Attribution Mapping
MoveRight maps CallRail webhook fields to internal job source data. Source data is never overwritten — if a job already has source data from an earlier touchpoint, the CallRail data is skipped for those fields.
| MoveRight field | CallRail field | Notes |
|---|---|---|
| entry_gclid | gclid | Google Click ID for Ads attribution |
| entry_current_url | landing_page_url | Page the caller was on |
| entry_referring_domain | referrer_domain | Domain of referring site |
| entry_utm_source | utm_source | |
| entry_utm_medium | utm_medium | |
| entry_utm_campaign | utm_campaign | Fallback: <code>campaign</code> then <code>milestones.last_touch.campaign</code> |
| entry_utm_term | utm_term | Fallback: <code>keywords</code> then <code>milestones.last_touch.keywords</code> |
| entry_utm_content | utm_content | |
| entry_gad_source | callsource | Ad network or extension name |
| session_ip | ip | Caller's IP address |
Zone Configuration
CallRail integration is controlled per zone (franchise location) through zone settings. Configure these in MoveRight under Settings → Business → CallRail.
| Setting | Default | Description |
|---|---|---|
| callrail.enabled | false | Master switch. When false, both webhooks return early without processing. |
| callrail.createJobInPrecall | false | When true and no existing job is found, the precall webhook creates a new lead job automatically. When false, only the postcall webhook can create jobs (if the call comes through the datadip endpoint). |
| callrail.postCallUpdateSource | false | When true, the postcall webhook updates the job's marketing source data (UTM params, GCLID, etc.) without overwriting existing values. |
| callrail.defaultSource | (none) | Fallback source name used when CallRail doesn't provide one. Examples: "Google Ads", "Bing", "Organic". |
| callrail.defaultOrigin | (none) | Fallback origin used when CallRail doesn't provide one. Examples: "Web", "Phone", "Referral". |
| openai.parseCallRailCallerIdPrompt | "Turn this caller ID into a first and last name…" | The AI prompt used to parse caller ID names like "SMITH, JOHN Q" into structured first/last names for new leads. |
cURL Example
Simulate a CallRail postcall webhook with curl:
curl -X POST \
"https://moveright.app/api/callrail/postcall?zone=YOUR_ZONE_ID" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "id=call-12345" \
-d "customer_phone_number=%2B17035550123" \
-d "customer_name=Alice+Wonderland" \
-d "direction=inbound" \
-d "call_type=answered" \
-d "answered=true" \
-d "duration=180" \
-d "tracking_phone_number=%2B18005551212" \
-d "source_name=Google+Ads" \
-d "utm_source=google" \
-d "utm_medium=cpc" \
-d "utm_campaign=fall-move-promo" \
-d "utm_term=moving+company+near+me" \
-d "gclid=CjwKCAjw-ZCDBhBQEiw" \
-d "landing_page_url=https://example.com/movers" \
-d "referrer_domain=google.com" \
-d "recording=https://cdn.callrail.com/recordings/call-12345.mp3" -d "transcription=Hi+I+need+help+with+a+move+next+week" application/x-www-form-urlencoded by default. If you configure your webhook in CallRail to use JSON, it will also work — the endpoint accepts both formats.
Common Use Cases
Speed-to-lead automation
When <code>callrail.createJobInPrecall</code> is enabled, inbound calls automatically create a lead job before anyone picks up the phone. Your team sees the customer's name, number, and call source instantly in the MoveRight activity feed — no manual data entry.
Marketing attribution for phone calls
The postcall webhook captures UTM parameters, GCLID, and referrer data from every call. MoveRight stores this on the job so you can trace revenue back to specific ad campaigns, keywords, and landing pages — even for phone conversions.
Unified call timeline
Every call creates an "Inbound Call" comment thread on the job. The precall creates the initial comment, and the postcall appends call details (duration, recording, transcription, tracking number) to the same thread. Your team sees the full call story in one place.
CallRail + Datadip for screen pop
Use the <a href="/docs/api/datadip/" class="text-brand-green hover:underline">Datadip Voice Endpoint</a> alongside CallRail webhooks. Datadip runs during the call for real-time screen pop data, while CallRail webhooks handle lead creation and post-call attribution. They work together seamlessly — Datadip creates the inbound call comment, and the CallRail postcall appends to the same thread.