Overview
Subscribe to the Order Statuses Webhook to get real-time updates on the status of your PlanOmatic order. Think of the webhook as a push notification! Get started by getting access to our integration in PlanOintegrate: email [email protected] to request access.
Improved V3 Order ObjectWe have cut out what is no longer needed from the order object that is included in our webhooks, and added new elements that are relevant to our industry today. For example, the V3 order object now includes photo URLs! No need to use an additional API for photo asset retrieval. Photo URLs are now included in webhooks when ready.
Stay Up to Date on the Status of Your Orders
By subscribing to the webhook, your team will immediately be alerted of any issues on-site and be notified within seconds of the photos being delivered. Store these updates in your system to create transparency throughout your marketing/leasing teams. Order Statuses and Events >
Order Status Webhook Features
| Feature | V3 | V2 | Description |
|---|---|---|---|
| Receive Real Time Updates on the Status of Your Orders | ✅ | ✅ | Stay in the loop throughout the life cycle of your order with live updates. |
| Receive 3D Tours Links As Soon As They Are Available | ✅ | ✅ | Once an order's 3D tour becomes available, a link to the tour can be found in the webhook payload. Learn more. |
| Receive Photo and Floor Plan Assets As Soon As They Are Available | ✅ | Once an order's photo and floor plan assets become available, you can grab links to them in the V3 Order Status Webhook. Learn more. | |
| Receive Custom Metadata About the Order | ✅ | Receive the metadata you sent with the order request (up to 4 key/value pairs). Read more in the metadata param in the Order API. |
Important Webhook Payload Data
The webhook body is the PlanOmatic order object. The same object is returned in the data field of every Create Order and Retrieve Order response, so everything described on this page is also available on demand through Retrieve Order. The only difference is the wrapper: API responses look like { "code": 200, "status": "action-successful", "data": { ...order object... }, "message": "Order Retrieved." }, while the webhook POST body is the order object itself with no wrapper. The majority of the data has the same structure as the parameters of the Create Order API; the sections below cover the data that appears as the order progresses.
Event That Triggered the Webhook
Every time an order status webhook is sent, it is because the order experienced an event. The events indicate an order has been scheduled, or the visit could not be completed, or the requested assets have been delivered, and more. Order Statuses and Events >
"order_status": "completed",
"event": "order.delivered"Links
The links object carries the URLs that exist for the order. It is empty ({}) until delivery. A delivered order carries tour_link and tour_mls_link. The 3D tour key depends on the service ordered: a Zillow 3D Home Tour arrives as zillow_3d_link.
"links": {
"tour_link": "https://tours.planomatic.com/865496",
"tour_mls_link": "https://tours.planomatic.com/mls865496",
"zillow_3d_link": "https://www.zillow.com/view-imx/XXXXXXXXXX"
}| Key | When it appears |
|---|---|
tour_link | Every delivered order. The PlanOmatic tour page for the property. |
tour_mls_link | Every delivered order. The unbranded version of the tour page for MLS use. |
zillow_3d_link | A Zillow 3D Home Tour was ordered and published. |
matterport_3d_link, video_slideshow_link, video_walkthrough_link, home_measurements_link | Legacy products. Rarely present; none of the services in the current catalog produce them. |
Only the keys that exist for the order are present, so read them defensively. Photos are not managed through a link in the payload; your team manages photos in Connect at app.planomatic.com.
Photo and Floor Plan Links
Photo and floor plan data can be found in the photos array. It is an empty array until order.delivered. Below is a description of the photo attributes and an example.
"photos": [
{
"sequence": 1,
"caption": "Front View",
"full_size_url": "https://assets.app.planomatic.com/connect/photos/{property}/{photo}/fullSize.jpg",
"full_size_watermarked_url": "https://assets.app.planomatic.com/connect/photos/{property}/{photo}/wm/fullSize.jpg",
"uploaded_at": "2026-10-16T16:23:11.000Z",
"modified_at": "2026-10-17T17:54:46.000Z"
}
]Attributes Details
| Attribute | Description |
|---|---|
sequence | Numerical sequence of the photo based on PlanOmatic's sorting pattern. Please consult [email protected] before building any logic based on this field. |
caption | Human readable text describing the photo. Please consult [email protected] before building any logic based on this field. |
full_size_url | URL to the full sized version of the photo. Generally at the aspect ratio of (3600 x 2400). |
full_size_watermarked_url | URL to the full sized watermarked version of the photo. Generally at the aspect ratio of (3600 x 2400). null when your account has no watermark on file, and on older orders delivered before your watermark was set up. |
uploaded_at | UTC datetime when the photo was uploaded. |
modified_at | UTC datetime when the photo was last modified. |
Appointment Rescheduled
From time to time a PlanOtech will need to reschedule an appointment because of weather, sickness, or other event. When this happens, PlanOmatic will broadcast a appointment.awaiting_reschedule webhook event to subscribers. This event will contain details about the reason for the rescheduling and a new appointment. Below is an example of an appointments array upon a appointment.awaiting_reschedule event.
"appointments": [
{
"appointment_id": 3760017,
"start_datetime": "2026-10-17T09:01:00.000+00:00",
"end_datetime": "2026-10-17T10:01:00.000+00:00",
"duration_m": 60,
"planotech_name": "Bobby",
"cancelation_reason": null,
"cancelation_notes": null,
"canceled": null
},
{
"appointment_id": 3759878,
"start_datetime": "2026-10-16T12:45:00.000+00:00",
"end_datetime": "2026-10-16T13:45:00.000+00:00",
"duration_m": 60,
"planotech_name": "Bobby",
"cancelation_reason": "Weather does not allow for outdoor photography",
"cancelation_notes": "Roads too icy",
"canceled": true
}
]You can pull a lot of useful information from an order's appointments object such as the time and date of the appointment start_datetime in Coordinated Universal Time (UTC). This time is an estimate until the PlanOtech is on site.
You can also note why a reschedule has taken place by observing the canceled appointment attribute and pulling the cancelation_reason and cancelation_notes.
Attributes Details
| Attribute | Description |
|---|---|
| appointments.appointment_id | PlanOmatic appointment id (integer). |
| appointments.start_datetime | Date time of the start of the appointment. Estimate prior to the actual appointment. |
| appointments.end_datetime | Date time of the end of the appointment. |
| appointments.duration_m | Planned duration in minutes. |
| appointments.planotech_name | The PlanOtech assigned to the appointment. |
| appointments.cancelation_reason | Reason for the reschedule, as a category chosen by the PlanOtech. |
| appointments.cancelation_notes | Additional notes on cancelation from PlanOtech |
| appointments.canceled | true when this specific appointment (not the order) has been canceled; false or null otherwise. |
Unable To Complete Appointment Details
From time to time you may send an order to PlanOmatic when the property is not ready for the services requested. For example, if there is no lockbox for our photographer to use to access the property, our photographer will call our client's on-site contact and if there is no response the PlanOtech will submit the order as appointment.unable_to_complete and provide a reason and description.
"unable_to_complete_appt": true,
"unable_to_complete_appt_reason": "Non-Rently - Invalid lockbox code",
"unable_to_complete_appt_notes": "Tried the code several times, checked both doors and around the house. Texted the contact but received no response.",Attributes Details
| Attribute | Description |
|---|---|
| unable_to_complete_appt | Simple true or false value to identify if the order resulted in an appointment that was unable to complete. |
| unable_to_complete_appt_reason | Category of issue, chosen by the PlanOtech. null unless unable_to_complete_appt is true. The categories are subject to change; keep an "other" bucket. |
| unable_to_complete_appt_notes | Free-text description of the issue written by the PlanOtech. null unless unable_to_complete_appt is true. |
Order Cancelation Details
An order can be canceled by you (through the Cancel Order API or the Connect portal) or by PlanOmatic. The most common PlanOmatic-initiated reason is Out of Range: the property is outside our currently supported service areas. If your team still desires services for the property, then they may need to find another vendor to temporarily assist until PlanOmatic is able to expand into the new service area.
"order_status": "canceled",
"event": "order.canceled",
"order_cancelation_reason": "Client Requested Cancelation",
"order_cancelation_notes": "Property re-rented before the shoot.",A cancelation you request through the API comes back with the display name of the reason you sent (Duplicate Order or Client Requested Cancelation) and your cancelation_notes.
Attributes Details
| Attribute | Description |
|---|---|
| order_cancelation_reason | Category of the cancelation. null unless the order is canceled. |
| order_cancelation_notes | Additional notes: yours for an API or portal cancelation, or our team's for a PlanOmatic cancelation. |
Example Webhook Payload
Below is an example of PlanOmatic's order object. This is the structure of the order data used for webhook payloads and, inside the data wrapper, for API response payloads.
{
"order_id": 2120062,
"external_property_id": "{property_id}",
"external_order_id": "{external_order_id}",
"created": "2026-10-14T10:40:23.000Z",
"order_status": "completed",
"event": "order.delivered",
"billing_entity": {
"slug": "investment_entity_1",
"name": "Investment Entity Number 1"
},
"metadata": {
"metadata_key1": "abc123",
"metadata_key2": "wxyz987"
},
"property": {
"location": {
"address": "123 Fake St, Unit A, Denver, CO, 80205",
"street": "123 Fake St",
"street2": "Unit A",
"city": "Denver",
"state": "CO",
"postal_code": "80205",
"country": "US",
"market": "Denver",
"latitude": "39.739236",
"longitude": "-104.990251"
},
"property_type": "single_family",
"bedrooms": "3",
"bathrooms": "2.5",
"price": "3000",
"square_footage": "1500"
},
"services": [
{
"slug": "photos",
"quantity": 15,
"name": "Photography",
"price": "149.0"
},
{
"slug": "zillow_3d_and_floor_plans",
"quantity": 1,
"name": "Zillow 3D",
"price": "169.0"
},
{
"slug": "ai_virtual_staging",
"quantity": 3,
"name": "AI Virtual Staging",
"price": 0.0
}
],
"property_access": {
"vacant": true,
"method_of_access": "lockbox_code",
"access_code": "4321",
"lockbox_location": "Behind A/C Unit",
"preferred_date": null,
"onsite_contact_name": "Jane Doe",
"onsite_contact_phone": "+1 555-555-5555"
},
"order_notes": "Some special instructions",
"appointments": [
{
"appointment_id": 3760017,
"start_datetime": "2026-10-16T15:00:00.000+00:00",
"end_datetime": "2026-10-16T16:00:00.000+00:00",
"duration_m": 60,
"planotech_name": "Bobby",
"cancelation_reason": null,
"cancelation_notes": null,
"canceled": null
}
],
"order_cancelation_reason": null,
"order_cancelation_notes": null,
"unable_to_complete_appt": false,
"unable_to_complete_appt_reason": null,
"unable_to_complete_appt_notes": null,
"links": {
"tour_link": "https://tours.planomatic.com/865496",
"tour_mls_link": "https://tours.planomatic.com/mls865496",
"zillow_3d_link": "https://www.zillow.com/view-imx/XXXXXXXXXX"
},
"photos": [
{
"sequence": 1,
"caption": "Front View",
"full_size_url": "https://assets.app.planomatic.com/connect/photos/{property}/{photo-1}/fullSize.jpg",
"full_size_watermarked_url": "https://assets.app.planomatic.com/connect/photos/{property}/{photo-1}/wm/fullSize.jpg",
"uploaded_at": "2026-10-16T16:23:11.000Z",
"modified_at": "2026-10-17T17:54:46.000Z"
},
{
"sequence": 2,
"caption": "Floor Plan",
"full_size_url": "https://assets.app.planomatic.com/connect/photos/{property}/{photo-2}/fullSize.jpg",
"full_size_watermarked_url": "https://assets.app.planomatic.com/connect/photos/{property}/{photo-2}/wm/fullSize.jpg",
"uploaded_at": "2026-10-17T17:49:37.000Z",
"modified_at": "2026-10-17T17:54:45.000Z"
},
{
"sequence": 3,
"caption": "Living Room",
"full_size_url": "https://assets.app.planomatic.com/connect/photos/{property}/{photo-3}/fullSize.jpg",
"full_size_watermarked_url": "https://assets.app.planomatic.com/connect/photos/{property}/{photo-3}/wm/fullSize.jpg",
"uploaded_at": "2026-10-16T16:22:53.000Z",
"modified_at": "2026-10-17T17:49:43.000Z"
}
],
"legacy_order_id": 2120062
}Field notes:
order_idandlegacy_order_idare integers. On orders placed since 2022 they are the same number; orders before 2022 may have referenced a different id, which we call thelegacy_order_id.external_property_idandexternal_order_idare strings, exactly as you sent them.property.bedrooms,bathrooms,priceandsquare_footagecome back as strings and may be"0", empty ornull.services[].priceis the line total in US dollars: a decimal string ("149.0") for a priced line, the number0.0for a line with no separate charge,nullwhen no price is configured. Prices in the payload >metadataisnullwhen none was sent with the order.property_access.method_of_accesscomes back as the slug you sent for API orders (lockbox_code); for orders placed in the Connect portal it is the display label (Lockbox,Rently).- Order ids in requests (
order_id,request_id,previous_order_id) are accepted as a number or as a numeric string. unable_to_complete_appt_reasonandunable_to_complete_appt_notesarenullunlessunable_to_complete_apptistrue.
