> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pylon.mortgage/llms.txt
> Use this file to discover all available pages before exploring further.

# Disclosures

> Learn how to retrieve disclosure documents and enable borrowers to sign them

Disclosures are regulatory documents that borrowers must review and sign at various stages of the loan process. Pylon automatically generates and sends disclosures, but you need to retrieve signing links and track signing status.

## Types of disclosures

| Disclosure Type        | When Issued                                                                                          | Purpose                                                                                      |
| ---------------------- | ---------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| **Initial Disclosure** | After [rate lock](/entity-models/key-concepts/rate-lock) or 3 days after attaching product structure | Loan Estimate and initial disclosures ([TRID](/entity-models/key-concepts/trid) requirement) |
| **Redisclosure**       | When loan terms change after initial disclosure                                                      | Updated Loan Estimate if terms change                                                        |
| **Closing Disclosure** | When loan reaches "Clear to Close"                                                                   | Final closing costs and loan terms (must be delivered 3 days before closing)                 |

## Retrieving disclosures

Query disclosures using the `disclosuresHistory` query and filter by `packageType`:

```graphql theme={null}
query GetDisclosures($loanId: ID!) {
  disclosuresHistory(loanId: $loanId) {
    disclosures {
      packageType
      status
      sentOn
      completedOn
      recipients {
        displayName
        email
        signed
        type
        link {
          url
          accessToken
        }
      }
      documentLinks {
        id
        title
        url
      }
    }
  }
}
```

Filter the results by `packageType` to get specific disclosure types:

* `INITIAL_DISCLOSURE` - Initial disclosures (Loan Estimate)
* `REDISCLOSURE` - Redisclosures (updated Loan Estimate)
* `CLOSING_DISCLOSURE` - Closing disclosures (Closing Disclosure)

## Disclosure status

| Status      | Description                                                           |
| ----------- | --------------------------------------------------------------------- |
| `SENT`      | Disclosure has been sent to borrowers                                 |
| `COMPLETED` | All recipients have signed, including borrowers and loan officer (LO) |
| `SENDING`   | Disclosure is being sent                                              |
| `FAILED`    | Disclosure sending failed                                             |
| `CANCELLED` | Disclosure was cancelled                                              |
| `EXPIRED`   | Disclosure signing link expired                                       |

<Note>
  **For borrower experiences and ITP checking**: The `COMPLETED` status includes all recipients (borrowers and LO). If you're building borrower-facing experiences or checking if [Intent to Proceed (ITP)](/entity-models/key-concepts/itp) was received, use the [Checking for Intent to Proceed (ITP)](#checking-for-intent-to-proceed-itp) section instead, which checks only borrower signatures.
</Note>

## Getting signing links

Each disclosure has `recipients[]` that contain signing links. Query `disclosuresHistory` and filter by `packageType`:

```graphql theme={null}
query GetDisclosureSigningLinks($loanId: ID!) {
  disclosuresHistory(loanId: $loanId) {
    disclosures {
      packageType
      recipients {
        displayName
        signed
        link {
          url
          accessToken
        }
      }
    }
  }
}
```

The `link.url` contains the signing URL, and `link.accessToken` is the authentication token needed to access it.

## Using signing links

When you get a disclosure URL and access token back, you need to submit the access token via an HTML POST request into an iframe to access and sign the disclosure packet.

Here's a React component implementation:

```tsx theme={null}
import React from "react";

/*
 * Inputs to this component are:
 * document URL, title, and access token
 */
interface PdfSsoViewerProps {
  url: string;
  title?: string;
  accessToken?: string;
}

/*
 * Styles for the PDF SSO Viewer component.
 * The container is set to scroll and the iframe is styled to fit within it.
 */
const styles = {
  container: {
    display: "flex",
    flexDirection: "column" as const,
    width: "100%",
    height: "100%",
    flex: 1,
    overflow: "auto",
    frameBorder: "0",
  },
  iframe: {
    width: "100%",
    height: "100%",
    flex: 1,
    border: "none",
    scrolling: "auto",
    frameBorder: "0",
  },
};

/**
 * PdfSsoViewer component renders an iframe that loads a PDF document
 * using a single sign-on (SSO) mechanism. It automatically submits a form
 * with the access token to the specified URL when the component mounts.
 *
 * @param {PdfSsoViewerProps} props - The properties for the component.
 * @returns {JSX.Element} The rendered component.
 */
export const PdfSsoViewer = ({
  url,
  title,
  accessToken,
}: PdfSsoViewerProps) => {
  /*
   * Effect to automatically submit the form when the component mounts.
   * This is used to pass the access token to the SSO URL.
   */
  React.useEffect(() => {
    (document.getElementById("redirect-form") as HTMLFormElement)?.submit();
  }, []);

  return (
    <div
      style={{
        ...styles.container,
      }}
    >
      <iframe
        name={"sso-viewer"}
        title={`${title ?? ""}`}
        style={styles.iframe}
      ></iframe>
      <form
        action={`${url}`}
        method={"post"}
        id={"redirect-form"}
        target={"sso-viewer"}
        style={{ display: "none" }}
      >
        <input name={"access_token"} type={"hidden"} value={`${accessToken}`} />
        <input name={"cookiePassthrough"} type={"hidden"} value={"true"} />
      </form>
    </div>
  );
};
```

