https://retellai.com/?utm_source=discord&utm_medium=seo logo
what is the best way of referencing dynamic variab...
# ❓・ai-evy-support
i
@AI Evy what is the best way of referencing dynamic variables in prompting in conversations nodes?
a
Thanks for tagging me @Idriss. I'll look through relevant sources and get back to you shortly.
### Answer To reference dynamic variables in conversation node prompts, use the
{{variable_name}}
syntax with double curly braces. [(1) ]() Key points: - Reference variables directly by name:
{{customer_name}}
,
{{order_id}}
, etc. - Spaces around the variable name will be trimmed automatically when evaluated. - Use the variable name directly (e.g.,
{{make}}
) rather than dot notation like
scheduling_info_extraction.make
— dot notation won't work. [(2) ]() Example in a prompt:
Copy code
"Hello {{user_name}}, I understand you're interested in {{product_name}}. How can I help you today?"
[(1) ]() Handling missing variables: If a variable has no value, it remains in raw form with curly braces intact. You can add conditional logic in your prompt:
Copy code
If {{user_name}} appears with curly braces, use a generic greeting.
Otherwise, greet the customer by name.
You can also check if a variable exists in conversation flow equations using:
Copy code
Equation: {{user_name}} exists
Result: True if variable is set
​ To learn more, see the sources I considered: [[1] Dynamic Variables](https://docs.retellai.com/build/dynamic-variables) [[2] Extracted Dynamic Variables not sending through custom function in Conversation Flow Prompt](https://discord.com/channels/1186424376300679209/1442542149546803312) ​
Feel free to tag @AI Evy with additional questions. I also appreciate feedback, it helps me improve.
a
Thanks for the follow-up, @Idriss. I'll get back to you soon.
### Answer I can see you've shared an image of your dynamic variables, but I'm unable to view the image content directly. However, I can explain how to reference dynamic variables in your prompts based on their names. General syntax: Use double curly braces with the exact variable name:
{{variable_name}}
[(1) ]() Example prompt language: If your dynamic variables include fields like
customer_name
,
appointment_date
, or
order_id
, you would write prompts like:
Copy code
"Hello {{customer_name}}, I see you're calling about order {{order_id}}."
Important formatting notes: - No spaces inside the braces — use
{{provider_info}}
not
{{ provider_info }}
[(2) ]() - Spaces around the variable name are trimmed automatically when evaluated [(1) ]() - All dynamic variable values must be strings Handling potentially empty variables: Add conditional logic in your prompt:
Copy code
If {{user_name}} appears with curly braces, use a generic greeting.
Otherwise, greet the customer by name.
If you can share the specific variable names you're working with (typed out) , I can help you craft the exact prompt language for your use case.
​ To learn more, see the sources I considered: [[1] Dynamic Variables](https://docs.retellai.com/build/dynamic-variables) [[2] Issue with Dynamic Variables.](https://discord.com/channels/1186424376300679209/1336469878068219904) ​
Feel free to tag @AI Evy with additional questions. I also appreciate feedback, it helps me improve.
i
Business Name: Test Business Call Direction: outbound Email: idrizz28@gmail.com First Name: Test Last Name: Tester Niche: HVAC Phone Number: +63 949 876 0508 @AI Evy these are the variables that were passed to retell. how do I prompt these to my nodes
a
Thanks for the follow-up, @Idriss. I'll get back to you soon.
### Answer Based on your variables, here's how to reference them in your node prompts using the
{{variable_name}}
syntax: [(1) ]() Your variables mapped to prompt syntax: - Business Name →
{{Business Name}}
or
{{business_name}}
(depending on how you passed them) - Call Direction →
{{Call Direction}}
or
{{call_direction}}
- Email →
{{Email}}
or
{{email}}
- First Name →
{{First Name}}
or
{{first_name}}
- Last Name →
{{Last Name}}
or
{{last_name}}
- Niche →
{{Niche}}
or
{{niche}}
- Phone Number →
{{Phone Number}}
or
{{phone_number}}
Example prompt in a node:
Copy code
"Hello {{First Name}}, I'm calling from {{Business Name}}. I understand you're in the {{Niche}} industry. How can I help you today?"
Important: The variable name inside the curly braces must match exactly how you passed them in your
retell_llm_dynamic_variables
field when creating the call. If you passed them with spaces (e.g.,
"First Name": "Test"
) , use
{{First Name}}
. If you used snake_case (e.g.,
"first_name": "Test"
) , use
{{first_name}}
. Check your API call payload to confirm the exact key names you're using.
​ To learn more, see the sources I considered: [[1] Dynamic Variables](https://docs.retellai.com/build/dynamic-variables) ​
Feel free to tag @AI Evy with additional questions. I also appreciate feedback, it helps me improve.
i
@AI Evy I have a logic node that has this as a prompt: {{Call Direction}} = outbound and if true it goes to aunique path. is this prompting correct?
a
Thanks for the follow-up, @Idriss. I'll get back to you soon.
### Answer --- Yes, your approach is correct for a Logic Split Node. The syntax
{{Call Direction}} = outbound
is the right way to set up a condition that checks if your dynamic variable equals a specific value. [(1) ]() A few things to verify: 1. Exact variable name match — Make sure
Call Direction
matches exactly how you passed it in your API call (including spaces and casing) . If you passed it as
call_direction
, use
{{call_direction}} = outbound
instead. [(2) ]() 2. String matching — The value
outbound
should match exactly what you're passing. Ensure there are no extra spaces or different casing in your API payload. 3. Else destination — Logic Split Nodes always require an else destination as the default path if none of the conditions are met. Make sure you have that configured. [(1) ]() Example condition setup:
Copy code
{{Call Direction}} = outbound
If the condition isn't working as expected, double-check your API payload to confirm the exact key name and value format you're sending under
retell_llm_dynamic_variables
. [(2) ]()
​ To learn more, see the sources I considered: [[1] Logic Split Node](https://docs.retellai.com/build/conversation-flow/logic-split-node) [[2] Logic Split Node Equation Not Working As Expected](https://discord.com/channels/1186424376300679209/1406310248691667125) ​
Feel free to tag @AI Evy with additional questions. I also appreciate feedback, it helps me improve.
i
@AI Evy in conversation nodes in the conversational flow agent, what is the best practice when prompting inside these nodes? should one treat it like a mini system prompt? examples of complex prompting insdie these
a
Thanks for the follow-up, @Idriss. I'll get back to you soon.
### Answer ### Best Practices for Prompting in Conversation Nodes Yes, you can think of each conversation node prompt as a focused, mini system prompt for that specific step in the conversation. Each node should define a small set of logic for that particular stage. [(1) ]() How to divide your prompting: - Global prompt → Use for your agent's overall personality, tone, and universal rules [(2) ]() - Conversation node prompts → Use for step-by-step logic, specific questions, and transitions for that node - Global nodes → Use only for actions available anytime (e.g., "transfer to human") Why this approach works: By breaking complex workflows into individual nodes, you get: [(3) ]() - Precise Control — Define exact behavior for each conversation scenario - Better Performance — Fine-tune specific parts without affecting others - Easier Debugging — Isolate and fix issues in specific conversation paths When to break down a node further: Consider splitting a node when: - The node handles multiple complex logic paths - The LLM struggles with consistency (hallucinations or incorrect responses) - You need different settings (model, temperature) for different parts - The conversation flow becomes hard to follow or debug Example node instruction structure: Based on the API response schema, a conversation node instruction looks like: [(4) ]()
Copy code
json
{
  "id": "collect_info",
  "type": "conversation",
  "instruction": {
    "type": "prompt",
    "text": "Ask the customer for their name and contact information."
  }
}
So for your HVAC outbound use case, a node prompt might be:
Copy code
Introduce yourself as calling from {{Business Name}}. 
Greet {{First Name}} {{Last Name}} and confirm they are the right person.
If confirmed, explain you're reaching out about their {{Niche}} needs.
Keep the tone professional but friendly.
The key is keeping each node focused on one specific task or conversation stage rather than trying to handle everything in one prompt.
​ To learn more, see the sources I considered: [[1] Conversation Flow Overview](https://docs.retellai.com/build/conversation-flow/overview) [[2] CONVERSATION FLOW AGENT - GLOBAL PROMPT](https://discord.com/channels/1186424376300679209/1371534043157500025) [[3] Node Overview](https://docs.retellai.com/build/conversation-flow/node) [[4] Get Conversation Flow](https://docs.retellai.com/api-references/get-conversation-flow) ​
Feel free to tag @AI Evy with additional questions. I also appreciate feedback, it helps me improve.
i
@AI Evy how do I set up a failsafe for whenever my custom functions in conversation flow agents API request failed. So every time that I get this error for my custom function: {"result":"{\"success\": false, \"error\": \"GHL API request failed: 400 Client Error: Bad Request for url: https://services.leadconnectorhq.com/contacts\", \"message\": \"Failed to book appointment: GHL API request failed: 400 Client Error: Bad Request for url: https://services.leadconnectorhq.com/contacts\"}"} how do I set it up to have a failsafe where I call the same custom function for another attempt?
a
Thanks for the follow-up, @Idriss. I'll get back to you soon.
### Answer ### Answer Based on the documentation, Retell's built-in retry behavior works like this: when a custom function request fails, it will be retried up to 2 times automatically. [(1) ]() However, your specific issue is that you're receiving an HTTP 400 response, which is actually a "successful" HTTP request from Retell's perspective (the server responded) . The 400 status indicates a client-side error with your request payload to the GHL API. Options for handling this: 1. Return a 2xx status from your endpoint — Your custom function endpoint should return a 200-299 status code to indicate HTTP success, then include the error details in the response body for the agent to handle conversationally. [(2) ]() 2. Handle retries in your backend — Implement retry logic within your custom function endpoint itself before returning a response to Retell. 3. Use conversation flow logic — After the function node, you can use a Logic Split Node to check the response. Extract the
success
field from your response into a dynamic variable using "Set response variables," then branch based on whether
{{success}} = false
to route back to retry the function or handle the error gracefully. For the response variable extraction: You can extract values from the API response and save them as dynamic variables for use later in the conversation. [(1) ]() Example: If your response body contains
{"success": false, "error": "..."}
, you can extract
success
as a dynamic variable, then use a Logic Split Node to check
{{success}} = false
and route accordingly. I don't have documentation on a built-in "retry on application-level failure" feature within conver
sation flows. The automatic retry (up to 2 times) only applies when the HTTP request itself fails, not when your API returns a 400 error.
​ To learn more, see the sources I considered: [[1] Custom Function](https://docs.retellai.com/build/conversation-flow/custom-function) [[2] custom function](https://discord.com/channels/1186424376300679209/1354455400996409576) ​
Feel free to tag @AI Evy with additional questions. I also appreciate feedback, it helps me improve.
i
@AI Evy so with Use conversation flow logic — After the function node, you can use a Logic Split Node to check the response. Extract the success field from your response into a dynamic variable using "Set response variables," then branch based on whether {{success}} = false to route back to retry the function or handle the error gracefully. can I use the transition to have the type of response to a fallback? if yes, how should it be worded? https://cdn.discordapp.com/attachments/1451603496833908941/1453101841637310656/image.png?ex=694c3a0c&is=694ae88c&hm=89b8bdf5063ded070d37d3f5eb3e074296b7b8fa8196dcfec347dde62ba5eee3&
a
Thanks for the follow-up, @Idriss. I'll get back to you soon.
### Answer --- Yes, you can use a Logic Split Node to check the response and route to a fallback path. [(1) ]() How to set it up: 1. Extract the response variable — In your custom function node, use "Set response variables" to extract the
success
field from your API response into a dynamic variable like
{{success}}
. 2. Add a Logic Split Node after the function node. 3. Configure the condition — Use an equation-based condition: -
{{success}} = false
→ routes to your fallback/retry path - The else destination handles the success case Important: Make sure you're using "Response Variables" to properly set the variable value. The LLM may understand what you mean conversationally, but for equation-based conditions in Logic Split Nodes, you need to explicitly set the variable to the right value. [(2) ]() For the condition wording:
Copy code
{{success}} = false
[(1) ]() , [(3) ]() This routes to your fallback node when the function returns a failure. The else path handles successful responses. Note: If you're experiencing issues where the Logic Split Node always goes to else, verify that your variable is being properly populated by checking the call history for the actual value (not just the variable name) . [(3) ]()
​ To learn more, see the sources I considered: [[1] Logic Split Node](https://docs.retellai.com/build/conversation-flow/logic-split-node) [[2] {{variable}} / Logic Split Node Issue](https://discord.com/channels/1186424376300679209/1417167639524151441) [[3] Logic split node stuck](https://discord.com/channels/1186424376300679209/1435621223294959616) ​
Feel free to tag @AI Evy with additional questions. I also appreciate feedback, it helps me improve.
i
@AI Evy can you explain this more thoroughly please: Extract the response variable — In your custom function node, use "Set response variables" to extract the success field from your API response into a dynamic variable like {{success}}.
a
Thanks for the follow-up, @Idriss. I'll get back to you soon.
### Answer ### Setting Response Variables in Custom Functions When your custom function returns a response from an external API, you can extract specific values from that response and save them as dynamic variables for use later in the conversation. [(1) ]() Step-by-step: 1. In your Custom Function node, scroll to the "Set response variables" section (step 7 in the custom function setup) . 2. Define which fields to extract from your API response. You map a path in the response JSON to a dynamic variable name. Example: If your API returns this response body:
Copy code
json
{
  "properties": {
    "user": {
      "name": "John Doe",
      "age": 26
    }
  }
}
You can extract the user's name and reference it later using
{{user_name}}
. For your use case: If your GHL API response looks like
{"success": false, "error": "..."}
, you would: 1. In "Set response variables," map the
success
field to a variable name like
success
2. After the function executes,
{{success}}
will contain
false
or
true
3. Use a Logic Split Node with the condition
{{success}} = false
to route to your fallback path Important notes: - If the response is an array (not an object) , you need to specify the index, like
patient[0].providerNo
. [(2) ]() - Make sure the variable name you reference later matches exactly what you defined in the response variables section.
​ To learn more, see the sources I considered: [[1] Custom Function](https://docs.retellai.com/build/conversation-flow/custom-function) [[2] Custom Function API call response not being saved as dynamic variable](https://discord.com/channels/1186424376300679209/1444715331372322917) ​
Feel free to tag @AI Evy with additional questions. I also appreciate feedback, it helps me improve.
i
@AI Evy so the name of the dynamic variable should be "success" and the path to the value in response body should be what? https://cdn.discordapp.com/attachments/1451603496833908941/1453104200501166231/image.png?ex=694c3c3e&is=694aeabe&hm=273fd5e50a0f28d8b934905eb8911af7ef05c405438986567b4649e5f04fc1c7&
a
Thanks for the follow-up, @Idriss. I'll get back to you soon.
### Answer Based on your API response structure, here's how to set it up: Your response body looks like:
Copy code
json
{"result":"{\"success\": false, \"error\": \"...\", \"message\": \"...\"}"}
[(1) ]() , [(2) ]() The path depends on your response structure: If your response is a simple object like:
Copy code
json
{
  "success": false,
  "error": "..."
}
Then the path would just be
success
. [(1) ]() , [(2) ]() However, looking at your actual error response, it appears the data is nested inside a
result
field as a stringified JSON. If your endpoint returns the response in this nested format, you may need to: 1. Modify your backend to return a cleaner response structure like:
Copy code
json
{
  "success": false,
  "error": "GHL API request failed..."
}
2. Then in "Set response variables": - Variable Name:
success
- Path:
success
Example from documentation: For a response body like:
Copy code
json
{
  "properties": {
    "user": {
      "name": "John Doe",
      "age": 26
    }
  }
}
You would use
properties.user.name
as the path to extract the name. [(1) ]() , [(2) ]() So for your case with a clean response, the path is simply
success
to extract the boolean value.
​ To learn more, see the sources I considered: [[1] Integrate any system with custom function](https://docs.retellai.com/build/single-multi-prompt/custom-function) [[2] Custom Function](https://docs.retellai.com/build/conversation-flow/custom-function) ​
Feel free to tag @AI Evy with additional questions. I also appreciate feedback, it helps me improve.
i
@AI Evy so this API response is when the API request failed so the function didn't do it's job: {"result":"{\"success\": false, \"error\": \"GHL API request failed: 400 Client Error: Bad Request for url: https://services.leadconnectorhq.com/contacts\", \"message\": \"Failed to book appointment: GHL API request failed: 400 Client Error: Bad Request for url: https://services.leadconnectorhq.com/contacts\"}"} this is the API response when it works: {"result":"{\"success\": true, \"appointment_id\": \"gPYKvv35v6AcMX5isBdX\", \"contact_id\": \"6eUwVaBSmNmpmJRIRJW4\", \"start_time\": \"2025-12-26T10:30:00-06:00\", \"end_time\": \"2025-12-26T11:00:00-06:00\", \"message\": \"Appointment booked for Ivan Romanov on December 26, 2025 at 10:30 AM\", \"details\": {\"title\": \"LeadKast Discovery Call\", \"duration\": \"30 minutes\", \"contact\": \"Ivan Romanov\", \"phone\": \"+16157038566\", \"email\": \"IvanRomanov@gmail.com\"}}"} so I need you to tell me how to do this
a
Thanks for the follow-up, @Idriss. I'll get back to you soon.
### Answer Looking at your response structure, the data is nested inside a
result
field as a stringified JSON string. This is the challenge — the
success
field isn't directly accessible at the top level. The issue: Your response looks like:
Copy code
json
{"result":"{\"success\": true, ...}"}
[(1) ]() The
result
field contains a string (not a parsed object) , so you can't use dot notation like
result.success
to extract it directly. Recommended solution: Modify your backend endpoint to return a cleaner, parsed response structure:
Copy code
json
{
  "success": true,
  "appointment_id": "gPYKvv35v6AcMX5isBdX",
  "message": "Appointment booked..."
}
, [(2) ]() Then in your Custom Function's "Set response variables" section: - Variable Name:
success
- Path:
success
After that, use a Logic Split Node with the condition
{{success}} = false
to route to your retry/fallback path. [(3) ]() If you can't modify your backend: You could parse the stringified JSON within your backend before returning it to Retell, or handle the retry logic entirely within your backend endpoint itself before returning a response. You can also use dynamic variables inside the JSON of future function calls. [(5) ]()
​ To learn more, see the sources I considered: [[1] Custom Function](https://docs.retellai.com/build/conversation-flow/custom-function) [[2] Integrate any system with custom function](https://docs.retellai.com/build/single-multi-prompt/custom-function) [[3] Help with Logic Split Nodes (and an example of a custom variable being used would be lovely!)](https://discord.com/channels/1186424376300679209/1358825261986087126) ​
Feel free to tag @AI Evy with additional questions. I also appreciate feedback, it helps me improve.
i
@AI Evy I cannot modify the backend endpoint because this function was made using a marketplace app in GHL that essentially makes this like an MCP for retell communicating with GHL using the app in GHL.
a
Thanks for the follow-up, @Idriss. I'll get back to you soon.
### Answer Since you can't modify the backend and the response returns a stringified JSON inside the
result
field, you have a couple of options: Option 1: Use the full response conversationally All response formats (string, buffer, JSON object, blob) are converted to string before being sent to the LLM for further processing. [(1) ]() This means the agent can read the entire response text and understand whether it succeeded or failed based on the content. You can prompt your conversation node to:
Copy code
Check the function response. If it contains "success": false or mentions an error, apologize and offer to try again or collect the information differently.
Option 2: Use Extract Dynamic Variables function Based on community discussions, you can use the Extract Dynamic Variables function in your prompt to have the LLM parse and extract values from the response. [(2) ]() , [(3) ]() In your prompt, instruct the agent:
Copy code
After calling the booking function, extract whether the booking was successful from the response. If the response contains "success": false, save this as {{booking_failed}} = true.
Then use a Logic Split Node to check
{{booking_failed}} = true
to route to your retry path. Important note on response variables: When using description-based parameters (not const values) , include the variable reference in the description field itself, like:
Use this as the treatment id: {{treatment_id}}
. The limitation here is that Retell's response variable extraction works best with properly structured JSON responses where you can specify a direct path like
success
or
properties.user.name
. [(1) ]() , [(4) ]() With your stringified nested response, the LLM-based extraction approach is your best option since you cannot modify the endpoint.
​ To learn more, see the sources I considered: [[1] Custom Function](https://docs.retellai.com/build/conversation-flow/custom-function) [[2] Custom Function Not substituting variable](https://discord.com/channels/1186424376300679209/1450867178906714123) [[3] Dynamic Variable is not sending via custom function.](https://discord.com/channels/1186424376300679209/1430715756030722149) [[4] Integrate any system with custom function](https://docs.retellai.com/build/single-multi-prompt/custom-function) ​
Feel free to tag @AI Evy with additional questions. I also appreciate feedback, it helps me improve.
Thanks for the follow-up, @Idriss. I'll get back to you soon.
i
@AI Evy this is helpful but I need a solution that is involves nodes in a conversation flow agent, not in single-prompt agent.
a
### Answer --- Here's a node-based solution for your Conversation Flow agent to handle the stringified response from your GHL function: Approach: Use a Conversation Node after the Function Node to extract the result Since the LLM receives the full response as a string (all response formats are converted to string before being sent to the LLM for further processing) , you can use a Conversation Node immediately after your Function Node to have the agent parse and extract the success status. [(1) ]() Step-by-step setup: 1. Function Node — Calls your GHL booking function with "Wait for Result" enabled so the response is available before transitioning. 2. Conversation Node (immediately after) — Use instruction/prompt like:
Copy code
Review the booking function response. 
   If the response contains "success": true, extract and save {{booking_success}} as "true".
   If the response contains "success": false or mentions an error, save {{booking_success}} as "false".
   Do not speak to the user in this step.
​ To learn more, see the sources I considered: [[1] Function Node Overview](https://docs.retellai.com/build/conversation-flow/function-node) [[2] Logic Split Node](https://docs.retellai.com/build/conversation-flow/logic-split-node) [[3] Logic split node stuck](https://discord.com/channels/1186424376300679209/1435621223294959616) ​
3. Logic Split Node — Add conditions to branch based on the extracted variable: [(2) ]() - Condition:
{{booking_success}} = false
→ routes to your retry/fallback path - Else destination → routes to your success confirmation path Important configuration notes: - In the Function Node, enable "Wait for Result" so the response is ready before transitioning. [(1) ]() - The Logic Split Node always requires an else destination as the default path if none of the conditions are met. [(2) ]() - Make sure your variable name in the Logic Split condition matches exactly what you're extracting. [(3) ]() This approach uses the LLM's ability to read and understand the stringified response content, then saves the parsed value as a dynamic variable for the Logic Split Node to evaluate.
Feel free to tag @AI Evy with additional questions. I also appreciate feedback, it helps me improve.
i
@AI Evy I got the fallback to work. thanks! let's pivot. I have custom fields for gohighlevel that I want to be filled by the retell agent via custom function. how should I go about the JSON?
a
Thanks for the follow-up, @Idriss. I'll get back to you soon.
### Answer --- Great that the fallback is working! For your custom function JSON to pass custom fields to GoHighLevel, you'll define parameters using JSON schema format. The schema must include
"type": "object"
at the top level. [(1) ]() Example parameter schema structure:
Copy code
json
{
  "type": "object",
  "required": [
    "order_id"
  ],
  "properties": {
    "name": {
      "type": "object",
      "description": "",
      "properties": {
        "first_name": {
          "type": "string",
          "description": "User first name"
        },
        "last_name": {
          "type": "string",
          "const": "{{last_name}}"
        }
      }
    },
    "order_id": {
      "type": "number",
      "const": 1234
    }
  }
}
Key points for your GHL custom fields: - Use
"description"
for fields the LLM should determine from the conversation (e.g.,
"description": "The customer's preferred service type"
) - Use
"const"
for values that should always be a specific value or a dynamic variable (e.g.,
"const": "{{customer_phone}}"
) - Add fields to the
"required"
array if they must be present Troubleshooting: If you get a 400 error when saving, it's likely because the parameters are not valid. One common mistake is not adding
"type": "object"
to the top level of the JSON schema. , [(2) ]() Structure your properties to match whatever custom field names your GHL endpoint expects, using descriptions for LLM-resolved values and const for fixed or dynamic variable values.
​ To learn more, see the sources I considered: [[1] Custom Function](https://docs.retellai.com/build/conversation-flow/custom-function) [[2] 400 error when adding a function to a conversation flow](https://discord.com/channels/1186424376300679209/1411008678487916646) ​
Feel free to tag @AI Evy with additional questions. I also appreciate feedback, it helps me improve.
i
@AI Evy so if the GHL field unique keys are {{ contact.business_name }} as string, {{ contact.business_niche }} as an enum, {{ contact.business_website }} as string, where the enum selection is Auto-Dealers, Business Loan Brokers, Franchise Brokers, HVAC, MEDSPAS, Mortgage Lenders, how would the JSON look like now
a
Thanks for the follow-up, @Idriss. I'll get back to you soon.
Feel free to tag @AI Evy with additional questions. I also appreciate feedback, it helps me improve.
### Answer Based on the parameter schema format from the documentation, here's how your JSON would look for those GHL custom fields:
Copy code
json
{
  "type": "object",
  "required": [
    "business_name",
    "business_niche"
  ],
  "properties": {
    "business_name": {
      "type": "string",
      "description": "The name of the contact's business"
    },
    "business_niche": {
      "type": "string",
      "enum": ["Auto-Dealers", "Business Loan Brokers", "Franchise Brokers", "HVAC", "MEDSPAS", "Mortgage Lenders"],
      "description": "The business niche or industry category"
    },
    "business_website": {
      "type": "string",
      "description": "The contact's business website URL"
    }
  }
}
[(1) ]() Key points: - Use
"type": "string"
for text fields - For enum fields, add an
"enum"
array with the allowed values alongside the type - Add
"description"
to each field so the LLM knows what information to extract from the conversation - Include fields in
"required"
if they must be present for the function call - Make sure
"type": "object"
is at the top level—a common mistake is omitting this If you want any field to use a dynamic variable instead of LLM-resolved values, replace the
"description"
with
"const": "{{variable_name}}"
.
​ To learn more, see the sources I considered: [[1] Integrate any system with custom function](https://docs.retellai.com/build/single-multi-prompt/custom-function) ​
i
@AI Evy { "type": "object", "required": [ "business_name", "business_niche" ], "properties": { "business_name": { "type": "string", "description": "The name of the contact's business" }, "business_niche": { "type": "string", "enum": ["Auto-Dealers", "Business Loan Brokers", "Franchise Brokers", "HVAC", "MEDSPAS", "Mortgage Lenders"], "description": "The business niche or industry category" }, "business_website": { "type": "string", "description": "The contact's business website URL" } } } (1) Key points: Use "type": "string" for text fields For enum fields, add an "enum" array with the allowed values alongside the type Add "description" to each field so the LLM knows what information to extract from the conversation Include fields in "required" if they must be present for the function call Make sure "type": "object" is at the top level—a common mistake is omitting this If you want any field to use a dynamic variable instead of LLM-resolved values, replace the "description" with "const": "{{variable_name}}".
a
Thanks for the follow-up, @Idriss. I'll get back to you soon.
### Answer Yes, that JSON schema looks correct for your GHL custom fields. [(1) ]() To summarize the key points: -
"type": "object"
at the top level is required , [(2) ]() - Use
"description"
for fields the LLM should extract from the conversation [(1) ]() - Use
"enum"
array for fields with predefined options - Add fields to
"required"
if they must be present for the function call If you want any field to always use a specific dynamic variable value instead of having the LLM determine it from conversation, replace the
"description"
with `"const"`:
Copy code
json
"business_name": {
  "type": "string",
  "const": "{{business_name}}"
}
This passes the dynamic variable value directly rather than having the LLM resolve it from the conversation context.
​ To learn more, see the sources I considered: [[1] Integrate any system with custom function](https://docs.retellai.com/build/single-multi-prompt/custom-function) [[2] Custom Function](https://docs.retellai.com/build/conversation-flow/custom-function) ​
Feel free to tag @AI Evy with additional questions. I also appreciate feedback, it helps me improve.
i
@AI Evy so if I need to pass these information to make a contact in ghl and Business Name, Business Niche (Enum), and Business Website are all custom fields under contact in GHL, how should the parameters or JSON schema look like to pass this from retell to gohighlevel? https://cdn.discordapp.com/attachments/1451603496833908941/1453204713049690223/image.png?ex=694c99da&is=694b485a&hm=ce0a4d35293b770fe1d05ceb382f2bd81bb994c4d499ee0f8fcd19a10b2c6240&
a
Thanks for the follow-up, @Idriss. I'll get back to you soon.
​ To learn more, see the sources I considered: [[1] Integrate any system with custom function](https://docs.retellai.com/build/single-multi-prompt/custom-function) [[2] Custom Function](https://docs.retellai.com/build/conversation-flow/custom-function) [[3] Discord message from @noahreis on 2024-11-18](https://discord.com/channels/1186424376300679209/1294891368505937952/1308171340859183164) ​
[(1) ]() , [(2) ]() Key points: -
"type": "object"
at the top level is required—omitting this is a common mistake that causes 400 errors [(1) ]() , [(2) ]() - Use
"description"
for fields the LLM should extract from the conversation [(1) ]() - For the enum field, include the
"enum"
array with allowed values - Add fields to
"required"
if they must be present for the function call For the phone number specifically: If you want to automatically pass the caller's phone number rather than having the LLM extract it, you can use
"const"
with a dynamic variable or access it from the
call
object that Retell passes to your endpoint:
Copy code
json
"phone": {
  "type": "string",
  "const": "{{from_number}}"
}
, [(2) ]() Your backend endpoint will also receive the full call object containing
from_number
and
to_number
which you can use directly. [(3) ]() , [(7) ]()
### Answer --- For creating a GHL contact with custom fields, your JSON schema should include both the standard contact fields GHL requires (like name, email, phone) and your custom fields. Here's how to structure it:
Copy code
json
{
  "type": "object",
  "required": [
    "first_name",
    "phone",
    "business_name",
    "business_niche"
  ],
  "properties": {
    "first_name": {
      "type": "string",
      "description": "The contact's first name"
    },
    "last_name": {
      "type": "string",
      "description": "The contact's last name"
    },
    "email": {
      "type": "string",
      "description": "The contact's email address"
    },
    "phone": {
      "type": "string",
      "description": "The contact's phone number"
    },
    "business_name": {
      "type": "string",
      "description": "The name of the contact's business"
    },
    "business_niche": {
      "type": "string",
      "enum": ["Auto-Dealers", "Business Loan Brokers", "Franchise Brokers", "HVAC", "MEDSPAS", "Mortgage Lenders"],
      "description": "The business niche or industry category"
    },
    "business_website": {
      "type": "string",
      "description": "The contact's business website URL"
    }
  }
}
Feel free to tag @AI Evy with additional questions. I also appreciate feedback, it helps me improve.
i
@AI Evy when making the test this is the response: tool_call_id: tool_call_80e7bb {"result":"{\"success\": false, \"error\": \"Phone number is required to create contact\", \"details\": \"Cannot create contact without caller's phone number\"}"}
a
Thanks for the follow-up, @Idriss. I'll get back to you soon.
[(4) ]() The
{{user_number}}
system variable gives you the user's phone number (inbound: from_number, outbound: to_number) . 2. Your backend can extract it from the
call.from_number
field in the request body that Retell sends. [(2) ]() , [(3) ]() , [(1) ]() For dashboard testing: Since there's no live call, you'll need to either set a default value for the phone variable in your agent settings, or test with an actual phone call to have real call context. [(3) ]()
### Answer --- The error indicates your GHL endpoint isn't receiving the phone number. When testing from the dashboard (not a live call) , the caller's phone number isn't available because there's no actual call context. For live calls: When your custom function is called, Retell passes a
call
object in the request body that includes
from_number
and `to_number`: [(1) ]()
Copy code
json
{
  "call": {
    "call_type": "phone_call",
    "from_number": "+12137771234",
    "to_number": "+12137771235",
    "direction": "inbound",
    "call_id": "Jabr9TXYYJHfvl6Syypi88rdAHYHmcq6",
    "agent_id": "oBeDLoLOeuAbiuaMFXRtDOLriTJ5tSxD",
    "call_status": "registered",
    "metadata": {},
    "retell_llm_dynamic_variables": {
      "customer_name": "John Doe"
    },
    "opt_out_sensitive_data_storage": true
  },
  "name": "get_weather",
  "args": {
    "city": "New York"
  }
}
Two options to pass the phone number: 1. Use
const
with a system dynamic variable
in your parameter schema: [(2) ]() , [(3) ]()
Copy code
json
"phone": {
  "type": "string",
  "const": "{{user_number}}"
}
​ To learn more, see the sources I considered: [[1] Discord message from @noahreis on 2024-11-18](https://discord.com/channels/1186424376300679209/1294891368505937952/1308171340859183164) [[2] Custom Function](https://docs.retellai.com/build/conversation-flow/custom-function) [[3] Integrate any system with custom function](https://docs.retellai.com/build/single-multi-prompt/custom-function) [[4] Platform Changelogs | Retell AI](https://www.retellai.com/changelog) ​
Feel free to tag @AI Evy with additional questions. I also appreciate feedback, it helps me improve.
i
@AI Evy or we can jsut rename the phone variable to "phone number"? maybe?
a
Thanks for the follow-up, @Idriss. I'll get back to you soon.
### Answer Renaming the variable won't solve the issue. The problem is that during dashboard testing, there's no actual call context, so system variables like
{{user_number}}
have no value to substitute. [(1) ]() The variable name in your JSON schema (whether
phone
or
phone_number
) doesn't matter as long as it matches what your GHL endpoint expects. What matters is how you're providing the value. Your options: 1. Use
const
with the system variable:
Copy code
json
"phone": {
  "type": "string",
  "const": "{{user_number}}"
}
[(2) ]() , [(3) ]() This will work on live calls where
{{user_number}}
contains the caller's phone number. [(1) ]() 2. Set a default value for testing: Configure agent-level default dynamic variables as a fallback for when specific variables aren't included in the call request. 3. Test with an actual phone call instead of the dashboard to have real call context. The core issue is the lack of call context during testing, not the variable naming.
​ To learn more, see the sources I considered: [[1] Dynamic Variables](https://docs.retellai.com/build/dynamic-variables) [[2] Custom Function](https://docs.retellai.com/build/conversation-flow/custom-function) [[3] Integrate any system with custom function](https://docs.retellai.com/build/single-multi-prompt/custom-function) ​
Feel free to tag @AI Evy with additional questions. I also appreciate feedback, it helps me improve.
i
@AI Evy ok so how would the JSON look like now with the changes you proposed?
a
Thanks for the follow-up, @Idriss. I'll get back to you soon.
[(1) ]() , [(2) ]() Key changes: - The
phone
field now uses
"const": "{{user_number}}"
instead of a description [(1) ]() , [(2) ]() - This passes the caller's phone number directly as a fixed value rather than having the LLM extract it from conversation [(1) ]() Remember: This will only work on live calls where
{{user_number}}
has an actual value. For dashboard testing, you'll need to set a default value for the variable or test with an actual phone call. [(6) ]()
### Answer Here's the updated JSON schema with
{{user_number}}
for the phone field:
Copy code
json
{
  "type": "object",
  "required": [
    "first_name",
    "phone",
    "business_name",
    "business_niche"
  ],
  "properties": {
    "first_name": {
      "type": "string",
      "description": "The contact's first name"
    },
    "last_name": {
      "type": "string",
      "description": "The contact's last name"
    },
    "email": {
      "type": "string",
      "description": "The contact's email address"
    },
    "phone": {
      "type": "string",
      "const": "{{user_number}}"
    },
    "business_name": {
      "type": "string",
      "description": "The name of the contact's business"
    },
    "business_niche": {
      "type": "string",
      "enum": ["Auto-Dealers", "Business Loan Brokers", "Franchise Brokers", "HVAC", "MEDSPAS", "Mortgage Lenders"],
      "description": "The business niche or industry category"
    },
    "business_website": {
      "type": "string",
      "description": "The contact's business website URL"
    }
  }
}
​ To learn more, see the sources I considered: [[1] Integrate any system with custom function](https://docs.retellai.com/build/single-multi-prompt/custom-function) [[2] Custom Function](https://docs.retellai.com/build/conversation-flow/custom-function) ​
Feel free to tag @AI Evy with additional questions. I also appreciate feedback, it helps me improve.
Thanks for the follow-up, @Idriss. I'll get back to you soon.
i
@AI Evy
Copy code
{
  "type": "object",
  "properties": {
    "business_name": {
      "type": "string",
      "description": "Caller's business name"
    },
    "custom_fields": {
      "type": "object",
      "description": "Additional custom field data captured during call"
    },
    "business_niche": {
      "type": "string",
      "description": "Caller's business niche"
    },
    "last_name": {
      "type": "string",
      "description": "Caller's last name"
    },
    "source": {
      "type": "string",
      "description": "Lead source"
    },
    "initial_notes": {
      "type": "string",
      "description": "Notes about the initial call conversation"
    },
    "business_website": {
      "type": "string",
      "description": "Caller's business website"
    },
    "tags": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Tags to categorize the contact (e.g., 'inbound-lead')"
    },
    "call_context": {
      "type": "object",
      "description": "Context about the current call",
      "properties": {
        "call_purpose": {
          "type": "string",
          "description": "Why the caller contacted the business"
        },
        "call_id": {
          "type": "string",
          "description": "ID of the current call"
        }
      }
    },
    "phone_number": {
      "type": "string",
      "description": "Caller's phone number (primary identifier)"
    },
    "company": {
      "type": "string",
      "description": "Company name (optional)"
    },
    "first_name": {
      "type": "string",
      "description": "Caller's first name"
    },
    "email": {
      "type": "string",
      "description": "Caller's email address (optional)"
    }
  },
  "required": [
    "phone_number"
  ]
}
a
Copy code
json
{
  "type": "object",
  "properties": {
    "business_name": {
      "type": "string",
      "description": "Caller's business name"
    },
    "custom_fields": {
      "type": "object",
      "description": "Additional custom field data captured during call"
    },
    "business_niche": {
      "type": "string",
      "description": "Caller's business niche"
    },
    "last_name": {
      "type": "string",
      "description": "Caller's last name"
    },
    "source": {
      "type": "string",
      "description": "Lead source"
    },
    "initial_notes": {
      "type": "string",
      "description": "Notes about the initial call conversation"
    },
    "business_website": {
      "type": "string",
      "description": "Caller's business website"
    },
    "tags": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Tags to categorize the contact (e.g., 'inbound-lead') "
    },
    "call_context": {
      "type": "object",
      "description": "Context about the current call",
      "properties": {
        "call_purpose": {
          "type": "string",
          "description": "Why the caller contacted the business"
        },
        "call_id": {
          "type": "string",
          "description": "ID of the current call"
        }
      }
    },
    "phone_number": {
      "type": "string",
      "const": "{{user_number}}"
    },
    "company": {
      "type": "string",
      "description": "Company name (optional) "
    },
    "first_name": {
      "type": "string",
      "description": "Caller's first name"
    },
    "email": {
      "type": "string",
      "description": "Caller's email address (optional) "
    }
  },
  "required": [
    "phone_number"
  ]
}
[(1) ]() , [(2) ]() Key change: The
phone_number
field now uses
"const": "{{user_number}}"
which passes the caller's phone number directly as a fixed value rather than having the LLM extract it from conversation. [(1) ]() This will work on live calls where
{{user_number}}
contains the caller's phone number. For dashboard testing, you'll still need to set a default value or test with an actual call.
### Answer Your schema looks good, but to fix the phone number issue from earlier, you should use
const
with the system variable instead of
description
for the
phone_number
field: [(1) ]() , [(2) ]()
​ To learn more, see the sources I considered: [[1] Integrate any system with custom function](https://docs.retellai.com/build/single-multi-prompt/custom-function) [[2] Custom Function](https://docs.retellai.com/build/conversation-flow/custom-function) ​
Feel free to tag @AI Evy with additional questions. I also appreciate feedback, it helps me improve.
i
@AI Evy there is this "custom_fields": { "type": "object", "description": "Additional custom field data captured during call" should the custom fields be under this for it to be successfully captured in GHL?
a
Thanks for the follow-up, @Idriss. I'll get back to you soon.
​ To learn more, see the sources I considered: [[1] Integrate any system with custom function](https://docs.retellai.com/build/single-multi-prompt/custom-function) ​
### Answer The structure of your JSON schema for custom fields depends on what your GHL endpoint expects, not on a specific Retell requirement. [(1) ]() Retell passes the
args
object from your schema directly to your endpoint. Your backend receives the parameters you define, so the schema should match the payload format your GHL integration expects. Two approaches: 1. Nested under
custom_fields
— if your GHL endpoint expects custom fields grouped together:
Copy code
json
"custom_fields": {
  "type": "object",
  "properties": {
    "business_name": {
      "type": "string",
      "description": "Caller's business name"
    },
    "business_niche": {
      "type": "string",
      "description": "Caller's business niche"
    }
  }
}
2. Flat at the top level — if your GHL endpoint expects each field as a separate property (as in your earlier schema) . Check your GHL API documentation or webhook handler to see what structure it expects. The JSON schema you define in Retell simply determines how the
args
are structured when sent to your endpoint. , [(4) ]()
Feel free to tag @AI Evy with additional questions. I also appreciate feedback, it helps me improve.
i
@AI Evy how about this, instead of relying on JSON, I'll be utilizing the Form feature so I don't have to worry about semantics. In the screenshot, that should be the only things I need for the contact to be created in GHL. will this successfully populate the custom fields I made for that contact? https://cdn.discordapp.com/attachments/1451603496833908941/1453212059226603672/image.png?ex=694ca0b2&is=694b4f32&hm=7eeb284dd539f7e8bb93c78f3ec18f01549cebdb390c2add902f17c484bde842&
a
Thanks for the follow-up, @Idriss. I'll get back to you soon.
### Answer --- Yes, you can use the JSON form feature instead of writing raw JSON schema. The documentation shows an example of the JSON form interface for defining parameters. [(1) ]() The form lets you define parameters visually—you add each field with its name, type, and either a description (for LLM-resolved values) or a const value (for fixed/dynamic variable values) . Key points for your setup: - For
phone_number
, use const value with
{{user_number}}
to pass the caller's phone directly - For fields like
business_name
,
business_niche
,
business_website
, use description so the LLM extracts them from conversation - Both description and const value can include dynamic variables Whether your custom fields populate correctly in GHL depends on: 1. Your endpoint URL receiving the data Retell sends 2. Your backend mapping those fields to GHL's custom field format The form generates the same underlying JSON schema—it's just a visual alternative. The data Retell sends to your endpoint will be the same regardless of whether you use the form or raw JSON.
​ To learn more, see the sources I considered: [[1] Custom Function](https://docs.retellai.com/build/conversation-flow/custom-function) ​
Feel free to tag @AI Evy with additional questions. I also appreciate feedback, it helps me improve.
Thanks for the follow-up, @Idriss. I'll get back to you soon.
i
@AI Evy
Copy code
{
  "type": "object",
  "properties": {
    "business_name": {
      "type": "string",
      "description": "Caller's business name"
    },
    "custom_fields": {
      "type": "object",
      "description": "Additional custom field data captured during call"
    },
    "business_niche": {
      "type": "string",
      "description": "Caller's business niche"
    },
    "last_name": {
      "type": "string",
      "description": "Caller's last name"
    },
    "source": {
      "type": "string",
      "description": "Lead source"
    },
    "initial_notes": {
      "type": "string",
      "description": "Notes about the initial call conversation"
    },
    "business_website": {
      "type": "string",
      "description": "Caller's business website"
    },
    "tags": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Tags to categorize the contact (e.g., 'inbound-lead')"
    },
    "call_context": {
      "type": "object",
      "description": "Context about the current call",
      "properties": {
        "call_purpose": {
          "type": "string",
          "description": "Why the caller contacted the business"
        },
        "call_id": {
          "type": "string",
          "description": "ID of the current call"
        }
      }
    },
    "phone_number": {
      "type": "string",
      "description": "Caller's phone number (primary identifier)"
    },
    "company": {
      "type": "string",
      "description": "Company name (optional)"
    },
    "first_name": {
      "type": "string",
      "description": "Caller's first name"
    },
    "email": {
      "type": "string",
      "description": "Caller's email address (optional)"
    }
  },
  "required": [
    "phone_number"
  ]
}
a
Feel free to tag @AI Evy with additional questions. I also appreciate feedback, it helps me improve.
​ To learn more, see the sources I considered: [[1] Integrate any system with custom function](https://docs.retellai.com/build/single-multi-prompt/custom-function) ​
### Answer --- Your schema structure looks correct, but based on our earlier discussion, you still have
phone_number
using
description
instead of
const
. To fix the phone number issue from testing, change it to: [(1) ]()
Copy code
json
"phone_number": {
  "type": "string",
  "const": "{{user_number}}"
}
This passes the caller's phone number directly as a fixed value rather than having the LLM extract it from conversation. Other notes on your schema: - The
custom_fields
property with just
"type": "object"
and a description is vague—if you need specific nested custom fields for GHL, define them explicitly with their own
properties
- The
call_context
nested object looks fine if your backend expects that structure - Make sure
"type": "object"
is at the top level—a common mistake is omitting this, which causes 400 errors Whether the custom fields populate correctly in GHL depends on your backend mapping those fields to GHL's expected format—Retell sends the
args
object to your endpoint as defined in your schema.
i
@AI Evy listen, this is working it isn't giving an error when creating the contact vs the previous JSON that you made. what is the difference? and I want you to modify this that the custom fields would appear in GHL because the only problem for this is that it doesn't populate the custom fields in the contact.
a
Thanks for the follow-up, @Idriss. I'll get back to you soon.
### Answer The key difference between the two schemas is that your current working schema uses
description
for
phone_number
, which lets the LLM try to extract it from conversation—but this won't reliably capture the actual caller's phone number. The earlier schema I suggested used
const
to pass a fixed value. [(1) ]() **The custom fields not populating in GHL is not a Retell issue**—it's about how your backend endpoint maps the data to GHL's API format. Retell sends the
args
object to your endpoint exactly as you define it in the schema. , [(2) ]() Your backend needs to: 1. Receive the
args
from Retell 2. Transform them into GHL's expected custom field format (which typically requires field IDs, not just field names) What you should check: - GHL custom fields usually need to be passed with their specific field IDs, not just the field names like
business_name
- Your webhook/backend handler needs to map your schema fields to GHL's custom field structure This is outside Retell's scope—Retell delivers the data you define in your schema to your endpoint. The mapping to GHL's custom field format happens in your backend code. Would you like a general answer on how GHL custom field mapping typically works, or do you have specific questions about the Retell side of the integration?
​ To learn more, see the sources I considered: [[1] Integrate any system with custom function](https://docs.retellai.com/build/single-multi-prompt/custom-function) [[2] Custom Function](https://docs.retellai.com/build/conversation-flow/custom-function) ​
Feel free to tag @AI Evy with additional questions. I also appreciate feedback, it helps me improve.
i
@AI Evy I just need it to work. pls make it work
a
Thanks for the follow-up, @Idriss. I'll get back to you soon.
### Answer I understand the frustration. Here's what's happening: Retell's role ends at sending data to your endpoint. When your custom function is called, Retell sends a request to your specified URL with the function name and parameters you defined. [(1) ]() The custom fields not appearing in GHL is a backend/GHL integration issue, not a Retell configuration issue. Your schema is working—Retell is sending the data. The problem is how that data gets mapped to GHL's custom field format on your server. What you need to do: 1. Your backend endpoint (the URL you configured in the custom function) receives the
args
from Retell 2. Your backend code must transform those args into GHL's required format—GHL typically requires custom field IDs, not just field names 3. This transformation happens in your server code, not in Retell For custom fields beyond phone/email with integrations like Cal.com, you need to use a custom function and handle the mapping in your own server. [(2) ]() I can help with the Retell side of this, but the GHL custom field mapping requires work on your backend. If you don't have a backend set up, you may need to build one or use a service like Make/Zapier to handle the transformation.
​ To learn more, see the sources I considered: [[1] Custom Function](https://docs.retellai.com/build/conversation-flow/custom-function) [[2] Cal.com custom fields (not phone/email) = 400 errors every time](https://discord.com/channels/1186424376300679209/1417434187685822535) ​
Feel free to tag @AI Evy with additional questions. I also appreciate feedback, it helps me improve.
i
@AI Evy wait I think I figured it out. it says here in the screenshot that This function "create contact" works with my Global Variables only. meaning it can't do other variables like custom fields? maybe I was looking at this wrong? I was thinking maybe I should use another function that would allow me to use other variables like what it says with update contact? what do you think? https://cdn.discordapp.com/attachments/1451603496833908941/1453218544472035328/image.png?ex=694ca6bc&is=694b553c&hm=0be484e016843dbb1ac8cd6cc0e82f8d37209976df28a87bc85a0bba7c89e552& https://cdn.discordapp.com/attachments/1451603496833908941/1453218544962768986/image.png?ex=694ca6bc&is=694b553c&hm=399147b466fdb295bce6871f9636f588b502dc4bb9d1348470dd0175ed91df0a&
a
Thanks for the follow-up, @Idriss. I'll get back to you soon.
### Answer --- ### Answer Based on what you're describing, this sounds like a limitation of the specific GHL function you're using—not a Retell limitation. From the GHL integration workshop, when creating contacts in GHL you can include custom fields in the JSON body you send. [(1) ](

▾

) The example shows adding fields like name, phone, and notes that you can also add custom fields to: > "Custom fields, right? So custom fields, you can just add them there as well. So just kind of in a nested..." Your two options: 1. Use a "create contact" function that supports custom fields — If your current GHL function only accepts global/standard variables, you may need to use a different endpoint or modify your backend to accept additional parameters. 2. Use "update contact" after creation — Create the contact first with basic info, then call an update function to add custom fields. This is a valid workaround if the create endpoint is limited. The key point: Retell sends whatever parameters you define in your schema to your endpoint. [(2) ]() The limitation you're seeing ("works with Global Variables only") is on the GHL/backend side, not Retell's. If you're using a pre-built GHL integration template, check if there's a more flexible version that accepts custom fields, or build a custom function endpoint that handles the full payload including custom fields.
​ To learn more, see the sources I considered: [[1] Live Workshop: Retell x GoHighLevel Integration](

https://www.youtube.com/watch?v=TlzrfYgiPLQ▾

) [[2] Cal.com custom fields (not phone/email) = 400 errors every time](https://discord.com/channels/1186424376300679209/1417434187685822535) ​
Feel free to tag @AI Evy with additional questions. I also appreciate feedback, it helps me improve.
Thanks for the follow-up, @Idriss. I'll get back to you soon.
i
@AI Evy
a
Feel free to tag @AI Evy with additional questions. I also appreciate feedback, it helps me improve.
### Answer It looks like you're asking for help with the next step. Based on our conversation, here's where things stand: What Retell does: Retell sends the parameters you define in your custom function schema to your endpoint URL. The request includes the
args
object with the data you've configured. [(1) ]() The GHL custom fields issue: From the workshop, custom fields can be added to the create contact request body in GHL: > "Custom fields, right? So custom fields, you can just add them there as well." [(2) ](

▾

) Your options: 1. Build a custom function endpoint — Create your own backend that receives Retell's data and maps it to GHL's custom field format (which typically requires field IDs) . Retell sends the request to your specified URL with the function name and parameters. [(1) ]() 2. Use the update contact approach — Create the contact first, then use a separate function to update it with custom fields if your current create function is limited. 3. Check GHL's API documentation — GHL custom fields usually need to be passed with their specific field IDs, not just field names. For integrations requiring custom fields beyond standard ones, using a custom function with your own server to handle the mapping is the recommended approach. [(3) ]() Is there a specific part of the Retell configuration you'd like help with?
​ To learn more, see the sources I considered: [[1] Integrate any system with custom function](https://docs.retellai.com/build/single-multi-prompt/custom-function) [[2] Live Workshop: Retell x GoHighLevel Integration](

https://www.youtube.com/watch?v=TlzrfYgiPLQ▾

) [[3] Cal.com custom fields (not phone/email) = 400 errors every time](https://discord.com/channels/1186424376300679209/1417434187685822535) ​
i
@AI Evy so I want to make a fallback option everytime my get slots function fails. this is what it gives back when it fails: {"result":"{\"traceId\": \"04e2c339-8692-49c8-a89f-60bdbd55b0d4\"}"} and this is when it get's acutal slots: {"result":"{\"2025-12-25\": {\"slots\": [\"2025-12-25T11:00:00-05:00\", \"2025-12-25T11:30:00-05:00\", \"2025-12-25T12:00:00-05:00\", \"2025-12-25T12:30:00-05:00\", \"2025-12-25T13:00:00-05:00\", \"2025-12-25T13:30:00-05:00\", \"2025-12-25T14:00:00-05:00\", \"2025-12-25T15:00:00-05:00\", \"2025-12-25T15:30:00-05:00\", \"2025-12-25T16:00:00-05:00\"]}, \"traceId\": \"6cbd835a-3f4b-4d67-9d80-082ce75bf97e\"}"} how do I manage this>\
a
Thanks for the follow-up, @Idriss. I'll get back to you soon.
### Answer To handle fallback logic based on your function response, you can use Conversation Flow with conditional transitions based on the function result. [(1) ]() How it works: Since the function node takes function result into consideration for transition timing, you can write your transition condition to be based on the function result. Your approach: 1. Create a Function Node for your get slots function 2. Enable "Wait for Result" — this ensures the agent waits for the function to finish before transitioning, guaranteeing the result is ready to evaluate 3. Add conditional transitions from the function node: - **Success path**: Transition when the response contains actual slots (e.g., check if the result contains a date key or "slots" array) - **Failure path**: Transition to a fallback conversation node when the response only contains
traceId
without slots 4. Attach different Conversation Nodes for each path — one to present available slots, another to handle the fallback (e.g., apologize and offer alternative options) The transition conditions would evaluate the function result. For example, your failure response only has
traceId
, while success has date keys with slots arrays — you can base your condition on whether slots exist in the response.
​ To learn more, see the sources I considered: [[1] Function Node Overview](https://docs.retellai.com/build/conversation-flow/function-node) ​
Feel free to tag @AI Evy with additional questions. I also appreciate feedback, it helps me improve.