Tutorial: Listen for webhooks
Get notified the moment an iGUIDE is ready instead of polling—register a webhook and handle the ready event in your application.
Time to complete: ~15 minutes
What you'll learn:
- How to register a webhook when creating an iGUIDE
- How to build a receiver that handles the
readyevent - What the event payload contains and how to use it
- How retry delivery works
Prerequisites
Before you begin, you need:
- App ID and Access Token (see Your First iGUIDE for setup)
- A publicly accessible URL to receive events (or a tunneling tool like ngrok for local development)
- Node.js 18+ or Python 3.8+ (for the receiver examples)
Why webhooks?
In the Your First iGUIDE tutorial, you polled the work order status endpoint in a loop to check when processing finished. Polling works, but has drawbacks:
- You make requests even when nothing has changed
- There's a gap between the iGUIDE becoming ready and your next poll
- Aggressive polling wastes resources and can hit rate limits
Webhooks flip this around—instead of asking "is it ready yet?", the Portal API tells you when the iGUIDE is ready by sending an HTTP POST to a URL you provide.
Step 1: Set up your environment
Store your credentials as environment variables:
export APP_ID="your-app-id-here"
export APP_TOKEN="your-access-token-here"
Step 2: Register a webhook
You register webhooks when creating an iGUIDE by including a webhooks array in the request body. Each webhook needs an event type and a destination url:
curl -X POST https://manage.youriguide.com/api/v1/iguides \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-H "X-Plntr-App-Id: $APP_ID" \
-H "X-Plntr-App-Token: $APP_TOKEN" \
-d '{
"type": "standard",
"industry": "residential",
"address": {
"country": "CA",
"provinceState": "ON",
"city": "Kitchener",
"postalCode": "N2G 2L3",
"streetName": "King St E",
"streetNumber": "301"
},
"webhooks": [
{
"event": "*",
"url": "https://your-server.com/webhooks/iguide"
}
]
}'
Set event to "*" (wildcard) to subscribe to all event types. Currently the only event type is ready, but the wildcard ensures you receive future event types as they're added.
Your webhook URL must include a scheme (https:// or http://) and a valid hostname. URLs are limited to 512 characters.
Step 3: Build a receiver
Your webhook receiver is a standard HTTP endpoint that accepts POST requests. When an iGUIDE finishes processing, the Portal API delivers the event payload as JSON.
Node.js (Express):
import express from "express";
const app = express();
app.use(express.json());
app.post("/webhooks/iguide", (req, res) => {
const event = req.body;
if (event.type === "ready") {
console.log(`iGUIDE ready: ${event.iguideId}`);
console.log(`Public URL: ${event.urls.publicUrl}`);
console.log(`Embed URL: ${event.urls.embeddedUrl}`);
// Store the authtoken — you need it to access private media
console.log(`Auth token (valid 3 weeks): ${event.authtoken}`);
}
// Return 2xx to acknowledge receipt
res.sendStatus(200);
});
app.listen(3000, () => {
console.log("Webhook receiver listening on port 3000");
});
Python (Flask):
from flask import Flask, request
app = Flask(__name__)
@app.route("/webhooks/iguide", methods=["POST"])
def handle_webhook():
event = request.get_json()
if event["type"] == "ready":
print(f"iGUIDE ready: {event['iguideId']}")
print(f"Public URL: {event['urls']['publicUrl']}")
print(f"Embed URL: {event['urls']['embeddedUrl']}")
# Store the authtoken — you need it to access private media
print(f"Auth token (valid 3 weeks): {event['authtoken']}")
# Return 2xx to acknowledge receipt
return "", 200
if __name__ == "__main__":
app.run(port=3000)
To test webhooks locally, use a tunneling service to expose your local server:
ngrok http 3000
Use the generated https:// URL as your webhook URL when creating the iGUIDE.
Step 4: Understand the ready event
When your iGUIDE finishes processing, the Portal API sends a ready event via HTTP POST to your webhook URL. The request arrives with these headers:
| Header | Value |
|---|---|
Content-Type | application/json |
User-Agent | iGUIDE-Event-Dispatcher |
X-Plntr-Event | ready |
The JSON payload contains everything you need to display or distribute the iGUIDE:
| Field | Description |
|---|---|
type | Always "ready" |
iguideId | Immutable iGUIDE identifier |
defaultViewId | ID of the default view |
workOrderId | ID of the work order that completed |
iguideAlias | URL-friendly alias for the iGUIDE |
authtoken | Access token for private media (valid 3 weeks) |
urls | Public, embedded, unbranded and management URLs plus downloadable media |
property | Full address and geo-coordinates |
summary | Floor area, room dimensions and measurement data |
billingInfo | Package type, add-ons and billable area |
banner | Agent branding info (included only if the view has a banner) |
Here's a trimmed example of what the payload looks like:
{
"type": "ready",
"iguideId": "igYGFV5GG6V8DD1",
"defaultViewId": "vXY7P2R1A",
"iguideAlias": "api-docs-sample",
"workOrderId": "WO1234",
"authtoken": "<view-access-token>",
"urls": {
"publicUrl": "https://youriguide.com/api-docs-sample/",
"embeddedUrl": "https://youriguide.com/embed/api-docs-sample/",
"unbrandedUrl": "https://unbranded.youriguide.com/api-docs-sample/",
"manageUrl": "https://manage.youriguide.com/iguides/edit/igYGFV5GG6V8DD1",
"mediaUrls": {
"en": {
"pdfMetric": "https://youriguide.com/api-docs-sample/doc/floorplan_metric_en.pdf",
"pdfImperial": "https://youriguide.com/api-docs-sample/doc/floorplan_imperial_en.pdf",
"galleryFrontImage": "https://youriguide.com/api-docs-sample/doc/front.image",
"galleryZip": "https://youriguide.com/api-docs-sample/doc/gallery.zip"
}
}
},
"property": {
"fullAddress": "301-301 King St E, Kitchener, ON",
"country": "CA",
"stateProvince": "ON",
"city": "Kitchener",
"postalCode": "N2G 2L3",
"streetName": "King St E",
"streetNumber": "301"
},
"billingInfo": {
"iguideType": "standard",
"package": "",
"addons": ["vr", "advmeas"],
"billableAreaSqFeet": 4214.89,
"billableAreaSqMeters": 391.58
}
}
The ready event can fire more than once for the same iGUIDE—for example, if a draft error required re-processing. Make your receiver handle duplicate events gracefully (e.g. use iguideId + workOrderId as a deduplication key).
For the complete payload schema with every nested field, see the Webhooks reference.
Step 5: Access private media URLs
Some media URLs in the payload—gallery ZIPs, floor plan JPGs, SVGs, DXFs—are private and require the authtoken from the event. Append it as a query parameter:
https://youriguide.com/api-docs-sample/doc/gallery.zip?accessToken=<authtoken>
Public URLs (PDF floor plans, front image, embed preview) work without a token.
For the full list of which URLs are public vs private, see Webhooks — Media URL object.
Step 6: Test your receiver
You can test your webhook receiver without waiting for an iGUIDE to process. Download the sample event payload and send it with curl:
# Download the sample ready event
curl -fsL 'https://docs.youriguide.com/samples/iguide-ready-event.json' \
-o iguide-ready-event.json
# Send it to your local webhook receiver
curl -X POST \
-H 'Content-Type: application/json' \
-H 'X-Plntr-Event: ready' \
-H 'User-Agent: iGUIDE-Event-Dispatcher' \
-d @iguide-ready-event.json \
"http://localhost:3000/webhooks/iguide"
If your receiver is working correctly, you'll see the iGUIDE details logged to your console.
Retry behavior
If your server can't process the event, the Portal API retries delivery based on the HTTP status code you return:
| Your server returns | What happens |
|---|---|
| 2xx | Event delivered successfully. No retries. |
| 5xx | Retried up to 5 times, spaced 10–15 minutes apart. |
| 4xx, 3xx or timeout | Event dropped immediately. No retries. |
If your server is temporarily unavailable, return a 500 or 503 status. A 4xx response tells the dispatcher your server intentionally rejected the event, and delivery won't be retried.
After five failed attempts (all 5xx), the event is dropped permanently. There's no event log or replay mechanism—if your server misses an event, fall back to polling the work order status endpoint to recover.
Next steps
You now know how to replace polling with webhooks to get real-time notifications when iGUIDEs are ready. Here's what to explore next:
- Webhooks reference — Full payload schemas, media URL details and access token usage
- Upload photos and assets — Add gallery photos and supplementary files to your iGUIDE
- iGUIDE Lifecycle — Understand the full lifecycle from creation to publication
- Work Order Lifecycle — All work order statuses and transitions