What the send endpoint gives you
This is the conversations API's send-message route. Set type to Email and it sends an email to the contact you name. The token needs the conversations/message.write scope.
- contactId is required. The email goes to a contact that already exists in the sub-account.
- subject and html are separate fields. Put the subject line in one and the body markup in the other.
- emailTo lets you pick another email address that belongs to the same contact, when the main one isn't right for this message.
Which service actually sends it
HighLevel decides the sending service in an order. A default provider set on the sub-account comes first, ahead of anything configured at the agency. When no provider is configured, LeadConnector Email is the documented fallback.
HighLevel's article on that order dates from 2023, so don't build on it alone. Open the sub-account's email settings and see what it's actually set to use. That tells you which service and sending domain your transactional mail will go out on.
Setting up a sending domain on LC Email
On a shared sending domain, the from address your contact sees can be rewritten. If you want mail to show your own domain, set up a dedicated one.
- In the sub-account, go to Settings > Email Services.
- Open Dedicated Domain and IP and click Add Domain.
- Add the DNS records HighLevel gives you and complete the verification.
- If you send different kinds of email, HighLevel lets you assign a dedicated domain to each email category separately.
A sending domain decides where mail comes from. A Reply-To decides where answers go. Set both on purpose.
Building the request
Once you know the provider and the domain, the call itself is short.
- Find the contact and take its contactId.
- Set type to Email.
- Put the subject line in subject and the full body markup in html.
- Set emailTo only if the message should go to one of the contact's other addresses.
- Send with a token that has conversations/message.write.
- Save the ids that come back in the response.
Send to a contact that already exists
It's tempting to upsert the contact in the same step, so the send never fails for want of a record. We don't. HighLevel's upsert can create a new contact whenever it doesn't find a match, so a typo or a second address turns one person into two records, and the email goes to the new one.
Our rule is to look the contact up first and send only to a contact that exists. If the lookup finds nobody, don't send; handle that case on purpose. That's our practice, not a HighLevel restriction: the API allows upsert, and some setups will want it. Just don't let it run as a side effect of sending.
Keep both ids from the response
The send response returns a messageId and an emailMessageId as separate values. They identify different things, and later calls can ask for one or the other. Store both against your own record of the send, so you can find the message again without searching for it.
Rather have it set up for you?
Starting from a 30-minute 1:1 with Rob, we set up your sending domain, build the email call into your app and test it on a real contact record.
