openapi: 3.1.0
info:
  title: Frontline API
  version: '1.0'
  description: |
    Machine-readable reference for the two Frontline integrations. The
    [Outbound lead enrollment](/docs/frontline/outbound-leads) and
    [Call completions and agreements](/docs/frontline/call-completions) guides
    remain the full contract, including retry rules and worked examples. This
    reference mirrors them so you can import the spec into your own tooling.

    **Outbound leads** is an endpoint we host. You call it to enroll a lead for
    outbound calling and to stop those calls.

    **Call completions** and **signed agreements** are webhooks. We send them to
    an endpoint you host. They appear under *Webhooks* in the sidebar.

    Frontline was previously named Intake360. The product and the data contract
    are unchanged.

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

servers:
  - url: https://{host}
    description: Issued to your firm with your credentials.
    variables:
      host:
        default: your-frontline-host.example.com
        description: Provided by Attorney Assistant together with your firm name and shared secret.

tags:
  - name: Outbound leads
    description: Enroll leads for outbound calling, and stop calling a lead, through a single signed endpoint we host.
  - name: Webhooks
    description: Events Frontline delivers to an HTTPS endpoint you host.

x-tagGroups:
  - name: Endpoints we host
    tags: [Outbound leads]
  - name: Events we send you
    tags: [Webhooks]