**How it works:**

1. The component creates a hidden form with `method="post"` that targets an iframe
2. The form includes the `access_token` as a hidden input field with the value from `link.accessToken`
3. The form's `action` is set to the `link.url` from the disclosure query
4. When the component mounts, it automatically submits the form, which POSTs the access token to the disclosure URL
5. The disclosure signing interface loads in the iframe, authenticated via the access token

**Usage example:**

```tsx theme={null}
// After querying disclosuresHistory and filtering by packageType
const initialDisclosure = disclosuresHistory.disclosures.find(
  d => d.packageType === "INITIAL_DISCLOSURE"
);
const disclosureLink = initialDisclosure?.recipients[0]?.link;

<PdfSsoViewer
  url={disclosureLink.url}
  accessToken={disclosureLink.accessToken}
  title="Initial Disclosure Packet"
/>
```

## Checking signing status

Check if all borrowers have signed by querying the `signed` field for each recipient:

```graphql theme={null}
query CheckDisclosureSigningStatus($loanId: ID!) {
  disclosuresHistory(loanId: $loanId) {
    disclosures {
      packageType
      recipients {
        displayName
        signed
        type
      }
    }
  }
}
```

### Checking for Intent to Proceed (ITP)

**[Intent to Proceed (ITP)](/entity-models/key-concepts/itp)** is established when all borrowers (type) have signed the `INITIAL_DISCLOSURE`. To check for ITP:

1. Query `disclosuresHistory` and filter for `packageType: "INITIAL_DISCLOSURE"`
2. Check that all recipients with `type === "BORROWER"` have `signed === true`

```graphql theme={null}
query CheckITP($loanId: ID!) {
  disclosuresHistory(loanId: $loanId) {
    disclosures {
      packageType
      recipients {
        type
        signed
        displayName
      }
    }
  }
}
```

Filter the results to find the disclosure where `packageType === "INITIAL_DISCLOSURE"`, then verify that all recipients with `type === "BORROWER"` have `signed === true`.

[ITP](/entity-models/key-concepts/itp) is required before order-outs can be triggered automatically.

## Signing workflow

<Steps>
  <Step title="Retrieve disclosure">
    Query the `disclosuresHistory` query to get disclosure information and signing
    links. Filter by `packageType` to get the specific disclosure type you need.
  </Step>

  <Step title="Get signing URL">
    Extract the `recipients[].link.url` and `link.accessToken` for each
    borrower.
  </Step>

  <Step title="Direct borrower to signing">
    Open the signing URL in a browser or embedded viewer. The URL includes
    authentication via the access token.
  </Step>

  <Step title="Poll for signing status">
    Poll the `disclosuresHistory` query to check when `recipients[].signed` becomes
    `true`.
  </Step>

  <Step title="Confirm ITP">
    Once all borrowers have signed initial disclosures, [ITP](/entity-models/key-concepts/itp) is established and
    order-outs automatically trigger.
  </Step>
</Steps>

## Disclosure previews

Before disclosures are sent, you can preview them using the `disclosuresPreviews` query:

```graphql theme={null}
query PreviewDisclosures($loanId: String!) {
  disclosuresPreviews(loanId: $loanId) {
    initialDisclosure {
      documentLinks {
        id
        title
        url
      }
    }
    closingDisclosure {
      documentLinks {
        id
        title
        url
      }
    }
  }
}
```

Previews show individual document URLs (vs. a single packet URL), making it easier to display documents in your UI.

## TRID timing requirements

<Warning>
  **TRID timing is critical**: Closing disclosures must be delivered to
  borrowers at least 3 business days before closing. Monitor the loan stage and
  ensure closing disclosures are requested and delivered in time.
</Warning>

* **Initial disclosures**: Must be sent within 3 business days of selecting a loan product (or when rate is locked, whichever comes first)
* **Closing disclosures**: Must be delivered at least 3 business days before closing
* **Redisclosures**: Required when loan terms change after initial disclosure

## Best practices

1. **Poll for new disclosures** - Check for new disclosures every 30-60 minutes
2. **Notify borrowers immediately** - Alert borrowers when disclosures are available for signing
3. **Track signing status** - Poll regularly to confirm all borrowers have signed
4. **Handle expired links** - Re-query `disclosuresHistory` if signing links expire
5. **Explain importance** - Help borrowers understand that signing disclosures establishes [ITP](/entity-models/key-concepts/itp) and triggers order-outs, moving the loan forward

## Related resources

* [Order-Outs Overview](/guides/getting-started/order-outs-and-integrations/overview) - Understand when disclosures trigger order-outs
* [TRID (TILA-RESPA Integrated Disclosure)](/entity-models/key-concepts/trid) - Learn about TRID requirements
* [Tracking loan updates](/recipes/loan-updates) - Monitor disclosure status changes
* [Tasks](/entity-models/tasks) - Learn about disclosure-related tasks
