Planimble for developers

Planimble for developers

Put the Planimble sketcher inside your inspection app. Your inspector draws the property there, and your system gets back the areas, the totals and the geometry, recalculated by Planimble's server.

Everything here is available on the B2B API plan. Requests go to https://app.planimble.com; on your own test server, use its address instead.

How it fits together

Four parties take part. Your API key never leaves your server; the browser only ever holds a short-lived link for one inspection.

Embed flow Your server asks Planimble for an embed link with its API key. Your page mounts the link in an iframe with the SDK. The sketcher saves to Planimble and tells your page. Your server reads the saved sketch. Your server Planimble Your page Sketcher (iframe) 1 POST /embed/sessions + key embedUrl, valid 2 hours 2 embedUrl to your page 3 SDK mounts the iframe 4 inspector draws; sketch saves 5 PLANIMBLE_SAVED 6 GET /sketches/{id} + key

Quickstart

  1. Get an API key

    API access requires an active B2B API plan. There is no separate test-key service: a live key operates on real account data. Open the playground’s key-creation steps: sign in as the owner in another tab, prepare the key request, then send it from the API console. The key is shown once; store it in your server’s secret store. You can also test the public health endpoint without a key.

    POST /api/v1/org/api-keys        (signed in as the owner)
    → { "apiKey": "pk_live_…", "note": "Store this now; it is not shown again." }
  2. Register your website

    Only registered websites can show the sketcher. Use the address of the page that will hold the iframe: https, no path. http://localhost is accepted for testing. Up to 10.

    PUT /api/v1/org/embed-origins    (signed in as the owner)
    { "origins": ["https://inspections.example.com"] }
  3. Ask for a link from your server

    One link per inspection visit. It is valid for two hours, works only on the website you name, and reaches only this one sketch.

    curl -X POST "https://app.planimble.com/api/v1/embed/sessions" \
      -H "X-Api-Key: $PLANIMBLE_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{ "externalInspectionId": "INS-1042",
            "origin": "https://inspections.example.com",
            "userName": "Pat Field",
            "subject": "12 Elm St, policy 77-1042",
            "mode": "edit" }'
  4. Mount the sketcher on your page

    Load the SDK from Planimble and give it the link. It saves on its own while the inspector works; onSaved fires after every save on the server.

    <div id="sketch" style="height: 720px"></div>
    <script src="https://app.planimble.com/sdk/planimble-embed-sdk.js"></script>
    <script>
      const { embedUrl } = await (await fetch('/inspections/INS-1042/sketch-link', { method: 'POST' })).json();
      const sketch = PlanimbleEmbed.mount('#sketch', {
        embedUrl,
        height: '100%',
        onReady: m => console.log('ready', m.mode),
        onSaved: m => showTotals(m.calculatedSummary),     // { totalGlaSqFt, totalNonGlaSqFt, … }
        onError: m => m.code === 'embed_expired' && reopen()
      });
    </script>
  5. Read the result from your server

    Read by your own inspection ID. Treat this server copy as the record: it is recalculated by Planimble, not taken from the browser.

    curl "https://app.planimble.com/api/v1/sketches/INS-1042" \
      -H "X-Api-Key: $PLANIMBLE_API_KEY"

Keys and tokens

CredentialWho holds itSent asReaches
API key pk_live_…Your server onlyX-Api-Key headerEvery sketch, image and review note of your account
Embed tokenThe iframe, inside embedUrlAdded by the sketcher itselfOne sketch and its images, for two hours, on one website. view tokens cannot save.
Owner sign-inThe account owner's browserCookieAccount settings: API keys and registered websites

Call the API from your server, not a browser. If a browser that is signed in to Planimble sends a request, its sign-in takes priority over X-Api-Key. Server-to-server calls carry no cookie, so the key is always used.

An API key works only while the account is on an active B2B API plan (or a payment is past due). Removing a website from the registered list stops its links at once, including ones already handed out.

Embedding the sketcher

Create a link

POST /api/v1/embed/sessions API key

FieldRequiredMeaning
externalInspectionIdYesYour ID for the inspection, up to 100 characters. The sketch is stored and read under it.
originYesThe registered website that will show the iframe, for example https://inspections.example.com.
modeNoedit (default) or view. In view mode every floor is locked and nothing is saved.
userNameNoShown in the sketcher as the inspector, up to 80 characters.
subjectNoThe address or policy line shown on the sketch, up to 200 characters.
{
  "token": "CfDJ8…",
  "embedUrl": "https://app.planimble.com/embed?token=CfDJ8…&subject=12%20Elm%20St",
  "expiresAtUtc": "2026-10-24T13:40:00+00:00",
  "origin": "https://inspections.example.com",
  "mode": "edit"
}

