Skip to main content

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.

note

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​

AspectContract
TransportHTTPS POST, TLS 1.2 or later. Your certificate must be valid for your hostname and issued by a publicly trusted certificate authority.
EndpointA 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 headersContent-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 typeEvery payload has a top-level event field, either call_completion or agreement_signed. Route on this field, not on the presence of other fields.
Correlationusage_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 IDsFor 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.
IndependenceThe 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.
OrderAn 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 eventWe 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 fieldsEvery payload starts with the same fields: event, event_id, schema_version, received_at, and usually merged_versions. See Fields on every event.
Null valuesA 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 typesEvery 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.
TimestampsAll timestamps are UTC in ISO-8601 format, for example 2026-09-15T20:00:00Z.
Character encodingUTF-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.
Validate on every request

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 responseWhat we do
Any 2xx within 10 secondsDelivered. Nothing further.
No response within 10 seconds, a connection error or a TLS errorRetry.
408, 425, 429, or any 5xxRetry.
Any 3xxNo retry. We do not follow redirects. The event is held for our support team.
Any other 4xx, for example 400, 401, 403, 404 or 422No 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 4xx tells us the request itself was rejected, so we do not repeat it unchanged. Return a 4xx only for an event you mean to reject. For a temporary problem on your side, return 503 so that we retry.
  • Because deliveries are retried and can be resent, the same event may reach you more than once.
De-duplication

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.

FieldTypeMeaning
eventstringThe event type, call_completion or agreement_signed. Route on this value.
event_idstring (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_versionstringThe version of the payload format, for example "1". See Payload versioning.
received_atstring (UTC ISO-8601)When we received the event.
merged_versionsstring, optionalHow 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​

call_completion
{
"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.

FieldTypeMeaning
eventstringAlways call_completion for this event. Route on this value.
usage_idstringIdentifies 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_idstringIdentifies the engagement the event belongs to.
outbound_lead_idstring or nullThe recordId we returned when you enrolled this lead. null for inbound calls. See Matching events to leads you enrolled.
external_system_record_idstring or nullYour 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​

FieldTypeMeaning
call_startstring (UTC ISO-8601) or nullWhen the call began.
call_endstring (UTC ISO-8601) or nullWhen the call ended.
call_dispositionstring or nullThe outcome the agent recorded for the call.
agent_notesstring or nullFree-text notes the agent recorded.
ai_summarystring or nullA generated summary of the call.
call_idstring or nullUnique identifier for the call.
recording_linkstring (URL) or nullLink to the call recording.
directionstring or nullCall 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.

FieldTypeMeaning
case_typestring or nullThe category of the legal matter.
calling_on_behalf_ofstring or nullWho the caller is calling on behalf of, for example Self.
what_is_your_relation_to_themstring or nullThe caller's relationship to the injured party.
injured_party_first_namestring or nullInjured party first name.
injured_party_last_namestring or nullInjured party last name.
your_name_the_callerstring or nullThe caller's own name.
date_of_birthstring or nullInjured party date of birth.
best_callback_numberstring or nullBest number to reach the caller.
backup_phone_or_emergency_contactstring or nullBackup phone or emergency contact.
emailstring or nullContact email supplied during the questionnaire.
mailing_addressstring or nullMailing street address.
mailing_citystring or nullMailing city.
mailing_statestring or nullMailing state.
mailing_postalzipstring or nullMailing postal or ZIP code.
incident_datestring or nullDate the incident occurred.
incident_approximate_timestring or nullApproximate time of the incident.
incident_citystring or nullCity where the incident occurred.
incident_statestring or nullState where the incident occurred.
incident_zipstring or nullPostal or ZIP code of the incident location.
incident_locationstring or nullDescription of the exact incident location.
how_did_it_happenstring or nullHow the incident happened.
injury_descriptionstring or nullDescription of the injuries.

intake_information.client_information​

FieldTypeMeaning
firstNamestring or nullClient first name.
lastNamestring or nullClient last name.
phonestring or nullClient phone number.
emailstring or nullClient 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.

FieldTypeMeaning
questionstringThe question identifier.
answerstring or nullThe 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.

Caseoutbound_lead_idexternal_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 callnullnull

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​

agreement_signed
{
"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.

FieldTypeMeaning
eventstringAlways agreement_signed for this event. Route on this value.
agreement_information.agreement_statusstring or nullStatus of the signed document, for example completed.
agreement_information.agreement_urlstring (URL) or nullLink to the agreement document. Retrieve the document yourself from this URL; we deliver the link, not the file.
usage_idstringIdentifies 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_idstringIdentifies the engagement the event belongs to.
recent_call_idstring or nullThe 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_idstring or nullAs on call_completion. See Matching events to leads you enrolled.
external_system_record_idstring or nullAs 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​

  1. If recent_call_id has a value, the agreement belongs to the call_completion event whose call_information.call_id has the same value, for the same engagement_id.
  2. If recent_call_id is null, fall back to the most recent call_completion event with the same engagement_id and usage_id.
  3. 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​

  1. 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.
  2. Validate the x-api-key header, or the scheme we agreed with you, on every request; reject requests without the correct value.
  3. Read the event field and route to your call or agreement handler.
  4. Parse the payload, treating any field as possibly null, every value as a string, and merged_versions as possibly absent. Ignore fields you do not recognise.
  5. Store the event, then return a 2xx status within 10 seconds. Return a 4xx only for an event you mean to reject; for a temporary problem, return 503 so that we retry.
  6. De-duplicate on event_id so a retried delivery is accepted only once. Never de-duplicate on usage_id.
  7. Link a signed agreement to its call using recent_call_id, falling back to usage_id when it is null. See Linking an agreement to its call.
  8. If you enroll leads with us, match events to your leads using outbound_lead_id or external_system_record_id.

Need help?​

Integration questions, credential rotation, and endpoint changes go through your Attorney Assistant account contact.