Integrate ArchWalk 360 into your product
Provision Experiences from your own data, open ArchWalk Creator inside your app, and reuse the published Viewer across matching listings.
RECOMMENDED PLUGIN FLOW
- Import your inventory
- Map visual variants
- Create-or-resolve Experiences
- Edit 360 in Embedded Creator
- Publish
- Reuse the Viewer on matching rooms
29. Partner plugin integration
Recommended path: Partner API for provisioning/sync + ArchWalk Embedded Creator for authoring + ArchWalk Viewer for guests.
ArchWalk does not persist resorts, physical rooms, or room types. Those remain partner-owned. ArchWalk stays generic: Integration → Experience → Panoramas → navigation / optional Floor Plan → publication → Viewer.
Architecture
Partner data/import
↓
Partner backend
↓
ArchWalk Partner API — provisioning
↓
Experience mapping
Partner user
↓
Partner UI
↓
Embedded Creator
Guest
↓
Partner room/listing
↓
ArchWalk Viewer
200 rooms is not 200 Experiences
Example: Blue Lagoon Resort imports 200 physical rooms. Those collapse into 8 visually distinct room types. The partner create-or-resolves 8 ArchWalk Experiences — not 200.
| Partner-owned | ArchWalk-owned |
|---|---|
| Physical rooms 101–… | One Experience per visual variant |
| Room type / layout / renovation variant | Panoramas, navigation, Floor Plan, publication |
| Mapping of rooms → variant | Stable experience_id / public_id |
Create another Experience when the representation is materially different (layout, accessible configuration, renovation, attached spaces, or another difference that would make reuse misleading).
Identity
Example mapping for Deluxe King at Blue Lagoon:
{
"name": "Deluxe King",
"external_customer_id": "blue_lagoon",
"external_resource_id": "room_type_deluxe_king"
}
external_customer_id is optional. Use it when you already namespace resources by customer or resort. If external_resource_id is globally stable inside the Integration, omit external_customer_id.
Store returned experience_id, public_id, and publication_status in your own system if useful. The external identity remains sufficient to re-resolve safely.
Provisioning (create-or-resolve)
Call POST /api/v1/360/experiences once per distinct visual variant, not once per physical room.
const variants = groupImportedRoomsByVisualVariant(rooms);
for (const variant of variants) {
const experience = await archwalk.createOrResolveExperience({
name: variant.name,
external_customer_id: resort.id,
external_resource_id: variant.stableId,
});
saveMapping(variant.id, {
experienceId: experience.experience_id,
publicId: experience.public_id,
publicationStatus: experience.publication_status,
draftRevision: experience.draft_revision,
});
}
Idempotency: the same Integration + exact identity returns 200 and the same experience_id / public_id. Name is not overwritten on resolve. Safe to retry. Concurrent creates collapse to one row.
A sync should not be all-or-nothing. If one variant fails (validation, 409 deletion-reserved, 429, 5xx), continue the others. Successful variants remain usable. Retry later; already-synced identities resolve instead of duplicating. Honor 429 Retry-After / retry_after_seconds. There is no bulk “create 200 Experiences” Partner route — existing create-or-resolve is the contract.
360 status in the partner UI
publication_status on the Experience is only unpublished or published. Partner UI labels are derived, not extra ArchWalk fields:
| Partner label | Meaning |
|---|---|
| Not configured | No mapping yet, or create-or-resolve has not succeeded |
| Draft | Experience exists; publication_status is unpublished |
| Published | publication_status is published |
| Needs attention | This variant’s provision/sync call failed; other variants can still work |
Edit 360
- Partner UI Edit 360 for that variant.
- Partner backend mints
POST /api/v1/360/creator-sessionsbound to that Experience. The API key stays on the server. - Embed
/aw360/c/{api_id}(api_idfrom the mint response; the path parameter is the session id). - Exact-origin handshake:
creator:ready-for-initthencreator:initwith{ token }in memory only.
Never put the Partner API key in HTML, browser JS, URL, localStorage, sessionStorage, logs, or postMessage. Never put the Creator token in the iframe URL. Switching variants must drop the previous token.
Existing Embedded Creator already handles Panorama upload, external media where permitted, labels, order, starting view, room-to-room navigation, Floor Plan, and publish/unpublish. Do not rebuild those controls in the partner shell.
Creator events
Use the public Creator contract: creator:ready-for-init, creator:ready, creator:dirty, creator:saved, creator:published, creator:unpublished, creator:error, creator:reauth-required.
On creator:published, update the selected variant to Published and offer View 360 / Preview using /aw360/v/{public_id} (publicId on the event, or re-GET the Experience). On creator:unpublished, return the variant to Draft. A full page reload is not required for these known events.
Viewer reuse
Physical rooms 101, 102, and 103 that share Deluxe King all embed the same published public_id. Two rooms of the same visual variant must resolve to the same Viewer URL.
Security (unchanged platform rules)
- API key — server-to-server only.
- Creator session — short-lived browser credential.
- Allowed origins — framing / Creator origin trust. Not API-key authorization, not IP allowlisting, not global API CORS.
- API CORS — separate (
frontend_originfor the ArchWalk app). - Tenant — Organization + Integration come from the Partner credential. Browser-supplied tenant ids must not choose tenancy.
draft_revision— optimistic concurrency. Pure API editors must re-read and retry on409archwalk_360_draft_revision_conflict. Hybrid users let Embedded Creator own that behavior.
See the AcmeStay reference /seller/plugin and /listing/plugin?room=101 for an executable version of this flow.
Authored Info Points
Info Points inspect content without navigating. They use independent opaque
info_point_id identities and spherical yaw/pitch in radians. Required
label is trimmed and limited to 120 characters; optional description is
limited to 4000 characters. panorama_id must belong to this Experience.
Create/PATCH/DELETE require expected_draft_revision; stale revisions return
409. PATCH image_upload_id: null removes an image. Omit it to keep the image.
| Method | Path | Scope |
|---|---|---|
| GET | /api/v1/360/experiences/{experience_api_id}/info-points | panoramas:read |
| POST | /api/v1/360/experiences/{experience_api_id}/info-points | panoramas:write |
| GET | /api/v1/360/experiences/{experience_api_id}/info-points/{info_point_id} | panoramas:read |
| PATCH | /api/v1/360/experiences/{experience_api_id}/info-points/{info_point_id} | panoramas:write |
| DELETE | /api/v1/360/experiences/{experience_api_id}/info-points/{info_point_id} | panoramas:write |
| POST | /api/v1/360/experiences/{experience_api_id}/info-image-uploads | panoramas:upload |
| POST | /api/v1/360/experiences/{experience_api_id}/info-image-uploads/{upload_api_id}/complete | panoramas:upload |
| GET | /api/v1/360/experiences/{experience_api_id}/info-image-uploads/{upload_api_id} | panoramas:upload |
| DELETE | /api/v1/360/experiences/{experience_api_id}/info-image-uploads/{upload_api_id} | panoramas:upload |
{ "expected_draft_revision": 4, "panorama_id": "<panorama-id>",
"yaw": 0.4, "pitch": -0.1, "label": "Private glass elevator",
"description": null, "image_upload_id": null }
Detail images use the existing managed normal-image reservation, PUT and
completion pipeline, with explicit info_image role and aw360_info_image
StoredAsset type. Upload initiation accepts content_type,
declared_size_bytes, optional client_request_id. Completion validates
JPEG/PNG/WebP structure, bounded dimensions (32 MP maximum), real byte size
and quota. No equirectangular aspect ratio is required. Integration managed
media policy applies. Attachment accepts only completed uploads from the
same Experience and organization. Draft references and the current published
revision protect bytes; superseded metadata follows existing byte-retention
policy. Removing/replacing media does not release accounted bytes until
physical deletion is confirmed.
Publishing snapshots the exact Info Point content atomically with panoramas
and navigation. Public info_points contains only the current published
revision: info_point_id, panorama_id, yaw, pitch, label, optional
description/image_url. Internal database and asset IDs are not exposed.
Draft edits stay private until Publish. Hosted Manager and Embedded Creator
share the same primitives and expected-revision semantics.
Experience About and overview video (AW360-046)
The existing Experience PATCH (experiences:write) accepts details alongside
expected_draft_revision. This object replaces all Experience details; omitted
details preserves existing content, and {} clears it.
{
"expected_draft_revision": 7,
"details": {
"description": "A quiet coastal retreat.",
"highlights": ["Private pool", "Ocean terrace"],
"video_url": "https://vimeo.com/123456789"
}
}
Description is optional plain text up to 3,000 characters. Highlights preserve
order, with at most 12 items of 1–120 trimmed characters. Video accepts supported
HTTPS YouTube/Vimeo single-video URLs only and discards supplied query options.
No iframe HTML, upload or managed-video asset is accepted. Private reads contain
current draft details; the public Viewer receives only the immutable published
snapshot after Publish. See aw360-046-experience-details.md for supported URL
formats, migration and owner smoke tests.

