API / Developers Geometry Embed

Geometry Embed v1

Bring geometry into your application.

Pre-release quickstart and reference

Embed access and keys

Generate an embed key in Account settings with a Free account and register your exact host origins. Display and editing are currently included within operating limits. Availability is subject to change.

Use a publishable embed key restricted to the intended host origins and enabled capabilities. Authoring requires editing entitlement. A confidential server API key cannot authorize the picker.

Register exact origins such as https://your-app.example, with separate development and staging origins as needed. Paths and wildcards are not accepted. HTTP is allowed only for localhost or loopback development. Origin restrictions control browser integration; they do not authenticate arbitrary non-browser callers.

Account access, any configured expiry, revocation and operation permissions are checked on each server operation. To rotate a key, issue a replacement, update your integration and then revoke the old key. The full key is shown only when issued. Signing in to this website does not replace an integration credential.

Need backend encode/decode as well? Obtain a separate confidential server key.

Embed the geometry picker

  1. Obtain a publishable embed key with the intended host origins and geometry access. Authoring also needs the key’s editing entitlement.
  2. Put the HTML below on your host page and replace ISSUED_PUBLISHABLE_EMBED_KEY. Serve the host from a registered origin.
  3. Keep stable application IDs for entries. Only confirmed changes should update your stored business data.

The example shows a photo vector, turbine point and windfarm area, with an application-owned list outside the iframe. It handles selection, errors, previews and confirmations. No SDK is required.

Complete HTML host example html
<iframe id="geometry-picker" title="Select asset geometry"
        style="width:100%;height:600px;border:0"></iframe>
<p id="picker-status" role="status"></p>
<ul id="asset-list"></ul>
<script>
const frame = document.getElementById('geometry-picker');
const embedOrigin = "https://spatialglobe.com";
const key = 'ISSUED_PUBLISHABLE_EMBED_KEY';
const requestId = 'asset-map';
const records = new Map([{"id": "photo:001", "title": "DSC_0001.jpg", "srl": "p1!D6GX-USMO-AWUM-GIKX", "color": "#d946ef"}, {"id": "component:turbine-01", "title": "Turbine 01", "srl": "!D6GX-USMO-CTIL", "color": "#16a34a"}, {"id": "component:windfarm", "title": "Windfarm", "srl": "p1!D6GX-USMO-VE3K-KUBM-FKSP-EGSV", "color": "#2563eb"}].map(entry => [entry.id, entry]));
const errors = new Map(), accepted = new Set();
let instanceId = null, selectedId = null, loaded = false;
const status = document.getElementById('picker-status');
const send = command => frame.contentWindow.postMessage({
  source: 'spatialglobe.picker', version: 1,
  instanceId, requestId, ...command
}, embedOrigin);
function renderList() {
  const list = document.getElementById('asset-list');
  list.replaceChildren();
  for (const entry of records.values()) {
    const row = document.createElement('li');
    const button = document.createElement('button');
    button.type = 'button';
    button.textContent = entry.title || entry.srl;
    button.disabled = !loaded;
    button.setAttribute('aria-pressed', String(entry.id === selectedId));
    button.addEventListener('click', () => send({type:'select', entryId:entry.id}));
    row.appendChild(button);
    if (errors.has(entry.id)) {
      const error = document.createElement('p');
      error.textContent = errors.get(entry.id).message;
      row.appendChild(error);
    }
    list.appendChild(row);
  }
}
window.addEventListener('message', event => {
  if (event.origin !== embedOrigin || event.source !== frame.contentWindow) return;
  const m = event.data;
  if (m?.source !== 'spatialglobe.picker' || m.version !== 1) return;
  if (m.type === 'ready') {
    if (m.instanceId === instanceId) return;
    instanceId = m.instanceId;
    loaded = false;
    accepted.clear();
    errors.clear();
    send({type:'init', entries:[...records.values()], config:{ui:{list:false}}});
    renderList();
    return;
  }
  if (m.instanceId !== instanceId || m.requestId !== requestId) return;
  if (m.type === 'loaded') loaded = true;
  if (m.type === 'selection') selectedId = m.entryId;
  if (m.type === 'preview') status.textContent = 'Unconfirmed geometry preview';
  if (m.type === 'confirm') {
    const operation = `${instanceId}:${m.operationId}`;
    if (accepted.has(operation)) return;
    accepted.add(operation);
    records.set(m.entryId, m.entry);
    errors.delete(m.entryId);
    status.textContent = 'Geometry confirmed';
    // Persist the entry in your backend here, with your own authorization.
  }
  if (m.type === 'error') {
    if (m.entryId) errors.set(m.entryId, m.error);
    status.textContent = m.error.message;
  }
  if (m.type === 'cancel') {
    status.textContent = m.scope === 'session' ? 'Session closed' : 'Draft discarded';
    if (m.scope === 'session') loaded = false;
  }
  renderList();
});
renderList(); // Install the listener before navigating the iframe.
frame.src = `${embedOrigin}/embed/geometry/${encodeURIComponent(key.split('.')[0])}/`
  + `?origin=${encodeURIComponent(location.origin)}#key=${encodeURIComponent(key)}`;
