Skip to main content

How to build a short-term rental permit tracker for city governments with AI-flagged compliance risk in Formaloo

Learn how to build a short-term rental permit tracker in Formaloo, with a public host application form, an AI field that flags compliance risk from uploaded documents, status-driven emails, a permit PDF, and a host portal for status and renewal.

What you'll build

A public application form where short-term rental hosts submit property details, safety documentation, and complaint history. An AI field reads every submission the moment it lands and privately flags compliance risk before a reviewer opens the file. Logic routes high-risk and incomplete applications back for more information, sends the right email at every stage, and generates an official permit PDF the moment a permit is approved. Hosts track their status and renewal date from their own portal page, while your Code Compliance team works from a Kanban pipeline, an assigned-to-me queue, and a set of filtered views built around the workflow's real edge cases.

When to use this

  • Your city processes short-term rental permit applications by hand, and reviewers re-check documentation and complaint history on every single one before they can even start the review

  • You want every application flagged for risk the moment it arrives, so obviously incomplete or previously flagged properties don't sit in the same queue as clean, low-risk ones

  • Hosts have no way to check their own permit status, renewal date, or what's missing without calling or emailing your office

  • Permits need a renewal cycle, and nothing currently reminds a host before their inspection or renewal date arrives

  • Inspectors and compliance officers need their own queue of assigned properties instead of one shared, unsorted table

Part 1 — Build the host application form with Magic Create

This is the form every host fills out to apply for a permit. It captures property details, the four required documents, and complaint history, all in one submission, so nothing arrives split across emails and attachments.

1. Open Magic Create: From your Formaloo homepage, click Magic Create. Describe the form in plain text, or upload your existing paper permit application and let Magic Create convert it into an online form.

2. Write the Magic Create prompt: Describe every field you need, including which ones should be admin-only. For example: "Build a short-term rental permit application with the property address, the host's full name, email, and phone number, the rental type (entire home, private room, or shared room), maximum occupancy, a yes/no question asking about prior code violations or complaints with a text field to describe them if yes, four file uploads for proof of ownership or lease authorization, a fire safety inspection certificate, a floor plan showing exits and sleeping areas, and proof of liability insurance, and a yes/no consent question asking whether the host agrees to allow a compliance inspection if requested. Add these as admin-only fields: a permit status dropdown (Submitted, Under review, Needs more info, Inspection scheduled, Approved, Rejected, Renewal due), an assignee field for the reviewer, an internal notes field, and an inspection date field."

3. Click Create: Magic Create generates the form and its fields in seconds.

Your form should look like this. Magic Create will have generated most of it. Verify each field and field ID matches exactly.

📖 Want to know exactly what the Assignee field can do? What is the Assignee field in Formaloo and how to use it is the atomic reference.

Field (question form)

Field ID

Type

What's the property address for this permit application?

property_address

Short text

What's the property owner or host's full name?

host_name

Short text

What's the best email to reach you at?

host_email

Email

What's the best phone number to reach you at?

host_phone

Short text

What type of short-term rental is this?

rental_type

Dropdown

What's the maximum occupancy you're requesting for this permit?

max_occupancy

Number

Has this property had any prior code violations or neighbor complaints?

prior_complaints

Yes/No

Briefly describe the prior violation or complaint

prior_complaint_details

Long text

Upload proof of property ownership or lease authorization

ownership_proof_upload

File

Upload your current fire safety inspection certificate

fire_safety_cert_upload

File

Upload a floor plan showing exits and sleeping areas

floor_plan_upload

File

Upload proof of active liability insurance

insurance_upload

File

Do you agree to allow a compliance inspection if one is requested?

inspection_consent

Yes/No

Compliance inspection required notice

inspection_warning_notice

Meta (static content)

Permit status

permit_status

Dropdown (admin-only)

Assignee

permit_assignee

Assignee (admin-only)

Internal notes

internal_notes

Long text (admin-only)

Inspection date

inspection_date

Date (admin-only)

The four document uploads are left as optional at the field level on purpose. That's what lets an incomplete submission still come through, so the logic in Part 3 can catch it and route it back to the host, instead of the form silently blocking submission.

📖 Want internal fields, status, assignee, notes, invisible to the person filling out the form? What are admin-only fields and how they help is the foundational guide to that mechanism.

Part 2 — Add the AI field that flags compliance risk

This is the part that does your reviewers' first pass for them. An AI Analysis field reads the four uploaded documents plus the complaint history the moment a permit is submitted, and writes a private risk read that only your team ever sees. The applicant never sees it, and there's no setting that changes that.

