> ## 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.

# Loan change requests

> Ask Pylon to change a loan's terms after disclosure, with supporting documents

Once a loan has been disclosed it is **frozen**: its terms can no longer be edited directly through the API. To change the terms of a frozen loan, open a **change request** describing what should change. Pylon's disclosure desk reviews the request and, if the change is a [change of circumstance](/entity-models/key-concepts/change-of-circumstance), issues a revised Loan Estimate.

Change requests are also accepted on loans that are not yet frozen.

## Open a change request

<Note>
  [`openChangeRequest`](https://sandbox.pylon.mortgage/documentation/graphql/index.html#definition-OpenLoanChangeRequestInput) lives in the `loan` mutation namespace and is authorized with the `update:loan` scope.
</Note>

```graphql theme={null}
mutation OpenChangeRequest($input: OpenLoanChangeRequestInput!) {
  loan {
    openChangeRequest(input: $input) {
      id
      status
      plainTicketId
    }
  }
}
```

```json theme={null}
{
  "input": {
    "loanId": "loan_abc123",
    "description": "Borrower is increasing the down payment to $120,000; please reduce the loan amount to $480,000.",
    "attachmentDocumentIds": ["document_def456"]
  }
}
```

| Field                   | Required | Description                                                                                            |
| ----------------------- | -------- | ------------------------------------------------------------------------------------------------------ |
| `loanId`                | Yes      | The loan's Pylon ID or friendly loan ID.                                                               |
| `description`           | Yes      | What should change and why. This is what the disclosure desk reads, so be specific.                    |
| `attachmentDocumentIds` | No       | Up to 20 document IDs to send along with the request. See [Attaching documents](#attaching-documents). |

Response:

```json theme={null}
{
  "data": {
    "loan": {
      "openChangeRequest": {
        "id": "lcr_2fXk9QpLmN4vR7tYw3Zb8c",
        "status": "PENDING",
        "plainTicketId": "th_01M1Q4EE3HF9P27J8PF84PQZ80"
      }
    }
  }
}
```

| Field           | Description                                                                                                                                                                                                    |
| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`            | The change request's ID (`lcr_…`). It appears in the loan's `changeRequests` list.                                                                                                                             |
| `status`        | Pylon's internal review status. Always `PENDING` on creation. See [Tracking a change request](#tracking-a-change-request) — this is **not** the field to poll for progress.                                    |
| `plainTicketId` | The ID (`th_…`) of the support ticket Pylon filed for this request, when support-ticket filing is enabled for your organization; `null` otherwise. **Save this** — it is how you track the request's progress. |

<Warning>
  Every change request is worked by the disclosure desk as a support ticket. Open it with `openChangeRequest`, not with `support.createSupportTicket` under a change-of-circumstance issue type: `openChangeRequest` files the same ticket *and* records the change request on the loan, so both `id` and `plainTicketId` point at the same request. Filing a ticket directly creates a ticket with no change request behind it.
</Warning>

## Attaching documents

Use `attachmentDocumentIds` to hand the disclosure desk the evidence behind the change (an updated purchase contract, an appraisal, a rate lock confirmation). Each ID can be either:

* A document already on the loan, as returned by the loan's `documents` field or by a [document upload](/guides/getting-started/documents).
* A support document uploaded for this loan: call `support.requestSupportDocumentUpload` (experimental; requires the `create:support-ticket` and `use:experimental-api` scopes), POST the file to the returned `uploadUrl` as `multipart/form-data` with a single `files` field, and use the `documentId` from the upload response.

Attachments are forwarded to the support ticket; they are not stored on the change request itself. If support-ticket filing is not enabled for your organization, attachments are ignored.

## When ticket filing fails

For organizations with support-ticket filing enabled, the ticket is filed **before** the change request is saved. If the ticket cannot be filed, the mutation fails and nothing is recorded:

```json theme={null}
{
  "errors": [
    {
      "message": "Your change request could not be submitted because the required support follow-up could not be recorded. Please contact your account manager.",
      "extensions": {
        "code": "INTERNAL_SERVER_ERROR",
        "errorId": "error_xyz789",
        "errorDetails": {
          "code": "CHANGE_REQUEST_TICKET_FAILED"
        }
      }
    }
  ]
}
```

Check `extensions.errorDetails.code` for `CHANGE_REQUEST_TICKET_FAILED` and treat it as transient: retry with the same input after a short delay. Because nothing was saved, retrying does not create duplicates. If it keeps failing, contact your account manager and quote the `errorId`. See [Error handling](/playground/error-handling) for the general error format.

## Tracking a change request

The disclosure desk works the request in its support ticket, so the ticket is where progress is reflected. Read it with the [Support API](/guides/getting-started/support-tickets#reading-tickets-back), passing the `plainTicketId` returned by `openChangeRequest`:

```graphql theme={null}
query ChangeRequestTicket($ticketId: ID!) {
  support {
    supportTicket(id: $ticketId) {
      id
      reference
      status
      title
    }
  }
}
```

```json theme={null}
{ "ticketId": "th_01M1Q4EE3HF9P27J8PF84PQZ80" }
```

<Note>
  `supportTicket` requires the `read:support-ticket` and `use:experimental-api` scopes. Pass the ticket ID (`th_…`), not the change request ID (`lcr_…`) — the two are different objects and `supportTicket` does not accept a change request ID.
</Note>

`status` is the ticket's state in the support platform, for example open, snoozed or done. Use `supportTicketMessages` to read the desk's replies, and watch for the loan's terms and disclosures to update once the change is applied. See [Tracking loan updates](/recipes/loan-updates).

If you did not keep the ticket ID, `supportTicketsForLoan` lists every ticket on the loan; change-request tickets carry "Change of Circumstance" in their `title`.

### The change request record

The change request itself is also returned on the loan:

```graphql theme={null}
query {
  loan(id: "loan_abc123") {
    changeRequests {
      id
      status
    }
  }
}
```

Its `status` is Pylon's internal review marker and is not updated as the desk works the ticket — in practice it stays `PENDING`. Do not poll it to track progress; use the ticket status above. If `plainTicketId` was `null` when you opened the request (ticket filing not enabled for your organization), there is no ticket to poll and progress is only visible through changes to the loan itself.