What the sketcher shows

Inside your page the sketcher shows only the sketch: no Planimble sign-in, account, billing or list of other sketches. It saves to the server automatically while the inspector works, and works on touch screens with the number pad, voice and photos. Grant microphone and camera to the iframe (the SDK does) if you want voice input and photos.

When a link expires

After two hours, or if the website is removed, the sketcher shows "This sketch session has ended" and sends PLANIMBLE_ERROR with code: "embed_expired". Anything saved so far is kept. Ask your server for a new link and mount it again.

JavaScript SDK

Load /sdk/planimble-embed-sdk.js from Planimble. It adds window.PlanimbleEmbed (version 2.0) and talks only to the Planimble origin in embedUrl, and only with the iframe it created.

PlanimbleEmbed.mount(container, options)

OptionTypeMeaning
embedUrlstringRequired. The link from your server.
heightstringCSS height of the iframe. Default 720px.
titlestringAccessible name of the iframe. Default "Property sketch".
onReady(m)functionThe sketch is open. m.externalInspectionId, m.mode.
onSaved(m)functionAfter every save on the server. m.revision, m.calculatedSummary.
onError(m)functionm.code: embed_expired, not_ready or not_saved (it retries on its own).
onData(payload)functionEvery answer to getData().

Instance methods

MethodDoes
save()Saves now. Normally not needed; useful before your own "Submit" button.
getData(timeoutMs = 8000)Resolves with the current sketch as JSON (see Sketch data), including unsaved edits. Rejects if the sketcher does not answer.
load(payload)Replaces the drawing with your JSON, for example a sketch from a previous inspection. Keep the same externalInspectionId as the link, or the server refuses to save it.
destroy()Removes the iframe and stops listening.

Messages

The SDK is a thin wrapper over postMessage. If you build your own, send to and accept from the exact Planimble origin, and check event.source is your iframe.

DirectionTypeFields
To the sketcherPLANIMBLE_SAVEnone
To the sketcherPLANIMBLE_GET_DATAnone
To the sketcherPLANIMBLE_LOADpayload: sketch JSON
From the sketcherPLANIMBLE_READYexternalInspectionId, mode
From the sketcherPLANIMBLE_SAVEDexternalInspectionId, revision, calculatedSummary
From the sketcherPLANIMBLE_DATA_RESPONSEpayload: sketch JSON
From the sketcherPLANIMBLE_ERRORcode, sometimes message
From the sketcherPLANIMBLE_DIRTY_CHANGEDisDirty, changeCount: there are unsaved edits
From the sketcherPLANIMBLE_VALIDATION_WARNINGA drawing problem the inspector should look at, in args

The last two are not passed to SDK callbacks yet; listen for them on window if you need them. The playground's message log shows every message.

API reference

All responses are JSON. Errors return { "code": "…", "message": "…" }; save validation errors return { "code": "…", "error": "…" }. Every /api response is sent with Cache-Control: no-store.

{id} is your externalInspectionId or Planimble's sketchId.

GET /api/v1/sketches API key

Your account's sketches, newest first: sketchId, externalInspectionId, subject, status, revision, totalGlaSqFt, areaCount, created, updated and exported times.

GET /api/v1/sketches/{id} API key

One sketch: revision, status, rates, homeStyle, rcFormula, calculatedSummary and the full drawing in vectorCanvas (see Sketch data). 404 not_found if there is none yet.

GET /api/v1/sketches/{id}/versions API key

Saved versions, newest first: versionId, revision, createdAtUtc, totalGlaSqFt, areaCount. Read one with GET /api/v1/sketches/{id}/versions/{versionId}; it has the same shape as a sketch.

POST /api/v1/sketches/sync API key

Creates or updates a sketch from your server, for example to start an inspection from last year's sketch. Send baseRevision (the revision you read, or 0 for a new sketch). Planimble recalculates every area and returns the result, with validation.issues and the month's usage. With an API key you may also set status to Draft, QA_Review or Approved, and ask for a webhook.

POST /api/v1/sketches/{id}/exported API key

Records that a report was produced. From then on the sketch keeps its place in the monthly count even if it is deleted.

