Call Completions and Signed Agreements Guide
This document describes everything required to receive completed intake calls and signed agreements from Frontline. A developer can build and operate the receiving endpoint from this document alone.
Frontline was previously named Intake360. The rename did not change the product. The payloads described here have since gained new fields; every change to them has been an addition, so existing fields keep their meaning. See Payload versioning.
1. Overview
Frontline delivers two kinds of event to an endpoint that you host:
- A completed intake call (
call_completion). - A signed agreement (
agreement_signed).
We send each event to your endpoint as a JSON POST over HTTPS shortly after it occurs, normally within a minute. A signed agreement is sent after the call it belongs to. You acknowledge receipt with an HTTP 2xx status, such as 200. That is the entire contract.
2. How delivery works
| Aspect | Contract |
|---|---|
| Transport | HTTPS POST, TLS 1.2 or later. Your certificate must be valid for your hostname and issued by a publicly trusted certificate authority. |
| Endpoint | A single HTTPS URL that you host and provide to us. Both event types are delivered to the same URL. It must be reachable on the public internet and must not contain a username or password. Give us the final URL: we do not follow redirects. |
| Request headers | Content-Type: application/json; X-Event-Id, the same value as event_id; X-Schema-Version, the same value as schema_version; User-Agent, which identifies our delivery service (do not rely on its exact value); and your authentication header. See Authentication. |
| Event type | Every payload has a top-level event field, either call_completion or agreement_signed. Route on this field, not on the presence of other fields. |
| Correlation | usage_id identifies a person and potential case at your firm. One usage_id can have several calls and more than one agreement, so it does not identify a single event. To link a signed agreement to its call, use recent_call_id. See Linking an agreement to its call. |
| Your lead IDs | For a lead you enrolled through Outbound lead enrollment, both events carry outbound_lead_id and external_system_record_id, so you can match each event to the lead you sent. See Matching events to leads you enrolled. |
| Independence | The signed agreement is a separate, later event. It does not arrive for every matter, and it may arrive before or after you expect. Do not wait for it and do not treat its absence as an error. |
| Order | An agreement is delivered after the call it belongs to. If we have not finished sending you that call, the agreement waits for it. If the call has not arrived after about 30 minutes, at most 35, we send the agreement on its own. See Delivery timing and order. |
| One delivery per event | We send each event once, with the most complete data we have. We do not send updated versions of an event we have already delivered. A retry of a failed delivery is the same event, with the same event_id. |
| Common fields | Every payload starts with the same fields: event, event_id, schema_version, received_at, and usually merged_versions. See Fields on every event. |
| Null values | A field we do not have a value for is sent as an explicit null. Fields are never omitted, with one exception: merged_versions may be absent. Your parser can rely on every other key being present. |
| Value types | Every value is a string or null, including numbers, counts and yes/no answers. A true/false value is sent as the string "true" or "false". The only array is custom_questions. |
| Timestamps | All timestamps are UTC in ISO-8601 format, for example 2026-09-15T20:00:00Z. |
| Character encoding | UTF-8. |
2.1 Delivery timing and order
Calls. Our call platform can report the same completed call several times within a few seconds, sometimes adding detail with each report. We wait until the reports stop, normally about 10 seconds and at most about a minute, merge them, and send you the call once. If details of a call change after we have sent it, we do not send an update.
Agreements. A signed agreement is sent after the call it belongs to:
- If we have already sent you that call, the agreement follows without further delay.
- If the call is still on its way, for example because the caller signed while still on the phone, the agreement waits until the call has been sent.
- If we are retrying the call because your endpoint did not accept it, the agreement waits until the call is accepted or our retries end.
- If we have no call for the agreement after about 30 minutes, we send the agreement on its own, at the latest 35 minutes after we received it. This is the only case in which an agreement can reach you before its call, or without one.
Your integration should still accept an agreement whose call it has not seen yet. See Linking an agreement to its call.
3. Authentication
Every request carries a credential that we agree with you. By default it is a token that we
generate and issue to you, sent in the x-api-key request header on every delivery.
If your endpoint needs a different scheme, we can instead send the token in a request header with a
name you choose, as Authorization: Bearer <token>, or as HTTP Basic credentials. Ask your Attorney
Assistant account contact. The rest of this guide uses x-api-key.
- Validate the credential on every incoming request. Reject any request that is missing it or presents the wrong value.
- Keep the credential secret. Do not log it or expose it.
- Rotation is available on manual request. There is no automatic rotation at this time. To rotate, ask us and we will issue a new token for you to put into effect. For a few minutes after we switch, deliveries can still carry the old value, so accept both until we confirm the switch is complete.
An endpoint that accepts unauthenticated POSTs will accept anything the public internet sends it. Check the header before you parse the body.
4. The response we require
Return any HTTP 2xx status, such as 200, once you have received and stored the event.
- Respond within 10 seconds. Return as soon as the payload is safely received. Do not wait for your downstream processing to complete.
- We do not read the response body. An empty body is fine.
- Any other status, or no response within 10 seconds, is a failed delivery. Whether we retry it depends on what you returned. See Retry behaviour below.
5. Retry behaviour
What happens after a delivery depends on your response:
| Your response | What we do |
|---|---|
| Any 2xx within 10 seconds | Delivered. Nothing further. |
| No response within 10 seconds, a connection error or a TLS error | Retry. |
408, 425, 429, or any 5xx | Retry. |
Any 3xx | No retry. We do not follow redirects. The event is held for our support team. |
Any other 4xx, for example 400, 401, 403, 404 or 422 | No retry. The event is held for our support team. |
- We make up to 5 attempts in total, about 3 minutes apart, so retries end roughly 12 minutes after the first attempt.
- An event that is held, or whose retries have all failed, is not lost. Our support team is alerted
and, once the cause is fixed, can send it again. A resent event has the same
event_id. - A
4xxtells us the request itself was rejected, so we do not repeat it unchanged. Return a4xxonly for an event you mean to reject. For a temporary problem on your side, return503so that we retry. - Because deliveries are retried and can be resent, the same event may reach you more than once.
Use event_id as the unique key for an event. A retried delivery carries the same event_id, so
you can safely ignore one you have already accepted. Do not de-duplicate on usage_id: several
calls and agreements for the same person share one usage_id, and each of them is a separate event.
The same value is also sent in the X-Event-Id header, so you can check for a duplicate before you
parse the body.
6. Fields on every event
Both event types begin with the same fields, in this order. The event-specific fields follow them.
| Field | Type | Meaning |
|---|---|---|
event | string | The event type, call_completion or agreement_signed. Route on this value. |
event_id | string (UUID) | Unique identifier for the event. If we deliver the same event more than once, every delivery carries the same event_id. Use it to de-duplicate. See Retry behaviour. |
schema_version | string | The version of the payload format, for example "1". See Payload versioning. |
received_at | string (UTC ISO-8601) | When we received the event. |
merged_versions | string, optional | How many copies of this event we received from our call platform and combined into this single delivery, for example "2". For information only. This field may be absent; do not rely on it. |
7. Event: call_completion
7.1 Example payload
{
"event": "call_completion",
"event_id": "7d93ce2a-6246-42dc-9435-cb174e2e9e08",
"schema_version": "1",
"received_at": "2026-09-15T20:07:41Z",
"merged_versions": "2",
"call_information": {
"call_start": "2026-09-15T20:00:00Z",
"call_end": "2026-09-15T20:07:30Z",
"call_disposition": "Qualified",
"agent_notes": "Caller was rear-ended; no police report.",
"ai_summary": "Inbound caller reported a motor vehicle accident and is seeking representation.",
"call_id": "CALL-9001",
"recording_link": "https://recordings.example.com/CALL-9001",
"direction": "Inbound"
},
"intake_information": {
"standard_questions": {
"case_type": "Motor Vehicle Accident",
"calling_on_behalf_of": "Self",
"what_is_your_relation_to_them": null,
"injured_party_first_name": "Jane",
"injured_party_last_name": "Doe",
"your_name_the_caller": null,
"date_of_birth": "2000-10-05",
"best_callback_number": "5555550147",
"backup_phone_or_emergency_contact": null,
"email": "jane.doe@example.com",
"mailing_address": "123 Main St",
"mailing_city": "Denver",
"mailing_state": "CO",
"mailing_postalzip": "80202",
"incident_date": "2026-09-03",
"incident_approximate_time": "14:30",
"incident_city": "Denver",
"incident_state": "CO",
"incident_zip": "80202",
"incident_location": "Intersection of 1st and Main",
"how_did_it_happen": "Rear-ended at a red light.",
"injury_description": "Neck and lower back pain."
},
"client_information": {
"firstName": "Jane",
"lastName": "Doe",
"phone": "5555550147",
"email": "jane.doe@example.com"
},
"custom_questions": [
{ "question": "currently_represented", "answer": "No" },
{ "question": "received_medical_care", "answer": "Yes" }
]
},
"usage_id": "12345",
"engagement_id": "13",
"outbound_lead_id": null,
"external_system_record_id": null
}
7.2 Field definitions
Top level
The payload starts with the fields on every event. The fields below follow them.
| Field | Type | Meaning |
|---|---|---|
event | string | Always call_completion for this event. Route on this value. |
usage_id | string | Identifies a person and potential case at your firm. Several calls and agreements can share one usage_id, so it is not unique per event. |
engagement_id | string | Identifies the engagement the event belongs to. |
outbound_lead_id | string or null | The recordId we returned when you enrolled this lead. null for inbound calls. See Matching events to leads you enrolled. |
external_system_record_id | string or null | Your own External_System_Record_id, as you sent it when you enrolled the lead. null for inbound calls, or when you sent no ID of your own. |
call_information
| Field | Type | Meaning |
|---|---|---|
call_start | string (UTC ISO-8601) or null | When the call began. |
call_end | string (UTC ISO-8601) or null | When the call ended. |
call_disposition | string or null | The outcome the agent recorded for the call. |
agent_notes | string or null | Free-text notes the agent recorded. |
ai_summary | string or null | A generated summary of the call. |
call_id | string or null | Unique identifier for the call. |
recording_link | string (URL) or null | Link to the call recording. |
direction | string or null | Call direction, for example Inbound or Outbound. |
intake_information.standard_questions
A fixed set of intake fields. All keys below are always present; a field with no answer is null.
| Field | Type | Meaning |
|---|---|---|
case_type | string or null | The category of the legal matter. |
calling_on_behalf_of | string or null | Who the caller is calling on behalf of, for example Self. |
what_is_your_relation_to_them | string or null | The caller's relationship to the injured party. |
injured_party_first_name | string or null | Injured party first name. |
injured_party_last_name | string or null | Injured party last name. |
your_name_the_caller | string or null | The caller's own name. |
date_of_birth | string or null | Injured party date of birth. |
best_callback_number | string or null | Best number to reach the caller. |
backup_phone_or_emergency_contact | string or null | Backup phone or emergency contact. |
email | string or null | Contact email supplied during the questionnaire. |
mailing_address | string or null | Mailing street address. |
mailing_city | string or null | Mailing city. |
mailing_state | string or null | Mailing state. |
mailing_postalzip | string or null | Mailing postal or ZIP code. |
incident_date | string or null | Date the incident occurred. |
incident_approximate_time | string or null | Approximate time of the incident. |
incident_city | string or null | City where the incident occurred. |
incident_state | string or null | State where the incident occurred. |
incident_zip | string or null | Postal or ZIP code of the incident location. |
incident_location | string or null | Description of the exact incident location. |
how_did_it_happen | string or null | How the incident happened. |
injury_description | string or null | Description of the injuries. |
intake_information.client_information
| Field | Type | Meaning |
|---|---|---|
firstName | string or null | Client first name. |
lastName | string or null | Client last name. |
phone | string or null | Client phone number. |
email | string or null | Client email address. |
intake_information.custom_questions
An array of firm-specific questionnaire items that fall outside the standard set. Empty array when there are none.
| Field | Type | Meaning |
|---|---|---|
question | string | The question identifier. |
answer | string or null | The recorded answer. |
7.3 Matching events to leads you enrolled
When you enroll a lead through Outbound lead enrollment, the
response returns a recordId, and you can send your own
External_System_Record_id. Both call_completion and agreement_signed carry these values back to
you as outbound_lead_id and external_system_record_id.
| Case | outbound_lead_id | external_system_record_id |
|---|---|---|
| A lead you enrolled, with your own ID | "a0X000000000001" | "LEAD-48213" |
| A lead you enrolled, without your own ID | "a0X000000000001" | null |
| An inbound call | null | null |
Treat both fields as optional. Either can be null, and an event is never held back because a value
is missing.
8. Event: agreement_signed
8.1 Example payload
{
"event": "agreement_signed",
"event_id": "3f2b8c1e-9a4d-4e6f-8b7a-2c5d1e0f9a63",
"schema_version": "1.5",
"received_at": "2026-09-15T20:31:05Z",
"merged_versions": "1",
"agreement_information": {
"agreement_status": "completed",
"agreement_url": "https://app.pandadoc.com/s/abc123"
},
"usage_id": "12345",
"engagement_id": "13",
"recent_call_id": "CALL-9001",
"outbound_lead_id": null,
"external_system_record_id": null
}
8.2 Field definitions
The payload starts with the fields on every event. The fields below follow them.
| Field | Type | Meaning |
|---|---|---|
event | string | Always agreement_signed for this event. Route on this value. |
agreement_information.agreement_status | string or null | Status of the signed document, for example completed. |
agreement_information.agreement_url | string (URL) or null | Link to the agreement document. Retrieve the document yourself from this URL; we deliver the link, not the file. |
usage_id | string | Identifies a person and potential case at your firm. Several calls and agreements can share one usage_id, so it is not unique per event. |
engagement_id | string | Identifies the engagement the event belongs to. |
recent_call_id | string or null | The call_id of the most recent call for this person and case when the agreement was signed. It matches call_information.call_id on that call's call_completion event. null when no call is known. |
outbound_lead_id | string or null | As on call_completion. See Matching events to leads you enrolled. |
external_system_record_id | string or null | As on call_completion. |
An agreement event carries no call or intake data. Use recent_call_id to find the call it belongs to.
8.3 Linking an agreement to its call
- If
recent_call_idhas a value, the agreement belongs to thecall_completionevent whosecall_information.call_idhas the same value, for the sameengagement_id. - If
recent_call_idisnull, fall back to the most recentcall_completionevent with the sameengagement_idandusage_id. - If you have not received that call yet, keep the agreement and link it when the call arrives. This is rare: we normally send the call first. See Delivery timing and order.
9. Payload versioning
schema_version tells you which version of the payload format an event uses.
- Minor versions (for example, a change in the number after the dot) only add fields. Existing integrations keep working without changes. Ignore any field you do not recognise rather than rejecting the event.
- Major versions (a change in the number before the dot) remove or rename a field, or change its meaning or type. We announce a major version to you in advance.
10. What you need to implement
- Host an HTTPS endpoint that accepts POST requests with
Content-Type: application/json. It must be reachable on the public internet with a publicly trusted certificate. Give us its final URL; we do not follow redirects. - Validate the
x-api-keyheader, or the scheme we agreed with you, on every request; reject requests without the correct value. - Read the
eventfield and route to your call or agreement handler. - Parse the payload, treating any field as possibly
null, every value as a string, andmerged_versionsas possibly absent. Ignore fields you do not recognise. - Store the event, then return a 2xx status within 10 seconds. Return a
4xxonly for an event you mean to reject; for a temporary problem, return503so that we retry. - De-duplicate on
event_idso a retried delivery is accepted only once. Never de-duplicate onusage_id. - Link a signed agreement to its call using
recent_call_id, falling back tousage_idwhen it isnull. See Linking an agreement to its call. - If you enroll leads with us, match events to your leads using
outbound_lead_idorexternal_system_record_id.
Need help?
Integration questions, credential rotation, and endpoint changes go through your Attorney Assistant account contact.