{"openapi":"3.1.0","info":{"title":"Sado AI API","version":"1.0.0","description":"Leads in, call results out. Authenticate with `Authorization: Bearer sado_live_…` (portal → Settings → API keys; each key has scopes). Errors are JSON `{error, issues?}`. Lists are newest-first with keyset cursors; the event feed is oldest-first. Rate limit per key: 600 requests/min (429 with Retry-After). Uzbek/Russian guide: https://sadoai.uz/developers"},"servers":[{"url":"https://api.sadoai.uz"}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"Scopes: leads:write, leads:read, calls:read, calls:content, catalog:write, onec:sync"},"webhookSignature":{"type":"apiKey","in":"header","name":"X-Sado-Signature","description":"sha256=<hex HMAC-SHA256(secret, `${X-Sado-Timestamp}.${raw body}`)>, timestamp within 5 minutes."}},"responses":{"Error":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"string","examples":["unauthorized","insufficient_scope","validation_failed","rate_limited"]},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}}}}}},"LeadInput":{"type":"object","required":["event_id","phone","consent"],"properties":{"event_id":{"type":"string","maxLength":200,"description":"Your id for this submission; repeats are recognised (idempotent)."},"external_id":{"type":"string","maxLength":200},"phone":{"type":"string","examples":["+998901112233","90 111 22 33"]},"name":{"type":"string","maxLength":120},"purpose":{"type":"string","maxLength":300},"consent":{"type":"object","required":["basis","evidence","captured_at"],"properties":{"basis":{"enum":["inbound_enquiry","explicit_consent","existing_customer"]},"evidence":{"type":"string","minLength":3,"maxLength":500,"examples":["website form #42"]},"captured_at":{"type":"string","format":"date-time"}}}}},"LeadResult":{"type":"object","properties":{"status":{"enum":["created","duplicate","suppressed"]},"leadId":{"type":"string"},"queued":{"type":"boolean"}}},"Lead":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"},"phone":{"type":"string"},"name":{"type":["string","null"]},"purpose":{"type":["string","null"]},"source":{"type":"string"},"external_id":{"type":["string","null"]},"fields":{"type":"object","description":"Answers the caller confirmed (key → {value, callId})."},"crm":{"type":["object","null"],"properties":{"type":{"type":"string"},"id":{"type":"string"}}},"received_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"Call":{"type":"object","properties":{"id":{"type":"string"},"direction":{"enum":["inbound","outbound"]},"status":{"type":"string"},"outcome":{"type":["string","null"],"examples":["booked","qualified","callback_requested"]},"from":{"type":["string","null"]},"to":{"type":["string","null"]},"started_at":{"type":"string","format":"date-time"},"ended_at":{"type":["string","null"],"format":"date-time"},"duration_sec":{"type":"integer"},"agent":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"version":{"type":"integer"}}},"lead_id":{"type":["string","null"]},"recording":{"type":"boolean"},"analysis":{"description":"Only with the calls:content scope.","type":"object","properties":{"summary":{"type":["string","null"]},"sentiment":{"type":["string","null"]},"temperature":{"enum":["hot","warm","cold",null]},"next_action":{"type":["string","null"]},"suggested_outcome":{"type":["string","null"]},"quality_score":{"type":["integer","null"],"minimum":0,"maximum":100,"description":"Quality v1: weighted checks (answered the question, only approved facts, required questions, clear next step, polite, ended cleanly)."}}}}},"Event":{"type":"object","required":["id","type","schema_version","occurred_at","organization_id","data"],"properties":{"id":{"type":"string","examples":["evt_3f1c…"]},"type":{"enum":["call.ended","call.analyzed","lead.created","lead.updated","booking.created","booking.cancelled"]},"schema_version":{"const":1},"occurred_at":{"type":"string","format":"date-time"},"organization_id":{"type":"string"},"data":{"type":"object","description":"call.* → {call, analysis?, portal_url}; lead.* → {lead, portal_url}; booking.* → {booking: {id, status, starts_at, ends_at, starts_local, timezone, customer_name, phone, purpose, calendar: {id, name, location}, call_id, lead_id}, portal_url}"}}},"CatalogItem":{"type":"object","required":["external_id","name"],"properties":{"external_id":{"type":"string","maxLength":200},"name":{"type":"string","maxLength":200},"name_alt":{"type":["string","null"],"description":"Another language or spelling; searched by the agent."},"sku":{"type":["string","null"],"maxLength":80},"category":{"type":["string","null"]},"description":{"type":["string","null"],"maxLength":2000},"price_uzs":{"type":["integer","null"],"minimum":0},"stock":{"type":["integer","null"]},"available":{"type":["boolean","null"],"description":"Default: stock > 0, or true without stock."}}}}},"paths":{"/healthz":{"get":{"summary":"Health check","responses":{"200":{"description":"OK"}}}},"/v1/me":{"get":{"summary":"The key's organization and scopes","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK"},"401":{"$ref":"#/components/responses/Error"}}}},"/v1/leads":{"post":{"summary":"Send a lead (it is called by the configured campaign)","security":[{"apiKey":["leads:write"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeadInput"}}}},"responses":{"200":{"description":"Duplicate or opted out"},"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeadResult"}}}},"422":{"$ref":"#/components/responses/Error"}}},"get":{"summary":"List leads","security":[{"apiKey":["leads:read"]}],"parameters":[{"name":"status","in":"query","schema":{"type":"string"}},{"name":"updated_since","in":"query","schema":{"type":"string","format":"date-time"}},{"name":"limit","in":"query","schema":{"type":"integer","maximum":200,"default":50}},{"name":"cursor","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Page","content":{"application/json":{"schema":{"type":"object","required":["data","next_cursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Lead"}},"next_cursor":{"type":["string","null"],"description":"Pass as `cursor` (lists) or `after` (events) for the next page."}}}}}},"401":{"$ref":"#/components/responses/Error"}}}},"/v1/leads/{id}":{"get":{"summary":"One lead","security":[{"apiKey":["leads:read"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Lead"},"404":{"$ref":"#/components/responses/Error"}}}},"/v1/calls":{"get":{"summary":"List phone calls","security":[{"apiKey":["calls:read"]}],"parameters":[{"name":"since","in":"query","schema":{"type":"string","format":"date-time"}},{"name":"limit","in":"query","schema":{"type":"integer","maximum":200,"default":50}},{"name":"cursor","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Page","content":{"application/json":{"schema":{"type":"object","required":["data","next_cursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Call"}},"next_cursor":{"type":["string","null"],"description":"Pass as `cursor` (lists) or `after` (events) for the next page."}}}}}}}}},"/v1/calls/{id}":{"get":{"summary":"One call (transcript with ?include=transcript and calls:content)","security":[{"apiKey":["calls:read"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"include","in":"query","schema":{"enum":["transcript"]}}],"responses":{"200":{"description":"Call"},"404":{"$ref":"#/components/responses/Error"}}}},"/v1/events":{"get":{"summary":"Event feed for systems that can't receive webhooks (oldest first, 30 days)","security":[{"apiKey":["calls:read","leads:read"]}],"parameters":[{"name":"types","in":"query","schema":{"type":"string","examples":["call.analyzed,lead.created"]}},{"name":"after","in":"query","schema":{"type":"string"},"description":"The previous page's next_cursor."},{"name":"limit","in":"query","schema":{"type":"integer","maximum":500,"default":100}}],"responses":{"200":{"description":"Page","content":{"application/json":{"schema":{"type":"object","required":["data","next_cursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Event"}},"next_cursor":{"type":["string","null"],"description":"Pass as `cursor` (lists) or `after` (events) for the next page."}}}}}},"403":{"$ref":"#/components/responses/Error"}}}},"/v1/catalog/items":{"put":{"summary":"Upsert catalog items (≤1000 per request) the agent can quote","security":[{"apiKey":["catalog:write"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["items"],"properties":{"source":{"type":"string","default":"default","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$"},"items":{"type":"array","maxItems":1000,"items":{"$ref":"#/components/schemas/CatalogItem"}}}}}}},"responses":{"200":{"description":"Upserted"},"422":{"$ref":"#/components/responses/Error"}}}},"/v1/catalog/items/retire":{"post":{"summary":"After a full sync: stop offering items not re-sent since `before`","security":[{"apiKey":["catalog:write"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["before"],"properties":{"source":{"type":"string"},"before":{"type":"string","format":"date-time"}}}}}},"responses":{"200":{"description":"{retired}"}}}},"/v1/webhooks/leads/{sourceId}":{"post":{"summary":"Signed lead webhook (no API key; HMAC with the lead source's secret)","security":[{"webhookSignature":[]}],"parameters":[{"name":"sourceId","in":"path","required":true,"schema":{"type":"string"}},{"name":"X-Sado-Timestamp","in":"header","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeadInput"}}}},"responses":{"201":{"description":"Created"},"401":{"$ref":"#/components/responses/Error"}}}},"/v1/webhooks/amocrm/{integrationId}/{token}":{"post":{"summary":"amoCRM new-lead / stage-change webhook (Sado registers it in the account; the URL is also shown once when connecting)","security":[],"parameters":[{"name":"integrationId","in":"path","required":true,"schema":{"type":"string"}},{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object"}}}},"responses":{"200":{"description":"Accepted"},"401":{"$ref":"#/components/responses/Error"}}}},"/v1/webhooks/bitrix24/{integrationId}/{token}":{"post":{"summary":"Bitrix24 outgoing webhook (ONCRMLEADADD/UPDATE, ONCRMDEALADD/UPDATE) or an automation-rule webhook with ?deal_id= / ?lead_id= (URL shown in the portal)","security":[],"parameters":[{"name":"integrationId","in":"path","required":true,"schema":{"type":"string"}},{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object"}}}},"responses":{"200":{"description":"Accepted"},"401":{"$ref":"#/components/responses/Error"}}}},"/v1/intake/{token}":{"post":{"summary":"No-code lead URL (Tilda, website forms, Make/Zapier, Google Forms script). Any field names: the phone, name and comment are recognised (uz/ru/en). Consent is the one declared for this URL. Tilda's `test=test` check answers ok.","security":[],"parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","pattern":"^lu_"}}],"requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object"}},"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"description":"Duplicate of an earlier post, or the test check"},"201":{"description":"{ok, status: created, lead_id, queued}"},"401":{"$ref":"#/components/responses/Error"},"422":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}}}},"/v1/calendar/{token}":{"get":{"summary":"Private iCal feed of the company's bookings (subscribe in Google/Apple Calendar). Token in the URL; revoke it in the portal.","security":[],"parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","pattern":"^cal_"}}],"responses":{"200":{"description":"text/calendar"},"404":{"$ref":"#/components/responses/Error"}}}},"/v1/recordings/{callId}/{exp}/{sig}":{"get":{"summary":"Signed, expiring call recording link (put into CRMs by Sado; plays without a login). Range requests supported.","security":[],"parameters":[{"name":"callId","in":"path","required":true,"schema":{"type":"string"}},{"name":"exp","in":"path","required":true,"schema":{"type":"integer"}},{"name":"sig","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"audio/mp4"},"206":{"description":"Partial audio"},"404":{"$ref":"#/components/responses/Error"},"410":{"$ref":"#/components/responses/Error"}}}}},"webhooks":{"event":{"post":{"summary":"Outgoing webhook (portal → Integrations → Add webhook). Headers: X-Sado-Event, X-Sado-Event-Id, X-Sado-Timestamp, X-Sado-Signature.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Event"}}}},"responses":{"2XX":{"description":"Received; anything else is retried with backoff (8 attempts)."}}}}}}