paths:
  /services/apexrest/Outbound/Leads:
    post:
      tags: [Outbound leads]
      operationId: enrollOrUnenrollLead
      summary: Enroll or unenroll a lead
      description: |
        One endpoint handles both actions. The `action` field inside `data`
        selects the behaviour.

        - **enroll** creates a lead record for your firm, or updates the record
          we already hold for the same `External_System_Record_id`. Returns 201.
        - **unenroll** records a stop-calling request and ends the calling
          cadence in the background. Returns 201, or 200 when the same lead was
          already stopped within the last 60 minutes.

        Both actions are safe to retry. Retry on network errors, timeouts, 500
        and 503. Do not retry a 400 or 401 without changing the request.

        **Signing.** Serialize the JSON body to a string once, compute an
        HMAC-SHA256 over that exact string using the shared secret we issued,
        hex-encode it in lowercase, send it in `x-auth-token`, and send the same
        string unchanged as the body. See
        [Authentication](/docs/frontline/outbound-leads#3-authentication) for
        Node.js and Python examples.

        The *Send API Request* panel cannot compute the signature for you, so a
        request sent from this page returns 401. Use the generated code samples
        as a starting point and add the signing step.
      security:
        - firmName: []
          hmacSignature: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OutboundLeadRequest'
            examples:
              enroll:
                summary: Enroll a lead
                value:
                  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
              unenrollByReferenceId:
                summary: Unenroll by reference ID
                value:
                  data:
                    action: unenroll
                    Law_Firm_Name: Example Law Firm
                    External_System_Record_id: LEAD-48213
              unenrollByPhone:
                summary: Unenroll by phone number
                value:
                  data:
                    action: unenroll
                    Law_Firm_Name: Example Law Firm
                    Lead_Phone_No: '5555550147'
      responses:
        '201':
          description: Success. A lead was created or updated, or a stop-calling request was queued.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OutboundLeadResponse'
              example:
                success: true
                message: Lead created
                recordId: a0X000000000001
        '200':
          description: Success. A repeat unenroll for a lead we already stopped within the last 60 minutes. Treat as handled.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OutboundLeadResponse'
              example:
                success: true
                message: Stop-calling request already recorded
                recordId: a0X000000000001
        '400':
          description: Bad request. Required information is missing or invalid. Correct the request; do not retry unchanged.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OutboundLeadResponse'
              example:
                success: false
                message: Lead_Phone_No is required for enroll
                recordId: null
        '401':
          description: Not authorized. The firm name or signature is invalid. Check `c-name`, `Law_Firm_Name`, and your signature computation; do not retry unchanged.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OutboundLeadResponse'
              example:
                success: false
                message: Invalid signature
                recordId: null
        '500':
          description: Server error on our side. Retry after a short delay. Contact us if it persists.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OutboundLeadResponse'
              example:
                success: false
                message: Unexpected error
                recordId: null
        '503':
          description: The stop-calling service is temporarily unavailable. Enroll requests are unaffected. Retry the unenroll later.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OutboundLeadResponse'
              example:
                success: false
                message: Stop-calling service unavailable
                recordId: null
    get:
      tags: [Outbound leads]
      operationId: outboundLeadsHealthCheck
      summary: Health check
      description: |
        Returns a static message and does not process anything. Use it to
        confirm connectivity only. No headers or signature are required.
      security: []
      responses:
        '200':
          description: The endpoint is reachable.
          content:
            text/plain:
              schema:
                type: string
              example: Outbound Leads endpoint is available

webhooks:
  call_completion:
    post:
      tags: [Webhooks]
      operationId: callCompletion
      summary: call_completion
      description: |
        Sent to your endpoint as soon as an intake call completes. Return
        HTTP 200 once the payload is safely received; do not wait for your
        downstream processing.

        Any other status, or no response within our timeout, is treated as a
        failed delivery and retried up to 5 times over approximately 20
        minutes. A retried delivery can reach you more than once, so
        de-duplicate on the pair (`usage_id`, `event`).

        Every key listed below is always present. A field we have no value for
        is sent as an explicit `null`. Timestamps are UTC ISO-8601.
      security:
        - apiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CallCompletionEvent'
            example:
              event: call_completion
              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'
      responses:
        '200':
          description: Acknowledged. We stop retrying this delivery.
        default:
          description: Any other status, or no response within our timeout, is treated as a failed delivery and retried.

  agreement_signed:
    post:
      tags: [Webhooks]
      operationId: agreementSigned
      summary: agreement_signed
      description: |
        Sent to the same endpoint when a signed agreement is completed for a
        matter. It is a separate, later event. It does not arrive for every
        matter and may arrive before or after you expect it, so do not wait
        for it and do not treat its absence as an error.

        Link it back to its call using `usage_id`, which matches the
        `call_completion` event for the same matter. We deliver the link to the
        agreement, not the file; retrieve the document from `agreement_url`.

        Return HTTP 200 once received. Delivery and retry rules are the same as
        for `call_completion`.
      security:
        - apiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AgreementSignedEvent'
            example:
              event: agreement_signed
              agreement_information:
                agreement_status: completed
                agreement_url: https://app.pandadoc.com/s/abc123
              usage_id: '12345'
              engagement_id: '13'
      responses:
        '200':
          description: Acknowledged. We stop retrying this delivery.
        default:
          description: Any other status, or no response within our timeout, is treated as a failed delivery and retried.

components:
  securitySchemes:
    firmName:
      type: apiKey
      in: header
      name: c-name
      description: Your firm's name, exactly as we configured it. We use it to look up your secret. Must match `Law_Firm_Name` in the body.
    hmacSignature:
      type: apiKey
      in: header
      name: x-auth-token
      description: |
        HMAC-SHA256 signature of the raw request body, keyed with the shared
        secret we issued to your firm, encoded as lowercase hexadecimal. We
        recreate the signature from the bytes we received and reject any
        mismatch with HTTP 401. Sign the exact string you send; re-serializing
        the JSON after signing changes the bytes and invalidates the signature.
    apiKey:
      type: apiKey
      in: header
      name: x-api-key
      description: |
        Token we generate and issue to you. Sent on every webhook delivery.
        Validate it on every incoming request and reject any request that is
        missing the header or presents the wrong value. Rotation is on manual
        request through your account contact.

  schemas:
    OutboundLeadRequest:
      type: object
      required: [data]
      properties:
        data:
          oneOf:
            - $ref: '#/components/schemas/EnrollData'
            - $ref: '#/components/schemas/UnenrollData'
          discriminator:
            propertyName: action
            mapping:
              enroll: '#/components/schemas/EnrollData'
              unenroll: '#/components/schemas/UnenrollData'

    LeadFields:
      type: object
      description: Lead fields shared by both actions. All values are strings. Omit a field you have no value for.
      properties:
        Law_Firm_Name:
          type: string
          description: Your firm's name, exactly as we configured it. Must match the `c-name` header.
          example: Example Law Firm
        Lead_Phone_No:
          type: string
          description: The lead's phone number.
          example: '5555550147'
        External_System_Record_id:
          type: string
          description: |
            Your own reference ID for the lead. Recommended on every request.
            With it, a repeat enroll updates the existing record instead of
            creating a second one, and an unenroll can target the lead
            precisely. Without it, unenroll has to match on phone number alone.
          example: LEAD-48213
        Source:
          type: string
          description: Where the lead originated. Free-form label.
          example: Website form
        Lead_First_Name:
          type: string
          description: The lead's first name.
          example: Jane
        Lead_Last_Name:
          type: string
          description: The lead's last name.
          example: Doe
        Lead_Email:
          type: string
          description: The lead's email address.
          example: jane.doe@example.com
        Date_of_Birth:
          type: string
          description: The lead's date of birth.
          example: '2000-10-05'
        Calling_On_Behalf_Of:
          type: string
          description: Name of the person the lead is calling on behalf of, if not themselves.
        Mailing_Address:
          type: string
          description: The lead's street address.
          example: 123 Main St
        Mailing_City:
          type: string
          description: The lead's city.
          example: Denver
        Mailing_State:
          type: string
          description: The lead's state.
          example: CO
        Mailing_Postal_Zip:
          type: string
          description: The lead's ZIP or postal code.
          example: '80202'
        Incident_Location:
          type: string
          description: Where the incident happened.
          example: Intersection of 1st and Main
        Date_Of_Incident:
          type: string
          description: Date the incident happened.
          example: '2026-09-03'
        Currently_Represented:
          type: string
          description: Whether the lead already has an attorney.
          example: 'No'
        Injury_Description:
          type: string
          description: Short description of the injury.
          example: Neck and lower back pain.
        Outcome:
          type: string
          description: Outcome or status detail.
        Best_Callback_Time:
          type: string
          description: The lead's preferred callback time.
          example: Weekdays after 5pm
        Other_Questions_And_Answers:
          type: string
          description: Any additional questions and answers captured during your intake.
        Case_Type:
          type: string
          description: Type of case. Should match the case-type list agreed for your firm.
          example: Motor Vehicle Accident

    EnrollData:
      title: Enroll
      description: Enroll a lead for outbound calling. `Law_Firm_Name` and `Lead_Phone_No` are required.
      allOf:
        - type: object
          required: [action, Law_Firm_Name, Lead_Phone_No]
          properties:
            action:
              type: string
              enum: [enroll]
              description: Selects the enroll behaviour.
        - $ref: '#/components/schemas/LeadFields'

    UnenrollData:
      title: Unenroll
      description: |
        Stop calling a lead. `Law_Firm_Name` is required, plus one identifier:
        `External_System_Record_id` if you have it, otherwise `Lead_Phone_No`.
        Other fields may be sent and are kept for audit, but play no part in
        locating the lead.
      allOf:
        - type: object
          required: [action, Law_Firm_Name]
          properties:
            action:
              type: string
              enum: [unenroll]
              description: Selects the unenroll behaviour.
        - $ref: '#/components/schemas/LeadFields'

    OutboundLeadResponse:
      type: object
      required: [success, message, recordId]
      properties:
        success:
          type: boolean
          description: Whether the request was accepted.
        message:
          type: string
          description: Short human-readable description of the outcome.
        recordId:
          type: [string, 'null']
          description: The ID of the record we created or matched, when applicable.

    CallCompletionEvent:
      type: object
      required: [event, call_information, intake_information, usage_id, engagement_id]
      properties:
        event:
          type: string
          enum: [call_completion]
          description: Always `call_completion` for this event. Route on this value.
        call_information:
          $ref: '#/components/schemas/CallInformation'
        intake_information:
          $ref: '#/components/schemas/IntakeInformation'
        usage_id:
          type: string
          description: Correlation key for the matter. The `agreement_signed` event for the same matter carries the same value.
          example: '12345'
        engagement_id:
          type: string
          description: Identifies the engagement the event belongs to.
          example: '13'

    CallInformation:
      type: object
      required: [call_start, call_end, call_disposition, agent_notes, ai_summary, call_id, recording_link, direction]
      properties:
        call_start:
          type: [string, 'null']
          format: date-time
          description: When the call began. UTC ISO-8601.
        call_end:
          type: [string, 'null']
          format: date-time
          description: When the call ended. UTC ISO-8601.
        call_disposition:
          type: [string, 'null']
          description: The outcome the agent recorded for the call.
        agent_notes:
          type: [string, 'null']
          description: Free-text notes the agent recorded.
        ai_summary:
          type: [string, 'null']
          description: A generated summary of the call.
        call_id:
          type: [string, 'null']
          description: Unique identifier for the call.
        recording_link:
          type: [string, 'null']
          format: uri
          description: Link to the call recording.
        direction:
          type: [string, 'null']
          description: Call direction, for example `Inbound` or `Outbound`.

    IntakeInformation:
      type: object
      required: [standard_questions, client_information, custom_questions]
      properties:
        standard_questions:
          $ref: '#/components/schemas/StandardQuestions'
        client_information:
          $ref: '#/components/schemas/ClientInformation'
        custom_questions:
          type: array
          description: Firm-specific questionnaire items outside the standard set. Empty array when there are none.
          items:
            $ref: '#/components/schemas/CustomQuestion'

    StandardQuestions:
      type: object
      description: A fixed set of intake fields. All keys are always present; a field with no answer is `null`.
      required:
        - case_type
        - calling_on_behalf_of
        - what_is_your_relation_to_them
        - injured_party_first_name
        - injured_party_last_name
        - your_name_the_caller
        - date_of_birth
        - best_callback_number
        - backup_phone_or_emergency_contact
        - email
        - mailing_address
        - mailing_city
        - mailing_state
        - mailing_postalzip
        - incident_date
        - incident_approximate_time
        - incident_city
        - incident_state
        - incident_zip
        - incident_location
        - how_did_it_happen
        - injury_description
      properties:
        case_type:
          type: [string, 'null']
          description: The category of the legal matter.
        calling_on_behalf_of:
          type: [string, 'null']
          description: Who the caller is calling on behalf of, for example `Self`.
        what_is_your_relation_to_them:
          type: [string, 'null']
          description: The caller's relationship to the injured party.
        injured_party_first_name:
          type: [string, 'null']
          description: Injured party first name.
        injured_party_last_name:
          type: [string, 'null']
          description: Injured party last name.
        your_name_the_caller:
          type: [string, 'null']
          description: The caller's own name.
        date_of_birth:
          type: [string, 'null']
          description: Injured party date of birth.
        best_callback_number:
          type: [string, 'null']
          description: Best number to reach the caller.
        backup_phone_or_emergency_contact:
          type: [string, 'null']
          description: Backup phone or emergency contact.
        email:
          type: [string, 'null']
          description: Contact email supplied during the questionnaire.
        mailing_address:
          type: [string, 'null']
          description: Mailing street address.
        mailing_city:
          type: [string, 'null']
          description: Mailing city.
        mailing_state:
          type: [string, 'null']
          description: Mailing state.
        mailing_postalzip:
          type: [string, 'null']
          description: Mailing postal or ZIP code.
        incident_date:
          type: [string, 'null']
          description: Date the incident occurred.
        incident_approximate_time:
          type: [string, 'null']
          description: Approximate time of the incident.
        incident_city:
          type: [string, 'null']
          description: City where the incident occurred.
        incident_state:
          type: [string, 'null']
          description: State where the incident occurred.
        incident_zip:
          type: [string, 'null']
          description: Postal or ZIP code of the incident location.
        incident_location:
          type: [string, 'null']
          description: Description of the exact incident location.
        how_did_it_happen:
          type: [string, 'null']
          description: How the incident happened.
        injury_description:
          type: [string, 'null']
          description: Description of the injuries.

    ClientInformation:
      type: object
      required: [firstName, lastName, phone, email]
      properties:
        firstName:
          type: [string, 'null']
          description: Client first name.
        lastName:
          type: [string, 'null']
          description: Client last name.
        phone:
          type: [string, 'null']
          description: Client phone number.
        email:
          type: [string, 'null']
          description: Client email address.

    CustomQuestion:
      type: object
      required: [question, answer]
      properties:
        question:
          type: string
          description: The question identifier.
          example: currently_represented
        answer:
          type: [string, 'null']
          description: The recorded answer.
          example: 'No'

    AgreementSignedEvent:
      type: object
      required: [event, agreement_information, usage_id, engagement_id]
      properties:
        event:
          type: string
          enum: [agreement_signed]
          description: Always `agreement_signed` for this event. Route on this value.
        agreement_information:
          type: object
          required: [agreement_status, agreement_url]
          properties:
            agreement_status:
              type: [string, 'null']
              description: Status of the signed document, for example `completed`.
            agreement_url:
              type: [string, 'null']
              format: uri
              description: Link to the agreement document. Retrieve the document yourself from this URL; we deliver the link, not the file.
        usage_id:
          type: string
          description: Correlation key. Matches the `call_completion` event for the same matter.
          example: '12345'
        engagement_id:
          type: string
          description: Identifies the engagement the event belongs to.
          example: '13'