1. Add an AI Analysis field: In your form editor, add a field and select AI Analysis. It's admin-only by default and hidden from respondents automatically.

2. Write the prompt: Open the field's prompt box and use the @ menu to pipe in the relevant fields, including the four uploads directly. For example: "Read @ownership_proof_upload, @fire_safety_cert_upload, @floor_plan_upload, and @insurance_upload, along with @prior_complaints and @prior_complaint_details. Assess this short-term rental permit application's compliance risk as High, Medium, or Low, and explain your reasoning in one short paragraph, noting any missing documents or prior complaint history."

3. Name the field clearly: Title it "Compliance risk flag." This becomes the column header in your Responses table.

Once a host submits, Formaloo creates the response row and runs the AI Analysis field automatically, reading the uploaded files directly rather than just their filenames. While it generates, the cell shows a loading state, then displays the risk level and reasoning as plain text you can sort, filter, and export like any other field.

Note: the AI Analysis field is available on the Business and Enterprise plans only.

📖 See the full mechanics, including how it reads uploaded files, in how to generate AI analysis and let it update your form's own records.

Part 3 — Route permits with Magic Logic

Now that every submission arrives with a compliance risk read attached, use logic to reveal the right follow-up fields, flag high-risk and incomplete applications, and fire the right email at every stage, including a reminder before renewal.

1. Open Advanced logic: In the form's settings panel on the right side, open Advanced logic and go to the Logic tab.

2. Click Magic Logic: In the top right corner of the Logic panel, click Magic Logic.

3. Describe your visibility rules: Paste a prompt like: "Hide 'Briefly describe the prior violation or complaint' by default, and show it only when 'Has this property had any prior code violations or neighbor complaints?' is Yes, and require it in that case. Hide 'Compliance inspection required notice' by default, and show it only when 'Do you agree to allow a compliance inspection if one is requested?' is No."

4. Describe your submission and routing rules: In the same prompt, add: "On submit, set Permit status to Submitted, then run the compliance risk AI field. If Compliance risk flag is High, set Permit status to Needs more info and send an internal notification email to the assigned reviewer. If any of the four document uploads is empty, set Permit status to Needs more info and send the missing-documents email to the host. When Permit status changes to Approved, send the approval email to the host with the permit PDF attached. When Permit status changes to Rejected, send the rejection email to the host."

5. Describe the renewal reminder: Add: "Thirty days before the date in Inspection date, set Permit status to Renewal due and send the renewal reminder email to the host." Magic Logic can generate this kind of scheduled follow-up directly from a plain-language wait period, tied to the date field rather than a fixed calendar date.

6. Click Generate Rules: Review the AI-generated rules, approve, redo, or cancel each one.

7. Save your logic: Once you're satisfied, click Save in the top right corner.

📖 First time opening this panel? How to add advanced logic to your form walks through it in more detail.

Part 4 — Build the email templates

Hosts need to hear from you at every stage, when their application lands, when something's missing, when a decision is made, and before their permit needs renewing, without your team writing each one by hand.

1. Open the template library: Click your profile icon, go to Apps and Integrations, and open Custom Email Templates.

2. Create the submission confirmation: Click Add New Template, name it "Permit application received," and write a short confirmation using answer piping to pull in @host_name and @property_address.

3. Create the missing-documents notice: Add a second template, "Additional documentation needed," explaining plainly which document types are still required and linking back to the host's portal page.

4. Create the internal high-risk alert: Add a third template addressed to your reviewer, referencing @property_address and @host_name, letting them know a submission needs a closer look before deciding next steps.

5. Create the approval notice: Add a fourth template, "Permit approved," referencing @property_address, and note in the template that the generated PDF (built in Part 5) will be attached automatically.

6. Create the rejection notice: Add a fifth template, "Permit application update," referencing @property_address and, where useful, @internal_notes for the reviewer's reasoning.

7. Create the renewal reminder: Add a sixth template, "Permit renewal reminder," referencing @property_address and linking hosts back to their portal to start the renewal process.

📖 For a full walkthrough on writing and saving templates, see how to create and send custom email templates.

Part 5 — Build the PDF permit template

Once a permit is approved, hosts need an actual document, the official permit with their reference number, property details, and renewal date, not just an email saying so.

1. Open the PDF template library: Click your profile icon, go to Apps and Integrations, and open Custom PDF Template.

2. Add a new template: Click Add New Template, name it "Short-term rental permit," and switch the editor to the HTML view.

