Automating 360 virtual tours from your own app: a REST + webhooks workflow
If you run a listing platform, a property CRM or an archviz pipeline, clicking through a tour editor for every property doesn't scale. You want your software to push panoramas in and get a shareable tour link back. Here'
If you run a listing platform, a property CRM or an archviz pipeline, clicking through a tour editor for every property doesn't scale. You want your software to push panoramas in and get a shareable tour link back. Here's the shape of that workflow, using the Prewalk API as the concrete example (it's part of our Enterprise plan), but the pattern applies to most media-processing APIs.
The flow in one picture
- Create an empty tour.
- Upload each 360Β° panorama as a scene.
- Wait for processing (webhooks or a progress stream, not polling in a tight loop).
- Set visibility and hand the link to your user.
0. Basics that save you an afternoon
- Use the canonical host
https://prewalk.coas the base URL. Hitting a host that redirects is a classic way to lose theAuthorizationheader, because many HTTP clients drop it on cross-host redirects. - Auth is a bearer token:
Authorization: Bearer pw_.... Keys are scoped (read,write,stats), so give your uploaderwriteand your dashboardread+stats, not one key that can do everything. -
GET /api/metells you which account and scopes a key has. Call it once at startup and fail loudly if a scope is missing.
1. Create the tour
curl -X POST https://prewalk.co/api/tours \
-H "Authorization: Bearer $PREWALK_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Apartment 3B"}'
Store the returned tour ID next to your own listing ID. IDs are short lowercase strings, so they're fine as a column in your table.
2. Upload panoramas as raw bodies
Scenes go to POST /api/tours/{id}/scenes. The interesting design choice: the image is sent as the raw request body (JPG, PNG, TIFF or WebP), not multipart. File name and upload order travel in headers (see the API reference for the exact names).
Why that matters in practice:
- You can stream a file from disk or object storage straight into the request without buffering a multipart body in memory. That's noticeable with 16K panoramas.
- Retrying one failed scene is trivial, because each scene is its own request.
Before uploading, validate the one rule that breaks most tours: the image must be a full 2:1 equirectangular panorama (width exactly twice the height). Checking it client-side gives your user a clear error instead of a confusing processing failure.
3. Don't poll: use webhooks
Processing a large panorama takes a moment (tiling, a preview, then the sharp version, plus a VR version for stereo images). Instead of polling, register a webhook and react to events such as:
-
scene.ready: the scene can be viewed. -
scene.sharpened: full resolution is available. -
scene.vr_ready: the headset version is ready. -
scene.failed: show the error to the user.
Each delivery is signed with HMAC-SHA256, so verify the signature before trusting the payload, and make your handler idempotent: deliveries are retried for a few hours if your endpoint is down, which means you can receive the same event twice.
If you'd rather watch progress live (for an upload progress bar, say), there's also a server-sent events stream per tour.
4. Publish
Visibility is a single call: POST /api/content/{id}/visibility with link, public or password. Link-only is a good default for listings that shouldn't appear in public galleries.
5. Respect the rate limit
The limit is 600 requests per minute per key. When you exceed it you get a 429 with a Retry-After header in seconds. Honour that header instead of exponential guessing, and you'll never need to think about it again.
6. Close the loop with stats
With a stats-scoped key you can pull views per tour or project (or a CSV export) back into your CRM, so agents see which listings people actually walk through.
The full endpoint list, headers and a curl walkthrough are in the Prewalk API reference. If you're building something similar for panoramas or other heavy media, I'm curious how you handle uploads and retries; drop it in the comments.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes β full credit and traffic to the original publisher.