Developers
Estibrain API reference
Pull your estimates, proposals, and invoices into your own tools, and receive real-time webhooks when things happen. The machine-readable spec lives at /api/v1/openapi (OpenAPI 3.1).
Authentication
Create an API key in Settings → API (you'll see it once). Send it as a Bearer token on every request. Keys are scoped to your organization: read, or read + write (needed to send customers and leads in).
curl https://cortex.structureworkstn.com/api/v1/estimates \
-H "Authorization: Bearer estibrain_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"Rate limit: 120 requests / minute per key. Exceeding it returns 429.
Endpoints
/api/v1/leadsA lead from your CRM, website or form tool: the customer (matched by external_ref, then email, else created) plus a job at stage Lead on the pipeline. Safe to send twice.
Returns: customer_id, customer_created, job_id, job_created, stage
/api/v1/customersList customers. Filter with ?email=, ?external_ref= (your CRM's id) or ?q= (name/company/email).
Returns: id, name, company_name, email, phone, address, lifecycle, source, external_ids
/api/v1/customersCreate a customer, or update the existing match (same external_ref, then same email). Existing details are only filled in, never overwritten.
Returns: the customer + created (true/false)
/api/v1/customers/{id}One customer with their jobs and pipeline stages.
Returns: customer + jobs[] (id, name, status, estimated_value)
/api/v1/customers/{id}Change name, company_name, email, phone, address, notes, lifecycle or external_ref. Omitted fields are kept.
Returns: the updated customer
/api/v1/pipelineEvery job with its stage (lead, estimating, proposed, won, lost, active, complete), customer and value.
Returns: id, name, stage, customer_id, value, external_ids
/api/v1/estimatesList your 100 most recent estimates.
Returns: id, title, status, total, created_at
/api/v1/estimates/{id}One estimate with line items, assumptions, exclusions, and needs-review flags.
Returns: estimate + lines[] (description, qty, unit, unit_cost, price_source, confidence)
/api/v1/proposalsList your 100 most recent proposals.
Returns: id, number, title, status, total, signed_at
/api/v1/invoicesList your 100 most recent invoices.
Returns: id, number, status, total, amount_paid, balance
All responses are JSON: { "data": … } on success, { "error": "…" } otherwise (401 bad key, 403 scope/plan, 404 not found, 429 rate limit).
Webhooks
Add endpoints in Settings → Webhooks. Estibrain POSTs a JSON body to each active endpoint subscribed to the event:
{
"event": "invoice.paid",
"data": { "invoice_id": "…", "number": "INV-1042", "amount": 2500 },
"ts": 1760000000000
}| Event | Fires when |
|---|---|
| estimate.created | An estimate is saved (by the AI estimator or the API). |
| proposal.sent | A proposal is marked sent and its client link goes live. |
| proposal.signed | A client signs a proposal on the public portal. |
| invoice.paid | A payment lands on an invoice (Stripe checkout completes). |
| change_order.approved | A change order is approved. |
| customer.created | A customer is added (in the app, by the API, or from a connected system). |
| customer.updated | A customer's details change. |
| lead.created | A new lead (customer + job) arrives through the API or a connected system. |
| project.status_changed | A job moves stage on the pipeline (e.g. proposed → won). |
Verifying signatures
Every delivery includes an X-Estibrain-Signature header (also sent as the deprecated X-Cortex-Signature during the rename) — a hex HMAC-SHA256 of the raw request body using your endpoint's signing secret (shown once at creation). Reject anything that doesn't match:
import { createHmac, timingSafeEqual } from "node:crypto";
function verify(rawBody: string, header: string, secret: string): boolean {
const expected = createHmac("sha256", secret).update(rawBody).digest("hex");
return (
header.length === expected.length &&
timingSafeEqual(Buffer.from(header), Buffer.from(expected))
);
}Respond with any 2xx to acknowledge. Failed deliveries are logged and visible in Settings → Webhooks.
Connect the CRM you already use
Estibrain keeps its own customers and pipeline, and stays in step with yours through the API and webhooks — directly, or with Zapier / Make / n8n. Send your CRM's own record id as external_ref and nothing ever duplicates.
New lead in your CRM → a job in Estibrain
curl -X POST https://cortex.structureworkstn.com/api/v1/leads \
-H "Authorization: Bearer estibrain_live_…" -H "Content-Type: application/json" \
-d '{
"customer": { "name": "Dana Price", "email": "dana@example.com",
"phone": "(865) 555-0142", "external_ref": "crm-contact-881" },
"lead": { "title": "Composite deck 14x20", "address": "100 Main St",
"zip": "37919", "estimated_value": 18500, "external_ref": "crm-deal-4410" }
}'Estibrain → your CRM
proposal.signed→ mark the deal won and attach the amount.project.status_changed→ move the deal to the matching stage.customer.created/customer.updated→ keep contacts in step.estimate.created→ log the estimate total on the deal.
Building your own app? Use Estibrain Connect
If your company runs its own CRM or project app, implement the three-operation Estibrain Connect protocol and add it in Settings → Integrations: Estibrain pulls your customers and leads and sends finished estimates back for a person to review.