3. Paste the HTML below: This is the exact template used for this workflow. Property and host information sit in a two-column grid, current status and the renewal date are called out clearly, and a compliance acknowledgement sits in a soft inset block referencing the inspection consent the host agreed to. For the reference number, @rowid is piped directly into the header text as STR-@rowid, there's no separate form field for it.

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Short-term rental permit</title> <style>   @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');    * { box-sizing: border-box; margin: 0; padding: 0; }    body {     font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;     background: #ffffff;     color: #1a1a1a;   }    .page {     max-width: 760px;     margin: 0 auto;     padding: 60px 72px;   }    .header {     display: flex;     justify-content: space-between;     align-items: flex-start;     border-bottom: 1px solid #eeeeee;     padding-bottom: 24px;     margin-bottom: 36px;   }    .header-title {     font-size: 24px;     font-weight: 700;     color: #1a1a1a;   }    .header-sub {     margin-top: 6px;     font-size: 13px;     color: #999999;   }    .header-meta {     text-align: right;     font-size: 13px;     color: #999999;   }    .header-meta .ref {     font-size: 15px;     font-weight: 600;     color: #1a1a1a;     margin-bottom: 4px;   }    .section {     margin-bottom: 32px;   }    .section-title {     font-size: 10px;     font-weight: 600;     text-transform: uppercase;     letter-spacing: 1.5px;     color: #aaaaaa;     margin-bottom: 14px;   }    .field-grid {     display: grid;     grid-template-columns: 1fr 1fr;     gap: 0 32px;   }    .field {     padding-bottom: 12px;     margin-bottom: 12px;     border-bottom: 1px solid #f0f0f0;   }    .field.full {     grid-column: 1 / -1;   }    .field-label {     font-size: 11px;     color: #999999;     margin-bottom: 4px;   }    .field-value {     font-size: 14px;     color: #1a1a1a;     font-weight: 500;   }    .status-badge {     display: inline-block;     padding: 4px 12px;     border-radius: 999px;     font-size: 12px;     font-weight: 600;     background: #eef6ee;     color: #2e7d32;   }    .inset {     background: #f9f9f9;     border-left: 3px solid #dddddd;     padding: 18px 20px;     font-size: 13px;     line-height: 1.6;     color: #555555;     border-radius: 0 6px 6px 0;   }    .footer {     margin-top: 48px;     padding-top: 20px;     border-top: 1px solid #eeeeee;     display: flex;     justify-content: space-between;     font-size: 11px;     color: #999999;   } </style> </head> <body>   <div class="page">      <div class="header">       <div>         <div class="header-title">Short-term rental permit</div>         <div class="header-sub">City code compliance office</div>       </div>       <div class="header-meta">         <div class="ref">STR-@rowid</div>         <div>Submitted @created_at</div>       </div>     </div>      <div class="section">       <div class="section-title">Property information</div>       <div class="field-grid">         <div class="field full">           <div class="field-label">Property address</div>           <div class="field-value">@property_address</div>         </div>         <div class="field">           <div class="field-label">Rental type</div>           <div class="field-value">@rental_type</div>         </div>         <div class="field">           <div class="field-label">Maximum occupancy</div>           <div class="field-value">@max_occupancy</div>         </div>       </div>     </div>      <div class="section">       <div class="section-title">Host information</div>       <div class="field-grid">         <div class="field">           <div class="field-label">Host name</div>           <div class="field-value">@host_name</div>         </div>         <div class="field">           <div class="field-label">Email</div>           <div class="field-value">@host_email</div>         </div>         <div class="field">           <div class="field-label">Phone</div>           <div class="field-value">@host_phone</div>         </div>       </div>     </div>      <div class="section">       <div class="section-title">Permit status</div>       <div class="field-grid">         <div class="field">           <div class="field-label">Current status</div>           <div class="field-value"><span class="status-badge">@permit_status</span></div>         </div>         <div class="field">           <div class="field-label">Next inspection / renewal due</div>           <div class="field-value">@inspection_date</div>         </div>       </div>     </div>      <div class="section">       <div class="section-title">Compliance acknowledgement</div>       <div class="inset">         The host has agreed to allow a compliance inspection if one is requested by the code compliance office. This permit certifies status as of the submission date above and is subject to review, suspension, or revocation if the property is found in violation of local short-term rental regulations, occupancy limits, or safety requirements.       </div>     </div>      <div class="footer">       <div>Confidential, for permit holder and city records only</div>       <div>[City / department name]</div>     </div>    </div> </body> </html>

