How to Log Photographed Maintenance Issues from a Mailhook into MongoDB
Receive a tenant email with photos of a maintenance problem at a Spojit Mailhook address, fetch each image, build a small preview thumbnail, and store the request in MongoDB with the property, the tenant reply-to, and the attachment details for the property manager.
What This Integration Does
Property managers get maintenance reports by email all day: a leaking tap, a cracked tile, a broken gate, usually with a couple of phone photos attached. Those emails are easy to lose in an inbox and slow to triage. This workflow turns every inbound report into a structured record. A tenant (or a forwarding rule on your shared maintenance mailbox) sends mail to a dedicated Spojit Mailhook address; Spojit reads the subject and body, pulls the photo bytes, and writes a single maintenance request document to MongoDB that your team can sort, assign, and report on.
The run model is push based. The Mailhook trigger fires within seconds of an email arriving, with no mailbox or sign-in to manage. The body and the list of attachment references are exposed as {{ input }}. An Attachment node in Multiple mode fetches the image bytes, a Loop walks each photo so the image connector can build a thumbnail preview, a Transform node assembles the final document, and the mongodb connector inserts it. Each email is deduplicated, so a re-sent message will not create a duplicate request, and every run leaves exactly one new document behind. There is no reply to the sender unless you add a Send Email step yourself.
Prerequisites
- A MongoDB connection added in Spojit under Connections, with write access to the database and collection you will use (for example database
property_ops, collectionmaintenance_requests). - A new workflow with the Mailhook trigger selected and an address generated (see Setting Up a Mailhook Trigger).
- A way to get tenant emails to that address: share it with tenants directly, or add a forwarding rule on your existing maintenance mailbox.
- The built-in image and json connectors, which need no connection or sign-in.
- Decide your photo limits up front: the Attachment node defaults allow 10 MB per attachment and 25 MB per run.
Step 1: Receive the tenant email with a Mailhook trigger
Open your workflow and set the Trigger node type to Mailhook. Optionally set an Address prefix such as maint (1 to 24 characters) so the generated address is recognisable, then choose Generate email address to get an address like maint-3f9a2c1b7e4d6058@mailhook.spojit.com. Copy it and give it to tenants or point a forwarding rule at it.
To keep noise out, add an optional From allowlist (only addresses or domains you expect) and an optional Subject regex so only genuine maintenance reports start a run. After the trigger fires, the email is available as {{ input }} with fields including {{ input.from }}, {{ input.subject }}, {{ input.text }}, {{ input.replyTo }}, {{ input.receivedAt }}, and {{ input.attachments }}. Each entry in attachments is a reference of the shape { id, filename, contentType }; the actual photo bytes are fetched in the next step.
Step 2: Fetch the photo bytes with an Attachment node
Add an Attachment node directly after the trigger. The designer only allows this node when the workflow uses a Mailhook trigger, which this one does. Set Mode to Multiple so it returns every matching photo as a list. To restrict it to images, set the Content type filter to image/* and, if your tenants tend to attach signatures or logos, narrow the Filename pattern (for example *.jpg, *.jpeg, *.png, *.heic). Leave Fail if no attachment matches off so a text-only report still records a request.
In Multiple mode the node outputs an object you can name photos, shaped like this:
{
"attachments": [
{ "filename": "leak.jpg", "contentType": "image/jpeg", "size": 184213, "content": "/9j/4AAQSkZJRg..." }
],
"count": 1,
"totalBytes": 184213
}
The content field on each attachment is the base64 image data you will pass to the image connector in the loop. Reference the list downstream as {{ photos.attachments }}.
Step 3: Loop over each photo and build a thumbnail preview
Add a Loop node in ForEach mode iterating {{ photos.attachments }}. Inside the loop body, the current photo is available as the loop item (for example {{ attachment }}), so you can read {{ attachment.filename }}, {{ attachment.contentType }}, {{ attachment.size }}, and {{ attachment.content }}.
Inside the loop, add a Connector node in Direct mode on the image connector and pick the thumbnail tool. Map its inputs: set image to {{ attachment.content }}, set size to the longest edge you want (for example 150 pixels), and set format to jpeg. Direct mode runs the tool deterministically with no AI cost. The tool returns:
{
"width": 150,
"height": 113,
"format": "jpeg",
"image": "/9j/4AAQSkZJRgABAQ..."
}
The image field is the base64 thumbnail. Use the Loop output to collect, per photo, a small record of filename, original size, thumbnail dimensions, and the thumbnail data so the next step can attach it to the request document.
Step 4: Assemble the maintenance request with a Transform node
After the loop, add a Transform node to shape the data into one clean document. Pull the email metadata from the trigger and the per-photo details from the loop output. Aim for an object like this:
{
"property": "{{ input.subject }}",
"reportedBy": "{{ input.from }}",
"replyTo": "{{ input.replyTo }}",
"description": "{{ input.text }}",
"receivedAt": "{{ input.receivedAt }}",
"status": "new",
"photoCount": "{{ photos.count }}",
"photos": "{{ loopOutput }}"
}
If you prefer to manipulate the structure with explicit tools rather than templating, add a Connector node in Direct mode on the json connector: use set to add fields such as status onto the object, pick to keep only the fields you want to store, or merge to combine the email metadata with the photo records. Keep the photo entries to the thumbnail and metadata so the document stays small and quick to read in your dashboards.
Step 5: Store the request in MongoDB
Add a Connector node in Direct mode on the mongodb connector and choose the insert-documents tool. Select your MongoDB connection, set the database (for example property_ops) and collection (for example maintenance_requests), and map the document built in the previous step into the documents input:
{
"database": "property_ops",
"collection": "maintenance_requests",
"documents": [
{
"property": "Unit 4B - leaking kitchen tap",
"reportedBy": "tenant@example.com",
"replyTo": "tenant@example.com",
"description": "The tap under the sink drips constantly...",
"receivedAt": "2026-06-21T09:14:00.000Z",
"status": "new",
"photoCount": 2,
"photos": [
{ "filename": "leak.jpg", "size": 184213, "thumbWidth": 150, "thumbHeight": 113, "thumbnail": "/9j/4AAQ..." }
]
}
]
}
On success, insert-documents returns the inserted id (or ids), which you can keep as a reference number for the property manager. The collection now holds one queryable record per reported issue, ready for triage boards, status filters, and reporting.
Step 6: Notify the property manager (optional)
To alert the team without leaving the workflow, add a Send Email node after the insert. Set Recipients to your maintenance distribution address, set a templated Subject such as New maintenance request: {{ input.subject }}, and write a short Body that includes the reporter, the description, and the photo count. Set Reply-To to {{ input.replyTo }} so a manager can reply straight to the tenant. Remember that recipients must be on your org allowlist under Settings, General, Email recipients, and that Send Email counts toward your monthly email allowance. If you would rather send from your own domain, use the resend or smtp connector instead. You can also ask Miraxa, the intelligent layer across your automation, to scaffold this step for you, then fine tune it in the properties panel.
Tips
- Use the
Subject regexfilter on the Mailhook so internal forwards or out-of-office replies do not create empty requests, and use theFrom allowlistif only known tenants should be able to file reports. - Keep thumbnails small (a
sizeof 150 to 320) so documents stay light; if you also need the full-resolution photo, store it separately rather than inline in the same document. - Phone photos can be large. The Attachment node defaults cap each attachment at 10 MB and each run at 25 MB, so set a
Max sizethat matches your storage budget and let oversized files be skipped. - Add a
statusfield ofnewon insert so a separate triage workflow or your dashboard can move requests throughassignedandclosedlater.
Common Pitfalls
- Storing raw image base64 in the document instead of a thumbnail bloats the collection fast. Run each photo through the
thumbnailtool in the Loop and store the small version. - Forgetting that
{{ input.attachments }}holds references, not bytes. You must add the Attachment node to fetchcontentbefore the image connector can read it. - Heavy formats such as HEIC may not preview the way you expect. Narrow the
Filename patternor convert with the image connector first if your tenants send unusual formats. - Received emails are retained for 30 days, so the Attachment bytes are only fetchable within that window. Run and confirm the workflow before relying on it for older reports.
Testing
Before sharing the address widely, send one test email to your Mailhook address with two small photos attached and a clear subject like Unit 4B - leaking tap. Open the workflow run in execution history and check each step: the Mailhook output shows the right {{ input.subject }} and two attachment references, the Attachment node reports count: 2, the Loop runs twice, and each thumbnail call returns image data. Then query MongoDB (for example with the find-documents tool or your database client) for the new document in maintenance_requests and confirm the property, reply-to, photo count, and thumbnails look correct. Once one request lands cleanly, point the real forwarding rule at the address.