DELETE /api/v1/sketches/{id} API key

Deletes the sketch, and after a day any images no other sketch uses. If it was never exported, this month's count is given back: { "deleted": true, "refunded": true, "usage": {…} }.

GET /api/v1/sketches/{id}/comments API key

QA review notes pinned to walls, areas or points, with replies and open / resolved status. Writing notes needs a signed-in reviewer.

POST /api/v1/files API key

Uploads a PNG or JPEG (body is the image; Content-Type: image/png or image/jpeg; up to 8 MB and 8192 pixels per side). Returns file.id to use as a photo's fileId or a background image's imageFileId. Read it back with GET /api/v1/files/{fileId}; check storage with GET /api/v1/files/usage.

GET PUT /api/v1/org/embed-origins Owner sign-in

Lists or replaces the registered websites: { "origins": [ … ] }. PUT replaces the whole list.

POST /api/v1/org/api-keys Owner sign-in

Creates a new API key and returns it once. Existing keys keep working.

GET /api/v1/health, /api/v1/ready Public

Health answers while the server runs. Ready also checks the database and returns 503 when it is unavailable.

Sketch data

The same shape is used to save (POST /sketches/sync), to load into the sketcher (PLANIMBLE_LOAD), and in vectorCanvas when you read a sketch (schema 1.7). Lengths are feet. x grows east and y grows south, so north is up on screen.

{
  "externalInspectionId": "INS-1042",
  "subject": "12 Elm St, policy 77-1042",
  "baseRevision": 0,
  "glaRate": 195, "auxRate": 88, "materialMultiplier": 1.0,
  "floors": [ { "id": "main", "name": "Main floor", "visible": true, "locked": false } ],
  "activeFloorId": "main",
  "areas": [
    { "id": "a1", "code": "GLA1", "label": "Main house", "isLivingAreaGla": true, "factor": 1,
      "floorId": "main",
      "verticesFt": [ {"x":0,"y":0}, {"x":48,"y":0}, {"x":48,"y":30}, {"x":0,"y":30} ] },
    { "id": "a2", "code": "GAR", "label": "Garage", "isLivingAreaGla": false, "factor": 1,
      "floorId": "main",
      "verticesFt": [ {"x":48,"y":6}, {"x":72,"y":6}, {"x":72,"y":30}, {"x":48,"y":30} ] }
  ],
  "stamps": []
}

Areas

FieldMeaning
idYour stable ID for the area. Keep it between saves.
code, labelArea type code and name. GLA, GLA1, GLA2 and GLA3 always count as living area; others count as living area when isLivingAreaGla is true.
factorMultiplies the measured area, for example 0.5 for a half storey. Above 1.0 is allowed but reported as a warning.
verticesFtCorners in order, at least 3, up to 2,000. Don't repeat the first corner at the end.
bulgesOptional, one per wall: 0 is straight, ±1 is a half circle (DXF bulge).
holesOptional openings cut out of the area (courtyards, stairwells), up to 50.
floorIdWhich floor the area is on. Required once floors is sent.

Everything else

FieldMeaning
floors, activeFloorId1 to 50 floors with unique IDs and names (up to 60 characters).
stampsLabels and symbols (id, type, x, y, floorId). A door, window, garage door or opening also has wallRef {polyId, edge, t 0–1} and opening {kind, width, swing, hinge}.
annotationsText notes on the sketch.
photosUp to 300: id, fileId, optional polyId, edge, caption (200 characters), takenAt, floorId. Photos never change square footage.
underlayBackground image with its scale and position, from imageFileId or an embedded PNG/JPEG up to 2 MB. Up to 3 more in layers. Send null to remove it.
homeStyle{ code, name, group }, for example a colonial or a ranch.
rcFormulaCustom replacement cost: { "mode": "custom", glaRate, auxRate, multiplier, adjustment, note }, or { "mode": "default" }.

Leaving out underlay, annotations, photos, bulges or holes keeps what is saved, so an older integration never wipes them by accident. Send an empty list or null to clear them.

How the totals are worked out

Planimble ignores any areas or totals you send and measures every area itself (curved walls and openings included), rounded to a tenth of a square foot. Replacement cost is (living × glaRate + other × auxRate) × materialMultiplier, rounded to whole dollars, unless a custom rcFormula is set. Drawing problems are reported in validation.issues, never silently fixed:

