Automations
Automations run steps when something happens: a reply, a new contact, a tag, or a call to your inbound webhook. Over the API you can create, change, turn on or off, copy and delete them, add contacts to one, and fire them from another system. Build automations covers the flow canvas, settings, tests, folders, templates and earlier versions; Automation runs and stats covers what they did. Every text an automation sends goes through the same opt-out, quiet hours and balance checks as any other text. Every route here needs the manage_campaigns permission unless it says otherwise. Test keys can read, but any change is refused with 403 test_key_not_allowed.
#The Automation object
List automations returns these. Create and update return the same object without triggers, folder_id, has_canvas and trigger_error.
idinteger- The automation id.
namestring- The automation’s name.
enabledinteger1(on) or0(off).trigger_typestring- What starts it, such as
inbound_webhook. On an automation with more than one trigger, this is only the first: readtriggers. trigger_configobject- The settings for that trigger.
triggersobject[] | null- When any of several triggers starts it: each one as
{ trigger_type, trigger_config }.nullwhen it has one trigger. actionsobject[]- Its steps.
folder_idinteger | null- The folder it is in, or
null. has_canvasbooleantruewhen it was saved from the flow canvas. Change its steps withPUT /automations/:id/graph.trigger_errorbooleantruewhen its Google Sheets trigger is reporting an error. A rate limit doesn’t count.last_fired_atISO 8601 string | null- When it last fired.
fire_countinteger- How many times it has fired.
created_atISO 8601 string- When it was created.
updated_atISO 8601 string- When it was last changed.
#List automations
Every automation on your account, newest first. Needs the manage_campaigns permission. Returns a list of Automation objects in data.
#Create an automation
Saves a new automation from a trigger and a list of steps. Leave out enabled and it starts on, so it can fire and send right away: send false to save it paused. To build from a flow graph instead, use POST /automations/from-graph, which saves paused unless you ask. Needs the manage_campaigns permission.
Parameters
namestringRequired- Up to 180 characters; a longer name is cut.
trigger_typestringRequired- What starts it, such as
inbound_webhook,inbound_keyword,new_contact,contact_taggedormanual_enrollment. trigger_configobject- The trigger’s settings, as List automations returns them.
inbound_intentneeds anintent; aninbound_keywordkeyword can’t be an opt-out or opt-in word. actionsobject[]- The steps, in the shape List automations returns, such as
{ "type": "send_sms", "body": "Hi {{contact.first_name}}" }. For a webhook step, see The Webhook step. enabledbooleantrue,1or absent is on;false,0,"0"or"false"is off. Anything else is refused.Idempotency-Keystring · header- Send one to retry safely: a repeat with the same key returns the first answer instead of a second automation.
Returns
dataobject- The new Automation object.
empty_mergeobject[] | null- Texts with a merge field that is blank for some of your newest contacts. A warning only: the automation was saved. A contact with a blank there is skipped when it runs.
Errors
| HTTP | Code | When |
|---|---|---|
| 400 | NAME_REQUIRED | No name. |
| 400 | TRIGGER_INVALID | No trigger_type, one we don’t know, or settings it can’t use. |
| 400 | TRIGGER_KEYWORD_RESERVED | A keyword is an opt-out or opt-in word. |
| 400 | STEPS_INVALID | A step isn’t set up correctly. Other step codes, such as WEBHOOK_URL_INVALID or STEP_NOT_SUPPORTED, name the problem; error says what to fix. |
| 400 | ENABLED_INVALID | enabled isn’t on or off. |
| 500 | AUTOMATION_CREATE_UNCONFIRMED | We couldn’t confirm it was saved. List your automations before you create it again. |
#Update an automation
Changes only the fields you send. This is also how you turn an automation on or off: send { "enabled": true } or { "enabled": false }. Turning it off stops new runs. A run that is waiting in it is dropped if its wait ends while the automation is off. If has_canvas is true, change its steps with PUT /automations/:id/graph instead, so the canvas and the steps stay the same. Needs the manage_campaigns permission.
Parameters
idinteger, pathRequired- The automation id.
More parameters
namestring- A new name.
enabledboolean- On or off.
nullor""is off. trigger_typestring- A new trigger.
trigger_configobject- New trigger settings, checked against
trigger_type(the new one if you send it). actionsobject[]- The whole new list of steps.
folder_idinteger | null- Move it into one of your folders.
nullor0takes it out of its folder. check_empty_mergeboolean- With
enabled: trueon an automation that is off: first check its texts for merge fields that are blank for some of your newest contacts. If any are, nothing is saved and you get200withsuccess: falseandneeds_confirmation: "empty_merge_field". Send again withconfirm_empty_merge: trueto turn it on anyway.
Returns
dataobject- The saved Automation object.
empty_mergeobject[] | null- As on create, when you sent
actions; an empty list otherwise.
Errors
| HTTP | Code | When |
|---|---|---|
| 400 | NOTHING_TO_SAVE | The body changes nothing. |
| 400 | NAME_REQUIRED | A blank name. |
| 400 | FOLDER_NOT_FOUND | No folder with this id on your account. |
| 400 | ACTIONS_UNREADABLE | actions isn’t a list. |
| 400 | TRIGGER_INVALID | As on create. So are the step codes. |
| 400 | ENABLED_INVALID | enabled isn’t on or off. |
| 404 | AUTOMATION_NOT_FOUND | No automation with this id on your account. |
| 409 | AUTOMATION_UNFINISHED | You turned on a canvas-built automation that isn’t finished: an If / else with an empty path, a step the trigger can’t reach, a keyword trigger with no keyword, or a template placeholder left in a text. Nothing in the request was saved and it stays off. |
| 500 | AUTOMATION_UPDATE_FAILED | We couldn’t save. Read it again to see what is saved, then retry. |
#Delete an automation
Deletes it for good, with its earlier versions. Runs waiting in it end when their wait does, and a bulk add still in progress stops; nothing more is sent from it. Needs the manage_campaigns permission. Returns { "success": true }.
Parameters
idinteger, pathRequired- The automation id.
Errors
| HTTP | Code | When |
|---|---|---|
| 404 | AUTOMATION_NOT_FOUND | No automation with this id on your account, or it was already deleted. |
| 500 | AUTOMATION_DELETE_FAILED | We couldn’t delete it. Read it again before you retry. |
#Copy an automation
Makes a copy named "name (copy)", with the same triggers, steps, canvas, folder and settings. The copy is always off. Needs the manage_campaigns permission. Takes an optional Idempotency-Key header, as on create.
Parameters
idinteger, pathRequired- The automation to copy.
Returns
dataobject- The copy, paused (
enabledis0), with itstrigger_configandactions, in the same shape as Create an automation returns.
Errors
| HTTP | Code | When |
|---|---|---|
| 404 | AUTOMATION_NOT_FOUND | No automation with this id on your account. |
| 500 | AUTOMATION_DUPLICATE_FAILED | We couldn’t copy it. List your automations before you retry. |
#Add a contact to an automation
Starts an automation for one contact right now, whatever its trigger. The automation must be on. Each text it sends costs your per-segment rate. Needs the manage_campaigns permission.
Parameters
idinteger, pathRequired- The automation id.
contact_idintegerRequired- The contact to start it for.
Returns
startedbooleantruewhen it started.already_runningbooleantruewhen the contact is already in it. Nothing new started.rule_idinteger- The automation id.
rule_namestring- The automation’s name.
contact_idinteger- The contact it started for.
Errors
| HTTP | Code | When |
|---|---|---|
| 400 | CONTACT_REQUIRED | No contact_id. |
| 402 | BILLING_PAST_DUE | Your last payment failed. Update your card in Billing. |
| 404 | AUTOMATION_NOT_FOUND | No automation with this id on your account. |
| 404 | CONTACT_NOT_FOUND | No contact with this id on your account. |
| 409 | AUTOMATION_PAUSED | It’s off. Turn it on with Update an automation. |
| 409 | NO_STEPS | It has no steps yet. |
| 409 | AUTOMATION_ALREADY_ENTERED | The contact already went through it, and it doesn’t let a contact enter twice. |
#Add many contacts to an automation
Queues up to 50,000 contacts into an automation. Returns 202 and works through them in the background. Each text it sends costs your per-segment rate. Needs the manage_campaigns and edit_contacts permissions.
Parameters
idinteger, pathRequired- The automation id.
contact_idsinteger[]Required- Up to 50,000.
request_idstringRequired- Your id for this request, 16 to 100 letters, digits,
_or-. Sending it again returns the same job withreplayed: true, so a retry never adds anyone twice. consent_attestedbooleanRequired- Must be
true: every contact agreed to get texts from you (consent). consent_attestation_textstring- The consent sentence the person who confirmed it saw. We keep it with the job, with the request’s IP address and user agent, as your record. Up to 1,000 characters; a longer one is cut. Leave it out and no sentence is kept.
Returns
data.job_idinteger- The background job.
data.totalinteger- Contacts queued.
data.rule_namestring- The automation’s name.
data.replayedbooleantruewhen thisrequest_idwas sent before and you got the same job back.
Errors
| HTTP | Code | When |
|---|---|---|
| 400 | BAD_SELECTION | No contacts, or more than 50,000. |
| 400 | BAD_REQUEST_ID | A bad request_id. |
| 400 | NO_CONSENT | No consent_attested: true. error_code is attestation_required, as on campaigns. |
| 402 | BILLING_PAST_DUE | The account is past due. |
| 404 | RULE_MISSING | No automation with this id on your account. error_code is AUTOMATION_NOT_FOUND, as on a single enroll. |
| 409 | RULE_PAUSED | The automation is off. error_code is AUTOMATION_PAUSED, as on a single enroll. |
| 409 | RULE_EMPTY | The automation has no steps. error_code is NO_STEPS, as on a single enroll. |
| 409 | IN_FLIGHT | This request_id is still being started. Retry the same request_id in a few seconds. If you got 503 ENROLL_QUEUE_FAILED for it before, it will never start: see that row. |
| 409 | OTHER_SELECTION | This request_id was used for other contacts. Use a new request_id. |
| 409 | CONTACTS_GONE | Some contacts were deleted. |
| 429 | rate_limited | Too many requests. Wait and retry. |
| 503 | ENROLL_QUEUE_FAILED | The enrollment could not be started safely, and no contacts were added. Retry once with the same request_id. If that answers 409 IN_FLIGHT, the first try stopped after its job was made and that request_id can’t be used again: send the same contacts with a new request_id. |
#Get your inbound webhook
Your account’s inbound webhook address. Any automation with the Inbound webhook trigger runs when something posts to it. Needs the manage_campaigns permission.
Returns
urlstring- Post to this. Keep it secret: anyone with it can fire your automations.
tokenstring- The last part of
url.
#Fire your inbound webhook
Runs your Inbound webhook automations. Point a form, CRM or Zapier here. No API key: the token in the address is the key. Each text it sends costs your per-segment rate.
Parameters
tokenstring, pathRequired- From
GET /automations/inbound-webhook.
More parameters
phonestring- The person this is about. We find the contact by number, or create one.
first_namestring- Used when we create the contact.
firstNameworks too. last_namestring- Used when we create the contact.
lastNameworks too. emailstring- Used when we create the contact.
contact_idinteger- Or an existing contact’s id.
any other fieldany- Passed to the automation, so its steps and conditions can use it.
Returns
firedbooleantruewhen the event was handed to your Inbound webhook automations. It does not mean one ran.contact_idinteger | null- The contact it ran for.
duplicateboolean- The same body posted twice within 60 seconds runs once; the second answers
duplicate: true.
Errors
| HTTP | Code | When |
|---|---|---|
| 404 | WEBHOOK_UNKNOWN | Unknown token. It may have been changed. |
| 429 | no code | More than 60 calls in a minute to this address. |
#Change your inbound webhook
Replaces the address, for example after it leaked. The old one stops working right away, so update every system that uses it. Needs the manage_campaigns permission.
Returns
urlstring- The new address.
tokenstring- The last part of the new
url.
Errors
| HTTP | Code | When |
|---|---|---|
| 409 | INBOUND_WEBHOOK_CLOSE_CONNECTED | Close is connected to this address. Disconnect Close first. |
#Check for a Webhook step
Whether any automation that is on has a Webhook step, so Send a lead to your webhook has somewhere to post. Needs the manage_campaigns permission.
Returns
availablebooleantruewhen at least one turned-on automation has a Webhook step.
#Send a lead to your webhook
Hands a conversation to your CRM by hand. We pick the turned-on automation with a Webhook step that best fits this contact (its phone line and tag filters; one that requires a tag wins over a catch-all) and run only its Webhook steps, with the contact’s latest text as the event. No other step runs and no text is sent. Every call posts again, so call it once per lead. Needs the manage_campaigns permission.
Parameters
conversation_idinteger- The conversation. Send this or
contact_id. contact_idinteger- Or the contact.
Returns
rule_namestring- The automation whose Webhook steps ran.
captured_lead_idstring | null- The lead id a Shape CRM post answered with, when we stored it.
nullfor any other webhook.
Errors
| HTTP | Code | When |
|---|---|---|
| 400 | INVALID_ID | Neither id, or one that isn’t a whole number. |
| 400 | NO_MATCHING_WEBHOOK | No turned-on automation with a Webhook step fits this contact. |
| 402 | BILLING_PAST_DUE | The account is past due. |
| 404 | NO_INBOUND_MESSAGE | The contact hasn’t texted you yet, so there is no lead to send. |
| 429 | rate_limited | Too many requests. Wait and retry. |
| 502 | WEBHOOK_REJECTED | Your webhook answered with an error. rule_name says which automation. |
| 502 | WEBHOOK_URL_INVALID | The Webhook step’s address isn’t valid. |
| 502 | WEBHOOK_NOT_SENT | The Webhook step isn’t fully set up, so nothing was posted. |
| 502 | WEBHOOK_OUTCOME_UNKNOWN | Your webhook didn’t answer. The lead may have arrived: check your CRM before you send it again. |
| 503 | WEBHOOK_CONTACT_UNREADABLE | We couldn’t read the contact, so nothing was posted. Retry in a minute. |
| 500 | SEND_TO_WEBHOOK_FAILED | Nothing was sent. Retry in a minute. |
#The Webhook step
The Webhook step calls any web API from an automation. Method picks GET, POST, PUT, PATCH or DELETE (GET and DELETE send no data). Headers adds your own, such as Authorization with Bearer your_token; the names Host, Content-Length, Connection, Transfer-Encoding and Cookie are not allowed. Send only these fields sends just the fields you list, with none of Ready’s contact details around them. Without it, the post carries the event, its data, the contact (name, email, phone, address, tags, score, status), the assigned team member’s name and email, and notes: the last 50 texts with this contact, both directions, in full. For example, to add a message in GoHighLevel, POST to https://services.leadconnectorhq.com/conversations/messages/inbound with the headers Authorization: Bearer your_token and Version: 2021-04-15, tick Send only these fields, and add type = SMS, contactId and message = {{last_reply}}.
Without Send only these fields, the JSON body has these fields. Any extra field you add to the step is set last, so it replaces a field of the same name.
eventstring- The event that started the run.
dataobject- The event’s data, such as the fields posted to your inbound webhook. Keys that start with
__are left out. contactobject | null- The contact:
contact_first_name,contact_last_name,contact_full_name,contact_email,contact_phone,contact_company, the address fields (contact_address1,contact_address2,contact_city,contact_state,contact_postal_code,contact_country),contact_score,contact_pipeline_stage,contact_status,contact_source,contact_tags(a list) andcontact_tags_csv, plus these extra keys, named the way one CRM (Shape) expects its fields:firstname,lastname,leadfullname,phone,email,refconcompanyname,address2,city,state,zip,leadfulladdressandleadtags. Most repeat acontact_*value, butaddress2carries the street line (contact_address1), notcontact_address2.nullwhen the run has no contact. assigneeobject | null- The team member the contact is assigned to:
assigned_to_id,assigned_to_email,assigned_to_first_name,assigned_to_last_nameandassigned_to_name, with the email and name repeated asassignee_email,assignee_name,agent_emailandagent_name, plusassignedUserId,shapeUserIdanddepursAE, which arenullunless your CRM routes leads by rep.nullwhen nobody is assigned. notesstring | null- The last 50 texts with the contact, oldest first, one per line, like
[2026-10-15 18:22] IN : Yes please. The times are UTC, not the account’s time zone.nullwhen there are none. rule_idinteger- The automation id.
fired_atISO 8601 string- When the step posted.
See also: the guide to doing this in the app.