How the integration works
Everything this demo does is a handful of REST calls and one URL. The code making the calls is js/api.js - about 200 readable lines. The full interactive API reference is at app.vergeag.com/docs.
What this demo does today
sequenceDiagram
autonumber
participant YU as Your Ag App (browser)
participant LA as Launch Pad API
participant LU as Launch Pad UI
note over YU,LA: verify the API key
YU->>LA: GET /api/user/current (X-API-KEY)
LA-->>YU: user + company access
note over YU,LA: provision the data hierarchy
YU->>LA: POST /api/companies/{companyId}/growers
YU->>LA: POST /api/growers/{growerId}/farms
YU->>LA: POST /api/fields/save (field + boundary ring)
LA-->>YU: fieldId + boundaryId
note over YU,LU: hand over to Launch Pad
YU->>LU: open /path-planning/boundary/{boundaryId}
(new window, or iframe with ?embedded=true&chrome=false)
LU->>LU: user plans the path (existing Launch Pad session)
In a production integration the API calls run from your backend (the API key must never ship to a browser); this demo runs them client-side purely so you can watch the traffic.
Partner mode: provisioning + launch codes (auto sign-in)
With a Partner Admin API key, the demo goes further: your backend creates the Launch Pad user and their org from your own external ids (idempotent - retries never duplicate), then requests a short-lived, single-use launch code. The user's browser exchanges the code for a Launch Pad session on arrival - no sign-up, no sign-in. Try it on the Partner mode page.
sequenceDiagram
autonumber
participant YU as YourApp UI
participant YB as YourApp backend
participant LA as Launch Pad API
participant LU as Launch Pad UI
YU->>YB: user clicks "Open Path Plan"
note over YB,LA: provision identity from YOUR ids (idempotent)
YB->>LA: POST /api/partner/users { partnerUserId, email, ... }
YB->>LA: POST /api/partner/companies { partnerCompanyId, name }
YB->>LA: POST /api/partner/company-accesses { partnerUserId, partnerCompanyId }
note over YB,LA: push the data hierarchy (as today)
YB->>LA: POST growers / farms / fields/save (boundary)
note over YB,LA: request a launch code
YB->>LA: POST /api/partner/launch { userId: partnerUserId, returnUrl }
LA-->>YB: { code, userId, expiresInSec: 600 }
YB-->>YU: redirect to Launch Pad /launch?code=...&userId=...&returnUrl=...
YU->>LU: browser opens /launch
note over LU,LA: exchange the code for a session (code is single-use)
LU->>LA: POST /api/partner/launch/exchange { code, userId }
LA-->>LU: JWT session
LU->>LU: user lands on the boundary, signed in
REST call reference
| Call | Purpose | Notes |
|---|---|---|
GET /api/user/current |
Resolve the user owning the API key | Good first call to validate a key. 401 = key invalid/revoked. |
GET /api/user/current/companies |
List companies the key can access | All data lives under a company: Company > Grower > Farm > Field > Boundary. |
POST /api/companies/{companyId}/growers |
Create a grower | Body {"name", "source": "api", "archived": false}. Keep growerId. |
POST /api/growers/{growerId}/farms |
Create a farm | Same body shape. Keep farmId. |
POST /api/fields/save |
Create field + boundary in one call | Boundary is coordinate rings of [lon,lat], not GeoJSON. Response returns
field.fieldId and vBoundary.boundaryId. |
/path-planning/boundary/{boundaryId} |
UI deep link (not an API call) | Open in a new window, or append ?embedded=true&chrome=false for iframes. |
POST /api/partner/users |
Create/find a Launch Pad user from your external user id | Partner Admin key only. Idempotent on partnerUserId;
email/firstName/lastName required only on first creation. |
POST /api/partner/companies |
Create/find a customer org from your external company id | Partner Admin key only. Idempotent on partnerCompanyId; your key's
user gets admin access to the org. |
POST /api/partner/company-accesses |
Grant your user access to their org | Partner Admin key only. Both referenced by your external ids. Idempotent. |
POST /api/partner/launch |
Issue a single-use launch code (auto sign-in) | Partner Admin key only. userId = your external id; response carries the
code (10-min TTL) + the Launch Pad user guid. Redirect the browser to
/launch?code=…&userId=…&returnUrl=…. |
POST /api/partner/launch/exchange |
Exchange the code for a session | No API key - the code is the credential. Called by Launch Pad's own
/launch page, not by your integration. Single-use: replays fail. |
Errors and rate limits your integration should handle
| Status | Meaning | Response body |
|---|---|---|
401 |
API key missing, invalid, expired, or revoked | - |
402 |
Account is out of credits for a billable operation | {"creditsAvailable", "creditsRequired", "message"} |
403 |
Key is valid but not allowed - e.g. a non-Partner-Admin key calling
/api/partner/* |
{"correlationId", "errorCode": "NotAuthorized", "message"} |
429 |
Rate limit exceeded | {"error": {"code": "RATE_LIMIT_EXCEEDED", …}, "limit", "windowSeconds",
"retryAfterSeconds"} |
Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and
X-RateLimit-Reset headers (visible in the request log on the demo pages) - watch
Remaining and back off before you hit the limit; on 429, honor Retry-After.
Embedding contract
Two ways to control the embedded Launch Pad view:
- URL parameters (set once at load):
?embedded=true&chrome=false- hides side navigation, org and field selectors, and applies the partner theme. - postMessage (adjust at runtime):
{type: "embedded-mode", embedded, chrome, hideOrgSelector, hideFieldSelector, hideSideNav}posted to the iframe's contentWindow - try it live on the Embedded mode page.