</script>

Install the message listener before assigning iframe.src. Verify both event.origin and event.source, then the version, instance and request IDs. A new iframe instance gets a fresh instanceId; initialize again after reload. The key is delivered in the URL fragment rather than a query parameter.

Entries, titles and colors

Each entry has id and a full public srl. Optional title and color belong to that entry. IDs are opaque strings up to 128 characters, unique within the instance, and returned unchanged. Duplicate SRLs with different IDs are allowed.

Titles are at most 160 characters. Without a title, the internal list shows the SRL and the map label shows its geometry type. Colors use six-digit hex, such as #d946ef. Entries without a color receive different available colors from the shared 18-color palette, which cycles when exhausted. Explicit colors may repeat.

The save icon confirms a draft to the host. Clear/close discards the draft and prepares the next permitted geometry; it does not close the session. UI-created entries receive embed:<uuid> IDs. Map them to your records when accepting a confirmation.

Configure the picker

init.config can only reduce the capability ceiling configured on the key. Omitted configuration or {} requests the full implemented feature set allowed by that key. Display-only entitlement still prevents authoring.

Each instance supports up to 1,000 entries, including failed entries. Set maxEntries from 1 to 1000 on the key and/or host configuration to reduce that ceiling. The iframe loads entries in packets of at most 100 and keeps the previous accepted dataset if a packet fails. Each packet counts toward the key's request limits. Above 200 entries, the internal list uses 100-row pages and map detail labels follow the inspected geometry; all accepted geometries remain on the map.

Full workbench json
{}

Display and select json
{
  "operations": [
    "display"
  ],
  "ui": {
    "listManagement": false
  }
}

Create only · empty entries json
{
  "operations": [
    "create"
  ]
}

Host website list json
{
  "ui": {
    "list": false
  }
}

Points · bounded region json
{
  "geometries": {
    "point": {
      "precisions": [
        12,
        16
      ],
      "parameters": {
        "radius_m": {
          "min": 0,
          "max": 100
        }
      }
    }
  },
  "regions": [
    {
      "center": [
        11.575,
        48.137
      ],
      "radius_m": 5000
    }
  ]
}

Field Contract
operations Subset of display, create, edit. Empty array disables all. Create-only initialization needs entries: [].
geometries Map of point, segment, route, rectangle, polygon or vector. Omitted includes all; a supplied map includes only its named kinds; empty map disables all.
precisions Code lengths 12, 16, 20, 24, 28, intersected with per-kind support.
Per-kind options operations, precisions, regions and parameter intervals using min, max or both.
ui Boolean switches: search, srlInput, list, listManagement, info, geometry, parameters, views, measure. Default true.

Per-kind precision choices: point 12/16; segment 16/20/24; route, rectangle and vector 16; polygon 16/20/24/28. Parameter intervals use the physical parameter names in the geometry reference; point constraints may also use elevation_storeys. They check decoded, quantized values when present and do not require an optional value to exist.

Key and host operation, geometry and precision sets intersect. UI flags combine with AND. Parameter intervals intersect; contradictory intervals reject configuration. Hiding controls alone does not restrict the corresponding geometry operation. Search and SRL-input switches also gate those server operations.

Three list profiles

  • ui: {list:false}: your host website owns the visible list; rendering and confirmed results still work.
  • ui: {list:true, listManagement:false}: an internal selection list without rename, color or delete controls.
  • Both true: transient list management, including the edit hand, sections, movement, colors, copying and delayed deletion with Undo.

Positions use WGS84 [longitude, latitude] in degrees. Physical distances and elevations use meters, speeds use meters per second, and angles use degrees.

Region constraints

Root or per-kind regions accept one to four WGS84 circle regions: {"center":[11.575,48.137],"radius_m":5000,"scope":"geometry"}. Radius is 0–500,000 meters. Alternatives in one list form a union; root, per-kind, key and host restrictions all apply.

Default geometry scope checks supported decoded physical extent. Uncertifiable boundary cases are denied. anchors scope checks only decoded anchor/control vertices. Nonzero displacement vectors currently require anchors scope; full 3D vector containment is not supported. Search and camera navigation may leave the region; geometry results must pass the constraints.

General polygon masks and political-boundary constraints are not configuration inputs. Long/extruded segments and unsupported render geometries report errors rather than being reduced to points.