SELF_INTERSECTION TOO_FEW_POINTS ZERO_AREA DUPLICATE_POINT SHORT_WALL FACTOR_RANGE BULGE_RANGE HOLE_OUTSIDE HOLE_OVERLAP

Saving and conflicts

Every accepted save adds one to revision. Send the revision you last read as baseRevision. If someone saved in between (the inspector in the field, or QA in the office), the save is refused with 409, and the response carries the current server copy so you can merge or ask:

{ "error": "revision_conflict",
  "message": "This sketch was changed on another device or by the office since your last sync.",
  "yourBaseRevision": 3,
  "server": { "revision": 4, … } }

A new externalInspectionId counts toward the month's new sketches (500 on the B2B API plan). Edits to an existing sketch never count. Over the limit, a new sketch is refused with 402 quota_exceeded and resetsAtUtc.

Webhooks

When your server saves with an API key, add "webhookCallbackUrl": "https://…" to the body. Planimble then posts the save result to that address, signed with the webhook signing secret Planimble gives you: X-Planimble-Signature is the HMAC-SHA256 of the raw body, in upper-case hex.

Webhooks are sent once, with no retry, and only for saves made with your API key. Saves made by the inspector inside the sketcher do not send one: use onSaved on your page, then read the sketch from your server.

app.MapPost("/planimble/webhook", async (HttpRequest req, IConfiguration cfg) =>
{
    using var reader = new StreamReader(req.Body);
    var body = await reader.ReadToEndAsync();
    using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(cfg["Planimble:WebhookSecret"]!));
    var expected = Convert.ToHexString(hmac.ComputeHash(Encoding.UTF8.GetBytes(body)));
    var given = req.Headers["X-Planimble-Signature"].ToString();
    if (!CryptographicOperations.FixedTimeEquals(Encoding.ASCII.GetBytes(expected), Encoding.ASCII.GetBytes(given)))
        return Results.Unauthorized();
    var saved = JsonDocument.Parse(body).RootElement;   // eventType: PLANIMBLE_SKETCH_SAVED
    return Results.Ok();
});

Errors

StatusCodeWhat to do
400invalid_inspection_idSend an ID of 1–100 characters.
400invalid_origin, too_many_origins, invalid_modeFix the field named in the message.
400invalid_floors, invalid_shape, invalid_opening, invalid_underlay, invalid_annotations, invalid_photos, invalid_rc_formula, invalid_home_style, sketch_too_large, missing_fileThe sketch body breaks a rule; error says which.
401auth_required, api_key_requiredSend a valid X-Api-Key from your server. Check the plan is active.
401embed_expiredThe link ran out or its website was removed. Create a new link.
402plan_required, quota_exceededNeeds the B2B API plan, or the month's new sketches are used up.
403origin_not_allowedRegister the website first (owner).
403embed_scopeThe link is view-only, or the call is outside its one sketch.
403owner_only, not_allowedThe signed-in person lacks the role.
403csrf_header_missing, bad_originA browser call without the app's headers. Call from your server with the key.
404not_found, version_not_foundNo sketch, version or file with that ID in your account.
409revision_conflictSomeone saved first. See Saving and conflicts.
409account_changedOnly in a signed-in browser after switching accounts. Reload.
413, 415too_large, unsupported_type, invalid_imageImages must be PNG or JPEG, up to 8 MB.
429rate_limitedToo many uploads from one address; wait a minute.
507storage_fullImage storage is full. Delete sketches or images you no longer need.

Limits

LimitB2B API plan
New sketches a month500 (edits are free)
Image storage20 GB per account
Image uploads60 a minute from one IP address
Embed link lifetime2 hours
Registered websites10
Areas per sketch500, each up to 2,000 corners
Floors, photos, background images50 floors, 300 photos, 4 images
Image size8 MB and 8192 px per side (uploads); 2 MB and 4096 px (embedded background)

Security checklist

  • The API key is only on your server, in a secret store, never in page code or a mobile app.
  • Your page gets embedUrl from your own server, after your own sign-in and permission checks.
  • Create a new link for each visit; don't store embedUrl in your database or logs.
  • Use view mode for people who should only look.
  • Save the inspection's numbers from GET /api/v1/sketches/{id} on your server, not from browser messages.
  • Verify the webhook signature with a constant-time comparison, and accept only HTTPS callbacks.
  • Planimble refuses to be framed anywhere except your registered websites, sends no cookies inside the iframe, and never uses a Planimble sign-in from the same browser.