Skip to main content

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 ready event
  • 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"
}
]
}'
note

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.

URL requirements

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)
Local development with ngrok

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:

HeaderValue
Content-Typeapplication/json
User-AgentiGUIDE-Event-Dispatcher
X-Plntr-Eventready

The JSON payload contains everything you need to display or distribute the iGUIDE:

FieldDescription
typeAlways "ready"
iguideIdImmutable iGUIDE identifier
defaultViewIdID of the default view
workOrderIdID of the work order that completed
iguideAliasURL-friendly alias for the iGUIDE
authtokenAccess token for private media (valid 3 weeks)
urlsPublic, embedded, unbranded and management URLs plus downloadable media
propertyFull address and geo-coordinates
summaryFloor area, room dimensions and measurement data
billingInfoPackage type, add-ons and billable area
bannerAgent 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
}
}
tip

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 returnsWhat happens
2xxEvent delivered successfully. No retries.
5xxRetried up to 5 times, spaced 10–15 minutes apart.
4xx, 3xx or timeoutEvent dropped immediately. No retries.
Return 5xx when you want 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: