{"openapi":"3.1.0","info":{"title":"Tele CRM Public API","version":"2026-09-12","description":"Read access to leads, inquiries, follow-ups and the change feed, for an external app (n8n, a partner tool, a warehouse). Authenticate with an API key minted in Settings → Integrations → API keys.\n\n**Paging** is keyset, never offset: loop until `nextCursor` is null. It always becomes null.\n\n**Retry policy:** 429, 410 (after resetting your cursor) and 5xx are retryable. Every other 4xx is permanent — retrying a `SCOPE_DENIED` forever only burns your own rate limit.\n\n**Codes are per-organization rows**, not enums. Resolve stage, disposition, campaign, source and course codes from `GET /public/reference` before branching on them."},"servers":[{"url":"/api/v1","description":"Mounted under the global API prefix."}],"security":[{"ApiKeyAuth":[]}],"tags":[{"name":"identity","description":"What this key is."},{"name":"leads","description":"People."},{"name":"inquiries","description":"A person’s interest in one campaign."},{"name":"follow-ups","description":"Scheduled callbacks."},{"name":"events","description":"The append-only change feed."},{"name":"reference","description":"Per-organization codes."}],"paths":{"/public/whoami":{"get":{"tags":["identity"],"operationId":"whoami","summary":"What this key is, what it may do, and what it has spent today","description":"Requires a valid key but no particular scope. Carries no lead data and costs no rows — make it the first node of every workflow so a bad key fails loudly and early.","responses":{"200":{"description":"The key’s identity and today’s usage.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Whoami"}}}},"400":{"description":"`VALIDATION_FAILED` — a query parameter did not parse. Permanent; do not retry.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`MISSING_API_KEY`, `INVALID_API_KEY`, `KEY_EXPIRED` or `KEY_REVOKED`. Permanent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`SCOPE_DENIED` (body carries `requiredScope`), `ORG_SUSPENDED` or `ORG_ARCHIVED`. Permanent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`, `ROW_RATE_EXCEEDED` or `ROW_BUDGET_EXCEEDED`. Retry after `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Retryable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/public/leads":{"get":{"tags":["leads"],"operationId":"listLeads","summary":"List leads","description":"Scope: `LEADS_READ`. Ordered by id ascending.","parameters":[{"name":"updatedSince","in":"query","required":false,"description":"Incremental sync: only rows changed at or after this instant.","schema":{"type":"string","format":"date-time"}},{"name":"createdSince","in":"query","required":false,"description":"Only rows created at or after this instant.","schema":{"type":"string","format":"date-time"}},{"name":"primaryStatus","in":"query","required":false,"description":"The lead badge, which mirrors the person’s NEWEST inquiry.","schema":{"type":"string","enum":["NEW","IN_PROGRESS","CONVERTED","LOST","DORMANT"]}},{"name":"campaignId","in":"query","required":false,"description":"Leads holding at least one inquiry in this campaign.","schema":{"type":"string"}},{"name":"assignedToUserId","in":"query","required":false,"description":"A user id from GET /public/reference, or the literal `unassigned`.","schema":{"type":"string"}},{"name":"phone","in":"query","required":false,"description":"Digit-tail match on phone and alternatePhone, so \"+91 98765 43210\" and \"9876543210\" find the same person.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Rows per page.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"cursor","in":"query","required":false,"description":"Opaque keyset cursor. Pass back the previous response’s `nextCursor`; stop when it is null.","schema":{"type":"string"}}],"responses":{"200":{"description":"A page of leads.","content":{"application/json":{"schema":{"type":"object","required":["items","nextCursor","hasMore"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Lead"}},"nextCursor":{"type":["string","null"],"description":"Pass as `cursor` for the next page. `null` is the terminal condition."},"hasMore":{"type":"boolean"}}}}}},"400":{"description":"`VALIDATION_FAILED` — a query parameter did not parse. Permanent; do not retry.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`MISSING_API_KEY`, `INVALID_API_KEY`, `KEY_EXPIRED` or `KEY_REVOKED`. Permanent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`SCOPE_DENIED` (body carries `requiredScope`), `ORG_SUSPENDED` or `ORG_ARCHIVED`. Permanent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`, `ROW_RATE_EXCEEDED` or `ROW_BUDGET_EXCEEDED`. Retry after `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Retryable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/public/leads/{id}":{"get":{"tags":["leads"],"operationId":"getLead","summary":"One lead with all of its inquiries","description":"Scope: `LEADS_READ`. Costs 1 row.","parameters":[{"name":"id","in":"path","required":true,"description":"Lead id.","schema":{"type":"string"}}],"responses":{"200":{"description":"The lead, newest inquiry first.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Lead"},{"type":"object","properties":{"inquiries":{"type":"array","items":{"$ref":"#/components/schemas/Inquiry"}}}}]}}}},"400":{"description":"`VALIDATION_FAILED` — a query parameter did not parse. Permanent; do not retry.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`MISSING_API_KEY`, `INVALID_API_KEY`, `KEY_EXPIRED` or `KEY_REVOKED`. Permanent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`SCOPE_DENIED` (body carries `requiredScope`), `ORG_SUSPENDED` or `ORG_ARCHIVED`. Permanent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`NOT_FOUND`. A record belonging to another organization answers 404, never 403.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`, `ROW_RATE_EXCEEDED` or `ROW_BUDGET_EXCEEDED`. Retry after `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Retryable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/public/inquiries":{"get":{"tags":["inquiries"],"operationId":"listInquiries","summary":"List inquiries","description":"Scope: `INQUIRIES_READ`. This is where segmentation lives — `lifecycle` is resolved from the stage’s closes/converts flags, so it keeps working when an organization renames its stages.","parameters":[{"name":"updatedSince","in":"query","required":false,"description":"Incremental sync: only rows changed at or after this instant.","schema":{"type":"string","format":"date-time"}},{"name":"createdSince","in":"query","required":false,"description":"Only rows created at or after this instant.","schema":{"type":"string","format":"date-time"}},{"name":"campaignId","in":"query","required":false,"description":"Campaign id.","schema":{"type":"string"}},{"name":"campaignCode","in":"query","required":false,"description":"Campaign code from GET /public/reference.","schema":{"type":"string"}},{"name":"stageCode","in":"query","required":false,"description":"Stage code from GET /public/reference.","schema":{"type":"string"}},{"name":"lifecycle","in":"query","required":false,"description":"Derived from the stage flags, never from a hardcoded code.","schema":{"type":"string","enum":["open","closed","converted","lost"]}},{"name":"dispositionCode","in":"query","required":false,"description":"The last call outcome applied.","schema":{"type":"string"}},{"name":"assignedToUserId","in":"query","required":false,"description":"A user id from GET /public/reference, or the literal `unassigned`.","schema":{"type":"string"}},{"name":"hasBalance","in":"query","required":false,"description":"true = a fee is set and not fully paid.","schema":{"type":"boolean"}},{"name":"includeNurture","in":"query","required":false,"description":"Include the long-term Nurture pool. Off by default — a consumer asking for open work almost never means the parked rows.","schema":{"type":"boolean","default":false}},{"name":"limit","in":"query","required":false,"description":"Rows per page.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"cursor","in":"query","required":false,"description":"Opaque keyset cursor. Pass back the previous response’s `nextCursor`; stop when it is null.","schema":{"type":"string"}}],"responses":{"200":{"description":"A page of inquiries.","content":{"application/json":{"schema":{"type":"object","required":["items","nextCursor","hasMore"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Inquiry"}},"nextCursor":{"type":["string","null"],"description":"Pass as `cursor` for the next page. `null` is the terminal condition."},"hasMore":{"type":"boolean"}}}}}},"400":{"description":"`VALIDATION_FAILED` — a query parameter did not parse. Permanent; do not retry.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`MISSING_API_KEY`, `INVALID_API_KEY`, `KEY_EXPIRED` or `KEY_REVOKED`. Permanent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`SCOPE_DENIED` (body carries `requiredScope`), `ORG_SUSPENDED` or `ORG_ARCHIVED`. Permanent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`, `ROW_RATE_EXCEEDED` or `ROW_BUDGET_EXCEEDED`. Retry after `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Retryable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/public/inquiries/{id}":{"get":{"tags":["inquiries"],"operationId":"getInquiry","summary":"One inquiry, with the lead and optional expansions","description":"Scope: `INQUIRIES_READ`. Costs 1 row.","parameters":[{"name":"id","in":"path","required":true,"description":"Inquiry id.","schema":{"type":"string"}},{"name":"include","in":"query","required":false,"description":"CSV of `tags`, `customFields`, `formAnswers`, `payments`. Unknown names are ignored.","schema":{"type":"string","example":"tags,payments"}}],"responses":{"200":{"description":"The inquiry.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InquiryDetail"}}}},"400":{"description":"`VALIDATION_FAILED` — a query parameter did not parse. Permanent; do not retry.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`MISSING_API_KEY`, `INVALID_API_KEY`, `KEY_EXPIRED` or `KEY_REVOKED`. Permanent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`SCOPE_DENIED` (body carries `requiredScope`), `ORG_SUSPENDED` or `ORG_ARCHIVED`. Permanent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`NOT_FOUND`. A record belonging to another organization answers 404, never 403.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`, `ROW_RATE_EXCEEDED` or `ROW_BUDGET_EXCEEDED`. Retry after `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Retryable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/public/follow-ups":{"get":{"tags":["follow-ups"],"operationId":"listFollowUps","summary":"Live scheduled callbacks","description":"Scope: `FOLLOWUPS_READ`. Only live rows (PENDING or OVERDUE) on inquiries that are still open, so a closed lead’s stray callback never reaches an automation.","parameters":[{"name":"window","in":"query","required":false,"description":"`due` = scheduled at or before now. `overdue` = the sweep has already flagged it (a subset of due). `today` = the IST calendar day. `upcoming` = still in the future.","schema":{"type":"string","enum":["due","overdue","today","upcoming"],"default":"due"}},{"name":"assignedToUserId","in":"query","required":false,"description":"A user id from GET /public/reference, or the literal `unassigned`.","schema":{"type":"string"}},{"name":"includeNurture","in":"query","required":false,"description":"Include the long-term Nurture pool. Off by default — a consumer asking for open work almost never means the parked rows.","schema":{"type":"boolean","default":false}},{"name":"limit","in":"query","required":false,"description":"Rows per page.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"cursor","in":"query","required":false,"description":"Opaque keyset cursor. Pass back the previous response’s `nextCursor`; stop when it is null.","schema":{"type":"string"}}],"responses":{"200":{"description":"A page of follow-ups.","content":{"application/json":{"schema":{"type":"object","required":["items","nextCursor","hasMore"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/FollowUp"}},"nextCursor":{"type":["string","null"],"description":"Pass as `cursor` for the next page. `null` is the terminal condition."},"hasMore":{"type":"boolean"}}}}}},"400":{"description":"`VALIDATION_FAILED` — a query parameter did not parse. Permanent; do not retry.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`MISSING_API_KEY`, `INVALID_API_KEY`, `KEY_EXPIRED` or `KEY_REVOKED`. Permanent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`SCOPE_DENIED` (body carries `requiredScope`), `ORG_SUSPENDED` or `ORG_ARCHIVED`. Permanent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`, `ROW_RATE_EXCEEDED` or `ROW_BUDGET_EXCEEDED`. Retry after `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Retryable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/public/events":{"get":{"tags":["events"],"operationId":"listEvents","summary":"The change feed","description":"Scope: `EVENTS_READ`. Store `nextCursor` and send it back as `since` on the next poll.\n\nThe feed holds events back by a couple of seconds: a sequence number is allocated when a transaction inserts, not when it commits, so reading right up to \"now\" would let a poller step over an event that had not become visible yet — permanently.\n\nAn unrecognised `eventType` is not an error; it simply matches nothing.","parameters":[{"name":"since","in":"query","required":false,"description":"Exclusive: the last `seq` you processed.","schema":{"type":"integer","minimum":0}},{"name":"sinceMinutes","in":"query","required":false,"description":"Used ONLY when `since` is absent, so a first manual run needs no stored cursor.","schema":{"type":"integer","minimum":1,"maximum":1440,"default":60}},{"name":"eventType","in":"query","required":false,"description":"CSV, at most 20 entries. Published vocabulary: lead.created, lead.updated, lead.merged, lead.deleted, inquiry.created, inquiry.deleted, inquiry.stage_changed, inquiry.won, inquiry.lost, payment.recorded, followup.due, import.completed, leads.bulk_updated, org.leads_wiped.","schema":{"type":"string","example":"lead.created,inquiry.won"}},{"name":"includeSuppressed","in":"query","required":false,"description":"Include bulk/machine rows (a CSV import, the dormancy sweep) so a mirror stays correct.","schema":{"type":"boolean","default":false}},{"name":"limit","in":"query","required":false,"description":"Rows per page.","schema":{"type":"integer","minimum":1,"maximum":500,"default":100}}],"responses":{"200":{"description":"A page of events, oldest first.","content":{"application/json":{"schema":{"type":"object","required":["items","nextCursor","hasMore"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Event"}},"nextCursor":{"type":["integer","null"]},"hasMore":{"type":"boolean"}}}}}},"400":{"description":"`VALIDATION_FAILED` — a query parameter did not parse. Permanent; do not retry.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`MISSING_API_KEY`, `INVALID_API_KEY`, `KEY_EXPIRED` or `KEY_REVOKED`. Permanent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`SCOPE_DENIED` (body carries `requiredScope`), `ORG_SUSPENDED` or `ORG_ARCHIVED`. Permanent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"410":{"description":"`CURSOR_TOO_OLD` — `since` predates the retention window, so events have been lost. The body carries `oldestSeq` and `retentionDays`. Restart from `oldestSeq - 1` and reconcile with GET /public/leads?updatedSince=. This is an operator event, not a thing to swallow.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`, `ROW_RATE_EXCEEDED` or `ROW_BUDGET_EXCEEDED`. Retry after `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Retryable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/public/reference":{"get":{"tags":["reference"],"operationId":"reference","summary":"Every per-organization code an automation must resolve","description":"Scope: `REFERENCE_READ`. Stages, dispositions, sources and courses are editable rows with free-form codes — not enums — so branching on `admitted` without reading this first is how a workflow silently stops matching. Costs no rows.","responses":{"200":{"description":"The organization’s vocabulary.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Reference"}}}},"400":{"description":"`VALIDATION_FAILED` — a query parameter did not parse. Permanent; do not retry.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`MISSING_API_KEY`, `INVALID_API_KEY`, `KEY_EXPIRED` or `KEY_REVOKED`. Permanent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`SCOPE_DENIED` (body carries `requiredScope`), `ORG_SUSPENDED` or `ORG_ARCHIVED`. Permanent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`, `ROW_RATE_EXCEEDED` or `ROW_BUDGET_EXCEEDED`. Retry after `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Retryable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/public/openapi.json":{"get":{"tags":["reference"],"operationId":"openapi","summary":"This document","description":"No API key required.","security":[],"responses":{"200":{"description":"This OpenAPI document.","content":{"application/json":{"schema":{"type":"object"}}}}}}}},"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"The key minted in Settings → Integrations. `Authorization: ApiKey <key>` is accepted as an alternative for clients that cannot set a custom header."}},"schemas":{"Error":{"type":"object","required":["statusCode","error","message","requestId","path","timestamp"],"properties":{"statusCode":{"type":"integer"},"error":{"type":"string","enum":["MISSING_API_KEY","INVALID_API_KEY","KEY_EXPIRED","KEY_REVOKED","VALIDATION_FAILED","SCOPE_DENIED","ORG_SUSPENDED","ORG_ARCHIVED","NOT_FOUND","CURSOR_TOO_OLD","RATE_LIMITED","ROW_RATE_EXCEEDED","ROW_BUDGET_EXCEEDED","INTERNAL_ERROR"],"description":"A stable machine code. Branch on this, never on `message`."},"message":{"type":"string"},"requestId":{"type":"string","description":"Quote this in a support ticket."},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"},"requiredScope":{"type":"string","description":"SCOPE_DENIED only."},"issues":{"type":"array","items":{"type":"string"},"description":"VALIDATION_FAILED only — every field-level problem, not just the first."},"oldestSeq":{"type":"integer","description":"CURSOR_TOO_OLD only."},"retentionDays":{"type":"integer","description":"CURSOR_TOO_OLD only."}}},"Lead":{"type":"object","required":["id","phone","primaryStatus","tags","createdAt","updatedAt"],"properties":{"id":{"type":"string"},"name":{"type":["string","null"]},"phone":{"type":"string","description":"As stored, which is NOT guaranteed dialable — imports and ad forms wrote raw strings. Send to `phoneE164` and skip the row when it is null."},"phoneE164":{"type":["string","null"]},"alternatePhone":{"type":["string","null"]},"email":{"type":["string","null"]},"primaryStatus":{"type":"string","enum":["NEW","IN_PROGRESS","CONVERTED","LOST","DORMANT"],"description":"Mirrors the person’s NEWEST inquiry, so a lead can read NEW while holding a CONVERTED inquiry. Message the wrong one and you contact an admitted student."},"assignedTo":{"type":["object","null"],"properties":{"id":{"type":"string"},"fullName":{"type":"string"}}},"tags":{"type":"array","items":{"type":"string"}},"firstInquiryAt":{"type":["string","null"],"format":"date-time"},"lastInquiryAt":{"type":["string","null"],"format":"date-time"},"lastCalledAt":{"type":["string","null"],"format":"date-time"},"nextFollowupAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"Inquiry":{"type":"object","required":["id","leadId","campaignId","lifecycle","attemptCount","paidAmount","createdAt","updatedAt"],"properties":{"id":{"type":"string"},"leadId":{"type":"string"},"campaignId":{"type":"string"},"campaignCode":{"type":["string","null"]},"campaignName":{"type":["string","null"]},"stageCode":{"type":["string","null"]},"stageName":{"type":["string","null"]},"lifecycle":{"type":"string","enum":["open","converted","lost"],"description":"Derived from the stage’s closes/converts flags."},"dispositionCode":{"type":["string","null"]},"sourceCode":{"type":["string","null"]},"courseCode":{"type":["string","null"]},"assignedTo":{"type":["object","null"],"properties":{"id":{"type":"string"},"fullName":{"type":"string"}}},"attemptCount":{"type":"integer"},"totalFee":{"type":["integer","null"],"description":"Whole rupees. null = not captured yet."},"paidAmount":{"type":"integer"},"balance":{"type":["integer","null"],"description":"null when no fee is set — which is not the same as nothing owed."},"nextFollowupAt":{"type":["string","null"],"format":"date-time"},"closedAt":{"type":["string","null"],"format":"date-time"},"closedReason":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"InquiryDetail":{"allOf":[{"$ref":"#/components/schemas/Inquiry"},{"type":"object","required":["lead"],"properties":{"lead":{"$ref":"#/components/schemas/Lead"},"tags":{"type":"array","items":{"type":"string"}},"campaignCustomFields":{"type":"object","additionalProperties":true},"formAnswers":{"type":"array","description":"Facebook Lead Ads question/answer pairs, in display order.","items":{"type":"object","properties":{"label":{"type":"string"},"value":{"type":"string"}}}},"payments":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"amount":{"type":"integer"},"paidAt":{"type":"string","format":"date-time"},"method":{"type":["string","null"]}}}}}}]},"FollowUp":{"type":"object","required":["id","inquiryId","leadId","lead","scheduledAt","status","kind","createdAt"],"properties":{"id":{"type":"string"},"inquiryId":{"type":"string"},"leadId":{"type":"string"},"lead":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":["string","null"]},"phone":{"type":"string"},"phoneE164":{"type":["string","null"]}}},"assignedTo":{"type":["object","null"],"properties":{"id":{"type":"string"},"fullName":{"type":"string"}}},"scheduledAt":{"type":"string","format":"date-time"},"status":{"type":"string","enum":["PENDING","OVERDUE"]},"kind":{"type":"string"},"createdAt":{"type":"string","format":"date-time"}}},"Event":{"type":"object","required":["seq","apiVersion","eventType","data","suppressed","occurredAt"],"properties":{"seq":{"type":"integer","description":"Your cursor. Strictly increasing."},"apiVersion":{"type":"string"},"eventType":{"type":"string","examples":["lead.created","lead.updated","lead.merged","lead.deleted","inquiry.created","inquiry.deleted","inquiry.stage_changed","inquiry.won","inquiry.lost","payment.recorded","followup.due","import.completed","leads.bulk_updated","org.leads_wiped"]},"leadId":{"type":["string","null"],"description":"A plain id with no foreign key — a delete event outlives its row."},"inquiryId":{"type":["string","null"]},"data":{"type":"object","additionalProperties":true,"description":"Ids, codes and before/after values only — never a snapshot of the person. Hydrate the person from GET /public/leads/{id}, which is always current."},"suppressed":{"type":"boolean"},"occurredAt":{"type":"string","format":"date-time"}}},"Reference":{"type":"object","properties":{"organization":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"timezone":{"type":"string"}}},"stages":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"code":{"type":"string"},"order":{"type":"integer"},"closesInquiry":{"type":"boolean"},"convertsInquiry":{"type":"boolean"}}}},"dispositions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"code":{"type":"string"},"campaignId":{"type":["string","null"],"description":"null = available to every campaign."},"closesInquiry":{"type":"boolean"},"convertsInquiry":{"type":"boolean"}}}},"campaigns":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"code":{"type":"string"},"status":{"type":"string"}}}},"sources":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"code":{"type":"string"}}}},"courses":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"code":{"type":"string"}}}},"tags":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"scope":{"type":"string","enum":["LEAD","INQUIRY"]}}}},"users":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"fullName":{"type":"string"},"role":{"type":"string"},"active":{"type":"boolean"}}}}}},"Whoami":{"type":"object","properties":{"keyId":{"type":"string"},"keyPrefix":{"type":"string"},"name":{"type":"string"},"apiVersion":{"type":"string"},"organization":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"timezone":{"type":"string"}}},"scopes":{"type":"array","items":{"type":"string","enum":["LEADS_READ","INQUIRIES_READ","FOLLOWUPS_READ","REFERENCE_READ","EVENTS_READ"]}},"expiresAt":{"type":["string","null"],"format":"date-time"},"limits":{"type":"object","properties":{"rateLimitPerMinute":{"type":"integer"},"rowRateLimitPerMinute":{"type":"integer"},"dailyRowBudget":{"type":"integer"}}},"today":{"type":"object","description":"IST day. Excludes the whoami call you are making.","properties":{"day":{"type":"string","examples":["2026-09-12"]},"requests":{"type":"integer"},"rowsReturned":{"type":"integer"},"errors":{"type":"integer"},"rateLimited":{"type":"integer"}}}}}}}}