API Documentation

Integrate OperantOS products into your systems using webhook endpoints. All endpoints accept JSON payloads and return JSON responses.

Base URL: https://operantos.ai/api

Authentication

All API requests require authentication via a product-specific API key sent in a custom HTTP header. API keys are generated in your dashboard under Settings > API Keys.

ProductHeader NameFormat
CloseBotX-CloseBot-Keycb_live_...
LedgerPilotX-LedgerPilot-Keylp_live_...
FixFlowX-FixFlow-Keyff_live_...

Include Content-Type: application/json on all requests with a body.

Security: Keep your API keys secret. Do not expose them in client-side code, public repositories, or browser requests. Rotate keys immediately if compromised.

CloseBot Webhook

POST/api/closebot/webhook

Create an estimate and trigger the automated follow-up sequence. CloseBot begins the configured follow-up schedule immediately upon receiving a valid request.

Request Body

FieldTypeRequiredDescription
customer_namestringYesFull name of the customer
customer_emailstringYes*Customer email for email follow-ups
customer_phonestringYes*Customer phone for SMS follow-ups (E.164 format)
estimate_amountnumberNoEstimate total in dollars (e.g., 8500.00)
service_typestringNoType of service (e.g., "furnace replacement", "roof repair")
descriptionstringNoAdditional context or notes about the estimate
external_idstringNoYour system's unique ID for deduplication

* At least one of customer_email or customer_phone is required.

Response

json
{
  "id": "est_a1b2c3d4e5f6",
  "status": "scheduled",
  "followups_scheduled": 4,
  "first_followup_at": "2026-04-08T18:00:00Z"
}

Examples

curl

bash
curl -X POST https://operantos.ai/api/closebot/webhook \
  -H "Content-Type: application/json" \
  -H "X-CloseBot-Key: cb_live_your_api_key" \
  -d '{
    "customer_name": "Sarah Johnson",
    "customer_email": "[email protected]",
    "customer_phone": "+14165551234",
    "estimate_amount": 8500.00,
    "service_type": "furnace replacement",
    "description": "High-efficiency furnace install for 2-storey home",
    "external_id": "est-001"
  }'

JavaScript (fetch)

javascript
const response = await fetch("https://operantos.ai/api/closebot/webhook", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-CloseBot-Key": "cb_live_your_api_key",
  },
  body: JSON.stringify({
    customer_name: "Sarah Johnson",
    customer_email: "[email protected]",
    customer_phone: "+14165551234",
    estimate_amount: 8500.00,
    service_type: "furnace replacement",
    description: "High-efficiency furnace install for 2-storey home",
    external_id: "est-001",
  }),
});

const data = await response.json();
console.log(data.id, data.followups_scheduled);

Python (requests)

python
import requests

response = requests.post(
    "https://operantos.ai/api/closebot/webhook",
    headers={
        "Content-Type": "application/json",
        "X-CloseBot-Key": "cb_live_your_api_key",
    },
    json={
        "customer_name": "Sarah Johnson",
        "customer_email": "[email protected]",
        "customer_phone": "+14165551234",
        "estimate_amount": 8500.00,
        "service_type": "furnace replacement",
        "description": "High-efficiency furnace install for 2-storey home",
        "external_id": "est-001",
    },
)

data = response.json()
print(data["id"], data["followups_scheduled"])

FixFlow Webhook

POST/api/fixflow/requests

Create a maintenance request programmatically. FixFlow triages the issue, dispatches the appropriate vendor, and sends tenant acknowledgment automatically.

Request Body

FieldTypeRequiredDescription
unit_idstringYesProperty unit identifier (e.g., "4B" or "unit-123")
tenant_namestringYesTenant full name
tenant_phonestringYesTenant phone number (E.164 format)
descriptionstringYesDescription of the maintenance issue
categorystringNoIssue category: plumbing, hvac, electrical, appliance, structural, pest, general
urgencystringNoUrgency level: emergency, urgent, routine (auto-classified if omitted)

Response

json
{
  "id": "req_x7y8z9w0v1u2",
  "status": "triaged",
  "category": "plumbing",
  "urgency": "urgent",
  "vendor_dispatched": true,
  "tenant_notified": true,
  "estimated_response_minutes": 45
}

Examples

curl

bash
curl -X POST https://operantos.ai/api/fixflow/requests \
  -H "Content-Type: application/json" \
  -H "X-FixFlow-Key: ff_live_your_api_key" \
  -d '{
    "unit_id": "4B",
    "tenant_name": "Mike Chen",
    "tenant_phone": "+14165559876",
    "description": "Kitchen faucet is leaking badly, water pooling on floor",
    "category": "plumbing",
    "urgency": "urgent"
  }'

JavaScript (fetch)

javascript
const response = await fetch("https://operantos.ai/api/fixflow/requests", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-FixFlow-Key": "ff_live_your_api_key",
  },
  body: JSON.stringify({
    unit_id: "4B",
    tenant_name: "Mike Chen",
    tenant_phone: "+14165559876",
    description: "Kitchen faucet is leaking badly, water pooling on floor",
    category: "plumbing",
    urgency: "urgent",
  }),
});

const data = await response.json();
console.log(data.id, data.urgency, data.estimated_response_minutes);

Python (requests)

python
import requests

response = requests.post(
    "https://operantos.ai/api/fixflow/requests",
    headers={
        "Content-Type": "application/json",
        "X-FixFlow-Key": "ff_live_your_api_key",
    },
    json={
        "unit_id": "4B",
        "tenant_name": "Mike Chen",
        "tenant_phone": "+14165559876",
        "description": "Kitchen faucet is leaking badly, water pooling on floor",
        "category": "plumbing",
        "urgency": "urgent",
    },
)

data = response.json()
print(data["id"], data["urgency"], data["estimated_response_minutes"])

Rate Limits

All webhook endpoints are rate-limited to protect service quality. Limits apply per API key.

EndpointLimitWindow
/api/closebot/webhook100 requestsPer minute
/api/fixflow/requests60 requestsPer minute
All endpoints (daily)10,000 requestsPer day

Rate-limited responses include headers to help you manage your usage:

HeaderDescription
X-RateLimit-LimitMaximum requests allowed in the current window
X-RateLimit-RemainingRequests remaining in the current window
X-RateLimit-ResetUnix timestamp when the rate limit resets
Retry-AfterSeconds to wait before retrying (only on 429 responses)

When rate-limited, wait the number of seconds indicated by the Retry-After header before retrying. Use exponential backoff for reliability.

Error Codes

All errors return a JSON body with error and message fields.

json
{
  "error": "validation_error",
  "message": "customer_name is required"
}
StatusError CodeDescriptionAction
400validation_errorRequest body is missing required fields or contains invalid dataCheck field names, types, and required fields
401unauthorizedAPI key is missing or invalidVerify your API key in Settings > API Keys
403forbiddenAPI key does not have permission for this endpointEnsure you are using the correct product-specific key
429rate_limitedToo many requests in the current time windowWait for Retry-After seconds, then retry with exponential backoff
500internal_errorAn unexpected error occurred on our serversRetry after a short delay. Contact support if persistent