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

# Complete integration guide

> Build a complete mortgage origination integration with Pylon. This guide walks you through creating loans, managing borrowers, running underwriting, pricing products, and moving applications through to funding.

## Overview

This guide covers the complete loan origination flow from initial application through closing. By the end, you'll have a working integration that can:

* Create and manage loan applications
* Add borrowers and collect financial information
* Run credit checks and pre-approval
* Get real-time pricing
* Submit to underwriting
* Lock interest rates
* Allow borrowers to fulfill tasks and conditions to close their loan

**Estimated time:** 60-90 minutes

**Prerequisites:**

* API credentials (see [GraphQL authentication](/playground/authentication) in Reference)
* Access token
* Basic familiarity with GraphQL
* Familiarity with the entities and relational model. For detailed information about each entity, see the [Entity Relationships](/entity-models/erd) page and the [GraphQL API Reference](https://pylon.mortgage/documentation/graphql/index.html).

## Authentication

All API requests require authentication via OAuth Bearer token. See the [GraphQL authentication](/playground/authentication) guide in Reference for details on obtaining and using access tokens.

## Integration steps

<Steps>
  <Step title="Create a deal">
    Every loan belongs to a Deal. The Deal represents the customer relationship and can contain multiple loans over time.

    **Why this matters:** Even if you're only creating one loan, Pylon requires a Deal as the top-level container. This structure supports scenarios like multiple loan applications, refinancing, or family members applying together.

    **To create a deal:** Use the [`deal` mutation](https://sandbox.pylon.mortgage/documentation/graphql/index.html#mutation-deal) with [`CreateDealInput`](https://sandbox.pylon.mortgage/documentation/graphql/index.html#definition-CreateDealInput) to see all required fields.

    <Note>
      Save the `deal.id` - you'll use it for all subsequent operations.
    </Note>
  </Step>

  <Step title="Create the loan">
    Create the loan application itself. This establishes the basic loan parameters: purchase price, loan purpose, and closing date.

    **Why this matters:** The loan object is the central entity. It needs basic information upfront because many downstream operations (pricing, underwriting) depend on these fundamentals.

    **To create a loan:** Use the [`loan` mutation](https://sandbox.pylon.mortgage/documentation/graphql/index.html#mutation-loan) with [`CreateLoanInput`](https://sandbox.pylon.mortgage/documentation/graphql/index.html#definition-CreateLoanInput) to see all required fields.

    <Note>
      Save the `loan.id` - this is your primary identifier for all loan operations.
    </Note>
  </Step>

  <Step title="Add borrowers">
    Borrowers are the people applying for the loan. Most loans have 1-2 borrowers (primary and co-borrower).

    **Why this matters:** Borrowers provide the financial profile (income, assets, credit) that determines eligibility. You need at least one borrower before you can run credit checks or pricing.

    **To create a borrower:** Use the [`borrower` mutation](https://sandbox.pylon.mortgage/documentation/graphql/index.html#mutation-borrower) with [`CreateBorrowerInput`](https://sandbox.pylon.mortgage/documentation/graphql/index.html#definition-CreateBorrowerInput) to see all required fields.

    <Note>
      Save the `borrower.id` for credit pulls and income/asset associations.
    </Note>

    **Set Borrower Consent**

    Before pulling credit, you must obtain and record the borrower's consent. This is a legal requirement for credit checks. Federal law (FCRA) requires explicit consent before pulling credit reports. Pylon enforces this by requiring consent dates before credit operations.

    **To set borrower consent:** Use the [`borrower` mutation](https://sandbox.pylon.mortgage/documentation/graphql/index.html#mutation-borrower) with [`BorrowerPersonalInformationInput`](https://sandbox.pylon.mortgage/documentation/graphql/index.html#definition-BorrowerPersonalInformationInput) to see the consent fields.
  </Step>

  <Step title="Configure the subject property">
    The subject property is the home being purchased or refinanced. Pylon automatically creates a subject property when you create a loan, but you need to populate its details.

    **Why this matters:** Property details (location, type, taxes) affect loan eligibility, pricing, and underwriting requirements. Some loan products are only available for certain property types or locations.

    **To configure the subject property:** Use the [`subjectProperty` mutation](https://sandbox.pylon.mortgage/documentation/graphql/index.html#mutation-subjectProperty) with [`AttachSubjectPropertyInput`](https://sandbox.pylon.mortgage/documentation/graphql/index.html#definition-AttachSubjectPropertyInput) to see all required fields.
  </Step>

  <Step title="Add assets">
    Assets verify the borrower's ability to make the down payment and cover closing costs.

    **Why this matters:** Lenders need to verify "source of funds" for the down payment. Large deposits require documentation, and assets must be "seasoned" (held for a period) to count toward qualification.

    **To add assets:** Use the [`asset` mutation](https://sandbox.pylon.mortgage/documentation/graphql/index.html#mutation-asset) with [`CreateAssetInput`](https://sandbox.pylon.mortgage/documentation/graphql/index.html#definition-CreateAssetInput) to see all required fields.

    <Tip>
      Add assets early. The system uses them for pre-approval calculations and down payment verification.
    </Tip>
  </Step>

  <Step title="Add income">
    Income determines the borrower's [debt-to-income (DTI)](/entity-models/key-concepts/dti) ratio, which is a key underwriting metric.

    **Why this matters:** Lenders calculate [DTI](/entity-models/key-concepts/dti) as monthly debt payments divided by monthly income. Most conventional loans require [DTI](/entity-models/key-concepts/dti) below 43-45%. Income documentation is critical for underwriting.

    **To add income:** Use the [`income` mutation](https://sandbox.pylon.mortgage/documentation/graphql/index.html#mutation-income) with [`CreateIncomeInput`](https://sandbox.pylon.mortgage/documentation/graphql/index.html#definition-CreateIncomeInput) to see all required fields.

    <Tip>
      Add all income sources. The system will calculate total qualifying income and [DTI](/entity-models/key-concepts/dti) automatically.
    </Tip>
  </Step>

  <Step title="Pull credit">
    Credit reports are required for underwriting. Pylon integrates with credit bureaus to pull reports automatically.

    **Why this matters:** Credit scores and credit history are primary factors in loan approval and pricing. Higher scores mean better rates. Credit pulls are asynchronous jobs that take 30-60 seconds.

    See the [Credit guide](/guides/getting-started/order-outs-and-integrations/credit) for implementation details.

    <Tip>
      In production, poll for completion. Once completed, the credit data is automatically associated with the borrower and used for pricing and underwriting.
    </Tip>
  </Step>

  <Step title="Update borrower declarations">
    Borrowers must answer financial and property declarations. These are required disclosures about bankruptcy, foreclosures, lawsuits, and other financial matters.

    **Why this matters:** These declarations are required by federal regulations (Fannie Mae/Freddie Mac guidelines). They help underwriters identify risk factors and determine loan eligibility.

    **To update borrower declarations:** Use the [`borrower` mutation](https://sandbox.pylon.mortgage/documentation/graphql/index.html#mutation-borrower) with [`BorrowerFinancialDeclarationsInput`](https://sandbox.pylon.mortgage/documentation/graphql/index.html#definition-BorrowerFinancialDeclarationsInput) and [`BorrowerPropertyDeclarationsInput`](https://sandbox.pylon.mortgage/documentation/graphql/index.html#definition-BorrowerPropertyDeclarationsInput) to see all declaration fields.

    <Tip>
      Collect these declarations early. They're quick to answer but required for underwriting.
    </Tip>
  </Step>

  <Step title="Assign a loan officer">
    Loan officers are licensed professionals who manage the loan. Assigning a loan officer ensures accurate disclosures, proper email communications, and correct assignment in the Command Center.

    **Why this matters:** Loan officers have state licenses and must be assigned to loans in their licensed states. They also receive notifications and manage the loan through underwriting.

    **To assign a loan officer:** Use the [`loan` mutation](https://sandbox.pylon.mortgage/documentation/graphql/index.html#mutation-loan) with [`AssignLoanOfficerInput`](https://sandbox.pylon.mortgage/documentation/graphql/index.html#definition-AssignLoanOfficerInput) to see the required fields. Query [`organizationUsers`](https://sandbox.pylon.mortgage/documentation/graphql/index.html#query-organizationUsers) to get available loan officers.

    <Tip>
      Assign a loan officer before pricing. Some disclosure requirements depend on the assigned officer's license state.
    </Tip>
  </Step>

  <Step title="Run pre-approval">
    Pre-approval determines the maximum loan amount and purchase price the borrower qualifies for, and generates a pre-approval letter.

    **Why this matters:** Pre-approval gives borrowers confidence and helps them shop for homes within their budget. It's also required before many real estate agents will show properties.

    See the [Prequalification](/recipes/prequalification) guide for implementation details.

    <Tip>
      Run pre-approval early in the flow. It's a quick eligibility check that helps identify issues before you invest time in full underwriting.
    </Tip>
  </Step>

  <Step title="Get pricing">
    Pricing returns available loan products (e.g., "30-year fixed", "15-year ARM") with interest rates, [APRs](/entity-models/key-concepts/apr), and monthly payments.

    **Why this matters:** Borrowers need to see rates and payments to make decisions. Pricing is calculated based on credit score, loan amount, property type, and other factors.

    See the [Pricing scenarios](/recipes/scenarios) guide for implementation details.

    <Tip>
      Present multiple rate/point options to borrowers. They can choose lower rates (with points) or higher rates (no points) based on their situation.
    </Tip>
  </Step>

  <Step title="Attach a product structure">
    Once the borrower selects a rate, attach the product structure to the loan. This locks in the pricing and triggers [TRID (TILA-RESPA Integrated Disclosure)](/entity-models/key-concepts/trid) requirements.

    **Why this matters:** [TRID](/entity-models/key-concepts/trid) is a federal regulation that requires lenders to provide specific disclosures within 3 business days of selecting a loan product. Attaching the product structure starts this clock and generates the initial Loan Estimate.

    **To attach a product structure:** Use the [`loan` mutation](https://sandbox.pylon.mortgage/documentation/graphql/index.html#mutation-loan) with [`AttachProductPricingRateInput`](https://sandbox.pylon.mortgage/documentation/graphql/index.html#definition-AttachProductPricingRateInput) to see all required fields.

    <Warning>
      Once you attach a product structure:

      1. [TRID](/entity-models/key-concepts/trid) disclosure clock starts (3 business days)
      2. Disclosures will automatically fire after 3 days OR when you lock the rate (whichever comes first)
      3. You cannot change the product without creating a new loan application

      Only attach a product structure when the borrower has committed to a rate.
    </Warning>
  </Step>

  <Step title="Run AUS (automated underwriting system)">
    AUS runs the loan through automated underwriting engines (Fannie Mae Desktop Underwriter or Freddie Mac Loan Product Advisor) to get an initial underwriting decision.

    **Why this matters:** AUS provides a preliminary approval decision and identifies conditions (documents or requirements) needed for final approval. Most loans require AUS approval before human underwriting.

    AUS runs are handled automatically by Pylon. Use the [`aus` mutation](https://sandbox.pylon.mortgage/documentation/graphql/index.html#mutation-aus) to trigger an AUS run, and query the [`aus` query](https://sandbox.pylon.mortgage/documentation/graphql/index.html#query-aus) to check AUS status.

    <Tip>
      Wait for AUS to complete before locking the rate. Some products require AUS approval before rate lock.
    </Tip>
  </Step>

  <Step title="Preview initial disclosures">
    Before locking the rate, preview the initial disclosures. This shows you exactly what documents will be sent to the borrower.

    **Why this matters:** It's helpful to review disclosures before they're automatically sent. This preview provides individual document URLs (vs. a single packet URL), making it easier to display documents in your UI.

    Query [`disclosuresPreviews`](https://sandbox.pylon.mortgage/documentation/graphql/index.html#query-disclosuresPreviews) to preview initial disclosures before they're sent.

    <Tip>
      Preview disclosures to ensure everything looks correct. Once disclosures fire, you cannot undo them (though you can send corrected versions if needed).
    </Tip>
  </Step>

  <Step title="Lock the rate">
    Rate lock commits the borrower to the selected interest rate for a specified period (typically 30-60 days).

    **Why this matters:** Interest rates fluctuate daily. Locking protects the borrower from rate increases before closing. Once locked, the rate is guaranteed (assuming loan closes within lock period).

    **To lock the rate:** Use the [`loan` mutation](https://sandbox.pylon.mortgage/documentation/graphql/index.html#mutation-loan) with [`ConfirmRateLockInput`](https://sandbox.pylon.mortgage/documentation/graphql/index.html#definition-ConfirmRateLockInput) to see all required fields.

    **What happens when you lock:**

    1. Rate is locked for the specified term (typically 30-60 days)
    2. Initial disclosures are automatically sent (if not already sent)
    3. Loan moves to "Processing" stage
    4. Borrower tasks are created (document collection)

    **Product and rate changes after lock:**

    * **You cannot change the product** - Changing the product would require creating a new loan application, as it would result in new underwriting against that product's pricing and guidelines
    * **You can change the rate within that product** - After rate lock, borrowers can still change the rate within the same product, with pricing anchored around the lock date

    <Warning>
      You can lock immediately after attaching product structure, OR disclosures will auto-fire 3 business days after attaching product structure (whichever comes first). Once locked, you cannot change the product without creating a new loan application.
    </Warning>

    <Tip>
      Lock the rate as soon as the borrower commits. Rate locks have expiration dates - make sure you can close before the lock expires.
    </Tip>
  </Step>

  <Step title="Sign initial disclosures (trigger ITP)">
    After rate lock, initial disclosures are automatically sent. All borrowers must sign these disclosures to establish [Intent to Proceed (ITP)](/entity-models/key-concepts/itp), which triggers all third-party services.

    **Why this matters:** [ITP](/entity-models/key-concepts/itp) is a regulatory requirement that indicates borrower commitment. Once [ITP](/entity-models/key-concepts/itp) is established, Pylon automatically orders appraisals, title services, income verification, and other third-party services. Getting [ITP](/entity-models/key-concepts/itp) early is critical for quick closes.

    **To get disclosure signing links:** Query [`disclosuresHistory`](https://sandbox.pylon.mortgage/documentation/graphql/index.html#query-disclosuresHistory) and filter by `packageType: "INITIAL_DISCLOSURE"` to get `disclosures[].recipients[].link` (signing URLs with `accessToken`).

    **To display and sign disclosures:** Use the disclosure URL and access token together by submitting the access token via an HTML POST request into an iframe. See the [Disclosures entity guide](/entity-models/disclosures#using-signing-links) for a complete React component implementation (`PdfSsoViewer`) that handles this automatically.

    **To check [ITP](/entity-models/key-concepts/itp) status:** Query `disclosuresHistory` and filter by `packageType: "INITIAL_DISCLOSURE"`, then check `disclosures[].recipients[].signed` - [ITP](/entity-models/key-concepts/itp) is established when ALL recipients have `signed: true`. See the [Disclosures](/entity-models/disclosures) guide for implementation details.

    <Warning>
      **Quick closes require early [ITP](/entity-models/key-concepts/itp)**: The faster borrowers sign initial disclosures, the sooner order-outs begin. Delays in getting [ITP](/entity-models/key-concepts/itp) directly impact closing timelines.
    </Warning>
  </Step>

  <Step title="Monitor and complete borrower tasks">
    After rate lock, the loan moves to "Processing" stage and borrower tasks are automatically created. These tasks require borrowers to upload documents like pay stubs, bank statements, tax returns, and other verification documents.

    **Why this matters:** Underwriting requires documentation to verify the information provided in the application. Tasks guide borrowers through exactly what documents are needed and when. The loan cannot progress to underwriting until required documents are collected.

    **To retrieve borrower tasks:** Query the [`borrower` query](https://sandbox.pylon.mortgage/documentation/graphql/index.html#query-borrower) and access the `borrowerTasks` field to see all tasks assigned to each borrower. Each task includes the task type, status, description, and due date.

    **To upload documents:** Use the REST endpoint `POST /api/loan-applications/{loanId}/borrower-tasks/{taskId}/documents` with multipart/form-data (form field: `borrower-task-files`). See the [Tasks entity guide](/entity-models/tasks) for implementation details.

    <Tip>
      Poll for borrower tasks regularly (every 15-30 minutes) to detect new tasks as they're created. Tasks are created automatically as the loan progresses through processing and underwriting.
    </Tip>
  </Step>

  <Step title="Submit loan to underwriting">
    Once all required documents are collected, manually submit the loan to underwriting to begin the detailed review process.

    **Why this matters:** Submitting to underwriting starts the detailed evaluation by human underwriters. You can include notes for the underwriter to highlight important information or special circumstances.

    **To submit to underwriting:** Use the [`loan` mutation](https://sandbox.pylon.mortgage/documentation/graphql/index.html#mutation-loan) with [`SubmitUnderwritingNotesInput`](https://sandbox.pylon.mortgage/documentation/graphql/index.html#definition-SubmitUnderwritingNotesInput) to submit the loan. This moves the loan from "Processing" to "Underwriting" stage.

    <Tip>
      Include helpful notes when submitting to underwriting. Notes can highlight borrower strengths, explain special circumstances, or provide context that helps underwriters make decisions faster.
    </Tip>
  </Step>

  <Step title="Poll for loan stage updates">
    As documents are collected and underwriting progresses, the loan moves through stages: Processing → Underwriting → Conditionally Approved → Final Review → Pre-CTC QC → Clear to Close.

    **Why this matters:** Loan stages indicate where the loan is in the process. You need to track stage changes to know when to take action (e.g., request closing documents when the loan reaches "Clear to Close"). Conditional approval means the loan is approved pending conditions.

    **To track loan stages:** Query the [`loan` query](https://sandbox.pylon.mortgage/documentation/graphql/index.html#query-loan) and monitor the `currentStage` field. The `stages` field provides a complete history of stage transitions with timestamps. Continue completing any new borrower tasks that appear during underwriting. See the [Tracking loan updates](/recipes/loan-updates) guide for polling implementation.

    <Info>
      **Common stages after rate lock:** Processing (document collection), Underwriting (detailed evaluation), Conditionally Approved (pending conditions), Final Review (final check), Pre-CTC QC (quality control), Clear to Close (ready for closing), Docs Out (closing documents sent), Funding (loan funded), Closed (loan closed).
    </Info>
  </Step>

  <Step title="Monitor order-out status">
    After [ITP](/entity-models/key-concepts/itp) is established, Pylon automatically orders third-party services. Track appraisal and title order status to communicate progress to borrowers.

    **Why this matters:** Borrowers want updates on appraisal inspections and title progress. These are critical path items that can delay closing. Appraisal inspections require borrower coordination for property access.

    **To track appraisal status:** Query `loan.appraisal(id: $appraisalId) { orderStatus, scheduledAt, inspectionCompletedAt, receivedAt, actualValueAmount }` (or `loan.appraisalOrders` to list all orders) to monitor appraisal progress. See the [Appraisal guide](/guides/getting-started/order-outs-and-integrations/appraisal) for status values and implementation details.

    **To track title status:** Query `loan.title { ... }` to monitor title order status and progress.

    <Tip>
      Poll order-out status every 2-4 hours (more frequently for full appraisals with inspections). Notify borrowers when inspections are scheduled so they can arrange property access.
    </Tip>
  </Step>

  <Step title="Handle underwriting conditions">
    During underwriting, conditions may be issued that require additional documentation or clarification. These appear as borrower tasks or loan officer tasks.

    **Why this matters:** Conditions must be satisfied before the loan can be approved. Common conditions include explanations for credit inquiries, verification of employment, or clarification of income sources. Timely resolution prevents closing delays.

    **To check for conditions:** Query each borrower's `borrowerTasks[]` and the loan's `loanOfficerDocumentTasks[]` fields. Conditions appear as tasks with specific requirements. Upload required documents via the same document upload endpoint. Continue polling until all conditions are cleared.

    <Tip>
      Monitor tasks regularly to identify conditions early. Some conditions have deadlines, and timely resolution prevents delays in closing.
    </Tip>
  </Step>

  <Step title="Sign redisclosures (if needed)">
    If loan terms change after initial disclosure, redisclosures are issued. Borrowers must sign these updated disclosures before closing.

    **Why this matters:** [TRID](/entity-models/key-concepts/trid) requires redisclosures when loan terms change after initial disclosure. Borrowers must sign before closing. Common scenarios that trigger redisclosures include rate changes, loan amount adjustments, or closing date changes.

    **To get redisclosure signing links:** Query [`disclosuresHistory`](https://sandbox.pylon.mortgage/documentation/graphql/index.html#query-disclosuresHistory) and filter by `packageType: "REDISCLOSURE"`. If a redisclosure exists, get signing links from `disclosures[].recipients[].link`.

    **To check signing status:** Query `disclosuresHistory` and filter by `packageType: "REDISCLOSURE"`, then check `disclosures[].recipients[].signed` - all borrowers must sign. See the [Disclosures](/entity-models/disclosures) guide for implementation details.

    <Tip>
      Monitor for redisclosures after any loan term changes. Borrowers must sign redisclosures before the loan can proceed to closing.
    </Tip>
  </Step>

  <Step title="Sign closing disclosures">
    Once the loan reaches "Clear to Close" stage, closing disclosures are available. Borrowers must sign these disclosures at least 3 business days before closing.

    **Why this matters:** [TRID](/entity-models/key-concepts/trid) requires closing disclosures be delivered to borrowers at least 3 business days before closing. Borrowers must sign before the loan can fund. This is a hard regulatory requirement.

    **To get closing disclosure signing links:** Query [`disclosuresHistory`](https://sandbox.pylon.mortgage/documentation/graphql/index.html#query-disclosuresHistory) and filter by `packageType: "CLOSING_DISCLOSURE"` to get `disclosures[].recipients[].link` (signing URLs with `accessToken`).

    **To check signing status:** Query `disclosuresHistory` and filter by `packageType: "CLOSING_DISCLOSURE"`, then check `disclosures[].recipients[].signed` - all borrowers must sign. See the [Disclosures](/entity-models/disclosures) guide for implementation details.

    <Warning>
      **[TRID](/entity-models/key-concepts/trid) timing is critical**: Closing disclosures must be delivered at least 3 business days before closing. Monitor the loan stage and ensure closing disclosures are requested and delivered in time.
    </Warning>
  </Step>

  <Step title="Monitor Clear to Close and funding">
    After closing disclosures are signed, the loan moves through final stages: Clear to Close → Docs Out → Funding → Closed.

    **Why this matters:** Clear to Close means the loan is approved and ready to close. Funding is when the loan amount is disbursed. After "Closed", the loan origination process is complete and the loan moves to servicing.

    **To monitor final stages:** Continue polling `loan.currentStage` to track: Clear to Close → Docs Out → Funding → Closed. Query [`disclosuresHistory`](https://sandbox.pylon.mortgage/documentation/graphql/index.html#query-disclosuresHistory) to see the complete disclosure timeline.

    **To confirm the loan actually funded:** Query the loan's `fundedDate`. It is `null` until the loan is disbursed and is populated with the calendar date on which the loan was funded once the wire is sent, so a non-null `fundedDate` is the authoritative signal that funding has completed. This is distinct from `closingDate`, which is the closing date on the loan file that documents are executed against. On refinances, `fundedDate` is always three business days after `closingDate` because of the right-of-rescission waiting period; on purchases, the two usually match unless funding authorization comes in late enough that the wire has to go out the next business day.

    <Tip>
      Once the loan reaches "Closed" stage, the loan origination process is complete. You can archive the loan or continue monitoring for post-closing activities like document recording and loan purchase.
    </Tip>
  </Step>
</Steps>

## Error handling

Throughout the integration, you'll encounter errors. Here's how to handle common ones:

| Error                    | Cause                                                           | Solution                                                                                           |
| ------------------------ | --------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| `MissingDataError`       | Required data is missing (e.g., borrower SSN, property address) | Query the loan/borrower to identify missing fields, then update with required data                 |
| `MissingCreditPullError` | Attempting to price or run AUS before credit is pulled          | Ensure credit pull job has completed successfully before proceeding                                |
| `ExpiredCreditPullError` | Credit pull is older than 120 days                              | Pull a new credit report                                                                           |
| `NoProductPricingError`  | No loan products available for this borrower/loan combination   | Check loan parameters, verify credit score meets minimums, contact Pylon support if issue persists |

Always check for `userErrors` in mutation responses. See individual entity documentation for response structures.

<Tip>
  Log all errors for debugging, display user-friendly error messages to
  borrowers, and implement retry logic for transient errors (network issues,
  timeouts).
</Tip>

## Best practices

**Create objects in order** - Follow the step sequence above for best results. Each step builds on the previous one.

**Update vs Create** - Use `create` mutations for new entities and `update` mutations to modify existing entities. Only non-null fields in update inputs are changed.

**Handle asynchronous operations** - Credit pulls and AUS runs are asynchronous. Poll for completion.

**Validate data before API calls** - Validate required fields and formats before making API calls to avoid errors.

**Store IDs** - Always save the IDs returned from create mutations. You'll need them for updates and associations.

**Handle partial updates** - Update mutations only change fields you provide. Omit fields you don't want to change.

## Production checklist

Before going live, ensure you've implemented:

* Authentication with production API credentials
* Error handling for all API calls
* Idempotent operations (retry-safe mutations)
* Logging for all API requests/responses
* Rate limiting and backoff strategies
* Data validation before API calls
* Secure storage of borrower PII
* Compliance with data retention policies
* Monitoring for API errors and stuck loans

## Next steps

Now that you understand the end-to-end flow:

1. **Explore the GraphQL Schema** - Use GraphQL introspection to discover all available queries and mutations
2. **Build Error Recovery** - Implement retry logic and error recovery workflows
3. **Optimize Performance** - Batch operations where possible
4. **Test Edge Cases** - Test with different loan types, borrower profiles, and error scenarios
5. **Monitor Production** - Set up alerts for API errors and stuck loans
