agentlyleads docs
Cases API

The Case object

A case is an issue somebody is working, with a severity and a resolution clock. It exists alongside the shared inbox rather than replacing it: the inbox already handles assignment, open and closed, and internal comments for email. A case adds the three things it does not have: a severity, a resolution target distinct from first response, and a way to record an issue that never had an email behind it.

The Cases API is read-only. Cases are opened and moved in the app or through the AI connector.

FieldTypeNotes
idstringCUID (read-only)
numberintegerPer-workspace case number, shown to people as #31
subjectstringWhat the issue is
descriptionstring | nullThe detail: what happened, what was tried
statusstringOne of NEW, OPEN, PENDING, RESOLVED, CLOSED
severitystringOne of LOW, MEDIUM, HIGH, URGENT
sourcestringMANUAL, EMAIL or LEAD. Which intake door it came through
openbooleanComputed. True while the status is NEW, OPEN or PENDING
ageMinsintegerComputed. Whole minutes the case has been open, frozen at resolvedAt once resolved
overduebooleanComputed. True when a HIGH or URGENT case has been open longer than three days
companyobject | nullThe account it is on: { id, name }
contactobject | nullThe person who raised it: { id, name }
assigneeobject | nullThe rep who has it: { id, name }, or null when it is still in the queue
threadKeystring | nullThe email conversation it came from, when it came from one
firstResponseAtstring (ISO 8601) | nullStamped once, when the case first leaves NEW
resolvedAtstring (ISO 8601) | nullWhen it was resolved
createdAtstring (ISO 8601)Assigned on create (read-only)
updatedAtstring (ISO 8601)Updated on every write (read-only)

Example object

{
  "id": "cmq2case0alk34567890",
  "number": 31,
  "subject": "Pallet arrived damaged",
  "description": "Two of six bags split in transit. Photos sent.",
  "status": "OPEN",
  "severity": "HIGH",
  "source": "EMAIL",
  "open": true,
  "ageMins": 4320,
  "overdue": true,
  "company": { "id": "cmq2comp0alk34567890", "name": "Acme Corp" },
  "contact": { "id": "cmq2cont0alk34567891", "name": "Jane Buyer" },
  "assignee": { "id": "cmq9user0alk34567890", "name": "Kevin Rep" },
  "threadKey": "AAMkAGI2...",
  "firstResponseAt": "2026-09-04T08:12:00.000Z",
  "resolvedAt": null,
  "createdAt": "2026-09-04T07:55:00.000Z",
  "updatedAt": "2026-09-06T14:02:00.000Z"
}

Notes on specific fields

open, ageMins and overdue are computed on read, never stored. A stored age is wrong the moment nobody writes to the row, and everything needed to work it out is already on the record. That means a queue you poll every minute always reports the current age, with no job keeping a column in step with the clock.

The clock stops at resolvedAt. A case resolved in an hour still reads as an hour a month later, which is what makes ageMins usable as a resolution-time measure and not just "how long ago was this".

overdue only applies to HIGH and URGENT. The target is three days. LOW and MEDIUM cases carry no target and are never flagged, because a target nobody believes is worse than no target.

threadKey is the inbox's own conversation key. A case opened from an email keeps that key and links back to the conversation rather than copying it: the thread stays where it lives, and the case is the thing carrying a severity. Opening a case twice on the same conversation returns the same case, so history is never split in half.

firstResponseAt is stamped once, when somebody picks the case up and moves it out of NEW. Later status changes do not touch it. Moving a case from RESOLVED to CLOSED likewise keeps the original resolvedAt, because filing is not a second resolution; reopening a case clears it, because it genuinely is not resolved any more.

On this page