{"openapi":"3.1.0","info":{"title":"DFM API","version":"1.0.0","description":"Org-scoped REST API for system-to-system integration: pull the verified work record\n(runs, deficiencies, equipment, tenant requests) into your CMMS/ERP, or push work in.\n\n**Authentication**: create a key under *Settings → API & integrations*, then send it on\nevery request: `Authorization: Bearer sbk_live_…`. Keys carry `read` and/or `write` scope\nand are shown once at creation.\n\n**Rate limits**: per key, 600 read requests / 5 min, 120 write requests / 5 min.\n429 responses include `Retry-After`.\n\n**Conventions**: all timestamps are ISO 8601 UTC; list endpoints return\n`{ data, pagination }`; errors return `{ error: { code, message } }`; the wire format is\ncamelCase and versioned under `/api/v1`; breaking changes mean a new version prefix.\n\n**Webhooks**: outbound events (`run.completed`, `deficiency.opened`,\n`deficiency.resolved`, `work_request.created`) are configured under\n*Settings → Integrations* and signed with `X-DFM-Signature: t=<unix>,v1=<hmac>`\nwhere `v1 = HMAC_SHA256(secret, \"{t}.{rawBody}\")`; reject timestamps older than 5 minutes."},"servers":[{"url":"/api/v1"}],"security":[{"apiKey":[]}],"paths":{"/ping":{"get":{"operationId":"ping","summary":"Verify a key","description":"Returns the organization the key belongs to and its scopes. The first call to make.","tags":["Meta"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"organizationId":{"type":"string","format":"uuid"},"organizationName":{"type":"string","nullable":true},"scopes":{"type":"array","items":{"type":"string","enum":["read","write"]}}}}}}},"401":{"description":"Missing, malformed, or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the required scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded. Honor the Retry-After header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sites":{"get":{"operationId":"listSites","summary":"List sites","tags":["Sites"],"parameters":[{"name":"limit","in":"query","description":"Page size, 1–200. Default 50.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"offset","in":"query","description":"Rows to skip. Default 0.","schema":{"type":"integer","minimum":0,"default":0}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Site"}},"pagination":{"$ref":"#/components/schemas/Pagination"}},"required":["data","pagination"]}}}},"401":{"description":"Missing, malformed, or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the required scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded. Honor the Retry-After header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/assets":{"get":{"operationId":"listAssets","summary":"List assets","description":"The equipment register, including manufacturer/model/serial, install date, and warranty.","tags":["Assets"],"parameters":[{"name":"limit","in":"query","description":"Page size, 1–200. Default 50.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"offset","in":"query","description":"Rows to skip. Default 0.","schema":{"type":"integer","minimum":0,"default":0}},{"name":"siteId","in":"query","description":"Filter to one site.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Asset"}},"pagination":{"$ref":"#/components/schemas/Pagination"}},"required":["data","pagination"]}}}},"400":{"description":"Invalid parameter.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the required scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded. Honor the Retry-After header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/workflows":{"get":{"operationId":"listWorkflows","summary":"List workflows","tags":["Workflows"],"parameters":[{"name":"limit","in":"query","description":"Page size, 1–200. Default 50.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"offset","in":"query","description":"Rows to skip. Default 0.","schema":{"type":"integer","minimum":0,"default":0}},{"name":"includeDrafts","in":"query","description":"Include unpublished workflows.","schema":{"type":"boolean","default":false}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Workflow"}},"pagination":{"$ref":"#/components/schemas/Pagination"}},"required":["data","pagination"]}}}},"401":{"description":"Missing, malformed, or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the required scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded. Honor the Retry-After header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/runs":{"get":{"operationId":"listRuns","summary":"List runs","description":"The execution log: who did what, when, with which identity verification and review state.","tags":["Runs"],"parameters":[{"name":"limit","in":"query","description":"Page size, 1–200. Default 50.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"offset","in":"query","description":"Rows to skip. Default 0.","schema":{"type":"integer","minimum":0,"default":0}},{"name":"status","in":"query","schema":{"type":"string","enum":["in_progress","completed","abandoned","expired"]}},{"name":"siteId","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"from","in":"query","description":"started_at ≥ this ISO timestamp.","schema":{"type":"string","format":"date-time"}},{"name":"to","in":"query","description":"started_at ≤ this ISO timestamp.","schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Run"}},"pagination":{"$ref":"#/components/schemas/Pagination"}},"required":["data","pagination"]}}}},"400":{"description":"Invalid parameter.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the required scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded. Honor the Retry-After header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/runs/{id}":{"get":{"operationId":"getRun","summary":"Get one run with all recorded steps","tags":["Runs"],"parameters":[{"name":"id","in":"path","required":true,"description":"Run id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/RunDetail"}}}}}},"401":{"description":"Missing, malformed, or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the required scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found in your organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded. Honor the Retry-After header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/deficiencies":{"get":{"operationId":"listDeficiencies","summary":"List deficiencies","tags":["Deficiencies"],"parameters":[{"name":"limit","in":"query","description":"Page size, 1–200. Default 50.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"offset","in":"query","description":"Rows to skip. Default 0.","schema":{"type":"integer","minimum":0,"default":0}},{"name":"status","in":"query","schema":{"type":"string","enum":["open","in_progress","resolved","dismissed"]}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Deficiency"}},"pagination":{"$ref":"#/components/schemas/Pagination"}},"required":["data","pagination"]}}}},"401":{"description":"Missing, malformed, or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the required scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded. Honor the Retry-After header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"createDeficiency","summary":"Open a deficiency","description":"Requires `write` scope. Source is recorded as `manual`.","tags":["Deficiencies"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDeficiency"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Deficiency"}}}}}},"401":{"description":"Missing, malformed, or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the required scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation failed (details in error.message).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded. Honor the Retry-After header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/work-requests":{"get":{"operationId":"listWorkRequests","summary":"List tenant requests","description":"Reporter email is intentionally omitted. Tenant PII stays inside DFM.","tags":["Work requests"],"parameters":[{"name":"limit","in":"query","description":"Page size, 1–200. Default 50.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"offset","in":"query","description":"Rows to skip. Default 0.","schema":{"type":"integer","minimum":0,"default":0}},{"name":"status","in":"query","schema":{"type":"string","enum":["new","scheduled","done","declined"]}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WorkRequest"}},"pagination":{"$ref":"#/components/schemas/Pagination"}},"required":["data","pagination"]}}}},"401":{"description":"Missing, malformed, or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the required scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded. Honor the Retry-After header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"createWorkRequest","summary":"File a request","description":"Requires `write` scope. Use this to bridge an existing tenant portal or ticketing system in.","tags":["Work requests"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWorkRequest"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/WorkRequest"}}}}}},"401":{"description":"Missing, malformed, or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the required scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded. Honor the Retry-After header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/assignments":{"post":{"operationId":"createAssignment","summary":"Schedule a workflow run","description":"Requires `write` scope. The assignee (optional) is resolved by member email.","tags":["Assignments"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAssignment"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string"},"dueAt":{"type":"string","format":"date-time","nullable":true},"createdAt":{"type":"string","format":"date-time"}}}}}}}},"401":{"description":"Missing, malformed, or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the required scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation failed (unknown workflow / unpublished / unknown assignee).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded. Honor the Retry-After header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","bearerFormat":"sbk_live_…","description":"Org-level API key from Settings → API & integrations."}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]}}},"Pagination":{"type":"object","properties":{"limit":{"type":"integer"},"offset":{"type":"integer"},"count":{"type":"integer","description":"Rows in this page. count < limit ⇒ last page."}}},"Site":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"address":{"type":"string","nullable":true},"timezone":{"type":"string","nullable":true,"description":"IANA zone, e.g. America/New_York."},"createdAt":{"type":"string","format":"date-time"}}},"Asset":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"siteId":{"type":"string","format":"uuid","nullable":true},"name":{"type":"string"},"assetTag":{"type":"string","nullable":true},"category":{"type":"string","nullable":true},"manufacturer":{"type":"string","nullable":true},"modelNumber":{"type":"string","nullable":true},"serialNumber":{"type":"string","nullable":true},"installedOn":{"type":"string","format":"date","nullable":true},"expectedLifeYears":{"type":"integer","nullable":true},"warrantyExpiresAt":{"type":"string","format":"date","nullable":true},"warrantyProvider":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time"}}},"Workflow":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"siteId":{"type":"string","format":"uuid","nullable":true},"name":{"type":"string"},"description":{"type":"string","nullable":true},"category":{"type":"string","nullable":true},"currentVersionId":{"type":"string","format":"uuid","nullable":true},"published":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"}}},"Run":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"siteId":{"type":"string","format":"uuid","nullable":true},"assetId":{"type":"string","format":"uuid","nullable":true},"workflowVersionId":{"type":"string","format":"uuid"},"actorUserId":{"type":"string","format":"uuid"},"relationshipType":{"type":"string","nullable":true,"enum":["employee","internal_contractor","vendor",null]},"status":{"type":"string","enum":["in_progress","completed","abandoned","expired"]},"startedAt":{"type":"string","format":"date-time"},"checkedInAt":{"type":"string","format":"date-time","nullable":true},"checkedOutAt":{"type":"string","format":"date-time","nullable":true},"completedAt":{"type":"string","format":"date-time","nullable":true},"durationSeconds":{"type":"integer","nullable":true},"verifiedVia":{"type":"string","nullable":true,"enum":["pin","email",null],"description":"How the worker proved identity after the scan."},"reviewStatus":{"type":"string","nullable":true,"enum":["pending","approved","rejected",null]}}},"RunDetail":{"allOf":[{"$ref":"#/components/schemas/Run"},{"type":"object","properties":{"reviewNote":{"type":"string","nullable":true},"reviewedAt":{"type":"string","format":"date-time","nullable":true},"checkInLat":{"type":"number","nullable":true},"checkInLng":{"type":"number","nullable":true},"steps":{"type":"array","items":{"type":"object","properties":{"stepKey":{"type":"string"},"stepType":{"type":"string"},"value":{"description":"The captured answer; shape depends on stepType."},"sequence":{"type":"integer"},"capturedAt":{"type":"string","format":"date-time"}}}}}}]},"Deficiency":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"siteId":{"type":"string","format":"uuid","nullable":true},"assetId":{"type":"string","format":"uuid","nullable":true},"runId":{"type":"string","format":"uuid","nullable":true,"description":"The run that found it, when detector-opened."},"source":{"type":"string","enum":["detector","manual"]},"severity":{"type":"string","enum":["low","medium","high","critical"]},"status":{"type":"string","enum":["open","in_progress","resolved","dismissed"]},"title":{"type":"string"},"detail":{"type":"string","nullable":true},"assignedToUserId":{"type":"string","format":"uuid","nullable":true},"dueAt":{"type":"string","format":"date-time","nullable":true},"resolvedAt":{"type":"string","format":"date-time","nullable":true},"resolutionNote":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time"}}},"CreateDeficiency":{"type":"object","required":["title"],"properties":{"title":{"type":"string","minLength":4,"maxLength":200},"detail":{"type":"string","maxLength":2000},"severity":{"type":"string","enum":["low","medium","high","critical"],"default":"medium"},"siteId":{"type":"string","format":"uuid"},"assetId":{"type":"string","format":"uuid"},"dueAt":{"type":"string","format":"date-time"}}},"WorkRequest":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"siteId":{"type":"string","format":"uuid"},"assetId":{"type":"string","format":"uuid","nullable":true},"source":{"type":"string","enum":["portal","staff"]},"reporterName":{"type":"string"},"reporterUnit":{"type":"string","nullable":true},"category":{"type":"string","enum":["hvac","plumbing","electrical","elevator","cleaning","safety","other"]},"title":{"type":"string"},"detail":{"type":"string","nullable":true},"priority":{"type":"string","enum":["low","normal","high","urgent"]},"status":{"type":"string","enum":["new","scheduled","done","declined"]},"assignmentId":{"type":"string","format":"uuid","nullable":true},"closedNote":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time"}}},"CreateWorkRequest":{"type":"object","required":["siteId","reporterName","title"],"properties":{"siteId":{"type":"string","format":"uuid"},"reporterName":{"type":"string","minLength":2,"maxLength":120},"reporterEmail":{"type":"string","format":"email","description":"Optional; the reporter gets status emails."},"reporterUnit":{"type":"string","maxLength":80},"category":{"type":"string","enum":["hvac","plumbing","electrical","elevator","cleaning","safety","other"],"default":"other"},"title":{"type":"string","minLength":4,"maxLength":160},"detail":{"type":"string","maxLength":2000},"priority":{"type":"string","enum":["low","normal","high","urgent"],"default":"normal"}}},"CreateAssignment":{"type":"object","required":["workflowId"],"properties":{"workflowId":{"type":"string","format":"uuid","description":"Must have a published version."},"siteId":{"type":"string","format":"uuid"},"assetId":{"type":"string","format":"uuid"},"assigneeEmail":{"type":"string","format":"email","description":"Must be an active member."},"dueAt":{"type":"string","format":"date-time"},"priority":{"type":"string","enum":["low","normal","high","critical"],"default":"normal"},"notes":{"type":"string","maxLength":500}}}}}}