Offer-to-Tenancy Conversion
Offer-to-Tenancy Conversion
When an offer reaches accepted status, you can convert it directly into a new tenancy without re-entering data. The system pre-populates a 5-stage wizard with all available information from the offer — applicant details, property info, landlord contacts, and the offered rent.
Prerequisites
- The offer must have a status of
accepted. The "Create Tenancy" option is not available for offers in any other status.
Starting the Conversion
- Open the offer detail page for an accepted offer.
- Click the "Create Tenancy" button in the page header (displayed in green).
- The conversion wizard opens as a modal overlay, pre-loaded with offer data.
Note: An info banner on the offer detail page also indicates when an offer is ready for tenancy conversion.
Wizard Stages
Stage 1 — Property
Confirms the property linked to the offer. Displayed details include:
- Full address
- Property type, number of bedrooms and bathrooms
- Furnished / unfurnished status
- Linked landlord information (if available)
If no property was associated with the original offer, a property search input is shown instead. Type an address, city, or postcode to find and select the correct property before proceeding.
Stage 2 — Occupants
Pre-fills contact details for the main tenant and landlord:
- Tenant name and email — from the lead applicant on the offer
- Landlord name and email — from the property's linked landlord record
All fields are editable. The following lead applicant information is shown as read-only context (it is not directly submitted but auto-populates the tenancy notes):
- Phone number
- Employment status and employer name
- Annual income
- Pets and smoking status
- Adverse credit history
Joint applicants and guarantors linked to the offer are also listed here for reference.
Stage 3 — Term
Configures the tenancy term:
| Field | Default | Notes |
|---|---|---|
| Start date | Today | Editable |
| Contract length | 12 months | Editable |
| Monthly rent | Offered rent | Pre-filled; editable |
| Notes | Auto-generated | Includes employment, income, and joint applicant context from the offer |
The computed end date updates in real time as you adjust the start date or contract length.
Stage 4 — Review
Displays a summary of all fields before submission, with a reference back to the source offer. Review all details and use the Back button to make corrections if needed.
Stage 5 — Complete
Shown after successful tenancy creation. The page auto-redirects to the new tenancy overview within 2 seconds. You can also navigate manually if needed.
What Happens in the Background
- Calling the wizard triggers the
offer.offerToTenancyDraftquery, which reads the accepted offer and assembles a pre-populated draft. An audit event (offer.tenancy_draft_generated) is recorded at this point. - On submission, the existing
tenancy.createLegacymutation creates the tenancy record. - The offer detail view and the tenancy list are both refreshed automatically on success.
- No new database tables are created — all data is sourced from the existing
offers,applicants,guarantors,properties, andlandlordstables.
Error Handling
- If the offer is not in
acceptedstatus, theofferToTenancyDraftquery returns an error and the wizard displays a descriptive message. - If offer data cannot be loaded for any other reason, an error screen is shown with the specific error message and a Close button.
API Reference
offer.offerToTenancyDraft
Type: Query
Input:
{
offerId: string; // Must belong to the current organisation
}
Returns:
{
propertyId: string | null;
propertyAddress: string | null;
branchId: string | null;
property: {
propertyType: string | null;
bedrooms: number | null;
bathrooms: number | null;
furnished: boolean;
} | null;
tenantName: string | null; // Lead applicant full name
tenantEmail: string | null; // Lead applicant email
landlordName: string | null; // From linked landlord record
landlordEmail: string | null;
monthlyRent: string | null; // Offered rent
leadApplicant: {
employmentStatus: string | null;
employerName: string | null;
annualIncome: number | null;
phone: string | null;
pets: boolean | null;
smoking: boolean | null;
adverseCredit: boolean | null;
// ...additional enrichment fields
} | null;
jointApplicants: Array<{
firstName: string;
lastName: string;
// ...full applicant details
}>;
guarantors: Array<{
// ...guarantor details linked to applicants
}>;
}
Errors:
- Throws if the offer does not have
status === "accepted" - Throws if the offer does not belong to the current organisation