The electronic reception: a citizen or an organisation submits an appeal on the public site, gets a tracking number plus an access code, and follows the review on the status page. Staff process appeals in the admin panel; every status change is logged and is visible to the applicant.
Anonymous submissions are accepted only for the anti-corruption channel
(corruption, conflict_of_interest) — the law does not consider other
anonymous appeals, so the API rejects is_anonymous for the remaining types.
Types and statuses
type |
Meaning |
|---|---|
application |
Ariza — a request for information or a service |
complaint |
Shikoyat |
proposal |
Taklif |
corruption |
Report of corruption (may be anonymous) |
conflict_of_interest |
Conflict of interest (may be anonymous) |
status |
Step on the status page |
|---|---|
received |
Qabul qilindi — set automatically on submission |
in_review |
Ko'rib chiqilmoqda |
info_requested |
Qo'shimcha ma'lumot talab qilindi — the note says what is missing |
answered |
Javob berildi — final; response holds the answer |
rejected |
Rad etildi — final |
deadline_at is created_at + APPEALS_REVIEW_DAYS (15 by default) and
is_overdue is true while an open appeal is past it.
Public
POST /api/v1/appeals
Throttled per IP (APPEALS_SUBMIT_PER_MINUTE, 5/min). Body:
| Field | Rules |
|---|---|
type |
required, one of the types above |
is_anonymous |
boolean; prohibited unless the type allows anonymity |
applicant_name |
required unless anonymous, ≤150 |
applicant_phone |
required unless anonymous, digits/spaces/dashes, stored normalised as +998… |
applicant_email |
optional email |
applicant_region |
optional, ≤100 |
subject |
required, 5–200 |
body |
required, 20–5000 |
website |
honeypot — must be absent |
The locale comes from Accept-Language; IP and user agent are recorded.
Response 201:
{
"status": "ok",
"message": "Murojaatingiz qabul qilindi.",
"data": {
"tracking_number": "2026/09-0001",
"access_code": "K7M4QP",
"status": "received",
"deadline_at": "2026-09-27T10:00:00+00:00",
"created_at": "2026-09-12T10:00:00+00:00"
}
}
Show both values to the applicant — the access code is not returned again.
Staff can read it from the admin detail if the applicant loses it. When
TELEGRAM_BOT_TOKEN/TELEGRAM_CHAT_ID are set, the staff group gets a
"new appeal" message after the response is sent.
POST /api/v1/appeals/status
Body { "tracking_number": "2026/09-0001", "access_code": "K7M4QP" }
(spaces and case are tolerated). POST rather than GET so the code never
lands in URLs or server logs. Throttled 20/min.
{
"data": {
"tracking_number": "2026/09-0001",
"type": "application",
"subject": "Bug'doy krediti bo'yicha savol",
"body": "…",
"status": "in_review",
"is_final": false,
"response": null,
"responded_at": null,
"deadline_at": "2026-09-27T10:00:00+00:00",
"is_overdue": false,
"created_at": "2026-09-12T10:00:00+00:00",
"history": [
{ "status": "received", "note": null, "created_at": "2026-09-12T10:00:00+00:00" },
{ "status": "in_review", "note": "Mas'ul bo'limga yuborildi", "created_at": "2026-09-13T08:00:00+00:00" }
]
}
}
A wrong pair answers 404 with api.appeal_not_found; the response does not
say which half was wrong.
Admin
Session cookie or Bearer token. Permissions: view appeals (viewer, admin,
super_admin) and update appeals (admin, super_admin). Editors have neither.
GET /api/v1/admin/appeals
Standard list contract. Filters: filter[search] (number,
subject, applicant name/email, phone digits), filter[status],
filter[type], filter[is_anonymous], filter[open]=1 (not answered or
rejected). Sorts: created_at (default -created_at), status_changed_at,
status, type, id. Each row carries the applicant fields, deadline_at,
is_overdue and handler (the user who last changed the status).
GET /api/v1/admin/appeals/{id}
The row plus body, response, access_code, ip, user_agent and
history (each entry with the acting user).
PATCH /api/v1/admin/appeals/{id}/status
{ "status": "info_requested", "note": "Ijara shartnomasi nusxasini yuboring." }
note is shown to the applicant on the status page. response is required
when status is answered and sets responded_at. Any transition is
allowed (including reopening a final appeal) — the log keeps the trail.
Returns the detail resource.
Configuration
APPEALS_REVIEW_DAYS=15
APPEALS_SUBMIT_PER_MINUTE=5
TELEGRAM_BOT_TOKEN= # shared with the assistant
TELEGRAM_CHAT_ID=
ADMIN_URL= # "open in panel" link in the notification