Agrofin

Appeals

On this page 9

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:

json
{
  "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.

json
{
  "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

json
{ "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

code
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