Commands and events

Every message contains source: "spatialglobe.picker", version: 1, instanceId, requestId and type. Before initialization, requestId is null. The host supplies a nonempty requestId up to 100 characters. Optional host state is JSON up to 2,048 JavaScript code units and is echoed on confirm/cancel; it grants no access rights.

Direction type Fields and behavior
Picker → host ready product=srl-geometry; capabilities with maxEntries, maxBytes, displayKinds, editableKinds, policy ceiling and persistence=false.
Host → picker init entries; optional config, state and selectedId. One configuration per iframe instance.
Picker → host loaded total, displayed, failedIds and effective policy.
Host → picker select entryId; optional commandId (up to 100 characters).
Picker → host selection entryId, entry, status, reason host/list/map and commandId. Failed entries include their error and do not move the camera.
Host → picker create entry with fresh id and optional title/color; optional starting point position. Explicitly starts a fresh draft.
Host → picker upsert entries and optional commandId; add new IDs or replace matching IDs without reload.
Host → picker remove entryIds and optional commandId; remove session entries. Unknown IDs reject the command without partial removal.
Picker → host updated action upsert/remove, commandId, entryIds and failedIds.
Picker → host preview entryId, entry, result, operation create/update and operationId. Uncommitted.
Picker → host confirm Same identity/result/operation fields, plus state. Deduplicate by (instanceId, operationId) before storing business changes.
Host ↔ picker cancel Host cancels a draft, or closes an idle session. Picker returns scope entry/session, entryId, state and operationId for a draft.
Picker → host error error with public code/message, recoverable=true, and entryId when tied to a failed supplied entry.
Picker → host listchange Local management action update/remove/move/sections; entryId/title/color or sectionId/sections. These are UI notifications, not instructions to delete business records.
Commands after the loaded event javascript
send({type:'upsert', commandId:'refresh-turbine', entries:[{
  id:'component:turbine-01', title:'Turbine 01',
  srl:'!D6GX-USMO-CTIL', color:'#f97316'
}]});
// Wait for updated with the matching commandId before continuing.
send({type:'remove', commandId:'remove-photo', entryIds:['photo:001']});
// After the removal acknowledgment, start a new host-assigned entry.
send({type:'create', entry:{id:'component:new', title:'New component'}});
// Clear an open draft first; configuration changes require a reload.

Confirm or clear an open draft before selection or dataset changes. Updates are serialized. Existing IDs retain position, identity and section; new IDs append. Omitted color keeps an existing color; omitted/empty title clears its title.

Structural batch errors reject initialization or update. Invalid, unsupported or policy-denied SRLs fail individually and stay identifiable by host ID. Invalid replacements remove stale map geometry and can be repaired by later upsert. Your host list should show those per-entry errors; the internal list does so when enabled.

Picker results contain the public SRL, generation/link fields, geometry_kind, GeoJSON geometry, represented parameters and requested_parameters. Titles and colors accompany the entry identity and remain separate from the SRL code.

Limits and errors

Boundary Contract
Request body 131,072 UTF-8 bytes per server request, including the signed policy.
Inventory 1,000 session entries in packets of up to 100, including failures. Filter larger inventories at the host. Invalid or unsupported entries do not prevent the remaining entries from being displayed.
Usage counters Shared minute/day quota per embed integration, with additional per-key ceilings, including compute and data operations.
Current defaults 60/minute and 10,000/day, unless configured otherwise.
Session policy Signed effective policy lasts one hour. Key-policy or authoring-entitlement changes require reload and initialization.

Rate values are provisional operational defaults, not purchased allowances or an SLA. Authorized invalid requests also consume quota. Application quotas are not a billing ledger. Responses use Cache-Control: no-store.

Configuration failures use API.EMBED.INVALID_CONFIG; denied capabilities use API.EMBED.POLICY_DENIED. Embed key/origin/entitlement denials return 403. Quota denial returns 429 with Retry-After: 60. Host-facing errors arrive as versioned error events, with entry IDs where applicable.

Handle structural batch failures separately from per-entry errors. Keep failed entries visible in your host list with their IDs and allow correction or removal. See Commands and events.

Backend requests have a separate Server API quota and HTTP error contract.

Versions and availability

Use Geometry Embed v1 for display, selection and permitted authoring. The retired point-only picker is removed. The picker’s workbench transport is an internal UI interface.

Session handoff and confirmed edits do not create persistent Library entries. Your application remains responsible for saving records and business associations. Explicit customer Library saving, custom saved-entry metadata/sharing, proximity queries and automatic photo association are separate work.

Higher-volume paid plans and pricing have not been set. View plans or contact us about your requirements.