Developer API
Start an asynchronous hotel flooring take-off, follow validation and measurement, then retrieve grounded quantities and evidence as JSON.
https://carpet-area.inhabitr.aiDocument contract
The service returns the strongest result supported by the uploaded pages.
Authentication
Use an API key for services or a session token for the dashboard.
API key
Send the key in the Authorization header on every protected request.
curl https://carpet-area.inhabitr.ai/api/v1/takeoffs \ -H "Authorization: Bearer sk_live_your_key"
Session token
Login returns a signed token valid for seven days.
curl -X POST https://carpet-area.inhabitr.ai/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"estimator","password":"your-password"}'
{
"token": "eyJhbGciOiJIUzI1NiIs...",
"username": "estimator"
}EventSource cannot set an Authorization header, so the events endpoint also accepts ?token=. Use HTTPS and avoid logging the query string.Create a take-off
Upload one hotel PDF and receive a job immediately.
Request
curl -X POST https://carpet-area.inhabitr.ai/api/v1/takeoffs \ -H "Authorization: Bearer sk_live_your_key" \ -F "file=@hotel-floor-plans.pdf" \ -F "shadow_mode=false"
Response
{
"job_id": "a26f62a2-6fc2-4cad-89f3-3b76a4f42c14",
"status": "queued",
"links": {
"self": "/api/v1/takeoffs/a26f62a2-6fc2-4cad-89f3-3b76a4f42c14",
"events": "/api/v1/takeoffs/a26f62a2-6fc2-4cad-89f3-3b76a4f42c14/events",
"result": "/api/v1/takeoffs/a26f62a2-6fc2-4cad-89f3-3b76a4f42c14/result"
}
}The multipart request also accepts mode and shadow_mode. Trusted server integrations may submit an existing private-storage object using storage_path and pdf_name instead of a file.
Job lifecycle
Poll status or consume the event stream until the job reaches a terminal state.
The PDF is being stored. This state is normally brief.
The job is waiting for an available worker.
The agent is validating, planning, measuring, and checking evidence.
Quantities exist, but an advisory gate requires human review.
The result passed required gates or received reviewer approval.
Legacy or explicit reviewer rejection. New automated runs preserve supported output as needs_review.
Processing failed after automatic retries were exhausted.
A queued or running job was cancelled by a client.
Status request
curl https://carpet-area.inhabitr.ai/api/v1/takeoffs/JOB_ID \ -H "Authorization: Bearer sk_live_your_key"
Status response
{
"job_id": "JOB_ID",
"status": "needs_review",
"pdf_name": "hotel-floor-plans.pdf",
"created_at": "2026-07-23T18:30:00+00:00",
"duration_ms": 21482,
"error": null,
"summary": {
"net_area": 34341,
"order_area": 37775,
"room_count": 130,
"confidence": "high"
}
}Server-sent events
Receive ordered progress logs followed by one terminal event.
Connect
curl -N "https://carpet-area.inhabitr.ai/api/v1/takeoffs/JOB_ID/events?token=sk_live_your_key"
Event stream
event: log
data: {"elapsed_ms":842,"level":"info","message":"Document preflight passed"}
event: log
data: {"elapsed_ms":19420,"level":"tool","message":"Quality gates complete"}
event: needs_review
data: {"job_id":"JOB_ID","status":"needs_review","duration_ms":21482}Log events use event name log. The final event name matches the terminal status: done, needs_review, rejected, error, or cancelled. Reconnect by reading current job status first because the stream does not expose an event cursor.
Human review
Reviewer actions preserve agent judgment while controlling unsupported conclusions.
Approve a result
curl -X POST https://carpet-area.inhabitr.ai/api/v1/takeoffs/JOB_ID/review \
-H "Authorization: Bearer sk_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"action": "approve",
"notes": "Scale and room count verified.",
"corrections": {}
}'Response
{
"job_id": "JOB_ID",
"action": "approve",
"status": "done"
}approvechangesneeds_reviewtodone.request_reanalysisreturns the job toqueued.commentrecords notes without changing status.rejectmakes the job terminal.
Generate a report
Customer reports are validated against the take-off JSON before publication.
Request
curl -X POST https://carpet-area.inhabitr.ai/api/v1/takeoffs/JOB_ID/report \ -H "Authorization: Bearer sk_live_your_key"
The job must be done. Reviewer-approved results may retain advisory evidence notes in the generated report.
Response
{
"report_url": "https://carpet-area.inhabitr.ai/api/v1/takeoffs/JOB_ID/report/view?token=...",
"validation": {
"outcome": "needs_review",
"review_gates": 1
}
}Errors
Non-2xx responses use a consistent JSON detail field.
{
"detail": "The PDF exceeds the 25 MB upload limit."
}| Status | Meaning | Typical cause |
|---|---|---|
| 400 | Invalid request | Missing upload input, unsupported review action, or malformed request data. |
| 401 | Unauthorized | The Bearer credential or report-specific access token is missing, invalid, inactive, or expired. |
| 404 | Not found | The job, result, metrics, or learning record does not exist or is not ready. |
| 409 | Invalid state | The action conflicts with job state, or report validation found inconsistent quantities. |
| 413 | Upload too large | The PDF is larger than 25 MB. |
| 415 | Unsupported media | The upload is not a PDF or does not contain a valid PDF signature. |
| 422 | Validation failed | A typed path, query, form, or JSON field failed request validation. |
| 500 | Server error | An unexpected service error occurred. Retry only idempotent requests automatically. |
Limits and pagination
List endpoints use bounded offset pagination and include navigation metadata.
Page request
curl "https://carpet-area.inhabitr.ai/api/v1/takeoffs?limit=2&offset=0" \ -H "Authorization: Bearer sk_live_your_key"
Pagination metadata
{
"takeoffs": [
{"id":"JOB_ID_1","pdf_name":"hotel-a.pdf","status":"done"},
{"id":"JOB_ID_2","pdf_name":"hotel-b.pdf","status":"needs_review"}
],
"pagination": {
"limit": 2,
"offset": 0,
"count": 2,
"has_more": true,
"next_offset": 2
}
}Continue while has_more is true and pass next_offset to the next request. Negative offsets are treated as zero. Oversized limits are reduced to the endpoint maximum. The service does not currently publish rate-limit headers; clients should still handle 429 and transient 5xx responses with bounded exponential backoff.
Endpoint reference
Protected endpoints require Bearer authentication unless marked public.
| Group | Method | Path | Description |
|---|---|---|---|
| Take-offs | POST | /api/v1/takeoffs | Upload a hotel floor-plan PDF. Returns 202 with a queued job. |
| Take-offs | GET | /api/v1/takeoffs | List take-offs with summaries and offset pagination. |
| Take-offs | GET | /api/v1/takeoffs/:id | Read job status, rejection details, links, and summary quantities. |
| Take-offs | GET | /api/v1/takeoffs/:id/result | Read the schema-conformant take-off JSON when a result is available. |
| Take-offs | GET | /api/v1/takeoffs/:id/events | Stream progress logs and the terminal job state over SSE. |
| Review | POST | /api/v1/takeoffs/:id/review | Approve, reject, comment, or request evidence-guided reanalysis. |
| Review | POST | /api/v1/takeoffs/:id/cancel | Cancel a queued or running job. |
| Review | POST | /api/v1/takeoffs/:id/report | Validate and generate a signed HTML report URL for an approved result. |
| Review | GET | /api/v1/takeoffs/:id/report/view | Render a generated report using its short-lived report token. |
| Observability | GET | /api/v1/takeoffs/:id/metrics | Read stage, agent, render DPI, resource fallback, evidence, cache, plan, review, and gate telemetry. |
| Observability | GET | /api/v1/metrics/overview | Read dashboard aggregates, stage latency, outcomes, and recent telemetry. |
| Learning hints | GET | /api/v1/learnings | List candidate, approved, and disabled reusable hints with pagination. |
| Learning hints | POST | /api/v1/learnings/:id/approve | Approve an evidence-backed candidate as a non-binding hint. |
| Learning hints | POST | /api/v1/learnings/:id/disable | Disable a reusable hint. |
| Public | GET | /healthz | Read service health and version without authentication. |
| Public | GET | /api/v1/schema | Read the JSON Schema for take-off results without authentication. |
Result contract
Every quantity remains reviewable and traceable to its measurement evidence.
Retrieve a result
curl https://carpet-area.inhabitr.ai/api/v1/takeoffs/JOB_ID/result \ -H "Authorization: Bearer sk_live_your_key"
A result may be retrieved for done and needs_review jobs. Rejected documents have no result.
Core shape
{
"schema_version": "1.0.0",
"property": {
"name": "Example Hotel",
"source_document": {
"filename": "hotel-floor-plans.pdf",
"page_count": 18
}
},
"takeoff": {
"unit_system": "imperial",
"area_unit": "sqft",
"target_finish": "carpet",
"grounding": {},
"buildings": [],
"totals": {
"net_area": 34341,
"waste_factor": 0.1,
"order_area": 37775,
"unit": "sqft",
"guest_room_count": 130,
"by_category": {},
"by_room_type": {},
"by_floor": {}
}
}
}GET /api/v1/schema. Unknown room-type labels are preserved in takeoff.totals.by_room_type; they are not used to decide whether the document is a supported hotel plan.