Cases
The case-management view of your conversations — the same rows, filters and ordering the Cases page in your workspace shows.
| Operation | Endpoint | Scope |
|---|---|---|
| List cases | GET /api/v1/cases | conversations:read |
A case is a conversation
Every row carries the conversation id, so you can list cases here and pull the full transcript from GET /api/v1/conversations/{id}/events without holding two identifiers for one thing.
Where GET /api/v1/conversations returns the plain record, this returns the case-management row: who the customer is (customer_id, customer_name, customer_email, customer_phone), which agent took it, how it turned out, the last_message, the message_count, and created_at / updated_at.
bash
curl "https://<your-workspace>-api.perfox.ai/api/v1/cases?status=active&page=1&page_size=20" \
-H "Authorization: Bearer sk_…"Two kinds of status, and they are not the same thing
| Field | Meaning |
|---|---|
status | The ticket lifecycle — active, ended, resolved, escalated, abandoned |
sentiment_label | The AI verdict on how the customer felt |
resolved / resolution_reason | Whether the AI considers the issue settled, and why |
needs_followup | Flagged for a human to pick up |
qa_scores | Scorecard: resolution, compliance, sentiment, response, tools, overall |
A conversation can be ended without being resolved. The lifecycle is what happened to the ticket; the rest is the AI's judgement about it.
Who handled it
workflow_id and workflow_name identify the agent. Two absences mean different things:
- no
workflow_idat all — no agent matched the inbound workflow_name: null— an agent did handle it, and was deleted afterwards
Search, filters, date range
| Parameter | Values |
|---|---|
q | Free text over the customer's name, email and phone, the details your site passed about the visitor, and the case id |
status | active, ended, resolved, escalated, abandoned |
channel | The channel the case started on, e.g. web, whatsapp, phone |
originator | Who spoke first: customer, agent (a greeting or a dial-out) or system |
needs_followup | true to list only cases flagged for a human |
customer_id | Only this customer's cases |
created_after / created_before | ISO dates bounding when the case was opened |
sort_by | updated_at (default), created_at, status, channel_started |
sort_order | desc (default) or asc |
By default the most recently active case comes first, so an old case that just received a message is at the top — the same order as the Cases page.
All filters are applied before paging, so totals and page counts stay correct as you scroll.
Paging reports a total
json
{
"data": [ … ],
"pagination": { "page": 1, "page_size": 20, "total": 347, "total_pages": 18 }
}page (default 1) and page_size (default 20, up to 100). This differs from the call log, which pages by cursor — a cursor suits an endless scroll but cannot tell you how many rows matched, and a table needs to say "20 of 347".