# 360° project
Source: https://viewerdocs.planpoint.io/360
This new feature allows you to create a 360º outlook of your project’s environment and enhance your user‘s experience. Please note you need more than one rendering or photo of your project to enable this feature.
**Change the icon** You can change the 360º icon to any SVG file of your choice. This allows for different experiences, like switching levels, changing buildings, etc. Please read below.
Access the configuration module within your project dashboard and navigate to the **Exterior** tab.

Arrange the desired views in the preferred sequence for presentation within your project. Simply click on the **plus** button to incorporate various views into the display order.
**Important tip:** Upload your images in the desired order for display

After organizing the images, you need to draw the simple polygons on each floor of each render. Just click on **Draw** or **Redraw** and repeat this process until finished.

## Review and edit
You can navigate the different views just by clicking here:

## Changing the 360º icon
You can change Planpoint's default 360º icon to any SVG file. You can only upload one file. This option is available in Content > Exterior.
## Troubleshooting
Here's how to solve some common problems when setting up a 360º project.
Please try refreshing the page.
This feature is only available in Planpoint V2. Please go to your V2 dashboard.
# Allocations
Source: https://viewerdocs.planpoint.io/allocations
The allocation feature is a brand-new feature to Planpoint. It allows you to assign limited access to floors and units for preferred people, whether it is VIP brokers working on your project or even stakeholders.
By using this feature, you will be able to create a custom Planpoint with the attributed units for a specific person that will require a password in order to access it. Please view the video below for step-by-step guide:
**Now that you’ve watched the video, let’s dive deeper into the details. Below, you’ll find a step-by-step guide with images to help you better understand the process.**
Under the **Allocations** tab, you can allocate restricted access to floors and units for designated individuals, be it VIP brokers contributing to your project or key stakeholders. Utilizing this functionality enables you to generate a personalized Planpoint with designated units for a particular individual, requiring a password for access.
* Start by clicking + new allocation
* Then simply give a title for this allocation
* Specify a password
* Select the units you want this allocation to apply and save
* At the bottom of the page you can see all the allocations you have setup for this project
# Amenities and parking
Source: https://viewerdocs.planpoint.io/amenities-parkings
This new feature allows you to add amenities and parking spaces to your Planpoint.
Adding either amenities or parking spaces is very easy, simply follow these steps:
Go to your units module and add a unit.
Now, select the type of unit you wish to add. Choose between **Amenity** or **Parking**.
Go to the **Floors** tab and click on **Draw floorplan** on the desired units.
To incorporate images into your amenities, navigate to the **Units** tab. Find the desire unit and select the **Cover** button and upload the image you wish to use as the main one.
Following the addition of your cover image, you may include additional pictures by clicking the **Gallery** button.
Remember that you can always see your work-in-progress in the **[Style and Embed](https://viewerdocs.planpoint.io/preview-embed)** tab
# Get Floors
Source: https://viewerdocs.planpoint.io/api-documentation/endpoint/floors/get
GET /floors
Fetch floors
# Get Floor
Source: https://viewerdocs.planpoint.io/api-documentation/endpoint/floors/id/get
GET /floors/{id}
Fetch a floor
# Update Floor
Source: https://viewerdocs.planpoint.io/api-documentation/endpoint/floors/id/patch
PATCH /floor/{id}
# Create Floor
Source: https://viewerdocs.planpoint.io/api-documentation/endpoint/floors/post
POST /floors
Create a new floor
# Get Groups
Source: https://viewerdocs.planpoint.io/api-documentation/endpoint/groups/get
GET /groups
# Get Group
Source: https://viewerdocs.planpoint.io/api-documentation/endpoint/groups/id/get
GET /groups/{id}
Fetch a group
# Update Group
Source: https://viewerdocs.planpoint.io/api-documentation/endpoint/groups/id/patch
PATCH /groups/{id}
Update an existing group by Id
# Create Group
Source: https://viewerdocs.planpoint.io/api-documentation/endpoint/groups/post
POST /groups
Create a new group
# Get Leads
Source: https://viewerdocs.planpoint.io/api-documentation/endpoint/leads/get
GET /leads
Fetch leads
# Get Projects
Source: https://viewerdocs.planpoint.io/api-documentation/endpoint/projects/get
GET /projects
Fetch projects
# Get Project
Source: https://viewerdocs.planpoint.io/api-documentation/endpoint/projects/id/get
GET /project/{id}
Fetch a project
# Update Project
Source: https://viewerdocs.planpoint.io/api-documentation/endpoint/projects/id/patch
PATCH /project/{id}
Update an existing project by Id
# Create Project
Source: https://viewerdocs.planpoint.io/api-documentation/endpoint/projects/post
POST /projects
Create a new project
# Get Units
Source: https://viewerdocs.planpoint.io/api-documentation/endpoint/units/get
GET /units
Fetch units
# Delete Unit
Source: https://viewerdocs.planpoint.io/api-documentation/endpoint/units/id/delete
DELETE /units/{id}
This can only be done by the logged in unit.
# Get Unit
Source: https://viewerdocs.planpoint.io/api-documentation/endpoint/units/id/get
GET /units/{id}
Fetch a unit
# Update Unit
Source: https://viewerdocs.planpoint.io/api-documentation/endpoint/units/id/patch
PATCH /units/{id}
Update an existing unit by Id
# Create Unit
Source: https://viewerdocs.planpoint.io/api-documentation/endpoint/units/post
POST /units
Create a new unit
# Login
Source: https://viewerdocs.planpoint.io/api-documentation/endpoint/user/login
POST /users/login
## Step-by-Step Guide: Planpoint Login API
### 1. **Prepare Your Request Body**
You'll need to send a JSON payload with the following fields:
* `username`: Your email address (required, must be valid email format)
* `password`: Your password
### 2. **Set Up the HTTP Request**
Configure a POST request with:
* **Method**: POST
* **URL**: `https://app.planpoint.io/api/users/login`
* **Content-Type**: `application/json`
### 3. **Standard Login Flow**
For regular user login, send this request body:
json
`{
"username": "your-email@example.com",
"password": "your-password"
}`
Example using cURL:
bash
`curl --request POST \ --url https://app.planpoint.io/api/users/login \ --header 'Content-Type: application/json' \ --data '{
"username": "your-email@example.com",
"password": "your-password"
}'`
### 4. **Handle the Response**
**Success Response (200):**
json
`{
"message": "Login successful",
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}`
The response includes:
* `access_token`: JWT token for authenticating future API requests
* HTTP-only cookies are automatically set in your browser:
* `planpoint`: Access token (expires in 1 day for regular users, 90 days for Zapier)
* `planpointRefresh`: Refresh token (expires in 7 days, not set for Zapier users)
**Error Responses:**
* **400**: Invalid request body (username not an email, etc.)
* **401**: User not found, incorrect credentials, or unauthorized
* **403**: Not allowed (for impersonation attempts)
* **405**: Method not allowed (if not using POST)
* **500**: Server error
### 5. **Use the Access Token**
For subsequent API requests, use the `access_token` from the response in your Authorization header:
bash
`Authorization: Bearer YOUR_ACCESS_TOKEN`
# Introduction to Planpoint API
Source: https://viewerdocs.planpoint.io/api-documentation/introduction
In this section, we will be exploring our public RESTful API.
If you're looking for more than simply communicating with Planpoint, you can look into our [SDK](/sdk-documentation/introduction).
Planpoint facilitates technological practices to help developers seamlessly integrate their project's data into other software.
Planpoint API is an advanced programming interface that allows your software to communicate with our services. Exposing our services through a RESTful API makes integrations with your application way easier and smoother.
In this section, we will be exploring our public RESTful API. We will explore how developers can make use of our API to create, view, and delete units and projects, through standardized REST requests.
## Sandbox Account for Developers
If you would like to test our API with demo projects in a sandbox account, please contact support and describe your project, who the end client is and what's the goal.
## Endpoints
The Planpoint API features endpoints for every common item of the Planpoint Viewer. This includes projects, groups, enterprises and units. Each of these items have dedicated endpoints that can be consumed by third party applications.
## Authorization
Planpoint API authorizes requests with a valid bearer token. A bearer token is an authorization token that must be included in the header of every request made to Planpoint API.
## Response Status Codes
Planpoint API response status is reflected in the status codes returned with each response. Here is a list of status codes with their corresponding meaning:
* 200 OK: Response Successful. The response body will contain relevant data.
* 400 Bad Request: The request was invalid. This can be caused by missing fields, etc.
* 401 Unauthorized: Authentication failed. Usually caused by invalid bearer tokens.
* 403 Forbidden: Authorization to resources failed. The user has a valid bearer token, but is not authorized to access requested data.
* 500 Internal Server Error: Unexpected error server-side.
## Fetch Projects
Returns all projects found in a namespace that a host has access to.
`POST
/api/projects/find`
```JavaScript JavaScript using Axios theme={null}
async function fetchProject() {
try {
const response = await axios({
method: 'POST',
headers: {
'content-type': 'application/json'
},
data: {
namespace: namespace,
hostName: hostName
},
url: '/api/projects/find'
})
setProject(response.data)
} catch (err) {
console.error(err)
}
}
```
## Fetch Groups
Returns all groups found in a namespace that a host has access to.
`POST
/api/groups/find`
```JavaScript JavaScript using Axios theme={null}
async function fetchGroup() {
try {
const response = await axios({
method: 'POST',
headers: {
'content-type': 'application/json',
},
data: {
namespace: namespace,
hostName: hostName
},
url: '/api/groups/find'
})
setGroup(response.data)
} catch (err) {
console.error(err)
}
}
```
## Fetch Enterprises
Returns all enterprises found in a namespace that a host has access to.
`POST
/api/enterprises/find`
```JavaScript JavaScript using Axios theme={null}
async function fetchEnterprise() {
try {
const response = await axios({
method: 'POST',
headers: {
'content-type': 'application/json',
},
data: {
namespace: namespace,
hostName: hostName
},
url: '/api/enterprises/find'
})
setEnterprise(response.data)
} catch (err) {
console.error(err)
}
}
```
# Checklist
Source: https://viewerdocs.planpoint.io/checklist
This checklist is designed to guide you through a successful Planpoint launch, from planning and preparation to execution.
### Cloud folder
The first thing you will need is a cloud folder in which you organize all the necessary content at the same place. You can then share this folder with your provider or with us.
### Property Images
You need one or many rendering(s) of your project. It cand also be a photo or many photos of your building. The required format is JPG. The maximum weight is 1MB, anything under is better. Images should not exceed 1000px horizontally.
* Use your best money-shot for this main image.
* If you have a multi-phase project, use a bird's eye-view of the project for the group image. [View a group project here.](https://www.planpoint.io/types/rentals)
* If you wish to use our [360° project feature](https://www.planpoint.io/new-releases/360-project), gather all the best shots from all around the building.
**PRO TIP:** Vertical images will look better than horizontal images, just like the one below:
### Floors
You also need all the floor plates for all floors of your building. Each of them need to be separated for each floor and saved in JPG format. Consider adding the following elements to provide a better experience for your clients:
* Compass
* Landscaping
* Parking area
* Streets and directions
* Natural features like rivers, mountains, etc.
* Design cleaned out of construction or architectural details
Here's a good example:
### Unit layouts
All unit layout plans must be created in two versions:
* **One JPG version** for the plan viewer. This is the first thing you see while looking at a specific unit.
* **One PDF version** for the download PDF button on every unit page.
When units span more than one floor, the **PDF** can contain multiple pages, but the **JPG** version of the same unit must be separated into multiple separate images - they will appear as a gallery.
### Unit list
You must have your project's unit list with all important information such as unit name, area, number of bedrooms, price, availability, occupancy dates, inclusions, etc.
**PRO TIP:** We provide an Excel [template that you can download](https://app.planpoint.io/assets/template.xlsx) and use as the basis for your unit list.
Please view the video below to understand the proper formatting to be used in your document:
### Optional content
Planpoint allows you to add images for each unit. Please resize your images to max 1800px horizontally and no more than 1MB per image - images that are compressed will load faster for your clients! We recommend using free tools such as [TinyJPG](https://tinyjpg.com) and [Squoosh](https://squoosh.app).
Here's some ideas of what you can display:
* interiors of units like kitchen, bathroom, dining room
* exterior views from the point of view of the unit
* project highlights
Planpoint allows you to add 3D tours for each unit in your project. There's two different technologies for this. The base 3D tour is the one you see below, which 3D artists can create in any typical rendering software as a '360 image'. It can also be a simple Matterport tour.
The second one is the [next-gen 3D tour](https://www.planpoint.io/new-releases/next-gen-3d-tours) which allows for dynamic choice of finishes, element selection and room measurements.
You can also include renderings or photos of your amenities such as the pool, the terrace, the interior courtyard and use our **Amenities** feature to showcase them.
## What kind of plan viewer are you creating?
It's important to take note of the various requirements for different types of plan viewer before going any further.
Condominium projects and new construction rental buildings.
Single-family home developments projects and pure land projects.
Existing multifamily buildings offering appartments for rent.
Retail, office and industrial properties for lease and sale.
# Colors
Source: https://viewerdocs.planpoint.io/colors
The Color module allows you to customize the color palette of the plan viewer so it better reflects your brand and visual preferences. You can define colors for key interface elements, including the background of the viewer, hover interactions where floors and plans are highlighted, buttons and button text, general text, and unit status indicators such as available, reserved, sold, and featured.
These colors are applied consistently across the project, including interactive areas when users engage with the exterior building image. All changes are applied immediately, allowing you to preview and fine-tune your styling in real time. Colors can be set using any HEX or RGB value, providing full flexibility and precision.
If you want to edit your color scheme, simply follow the instructions below:
Go to **Settings**. Under **Style & Embed**, select **Customize Style**.
Once there, select **Color Scheme**.
You can make use of transparency by using the transparency slider as you see in the video below. A slightly transparent color is preferable over a completely opaque one, as it will show some details about the building and floor plan.
# Conversion Tracking
Source: https://viewerdocs.planpoint.io/conversion-tracking
This page explains how to use Conversion Tracking for Planpoint.
### Here's a video guide:
### If you’re using Planpoint’s default Lead Gen Form
Additionally, you can also make use of UTM parameters to keep track of where specific leads are coming from.
You can use the **Redirect URL** to point to a thank you page on your website and Planpoint will dynamically add the specific UTM parameters that were present on the page when he filled up the form.
So, for example, let’s imagine you have the following setup:
* Your Planpoint Viewer is embedded at [www.project.com/plans](http://www.project.com/plans) and;
* IYour redirect page after the form is completed is [www.project.com/thank-you](http://www.project.com/thank-you);
* You’re running an advertising campaign that adds various UTMs such as `?utm_source=google` to the base URL;
* So, [www.project.com/plans](http://www.project.com/plans) becomes [www.project.com/plans?utm\_source=google](http://www.project.com/plans?utm_source=google) ;
* After the visitor fills up the form, he’s automatically redirected to [www.project.com/thank-you?utm\_source=google](http://www.project.com/thank-you?utm_source=google) where `?utm_source=google` was added dynamically.
# Custom buttons
Source: https://viewerdocs.planpoint.io/custom-buttons
The **Custom Button** feature allows you to add an almost infinite number of options to your units view, because it simply links to any given URL. You can also change the button text and icon.
### Here's a video guide:
# Custom CSS
Source: https://viewerdocs.planpoint.io/custom-css
Take full control of your viewer's styling with your own CSS, written and previewed live inside the style editor.
When the style editor's themes, colors and fonts aren't enough, you can write your own CSS. Your rules load **after** the default Planpoint styles, so you only need to write what you want to change.
Custom CSS is layered on top of the default theme — never publish a copy of the whole default stylesheet. If you do, your viewer freezes at that version of the design and stops picking up future product updates and fixes.
## Before you begin
* Your embed URLs need `?customCss=true` for the custom styles to load (see [Step 4](#step-4-add-the-url-parameter)).
* Nested elements must be targeted with wildcard selectors such as `[class*="unitTile"]` — plain class names won't work (see [Understanding CSS selectors](#understanding-css-selectors)).
***
## Video Guide for Visual Learners
***
## How to set up custom CSS
### Step 1: Open the CSS editor
1. Go to **Style & Embed** → **Style**
2. Click **Customize** to open the style editor
3. In the toolbar at the bottom of the preview, click the **`>` Custom CSS** button
The left sidebar turns into a code editor, and the preview on the right stays live.
The editor opens on a short comment block — an empty starting point that changes nothing until you write your first rule.
### Step 2: Write your rules
Type in the editor and the preview updates instantly — no upload, no reload.
* Pick a block from **Insert snippet** — Brand colours, Filter bar, Unit cards, Unit detail
* CTA buttons, Status badges, and more. Each one is pre-filled with the current default values, so you edit instead of starting from a blank page.
* Click **Select element**, then click anything in the preview. Planpoint works out the right selector for it, drops an empty rule into the editor and lists the element's current values as comments you can uncomment and edit. Press Esc to cancel. If the selector is already in your file, the cursor jumps to it instead.
* Use the **Desktop / Tablet / Mobile** buttons to check your styles at every breakpoint.
* Drag the sidebar's right edge to give the editor more room — the width is remembered.
* Collapse the sidebar (the **`‹`** button) for a full-width preview; your work is kept.
* Right-click an element in the preview and choose **Inspect** to find the selector you need.
* Click **View default theme** to read the current default stylesheet **for reference** — read it to find selectors, but don't paste it into the editor.
Add the **Brand colours** snippet first. It defines the variables the other snippets read, so one colour change carries through every block you add afterwards.
#### Available snippets
| Snippet | What it covers |
| ----------------------------- | --------------------------------------------------- |
| **Brand colours** | `--pp-brand-*` variables every other snippet reads |
| **Typography** | Base font family and the price type scale |
| **Filter bar** | Filter wrapper background and dropdown toggles |
| **Unit cards (grid)** | Card radius, border and hover lift |
| **Unit list (table)** | Table headers and row hover |
| **Unit detail + CTA buttons** | Unit card, "Request info" buttons, shortlist button |
| **Status badges** | Available / sold / leased / reserved / on hold |
| **Portal header** | Header bar and its call-to-action buttons |
| **Floor plan canvas** | Canvas container and its loading placeholder |
| **Map points of interest** | Pins, popup cards and category pills |
| **Hide an element** | A `display: none` starting point |
The searchbox editor has its own set: shell, filters row, dropdown menus, search button, range sliders and chips.
### Step 3: Save and publish
Click **Save & publish** (or press ⌘/Ctrl + S). The status above the editor tells you where you stand: *Nothing overridden yet*, *Unsaved changes* or *Published*.
Publishing uploads your stylesheet and turns custom CSS on for this viewer — there's no separate toggle to flip anymore.
The links under the button cover everything else:
| Button | What it does |
| ---------------------- | ------------------------------------------------------------------- |
| **View default theme** | Opens the default stylesheet in a new tab, as a reference |
| **Import .css** | Loads a `.css` file from your computer into the editor (max 1.5 MB) |
| **Download** | Saves the editor's current content as a `.css` file |
| **Published file** | Opens the stylesheet that's currently live |
| **Revert** | Discards unsaved changes and restores the published version |
| **Remove** | Stops using custom CSS on this viewer |
Every save uploads a new file, so your previous versions stay available — nothing is overwritten.
### Step 4: Add the URL parameter
Custom CSS only loads on embeds that ask for it:
**Before:**
```html theme={null}
```
**After:**
```html theme={null}
```
***
## Group, enterprise and searchbox stylesheets
Each surface has its own stylesheet, edited the same way:
| Surface | Where to find it |
| -------------- | -------------------------------------------------------------------------------- |
| **Project** | Project **Style & Embed** → **Style** → **Customize** → **`>` Custom CSS** |
| **Group** | Group **Style & Embed** → **Style** → **Customize** → **`>` Custom CSS** |
| **Enterprise** | Enterprise **Settings** → **Style** → **Customize** → **`>` Custom CSS** |
| **Searchbox** | Enterprise **Settings** → **Searchbox** → **Custom CSS** → **Open style editor** |
The searchbox uses the same editor. When you open it from an enterprise, a **Viewer / Searchbox** switch appears at the top of the sidebar so you can move between the two stylesheets — the preview and the snippet list follow along. Searchbox selectors aren't scoped to `[data-pp-embed]`, since the searchbox is its own page.
***
## Understanding CSS selectors
The viewer is built with CSS Modules, so class names get a unique suffix (`unitTile` becomes something like `Style1_unitTile__a1B2c`). Target elements with the stable `data-pp-*` attributes and with `[class*="…"]` wildcards.
**✅ Correct selectors that work:**
```css theme={null}
/* Data attribute selectors for main containers */
[data-pp-embed] /* Main embed wrapper */
[data-pp-container] /* Inner container */
[data-pp-layout] /* Layout container */
[data-pp-header] /* Portal header bar */
[data-pp-cta-desktop] /* Header CTA button (desktop) */
[data-pp-cta-mobile] /* Header CTA button (mobile) */
[data-pp-filters] /* Filters container */
[data-pp-filters-wrapper] /* Filters wrapper with background */
[data-pp-glass-filters] /* Glass-style filter variant */
[data-pp-unit-grid] /* Unit grid */
[data-pp-unit-list] /* Unit list table */
[data-pp-unit-view] /* Unit view container */
[data-pp-amenity-view] /* Amenity view container */
[data-pp-commerce-view] /* Commerce view container */
[data-pp-canvas] /* Canvas/floor plan area */
[data-pp-modal] /* Modal containers */
/* Scope-specific selectors */
[data-pp-scope="project"] /* Project view */
[data-pp-scope="floor"] /* Floor view */
[data-pp-scope="unit"] /* Unit detail view */
/* Class wildcard selectors for nested elements */
[data-pp-embed] [class*="unitTile"] /* Unit cards */
[data-pp-embed] [class*="unitTileBody"] /* Card body */
[data-pp-embed] [class*="unitTilePrice"] /* Price display */
[data-pp-embed] [class*="unitTileAttrs"] /* Attributes container */
[data-pp-embed] [class*="badge"] /* Status badges */
[data-pp-embed] [class*="dropdownToggle"] /* Filter dropdowns */
[data-pp-embed] [class*="dropdownMenu"] /* Dropdown menus */
[data-pp-embed] [class*="unitCardAction"] /* Action buttons */
[data-pp-embed] [class*="shortlistAction"] /* Favorite button */
/* Scope-specific selectors */
[data-pp-scope="project"] [data-pp-layout] /* Project view layout */
[data-pp-scope="floor"] [data-pp-layout] /* Floor view layout */
[data-pp-scope="unit"] [class*="unitCard"] /* Unit detail card */
```
**❌ Incorrect selectors that won't work:**
```css theme={null}
/* These data attribute selectors are NOT implemented */
[data-pp-button] /* Use [class*="button"] or [class*="unitCardAction"] */
[data-pp-unit-tile] /* Use [class*="unitTile"] instead */
[data-pp-badge] /* Use [class*="badge"] instead */
[data-pp-dropdown] /* Use [class*="dropdown"] instead */
[data-pp-list] /* Use [data-pp-unit-list] or [data-pp-unit-grid] */
[data-pp-floor] /* Use [data-pp-scope="floor"] instead */
/* Plain class names won't work due to CSS Modules */
.header
.unitTile
.badge
/* This attribute doesn't exist */
[data-theme="showcase"]
```
### Common elements you can style
| Element | Selector | What it controls |
| -------------------- | ---------------------------- | ------------------------------------- |
| Main container | `[data-pp-embed]` | Overall viewer wrapper, CSS variables |
| Inner container | `[data-pp-container]` | Inner container element |
| Layout | `[data-pp-layout]` | Grid layout container |
| Portal header | `[data-pp-header]` | Header bar with logo and CTAs |
| Header CTA (desktop) | `[data-pp-cta-desktop]` | Desktop call-to-action button |
| Header CTA (mobile) | `[data-pp-cta-mobile]` | Mobile call-to-action button |
| Filters wrapper | `[data-pp-filters-wrapper]` | Filter bar background/styling |
| Filters | `[data-pp-filters]` | Filter controls section |
| Glass filters | `[data-pp-glass-filters]` | Glass-style filter variant |
| Unit grid | `[data-pp-unit-grid]` | Grid of unit cards |
| Unit list | `[data-pp-unit-list]` | Table view of units |
| Unit cards | `[class*="unitTile"]` | Individual unit listings |
| Card body | `[class*="unitTileBody"]` | Card content area |
| Card price | `[class*="unitTilePrice"]` | Price display |
| Status badges | `[class*="badge"]` | Available/Sold/Reserved labels |
| Dropdowns | `[class*="dropdownToggle"]` | Filter dropdown buttons |
| Dropdown menus | `[class*="dropdownMenu"]` | Filter dropdown options |
| Action buttons | `[class*="unitCardAction"]` | CTA buttons |
| Modals | `[data-pp-modal]` | Popup windows |
| Canvas area | `[data-pp-canvas]` | Floor plan display area |
| Unit view | `[data-pp-unit-view]` | Unit view container |
| Amenity view | `[data-pp-amenity-view]` | Amenity view container |
| Commerce view | `[data-pp-commerce-view]` | Commerce view container |
| Gallery | `[class*="embla"]` | Image carousel |
| Shortlist button | `[class*="shortlistAction"]` | Favorite/save button |
***
## CSS custom properties (variables)
The default theme is built on CSS custom properties. Overriding them is the safest way to restyle the viewer: a few lines change everything consistently, and they keep working when the design is updated.
These variables live in *your* stylesheet, not in the viewer itself. Setting `--pp-brand-primary` on its own does nothing until a rule reads it — which is exactly what the snippets do. Add **Brand colours** first, then any other snippet follows it.
```css theme={null}
[data-pp-embed] {
/* Brand — every snippet derives from --pp-brand-primary */
--pp-brand-primary: #5b4cdb;
/* Solid brand colour with a subtle sheen: buttons, chips, filter bar.
Swap it for a gradient if you want a two-tone look. */
--pp-brand-surface: linear-gradient(
135deg,
rgba(255, 255, 255, 0.12) 0%,
rgba(0, 0, 0, 0.12) 100%
),
var(--pp-brand-primary);
/* Pale wash of the brand colour: hover and selected states */
--pp-brand-tint: linear-gradient(
0deg,
rgba(255, 255, 255, 0.9) 0%,
rgba(255, 255, 255, 0.9) 100%
),
var(--pp-brand-primary);
/* Optional accents — nothing uses them unless you do */
--pp-brand-secondary: #7c3aed;
--pp-brand-accent: #8b5cf6;
--pp-brand-light: #ede9fe;
/* Status Colors */
--pp-success: #059669;
--pp-success-bg: #d1fae5;
--pp-danger: #dc2626;
--pp-danger-bg: #fee2e2;
--pp-warning: #d97706;
--pp-warning-bg: #fef3c7;
/* Neutral Palette */
--pp-white: #ffffff;
--pp-gray-50: #f9fafb;
--pp-gray-100: #f3f4f6;
--pp-gray-200: #e5e7eb;
--pp-gray-500: #6b7280;
--pp-gray-700: #374151;
--pp-gray-900: #111827;
/* Shadows */
--pp-shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.06), 0 1px 2px rgba(0, 0, 0, 0.04);
--pp-shadow-md: 0 4px 8px -2px rgba(0, 0, 0, 0.08), 0 2px 4px -2px rgba(0, 0, 0, 0.04);
--pp-shadow-lg: 0 12px 24px -4px rgba(0, 0, 0, 0.1), 0 4px 8px -2px rgba(0, 0, 0, 0.05);
/* Border Radius */
--pp-radius-sm: 8px;
--pp-radius-md: 12px;
--pp-radius-lg: 16px;
--pp-radius-xl: 20px;
--pp-radius-full: 9999px;
}
```
***
## Example: styling status badges
```css theme={null}
/* Available badge - green gradient */
[data-pp-embed] [class*="badge"][data-style="available"] {
background: linear-gradient(135deg, #10b981 0%, #059669 100%) !important;
color: #ffffff !important;
font-weight: 600 !important;
padding: 6px 14px !important;
border-radius: 9999px !important;
}
/* Sold badge - red gradient */
[data-pp-embed] [class*="badge"][data-style="sold"] {
background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%) !important;
color: #ffffff !important;
font-weight: 600 !important;
padding: 6px 14px !important;
border-radius: 9999px !important;
}
/* Reserved/On Hold badge - gray gradient */
[data-pp-embed] [class*="badge"][data-style="reserved"],
[data-pp-embed] [class*="badge"][data-style="onhold"] {
background: linear-gradient(135deg, #6b7280 0%, #4b5563 100%) !important;
color: #ffffff !important;
font-weight: 600 !important;
padding: 6px 14px !important;
border-radius: 9999px !important;
}
```
***
## Troubleshooting
### It looks right in the editor but not on my site
1. **Missing URL parameter:** make sure `?customCss=true` is in your embed URL
2. **Not published:** the status above the editor must read *Published*, not *Unsaved changes* — click **Save & publish**
3. **Cache:** reload with a hard refresh, or test in a private window
### Some elements aren't styling correctly
You're probably using selectors that don't exist in the viewer:
* ❌ Don't use: `[data-pp-button]`, `[data-pp-unit-tile]`, `.unitTile`
* ✅ Do use: `[class*="button"]`, `[class*="unitTile"]`
Inspect the element in the live preview to see the exact class, then target it with a `[class*="…"]` wildcard.
### My rule is ignored
Add `!important`. CSS Modules generate high-specificity selectors, so most overrides need it.
### I changed a `--pp-*` variable and nothing happened
Variables only do something when a rule reads them. They're defined by your stylesheet, not by the viewer — so `--pp-brand-primary` on its own changes nothing. Add the **Brand colours** snippet plus the snippet for the section you want to restyle.
### My viewer stopped getting design updates
That happens when the published stylesheet is a full copy of the default theme: it pins every component to an old version. Trim it down to the rules you actually changed.
### How do I go back to the default theme?
Click **Remove** in the CSS editor, or drop the `?customCss=true` parameter from your embed URL. Removing doesn't delete your stylesheet — you can paste it back later.
# Form
Source: https://viewerdocs.planpoint.io/documentation/general/advanced-features/forms
The Form module allows you to create fully customizable forms to collect information from users interested in a specific unit. Form can be integrated directly into your project flow, supporting advanced customization, logic rules, previews, and multi-language translations.
This module is commonly used for:
* Lead capture
* Unit inquiries
* Reservation requests
* Contact and information collection
### Video Guide for Visual Learners
### Enabling the Form
To activate a form for your viewer:
1. In your dashboard, go to Sales & Marketing. From there, you will find the Form module.
2. Toggle Enable form to ON.
Once enabled, the form becomes available in the project flow and can be customized.
## Form Configuration
To customize your form, click Edit to open the Form builder. The builder is divided into four main sections:
* Designer
* Preview
* Logic
* Translations
### Designer
The Designer tab is where you build and configure the structure of your form.
### Form Title and Description
* Form title: Defines the main heading of the form. Example: Request info about unit unitName Dynamic variables like unitName are supported.
* Title visibility: You can choose whether the title and description are visible to end users.
### Form Language
* Select the default language for the form. Available default languages are English, Spanish, French, and German.
### Display Settings
* Form display mode
* Editable: Users can fill in and submit the form.
* Read-only: Form is visible but cannot be edited.
* Form width mode
* Auto: Adjust automatically to layout.
* Static: Fixed width.
* Responsive: Adapts to different screen sizes.
### Fields and Questions
You can add and configure multiple field types, including:
* Single-line input
* Long text
* Radio button groups
* Checkboxes
* Dropdowns
* Multi-select dropdowns
* Yes / No questions
Each field supports:
* Required / optional settings
* Custom labels
* Validation rules
**Example fields:**
### Logo and Header
You can upload a logo to display in the form header, helping mantain brand consistency.
### Navigation and Pages
* Organize the form into multiple pages
* Control navigation between pages
* Define the overall flow of the form
### Validation
Set validation rules to ensure users provide correct and complete information (e.g. required fields, email format).
### Thank you page
After submission, users are shown a confirmation screen. You can customize:
* Thank you message
* HTML markup
* Button text
## Preview
The Preview tab allows you to see exactly how the form will appear to end users before publishing.
* View the full layout
* Test field behaviour
* Confirm images, text, and button placement
This is especially useful when testing different screen sizes and layouts.
## Logic
The logic tab lets you define conditional rules that control the form flow.
### Logic Rules
You can create rules based on user input to:
* Show or hide specific questions
* Control navigation between pages
* Customize the user journey dynamically
To create a rule:
1. Go to Logic
2. Click Add New Rule
3. Define the condition and resulting action
If no rules are created, the form follows a linear flow by default.
## Translations
The Translations tab aloows you to manage multi-language versions of the form.
### Supported Translations
You can translate:
* Form title
* Button text
* Than you page content
* Questions labels
Each language is managed side by side for easy comparison and editing.
### Language Management
* Add or remove languages
* Enable only selected languages
* Keep English as default fallback
This makes Forms ideal for international or multi-region projects.
## Form Image
You can upload a Form Image that will override the main project image and be displayed alongside the form in preview and live views.
## Email Settings
Forms support email language configuration, allowing notification or automated messages to match the selected form language.
## Custom Reservation Flow
For advanced use cases, you can define a Custom Reservation Flow, including a custom confirmation message after submission.
If no custom message is set, the default confirmation behavior is applied.
Best Practices:
* Keep forms short to improve conversion rates
* Use required fileds only when neccessary
* Always preview before enabling the form
* Use translations to improve accessibility
* Leverage logic rules for personalized flows
# PostMessage Tracking
Source: https://viewerdocs.planpoint.io/documentation/general/advanced-features/post-message
## Overview
When Planpoint is embedded via iframe, the parent page cannot directly access what happens inside. **PostMessage Tracking** solves this by broadcasting events from the iframe to your page, allowing you to capture and forward them to any analytics tool.
***
## Setup
### 1. Enable in Dashboard
1. Go to **Sales & Marketing → Event Tracking**
2. Toggle **PostMessage Analytics** on
3. Select the events you want to track
4. Click **Select All** for comprehensive tracking
### 2. Copy Embed Code
Go to **Settings → Embed Code** and click "Copy". The generated code automatically includes the analytics listener script when PostMessage Analytics is enabled.
### 3. Uncomment Your Analytics Platform
The embed code includes commented-out lines for common platforms. Uncomment the one you use:
```javascript theme={null}
// Send to Google Analytics 4 (uncomment if using gtag)
// if (typeof gtag !== 'undefined') gtag('event', event.data.event, event.data.data);
// Send to GTM dataLayer (uncomment if using GTM)
// if (typeof dataLayer !== 'undefined') dataLayer.push({ event: event.data.event, ...event.data.data });
```
* **Using GA4 with gtag.js?** → Uncomment the `gtag` line
* **Using Google Tag Manager?** → Uncomment the `dataLayer` line
* **Using something else?** → Add your own handler (see examples below)
***
## Available Events
| Event | Description |
| ------------------------ | --------------------------------- |
| `project-viewed` | User views a project |
| `floor-viewed` | User selects a floor |
| `unit-viewed` | User views a unit detail |
| `commerce-viewed` | User views a commercial space |
| `favorite-added` | User adds item to favorites |
| `favorite-removed` | User removes item from favorites |
| `filters-applied` | User applies search filters |
| `contact-form-submitted` | User submits contact form |
| `share-initiated` | User shares via email |
| `download-initiated` | User downloads floorplan/brochure |
| `gallery-opened` | User opens image gallery |
| `3d-model-viewed` | User opens 3D virtual tour |
| `portal-signup` | User signs up for portal |
| `portal-signin` | User signs in to portal |
| `payment-completed` | User completes a payment |
***
## Integration Examples
### Google Analytics 4 (gtag.js)
```javascript theme={null}
window.addEventListener('message', function(event) {
if (event.data.type !== 'planpoint-event') return;
gtag('event', event.data.event, event.data.data);
});
```
### Google Tag Manager
```javascript theme={null}
window.addEventListener('message', function(event) {
if (event.data.type !== 'planpoint-event') return;
dataLayer.push({
event: event.data.event,
...event.data.data
});
});
```
### Custom Handler
```javascript theme={null}
window.addEventListener('message', function(event) {
if (event.data.type !== 'planpoint-event') return;
console.log('Event:', event.data.event);
console.log('Data:', event.data.data);
// Send to your analytics platform
myAnalytics.track(event.data.event, event.data.data);
});
```
***
## Event Data Structure
All events follow this format:
```javascript theme={null}
{
type: 'planpoint-event',
event: 'unit-viewed', // Event name
data: { // Event-specific data
projectId: '...',
projectName: '...',
unitId: '...',
unitName: '...',
// Additional fields vary by event
}
}
```
### Sample Event Payloads
**Unit Viewed:**
```javascript theme={null}
{
projectId: "abc123",
projectName: "Sunrise Towers",
unitId: "unit456",
unitName: "Unit 302",
floorId: "floor789",
floorName: "3rd Floor"
}
```
**Contact Form Submitted:**
```javascript theme={null}
{
projectId: "abc123",
projectName: "Sunrise Towers",
unitId: "unit456",
formType: "contact"
}
```
**Favorite Added:**
```javascript theme={null}
{
projectId: "abc123",
projectName: "Sunrise Towers",
unitId: "unit456",
unitName: "Unit 302"
}
```
***
## Best Practices
1. **Select only needed events** — Reduces noise in your analytics
2. **Test in browser console first** — Events log to console by default
3. **Use GTM for flexibility** — Easier to modify tracking without code changes
4. **Set up GA4 custom dimensions** — Map `projectName`, `unitName` for better reporting
***
## Troubleshooting
### Events not firing?
* Verify PostMessage Analytics is **enabled** in dashboard
* Check that specific events are **selected**
* Regenerate and update your embed code after changing settings
### Events firing but not in GA4?
* Confirm `gtag` is loaded before the listener
* Check GA4 DebugView for incoming events
* Verify your GA4 measurement ID is correct
***
## Migration from GTM Code Injection
If you were using the legacy **GTM Code Injection** feature:
| Legacy (GTM Injection) | New (PostMessage Analytics) |
| ------------------------------- | -------------------------------- |
| Injects GTM into Planpoint page | Broadcasts events to parent page |
| Works for direct page visits | Works for embedded iframes |
| Limited to GTM | Works with any analytics tool |
For embedded usage, **PostMessage Analytics is recommended**.
# SEO indexing
Source: https://viewerdocs.planpoint.io/documentation/general/advanced-features/untitled-page
Turn your Planpoint Viewer into search engine-indexable pages so clients can find your units directly from search result pages (Google, Bing, and AI).
## How It Works, In Short
* Your real estate project's website is hosted on its own domain (e.g. `astoria.com`).
* Planpoint Viewer is embedded on a page like `astoria.com/floorplans`. That embedded viewer is **not** crawlable by Google because it's an iframe.
* To make the content of Planpoint Viewer searchable, add a **CNAME subdomain** (e.g. `floorplans.astoria.com`) and connect it in Style & Embed settings.
* On that subdomain we serve the units as **dedicated, server-rendered SEO pages** — not the embedded Viewer. The content lives in the raw HTML, so Google can read and index it. Each page still links into the interactive viewer.
* The **sitemap.xml and robots.txt** documents are generated automatically and stay up to date on every publish — nothing manual.
* **Titles and descriptions** are automatic and descriptive, e.g. the landing page reads `Astoria — Floor Plans & Availability`, unit pages read `Unit 202 — 2 Bedroom Floor Plan at Astoria`, with auto descriptions summarizing available units, layouts and pricing.
***
When you connect a **custom domain** to your project (e.g. `viewer.yourproject.com`), Planpoint automatically serves a set of public, search-engine-friendly pages for it. The domain exposes your units as real, crawlable web pages — so they can be indexed by Google and show up when buyers search.
The pages are fully server-rendered (the content is in the HTML, not loaded later by JavaScript), which is what makes them indexable.
***
## Before You Start
You need a **custom domain connected to your project**. If you haven't set one up yet, please go to [Style & Embed](https://viewerdocs.planpoint.io/preview-embed).
This applies only to **custom domains**. Your Planpoint dashboard and the default `*.planpoint` links are unaffected — they keep working exactly as before.
***
## What Gets Published
On your custom domain, these pages are generated automatically:
| URL | Page |
| -------------------------------- | ------------------------------------------------ |
| `/` | Landing page listing all available units |
| `/unit/` | Individual unit page (beds, baths, area, floor…) |
| `/` (e.g. `/2-bedrooms`) | Filtered list for a unit type |
| `/sitemap.xml` | Sitemap of all the above, for search engines |
| `/robots.txt` | Crawl rules + link to the sitemap |
Each page carries a proper title, heading, and unit details so Google can understand and rank it.
***
## Step-by-Step
### Step 1 — Confirm your pages are live
Open your project's custom domain in a browser and check:
* The **root** (`viewer.yourproject.com`) shows your units
* Clicking a unit opens a page like `viewer.yourproject.com/unit/301`
* A filter URL like `viewer.yourproject.com/2-bedrooms` shows a filtered list
* `viewer.yourproject.com/sitemap.xml` lists your URLs
* `viewer.yourproject.com/robots.txt` loads
Right-click any page → **View Source**. If you can see the unit text in the raw HTML, the page is indexable. That's exactly what Googlebot reads.
### Step 2 — Add your domain to Google Search Console
Go to [Google Search Console](https://search.google.com/search-console) and add a new **URL prefix** property using your full domain (e.g. `https://viewer.yourproject.com/`).
A **URL-prefix** property covers just that subdomain. It does **not** cover your root marketing site (`yourproject.com`) — that's a separate property.
### Step 3 — Verify ownership
Choose the **HTML file** verification method (Google's recommended option):
* Download the verification file Google gives you (e.g. `google.html`).
* Send it to your Planpoint contact — we host it on your domain for you.
* Once it's live, click **Verify** in Search Console.
Planpoint serves this file automatically for connected domains — you don't upload anything yourself. Just share the file (or its name) with us and we'll confirm when it's ready to verify.
### Step 4 — Submit your sitemap
In Search Console, open **Sitemaps** and submit:
```text theme={null}
sitemap.xml
```
Google will read it and discover all your unit and filter pages. A **Success** status means it was accepted.

### Step 5 — Request indexing (optional, speeds things up)
Use **URL Inspection** at the top of Search Console, paste a page URL (e.g. a unit page), and click **Request Indexing**. Do this for your homepage and a few key units to jump the crawl queue instead of waiting.
***
## How to Check If You're Indexed
* **In Google:** search `site:viewer.yourproject.com`. It lists every page Google has already indexed for the domain. New pages appear here over time as they're crawled.
* **In Search Console:** the **Pages** report shows how many URLs are indexed, and **Performance** shows impressions and clicks once pages start ranking.
Indexing is not instant. New pages typically take **days to a few weeks** to appear in Google, even after submitting the sitemap. An empty `site:` result early on is normal — it means Google hasn't crawled yet, not that anything is wrong.
***
## FAQ
**Does this change my dashboard or embed?** No. Only public pages on your connected custom domain are affected. Your dashboard, embeds, and Planpoint links are unchanged.
**Will unavailable/sold units show up?** Only units surfaced on your public pages are included in the sitemap.
**Can I index my main website (`yourproject.com`) too?** That's a separate site and a separate Search Console property. This feature covers the Planpoint viewer domain only.
# Terminology
Source: https://viewerdocs.planpoint.io/documentation/general/editing-and-management/terminology
The Terminology feature allows you to customize the wording used throughout your project so it aligns with your business, market, or customer preferences. By adjusting key terms, you can ensure the interface language matches how your customers naturally refer to units, listings, and project details.
This is especially useful for adapting Planpoint to different industries, regions, or branding requirements without changing any underlying functionality.
Terminology updates are applied across the project wherever those terms appear, creating a more consistent and familiar experience for your users.
### How to Access Terminology Settings
1. Open your project and go to **Settings**.
2. Navigate to **Style & Embed**.
3. Click **Customize Style**.
4. Within the Style editor, open the **Terminology** tab.
From there, simply click on the term you want to edit and enter the wording of your choice. Changes are applied automatically and reflected everywhere that term appears in the project.
### Editable Terminology
The following terms can be customized on a per-project basis. Updating a term will change how it appears throughout the project interface.
| Default Term | Description | Example Customizations |
| :-------------------- | :--------------------------------- | :---------------------------- |
| **1. Project** | The overall development or listing | Building, Property, Community |
| **2. Floor** | A level within the project | Level, Story |
| **3. Unit** | An individual space or listing | Apartment, Suite, Office |
| **4. Area** | Size measurement for a unit | Sq Ft, Sq M, Size |
| **5. Price** | Cost associated with a unit | Rent, Lease Rate, Starting At |
| **6. Change model** | Action to switch unit variants | Switch Layout, Change Plan |
| **7. Available** | Unit is currently available | Open, For Sale |
| **8. Reserved** | Unit is temporarily held | On Hold |
| **9. Future** | Unit not yet released | Coming Soon |
| **10. Unavailable** | Unit cannot be selected | Not Available |
| **11. Sold / Leased** | Unit has been finalized | Sold, Leased |
# Themes
Source: https://viewerdocs.planpoint.io/documentation/general/editing-and-management/themes
Themes control the visual style and layout of your Planpoint project viewer. Each theme is prebuilt and ready to use, allowing you to present your project in a professional and consistent way without custom design work.
Themes define how your listings, units, floorplans, and project details are displayed to users across desktop and mobile devices.
## What are themes?
A theme is a predesigned visual layout that determines:
* Overall page structure and layout
* Typography, spacing, and visual hierarchy
* How units and listings are displayed
* Interaction patterns and UI presentation
* The overall look and feel of your project
All themes are responsive and optimized for modern browsers and devices.
## **How to Access and Apply Themes**
Follow these steps to access themes in your Planpoint dashboard:
1. From your dashboard, click Style & Embed at the top of the project. If you are already inside a project, navigate to Settings → Style & Embed.
2. Click on Customize Style.
3. In the Style editor, navigate to the Themes section and select the theme that best fits your needs. The theme will be applied immediately, and you can change it at any time.
You can also use your own CSS. To get started, download the default theme CSS, make your edits, and then upload it.
# Change information
Source: https://viewerdocs.planpoint.io/edit-info
Here you can find how to modify/update your projects.
There are many things you can modify in your projects. Below, you will find instructions on how to make these changes.
Go to settings, and under **project details**, you can modify the main image, name, website URL and internal URLs of the project.
As easy as it looks in the following video. Just select your desired modifications based on the project phase.
After updating the main render, you will need to modify the exterior drawing. Simply navigate to the **content** module, click on the **exterior** tab, and *redraw* the corresponding floors.
Navigate to the **Floors** tab and select **Floorplan** to change the actual floor plate. Then, click on **Draw Floorplan** for each unit and sketch the corresponding polygon.
In the **Units** tab, you can update your unit list either *manually* (one by one) or *automatically* by re-uploading an updated version of the CSV file.
To make any updates, you can download the current unit list
# Enterprise
Source: https://viewerdocs.planpoint.io/enterprise-new
This new feature allows you to showcase all your projects and availabilities in a single map interface. Here is how you can build it.
In Planpoint Enterprise, you can integrate projects from a single account or many different accounts (by invitation)
***
## Video Guide for Visual Learners
***
In your Planpoint dashboard, click the hamburger menu icon and then select **Enterprises**.
To create you enterprise project, click on **new enterprise**. Assign a name for the enterprise and click on **save and create**.


Once you create your project or projects, you can observe them displayed in your enterprise dashboard. Then click on **Settings** to begin editing your enterprise project.

Here, you can customize your enterprise project. You will be able to add your Enterprise image, edit the project's name, assign the embed options and prices, set the availabilities, layouts, and colors you want to use.
To finalize the project settings, you need to organize everything related to geolocation. Here you can select the map style, the target marker, the map point color and add directions if you wish.
To choose the map to be displayed on the enterprise dashboard, you must set the desired view you want using the map on the right side. Then click on **use this view**. You can also use the icons on the upper right corner of the map to adjust your map view.
You can choose to turn the initial zoom-in and dynamic centering on or off for your enterprise project
The Searchbox tab allows Enterprise administrators to configure how the Planpoint search experience behaves and which filters are available to end users.
This section controls visibility, behavior, and layout of the embedded searchbox used across Enterprise environments.
### Searchbox Settings
The Settings panel on the left defines the core behavior of the searchbox:
* Enable Searchbox: Turns the Enterprise seaarchbox on or off entirely.
* Show Locations Search: Enables location-based searching (e.g. cities, areas, neighborhoods) within the searchbox.
* Result URLs: Allows you to select the language used for result links.
* Searchbox URL: Opens the hosted searchbox in a new browser tab. Useful for testing or sharing.
* Searchbox Embed Code: Generates an embeddable snippet that can be copied and placed into external websites or applications.
* Enable Custom Styling: When enabled, allows advanced styling and branding customization for the search box using either the default theme CSS or a custom CSS file.
### Filters Configuration
The filters panel on the right controls which filters appear in the searchbox and how they are displayed.
Key rules:
* A maximum of **10 filters** can be enabled
* A minimum of **2 filters** is required
* Only the **first 4 enabled filters** are displayed by default in the searchbox UI
* Filters can be reordered via drag-and-drop
* Each filter can be individually enabled or disabled
Go to the **entities** tab to add any project or group project you want.
You can also add external projects by searching for them by their name. Simply go to the **External** tab, type the name of the project, choose the project and send a request. After the request is accepted by the project's owner, you will be able to add it to your enterprise.

You can also manage the members of your team who can administer or edit your enterprise
# Exterior
Source: https://viewerdocs.planpoint.io/exterior
Following the completion of the settings module, proceed to the exterior module to start building your Planpoint.
### Exterior
Your project currently indicates there are no floors added, which is expected at this stage. The next step involves initiating the addition of floors to your project. Simply click on **add floor.**
To draw the first floor, click on **draw** or **redraw** (depending on the case). The **edit** button allows you to change the floor name. And the **reset** button allows you to delete the drawn path and start again.
After clicking on **draw**, a new window will pop up, allowing you to draw the exterior floor of the corresponding level.
* You can rework minor adjustments by clicking on **Redraw** and then using the magnetic snap feature, as you see below.
* You can zoom in or zoom out the image using the + - buttons.
* If you wish to completely redraw the floor, click on **Reset**.
* You can also rename the floor how you want by clicking on **Edit** and editing the name field.
If Day & Night is turned on, you will be able to add an extra view of the project, but you will lose the ability to create a 360° project
# Floors
Source: https://viewerdocs.planpoint.io/floors
In the **Floors** module, selecting the **floorplan** option will grant you access to the corresponding floor plate for each floor:
You can also update the floor plan here:
Selecting **draw floorplan** option will enable you to draw the polygon for the corresponding unit in the floor plan:
After drawing the polygons for a floor, and if other floors share the same floor plate, you can clone the drawings. This will help speed up the process by duplicating the drawn polygons.
After choosing the floor you wish to copy from, it will serve as the reference floor, and its drawings will be cloned. You may choose as many floors as needed. Enabling the **Clone Floorplan** option will copy both the drawings and the floor plans from the selected floor.
Clicking on the **delete** button will remove the entire floor along with its units.
# Homes & Land
Source: https://viewerdocs.planpoint.io/homes-land
The **Homes & Land** option lets you choose between one of three models:
1. A regular image (i.e. money-shot rendering or a bird's-eye view)
2. An interactive SVG
3. Custom polygons directly on our native map
### Regular image
You can map your homes and plots of land to any type of image. In a **Homes & Land project**, you don't draw polygons on top of floors - *there's no concept of floors* - but directly on the home itself or the land itself.
Here's a **[live demo](https://www.planpoint.io/types/single-family).**
Here's a **[live demo](https://app.planpoint.io/Mariners-2/Mariners-2).**
You can use our traditional polygon drawing feature to make these homes or plots of land selectable and linkable to the relevant content.
### Interactive SVG
The interactive SVG model is mostly used for maps that have been created in vector editing software. This model allows pre-existing vector shapes to be selectable inside Planpoint. It also allows other elements to be placed on top of the shapes without affecting the hover color.
Here's a [live demo](https://app.planpoint.io/Mariners3/Mariners).
To use the interactive SVG model, you need to follow a few important requirements when exporting your file.
* All elements you want to be selectable must be grouped together under one group.
* This group must have a unique ID.
* Each element under this group must have it's own unique ID.
If you want to inspect an example file, you can **[download our SVG template here](https://svgshare.com/i/14Pw.svg)**.
### Custom polygons
This third model lets you draw polygons directly on our native map interface.
# Deep Linking to specific units
Source: https://viewerdocs.planpoint.io/link-units
## What Is This?
By default, your Planpoint embed opens at the top-level view when a visitor lands on your page. Deep-linking lets you share a URL that skips straight to a specific **unit** — or to a whole **collection** (a curated group of units, such as “The City Studios”). It's useful for email campaigns, ads, or any page where you want to highlight a particular listing or collection.
***
## Before You Start
This guide assumes you already have a Planpoint embed on your website. If you haven't set that up yet, follow the [How to Embed](https://viewerdocs.planpoint.io/preview-embed#how-to-embed) Planpoint on your website guide first, then come back here.
***
## Step-by-Step
### Step 1 — Find your unit's details
In your Planpoint dashboard, open the project that contains the unit you want to link to and note down:
* **The unit name** — the number or label shown on the unit card (e.g. `202`, `Unit A`)
* **The collection name** *(optional)* — the collection you want to open (e.g. `The City Studios`), exactly as named in your dashboard. Use this to land on a whole collection instead of a single unit
* **The collection link slug** *(optional)* — only needed for [pretty-path links](#pretty-path-links-using-your-page-url). Set it on the collection's **Edit** screen (e.g. `city-studios`)
* **The floor name** *(optional)* — the floor that unit is on (e.g. `Floor 2`). Only needed if the same unit name appears on more than one floor
* **The project name** *(group and enterprise embeds only)* — the full project name as entered in your dashboard (e.g. `Riverside Tower`). Only needed if multiple projects in your group or enterprise share unit names
### Step 2 — Go to the page where your embed lives
Open the page on your website that contains the Planpoint embed. Copy its full URL from the browser address bar.
Example:
```text theme={null}
https://yourwebsite.com/apartments
```
### Step 3 — Add the parameters
Append `?u=` followed by the unit name to the end of your URL:
```text theme={null}
https://yourwebsite.com/apartments?u=202
```
If you also want to specify the floor:
```text theme={null}
https://yourwebsite.com/apartments?f=Floor%202&u=202
```
If you have a group or enterprise embed and want to pin to a specific project:
```text theme={null}
https://yourwebsite.com/apartments?p=Riverside%20Tower&f=Floor%202&u=202
```
To open a whole **collection** instead of a single unit, use `?c=` followed by the collection name:
```text theme={null}
https://yourwebsite.com/apartments?c=The%20City%20Studios
```
> **Shortcut:** In your dashboard, open the project's **Collections** tab and click **Copy link** next to any collection. This copies a ready-made deep-link for that collection — no need to build the URL by hand.
### Step 4 — Test the link
Paste the URL into a new browser tab. The embed should open directly on the unit you specified. If it opens on the main view instead, double-check that the unit name, floor name, and project name match exactly what is in your dashboard.
### Step 5 — Use the link
You can now use this URL anywhere — in an email, an ad, a button on another page, or a QR code.
***
## Parameter Reference
| Param | What it does | Required? |
| ----- | ---------------------------------------- | ----------------------------------------------------------------------------------------- |
| `u` | The unit name to open | Yes — unless you're linking to a collection with `c` |
| `c` | The collection to open | Yes — unless you're linking to a unit with `u` |
| `f` | Narrows the search to a specific floor | Optional |
| `p` | Narrows the search to a specific project | Optional — only needed for group/enterprise embeds when unit names repeat across projects |
***
## Usage by Embed Type
### Project Embed
`f` and `p` are both optional. `?u=202` alone is enough:
```text theme={null}
https://yourwebsite.com/your-page?u=202
```
If the same unit number exists on multiple floors, add `f` to be precise:
```text theme={null}
https://yourwebsite.com/your-page?f=Floor%202&u=202
```
### Group Embed
Works the same way. Add `p` only if multiple projects share the same unit names:
```text theme={null}
https://yourwebsite.com/your-page?p=Riverside%20Tower&f=Floor%202&u=202
```
If the project has the **Skip Floor Step** setting enabled (used for commercial spaces with no floors), use `u` alone — there is no floor to specify:
```text theme={null}
https://yourwebsite.com/your-page?u=Shop%20A
```
### Enterprise Embed
Same as group, but searches across all projects and groups within the enterprise:
```text theme={null}
https://yourwebsite.com/your-page?p=Riverside%20Tower&f=Floor%202&u=202
```
### Linking to a Collection
Collections work with project, group, and enterprise embeds. Use `c` on its own to open straight on a collection:
```text theme={null}
https://yourwebsite.com/your-page?c=The%20City%20Studios
```
In a group or enterprise embed, Planpoint automatically opens the project that owns the collection — you don't need to add `p`.
### Pretty-Path Links (using your page URL)
If your collections live behind tidy URLs like:
```text theme={null}
https://yourwebsite.com/pricelist/city-studios
```
Planpoint can open the matching collection automatically — no `?c=` needed. It reads the **last segment of the page path** (`city-studios` above) and opens the collection whose **link slug** matches it.
Set the slug in your dashboard: open the project's **Collections** tab, click **Edit** on a collection, and set the **Link slug** field (e.g. `city-studios`). It defaults to a slug based on the collection name, so most collections work out of the box — only change it if you want it to match a specific path on your site.
This works on any platform (Webflow, WordPress, Squarespace, etc.) with no extra setup — just add the embed to the page. If a path segment doesn't match any collection, nothing changes and the embed opens normally. A `?c=` in the URL always takes priority over the path.
***
## Matching Rules
* **Unit name (`u`)** — must match exactly, including capitalisation (e.g. `202`, `Unit A`)
* **Floor name (`f`)** — must match exactly, including capitalisation (e.g. `Floor 2`, `Level 3`)
* **Project name (`p`)** — case-insensitive exact match (`p=riverside tower` matches "Riverside Tower")
* **Collection name (`c`)** — case-insensitive exact match (`c=the city studios` matches "The City Studios")
* When `p` is omitted in a group or enterprise embed, the first unit with a matching name is opened
***
## URL Encoding
Spaces and special characters must be percent-encoded in URLs:
| Character | Encoded form |
| --------- | ------------ |
| Space | `%20` |
| `&` | `%26` |
| `#` | `%23` |
So "Floor 2" becomes `Floor%202` and "Riverside Tower" becomes `Riverside%20Tower`.
Most website builders (Webflow, WordPress, Squarespace) handle this automatically when you paste a URL into a link field.
***
## Quick Examples
| Goal | Add to your page URL |
| --------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| Open unit 202 | `?u=202` |
| Open the "The City Studios" collection | `?c=The%20City%20Studios` |
| Open a collection from a pretty path | `yourwebsite.com/pricelist/city-studios` (set the collection's **Link slug** to `city-studios`) |
| Open unit 202 on Floor 2 | `?f=Floor%202&u=202` |
| Open unit 202 in project "Riverside Tower" (group/enterprise) | `?p=Riverside%20Tower&u=202` |
| Open unit 202 on Floor 2 in "Riverside Tower" (group/enterprise) | `?p=Riverside%20Tower&f=Floor%202&u=202` |
| Open commercial space "Shop A" (project with Skip Floor Step enabled) | `?u=Shop%20A` |
***
## Locked or Portal-Protected Viewers
If your viewer uses a Lock Screen or portal login, deep-links still work. The visitor is asked to log in or register first, and is then taken straight to the linked unit or collection — the link is never skipped, so your lead capture stays intact.
# Lock Screen
Source: https://viewerdocs.planpoint.io/lock-screen
The lock screen feature encourage visitors to share their information to gain access to your Planpoint before accessing the project plans. This approach ensures that your sales team can reach out to individuals showing interest in your project.
* Start by enabling the lock screen
* After enabling this feature, within the settings, activate the lock screen feature and specify the parameters you wish to gather from your leads, such as name, email, phone number. Alternatively, configure custom fields for the form according to your preferences.
* You can also enable the priority list feature that allows the customer to organize the list of preferred units of the project
# Media Gallery
Source: https://viewerdocs.planpoint.io/media-gallery
The **Media Gallery** allows you to upload many assets (such as images and plans) and apply them to many units at once.
### Video guide on how to use the Media Gallery
# Members
Source: https://viewerdocs.planpoint.io/members
Invite Admins or Editors to a specific projet or to the full account.
To access the Members module, click on Members on top of the project you wish to add it to in your dashboard. Alternatively, you can search for it within the Sales & Marketing module.

Click on the Invite members button to open up the menu. The table below will list all current members of a specific project.

Choose a permission level and simply enter the user's email address. **Administrators** have all rights except deleting the current project - this is a right only the main initial owner of a Planpoint account has. **Editors** can only edit the information contained inside the unit module. This is particularly useful for sales admin.

The member that you invited will receive an email from [support@planpoint.io](mailto:support@planpoint.io) asking them to accept the invitation by clicking on a hyperlink. They will be required to setup their account by creating a password to login. If invited to multiple projects with the same email, they will be able to see multiple projects in the dashboard and manage them from it.

For users with multiple Planpoints (projects), you can easily grant access to all of your projects at once for admins or editors. Simply navigate to the **Settings** in the top right corner of the dashboard and select the **Members** module to grant and manage permissions.

# Group project
Source: https://viewerdocs.planpoint.io/multi-phase
This article describes how to create a **multi-phase** or **multi-building** project, otherwise called a Group project.
**Prerequisite** You should have all your content already prepared. For more details about the content, please read the [checklist](/checklist).
### Video Guide for Visual Learners
Click on the + button on the upper left side of the dashboard.
These are instructions or content that only pertain to the second step.
These are instructions or content that only pertain to the third step.
## Troubleshooting
Here's how to solve some common problems when setting up your project.
Update to Node v18. Run `mintlify install` and try again.
Go to the `C:/Users/Username/.mintlify/` directory and remove the `mint` folder. Then Open the Git Bash in this location and run `git clone
https://github.com/mintlify/mint.git`.
Repeat step 3.
Try navigating to the root of your device and delete the \~/.mintlify folder. Then run `mintlify dev` again.
# Payments
Source: https://viewerdocs.planpoint.io/payments
Easily set up and accept payments for deposits and reservations in over 135 currencies and 40+ payment methods
Setting up your payments is straightforward, and best of all, you can customize various aspects of the payment process, as outlined below.
### How to Set Up Your Payments
**1. Select Your Project**\
Navigate to the project you wish to set up payments for and click on **Payments**.
**2. Access the Payments Module**\
After clicking on **Payments**, you will be redirected to the Payments module.
**3. Configure Payment Settings**\
In the Payments module, you can customize the following settings:
You will find two main sections in this module: the Planpoint section, where you can set up and customize your payments, and the Customers section, where you can view the history of all payments received.
The **Amount to be Charged** allows you to specify the charge amount. You can set a uniform amount for all units or customize the amount per unit. Additionally, you can select from over 135 supported currencies.
The **Button Information** section allows you to customize the text displayed on the payment button in the plan viewer. Additionally, you can upload an SVG file to use as an icon.
The **Statement Descriptor** explains the charges or payments that appear on your customers' bank statements.
The **Success Payment** allows you to provide a title and description to communicate the details of the reserved unit to your clients.
Once the user clicks the Payment button, the unit status will switch to **On Hold**. This status is temporary and unique to this process. It will remain On Hold for X minutes or until the user completes the payment, at which point the status will update to Reserved or Purchased.
Please note that you can enable or disable the payment options in the Settings module, under Style and Embed.
# Style & Embed
Source: https://viewerdocs.planpoint.io/preview-embed
In this module, you can preview your project, edit hosting options and customize the style of your Planpoint Viewer.
### How to customize the style
If you want deeper style customization capabilities, check out our [Custom CSS article](https://viewerdocs.planpoint.io/custom-css). You will need a developer for this.
### How to embed
Planpoint can be embedded into any kind of website, from custom HTML/CSS/JS to platforms like Webflow, SquareSpace, Wix or WordPress. A Planpoint embed code is simply a few lines of HTML and JavaScript.

Paste the code where you want your Planpoint Viewer to appear. The Planpoint Viewer will obey the width of the parent element in which it's placed in. The height will be decided by the content inside the viewer. For example, if the building image's proportions are more vertical than horizontal, Planpoint Viewer will occupy more height on your page. You can stylize the parent element however you see fit, for example by adding padding or giving it a fixed width.
> A Responsive Inline Channel will horizontally fill the width of its parent element and grow vertically based on the content of the channel.

### Video Guide on how to embed
### Step-by-step guide
To access the **Style & Embed** module, locate the project you wish to access in your Planpoint dashboard and click on **the Style & Embed** button.

To preview your work in progress, simply choose the desired **language** output, assign the **namespace**, **project name**, plan viewer **theme** and click on **Open a new tab**.

Assign a permanent namespace and project name before embedding the code. If these are modified, the code will cease to function, necessitating the need for re-embedding.
A new window will open up with the live preview of your current project. Test your plan viewer and make changes accordingly. You will need to reload the preview in order for changes to take effect.

Once you're satisfied with your plan viewer, simply select the desired language **output** and **theme**, and click on **Click to copy**.
* You can export as many instances of your plan viewer as you wish, in 5 languages (English, French, Spanish, Chinese and German). Each language will have it's own script to copy-paste.
* You should paste the code wherever you want your plan viewer to appear in your website. The plan viewer will fill the parent element in which it's inserted. The code will work on every major website platform, from Wordpress to Wix and custom HTML.

# Go
Source: https://viewerdocs.planpoint.io/sdk-documentation/go
# planpoint-sdk-go
Official Go SDK for the [Planpoint](https://app.planpoint.io) API.
## Installation
```bash theme={null}
go get github.com/planpoint-io/planpoint-sdk-go
```
> **Note:** Responses use `JSON200` or `JSON201` depending on the endpoint. `login`, `getFloors`, and `getLeads` return `JSON200`. All other endpoints return `JSON201`.
## Quick Start
```go theme={null}
package main
import (
"context"
"fmt"
"net/http"
openapi_types "github.com/oapi-codegen/runtime/types"
planpoint "github.com/planpoint-io/planpoint-sdk-go"
)
func main() {
ctx := context.Background()
// 1. Authenticate
unauthClient, _ := planpoint.NewClientWithResponses("https://app.planpoint.io")
pass := "yourpassword"
loginResp, _ := unauthClient.LoginWithResponse(ctx, planpoint.LoginJSONRequestBody{
Username: openapi_types.Email("you@example.com"),
Password: &pass,
})
token := loginResp.JSON200.AccessToken
// 2. Create an authenticated client
client, _ := planpoint.NewClientWithResponses("https://app.planpoint.io",
planpoint.WithRequestEditorFn(func(ctx context.Context, req *http.Request) error {
req.Header.Set("Authorization", "Bearer "+token)
return nil
}),
)
// 3. Fetch your projects
projectsResp, _ := client.GetMyProjectsWithResponse(ctx)
fmt.Println(projectsResp.JSON201)
}
```
## API Reference
### Authentication
#### `LoginWithResponse(ctx, body)`
Authenticate and receive a JWT token.
| Field | Type | Required | Description |
| ------------------ | --------------------- | -------- | ------------------------ |
| `body.Username` | `openapi_types.Email` | Yes | User email |
| `body.Password` | `*string` | No | User password |
| `body.Impersonate` | `*bool` | No | Impersonate another user |
**Returns:** `*LoginResponse` — `{ JSON200: *AuthResponse{ AccessToken, Message } }`
***
### Projects
#### `GetMyProjectsWithResponse(ctx)`
List all projects belonging to the authenticated user. Requires auth.
**Returns:** `*GetMyProjectsResponse` — `{ JSON201: *[]ProjectSummary }`
`ProjectSummary`: `{ Id, Name, Namespace?, HostName?, Status?, Paused?, CreatedAt? }`
***
#### `FindProjectWithResponse(ctx, body)`
Find a public project by namespace and hostname. No auth required.
| Field | Type | Required | Description |
| ---------------- | -------- | -------- | ----------------- |
| `body.Namespace` | `string` | Yes | Project namespace |
| `body.HostName` | `string` | Yes | Allowed hostname |
**Returns:** `*FindProjectResponse` — `{ JSON201: *Project }`
***
#### `GetProjectWithResponse(ctx, id)`
Get a project by ID. Requires auth.
| Param | Type | Required | Description |
| ----- | -------- | -------- | ---------------- |
| `id` | `string` | Yes | Project ObjectId |
**Returns:** `*GetProjectResponse` — `{ JSON201: *Project }`
***
#### `UpdateProjectWithResponse(ctx, id, body)`
Update project settings. Requires auth.
| Param | Type | Required | Description |
| ----- | -------- | -------- | ---------------- |
| `id` | `string` | Yes | Project ObjectId |
**Returns:** `*UpdateProjectResponse` — `{ JSON201: *Project }`
***
### Groups
#### `GetGroupsWithResponse(ctx)`
List all groups for the authenticated user. Requires auth.
**Returns:** `*GetGroupsResponse` — `{ JSON201: *GroupsListResponse{ Records, Count } }`
***
#### `GetGroupWithResponse(ctx, id)`
Get a group by ID. Requires auth.
| Param | Type | Required | Description |
| ----- | -------- | -------- | -------------- |
| `id` | `string` | Yes | Group ObjectId |
**Returns:** `*GetGroupResponse` — `{ JSON201: *Group }`
***
#### `CreateGroupWithResponse(ctx, body)`
Create a new group. Requires auth.
**Returns:** `*CreateGroupResponse` — `{ JSON201: *Group }`
***
#### `UpdateGroupWithResponse(ctx, id, body)`
Update a group. Requires auth.
**Returns:** `*UpdateGroupResponse` — `{ JSON201: *Group }`
***
### Floors
#### `GetFloorsWithResponse(ctx, params)`
List all floors for a project. Requires auth.
| Param | Type | Required | Description |
| ------------ | -------- | -------- | ---------------- |
| `params.Pid` | `string` | Yes | Project ObjectId |
**Returns:** `*GetFloorsResponse` — `{ JSON200: *[]FloorFull }`
`FloorFull`: `{ Id, Name?, Project, Level?, Position?, Path?, Image? }`
***
#### `GetFloorWithResponse(ctx, id)`
Get a floor by ID. Requires auth.
**Returns:** `*GetFloorResponse` — `{ JSON201: *FloorFull }`
***
#### `CreateFloorWithResponse(ctx, body)`
Create a new floor. Requires auth.
**Returns:** `*CreateFloorResponse` — `{ JSON201: *FloorFull }`
***
#### `UpdateFloorWithResponse(ctx, id, body)`
Update a floor. Requires auth.
**Returns:** `*UpdateFloorResponse` — `{ JSON201: *FloorFull }`
***
### Units
#### `GetUnitsWithResponse(ctx, params)`
List all units for a project. Requires auth.
| Param | Type | Required | Description |
| ------------ | -------- | -------- | ---------------- |
| `params.Pid` | `string` | Yes | Project ObjectId |
**Returns:** `*GetUnitsResponse` — `{ JSON201: *UnitsListResponse{ Records, Count } }`
`UnitFull`: `{ Id, Floor, Name?, UnitNumber?, Status?, Price?, Bed?, Bath?, Sqft?, Model? }`
`Status` values: `Available` | `OnHold` | `Sold` | `Leased` | `Unavailable`
***
#### `GetUnitWithResponse(ctx, id)`
Get a unit by ID. Requires auth.
**Returns:** `*GetUnitResponse` — `{ JSON201: *UnitFull }`
***
#### `CreateUnitWithResponse(ctx, body)`
Create a new unit. Requires auth.
**Returns:** `*CreateUnitResponse` — `{ JSON201: *UnitFull }`
***
#### `UpdateUnitWithResponse(ctx, id, body)`
Update a unit. Requires auth.
**Returns:** `*UpdateUnitResponse` — `{ JSON201: *UnitFull }`
***
#### `DeleteUnitWithResponse(ctx, id)`
Delete a unit. Requires auth.
**Returns:** `*DeleteUnitResponse`
***
#### `BatchUpdateUnitsWithResponse(ctx, body)`
Update multiple units at once. Requires auth.
**Returns:** `*BatchUpdateUnitsResponse` — `{ JSON201: *[]UnitFull }`
***
### Leads
#### `GetLeadsWithResponse(ctx, params)`
List all leads for a project. Requires auth.
| Param | Type | Required | Description |
| ------------ | -------- | -------- | ---------------- |
| `params.Pid` | `string` | Yes | Project ObjectId |
**Returns:** `*GetLeadsResponse` — `{ JSON200: *[]Lead }`
`Lead`: `{ Id?, Name?, Email?, Phone?, Message?, Unit?, CreatedAt? }`
***
## Links
* **[Install: go get github.com/planpoint-io/planpoint-sdk-go](https://github.com/planpoint-io/planpoint-sdk-go)**
* [Planpoint App](https://app.planpoint.io)
* [TypeScript SDK on npm](https://www.npmjs.com/package/@planpoint/sdk)
* [Python SDK on PyPI](https://pypi.org/project/planpoint-sdk)
* [PHP SDK on GitHub](https://github.com/planpoint-io/planpoint-sdk-php)
* [Java SDK on GitHub](https://github.com/planpoint-io/planpoint-sdk-java)
# Introduction to Planpoint SDK
Source: https://viewerdocs.planpoint.io/sdk-documentation/introduction
Integrate real estate project data into your application with the official Planpoint SDK.
## Overview
The Planpoint SDK gives developers programmatic access to the Planpoint platform — the same data that powers the interactive floor plan viewer on your website.
Use it to build custom dashboards, automate unit management, sync availability with your CRM, or embed project data into any application.
## What You Can Do
Log in with your Planpoint credentials and receive a JWT token to make authenticated requests.
List your projects, retrieve full project details including floors, units, and display settings.
Create, update, delete, and batch-update units. Control status, pricing, bedrooms, and more.
Create and update floor plans, set display order, and attach SVG paths or images.
Organize multiple projects under groups. Control team access with owner, admin, and editor roles.
Retrieve leads submitted through your project's contact forms, including contact details and unit interest.
## Core Concepts
### Authentication
All requests (except `findProject`) require a Bearer token. Obtain one by calling `login` with your Planpoint credentials. The token is a JWT and should be passed in the `Authorization` header of every subsequent request.
### Projects
A **Project** is the top-level entity. It contains floors, units, branding settings, and display configuration. Projects are identified by an `_id` (ObjectId) and a `namespace` (URL-friendly slug).
### Floors & Units
**Floors** are ordered levels within a project. Each floor contains **Units** — the individual listings with attributes like status, price, bedrooms, bathrooms, and square footage.
Unit status can be one of: `Available`, `OnHold`, `Sold`, `Leased`, or `Unavailable`.
### Groups
**Groups** allow you to organize multiple projects under a single entity (e.g. a development company). Groups support role-based access: `owner`, `admin`, and `editor`.
### Leads
**Leads** are contact form submissions captured through the Planpoint embed. Each lead includes name, email, phone, message, and the unit of interest.
## Available SDKs
Install via npm. Works in Node.js and browser environments.
```bash theme={null}
npm install @planpoint/sdk
```
Install via pip. Works with Python 3.8+.
```bash theme={null}
pip install planpoint-sdk
```
Install via `go get`. Works with Go 1.22+.
```bash theme={null}
go get github.com/planpoint-io/planpoint-sdk-go
```
Install via Composer. Works with PHP 8.1+.
```bash theme={null}
composer require planpoint/planpoint
```
Install via Maven or Gradle using JitPack. Works with Java 17+.
## Base URL
All API requests are made to:
```
https://app.planpoint.io
```
## Authentication Flow
```mermaid theme={null}
sequenceDiagram
participant App
participant Planpoint API
App->>Planpoint API: POST /api/users/login
Planpoint API-->>App: { access_token }
App->>Planpoint API: GET /api/projects/mine (Bearer token)
Planpoint API-->>App: ProjectSummary[]
```
# JavaScript
Source: https://viewerdocs.planpoint.io/sdk-documentation/java-script
# @planpoint/sdk
Official TypeScript SDK for the [Planpoint](https://app.planpoint.io) API.
## Installation
```bash theme={null}
npm install @planpoint/sdk
```
## Quick Start
```ts theme={null}
import { login, getMyProjects, createClient } from "@planpoint/sdk";
import { createClient } from "@hey-api/client-fetch";
// 1. Authenticate
const { data } = await login({
body: { username: "you@example.com", password: "yourpassword" },
});
// 2. Create an authenticated client
const client = createClient({
baseUrl: "https://app.planpoint.io",
headers: { Authorization: `Bearer ${data.access_token}` },
});
// 3. Fetch your projects
const { data: projects } = await getMyProjects({ client });
console.log(projects); // ProjectSummary[]
```
## API Reference
### Authentication
#### `login(options)`
Authenticate and receive a JWT token.
| Param | Type | Required | Description |
| ------------------ | --------- | -------- | ------------------------ |
| `body.username` | `string` | Yes | User email |
| `body.password` | `string` | No | User password |
| `body.impersonate` | `boolean` | No | Impersonate another user |
**Returns:** `LoginResponse` — `{ message: string, access_token: string }`
***
### Projects
#### `getMyProjects(options)`
List all projects belonging to the authenticated user. Requires auth.
**Returns:** `ProjectSummary[]` — `{ _id, name, namespace?, hostName?, status?, paused?, createdAt? }`
***
#### `findProject(options)`
Find a public project by namespace and hostname. No auth required.
| Param | Type | Required | Description |
| ---------------- | -------- | -------- | ----------------- |
| `body.namespace` | `string` | Yes | Project namespace |
| `body.hostName` | `string` | Yes | Allowed hostname |
**Returns:** `Project` — full project with floors, units, settings
***
#### `getProject(options)`
Get a project by ID. Requires auth.
| Param | Type | Required | Description |
| --------- | -------- | -------- | ---------------- |
| `path.id` | `string` | Yes | Project ObjectId |
**Returns:** `Project`
***
#### `updateProject(options)`
Update project settings. Requires auth.
| Param | Type | Required | Description |
| --------- | ------------------------- | -------- | ---------------- |
| `path.id` | `string` | Yes | Project ObjectId |
| `body` | `Record` | Yes | Fields to update |
**Returns:** `Project`
***
### Groups
#### `getGroups(options)`
List all groups for the authenticated user.
**Returns:** `GroupsListResponse` — `{ records: Group[], count: number }`
***
#### `getGroup(options)`
Get a group by ID.
| Param | Type | Required | Description |
| --------- | -------- | -------- | -------------- |
| `path.id` | `string` | Yes | Group ObjectId |
**Returns:** `Group` — `{ _id, name, namespace?, hostName?, type?, projects?, isOwner?, isAdmin?, isEditor? }`
***
#### `createGroup(options)`
Create a new group.
| Param | Type | Required | Description |
| ------------------- | -------- | -------- | ------------- |
| `body.name` | `string` | Yes | Group name |
| `body.namespace` | `string` | No | Namespace |
| `body.hostName` | `string` | No | Hostname |
| `body.type` | `string` | No | Group type |
| `body.propertyType` | `string` | No | Property type |
**Returns:** `Group`
***
#### `updateGroup(options)`
Update a group.
| Param | Type | Required | Description |
| --------- | ------------------------- | -------- | ---------------- |
| `path.id` | `string` | Yes | Group ObjectId |
| `body` | `Record` | Yes | Fields to update |
**Returns:** `Group`
***
### Floors
#### `getFloors(options)`
List all floors for a project.
| Param | Type | Required | Description |
| ----------- | -------- | -------- | ---------------- |
| `query.pid` | `string` | Yes | Project ObjectId |
**Returns:** `FloorFull[]` — `{ _id, name?, project, level?, position?, path?, image? }`
***
#### `getFloor(options)`
Get a floor by ID.
| Param | Type | Required | Description |
| --------- | -------- | -------- | -------------- |
| `path.id` | `string` | Yes | Floor ObjectId |
**Returns:** `FloorFull`
***
#### `createFloor(options)`
Create a new floor.
| Param | Type | Required | Description |
| ----------------------- | ---------- | -------- | ---------------- |
| `body.project._id` | `string` | Yes | Project ObjectId |
| `body.name` | `string` | Yes | Floor name |
| `body.position` | `number` | No | Display order |
| `body.path` | `string` | No | SVG/image path |
| `body.alternativePaths` | `string[]` | No | Additional paths |
**Returns:** `FloorFull`
***
#### `updateFloor(options)`
Update a floor.
| Param | Type | Required | Description |
| --------- | ------------------------- | -------- | ---------------- |
| `path.id` | `string` | Yes | Floor ObjectId |
| `body` | `Record` | Yes | Fields to update |
**Returns:** `FloorFull`
***
### Units
#### `getUnits(options)`
List all units for a project.
| Param | Type | Required | Description |
| ----------- | -------- | -------- | ---------------- |
| `query.pid` | `string` | Yes | Project ObjectId |
**Returns:** `UnitsListResponse` — `{ records: UnitFull[], count: number }`
`UnitFull`: `{ _id, floor, name?, unitNumber?, status?, price?, bed?, bath?, sqft?, model?, orientation?, parking? }`
`status` values: `Available` | `OnHold` | `Sold` | `Leased` | `Unavailable`
***
#### `getUnit(options)`
Get a unit by ID.
| Param | Type | Required | Description |
| --------- | -------- | -------- | ------------- |
| `path.id` | `string` | Yes | Unit ObjectId |
**Returns:** `UnitFull`
***
#### `createUnit(options)`
Create a new unit.
| Param | Type | Required | Description |
| ---------------- | -------- | -------- | -------------- |
| `body.floor._id` | `string` | Yes | Floor ObjectId |
**Returns:** `UnitFull`
***
#### `updateUnit(options)`
Update a unit.
| Param | Type | Required | Description |
| --------- | ------------------------- | -------- | ---------------- |
| `path.id` | `string` | Yes | Unit ObjectId |
| `body` | `Record` | Yes | Fields to update |
**Returns:** `UnitFull`
***
#### `deleteUnit(options)`
Delete a unit.
| Param | Type | Required | Description |
| --------- | -------- | -------- | ------------- |
| `path.id` | `string` | Yes | Unit ObjectId |
**Returns:** `{ message: string }`
***
#### `batchUpdateUnits(options)`
Update multiple units at once.
| Param | Type | Required | Description |
| ---------------- | ------------------------- | -------- | ------------------------ |
| `body.ids` | `string[]` | Yes | Unit ObjectIds to update |
| `body.patchData` | `Record` | Yes | Fields to apply to all |
**Returns:** `UnitFull[]`
***
### Leads
#### `getLeads(options)`
List all leads for a project.
| Param | Type | Required | Description |
| ----------- | -------- | -------- | ---------------- |
| `query.pid` | `string` | Yes | Project ObjectId |
**Returns:** `Lead[]` — `{ _id?, name?, email?, phone?, message?, unit?, createdAt? }`
***
## TypeScript Support
All methods are fully typed. Import types directly:
```ts theme={null}
import type {
Project,
ProjectSummary,
Group,
GroupsListResponse,
FloorFull,
UnitFull,
UnitsListResponse,
Lead,
LoginResponse,
} from "@planpoint/sdk";
```
## Links
* [**Install on npm: npm install @planpoint/sdk**](https://www.npmjs.com/package/@planpoint/sdk)
* [Planpoint App](https://app.planpoint.io)
* [Python SDK on PyPI](https://pypi.org/project/planpoint-sdk)
* [Go SDK on GitHub](https://github.com/planpoint-io/planpoint-sdk-go)
* [PHP SDK on GitHub](https://github.com/planpoint-io/planpoint-sdk-php)
* [Java SDK on GitHub](https://github.com/planpoint-io/planpoint-sdk-java)
# PHP
Source: https://viewerdocs.planpoint.io/sdk-documentation/php
# planpoint-sdk-php
Official PHP SDK for the [Planpoint](https://app.planpoint.io) API.
## Requirements
* PHP 8.1+
* Composer
## Installation
Create a `composer.json` in your project:
```json theme={null}
{
"require": { "planpoint/planpoint": "dev-main" },
"repositories": [
{
"type": "vcs",
"url": "https://github.com/planpoint-io/planpoint-sdk-php"
}
],
"minimum-stability": "dev"
}
```
Then install:
```bash theme={null}
composer install
```
> **SSL Note:** If you get a cURL SSL certificate error on Windows, download the CA bundle and add to your `php.ini`:
>
> ```ini theme={null}
> curl.cainfo=C:\path\to\cacert.pem
> openssl.cafile=C:\path\to\cacert.pem
> ```
>
> Download `cacert.pem` from [curl.se/ca/cacert.pem](https://curl.se/ca/cacert.pem).
## Quick Start
```php theme={null}
authentication->login(
new \Planpoint\Authentication\Requests\LoginBody([
'username' => 'you@example.com',
'password' => 'yourpassword',
])
);
$token = $loginResp->accessToken;
// 2. Create an authenticated client
$client = new PlanpointClient($token);
// 3. Fetch your projects
$projects = $client->projects->getMyProjects();
foreach ($projects as $project) {
echo $project->name . "\n";
}
```
## API Reference
### Authentication
#### `$client->authentication->login(LoginBody $request)`
Authenticate and receive a JWT token.
| Field | Type | Required | Description |
| ------------- | -------- | -------- | ------------------------ |
| `username` | `string` | Yes | User email |
| `password` | `string` | No | User password |
| `impersonate` | `bool` | No | Impersonate another user |
**Returns:** `LoginResponse` — `->accessToken: string`
***
### Projects
#### `$client->projects->getMyProjects()`
List all projects belonging to the authenticated user. Requires auth.
**Returns:** `ProjectSummary[]` — `->name`, `->namespace`, `->hostName`, `->status`, `->paused`, `->createdAt`
***
#### `$client->projects->findProject(FindProjectRequest $request)`
Find a public project by namespace and hostname. No auth required.
| Field | Type | Required | Description |
| ----------- | -------- | -------- | ----------------- |
| `namespace` | `string` | Yes | Project namespace |
| `hostName` | `string` | Yes | Allowed hostname |
**Returns:** `Project` — full project with floors, units, settings
***
#### `$client->projects->getProject(string $id)`
Get a project by ID. Requires auth.
| Param | Type | Required | Description |
| ----- | -------- | -------- | ---------------- |
| `$id` | `string` | Yes | Project ObjectId |
**Returns:** `Project`
***
#### `$client->projects->updateProject(string $id, UpdateProjectRequest $request)`
Update project settings. Requires auth.
**Returns:** `Project`
***
### Groups
#### `$client->groups->getGroups()`
List all groups for the authenticated user. Requires auth.
**Returns:** `GroupsListResponse` — `->records: Group[]`, `->count: int`
***
#### `$client->groups->getGroup(string $id)`
Get a group by ID. Requires auth.
**Returns:** `Group` — `->name`, `->namespace`, `->hostName`, `->type`, `->projects`, `->isOwner`, `->isAdmin`, `->isEditor`
***
#### `$client->groups->createGroup(CreateGroupRequest $request)`
Create a new group. Requires auth.
| Field | Type | Required | Description |
| -------------- | -------- | -------- | ------------- |
| `name` | `string` | Yes | Group name |
| `namespace` | `string` | No | Namespace |
| `hostName` | `string` | No | Hostname |
| `type` | `string` | No | Group type |
| `propertyType` | `string` | No | Property type |
**Returns:** `Group`
***
#### `$client->groups->updateGroup(string $id, UpdateGroupRequest $request)`
Update a group. Requires auth.
**Returns:** `Group`
***
### Floors
#### `$client->floors->getFloors(GetFloorsRequest $request)`
List all floors for a project. Requires auth.
| Field | Type | Required | Description |
| ----- | -------- | -------- | ---------------- |
| `pid` | `string` | Yes | Project ObjectId |
**Returns:** `FloorFull[]` — `->name`, `->project`, `->level`, `->position`, `->path`, `->image`
***
#### `$client->floors->getFloor(string $id)`
Get a floor by ID. Requires auth.
**Returns:** `FloorFull`
***
#### `$client->floors->createFloor(CreateFloorRequest $request)`
Create a new floor. Requires auth.
| Field | Type | Required | Description |
| ------------------ | ---------- | -------- | --------------------------- |
| `project` | `array` | Yes | `['_id' => '']` |
| `name` | `string` | Yes | Floor name |
| `position` | `int` | No | Display order |
| `path` | `string` | No | SVG/image path |
| `alternativePaths` | `string[]` | No | Additional paths |
**Returns:** `FloorFull`
***
#### `$client->floors->updateFloor(string $id, UpdateFloorRequest $request)`
Update a floor. Requires auth.
**Returns:** `FloorFull`
***
### Units
#### `$client->units->getUnits(GetUnitsRequest $request)`
List all units for a project. Requires auth.
| Field | Type | Required | Description |
| ----- | -------- | -------- | ---------------- |
| `pid` | `string` | Yes | Project ObjectId |
**Returns:** `UnitsListResponse` — `->records: UnitFull[]`, `->count: int`
`UnitFull` fields: `->floor`, `->name`, `->unitNumber`, `->status`, `->price`, `->bed`, `->bath`, `->sqft`, `->model`, `->orientation`, `->parking`
`status` values: `Available` | `OnHold` | `Sold` | `Leased` | `Unavailable`
***
#### `$client->units->getUnit(string $id)`
Get a unit by ID. Requires auth.
**Returns:** `UnitFull`
***
#### `$client->units->createUnit(CreateUnitRequest $request)`
Create a new unit. Requires auth.
| Field | Type | Required | Description |
| ------- | ------- | -------- | ------------------------- |
| `floor` | `array` | Yes | `['_id' => '']` |
**Returns:** `UnitFull`
***
#### `$client->units->updateUnit(string $id, UpdateUnitRequest $request)`
Update a unit. Requires auth.
**Returns:** `UnitFull`
***
#### `$client->units->deleteUnit(string $id)`
Delete a unit. Requires auth.
**Returns:** `array` — `['message' => string]`
***
#### `$client->units->batchUpdateUnits(BatchUpdateUnitsRequest $request)`
Update multiple units at once. Requires auth.
| Field | Type | Required | Description |
| ----------- | ---------- | -------- | ------------------------ |
| `ids` | `string[]` | Yes | Unit ObjectIds to update |
| `patchData` | `array` | Yes | Fields to apply to all |
**Returns:** `UnitFull[]`
***
### Leads
#### `$client->leads->getLeads(GetLeadsRequest $request)`
List all leads for a project. Requires auth.
| Field | Type | Required | Description |
| ----- | -------- | -------- | ---------------- |
| `pid` | `string` | Yes | Project ObjectId |
**Returns:** `Lead[]` — `->name`, `->email`, `->phone`, `->message`, `->unit`, `->createdAt`
***
## Links
* **[Install via GitHub (Composer)](https://github.com/planpoint-io/planpoint-sdk-php)**
* [Planpoint App](https://app.planpoint.io)
* [TypeScript SDK on npm](https://www.npmjs.com/package/@planpoint/sdk)
* [Python SDK on PyPI](https://pypi.org/project/planpoint-sdk)
* [Go SDK on GitHub](https://github.com/planpoint-io/planpoint-sdk-go)
* [Java SDK on GitHub](https://github.com/planpoint-io/planpoint-sdk-java)
# Python
Source: https://viewerdocs.planpoint.io/sdk-documentation/untitled-page
# planpoint-sdk
Official Python SDK for the [Planpoint](https://app.planpoint.io) API.
## Installation
```bash theme={null}
pip install planpoint-sdk
```
## Quick Start
```python theme={null}
import planpoint
# 1. Authenticate
auth_client = planpoint.PlanpointApi(token=None)
login_response = auth_client.authentication.login(
username="you@example.com",
password="yourpassword"
)
token = login_response.token
# 2. Create an authenticated client
client = planpoint.PlanpointApi(token=token)
# 3. Fetch your projects
projects = client.projects.get_my_projects()
for p in projects:
print(p.name, p.namespace)
```
## API Reference
### Authentication
#### `client.authentication.login(**kwargs)`
Authenticate and receive a JWT token.
| Param | Type | Required | Description |
| ------------- | ------ | -------- | ------------------------ |
| `username` | `str` | Yes | User email |
| `password` | `str` | No | User password |
| `impersonate` | `bool` | No | Impersonate another user |
**Returns:** `LoginResponse` — `.token: str`
***
### Projects
#### `client.projects.get_my_projects()`
List all projects belonging to the authenticated user. Requires auth.
**Returns:** `list[ProjectSummary]` — `.name`, `.namespace`, `.host_name`, `.status`, `.paused`, `.created_at`
***
#### `client.projects.find_project(**kwargs)`
Find a public project by namespace and hostname. No auth required.
| Param | Type | Required | Description |
| ----------- | ----- | -------- | ----------------- |
| `namespace` | `str` | Yes | Project namespace |
| `host_name` | `str` | Yes | Allowed hostname |
**Returns:** `Project` — full project with floors, units, settings
***
#### `client.projects.get_project(**kwargs)`
Get a project by ID. Requires auth.
| Param | Type | Required | Description |
| ----- | ----- | -------- | ---------------- |
| `id` | `str` | Yes | Project ObjectId |
**Returns:** `Project`
***
#### `client.projects.update_project(**kwargs)`
Update project settings. Requires auth.
| Param | Type | Required | Description |
| --------- | ------ | -------- | ---------------- |
| `id` | `str` | Yes | Project ObjectId |
| `request` | `dict` | Yes | Fields to update |
**Returns:** `Project`
***
### Groups
#### `client.groups.get_groups()`
List all groups for the authenticated user.
**Returns:** `GroupsListResponse` — `.records: list[Group]`, `.count: int`
***
#### `client.groups.get_group(**kwargs)`
Get a group by ID.
| Param | Type | Required | Description |
| ----- | ----- | -------- | -------------- |
| `id` | `str` | Yes | Group ObjectId |
**Returns:** `Group` — `.name`, `.namespace`, `.host_name`, `.type`, `.projects`, `.is_owner`, `.is_admin`, `.is_editor`
***
#### `client.groups.create_group(**kwargs)`
Create a new group.
| Param | Type | Required | Description |
| --------------- | ----- | -------- | ------------- |
| `name` | `str` | Yes | Group name |
| `namespace` | `str` | No | Namespace |
| `host_name` | `str` | No | Hostname |
| `type` | `str` | No | Group type |
| `property_type` | `str` | No | Property type |
**Returns:** `Group`
***
#### `client.groups.update_group(**kwargs)`
Update a group.
| Param | Type | Required | Description |
| --------- | ------ | -------- | ---------------- |
| `id` | `str` | Yes | Group ObjectId |
| `request` | `dict` | Yes | Fields to update |
**Returns:** `Group`
***
### Floors
#### `client.floors.get_floors(**kwargs)`
List all floors for a project.
| Param | Type | Required | Description |
| ----- | ----- | -------- | ---------------- |
| `pid` | `str` | Yes | Project ObjectId |
**Returns:** `list[FloorFull]` — `.name`, `.project`, `.level`, `.position`, `.path`, `.image`
***
#### `client.floors.get_floor(**kwargs)`
Get a floor by ID.
| Param | Type | Required | Description |
| ----- | ----- | -------- | -------------- |
| `id` | `str` | Yes | Floor ObjectId |
**Returns:** `FloorFull`
***
#### `client.floors.create_floor(**kwargs)`
Create a new floor.
| Param | Type | Required | Description |
| ------------------- | ----------- | -------- | ------------------------- |
| `project` | `dict` | Yes | `{"_id": ""}` |
| `name` | `str` | Yes | Floor name |
| `position` | `int` | No | Display order |
| `path` | `str` | No | SVG/image path |
| `alternative_paths` | `list[str]` | No | Additional paths |
**Returns:** `FloorFull`
***
#### `client.floors.update_floor(**kwargs)`
Update a floor.
| Param | Type | Required | Description |
| --------- | ------ | -------- | ---------------- |
| `id` | `str` | Yes | Floor ObjectId |
| `request` | `dict` | Yes | Fields to update |
**Returns:** `FloorFull`
***
### Units
#### `client.units.get_units(**kwargs)`
List all units for a project.
| Param | Type | Required | Description |
| ----- | ----- | -------- | ---------------- |
| `pid` | `str` | Yes | Project ObjectId |
**Returns:** `UnitsListResponse` — `.records: list[UnitFull]`, `.count: int`
`UnitFull` fields: `.floor`, `.name`, `.unit_number`, `.status`, `.price`, `.bed`, `.bath`, `.sqft`, `.model`, `.orientation`, `.parking`
`status` values: `Available` | `OnHold` | `Sold` | `Leased` | `Unavailable`
***
#### `client.units.get_unit(**kwargs)`
Get a unit by ID.
| Param | Type | Required | Description |
| ----- | ----- | -------- | ------------- |
| `id` | `str` | Yes | Unit ObjectId |
**Returns:** `UnitFull`
***
#### `client.units.create_unit(**kwargs)`
Create a new unit.
| Param | Type | Required | Description |
| ------- | ------ | -------- | ----------------------- |
| `floor` | `dict` | Yes | `{"_id": ""}` |
**Returns:** `UnitFull`
***
#### `client.units.update_unit(**kwargs)`
Update a unit.
| Param | Type | Required | Description |
| --------- | ------ | -------- | ---------------- |
| `id` | `str` | Yes | Unit ObjectId |
| `request` | `dict` | Yes | Fields to update |
**Returns:** `UnitFull`
***
#### `client.units.delete_unit(**kwargs)`
Delete a unit.
| Param | Type | Required | Description |
| ----- | ----- | -------- | ------------- |
| `id` | `str` | Yes | Unit ObjectId |
**Returns:** `dict` — `{ "message": str }`
***
#### `client.units.batch_update_units(**kwargs)`
Update multiple units at once.
| Param | Type | Required | Description |
| ------------ | ----------- | -------- | ------------------------ |
| `ids` | `list[str]` | Yes | Unit ObjectIds to update |
| `patch_data` | `dict` | Yes | Fields to apply to all |
**Returns:** `list[UnitFull]`
***
### Leads
#### `client.leads.get_leads(**kwargs)`
List all leads for a project.
| Param | Type | Required | Description |
| ----- | ----- | -------- | ---------------- |
| `pid` | `str` | Yes | Project ObjectId |
**Returns:** `list[Lead]` — `.name`, `.email`, `.phone`, `.message`, `.unit`, `.created_at`
***
## Links
* [**Install on PyPI: pip install planpoint-sdk**](https://pypi.org/project/planpoint-sdk)
* [Planpoint App](https://app.planpoint.io)
* [TypeScript SDK on npm](https://www.npmjs.com/package/@planpoint/sdk)
* [Go SDK on GitHub](https://github.com/planpoint-io/planpoint-sdk-go)
* [PHP SDK on GitHub](https://github.com/planpoint-io/planpoint-sdk-php)
* [Java SDK on GitHub](https://github.com/planpoint-io/planpoint-sdk-java)
# Settings
Source: https://viewerdocs.planpoint.io/settings
This article describes how to create a plan viewer with Planpoint starting with the first module: the general settings.
**Prerequisite** You should have all your content already prepared. For more details about the content, please read the [checklist](/checklist).
Click on the + button on the upper left side of the dashboard.
Assign a project name, address, choose the project type (sales, rental, land, commercial), and select whether you want to skip the floor or not. This allows you to jump directly from the project render to the unit you choose to view. This feature is typically used for small projects.
Choose the specific plan for this specific project. Plans are based on the number of **units per project.** Larger real estate projects require a larger monthly plan.
The project is now created but empty. In your dahsboard, click on **details.**
This will redirect you to the **settings** module, where you will find the following tabs:
Under **details** you can do the following:
* Define a cover image. This is the image that will appear in the card preview of the project in the dashboard and will also be your main building image. Please choose a high-resolution image, but no bigger than 1 MB.
* Edit the project's name.
* If you wish, you can add the website URL of your project, which will redirect you to the project's website.
* You can also add an internal URL for the project. This is intended for your marketing and sales teams.
* Configure the various connections we offer. If there is another connection you'd like to have, please contact us, and we’ll be happy to assist you. Additionally, you can explore our [Zapier](https://viewerdocs.planpoint.io/zapier) integration with Planpoint.
* Pause or delete the project's suscription
After finishing with the project details, move on to the **customization** tab. Here you can do the following:
* Select the property type (Condominium, rental, townhouse, house, land, retail, office, commercial, industrial).
* Turn on/off prices. When turned on, the plan viewer will show each unit's price.
* Turn on/off availabilities. When turned on, the plan viewer will specify wether each unit is available or not.
* Turn on/off freely the unit information. This includes inclusions, parking, orientation, bathrooms, descriptions, finishes and more.
* Finishing this section, we can see everything related to enterprise. Here you can add the project to the enterprise you have already created. Learn more about the [enterprise feature](https://viewerdocs.planpoint.io/enterprise).
Keep in mind that you can always preview your work in progress by navigating to the settings module, under the **Style & Embed** tab
## Troubleshooting
Here's how to solve some common problems when setting up a project.
Please remember to add a credit card to your profile before initiating a project.
Yes, you can change a monthly plan for a project only if it's a **rental** one. Rental projects are based in vacant units.
For any connections, please contact us via support @ planpoint.io.
# Superframe & Touchscreen TVs
Source: https://viewerdocs.planpoint.io/superframe
A specialized app designed to enhance your sales presentations on touchscreen TVs or tablets.
The process for creating the superframe is simple and straightforward. Please follow the next steps.
**Download** the Superframe app for Windows, macOS and iPadOS [on our website](https://www.planpoint.io/new-releases/superframe).
***
## Video Guide for Visual Learners
***
The Superframe section can be found in the Sales & Marketing module.

You can update the project logo displayed in the Superframe by simply browsing and uploading a file from your device. Additionally, you can customize the color scheme and text color to match your branding.

Here, you can divide the interior of the project into different rooms of the unit, allowing you to display each section separately. Please note that you can fully customize your project to suit your needs.

After adding a new gallery set, you'll need to upload the main render for the room by clicking the cloud storage icon and selecting the file from your computer.

The edit button allows the user to add a title for the room, include a 3D tour link (if available), and upload the image gallery for this area.

You can also delete the gallery set by simply clicking the trash can icon.
In this section, you can add content related to the common areas of the project. It follows the same format as the previous section (interior).
Simply add your choice of finishes to the project and edit the gallery set as demonstrated in the previous steps.

To add a video of the project or a map showing its location, simply insert the respective links. You can add multiple videos if needed.
The aspect ratio for televisions is 16:9 and Planpoint Superframe will make your images fit the screen entirely.
Since most images and renders don't follow this 16:9 ratio, your images might not appear in full.
We recommend adjusting your images before importing. Here's an example of a render that was adjusted to 16:9 ratio by adding blur on both sides:
# Unit variants
Source: https://viewerdocs.planpoint.io/unit-variants
The **Unit variants** feature is extremely useful for home development projects where many types of models of the same house can be sold on the same plot of land. It displays the various variants inside a neat menu for quick and easy access.
### Here's a video guide:
# Units
Source: https://viewerdocs.planpoint.io/units
The unit tab now contains all the floors previously created in the exterior module. You can import a CSV file containing all your units for quicker upload.
Download the [Excel template here](https://app.planpoint.io/assets/template.xlsx). The download button, located next to the upload button, allows you to download a CSV file of the current unit list. This feature is particularly useful if you need to make bulk updates to the unit list and re-upload it to update the project.
Please view the following video to understand how to prepare your Excel file before import:
You can also add units manually, one-by-one. Click on **Add unit** on the corresponding floor and repeat for each unit.
Once the units are imported, you will see the unit menu. It contains several other features that you can access through the icons on the right.
1. The **first** icon is for uploading a cover image for this specific unit. It will only be visible in the plan viewer when using grid mode.
2. The **second** icon is for uploading different finishes for this unit.
3. The **third** icon is for uploading an image gallery for this specific unit. For example, you could show interior renderings or exterior drone views.
4. The **fourth** icon allows you to add a URL for your custom button. This URL will redirect users to the selected website when the custom button is clicked. For example, you can add a "Book a Tour" button. The URL can be added to individual units or applied to multiple units as needed.
5. The **fifth** icon is for embedding a 3D virtual visit. You can simply copy-paste code from [Kuula](https://kuula.co/) or other virtual visit providers.
6. The **sixth** icon is for uploading the PDF version of the unit's plan layout. This PDF will be downloadable by the end-user.
7. The **seventh** icon is for uploading a JPG or PNG version of the unit's plan layout. This image will be shown in the plan viewer but is not downloadable.
8. The **eighth** icon allows you to define the perimeters of the lots in a [Homes & Land project](https://viewerdocs.planpoint.io/homes-land)
9. The **ninth** icon lets you edit the unit number, the model number, the type of unit, the area, the number of bedrooms, the price, availability, the occupancy dates, inclusions, description, number of bathrooms, the number of parking spaces and orientation.
10. The **tenth** icon is used to delete this specific unit.
\*The number in parentheses next to the floor number represents the total number of units on that floor.
With all your units in the system, you can click on the edit icon (ninth from the left), allowing you to modify the unit's basic information.

* The **Name** is your internal referral number. It can be numerical or alphanumerical.
* The **Model** number is the unit's model layout. A particular model can reappear on the same floor or on different floors. In such instance, simply writing the model number in this field will automatically fill up the area, number of bedrooms, price and availability fields.
* You assign a **Type** if the unit is either a parking or an amenity.
* The **Area** is the square footage or square meter of this particular plan. You decide wether to show net or gross area.
* The **Bedrooms** field is a simple dropdown choice.
* The **Price** number is the unit's price. For sales, you would enter the full list price (before or after tax is your choice). For rentals, you would enter the monthly rent amount.
* The **Availability** field is a simple dropdown choice between Available, Unavailable, Sold and Reserved. The unavailable units will not be shown in the plan viewer and the sold units cannot be clicked on in the plan viewer.
* The **Occupancy date** can be be selected on a calendar to show when the unit will be available.
* The **Inclusions** can be added in a comma separated list.
* The **Description** of the unit must be added as a text.
* For **Bathrooms**, just type the number of bathrooms the unit has.
* For **Parking Spaces**, just type the number of parking spaces the unit has.
\*The **Orientation** indicates the direction the unit will face. For example, you can specify "Mountain View" or simply "South."
If you do not observe the changes made to your units, please refresh the page.
## Troubleshooting
Here's how to solve some common problems when uploading units to the project.
It is crucial to meticulously follow each step outlined in the import video. If the problem persists, please contact us.
Yes, you have the option to upload an updated version of the CSV file, and it will overwrite the existing content. You can perform this action as many times as needed.
# Java
Source: https://viewerdocs.planpoint.io/untitled-page
# planpoint-sdk-java
Official Java SDK for the [Planpoint](https://app.planpoint.io) API.
## Requirements
* Java 17+
* Maven or Gradle
> **Windows Note:** Make sure `JAVA_HOME` is set before running Maven:
>
> ```powershell theme={null}
> $env:JAVA_HOME = "C:\Program Files\Microsoft\jdk-17.0.18.8-hotspot"
> $env:Path += ";$env:JAVA_HOME\bin"
> ```
## Installation
The SDK is served via [JitPack](https://jitpack.io) — no auth or registry setup required.
### Maven
```xml theme={null}
jitpack.io
https://jitpack.io
com.github.planpoint-io
planpoint-sdk-java
main-SNAPSHOT
org.codehaus.mojo
exec-maven-plugin
3.1.0
com.yourpackage.Main
```
### Gradle
```groovy theme={null}
repositories {
maven { url 'https://jitpack.io' }
}
dependencies {
implementation 'com.github.planpoint-io:planpoint-sdk-java:main-SNAPSHOT'
}
```
## Quick Start
```java theme={null}
import io.planpoint.ApiClient;
import io.planpoint.api.AuthenticationApi;
import io.planpoint.api.ProjectsApi;
import io.planpoint.model.LoginBody;
import io.planpoint.model.AuthResponse;
import io.planpoint.model.ProjectSummary;
import java.util.List;
public class Main {
static ApiClient makeClient(String token) {
ApiClient client = new ApiClient();
client.setScheme("https");
client.setHost("app.planpoint.io");
client.setPort(-1);
if (!token.isEmpty()) {
client.setRequestInterceptor(req -> req.header("Authorization", "Bearer " + token));
}
return client;
}
public static void main(String[] args) throws Exception {
// 1. Authenticate
LoginBody loginBody = new LoginBody();
loginBody.setUsername("you@example.com");
loginBody.setPassword("yourpassword");
AuthResponse auth = new AuthenticationApi(makeClient("")).login(loginBody);
String token = auth.getAccessToken();
// 2. Fetch your projects using authenticated client
List projects = new ProjectsApi(makeClient(token)).getMyProjects();
for (ProjectSummary p : projects) {
System.out.println(p.getName());
}
}
}
```
## API Reference
### Authentication
#### `new AuthenticationApi(client).login(LoginBody body)`
Authenticate and receive a JWT token.
| Field | Type | Required | Description |
| ------------- | --------- | -------- | ------------------------ |
| `username` | `String` | Yes | User email |
| `password` | `String` | No | User password |
| `impersonate` | `Boolean` | No | Impersonate another user |
**Returns:** `AuthResponse` — `.getAccessToken(): String`
***
### Projects
#### `new ProjectsApi(client).getMyProjects()`
List all projects belonging to the authenticated user. Requires auth.
**Returns:** `List` — `.getName()`, `.getNamespace()`, `.getHostName()`, `.getStatus()`, `.getPaused()`, `.getCreatedAt()`
***
#### `new ProjectsApi(client).findProject(FindProjectBody body)`
Find a public project by namespace and hostname. No auth required.
| Field | Type | Required | Description |
| ----------- | -------- | -------- | ----------------- |
| `namespace` | `String` | Yes | Project namespace |
| `hostName` | `String` | Yes | Allowed hostname |
**Returns:** `Project`
***
#### `new ProjectsApi(client).getProject(String id)`
Get a project by ID. Requires auth.
**Returns:** `Project`
***
#### `new ProjectsApi(client).updateProject(String id, Object body)`
Update project settings. Requires auth.
**Returns:** `Project`
***
### Groups
#### `new GroupsApi(client).getGroups()`
List all groups for the authenticated user. Requires auth.
**Returns:** `GroupsListResponse` — `.getRecords(): List`, `.getCount(): Integer`
***
#### `new GroupsApi(client).getGroup(String id)`
Get a group by ID. Requires auth.
**Returns:** `Group` — `.getName()`, `.getNamespace()`, `.getHostName()`, `.getType()`
***
#### `new GroupsApi(client).createGroup(CreateGroupBody body)`
Create a new group. Requires auth.
**Returns:** `Group`
***
#### `new GroupsApi(client).updateGroup(String id, Object body)`
Update a group. Requires auth.
**Returns:** `Group`
***
### Floors
#### `new FloorsApi(client).getFloors(String pid)`
List all floors for a project. Requires auth.
| Param | Type | Required | Description |
| ----- | -------- | -------- | ---------------- |
| `pid` | `String` | Yes | Project ObjectId |
**Returns:** `List` — `.getName()`, `.getProject()`, `.getLevel()`, `.getPosition()`, `.getPath()`
***
#### `new FloorsApi(client).getFloor(String id)`
Get a floor by ID. Requires auth.
**Returns:** `FloorFull`
***
#### `new FloorsApi(client).createFloor(CreateFloorBody body)`
Create a new floor. Requires auth.
**Returns:** `FloorFull`
***
#### `new FloorsApi(client).updateFloor(String id, Object body)`
Update a floor. Requires auth.
**Returns:** `FloorFull`
***
### Units
#### `new UnitsApi(client).getUnits(String pid)`
List all units for a project. Requires auth.
| Param | Type | Required | Description |
| ----- | -------- | -------- | ---------------- |
| `pid` | `String` | Yes | Project ObjectId |
**Returns:** `UnitsListResponse` — `.getRecords(): List`, `.getCount(): Integer`
`UnitFull` fields: `.getFloor()`, `.getName()`, `.getUnitNumber()`, `.getStatus()`, `.getPrice()`, `.getBed()`, `.getBath()`, `.getSqft()`, `.getModel()`
`status` values: `Available` | `OnHold` | `Sold` | `Leased` | `Unavailable`
***
#### `new UnitsApi(client).getUnit(String id)`
Get a unit by ID. Requires auth.
**Returns:** `UnitFull`
***
#### `new UnitsApi(client).createUnit(CreateUnitBody body)`
Create a new unit. Requires auth.
**Returns:** `UnitFull`
***
#### `new UnitsApi(client).updateUnit(String id, Object body)`
Update a unit. Requires auth.
**Returns:** `UnitFull`
***
#### `new UnitsApi(client).deleteUnit(String id)`
Delete a unit. Requires auth.
***
#### `new UnitsApi(client).batchUpdateUnits(BatchUpdateUnitsBody body)`
Update multiple units at once. Requires auth.
| Field | Type | Required | Description |
| ----------- | -------------- | -------- | ------------------------ |
| `ids` | `List` | Yes | Unit ObjectIds to update |
| `patchData` | `Object` | Yes | Fields to apply to all |
**Returns:** `List`
***
### Leads
#### `new LeadsApi(client).getLeads(String pid)`
List all leads for a project. Requires auth.
| Param | Type | Required | Description |
| ----- | -------- | -------- | ---------------- |
| `pid` | `String` | Yes | Project ObjectId |
**Returns:** `List` — `.getName()`, `.getEmail()`, `.getPhone()`, `.getMessage()`, `.getUnit()`, `.getCreatedAt()`
***
## Links
* **[Install via JitPack (Maven/Gradle)](https://jitpack.io/#planpoint-io/planpoint-sdk-java)**
* [Java SDK on GitHub](https://github.com/planpoint-io/planpoint-sdk-java)
* [Planpoint App](https://app.planpoint.io)
* [TypeScript SDK on npm](https://www.npmjs.com/package/@planpoint/sdk)
* [Python SDK on PyPI](https://pypi.org/project/planpoint-sdk)
* [Go SDK on GitHub](https://github.com/planpoint-io/planpoint-sdk-go)
* [PHP SDK on GitHub](https://github.com/planpoint-io/planpoint-sdk-php)
# Welcome
Source: https://viewerdocs.planpoint.io/welcome
In this documentation site, you will find all the necessary knowledge to create and edit a plan viewer, as well as developer docs for our API and our SDK kit.
## Getting Started
What are you looking for today?
Start building an awesome Planpoint in under 2 hours.
Our many API endpoints can connect both our applications.
Easy to understand SDK for a quick turnaround.
## Advanced features
There's a lot of advanced features to learn, from Enterprise to Immersive 3D. Let's dig in!
Display all your projects on a global map interface.
Create a 360° outlook of your project’s environment.
Showcase your project's amenities and available parking spaces.
Allocate limited access to specific floors and units for stakeholders.
# Zapier Integration with Planpoint
Source: https://viewerdocs.planpoint.io/zapier
Learn how to connect Planpoint with over 6,000 apps using Zapier.
You can find our Zapier app here: [https://zapier.com/apps/planpoint/integrations](https://zapier.com/apps/planpoint/integrations) 
## Zapier Integration with Planpoint
The **Planpoint Zapier app** enables seamless connectivity between your Planpoint account and over **6,000 applications**, facilitating direct data exchange for:
* **Leads**
* **Unit status**
* **Unit availability**
* **Unit prices**
* **And more...**
## Key Benefits
### Lead Management
* **Automatically route new leads** from Planpoint to your preferred **CRM or marketing tools**.
* Ensure **timely follow-ups** and enhance conversion rates.
### Real-Time Updates
* **Sync unit statuses, availability, and pricing** across platforms.
* Maintain **consistent and accurate** information across all your systems.
### Enhanced Analytics
* Integrate with analytics platforms to gain deeper **insights into customer behavior** and sales performance.
* Leverage **data-driven decision-making** for better business strategies.
## Getting Started
To begin utilizing the Zapier integration, follow these steps:
1. **Request an Invitation**: Contact our support team at [**support@planpoint.io**](mailto:support@planpoint.io).
2. **Receive Setup Instructions**: Our team will send you an invitation and guide you through the setup process.
3. **Connect Your Apps**: Use Zapier to link Planpoint with your preferred tools and automate workflows.
## Support
If you need assistance with the Zapier integration, our support team is ready to help:
* **Email**: [support@planpoint.io](mailto:support@planpoint.io)
By leveraging the **Zapier integration**, you can enhance your **Planpoint experience**, streamline workflows, and maintain up-to-date information across all your platforms. Automate your processes and **maximize efficiency today**!