# OpenLawn API v1

Canonical documentation: https://openlawn.ai/docs
OpenAPI specification: https://openlawn.ai/openapi.json
Updated: 2026-09-06
Base URL: https://api.openlawn.ai/v1
Alternate base: https://openlawn.ai/v1

## For AI agents

This reference, its HTML version, and the OpenAPI specification are publicly accessible without signing in. Only API operations and workspace data require authentication. Start with https://openlawn.ai/docs/agent-quickstart.md for the complete submit, poll, retrieve, and webhook workflow. A focused documentation index is available at https://openlawn.ai/docs/llms.txt and the site-wide index is https://openlawn.ai/llms.txt.

Use the REST API for backend agents and CRM connections; use https://openlawn.ai/developers for the separate signed-in browser skill. Neither path is a public MCP endpoint. Store keys outside agent prompts in server-side secrets. A newly completed measurement costs one credit; retrieving an existing result does not. Do not create duplicate jobs to check progress.

## Interactive console

https://openlawn.ai/docs#playground provides a mobile-friendly Scalar endpoint explorer with request editing and response inspection. Demo mode is the default: it simulates the job lifecycle inside your browser using an anonymized residential fixture. It does not send API requests, run a model, spend credits, or measure the address you enter. It is not a hosted sandbox; demo IDs and results do not exist in the live API.

Switch to Live API only to test your own workspace. Enter your own scoped key using Authorize; credentials stay in page memory and are cleared by Reset console or leaving the page. Every write requires confirmation, including credit-consuming job submissions. Live requests go directly to OpenLawn without a third-party proxy. Server-to-server integrations should continue to store keys in server-side secrets, never in shipped client code or agent prompts.

## Authentication

Owners and admins create workspace-scoped keys at https://openlawn.ai/settings/api. Send `Authorization: Bearer YOUR_KEY` from your server. Full keys are returned only at creation, stored as hashes, expire after 30/90/365 days, and can be revoked. Never embed keys in browser JavaScript, mobile clients, public repositories, or agent prompts.

Scopes: account:read, measurements:read, measurements:write, properties:read, webhooks:read, webhooks:write. Removing or demoting the creator below admin invalidates their keys. A banned creator cannot authenticate. Keys cannot buy credits, administer users, or create more API keys.

## Endpoints

- GET /account: workspace and key creator's credits.
- POST /measurement-jobs: queue one address; JSON `{ "address": "COMPLETE_US_ADDRESS" }`; required Idempotency-Key header.
- POST /measurement-jobs/batch: JSON `{ "jobs": [{ "address": "COMPLETE_US_ADDRESS", "idempotency_key": "unique-property-1" }] }`; 1–25 items.
- GET /measurement-jobs: list workspace jobs.
- GET /measurement-jobs/{id}: poll status and resulting measurement_id.
- DELETE /measurement-jobs/{id}: cancel a non-terminal job created by the same user.
- GET /measurements: list saved estimates.
- GET /measurements/{id}: area totals, normalized geometry, and a snapshot link valid for five minutes.
- GET /properties: group saved records by normalized address.
- GET /properties/{id}: retrieve a property's latest_measurement_id.
- GET /webhooks: list receivers, excluding signing secrets.
- POST /webhooks: JSON `{ "url": "APPROVED_HTTPS_RECEIVER" }`; returns signing_secret once.
- DELETE /webhooks/{id}: disable a receiver.
- POST /webhooks/{id}/test: queue a test event without spending credits.
- GET /webhooks/{id}/deliveries: inspect recent attempts and status.

## Jobs, retries, and credits

POST returns 202 acceptance, not a completed measurement. Poll every 5–10 seconds. States: queued, running, completed, failed, canceled. Completed jobs include measurement_id. API jobs use the key creator's account credits, shared with app usage. Reserve at execution start; debit on completion; release on failure/cancellation. Reading saved results and polling are free. Queue acceptance does not guarantee later credit availability.

Idempotency keys are 8–128 letters, numbers, hyphens, or underscores. Reuse the SAME key and address to retry under the same creator/workspace. A conflicting address returns 409. A new key can create a new paid measurement. Batch responses can mix accepted jobs and per-item errors; inspect each item and retry errors with their original keys.

## Results

List envelopes contain data, next_cursor, and request_id. Limit defaults to 25, maximum 100; pass next_cursor as cursor until null. Properties may move in the list when a new measurement is saved; upsert/deduplicate by stable ID.

Geometry uses normalized image coordinates, x/y in [0,1], origin top-left. It is NOT longitude/latitude GeoJSON. Preserve the complete image frame. Apply polygon additions and subtractions; totals separately identify parcel lawn, verge, and inferred maintained extensions when available. Private snapshot links expire in 300 seconds. Fetch the detail again for a fresh link.

Aerial estimates are not legal surveys or guaranteed accuracy. Review the overlay and uncertain areas before using square footage in a quote. Maintained extensions do not establish ownership.

## Webhooks

Events: measurement.completed, measurement.failed, measurement.canceled, webhook.test. Receivers get terminal jobs for their workspace, including browser-started measurements. Minimal payload: id, type, created_at, data containing job_id, measurement_id, status. Fetch full results using your API key.

Verify OpenLawn-Signature: `t=TIMESTAMP,v1=HEX_HMAC`. Compute HMAC-SHA256 with the signing secret over `TIMESTAMP + "." + EXACT_RAW_BODY`. Use constant-time comparison, reject timestamps more than five minutes old, and deduplicate event IDs. Durably save an event before responding 2xx within 10 seconds.

Dispatch checks every minute. Delivery is at least once while retries remain, may arrive out of order, and allows five attempts with increasing delay. Redirects are never followed. Disable stops future attempts but an in-flight request may finish.

Supported receiver hosts: hooks.zapier.com, hook.us1.make.com, hook.us2.make.com, hook.eu1.make.com, hook.eu2.make.com, hook.integromat.com. Custom HTTPS domains require approval from support@joinclicki.com. Arbitrary domains, private IP URLs, credentials, custom ports, and fragments are rejected.

## Limits and errors

120 authenticated requests per workspace per minute across all keys; 429 returns Retry-After: 60. At most 20 active keys, five active receivers, five test events per receiver per minute, 25 jobs per batch, and 100 queued/running jobs per creator. Worker concurrency is bounded and shared.

Errors: 400 invalid input, 401 invalid key, 403 insufficient scope, 404 record unavailable, 409 conflict, 413 oversized request, 422 receiver not approved, 429 rate limited, 503 temporarily unavailable. Responses include request_id; contact support with it, never your secrets. Use backoff and jitter for transient errors while retaining idempotency keys.
