The public API is a JSON REST API for reading and writing your own account's leads, contacts, properties, jobs, estimates, invoices, files and measurement orders with a Bearer API key. Signed webhooks notify your system when those records change. API keys and webhooks are included on the Enterprise plan; see pricing.
Every request needs a Bearer token. Generate an API key from Settings → Developer & Zapier inside your CRM account (requires an Enterprise plan). The full key is shown once, so store it when you create it.
Authorization: Bearer ar_live_YOUR_API_KEY
Keys are prefixed ar_live_ for production or ar_test_ for a sandbox-style key — both authenticate the same way and hit your real account data (there is no separate test environment; use a test key when you want to rotate/revoke credentials independently of your production integration).
List your jobs:
curl https://crm.aerialreports.com/api/jobs \ -H "Authorization: Bearer ar_live_..."
Create a lead:
curl -X POST https://crm.aerialreports.com/api/leads \
-H "Authorization: Bearer ar_live_..." \
-H "Content-Type: application/json" \
-d '{
"first_name": "Jamie", "last_name": "Rivera",
"phone": "+15125550100", "address": "412 Oak St, Austin, TX",
"source": "zapier"
}'
These resource endpoints follow the same shape: GET (list; supports ?q= text search and ?limit=, default 500, maximum 1000), GET /:id, POST (create), PATCH /:id (update) and DELETE /:id. An API key can reach only the paths in this table. Anything else, such as payments, tasks, conversations and reports, returns 403 for a key and is available in the CRM itself.
| Resource | Base path | Notes |
|---|---|---|
| Jobs | /api/jobs | Filter by property_id to get every job at one address. |
| Leads | /api/leads | |
| Contacts | /api/contacts | |
| Properties | /api/properties | One row per address; jobs link here via property_id. |
| Estimates | /api/estimates | Also /api/estimate-templates and /api/proposals under the same scope. |
| Invoices | /api/invoices | Includes an invoice_type field. |
| Measurement Orders | /api/measurement-orders | Lists and creates aerial measurement report orders and returns delivered results. |
| Files | /api/files | Presigned upload flow: POST /api/files/presign, then POST /api/files/confirm. Also /api/documents and /api/media. |
| Work orders | /api/work-orders | Requires a plan with work orders. |
When you create a key you choose its scopes. A request needs the matching resource:read (GET) or resource:write (everything else) scope, or the * wildcard. A missing scope returns 403.
Available scopes: *, leads:read, leads:write, jobs:read, jobs:write, contacts:read, contacts:write, estimates:read, estimates:write, invoices:read, invoices:write, files:read, files:write, measurements:read, webhooks:read, webhooks:write.
Creating or revoking API keys and registering webhook endpoints is done by a signed-in person in Settings → Developer & Zapier, not with an API key.
Register an endpoint from Settings → Developer & Zapier and we POST a signed JSON payload when an event happens, so you do not have to poll. Endpoints must be reachable http(s) URLs; private and internal addresses are rejected.
| Event | Fires when… |
|---|---|
job.created / job.updated | A job is created or edited. |
lead.created / lead.updated | A lead is created or edited. |
contact.created | A new contact is added. |
estimate.created / estimate.sent | An estimate is created, or sent to the homeowner. |
invoice.created / invoice.paid | An invoice is created, or its balance reaches zero. |
payment.created | A real payment clears. |
sign_request.signed / sign_request.declined | A homeowner signs or declines a signature request. |
measurement_order.delivered / measurement_order.imported | A measurement report is delivered, or its quantities are imported into a job. |
file.uploaded | A file finishes uploading (photos, documents). |
{
"event": "job.created",
"created_at": "2026-07-08T18:04:11.000Z",
"data": { "id": "job_abc123", "client_name": "Jamie Rivera", "...": "..." }
}
Every delivery includes:
X-AerialReports-Signature: t=1751234567,v1=8f3c...e21a X-AerialReports-Event: job.created
Compute the HMAC-SHA256 of {timestamp}.{raw body} using the webhook's signing secret (shown once, when you create the endpoint) and compare to v1:
// Node.js
const crypto = require('crypto');
function verify(rawBody, sigHeader, secret) {
const [t, v1] = sigHeader.split(',').map(s => s.split('=')[1]);
const expected = crypto.createHmac('sha256', secret)
.update(`${t}.${rawBody}`).digest('hex');
return crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected));
}
Failed deliveries retry up to 3 times (immediately, +30s, +5min). Every attempt is logged — the last 50 are shown in Settings → Developer & Zapier.
invoice.paid, sign_request.signed).There is no native Zapier app. To write back into the CRM (for example, create a lead when a new Google Sheets row appears), use Zapier's Webhooks by Zapier → POST action with an API key, targeting a resource endpoint above.
?limit= on list calls and webhooks instead of tight polling.