Outbound Lead Enrollment Guide
This document describes everything required to enroll leads for outbound calling and to unenroll them when calling should stop. A developer can build and operate the sending side from this document alone.
1. Overview
Your system sends lead information to a single HTTPS endpoint that we host. The same endpoint
supports two actions, selected by an action field in the request body:
- Enroll. Send a new lead you want our team to start calling.
- Unenroll. Ask us to stop calling a specific lead.
Every request is signed with a shared secret that we issue to you. We answer each request synchronously with a small JSON body and a standard HTTP status code.
2. The endpoint
| Aspect | Contract |
|---|---|
| Method | HTTPS POST with a JSON body. |
| Path | /services/apexrest/Outbound/Leads. The host is issued to you with your credentials. |
| Content type | Content-Type: application/json. |
| Actions | Both enroll and unenroll go to the same URL. The action field in the body selects the behaviour. |
| Health check | An HTTPS GET to the same URL returns a static message and does not process anything. Use it to confirm connectivity only. |
| Login | No user account is required. Every request is authenticated by the signature described below. |
| Character encoding | UTF-8. |
3. Authentication
Requests are authenticated with an HMAC-SHA256 signature over the raw request body, using a secret that we issue to your firm. Two headers are required on every request:
| Header | Value |
|---|---|
c-name | Your firm's name, exactly as we configured it. We use it to look up your secret. |
x-auth-token | The HMAC-SHA256 signature of the raw request body, keyed with your secret, encoded as lowercase hexadecimal. |
We recreate the signature from the body we received and compare it against x-auth-token. Any
mismatch is rejected with HTTP 401.
3.1 Computing the signature
Sign the exact bytes you send. Serialize the JSON body to a string first, compute the signature over that string, then send that same string unchanged as the request body.
import { createHmac } from 'node:crypto';
const body = JSON.stringify(payload);
const signature = createHmac('sha256', SHARED_SECRET).update(body, 'utf8').digest('hex');
await fetch(`${BASE_URL}/services/apexrest/Outbound/Leads`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'c-name': FIRM_NAME,
'x-auth-token': signature,
},
body,
});
import hashlib, hmac, json, requests
body = json.dumps(payload, separators=(",", ":"))
signature = hmac.new(SHARED_SECRET.encode("utf-8"), body.encode("utf-8"), hashlib.sha256).hexdigest()
requests.post(
f"{BASE_URL}/services/apexrest/Outbound/Leads",
headers={
"Content-Type": "application/json",
"c-name": FIRM_NAME,
"x-auth-token": signature,
},
data=body,
)
Re-serializing the JSON after signing it, or letting an HTTP library reformat the body, changes the bytes and invalidates the signature. Treat the body as an opaque string once it has been signed.
- Keep the secret confidential. Do not log it, embed it in client-side code, or share it outside the system that signs requests.
- To rotate the secret, ask your Attorney Assistant account contact. We will issue a new secret for you to put into effect.
4. Request body
Every request body is a JSON object with a single top-level data object. All values are strings.
Omit a field you have no value for.
{
"data": {
"action": "enroll",
"Law_Firm_Name": "Example Law Firm",
"Lead_Phone_No": "5555550147"
}
}
4.1 Fields
| Field | Meaning | Required |
|---|---|---|
action | Either enroll or unenroll. | Yes |
Law_Firm_Name | Your firm's name, exactly as we configured it. Must match the c-name header. | Yes |
Lead_Phone_No | The lead's phone number. | Enroll: yes. Unenroll: only if External_System_Record_id is not sent. |
External_System_Record_id | Your own reference ID for the lead. Used to match repeat requests to the same record. | Recommended |
Source | Where the lead originated. Free-form label. | No |
Lead_First_Name | The lead's first name. | No |
Lead_Last_Name | The lead's last name. | No |
Lead_Email | The lead's email address. | No |
Date_of_Birth | The lead's date of birth. | No |
Calling_On_Behalf_Of | Name of the person the lead is calling on behalf of, if not themselves. | No |
Mailing_Address | The lead's street address. | No |
Mailing_City | The lead's city. | No |
Mailing_State | The lead's state. | No |
Mailing_Postal_Zip | The lead's ZIP or postal code. | No |
Incident_Location | Where the incident happened. | No |
Date_Of_Incident | Date the incident happened. | No |
Currently_Represented | Whether the lead already has an attorney. | No |
Injury_Description | Short description of the injury. | No |
Outcome | Outcome or status detail. | No |
Best_Callback_Time | The lead's preferred callback time. | No |
Other_Questions_And_Answers | Any additional questions and answers captured during your intake. | No |
Case_Type | Type of case. Should match the case-type list agreed for your firm. | No |
External_System_Record_id is optional, but it is what lets us recognise a lead you have sent
before. With it, an enroll request for a lead we already hold updates that record instead of creating
a second one, and an unenroll request can target the lead precisely. Without it, unenroll has to
match on phone number alone.
5. Action: enroll
Enroll a lead for outbound calling.
5.1 Example request
{
"data": {
"action": "enroll",
"Source": "Website form",
"Law_Firm_Name": "Example Law Firm",
"Lead_First_Name": "Jane",
"Lead_Last_Name": "Doe",
"Lead_Phone_No": "5555550147",
"Lead_Email": "jane.doe@example.com",
"Date_of_Birth": "2000-10-05",
"Mailing_Address": "123 Main St",
"Mailing_City": "Denver",
"Mailing_State": "CO",
"Mailing_Postal_Zip": "80202",
"Incident_Location": "Intersection of 1st and Main",
"Date_Of_Incident": "2026-09-03",
"Currently_Represented": "No",
"Injury_Description": "Neck and lower back pain.",
"Best_Callback_Time": "Weekdays after 5pm",
"Case_Type": "Motor Vehicle Accident",
"External_System_Record_id": "LEAD-48213"
}
}
5.2 What happens
- We verify the signature and confirm the firm named in the request is active.
- We create a lead record for your firm, or update the existing record if we have seen the same
External_System_Record_idbefore. - If the lead was already in our calling queue, the queue is updated so both sides stay in sync.
- We return HTTP 201 with the record's ID.
6. Action: unenroll
Stop calling a lead. Only the firm name and one identifier are required. You may send other fields, and we keep them for audit, but they play no part in locating the lead or stopping the calls.
6.1 Example request
{
"data": {
"action": "unenroll",
"Law_Firm_Name": "Example Law Firm",
"External_System_Record_id": "LEAD-48213"
}
}
{
"data": {
"action": "unenroll",
"Law_Firm_Name": "Example Law Firm",
"Lead_Phone_No": "5555550147"
}
}
6.2 What happens
- We verify the signature and confirm the firm named in the request is active.
- We locate the lead using
External_System_Record_idif provided, otherwiseLead_Phone_No. - We record the stop-calling request and return HTTP 201 immediately.
- The calling cadence for that lead is ended in the background. You do not need to wait for or confirm this step.
7. Responses
Every response is a JSON body with three fields.
{
"success": true,
"message": "Lead created",
"recordId": "a0X000000000001"
}
| Field | Type | Meaning |
|---|---|---|
success | boolean | Whether the request was accepted. |
message | string | Short human-readable description of the outcome. |
recordId | string or null | The ID of the record we created or matched, when applicable. |
7.1 HTTP status codes
| Code | Meaning | What to do |
|---|---|---|
| 201 | Success. A lead was created or updated, or a stop-calling request was queued. | Nothing further. |
| 200 | Success. A repeat unenroll for a lead we already stopped within the last 60 minutes. | Nothing further. Treat as handled. |
| 400 | Bad request. Required information is missing or invalid. | Correct the request. Do not retry unchanged. |
| 401 | Not authorized. The firm name or signature is invalid. | Check c-name, Law_Firm_Name, and your signature computation. Do not retry unchanged. |
| 500 | Server error on our side. | Retry after a short delay. Contact us if it persists. |
| 503 | The stop-calling service is temporarily unavailable. Enroll requests are unaffected. | Retry the unenroll later. |
8. Retries and duplicate requests
Both actions are safe to retry.
- Enroll. Sending the same
External_System_Record_idmore than once updates the existing record. No duplicate lead is created. - Unenroll. A repeat stop-calling request for the same lead within 60 minutes returns HTTP 200 and performs no further action.
Retry any request that fails with a network error, a timeout, or HTTP 500 or 503. Do not retry a 400 or 401 without first changing the request.
9. What you need to implement
- Store the base URL, firm name, and shared secret we issue to you, keeping the secret out of logs and client-side code.
- Build the request body as a JSON object wrapped in
data, with the correctaction. - Serialize the body once, compute the HMAC-SHA256 hex signature over that exact string, and send the string unchanged.
- Send
Content-Type: application/json,c-name, andx-auth-tokenheaders on every request. - Include
External_System_Record_idon every lead so enroll and unenroll can target the same record. - Handle the status codes above, retrying only on 500, 503, and transport failures.
Need help?
Integration questions, credential rotation, and endpoint changes go through your Attorney Assistant account contact.