4. Save your template: Give it a clear name and click Save.

The internal-only fields, Compliance risk flag, Internal notes, and Assignee, are deliberately left out of this document. Only the host-facing status and dates appear, since this PDF goes straight into the host's inbox.

Part 6 — Set up the host portal and internal review pages

Hosts need somewhere to check status and renewal dates without calling your office, your Code Compliance team needs a pipeline and their own assigned queue, and city leadership needs a read on volume and risk without opening individual records.

1. Activate the portal: From your project, click the Settings icon and select Activate Portal. Open Manage User and run the Setup Wizard to connect a user directory, this is what lets hosts sign up and log in without needing a Formaloo account.

2. Add a Host role: In the User Directory, open Edit Profile Fields, scroll to User Roles, and add a "Host" role.

3. Build the host pages: Add "Apply for a permit" as the application form page, "My permit status" as a table filtered to the host's own records, and "Renewal due" as a table filtered to Permit status = Renewal due, also scoped to the host's own records. For both tables, switch to Edit Mode on the data block, click Options, scroll to Manage Access, select the profile field matching the host's email, and toggle on Allow users to view only their data.

4. Restrict the host pages to the Host role: On each page, click the three-dot menu next to the page name, select Access, and set external user access to the Host role you created.

5. Set up your Code Compliance team internally: Your reviewers and inspectors are internal users, not portal roles. Make sure they're workspace members, grouped into a team, for example "Code Compliance."

6. Build the "All applications" page: Add a Kanban board grouped by Permit status. Restrict it to your Code Compliance team via the page's Access menu → Internal users → Specific teams.

7. Build "My assigned permits": Switch to Edit Mode on that page's data block, click Options, scroll to Manage Access, select the Assignee field, and toggle on Allow assignees to view only their data.

8. Build the remaining filtered internal views: Add "High risk queue" filtered to Compliance risk flag = High, "Needs more info" filtered to Permit status = Needs more info and sorted oldest first, "Inspections scheduled" filtered to Permit status = Inspection scheduled and sorted by Inspection date, and "Renewals due soon" filtered to Permit status = Renewal due and sorted the same way. Restrict all four to the Code Compliance team the same way as step 6.

9. Build the Leadership reports group: Add "Permits by status," a bar chart grouped by Permit status, and "Risk distribution," a bar chart grouped by Compliance risk flag. Restrict both to internal leadership users, with full visibility across all properties.

Project pages spec

Group

Page

Access level

Data block

Filter

Sort

Form?

Host

Apply for a permit

External users, Host role

Form

None

N/A

Yes

Host

My permit status

Host, view own records only

Table

None (scoped by view-own-data)

Newest first

No

Host

Renewal due

Host, view own records only

Table

Permit status = Renewal due

Newest first

No

Code compliance

All applications

Internal users, Code Compliance team

Kanban

None

Grouped by Permit status

No

Code compliance

My assigned permits

Internal, assignee = logged-in user

Kanban

Assignee = logged-in user

Newest first

No

Code compliance

High risk queue

Internal, Code Compliance team

Table

Compliance risk flag = High

Newest first

No

Code compliance

Needs more info

Internal, Code Compliance team

Table

Permit status = Needs more info

Oldest first

No

Code compliance

Inspections scheduled

Internal, Code Compliance team

Table

Permit status = Inspection scheduled

Inspection date, soonest first

No

Code compliance

Renewals due soon

Internal, Code Compliance team

Table

Permit status = Renewal due

Inspection date, soonest first

No

Leadership reports

Permits by status

Internal leadership, full visibility

Chart (bar)

None

Grouped by Permit status

No

Leadership reports

Risk distribution

Internal leadership, full visibility

Chart (bar)

None

Grouped by Compliance risk flag

No

📖 First time building a portal? How to create a portal and manage users' access covers the full setup.

What you now have

A public application form that captures every document a permit review needs upfront, with an AI field quietly reading those documents and flagging compliance risk before a reviewer opens the file. High-risk applications and incomplete submissions route back automatically, so nothing sits unnoticed in a shared inbox. Hosts get a self-service portal for status and renewal, an actual PDF permit lands in their inbox the moment they're approved, and a reminder goes out on its own before the next renewal is due. Your Code Compliance team works from a pipeline, an assigned-to-me queue, and a handful of filtered views built around the workflow's real edge cases, while leadership gets a volume and risk read without opening a single record.

What's next

Ready-to-use templates

Don't build this from scratch, we've already put it together for you.

Learn more how-tos

Did this answer your question?