{
    "variable": [
        {
            "id": "baseUrl",
            "key": "baseUrl",
            "type": "string",
            "name": "string",
            "value": "https:\/\/www.dots.mhn.services"
        }
    ],
    "info": {
        "name": "Dots App API Documentation",
        "_postman_id": "b56f17c8-a157-420b-ad1c-ccfc69a831da",
        "description": "JSON API backend for the Dots App Flutter client: authentication (email OTP registration, login, forgot\/reset password), account management, and clinical case submission.",
        "schema": "https:\/\/schema.getpostman.com\/json\/collection\/v2.1.0\/collection.json"
    },
    "item": [
        {
            "name": "Authentication",
            "description": "\nRegistration, login, and password recovery. None of these endpoints require an\nAuthorization header except Logout.",
            "item": [
                {
                    "name": "Register",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/auth\/register",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/auth\/register"
                        },
                        "method": "POST",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "multipart\/form-data"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": {
                            "mode": "formdata",
                            "formdata": [
                                {
                                    "key": "full_name",
                                    "value": "Dr Jane Doe",
                                    "type": "text",
                                    "description": "The user's full name."
                                },
                                {
                                    "key": "email",
                                    "value": "jane@example.com",
                                    "type": "text",
                                    "description": "The user's email address. An OTP is sent here to verify. Must be a valid email address."
                                },
                                {
                                    "key": "password",
                                    "value": "password123",
                                    "type": "text",
                                    "description": "At least 8 characters. Must be at least 8 characters."
                                },
                                {
                                    "key": "phone_number",
                                    "value": "03001234567",
                                    "type": "text",
                                    "description": "The user's phone number."
                                },
                                {
                                    "key": "designation",
                                    "value": "Consultant Dermatologist",
                                    "type": "text",
                                    "description": "Professional designation\/title. Free text, no fixed list."
                                },
                                {
                                    "key": "bio",
                                    "value": "Dermatologist with a special interest in dermoscopy and skin cancer screening.",
                                    "type": "text",
                                    "description": "Optional short free-text bio shown on the profile."
                                },
                                {
                                    "key": "province",
                                    "value": "Punjab",
                                    "type": "text",
                                    "description": "Free text, no fixed list."
                                },
                                {
                                    "key": "city",
                                    "value": "Lahore",
                                    "type": "text",
                                    "description": "Free text, no fixed list."
                                },
                                {
                                    "key": "pmdc_number",
                                    "value": "PMDC-12345",
                                    "type": "text",
                                    "description": "PMDC registration number, if any."
                                },
                                {
                                    "key": "fellowship_number",
                                    "value": "FCPS-6789",
                                    "type": "text",
                                    "description": "Fellowship number, if any."
                                },
                                {
                                    "key": "institutional_number",
                                    "value": "INST-001",
                                    "type": "text",
                                    "description": "Institutional\/employee number, if any."
                                },
                                {
                                    "key": "fcm_token",
                                    "value": "dGhpcyBpcyBhIHNhbXBsZSB0b2tlbg==",
                                    "type": "text",
                                    "description": "The device's Firebase Cloud Messaging token, saved against the user for push notifications."
                                },
                                {
                                    "key": "avatar",
                                    "src": [],
                                    "type": "file"
                                }
                            ]
                        },
                        "description": "Creates a user (unverified) and emails a 6-digit OTP. `role` defaults to\n`student` server-side. All fields except full_name, email, password, and\nphone_number are optional. Registering again with an email that hasn't been\nverified yet updates that pending record and resends the OTP, rather than\nfailing.\n\nNothing else from this response is needed by the client - `verify-otp` and\n`resend-otp` both look the account up by email, and the full profile isn't\navailable until the account is actually verified.",
                        "auth": {
                            "type": "noauth"
                        }
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 201,
                            "body": "{\n  \"email\": \"jane@example.com\",\n  \"message\": \"Registered successfully. Please verify the OTP sent to your email.\"\n}",
                            "name": "Registered, pending verification"
                        },
                        {
                            "header": [],
                            "code": 422,
                            "body": "{\n  \"message\": \"The email has already been taken.\",\n  \"errors\": { \"email\": [\"The email has already been taken.\"] }\n}",
                            "name": "Email already registered and verified"
                        }
                    ]
                },
                {
                    "name": "Verify Registration OTP",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/auth\/register\/verify-otp",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/auth\/register\/verify-otp"
                        },
                        "method": "POST",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": {
                            "mode": "raw",
                            "raw": "{\"email\":\"jane@example.com\",\"otp\":\"482913\"}"
                        },
                        "description": "Verifies the OTP and marks the account verified. The client should send the\nuser to the login screen next - this does not issue a token or auto-login.",
                        "auth": {
                            "type": "noauth"
                        }
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"email\": \"jane@example.com\",\n  \"message\": \"Email verified successfully. Please log in.\"\n}",
                            "name": "Verified"
                        },
                        {
                            "header": [],
                            "code": 422,
                            "body": "{\n  \"message\": \"The provided OTP is invalid or has expired.\",\n  \"errors\": { \"otp\": [\"The provided OTP is invalid or has expired.\"] }\n}",
                            "name": "Invalid or expired OTP"
                        }
                    ]
                },
                {
                    "name": "Resend Registration OTP",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/auth\/register\/resend-otp",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/auth\/register\/resend-otp"
                        },
                        "method": "POST",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": {
                            "mode": "raw",
                            "raw": "{\"email\":\"jane@example.com\"}"
                        },
                        "description": "Invalidates the previous registration OTP and sends a new one. Only works\nfor accounts that have not yet been verified.",
                        "auth": {
                            "type": "noauth"
                        }
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"message\": \"A new OTP has been sent to your email.\"\n}",
                            "name": "OTP resent"
                        },
                        {
                            "header": [],
                            "code": 422,
                            "body": "{\n  \"message\": \"This email is already verified.\",\n  \"errors\": { \"email\": [\"This email is already verified.\"] }\n}",
                            "name": "Already verified"
                        }
                    ]
                },
                {
                    "name": "Login",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/auth\/login",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/auth\/login"
                        },
                        "method": "POST",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": {
                            "mode": "raw",
                            "raw": "{\"email\":\"jane@example.com\",\"password\":\"password123\",\"fcm_token\":\"dGhpcyBpcyBhIHNhbXBsZSB0b2tlbg==\"}"
                        },
                        "description": "Authenticates a verified user and issues a new Sanctum token. Fails if the\naccount has not completed OTP verification yet.",
                        "auth": {
                            "type": "noauth"
                        }
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"email\": \"jane@example.com\",\n  \"token\": \"6|To5i3l8yWR3Mb3voEv0mX0W4NK4iLQCxsmuARXfDbe3d9eab\"\n}",
                            "name": "Success"
                        },
                        {
                            "header": [],
                            "code": 422,
                            "body": "{\n  \"message\": \"These credentials do not match our records.\",\n  \"errors\": { \"email\": [\"These credentials do not match our records.\"] }\n}",
                            "name": "Wrong email or password"
                        },
                        {
                            "header": [],
                            "code": 422,
                            "body": "{\n  \"message\": \"Please verify your email before logging in.\",\n  \"errors\": { \"email\": [\"Please verify your email before logging in.\"] }\n}",
                            "name": "Account not yet verified"
                        }
                    ]
                },
                {
                    "name": "Forgot Password",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/auth\/forgot-password",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/auth\/forgot-password"
                        },
                        "method": "POST",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": {
                            "mode": "raw",
                            "raw": "{\"email\":\"jane@example.com\"}"
                        },
                        "description": "Emails a 6-digit OTP for an existing account. Fails with a validation\nerror if the email is not registered. This is step 1 of 3 in the reset\nflow: Forgot Password -> Verify Forgot Password OTP -> Reset Password.",
                        "auth": {
                            "type": "noauth"
                        }
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"message\": \"An OTP has been sent to your email.\"\n}",
                            "name": "OTP sent"
                        },
                        {
                            "header": [],
                            "code": 422,
                            "body": "{\n  \"message\": \"We can't find a user with that email address.\",\n  \"errors\": { \"email\": [\"We can't find a user with that email address.\"] }\n}",
                            "name": "Email not registered"
                        }
                    ]
                },
                {
                    "name": "Verify Forgot Password OTP",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/auth\/forgot-password\/verify-otp",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/auth\/forgot-password\/verify-otp"
                        },
                        "method": "POST",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": {
                            "mode": "raw",
                            "raw": "{\"email\":\"jane@example.com\",\"otp\":\"482913\"}"
                        },
                        "description": "Verifies the password-reset OTP and returns a short-lived `reset_token`\n(60 minutes, single-use) that must be passed to Reset Password. This is\nstep 2 of 3 in the reset flow.",
                        "auth": {
                            "type": "noauth"
                        }
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"message\": \"OTP verified. Use the reset token to set a new password.\",\n  \"reset_token\": \"SZyXkavPnBBJTtfAKD7L89NRvmbhHfopzoDbNt8982c1B16sRHdFJu8EOzNpz8c5\"\n}",
                            "name": "OTP verified"
                        },
                        {
                            "header": [],
                            "code": 422,
                            "body": "{\n  \"message\": \"The provided OTP is invalid or has expired.\",\n  \"errors\": { \"otp\": [\"The provided OTP is invalid or has expired.\"] }\n}",
                            "name": "Invalid or expired OTP"
                        }
                    ]
                },
                {
                    "name": "Resend Forgot Password OTP",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/auth\/forgot-password\/resend-otp",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/auth\/forgot-password\/resend-otp"
                        },
                        "method": "POST",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": {
                            "mode": "raw",
                            "raw": "{\"email\":\"jane@example.com\"}"
                        },
                        "description": "Invalidates the previous password-reset OTP and sends a new one.",
                        "auth": {
                            "type": "noauth"
                        }
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"message\": \"A new OTP has been sent to your email.\"\n}",
                            "name": "OTP resent"
                        },
                        {
                            "header": [],
                            "code": 422,
                            "body": "{\n  \"message\": \"We can't find a user with that email address.\",\n  \"errors\": { \"email\": [\"We can't find a user with that email address.\"] }\n}",
                            "name": "Email not registered"
                        }
                    ]
                },
                {
                    "name": "Reset Password",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/auth\/reset-password",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/auth\/reset-password"
                        },
                        "method": "POST",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": {
                            "mode": "raw",
                            "raw": "{\"email\":\"jane@example.com\",\"reset_token\":\"a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2\",\"new_password\":\"newPassword123\"}"
                        },
                        "description": "Sets a new password using the `reset_token` from Verify Forgot Password OTP.\nThis is step 3 of 3 in the reset flow. Revokes all previously issued tokens\nand returns a fresh one (auto-login) - store the new token.",
                        "auth": {
                            "type": "noauth"
                        }
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"email\": \"jane@example.com\",\n  \"message\": \"Password reset successfully.\",\n  \"token\": \"5|DUmbGpsWeDowDywPGdLTLsL53pceVqfWodvWwltNfaf1850e\"\n}",
                            "name": "Password reset"
                        },
                        {
                            "header": [],
                            "code": 422,
                            "body": "{\n  \"message\": \"The provided reset token is invalid or has expired.\",\n  \"errors\": { \"reset_token\": [\"The provided reset token is invalid or has expired.\"] }\n}",
                            "name": "Invalid, expired, or already-used reset token"
                        }
                    ]
                },
                {
                    "name": "Logout",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/auth\/logout",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/auth\/logout"
                        },
                        "method": "POST",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": null,
                        "description": "Revokes only the token used to authenticate this request (the current\ndevice\/session). Other devices stay logged in."
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"message\": \"Logged out successfully.\"\n}",
                            "name": "Success"
                        }
                    ]
                }
            ]
        },
        {
            "name": "Account",
            "description": "\nManaging the authenticated user's own profile, password, and account. All of\nthese require `Authorization: Bearer {token}`.",
            "item": [
                {
                    "name": "Get Profile",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/profile",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/profile"
                        },
                        "method": "GET",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": null,
                        "description": "Returns the authenticated user's profile."
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"data\": {\n    \"id\": 2,\n    \"full_name\": \"Dr Jane Doe\",\n    \"email\": \"jane@example.com\",\n    \"phone_number\": \"03007654321\",\n    \"designation\": \"Consultant\",\n    \"bio\": \"Dermatologist with a special interest in dermoscopy.\",\n    \"province\": \"Punjab\",\n    \"city\": \"Lahore\",\n    \"pmdc_number\": \"PMDC-1234\",\n    \"fellowship_number\": \"FEL-5678\",\n    \"institutional_number\": \"INST-9012\",\n    \"role\": \"student\",\n    \"avatar_url\": null,\n    \"is_verified\": true,\n    \"created_at\": \"2026-07-27T10:08:49.000000Z\"\n  }\n}",
                            "name": "Success"
                        }
                    ]
                },
                {
                    "name": "Update Profile",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/profile",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/profile"
                        },
                        "method": "PUT",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": {
                            "mode": "raw",
                            "raw": "{\"full_name\":\"Dr Jane A. Doe\",\"phone_number\":\"03009998888\",\"designation\":\"Consultant Dermatologist\",\"bio\":\"Dermatologist with a special interest in dermoscopy and skin cancer screening.\",\"province\":\"Sindh\",\"city\":\"Karachi\",\"pmdc_number\":\"PMDC-12345\",\"fellowship_number\":\"FCPS-6789\",\"institutional_number\":\"INST-001\"}"
                        },
                        "description": "Partially updates the authenticated user's profile - only send the fields\nyou want to change. The email cannot be changed here. To change the avatar,\nuse Update Profile Picture instead."
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"data\": {\n    \"id\": 2,\n    \"full_name\": \"New Name\",\n    \"email\": \"jane@example.com\",\n    \"phone_number\": \"03009998888\",\n    \"designation\": \"Consultant\",\n    \"bio\": \"Dermatologist with a special interest in dermoscopy.\",\n    \"province\": \"Punjab\",\n    \"city\": \"Karachi\",\n    \"pmdc_number\": \"PMDC-1234\",\n    \"fellowship_number\": \"FEL-5678\",\n    \"institutional_number\": \"INST-9012\",\n    \"role\": \"student\",\n    \"avatar_url\": null,\n    \"is_verified\": true,\n    \"created_at\": \"2026-07-27T10:08:49.000000Z\"\n  },\n  \"message\": \"Profile updated successfully.\"\n}",
                            "name": "Success"
                        }
                    ]
                },
                {
                    "name": "Update Profile Picture",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/profile\/avatar",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/profile\/avatar"
                        },
                        "method": "POST",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "multipart\/form-data"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": {
                            "mode": "formdata",
                            "formdata": [
                                {
                                    "key": "avatar",
                                    "src": [],
                                    "type": "file"
                                }
                            ]
                        },
                        "description": "Replaces the authenticated user's avatar. Deletes the previous file, if any."
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"avatar_url\": \"http:\/\/dots-app.test\/storage\/avatars\/example.png\",\n  \"message\": \"Profile picture updated successfully.\"\n}",
                            "name": "Success"
                        }
                    ]
                },
                {
                    "name": "Change Password",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/change-password",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/change-password"
                        },
                        "method": "POST",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": {
                            "mode": "raw",
                            "raw": "{\"current_password\":\"password123\",\"new_password\":\"newPassword123\"}"
                        },
                        "description": "Changes the authenticated user's password. Revokes ALL existing tokens\n(including the one used for this request) and returns a fresh one - the\napp must store the new token."
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"message\": \"Password changed successfully.\",\n  \"token\": \"14|MxL5u7YFFFDzlMkt3weFmTMGZYN57LWhBek5XYDef754b4ea\"\n}",
                            "name": "Success"
                        },
                        {
                            "header": [],
                            "code": 422,
                            "body": "{\n  \"message\": \"The provided password is incorrect.\",\n  \"errors\": { \"current_password\": [\"The provided password is incorrect.\"] }\n}",
                            "name": "Wrong current password"
                        }
                    ]
                },
                {
                    "name": "Delete Account",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/account",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/account"
                        },
                        "method": "DELETE",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": {
                            "mode": "raw",
                            "raw": "{\"current_password\":\"password123\"}"
                        },
                        "description": "Permanently deletes the authenticated user's account after confirming\nthe current password. Also deletes the avatar file and revokes all\ntokens. This is destructive and cannot be undone."
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"message\": \"Account deleted successfully.\"\n}",
                            "name": "Success"
                        },
                        {
                            "header": [],
                            "code": 422,
                            "body": "{\n  \"message\": \"The provided password is incorrect.\",\n  \"errors\": { \"current_password\": [\"The provided password is incorrect.\"] }\n}",
                            "name": "Wrong password"
                        }
                    ]
                }
            ]
        },
        {
            "name": "Cases",
            "description": "\nClinical case submission and the shared case feed. All of these require\n`Authorization: Bearer {token}`. A case only requires at least one image\n(clinical or dermoscopic); every other field is optional free text\/arrays -\nthere's no fixed option list yet, the Flutter app owns validation and\ndropdown values. Cases are stored with a `pending` status; there's no\nreview\/approval workflow yet. Any authenticated user can view any case and\nits comments, and can comment\/react on it - this is a shared feed, not\nprivate per-user data.\n\n## Case code\n\nEvery case carries a `code`, a short handle for that case:\n\n- Exactly five characters, always uppercase letters and digits.\n- Drawn from an alphabet that leaves out the characters people misread:\n  there is never an `O`, `0`, `I` or `1` in a code. `K7F2Q` is a real shape;\n  `K7F2O` can never occur.\n- Assigned by the server when the case is created. The client never sends\n  it, and it is never reissued: editing a case, adding or removing images,\n  rejection and resubmission all leave it unchanged.\n- Unique across every case, so a full code identifies exactly one case.\n\nThe code is on every case payload, on every endpoint, including the admin\nAPI. It is what the search box is for - see below.\n\n## Search\n\n`GET cases` and `GET cases\/mine` both accept `?search=`. One parameter,\ntwo behaviours, decided by what was typed:\n\n| Search term | Matches |\n|---|---|\n| A full case code, e.g. `K7F2Q` | That one case, by exact code. Letter case is ignored, so `k7f2q` works too |\n| Anything else, e.g. `melanoma` | A fragment of `clinical.diagnosis`, `dermoscopic.diagnosis`, or `body_site` |\n\nNotes for testing:\n\n- A partial code does **not** match. Searching `K7F` returns nothing unless\n  `K7F` happens to appear in a diagnosis or body site.\n- Search runs before pagination, so `meta.total` is the number of matches,\n  not the size of the whole feed.\n- No match is an empty result, never a 404: `{\"data\": [], \"meta\": {\"skip\":\n  0, \"limit\": 15, \"total\": 0}}`.\n- Search on the feed still only sees approved cases. To find your own\n  pending or rejected case by code, search `cases\/mine`.\n\n## Pinned cases\n\nAn admin can pin a case to the top of the feed, from the admin panel or\nfrom `POST \/api\/v1\/admin\/cases\/pin`. Every case payload carries three keys\nfor it, always present:\n\n| Key | Type | Meaning |\n|---|---|---|\n| `is_pinned` | bool | `true` while the case is pinned. Never null |\n| `pinned_at` | string, nullable | When it was pinned, ISO 8601. `null` when not pinned |\n| `pinned_by` | object, nullable | The admin who pinned it: `{id, full_name, avatar_url}`. `null` when not pinned |\n\nOrdering rules for `GET cases`, in order of precedence:\n\n1. Pinned cases first.\n2. Among pinned cases, most recently pinned first - by `pinned_at`, not by\n   when the case was created.\n3. Everything else after, newest created first.\n\nNotes for testing:\n\n- More than one case can be pinned at a time. There is no limit and no\n  \"only one pinned case\" rule.\n- Pinning an already-pinned case is allowed and refreshes `pinned_at`,\n  which moves it to the front of the pinned block.\n- Pinning is not review. Pinning a `pending` or `rejected` case stores the\n  pin, but the case still does not appear in the feed until it is approved;\n  its submitter sees the pin fields in `cases\/mine`.\n- Unpinning clears both `pinned_at` and `pinned_by` and drops the case back\n  into the normal newest-first order. Nothing else about the case changes.\n- `cases\/mine` is deliberately **not** reordered by pins: a submitter's own\n  history stays newest-first, even for a case that is pinned in the feed.\n- Deleting the admin account that pinned a case leaves the case pinned with\n  `pinned_by: null`. Render the badge from `is_pinned`, not from\n  `pinned_by`.\n\n**Response shape.** Every endpoint below returns a case with exactly the same\nkeys, so one client model parses all of them. Keys are never omitted. A value\nmay be null when the field is genuinely unset (`age`, `pdf`,\n`rejection_reason`), but lists are always lists and counts are always\nintegers. `comments` is populated only by Get Case; on the list endpoints it\nis `[]` and `comments_count` is the authoritative number.",
            "item": [
                {
                    "name": "List My Cases (History)",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/cases\/mine",
                            "query": [
                                {
                                    "key": "skip",
                                    "value": "0",
                                    "description": "Number of cases to skip. Defaults to 0.",
                                    "disabled": true
                                },
                                {
                                    "key": "limit",
                                    "value": "15",
                                    "description": "Max cases to return (capped at 100). Defaults to 15.",
                                    "disabled": false
                                },
                                {
                                    "key": "search",
                                    "value": "K7F2Q",
                                    "description": "A case code, or text to match against diagnosis or body site.",
                                    "disabled": false
                                }
                            ],
                            "raw": "{{baseUrl}}\/api\/v1\/cases\/mine?skip=0&limit=15&search=K7F2Q"
                        },
                        "method": "GET",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": null,
                        "description": "List of only the authenticated user's own cases, newest first. This is\nthe one place a submitter sees their `pending` and `rejected` cases, so\na rejected case here carries the admin's `rejection_reason`; editing it\nresubmits it and clears the reason.\n\nPins do **not** reorder this list: a case of yours that an admin pinned\nstill sits in date order here, with `is_pinned: true` on it.\n\nAccepts the same `search` param as the feed, so a submitter can find\ntheir own case by its code - including one still awaiting review, which\nthe feed search cannot see. Paginated with `skip`\/`limit` query params - `skip` defaults to 0, `limit` defaults\nto 15 (max 100). The client is responsible for advancing `skip` on\nsubsequent requests (e.g. `skip=15` for the next page after a `limit=15`\nfirst page) and for stopping once `skip + limit >= meta.total`."
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"code\": \"K7F2Q\",\n      \"user\": { \"id\": 3, \"full_name\": \"Dr Jane Doe\", \"avatar_url\": null },\n      \"status\": \"pending\",\n      \"is_pinned\": false,\n      \"pinned_at\": null,\n      \"pinned_by\": null,\n      \"age\": 52,\n      \"gender\": \"male\",\n      \"fitzpatrick_skin_type\": \"III\",\n      \"body_site\": \"trunk\",\n      \"pdf\": {\n        \"url\": \"http:\/\/dots-app.test\/storage\/cases\/1\/pdf\/report.pdf\",\n        \"name\": \"report.pdf\",\n        \"size\": 148213\n      },\n      \"clinical\": {\n        \"diagnosis\": \"melanoma\",\n        \"histopathology\": null,\n        \"images\": [\n          { \"id\": 1, \"url\": \"http:\/\/dots-app.test\/storage\/cases\/1\/clinical\/example.png\" }\n        ]\n      },\n      \"dermoscopic\": {\n        \"lesion_type\": \"melanocytic\",\n        \"features\": [\"pigment network\", \"streaks\"],\n        \"vascular_pattern\": [\"dotted\"],\n        \"colours_present\": [\"black\", \"brown\"],\n        \"scale\": \"fine\",\n        \"pattern\": \"reticular\",\n        \"image_metadata\": [\"polarized\"],\n        \"diagnosis\": \"melanoma\",\n        \"histopathology\": null,\n        \"images\": []\n      },\n      \"comments_count\": 2,\n      \"comments\": [],\n      \"created_at\": \"2026-07-28T06:41:56.000000Z\"\n    }\n  ],\n  \"meta\": { \"skip\": 0, \"limit\": 15, \"total\": 1 }\n}",
                            "name": "Success - own cases, including pending and rejected"
                        },
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"data\": [\n    {\n      \"id\": 4,\n      \"code\": \"P8VNC\",\n      \"user\": { \"id\": 3, \"full_name\": \"Dr Jane Doe\", \"avatar_url\": null },\n      \"status\": \"rejected\",\n      \"is_pinned\": false,\n      \"pinned_at\": null,\n      \"pinned_by\": null,\n      \"rejection_reason\": \"Photos are out of focus.\",\n      \"age\": 61,\n      \"gender\": \"male\",\n      \"fitzpatrick_skin_type\": \"IV\",\n      \"body_site\": \"lower limb\",\n      \"pdf\": null,\n      \"clinical\": {\n        \"diagnosis\": \"nevus\",\n        \"histopathology\": null,\n        \"images\": [\n          { \"id\": 30, \"url\": \"https:\/\/www.dots.mhn.services\/storage\/cases\/4\/clinical\/blurry.png\" }\n        ]\n      },\n      \"dermoscopic\": {\n        \"lesion_type\": null,\n        \"features\": [],\n        \"vascular_pattern\": [],\n        \"colours_present\": [],\n        \"scale\": null,\n        \"pattern\": null,\n        \"image_metadata\": [],\n        \"diagnosis\": null,\n        \"histopathology\": null,\n        \"images\": []\n      },\n      \"comments_count\": 0,\n      \"comments\": [],\n      \"created_at\": \"2026-09-02T08:12:00.000000Z\"\n    }\n  ],\n  \"meta\": { \"skip\": 0, \"limit\": 15, \"total\": 1 }\n}",
                            "name": "a rejected case of your own, with the reason"
                        },
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"data\": [],\n  \"meta\": { \"skip\": 0, \"limit\": 15, \"total\": 0 }\n}",
                            "name": "you have not submitted anything yet"
                        },
                        {
                            "header": [],
                            "code": 401,
                            "body": "{\n  \"message\": \"Unauthenticated.\"\n}",
                            "name": "Missing or expired token"
                        }
                    ]
                },
                {
                    "name": "List Cases (Feed)",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/cases",
                            "query": [
                                {
                                    "key": "skip",
                                    "value": "0",
                                    "description": "Number of cases to skip. Defaults to 0.",
                                    "disabled": true
                                },
                                {
                                    "key": "limit",
                                    "value": "15",
                                    "description": "Max cases to return (capped at 100). Defaults to 15.",
                                    "disabled": false
                                },
                                {
                                    "key": "search",
                                    "value": "K7F2Q",
                                    "description": "A case code, or text to match against diagnosis or body site.",
                                    "disabled": false
                                }
                            ],
                            "raw": "{{baseUrl}}\/api\/v1\/cases?skip=0&limit=15&search=K7F2Q"
                        },
                        "method": "GET",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": null,
                        "description": "Feed of approved cases from every user. Cases an admin pinned come\nfirst, most recently pinned first; the rest follow newest first. Cases\nawaiting review, and cases an admin rejected, are not in the feed; a\nsubmitter still sees their own in `cases\/mine`.\n\nPass `search` to filter the feed. A five-character case code matches\nthat one case exactly; any other term is matched as a fragment of the\nclinical diagnosis, the dermoscopic diagnosis, or the body site.\n\nPaginated with `skip`\/`limit` query params - `skip` defaults to 0,\n`limit` defaults to 15 (max 100).\nThe client is responsible for advancing `skip` on subsequent requests\n(e.g. `skip=15` for the next page after a `limit=15` first page) and for\nstopping once `skip + limit >= meta.total`."
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"data\": [\n    {\n      \"id\": 8,\n      \"code\": \"M4XTB\",\n      \"user\": { \"id\": 3, \"full_name\": \"Dr Jane Doe\", \"avatar_url\": null },\n      \"status\": \"approved\",\n      \"is_pinned\": true,\n      \"pinned_at\": \"2026-09-05T09:15:00.000000Z\",\n      \"pinned_by\": { \"id\": 1, \"full_name\": \"Dots Admin\", \"avatar_url\": null },\n      \"rejection_reason\": null,\n      \"age\": 34,\n      \"gender\": \"female\",\n      \"fitzpatrick_skin_type\": \"II\",\n      \"body_site\": \"face\",\n      \"pdf\": null,\n      \"clinical\": {\n        \"diagnosis\": \"basal cell carcinoma\",\n        \"histopathology\": null,\n        \"images\": [\n          { \"id\": 12, \"url\": \"https:\/\/www.dots.mhn.services\/storage\/cases\/8\/clinical\/lesion.png\" }\n        ]\n      },\n      \"dermoscopic\": {\n        \"lesion_type\": \"non-melanocytic\",\n        \"features\": [\"arborizing vessels\"],\n        \"vascular_pattern\": [\"arborizing\"],\n        \"colours_present\": [\"pink\"],\n        \"scale\": null,\n        \"pattern\": null,\n        \"image_metadata\": [],\n        \"diagnosis\": null,\n        \"histopathology\": null,\n        \"images\": []\n      },\n      \"comments_count\": 5,\n      \"comments\": [],\n      \"created_at\": \"2026-08-14T11:02:31.000000Z\"\n    },\n    {\n      \"id\": 12,\n      \"code\": \"K7F2Q\",\n      \"user\": { \"id\": 5, \"full_name\": \"Dr John Roe\", \"avatar_url\": null },\n      \"status\": \"approved\",\n      \"is_pinned\": false,\n      \"pinned_at\": null,\n      \"pinned_by\": null,\n      \"rejection_reason\": null,\n      \"age\": 52,\n      \"gender\": \"male\",\n      \"fitzpatrick_skin_type\": \"III\",\n      \"body_site\": \"trunk\",\n      \"pdf\": {\n        \"url\": \"https:\/\/www.dots.mhn.services\/storage\/cases\/12\/pdf\/report.pdf\",\n        \"name\": \"report.pdf\",\n        \"size\": 148213\n      },\n      \"clinical\": {\n        \"diagnosis\": \"melanoma\",\n        \"histopathology\": null,\n        \"images\": [\n          { \"id\": 18, \"url\": \"https:\/\/www.dots.mhn.services\/storage\/cases\/12\/clinical\/example.png\" }\n        ]\n      },\n      \"dermoscopic\": {\n        \"lesion_type\": \"melanocytic\",\n        \"features\": [\"pigment network\", \"streaks\"],\n        \"vascular_pattern\": [\"dotted\"],\n        \"colours_present\": [\"black\", \"brown\"],\n        \"scale\": \"fine\",\n        \"pattern\": \"reticular\",\n        \"image_metadata\": [\"polarized\"],\n        \"diagnosis\": \"melanoma\",\n        \"histopathology\": null,\n        \"images\": [\n          { \"id\": 19, \"url\": \"https:\/\/www.dots.mhn.services\/storage\/cases\/12\/dermoscopic\/example.png\" }\n        ]\n      },\n      \"comments_count\": 2,\n      \"comments\": [],\n      \"created_at\": \"2026-09-01T06:41:56.000000Z\"\n    }\n  ],\n  \"meta\": { \"skip\": 0, \"limit\": 15, \"total\": 2 }\n}",
                            "name": "Success - a pinned case leads the feed"
                        },
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"data\": [\n    {\n      \"id\": 12,\n      \"code\": \"K7F2Q\",\n      \"user\": { \"id\": 5, \"full_name\": \"Dr John Roe\", \"avatar_url\": null },\n      \"status\": \"approved\",\n      \"is_pinned\": false,\n      \"pinned_at\": null,\n      \"pinned_by\": null,\n      \"rejection_reason\": null,\n      \"age\": 52,\n      \"gender\": \"male\",\n      \"fitzpatrick_skin_type\": \"III\",\n      \"body_site\": \"trunk\",\n      \"pdf\": null,\n      \"clinical\": {\n        \"diagnosis\": \"melanoma\",\n        \"histopathology\": null,\n        \"images\": [\n          { \"id\": 18, \"url\": \"https:\/\/www.dots.mhn.services\/storage\/cases\/12\/clinical\/example.png\" }\n        ]\n      },\n      \"dermoscopic\": {\n        \"lesion_type\": \"melanocytic\",\n        \"features\": [],\n        \"vascular_pattern\": [],\n        \"colours_present\": [],\n        \"scale\": null,\n        \"pattern\": null,\n        \"image_metadata\": [],\n        \"diagnosis\": null,\n        \"histopathology\": null,\n        \"images\": []\n      },\n      \"comments_count\": 2,\n      \"comments\": [],\n      \"created_at\": \"2026-09-01T06:41:56.000000Z\"\n    }\n  ],\n  \"meta\": { \"skip\": 0, \"limit\": 15, \"total\": 1 }\n}",
                            "name": "search by case code - one exact match"
                        },
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"data\": [],\n  \"meta\": { \"skip\": 0, \"limit\": 15, \"total\": 0 }\n}",
                            "name": "search matched nothing - empty list, not a 404"
                        },
                        {
                            "header": [],
                            "code": 401,
                            "body": "{\n  \"message\": \"Unauthenticated.\"\n}",
                            "name": "Missing or expired token"
                        }
                    ]
                },
                {
                    "name": "Submit Case",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/cases",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/cases"
                        },
                        "method": "POST",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "multipart\/form-data"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": {
                            "mode": "formdata",
                            "formdata": [
                                {
                                    "key": "age",
                                    "value": "52",
                                    "type": "text",
                                    "description": "Patient age in years."
                                },
                                {
                                    "key": "gender",
                                    "value": "male",
                                    "type": "text",
                                    "description": "Patient gender."
                                },
                                {
                                    "key": "fitzpatrick_skin_type",
                                    "value": "III",
                                    "type": "text",
                                    "description": "Fitzpatrick skin type."
                                },
                                {
                                    "key": "body_site",
                                    "value": "trunk",
                                    "type": "text",
                                    "description": "Anatomical site of the lesion."
                                },
                                {
                                    "key": "clinical_diagnosis",
                                    "value": "melanoma",
                                    "type": "text",
                                    "description": "Clinical (visual) diagnosis."
                                },
                                {
                                    "key": "clinical_histopathology",
                                    "value": "Superficial spreading melanoma, Breslow depth 0.8mm",
                                    "type": "text",
                                    "description": "Clinical histopathology findings, if biopsied."
                                },
                                {
                                    "key": "lesion_type",
                                    "value": "melanocytic",
                                    "type": "text",
                                    "description": "Lesion classification."
                                },
                                {
                                    "key": "dermoscopic_features[0]",
                                    "value": "pigment network",
                                    "type": "text",
                                    "description": ""
                                },
                                {
                                    "key": "dermoscopic_features[1]",
                                    "value": "streaks",
                                    "type": "text",
                                    "description": ""
                                },
                                {
                                    "key": "vascular_pattern[0]",
                                    "value": "dotted",
                                    "type": "text",
                                    "description": ""
                                },
                                {
                                    "key": "colours_present[0]",
                                    "value": "black",
                                    "type": "text",
                                    "description": ""
                                },
                                {
                                    "key": "colours_present[1]",
                                    "value": "brown",
                                    "type": "text",
                                    "description": ""
                                },
                                {
                                    "key": "scale",
                                    "value": "fine",
                                    "type": "text",
                                    "description": "FotoFinder-schema dermoscopy field."
                                },
                                {
                                    "key": "pattern",
                                    "value": "reticular",
                                    "type": "text",
                                    "description": "FotoFinder-schema dermoscopy field."
                                },
                                {
                                    "key": "image_metadata[0]",
                                    "value": "polarized",
                                    "type": "text",
                                    "description": ""
                                },
                                {
                                    "key": "dermoscopic_diagnosis",
                                    "value": "melanoma",
                                    "type": "text",
                                    "description": "Dermoscopic diagnosis."
                                },
                                {
                                    "key": "dermoscopic_histopathology",
                                    "value": "Not biopsied",
                                    "type": "text",
                                    "description": "Dermoscopic histopathology findings, if biopsied."
                                },
                                {
                                    "key": "clinical_images[]",
                                    "src": [],
                                    "type": "file"
                                },
                                {
                                    "key": "dermoscopic_images[]",
                                    "src": [],
                                    "type": "file"
                                },
                                {
                                    "key": "pdf",
                                    "src": [],
                                    "type": "file"
                                }
                            ]
                        },
                        "description": "Submits a new case for review. At least one of `clinical_images[]` or\n`dermoscopic_images[]` is required (both accept multiple files);\neverything else is optional. `dermoscopic_features[]`, `vascular_pattern[]`,\n`colours_present[]`, and `image_metadata[]` are multi-select - repeat the\nkey for each value.\n\nA single supporting PDF can be attached as `pdf` (up to 20 MB). It comes\nback on every case response as a `pdf` object with `url`, `name`, and\n`size`, or as `null` when the case has no PDF.\n\nThe response carries the server-assigned `code` for the new case. Show\nit to the submitter after a successful submission - it is how they, or\nanyone they give it to, find the case again through search. The client\nnever generates or sends a code, and a code sent in the request body is\nignored.\n\nA new case is always `status: \"pending\"`, `is_pinned: false`, and has an\nempty `comments` list with `comments_count: 0`."
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 201,
                            "body": "{\n  \"data\": {\n    \"id\": 1,\n    \"code\": \"K7F2Q\",\n    \"user\": { \"id\": 3, \"full_name\": \"Dr Jane Doe\", \"avatar_url\": null },\n    \"status\": \"pending\",\n    \"is_pinned\": false,\n    \"pinned_at\": null,\n    \"pinned_by\": null,\n    \"age\": 52,\n    \"gender\": \"male\",\n    \"fitzpatrick_skin_type\": \"III\",\n    \"body_site\": \"trunk\",\n    \"pdf\": {\n      \"url\": \"http:\/\/dots-app.test\/storage\/cases\/1\/pdf\/report.pdf\",\n      \"name\": \"report.pdf\",\n      \"size\": 148213\n    },\n    \"clinical\": {\n      \"diagnosis\": \"melanoma\",\n      \"histopathology\": null,\n      \"images\": [\n        { \"id\": 1, \"url\": \"http:\/\/dots-app.test\/storage\/cases\/1\/clinical\/example.png\" }\n      ]\n    },\n    \"dermoscopic\": {\n      \"lesion_type\": \"melanocytic\",\n      \"features\": [\"pigment network\", \"streaks\"],\n      \"vascular_pattern\": [\"dotted\"],\n      \"colours_present\": [\"black\", \"brown\"],\n      \"scale\": \"fine\",\n      \"pattern\": \"reticular\",\n      \"image_metadata\": [\"polarized\"],\n      \"diagnosis\": \"melanoma\",\n      \"histopathology\": null,\n      \"images\": [\n        { \"id\": 2, \"url\": \"http:\/\/dots-app.test\/storage\/cases\/1\/dermoscopic\/example.png\" }\n      ]\n    },\n    \"comments_count\": 0,\n    \"comments\": [],\n    \"created_at\": \"2026-07-28T06:41:56.000000Z\"\n  },\n  \"message\": \"Case submitted for review.\"\n}",
                            "name": "Case submitted"
                        },
                        {
                            "header": [],
                            "code": 401,
                            "body": "{\n  \"message\": \"Unauthenticated.\"\n}",
                            "name": "Missing or expired token"
                        },
                        {
                            "header": [],
                            "code": 422,
                            "body": "{\n  \"message\": \"Upload at least one clinical or dermoscopic image.\",\n  \"errors\": {\n    \"clinical_images\": [\"Upload at least one clinical or dermoscopic image.\"],\n    \"dermoscopic_images\": [\"Upload at least one clinical or dermoscopic image.\"]\n  }\n}",
                            "name": "No images attached"
                        }
                    ]
                },
                {
                    "name": "Get Case",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/cases\/:id",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/cases\/:id",
                            "variable": [
                                {
                                    "id": "id",
                                    "key": "id",
                                    "value": 1,
                                    "description": "The ID of the case."
                                },
                                {
                                    "id": "case",
                                    "key": "case",
                                    "value": 1,
                                    "description": "The case ID."
                                }
                            ]
                        },
                        "method": "GET",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": null,
                        "description": "Shows a single case with its images and comments (each comment includes\nthe commenter's name\/photo, like\/dislike counts, and the authenticated\nuser's own reaction, if any, as `my_reaction`).\n\nThis is the only endpoint that fills `comments`; the list endpoints\nleave it `[]`. The case is addressed by numeric `id`, not by `code` -\nto open a case from a code, search the feed for the code and use the\n`id` you get back.\n\nAny authenticated user can read any approved case here, so the pin\nfields are visible to everyone and a viewer who is not the submitter\nstill sees `is_pinned` and `pinned_by`."
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"data\": {\n    \"id\": 1,\n    \"code\": \"K7F2Q\",\n    \"user\": { \"id\": 3, \"full_name\": \"Dr Jane Doe\", \"avatar_url\": null },\n    \"status\": \"approved\",\n    \"is_pinned\": true,\n    \"pinned_at\": \"2026-09-05T09:15:00.000000Z\",\n    \"pinned_by\": { \"id\": 1, \"full_name\": \"Dots Admin\", \"avatar_url\": null },\n    \"age\": 52,\n    \"gender\": \"male\",\n    \"fitzpatrick_skin_type\": \"III\",\n    \"body_site\": \"trunk\",\n    \"pdf\": {\n      \"url\": \"http:\/\/dots-app.test\/storage\/cases\/1\/pdf\/report.pdf\",\n      \"name\": \"report.pdf\",\n      \"size\": 148213\n    },\n    \"clinical\": {\n      \"diagnosis\": \"melanoma\",\n      \"histopathology\": null,\n      \"images\": [\n        { \"id\": 1, \"url\": \"http:\/\/dots-app.test\/storage\/cases\/1\/clinical\/example.png\" }\n      ]\n    },\n    \"dermoscopic\": {\n      \"lesion_type\": \"melanocytic\",\n      \"features\": [\"pigment network\", \"streaks\"],\n      \"vascular_pattern\": [\"dotted\"],\n      \"colours_present\": [\"black\", \"brown\"],\n      \"scale\": \"fine\",\n      \"pattern\": \"reticular\",\n      \"image_metadata\": [\"polarized\"],\n      \"diagnosis\": \"melanoma\",\n      \"histopathology\": null,\n      \"images\": [\n        { \"id\": 2, \"url\": \"http:\/\/dots-app.test\/storage\/cases\/1\/dermoscopic\/example.png\" }\n      ]\n    },\n    \"comments_count\": 1,\n    \"comments\": [\n      {\n        \"id\": 5,\n        \"body\": \"Great case, thanks for sharing.\",\n        \"user\": { \"id\": 4, \"full_name\": \"Dr John Roe\", \"avatar_url\": null },\n        \"likes_count\": 2,\n        \"dislikes_count\": 0,\n        \"my_reaction\": \"like\",\n        \"created_at\": \"2026-07-29T09:00:00.000000Z\"\n      }\n    ],\n    \"created_at\": \"2026-07-28T06:41:56.000000Z\"\n  }\n}",
                            "name": "Success - a pinned case with its comments"
                        },
                        {
                            "header": [],
                            "code": 401,
                            "body": "{\n  \"message\": \"Unauthenticated.\"\n}",
                            "name": "Missing or expired token"
                        },
                        {
                            "header": [],
                            "code": 404,
                            "body": "{\n  \"message\": \"No query results for model [App\\\\Models\\\\ClinicalCase] 999\"\n}",
                            "name": "No case with that id"
                        }
                    ]
                },
                {
                    "name": "Update Case",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/cases\/:id",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/cases\/:id",
                            "variable": [
                                {
                                    "id": "id",
                                    "key": "id",
                                    "value": 1,
                                    "description": "The ID of the case."
                                },
                                {
                                    "id": "case",
                                    "key": "case",
                                    "value": 1,
                                    "description": "The case ID."
                                }
                            ]
                        },
                        "method": "POST",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "multipart\/form-data"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": {
                            "mode": "formdata",
                            "formdata": [
                                {
                                    "key": "remove_image_ids[0]",
                                    "value": "3",
                                    "type": "text",
                                    "description": ""
                                },
                                {
                                    "key": "remove_pdf",
                                    "value": "",
                                    "type": "text",
                                    "description": "Send true to detach and delete the current PDF. Ignored when a new `pdf` is sent."
                                },
                                {
                                    "key": "age",
                                    "value": "53",
                                    "type": "text",
                                    "description": "Patient age in years."
                                },
                                {
                                    "key": "gender",
                                    "value": "male",
                                    "type": "text",
                                    "description": "Patient gender."
                                },
                                {
                                    "key": "fitzpatrick_skin_type",
                                    "value": "III",
                                    "type": "text",
                                    "description": "Fitzpatrick skin type."
                                },
                                {
                                    "key": "body_site",
                                    "value": "trunk",
                                    "type": "text",
                                    "description": "Anatomical site of the lesion."
                                },
                                {
                                    "key": "clinical_diagnosis",
                                    "value": "melanoma, revised",
                                    "type": "text",
                                    "description": "Clinical (visual) diagnosis."
                                },
                                {
                                    "key": "clinical_histopathology",
                                    "value": "Superficial spreading melanoma, Breslow depth 0.8mm",
                                    "type": "text",
                                    "description": "Clinical histopathology findings, if biopsied."
                                },
                                {
                                    "key": "lesion_type",
                                    "value": "melanocytic",
                                    "type": "text",
                                    "description": "Lesion classification."
                                },
                                {
                                    "key": "dermoscopic_features[0]",
                                    "value": "pigment network",
                                    "type": "text",
                                    "description": ""
                                },
                                {
                                    "key": "dermoscopic_features[1]",
                                    "value": "streaks",
                                    "type": "text",
                                    "description": ""
                                },
                                {
                                    "key": "vascular_pattern[0]",
                                    "value": "dotted",
                                    "type": "text",
                                    "description": ""
                                },
                                {
                                    "key": "colours_present[0]",
                                    "value": "black",
                                    "type": "text",
                                    "description": ""
                                },
                                {
                                    "key": "colours_present[1]",
                                    "value": "brown",
                                    "type": "text",
                                    "description": ""
                                },
                                {
                                    "key": "scale",
                                    "value": "fine",
                                    "type": "text",
                                    "description": "FotoFinder-schema dermoscopy field."
                                },
                                {
                                    "key": "pattern",
                                    "value": "reticular",
                                    "type": "text",
                                    "description": "FotoFinder-schema dermoscopy field."
                                },
                                {
                                    "key": "image_metadata[0]",
                                    "value": "polarized",
                                    "type": "text",
                                    "description": ""
                                },
                                {
                                    "key": "dermoscopic_diagnosis",
                                    "value": "melanoma",
                                    "type": "text",
                                    "description": "Dermoscopic diagnosis."
                                },
                                {
                                    "key": "dermoscopic_histopathology",
                                    "value": "Not biopsied",
                                    "type": "text",
                                    "description": "Dermoscopic histopathology findings, if biopsied."
                                },
                                {
                                    "key": "new_clinical_images[]",
                                    "src": [],
                                    "type": "file"
                                },
                                {
                                    "key": "new_dermoscopic_images[]",
                                    "src": [],
                                    "type": "file"
                                },
                                {
                                    "key": "pdf",
                                    "src": [],
                                    "type": "file"
                                },
                                {
                                    "key": "_method",
                                    "value": "PUT",
                                    "type": "text"
                                }
                            ]
                        },
                        "description": "Partially updates a case you own - only send the fields you want to\nchange. Add new images with `new_clinical_images[]` \/\n`new_dermoscopic_images[]`, and remove existing ones by ID with\n`remove_image_ids[]`. Send `pdf` to attach or replace the case's PDF, or\n`remove_pdf=true` to delete it.\n\nEditing a case an admin rejected resubmits it: its status returns to\n`pending` and the previous `rejection_reason` is cleared.\n\nAn edit never changes the case's `code`, and never changes its pin: a\npinned case stays pinned with the same `pinned_at` through an edit, and\nthrough a rejection and resubmission."
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"data\": {\n    \"id\": 1,\n    \"code\": \"K7F2Q\",\n    \"user\": { \"id\": 3, \"full_name\": \"Dr Jane Doe\", \"avatar_url\": null },\n    \"status\": \"pending\",\n    \"is_pinned\": false,\n    \"pinned_at\": null,\n    \"pinned_by\": null,\n    \"age\": 53,\n    \"gender\": \"male\",\n    \"fitzpatrick_skin_type\": \"III\",\n    \"body_site\": \"trunk\",\n    \"pdf\": {\n      \"url\": \"http:\/\/dots-app.test\/storage\/cases\/1\/pdf\/report.pdf\",\n      \"name\": \"report.pdf\",\n      \"size\": 148213\n    },\n    \"clinical\": {\n      \"diagnosis\": \"melanoma, revised\",\n      \"histopathology\": null,\n      \"images\": [\n        { \"id\": 1, \"url\": \"http:\/\/dots-app.test\/storage\/cases\/1\/clinical\/example.png\" }\n      ]\n    },\n    \"dermoscopic\": {\n      \"lesion_type\": \"melanocytic\",\n      \"features\": [\"pigment network\", \"streaks\"],\n      \"vascular_pattern\": [\"dotted\"],\n      \"colours_present\": [\"black\", \"brown\"],\n      \"scale\": \"fine\",\n      \"pattern\": \"reticular\",\n      \"image_metadata\": [\"polarized\"],\n      \"diagnosis\": \"melanoma\",\n      \"histopathology\": null,\n      \"images\": []\n    },\n    \"comments_count\": 0,\n    \"comments\": [],\n    \"created_at\": \"2026-07-28T06:41:56.000000Z\"\n  },\n  \"message\": \"Case updated successfully.\"\n}",
                            "name": "Success"
                        },
                        {
                            "header": [],
                            "code": 401,
                            "body": "{\n  \"message\": \"Unauthenticated.\"\n}",
                            "name": "Missing or expired token"
                        },
                        {
                            "header": [],
                            "code": 403,
                            "body": "{\n  \"message\": \"This action is unauthorized.\"\n}",
                            "name": "Not the case owner"
                        },
                        {
                            "header": [],
                            "code": 404,
                            "body": "{\n  \"message\": \"No query results for model [App\\\\Models\\\\ClinicalCase] 999\"\n}",
                            "name": "No case with that id"
                        }
                    ]
                },
                {
                    "name": "Delete Case",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/cases\/:id",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/cases\/:id",
                            "variable": [
                                {
                                    "id": "id",
                                    "key": "id",
                                    "value": 1,
                                    "description": "The ID of the case."
                                },
                                {
                                    "id": "case",
                                    "key": "case",
                                    "value": 1,
                                    "description": "The case ID."
                                }
                            ]
                        },
                        "method": "DELETE",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": null,
                        "description": "Permanently deletes a case you own, along with its images and PDF (files\nand records) and its comments. This is destructive and cannot be undone."
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"message\": \"Case deleted successfully.\"\n}",
                            "name": "Success"
                        },
                        {
                            "header": [],
                            "code": 401,
                            "body": "{\n  \"message\": \"Unauthenticated.\"\n}",
                            "name": "Missing or expired token"
                        },
                        {
                            "header": [],
                            "code": 403,
                            "body": "{\n  \"message\": \"This action is unauthorized.\"\n}",
                            "name": "Not the case owner"
                        },
                        {
                            "header": [],
                            "code": 404,
                            "body": "{\n  \"message\": \"No query results for model [App\\\\Models\\\\ClinicalCase] 999\"\n}",
                            "name": "No case with that id"
                        }
                    ]
                },
                {
                    "name": "Add Comment",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/cases\/:case_id\/comments",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/cases\/:case_id\/comments",
                            "variable": [
                                {
                                    "id": "case_id",
                                    "key": "case_id",
                                    "value": 1,
                                    "description": "The ID of the case."
                                },
                                {
                                    "id": "case",
                                    "key": "case",
                                    "value": 1,
                                    "description": "The case ID."
                                }
                            ]
                        },
                        "method": "POST",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": {
                            "mode": "raw",
                            "raw": "{\"body\":\"Great case, thanks for sharing.\"}"
                        },
                        "description": "Adds a comment to a case. Any authenticated user may comment on any case."
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 201,
                            "body": "{\n  \"data\": {\n    \"id\": 5,\n    \"body\": \"Great case, thanks for sharing.\",\n    \"user\": { \"id\": 4, \"full_name\": \"Dr John Roe\", \"avatar_url\": null },\n    \"likes_count\": 0,\n    \"dislikes_count\": 0,\n    \"my_reaction\": null,\n    \"created_at\": \"2026-07-29T09:00:00.000000Z\"\n  },\n  \"message\": \"Comment added.\"\n}",
                            "name": "Comment added"
                        }
                    ]
                },
                {
                    "name": "Like\/Dislike Comment",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/comments\/:comment_id\/react",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/comments\/:comment_id\/react",
                            "variable": [
                                {
                                    "id": "comment_id",
                                    "key": "comment_id",
                                    "value": 1,
                                    "description": "The ID of the comment."
                                },
                                {
                                    "id": "comment",
                                    "key": "comment",
                                    "value": 5,
                                    "description": "The comment ID."
                                }
                            ]
                        },
                        "method": "POST",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": {
                            "mode": "raw",
                            "raw": "{\"type\":\"like\"}"
                        },
                        "description": "Sets the authenticated user's reaction on a comment to `like` or\n`dislike`. Sending the same `type` again removes the reaction (toggle\noff); sending the other type switches it. A user can only have one\nreaction per comment."
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"data\": {\n    \"likes_count\": 1,\n    \"dislikes_count\": 0,\n    \"my_reaction\": \"like\"\n  },\n  \"message\": \"Reaction saved.\"\n}",
                            "name": "Reaction set"
                        },
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"data\": {\n    \"likes_count\": 0,\n    \"dislikes_count\": 0,\n    \"my_reaction\": null\n  },\n  \"message\": \"Reaction removed.\"\n}",
                            "name": "Reaction removed (same type sent again)"
                        }
                    ]
                }
            ]
        },
        {
            "name": "Quizzes",
            "description": "\nQuizzes as the mobile app sees them. All of these require\n`Authorization: Bearer {token}`.\n\nOnly published quizzes are visible. **Each person gets one attempt per\nquiz**: a second submit returns `409`, and the recorded attempt is then read\nback from the result endpoint. Correct answers, explanations, and references\nare never sent before submission.\n\n## The two axes: quiz format and question type\n\nA quiz has a **`format`**, and each question inside it has a **`type`**. They\nare independent, and both change how you read the response.\n\n`format` is one of:\n\n| format | Graded? | What comes back from submit |\n|---|---|---|\n| `standard` | Yes | `score` out of `total_questions`, plus per-question `answers` |\n| `clinical_case` | Yes | Same as standard, plus `diagnosis` and `management` revealed |\n| `poll` | No | Aggregate tallies. **No `score`, no `answers`** |\n\nA `clinical_case` quiz is a case stem: `clinical_image`, `dermoscopic_image`\nand `history` are shown up front, and `diagnosis` \/ `management` stay hidden\nuntil the attempt is submitted.\n\n`type` is one of `single_choice`, `multiple_choice`, `true_false`,\n`match_following`, `poll`. Note a `poll` **question** can appear inside a\n`standard` quiz; it simply scores nothing and its `is_correct` is `null`.\n\n## After submitting: the review\n\nSubmit and result both return the whole attempt, not just the marks. The\ntwo endpoints return **the same payload**, so one parser covers both: submit\ngives it to you once at `201`, result gives it back any time afterwards at\n`200`.\n\nThe top level carries the marks:\n\n| Key | Type | Meaning |\n|---|---|---|\n| `quiz_id` | int | The quiz that was taken |\n| `quiz_title` | string | Its title, so a result screen needs no second call |\n| `format` | string | `standard` or `clinical_case`. A `poll` quiz never reaches this shape |\n| `score` | int | Questions answered correctly |\n| `total_questions` | int | What the attempt was scored out of. Frozen at submission time, so later edits to the quiz do not rewrite an old attempt |\n| `percentage` | number | `score \/ total_questions * 100`, one decimal place. `0` when the quiz has no questions |\n| `correct_count` | int | Same as `score`, counted from the answers |\n| `incorrect_count` | int | Answers graded wrong. Unanswered questions are counted here too; poll questions never are |\n| `unanswered_count` | int | Questions the student skipped |\n| `submitted_at` | string | ISO 8601 timestamp of the attempt |\n| `diagnosis`, `management` | string, nullable | Revealed for a `clinical_case` quiz, `null` otherwise |\n| `answers` | list | One entry per question in the quiz, in the quiz's order |\n\nEach entry in `answers`:\n\n| Key | Type | Meaning |\n|---|---|---|\n| `question_id` | int | The question |\n| `question` | string | Its text |\n| `type` | string | `single_choice`, `multiple_choice`, `true_false`, `match_following`, `poll` |\n| `image` | string, nullable | The question's image URL, `null` when it has none |\n| `is_correct` | bool, **nullable** | `true`\/`false`, or `null` for a poll question, which is recorded but never graded |\n| `is_answered` | bool | `false` when the student skipped this question |\n| `explanation` | string, nullable | Why the correct answer is correct. Only ever sent after submission |\n| `reference` | string, nullable | Source for the question |\n| `options` | list | Every option the question offered: `{id, text, is_correct, is_selected}`. Empty for `match_following` |\n| `selected_option_ids` | list of string | What the student picked. Empty when skipped, and empty for `match_following` |\n| `correct_option_ids` | list of string | The answer key. Empty for `match_following` and for a poll question |\n| `response` | object | The student's `{left_id: right_id}` map. `match_following` only, `[]` otherwise |\n| `correct_pairs` | object | The correct `{left_id: right_id}` map. `match_following` only, `[]` otherwise |\n| `left`, `right` | list | Both sides of a `match_following` question as `{id, text}`, in the authored order. Empty for every other type |\n\nThe quickest way to render a reviewed choice question is to ignore the id\nlists entirely and walk `options`: `is_correct` marks the right answer,\n`is_selected` marks what the student tapped, and a question is wrong when an\noption has `is_selected` without `is_correct`.\n\n### Scenarios worth testing on the review screen\n\n- **All correct**: `score == total_questions`, `percentage: 100`, every\n  answer `is_correct: true`.\n- **Some wrong**: the wrong answer has one option with `is_selected: true,\n  is_correct: false` and another with `is_correct: true, is_selected: false`.\n- **Skipped question**: `is_answered: false`, `selected_option_ids: []`,\n  `is_correct: false`, and no option has `is_selected`. It still appears in\n  `answers`, so the review shows the whole quiz.\n- **Multiple choice, partly right**: grading is all-or-nothing. Picking one\n  of two correct options is `is_correct: false`, and `options` shows both\n  correct ones so the screen can display what was missed.\n- **match_following, partly right**: also all-or-nothing. Compare `response`\n  against `correct_pairs` per pair to shade the rows individually.\n- **Poll question inside a graded quiz**: `is_correct: null`, every option\n  `is_correct: false`, and it contributes to neither `score` nor\n  `incorrect_count`. Render it as \"recorded\", not as right or wrong.\n- **Empty quiz** (no questions attached): `score: 0`, `total_questions: 0`,\n  `percentage: 0`, `answers: []`.\n- **Re-open later**: call the result endpoint again; the payload is\n  byte-for-byte what submit returned.\n\n## Reading the payload\n\nKeys are never omitted, so one client model parses every question and every\nanswer. **Branch on `type` and `format`, never on which keys are present.**\n\n- A question always carries `options`, `left` and `right`. A\n  `match_following` question fills `left`\/`right` and leaves `options` empty;\n  every other type does the reverse.\n- `right` is shuffled, seeded from the question id, so the pairing is not\n  given away by row order but stays stable for the same student. Match\n  `left[i].id` to `right[j].id`, never by position.\n- An answer always carries `selected_option_ids`, `correct_option_ids`,\n  `response` and `correct_pairs`. The first two are lists and are used by\n  every type except `match_following`; the last two are objects and are used\n  only by `match_following`. The unused pair is empty.\n- An answer also carries the question's own `options`, `left` and `right`,\n  in the same shape the quiz detail endpoint uses, so the review screen can\n  render the whole quiz back from the result alone. Each option there adds\n  `is_correct` and `is_selected`, which is the short way to render a\n  reviewed question without walking the id lists.\n- Every question in the quiz appears in `answers`, in the quiz's own order,\n  including questions the student skipped. A skipped one has `is_answered`\n  set to false, an empty selection, and `is_correct` false.\n- `clinical_image`, `dermoscopic_image`, `history`, `diagnosis` and\n  `management` are always present, and are `null` unless the format is\n  `clinical_case`.\n\n## Types to watch, for a statically typed client\n\n- **`percentage` is a number, not always a decimal.** JSON encoding drops a\n  trailing `.0`, so a whole value serializes as `0` or `100` while a\n  fractional one serializes as `33.3`. Read it as a general number and\n  convert (in Dart, `(json['percentage'] as num).toDouble()`), or a whole\n  percentage will fail a strict decimal cast.\n- **`is_correct` has three states**: `true`, `false`, or `null` for a poll\n  question, which is recorded but never graded. It is nullable, not a plain\n  boolean.\n- **`response` and `correct_pairs` are objects** (`{\"p1\": \"p1\"}`) for\n  `match_following` and empty lists `[]` for every other type, so their type\n  depends on `type`.\n- Everything else holds to the contract in the introduction: lists are always\n  lists, counts are always integers.",
            "item": [
                {
                    "name": "List Quizzes",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/quizzes",
                            "query": [
                                {
                                    "key": "page",
                                    "value": "1",
                                    "description": "Page number. Defaults to 1.",
                                    "disabled": false
                                },
                                {
                                    "key": "limit",
                                    "value": "20",
                                    "description": "Quizzes per page, capped at 100. Defaults to 20.",
                                    "disabled": false
                                }
                            ],
                            "raw": "{{baseUrl}}\/api\/v1\/quizzes?page=1&limit=20"
                        },
                        "method": "GET",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": null,
                        "description": "Published quizzes, newest first. `has_submitted` tells the app whether\nthis user has already taken each one - a quiz where it is `true` cannot\nbe submitted again, only its result read.\n\n`format` is here as well as on the detail endpoint, so the list can be\nrendered (and the right result parser chosen) without fetching each quiz."
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"success\": true,\n  \"message\": \"Quizzes retrieved successfully.\",\n  \"data\": [\n    {\n      \"id\": 1,\n      \"title\": \"Dermoscopy Fundamentals\",\n      \"description\": \"Covers every question type.\",\n      \"format\": \"standard\",\n      \"total_questions\": 5,\n      \"has_submitted\": false,\n      \"created_at\": \"2026-09-04T06:45:07.000000Z\"\n    },\n    {\n      \"id\": 2,\n      \"title\": \"Pigmented Lesion on the Back\",\n      \"description\": \"A 52-year-old man.\",\n      \"format\": \"clinical_case\",\n      \"total_questions\": 1,\n      \"has_submitted\": false,\n      \"created_at\": \"2026-09-04T06:45:09.000000Z\"\n    },\n    {\n      \"id\": 3,\n      \"title\": \"Confidence Survey\",\n      \"description\": \"Anonymous poll.\",\n      \"format\": \"poll\",\n      \"total_questions\": 1,\n      \"has_submitted\": true,\n      \"created_at\": \"2026-09-04T06:45:09.000000Z\"\n    }\n  ],\n  \"pagination\": { \"current_page\": 1, \"per_page\": 20, \"total\": 3, \"last_page\": 1 }\n}",
                            "name": "Success"
                        }
                    ]
                },
                {
                    "name": "Get Quiz",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/quizzes\/:id",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/quizzes\/:id",
                            "variable": [
                                {
                                    "id": "id",
                                    "key": "id",
                                    "value": 1,
                                    "description": "The ID of the quiz."
                                },
                                {
                                    "id": "quiz",
                                    "key": "quiz",
                                    "value": 301,
                                    "description": "The quiz ID."
                                }
                            ]
                        },
                        "method": "GET",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": null,
                        "description": "A published quiz with its questions and options, ready to be answered.\nAnswer each question by sending back the `id` of the chosen option.\n\nThe correct answers are not included. Fetch the result endpoint after\nsubmitting to see which answers were right."
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"success\": true,\n  \"message\": \"Quiz retrieved successfully.\",\n  \"data\": {\n    \"id\": 1,\n    \"title\": \"Dermoscopy Fundamentals\",\n    \"description\": \"Covers every question type.\",\n    \"format\": \"standard\",\n    \"clinical_image\": null,\n    \"dermoscopic_image\": null,\n    \"history\": null,\n    \"total_questions\": 5,\n    \"questions\": [\n      {\n        \"id\": 101,\n        \"question\": \"Which feature most suggests melanoma?\",\n        \"type\": \"single_choice\",\n        \"image\": null,\n        \"options\": [\n          { \"id\": \"o1\", \"text\": \"Blue-white veil\" },\n          { \"id\": \"o2\", \"text\": \"Milia-like cysts\" },\n          { \"id\": \"o3\", \"text\": \"Central white patch\" }\n        ],\n        \"left\": [],\n        \"right\": []\n      },\n      {\n        \"id\": 102,\n        \"question\": \"Which features suggest melanoma? Select all that apply.\",\n        \"type\": \"multiple_choice\",\n        \"image\": \"https:\/\/dots.mhn.services\/storage\/questions\/102\/lesion.jpg\",\n        \"options\": [\n          { \"id\": \"o1\", \"text\": \"Atypical pigment network\" },\n          { \"id\": \"o2\", \"text\": \"Irregular streaks\" },\n          { \"id\": \"o3\", \"text\": \"Comedo-like openings\" }\n        ],\n        \"left\": [],\n        \"right\": []\n      },\n      {\n        \"id\": 103,\n        \"question\": \"Dermoscopy improves melanoma detection versus naked-eye examination.\",\n        \"type\": \"true_false\",\n        \"image\": null,\n        \"options\": [\n          { \"id\": \"o1\", \"text\": \"True\" },\n          { \"id\": \"o2\", \"text\": \"False\" }\n        ],\n        \"left\": [],\n        \"right\": []\n      },\n      {\n        \"id\": 104,\n        \"question\": \"Match each dermoscopic feature to its diagnosis.\",\n        \"type\": \"match_following\",\n        \"image\": null,\n        \"options\": [],\n        \"left\": [\n          { \"id\": \"p1\", \"text\": \"Milia-like cysts\" },\n          { \"id\": \"p2\", \"text\": \"Blue-white veil\" },\n          { \"id\": \"p3\", \"text\": \"Central white patch\" }\n        ],\n        \"right\": [\n          { \"id\": \"p2\", \"text\": \"Melanoma\" },\n          { \"id\": \"p3\", \"text\": \"Dermatofibroma\" },\n          { \"id\": \"p1\", \"text\": \"Seborrheic keratosis\" }\n        ]\n      },\n      {\n        \"id\": 105,\n        \"question\": \"How confident are you reading dermoscopy?\",\n        \"type\": \"poll\",\n        \"image\": null,\n        \"options\": [\n          { \"id\": \"o1\", \"text\": \"Not confident\" },\n          { \"id\": \"o2\", \"text\": \"Somewhat\" },\n          { \"id\": \"o3\", \"text\": \"Very confident\" }\n        ],\n        \"left\": [],\n        \"right\": []\n      }\n    ]\n  }\n}",
                            "name": "standard quiz - one of every question type"
                        },
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"success\": true,\n  \"message\": \"Quiz retrieved successfully.\",\n  \"data\": {\n    \"id\": 2,\n    \"title\": \"Pigmented Lesion on the Back\",\n    \"description\": \"A 52-year-old man.\",\n    \"format\": \"clinical_case\",\n    \"clinical_image\": \"https:\/\/dots.mhn.services\/storage\/quizzes\/2\/clinical.jpg\",\n    \"dermoscopic_image\": \"https:\/\/dots.mhn.services\/storage\/quizzes\/2\/dermoscopic.jpg\",\n    \"history\": \"52-year-old man, enlarging pigmented lesion on the back over 6 months.\",\n    \"total_questions\": 1,\n    \"questions\": [\n      {\n        \"id\": 201,\n        \"question\": \"What is the most likely diagnosis?\",\n        \"type\": \"single_choice\",\n        \"image\": null,\n        \"options\": [\n          { \"id\": \"o1\", \"text\": \"Melanoma\" },\n          { \"id\": \"o2\", \"text\": \"Seborrheic keratosis\" }\n        ],\n        \"left\": [],\n        \"right\": []\n      }\n    ]\n  }\n}",
                            "name": "clinical_case quiz - stem shown, diagnosis withheld"
                        },
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"success\": true,\n  \"message\": \"Quiz retrieved successfully.\",\n  \"data\": {\n    \"id\": 3,\n    \"title\": \"Confidence Survey\",\n    \"description\": \"Anonymous poll.\",\n    \"format\": \"poll\",\n    \"clinical_image\": null,\n    \"dermoscopic_image\": null,\n    \"history\": null,\n    \"total_questions\": 1,\n    \"questions\": [\n      {\n        \"id\": 301,\n        \"question\": \"How confident are you reading dermoscopy?\",\n        \"type\": \"poll\",\n        \"image\": null,\n        \"options\": [\n          { \"id\": \"o1\", \"text\": \"Not confident\" },\n          { \"id\": \"o2\", \"text\": \"Somewhat\" },\n          { \"id\": \"o3\", \"text\": \"Very confident\" }\n        ],\n        \"left\": [],\n        \"right\": []\n      }\n    ]\n  }\n}",
                            "name": "poll quiz"
                        },
                        {
                            "header": [],
                            "code": 404,
                            "body": "{\n  \"success\": false,\n  \"message\": \"Quiz not found.\"\n}",
                            "name": "Not published"
                        }
                    ]
                },
                {
                    "name": "Submit Quiz",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/quizzes\/:quiz_id\/submit",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/quizzes\/:quiz_id\/submit",
                            "variable": [
                                {
                                    "id": "quiz_id",
                                    "key": "quiz_id",
                                    "value": 1,
                                    "description": "The ID of the quiz."
                                },
                                {
                                    "id": "quiz",
                                    "key": "quiz",
                                    "value": 1,
                                    "description": "The quiz ID."
                                }
                            ]
                        },
                        "method": "POST",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": {
                            "mode": "raw",
                            "raw": "{\"answers\":[{\"question_id\":900,\"option_id\":\"o1\",\"response\":{\"p1\":\"p1\",\"p2\":\"p2\"}}]}"
                        },
                        "description": "Grades and records this user's single attempt. The response carries the\nscore and the full review of the attempt: every question in the quiz, in\nthe quiz's own order, with every option it offered, each option flagged\nas the correct one (`is_correct`) and as the student's own pick\n(`is_selected`), plus the explanation and reference.\n\nA question left out of `answers` counts as unanswered and scores zero.\n**One attempt per quiz** - submitting again returns `409`.\n\nHow to answer each question type, using the ids from the quiz detail\nresponse:\n\n| type | Send |\n|---|---|\n| `single_choice`, `true_false`, `poll` | `option_ids: [\"o1\"]` (or `option_id: \"o1\"`) |\n| `multiple_choice` | `option_ids: [\"o1\", \"o2\"]` - every correct option, no extras |\n| `match_following` | `response: {\"p1\": \"p1\", \"p2\": \"p2\"}` - one entry per pair, left id to right id |\n\nExample request body for a quiz holding one of every type:\n\n```json\n{\n  \"answers\": [\n    { \"question_id\": 101, \"option_ids\": [\"o1\"] },\n    { \"question_id\": 102, \"option_ids\": [\"o1\", \"o2\"] },\n    { \"question_id\": 103, \"option_ids\": [\"o1\"] },\n    { \"question_id\": 104, \"response\": { \"p1\": \"p1\", \"p2\": \"p2\", \"p3\": \"p3\" } },\n    { \"question_id\": 105, \"option_ids\": [\"o3\"] }\n  ]\n}\n```\n\nThe response shape depends on the quiz's `format`: a `standard` or\n`clinical_case` quiz returns a personal score, a `poll` returns aggregate\ntallies instead. Both scenarios are shown below."
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 201,
                            "body": "{\n  \"success\": true,\n  \"message\": \"Quiz submitted successfully.\",\n  \"data\": {\n    \"quiz_id\": 1,\n    \"quiz_title\": \"Dermoscopy Fundamentals\",\n    \"format\": \"standard\",\n    \"score\": 3,\n    \"total_questions\": 5,\n    \"percentage\": 60,\n    \"correct_count\": 3,\n    \"incorrect_count\": 1,\n    \"unanswered_count\": 0,\n    \"submitted_at\": \"2026-09-04T06:45:09.000000Z\",\n    \"diagnosis\": null,\n    \"management\": null,\n    \"answers\": [\n      {\n        \"question_id\": 101,\n        \"question\": \"Which feature most suggests melanoma?\",\n        \"type\": \"single_choice\",\n        \"image\": null,\n        \"is_correct\": true,\n        \"is_answered\": true,\n        \"explanation\": \"Blue-white veil is the classic finding.\",\n        \"reference\": \"Braun RP, et al.\",\n        \"options\": [\n          { \"id\": \"o1\", \"text\": \"Blue-white veil\", \"is_correct\": true, \"is_selected\": true },\n          { \"id\": \"o2\", \"text\": \"Milia-like cysts\", \"is_correct\": false, \"is_selected\": false },\n          { \"id\": \"o3\", \"text\": \"Central white patch\", \"is_correct\": false, \"is_selected\": false }\n        ],\n        \"selected_option_ids\": [\"o1\"],\n        \"correct_option_ids\": [\"o1\"],\n        \"response\": [],\n        \"correct_pairs\": [],\n        \"left\": [],\n        \"right\": []\n      },\n      {\n        \"question_id\": 102,\n        \"question\": \"Which features suggest melanoma? Select all that apply.\",\n        \"type\": \"multiple_choice\",\n        \"image\": \"https:\/\/dots.mhn.services\/storage\/questions\/102\/lesion.jpg\",\n        \"is_correct\": false,\n        \"is_answered\": true,\n        \"explanation\": \"Comedo-like openings point to seborrheic keratosis.\",\n        \"reference\": null,\n        \"options\": [\n          { \"id\": \"o1\", \"text\": \"Atypical pigment network\", \"is_correct\": true, \"is_selected\": true },\n          { \"id\": \"o2\", \"text\": \"Irregular streaks\", \"is_correct\": true, \"is_selected\": false },\n          { \"id\": \"o3\", \"text\": \"Comedo-like openings\", \"is_correct\": false, \"is_selected\": false }\n        ],\n        \"selected_option_ids\": [\"o1\"],\n        \"correct_option_ids\": [\"o1\", \"o2\"],\n        \"response\": [],\n        \"correct_pairs\": [],\n        \"left\": [],\n        \"right\": []\n      },\n      {\n        \"question_id\": 103,\n        \"question\": \"Dermoscopy improves melanoma detection versus naked-eye examination.\",\n        \"type\": \"true_false\",\n        \"image\": null,\n        \"is_correct\": true,\n        \"is_answered\": true,\n        \"explanation\": \"Supported by meta-analysis.\",\n        \"reference\": null,\n        \"options\": [\n          { \"id\": \"o1\", \"text\": \"True\", \"is_correct\": true, \"is_selected\": true },\n          { \"id\": \"o2\", \"text\": \"False\", \"is_correct\": false, \"is_selected\": false }\n        ],\n        \"selected_option_ids\": [\"o1\"],\n        \"correct_option_ids\": [\"o1\"],\n        \"response\": [],\n        \"correct_pairs\": [],\n        \"left\": [],\n        \"right\": []\n      },\n      {\n        \"question_id\": 104,\n        \"question\": \"Match each dermoscopic feature to its diagnosis.\",\n        \"type\": \"match_following\",\n        \"image\": null,\n        \"is_correct\": true,\n        \"is_answered\": true,\n        \"explanation\": null,\n        \"reference\": null,\n        \"options\": [],\n        \"selected_option_ids\": [],\n        \"correct_option_ids\": [],\n        \"response\": { \"p1\": \"p1\", \"p2\": \"p2\", \"p3\": \"p3\" },\n        \"correct_pairs\": { \"p1\": \"p1\", \"p2\": \"p2\", \"p3\": \"p3\" },\n        \"left\": [\n          { \"id\": \"p1\", \"text\": \"Milia-like cysts\" },\n          { \"id\": \"p2\", \"text\": \"Blue-white veil\" },\n          { \"id\": \"p3\", \"text\": \"Central white patch\" }\n        ],\n        \"right\": [\n          { \"id\": \"p1\", \"text\": \"Seborrheic keratosis\" },\n          { \"id\": \"p2\", \"text\": \"Melanoma\" },\n          { \"id\": \"p3\", \"text\": \"Dermatofibroma\" }\n        ]\n      },\n      {\n        \"question_id\": 105,\n        \"question\": \"How confident are you reading dermoscopy?\",\n        \"type\": \"poll\",\n        \"image\": null,\n        \"is_correct\": null,\n        \"is_answered\": true,\n        \"explanation\": null,\n        \"reference\": null,\n        \"options\": [\n          { \"id\": \"o1\", \"text\": \"Not confident\", \"is_correct\": false, \"is_selected\": false },\n          { \"id\": \"o2\", \"text\": \"Somewhat\", \"is_correct\": false, \"is_selected\": false },\n          { \"id\": \"o3\", \"text\": \"Very confident\", \"is_correct\": false, \"is_selected\": true }\n        ],\n        \"selected_option_ids\": [\"o3\"],\n        \"correct_option_ids\": [],\n        \"response\": [],\n        \"correct_pairs\": [],\n        \"left\": [],\n        \"right\": []\n      }\n    ]\n  }\n}",
                            "name": "standard quiz - graded, with the full review"
                        },
                        {
                            "header": [],
                            "code": 201,
                            "body": "{\n  \"success\": true,\n  \"message\": \"Quiz submitted successfully.\",\n  \"data\": {\n    \"quiz_id\": 2,\n    \"quiz_title\": \"Pigmented Lesion on the Back\",\n    \"format\": \"clinical_case\",\n    \"score\": 1,\n    \"total_questions\": 1,\n    \"percentage\": 100,\n    \"correct_count\": 1,\n    \"incorrect_count\": 0,\n    \"unanswered_count\": 0,\n    \"submitted_at\": \"2026-09-04T06:45:09.000000Z\",\n    \"diagnosis\": \"Superficial spreading melanoma, Breslow depth 0.8mm.\",\n    \"management\": \"Wide local excision with 1cm margins; sentinel node discussion.\",\n    \"answers\": [\n      {\n        \"question_id\": 201,\n        \"question\": \"What is the most likely diagnosis?\",\n        \"type\": \"single_choice\",\n        \"image\": null,\n        \"is_correct\": true,\n        \"is_answered\": true,\n        \"explanation\": \"The asymmetric network and blue-white veil point to melanoma.\",\n        \"reference\": null,\n        \"options\": [\n          { \"id\": \"o1\", \"text\": \"Melanoma\", \"is_correct\": true, \"is_selected\": true },\n          { \"id\": \"o2\", \"text\": \"Seborrheic keratosis\", \"is_correct\": false, \"is_selected\": false }\n        ],\n        \"selected_option_ids\": [\"o1\"],\n        \"correct_option_ids\": [\"o1\"],\n        \"response\": [],\n        \"correct_pairs\": [],\n        \"left\": [],\n        \"right\": []\n      }\n    ]\n  }\n}",
                            "name": "clinical_case quiz - diagnosis and management revealed"
                        },
                        {
                            "header": [],
                            "code": 201,
                            "body": "{\n  \"success\": true,\n  \"message\": \"Poll submitted successfully.\",\n  \"data\": {\n    \"quiz_id\": 3,\n    \"quiz_title\": \"Confidence Survey\",\n    \"total_responses\": 3,\n    \"questions\": [\n      {\n        \"question_id\": 301,\n        \"question\": \"How confident are you reading dermoscopy?\",\n        \"total_responses\": 3,\n        \"options\": [\n          { \"id\": \"o1\", \"text\": \"Not confident\", \"votes\": 1, \"percentage\": 33.3 },\n          { \"id\": \"o2\", \"text\": \"Somewhat\", \"votes\": 2, \"percentage\": 66.7 },\n          { \"id\": \"o3\", \"text\": \"Very confident\", \"votes\": 0, \"percentage\": 0 }\n        ]\n      }\n    ]\n  }\n}",
                            "name": "poll quiz - tallies, no score and no answers"
                        },
                        {
                            "header": [],
                            "code": 401,
                            "body": "{\n  \"success\": false,\n  \"message\": \"Unauthenticated.\"\n}",
                            "name": "Missing or expired token"
                        },
                        {
                            "header": [],
                            "code": 404,
                            "body": "{\n  \"success\": false,\n  \"message\": \"Quiz not found.\"\n}",
                            "name": "Quiz is a draft, or no quiz with that id"
                        },
                        {
                            "header": [],
                            "code": 409,
                            "body": "{\n  \"success\": false,\n  \"message\": \"You have already submitted this quiz.\"\n}",
                            "name": "Already taken - one attempt per quiz"
                        },
                        {
                            "header": [],
                            "code": 422,
                            "body": "{\n  \"success\": false,\n  \"message\": \"The answers field is required.\",\n  \"errors\": {\n    \"answers\": [\"The answers field is required.\"]\n  }\n}",
                            "name": "answers missing from the request body"
                        }
                    ]
                },
                {
                    "name": "Get Quiz Result",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/quizzes\/:quiz_id\/result",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/quizzes\/:quiz_id\/result",
                            "variable": [
                                {
                                    "id": "quiz_id",
                                    "key": "quiz_id",
                                    "value": 1,
                                    "description": "The ID of the quiz."
                                },
                                {
                                    "id": "quiz",
                                    "key": "quiz",
                                    "value": 1,
                                    "description": "The quiz ID."
                                }
                            ]
                        },
                        "method": "GET",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": null,
                        "description": "This user's own result for a quiz they have already taken: the score\nplus the full review of the attempt - every question, every option,\nwhich option was correct, and which one the student picked.\n\nA question the student skipped is still listed, with `is_answered` set\nto false and an empty selection, so the review always covers the whole\nquiz.\n\nReturns exactly the same payload the submit endpoint returned, so a\nclient can reuse one parser for both. As there, the shape follows the\nquiz's `format`: a score with `answers` for `standard` and\n`clinical_case`, aggregate tallies for `poll`.\n\nAnother user's result is never visible here; this is only ever the\nauthenticated user's own attempt."
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"success\": true,\n  \"message\": \"Quiz result retrieved successfully.\",\n  \"data\": {\n    \"quiz_id\": 1,\n    \"quiz_title\": \"Dermoscopy Fundamentals\",\n    \"format\": \"standard\",\n    \"score\": 3,\n    \"total_questions\": 5,\n    \"percentage\": 60,\n    \"correct_count\": 3,\n    \"incorrect_count\": 2,\n    \"unanswered_count\": 1,\n    \"submitted_at\": \"2026-09-04T06:45:09.000000Z\",\n    \"diagnosis\": null,\n    \"management\": null,\n    \"answers\": [\n      {\n        \"question_id\": 101,\n        \"question\": \"Which feature most suggests melanoma?\",\n        \"type\": \"single_choice\",\n        \"image\": null,\n        \"is_correct\": true,\n        \"is_answered\": true,\n        \"explanation\": \"Blue-white veil is the classic finding.\",\n        \"reference\": \"Braun RP, et al.\",\n        \"options\": [\n          { \"id\": \"o1\", \"text\": \"Blue-white veil\", \"is_correct\": true, \"is_selected\": true },\n          { \"id\": \"o2\", \"text\": \"Milia-like cysts\", \"is_correct\": false, \"is_selected\": false },\n          { \"id\": \"o3\", \"text\": \"Central white patch\", \"is_correct\": false, \"is_selected\": false }\n        ],\n        \"selected_option_ids\": [\"o1\"],\n        \"correct_option_ids\": [\"o1\"],\n        \"response\": [],\n        \"correct_pairs\": [],\n        \"left\": [],\n        \"right\": []\n      },\n      {\n        \"question_id\": 106,\n        \"question\": \"Which vessel pattern suggests basal cell carcinoma?\",\n        \"type\": \"single_choice\",\n        \"image\": null,\n        \"is_correct\": false,\n        \"is_answered\": false,\n        \"explanation\": \"Arborizing vessels are the classic finding.\",\n        \"reference\": null,\n        \"options\": [\n          { \"id\": \"o1\", \"text\": \"Arborizing\", \"is_correct\": true, \"is_selected\": false },\n          { \"id\": \"o2\", \"text\": \"Dotted\", \"is_correct\": false, \"is_selected\": false }\n        ],\n        \"selected_option_ids\": [],\n        \"correct_option_ids\": [\"o1\"],\n        \"response\": [],\n        \"correct_pairs\": [],\n        \"left\": [],\n        \"right\": []\n      }\n    ]\n  }\n}",
                            "name": "standard or clinical_case quiz"
                        },
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"success\": true,\n  \"message\": \"Poll results retrieved successfully.\",\n  \"data\": {\n    \"quiz_id\": 3,\n    \"quiz_title\": \"Confidence Survey\",\n    \"total_responses\": 3,\n    \"questions\": [\n      {\n        \"question_id\": 301,\n        \"question\": \"How confident are you reading dermoscopy?\",\n        \"total_responses\": 3,\n        \"options\": [\n          { \"id\": \"o1\", \"text\": \"Not confident\", \"votes\": 1, \"percentage\": 33.3 },\n          { \"id\": \"o2\", \"text\": \"Somewhat\", \"votes\": 2, \"percentage\": 66.7 },\n          { \"id\": \"o3\", \"text\": \"Very confident\", \"votes\": 0, \"percentage\": 0 }\n        ]\n      }\n    ]\n  }\n}",
                            "name": "poll quiz"
                        },
                        {
                            "header": [],
                            "code": 401,
                            "body": "{\n  \"success\": false,\n  \"message\": \"Unauthenticated.\"\n}",
                            "name": "Missing or expired token"
                        },
                        {
                            "header": [],
                            "code": 404,
                            "body": "{\n  \"success\": false,\n  \"message\": \"You have not submitted this quiz yet.\"\n}",
                            "name": "Not taken yet"
                        },
                        {
                            "header": [],
                            "code": 404,
                            "body": "{\n  \"success\": false,\n  \"message\": \"Resource not found.\"\n}",
                            "name": "No quiz with that id"
                        }
                    ]
                }
            ]
        },
        {
            "name": "Learning",
            "description": "\nLearning articles as the mobile app sees them. All of these require\n`Authorization: Bearer {token}`.\n\nOnly published articles are visible. Each person may like an article once;\nliking again removes the like.",
            "item": [
                {
                    "name": "List Learning Content",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/learning",
                            "query": [
                                {
                                    "key": "page",
                                    "value": "1",
                                    "description": "Page number. Defaults to 1.",
                                    "disabled": false
                                },
                                {
                                    "key": "limit",
                                    "value": "20",
                                    "description": "Items per page, capped at 100. Defaults to 20.",
                                    "disabled": false
                                }
                            ],
                            "raw": "{{baseUrl}}\/api\/v1\/learning?page=1&limit=20"
                        },
                        "method": "GET",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": null,
                        "description": "Published articles, newest first. The full `content` body is omitted here\nto keep the list small; fetch a single article to read it."
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"success\": true,\n  \"message\": \"Learning content retrieved successfully.\",\n  \"data\": [\n    {\n      \"id\": 701,\n      \"image\": \"https:\/\/dots.mhn.services\/storage\/learning\/cover.jpg\",\n      \"title\": \"Introduction to Clinical Diagnosis\",\n      \"description\": \"Basic information about clinical diagnosis.\",\n      \"total_likes\": 125,\n      \"has_liked\": false,\n      \"created_at\": \"2026-08-31T12:30:00.000000Z\"\n    }\n  ],\n  \"pagination\": { \"current_page\": 1, \"per_page\": 20, \"total\": 40, \"last_page\": 2 }\n}",
                            "name": "Success"
                        }
                    ]
                },
                {
                    "name": "Get Learning Content",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/learning\/:id",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/learning\/:id",
                            "variable": [
                                {
                                    "id": "id",
                                    "key": "id",
                                    "value": 1,
                                    "description": "The ID of the learning."
                                },
                                {
                                    "id": "learning",
                                    "key": "learning",
                                    "value": 701,
                                    "description": "The learning content ID."
                                }
                            ]
                        },
                        "method": "GET",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": null,
                        "description": "A single published article, including its full `content` body."
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 404,
                            "body": "{\n  \"success\": false,\n  \"message\": \"Learning content not found.\"\n}",
                            "name": "Not published"
                        }
                    ]
                },
                {
                    "name": "Like Learning Content",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/learning\/:learning_id\/like",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/learning\/:learning_id\/like",
                            "variable": [
                                {
                                    "id": "learning_id",
                                    "key": "learning_id",
                                    "value": 1,
                                    "description": "The ID of the learning."
                                },
                                {
                                    "id": "learning",
                                    "key": "learning",
                                    "value": 701,
                                    "description": "The learning content ID."
                                }
                            ]
                        },
                        "method": "POST",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": null,
                        "description": "Likes an article, or removes this user's existing like. One like per\nperson, so calling this twice leaves the article unliked."
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"success\": true,\n  \"message\": \"Learning content liked.\",\n  \"data\": { \"learning_id\": 701, \"has_liked\": true, \"total_likes\": 126 }\n}",
                            "name": "Liked"
                        },
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"success\": true,\n  \"message\": \"Like removed.\",\n  \"data\": { \"learning_id\": 701, \"has_liked\": false, \"total_likes\": 125 }\n}",
                            "name": "Like removed"
                        }
                    ]
                }
            ]
        },
        {
            "name": "Admin - Authentication",
            "description": "\nAdmin accounts are created directly in the database; there is no sign-up\nendpoint. The token returned here authorizes every other `\/api\/v1\/admin\/*`\nendpoint via `Authorization: Bearer {token}`.",
            "item": [
                {
                    "name": "Admin Login",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/admin\/login",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/admin\/login"
                        },
                        "method": "POST",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": {
                            "mode": "raw",
                            "raw": "{\"username\":\"admin\",\"password\":\"admin_password\"}"
                        },
                        "description": "Authenticates an admin by username and password and issues a Sanctum\ntoken. Deactivated admins are rejected with the same generic message as\nbad credentials, so the endpoint does not disclose which accounts exist.",
                        "auth": {
                            "type": "noauth"
                        }
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"success\": true,\n  \"message\": \"Admin login successful.\",\n  \"token\": \"12|To5i3l8yWR3Mb3voEv0mX0W4NK4iLQCxsmuARXfDbe3d9eab\",\n  \"admin\": { \"id\": 1, \"username\": \"admin\", \"name\": \"Admin\" }\n}",
                            "name": "Success"
                        },
                        {
                            "header": [],
                            "code": 401,
                            "body": "{\n  \"success\": false,\n  \"message\": \"Invalid username or password.\"\n}",
                            "name": "Wrong username or password"
                        }
                    ]
                }
            ]
        },
        {
            "name": "Admin - Dashboard",
            "description": "",
            "item": [
                {
                    "name": "Dashboard Analytics",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/admin\/dashboard",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/admin\/dashboard"
                        },
                        "method": "GET",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": null,
                        "description": "Platform totals, counted from live database records each time.\n\n`total_users` counts every account, admins included; filter the users\nlist by role to break that down. `total_published` counts published\nquizzes only, while `total_submissions` counts attempts across all\nquizzes. `total_content` counts learning articles in both states."
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"success\": true,\n  \"message\": \"Dashboard data retrieved successfully.\",\n  \"data\": {\n    \"users\": { \"total_users\": 1250 },\n    \"cases\": { \"total_cases\": 450, \"pending\": 120, \"approved\": 280, \"rejected\": 50 },\n    \"quizzes\": { \"total_published\": 25, \"total_submissions\": 1840 },\n    \"learning\": { \"total_content\": 40 }\n  }\n}",
                            "name": "Success"
                        }
                    ]
                }
            ]
        },
        {
            "name": "Admin - Users",
            "description": "\nEvery registered user, and the switch that activates or deactivates an\naccount. Requires an admin bearer token. Password hashes are never returned.",
            "item": [
                {
                    "name": "Users List",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/admin\/users",
                            "query": [
                                {
                                    "key": "page",
                                    "value": "1",
                                    "description": "Page number. Defaults to 1.",
                                    "disabled": false
                                },
                                {
                                    "key": "limit",
                                    "value": "20",
                                    "description": "Users per page, capped at 100. Defaults to 20.",
                                    "disabled": false
                                },
                                {
                                    "key": "search",
                                    "value": "ahmed",
                                    "description": "Matches name, email, phone, or PMDC number.",
                                    "disabled": false
                                },
                                {
                                    "key": "role",
                                    "value": "student",
                                    "description": "Filter by role, e.g. `student`, `doctor`, `admin`.",
                                    "disabled": false
                                },
                                {
                                    "key": "status",
                                    "value": "active",
                                    "description": "Filter by `active` or `inactive`.",
                                    "disabled": false
                                }
                            ],
                            "raw": "{{baseUrl}}\/api\/v1\/admin\/users?page=1&limit=20&search=ahmed&role=student&status=active"
                        },
                        "method": "GET",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": null,
                        "description": "Paginated list of registered users with the details captured at\nregistration. Optional filters narrow the list; omit them all to get\neveryone, newest first."
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"success\": true,\n  \"message\": \"Users retrieved successfully.\",\n  \"data\": [\n    {\n      \"id\": 101,\n      \"profile_image\": \"https:\/\/dots.mhn.services\/storage\/avatars\/101.jpg\",\n      \"name\": \"Dr. Ahmed Khan\",\n      \"designation\": \"Consultant\",\n      \"bio\": \"Dermatologist with a special interest in dermoscopy.\",\n      \"email\": \"ahmed@example.com\",\n      \"phone\": \"+92XXXXXXXXXX\",\n      \"province\": \"Punjab\",\n      \"city\": \"Lahore\",\n      \"pmdc_number\": \"PMDC12345\",\n      \"fellowship_number\": \"FEL12345\",\n      \"institutional_number\": \"INS12345\",\n      \"role\": \"student\",\n      \"status\": \"active\",\n      \"created_at\": \"2026-08-31T10:30:00.000000Z\"\n    }\n  ],\n  \"pagination\": { \"current_page\": 1, \"per_page\": 20, \"total\": 1250, \"last_page\": 63 }\n}",
                            "name": "Success"
                        }
                    ]
                },
                {
                    "name": "Update User Status",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/admin\/users\/status",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/admin\/users\/status"
                        },
                        "method": "POST",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": {
                            "mode": "raw",
                            "raw": "{\"user_id\":101,\"status\":\"inactive\"}"
                        },
                        "description": "Activates or deactivates a user without deleting the account.\nDeactivating revokes the user's access tokens immediately, so the app\nstops working for them until they are reactivated.\n\nAn admin cannot deactivate their own account."
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"success\": true,\n  \"message\": \"User status updated successfully.\",\n  \"data\": { \"user_id\": 101, \"status\": \"inactive\" }\n}",
                            "name": "Success"
                        },
                        {
                            "header": [],
                            "code": 422,
                            "body": "{\n  \"success\": false,\n  \"message\": \"You cannot deactivate your own account.\",\n  \"errors\": { \"user_id\": [\"You cannot deactivate your own account.\"] }\n}",
                            "name": "Deactivating your own account"
                        }
                    ]
                }
            ]
        },
        {
            "name": "Admin - Cases",
            "description": "\nReview queue for submitted cases. Requires an admin bearer token.\n\nA case carries clinical photos, dermoscopic photos, or both. `type=clinical`\nand `type=dermoscopic` match any case holding at least one photo of that\nkind, so a case holding both appears under either filter and reports its own\n`case_type` as `all`. `microscopic` is accepted as an alias for\n`dermoscopic`.\n\nEvery case here also carries its `code` (the five-character handle the app\nshows and searches on) and its pin state (`is_pinned`, `pinned_at`). The\nreview queue itself is always newest first - pinning changes the order of\nthe **app feed**, not of this list.",
            "item": [
                {
                    "name": "Cases List",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/admin\/cases",
                            "query": [
                                {
                                    "key": "type",
                                    "value": "dermoscopic",
                                    "description": "`all`, `clinical`, or `dermoscopic`.",
                                    "disabled": false
                                },
                                {
                                    "key": "status",
                                    "value": "pending",
                                    "description": "`all`, `pending`, `approved`, or `rejected`.",
                                    "disabled": false
                                },
                                {
                                    "key": "search",
                                    "value": "melanoma",
                                    "description": "Matches the case code, diagnosis, body site, or submitter name.",
                                    "disabled": false
                                },
                                {
                                    "key": "page",
                                    "value": "1",
                                    "description": "Page number. Defaults to 1.",
                                    "disabled": false
                                },
                                {
                                    "key": "limit",
                                    "value": "20",
                                    "description": "Cases per page, capped at 100. Defaults to 20.",
                                    "disabled": false
                                }
                            ],
                            "raw": "{{baseUrl}}\/api\/v1\/admin\/cases?type=dermoscopic&status=pending&search=melanoma&page=1&limit=20"
                        },
                        "method": "GET",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": null,
                        "description": "Paginated review queue, newest first, filterable by photo type and\nreview status. Pass `all` or omit a filter to leave it unrestricted.\n\n`search` matches a full case code (letter case ignored), or a fragment\nof the clinical diagnosis, the dermoscopic diagnosis, the body site, or\nthe submitter's name. Unlike the app feed, this searches cases in every\nstatus, so a pending or rejected case is findable by its code here.\n\nCases have no title or description: the app does not collect either, so\nneither is returned. Review a case from its photos and its clinical and\ndermoscopic detail blocks."
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"success\": true,\n  \"message\": \"Cases retrieved successfully.\",\n  \"filters\": { \"type\": \"dermoscopic\", \"status\": \"pending\" },\n  \"data\": [\n    {\n      \"id\": 501,\n      \"code\": \"K7F2Q\",\n      \"user_id\": 101,\n      \"user_name\": \"Dr. Ahmed Khan\",\n      \"user_profile_image\": \"https:\/\/dots.mhn.services\/storage\/avatars\/101.jpg\",\n      \"case_type\": \"dermoscopic\",\n      \"status\": \"pending\",\n      \"rejection_reason\": null,\n      \"reviewed_at\": null,\n      \"is_pinned\": true,\n      \"pinned_at\": \"2026-09-05T09:15:00.000000Z\",\n      \"images\": [\"https:\/\/dots.mhn.services\/storage\/cases\/501\/dermoscopic\/a.jpg\"],\n      \"pdf\": {\n        \"url\": \"https:\/\/dots.mhn.services\/storage\/cases\/501\/pdf\/histopath.pdf\",\n        \"name\": \"histopath.pdf\",\n        \"size\": 148213\n      },\n      \"patient\": { \"age\": 52, \"gender\": \"male\", \"fitzpatrick_skin_type\": \"III\", \"body_site\": \"trunk\" },\n      \"clinical\": { \"diagnosis\": null, \"histopathology\": null, \"images\": [] },\n      \"dermoscopic\": {\n        \"lesion_type\": \"melanocytic\",\n        \"features\": [\"pigment network\"],\n        \"vascular_pattern\": [\"dotted\"],\n        \"colours_present\": [\"brown\"],\n        \"scale\": \"fine\",\n        \"pattern\": \"reticular\",\n        \"image_metadata\": [\"polarized\"],\n        \"diagnosis\": \"melanoma\",\n        \"histopathology\": null,\n        \"images\": [{ \"id\": 9, \"url\": \"https:\/\/dots.mhn.services\/storage\/cases\/501\/dermoscopic\/a.jpg\" }]\n      },\n      \"comments_count\": 2,\n      \"created_at\": \"2026-08-31T10:30:00.000000Z\"\n    }\n  ],\n  \"pagination\": { \"current_page\": 1, \"per_page\": 20, \"total\": 120, \"last_page\": 6 }\n}",
                            "name": "Success"
                        }
                    ]
                },
                {
                    "name": "Approve or Reject Case",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/admin\/cases\/status",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/admin\/cases\/status"
                        },
                        "method": "POST",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": {
                            "mode": "raw",
                            "raw": "{\"case_id\":501,\"status\":\"approved\",\"reason\":\"Insufficient case information.\"}"
                        },
                        "description": "Sets a case to `approved` or `rejected`, recording who reviewed it and\nwhen. A rejection may carry a `reason`, which the submitter sees on their\nown case so they can correct it and resubmit. Approving clears any\nprevious rejection reason.\n\nOnly approved cases appear in the app's shared feed."
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"success\": true,\n  \"message\": \"Case approved successfully.\",\n  \"data\": { \"case_id\": 501, \"status\": \"approved\" }\n}",
                            "name": "Approved"
                        },
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"success\": true,\n  \"message\": \"Case rejected successfully.\",\n  \"data\": { \"case_id\": 501, \"status\": \"rejected\" }\n}",
                            "name": "Rejected"
                        }
                    ]
                },
                {
                    "name": "Pin or Unpin Case",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/admin\/cases\/pin",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/admin\/cases\/pin"
                        },
                        "method": "POST",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": {
                            "mode": "raw",
                            "raw": "{\"case_id\":501,\"pinned\":true}"
                        },
                        "description": "Pins a case to the top of the app's shared feed, or removes the pin.\nPinned cases come back first from `GET \/api\/v1\/cases`, most recently\npinned first, each flagged with `is_pinned` and carrying the admin who\npinned it, so the app can label it as pinned by an admin.\n\nAny number of cases can be pinned at once. Pinning does not review a\ncase: only approved cases appear in the feed, so pinning one still\nawaiting review has no visible effect until it is approved.\n\n### Request\n\n| Field | Type | Required | Notes |\n|---|---|---|---|\n| `case_id` | integer | yes | Must be an existing case id, in any review status |\n| `pinned` | boolean | yes | `true` pins, `false` unpins. `1`\/`0` and `\"true\"`\/`\"false\"` are accepted |\n\n### Behaviour to test\n\n- Pinning is idempotent-ish: pinning an already-pinned case succeeds and\n  **refreshes** `pinned_at`, which moves it ahead of other pinned cases\n  in the feed. Unpinning a case that is not pinned also succeeds and\n  simply leaves it unpinned.\n- Unpinning clears `pinned_at` and `pinned_by` together. A case is\n  pinned if and only if `pinned_at` is set.\n- Any number of cases can be pinned at the same time.\n- The case's review `status` is untouched. Pinning a `pending` case\n  stores the pin but the case still will not show in the app feed until\n  it is approved.\n- The pin records the admin from the bearer token, and that admin comes\n  back as `pinned_by` here and on every app-facing case payload."
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"success\": true,\n  \"message\": \"Case pinned successfully.\",\n  \"data\": {\n    \"case_id\": 501,\n    \"is_pinned\": true,\n    \"pinned_at\": \"2026-09-05T09:15:00.000000Z\",\n    \"pinned_by\": { \"id\": 1, \"full_name\": \"Dots Admin\", \"avatar_url\": null }\n  }\n}",
                            "name": "Pinned"
                        },
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"success\": true,\n  \"message\": \"Case unpinned successfully.\",\n  \"data\": {\n    \"case_id\": 501,\n    \"is_pinned\": false,\n    \"pinned_at\": null,\n    \"pinned_by\": null\n  }\n}",
                            "name": "Unpinned"
                        },
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"success\": true,\n  \"message\": \"Case pinned successfully.\",\n  \"data\": {\n    \"case_id\": 501,\n    \"is_pinned\": true,\n    \"pinned_at\": \"2026-09-05T14:02:44.000000Z\",\n    \"pinned_by\": { \"id\": 1, \"full_name\": \"Dots Admin\", \"avatar_url\": null }\n  }\n}",
                            "name": "Pinned again - pinned_at is refreshed"
                        },
                        {
                            "header": [],
                            "code": 401,
                            "body": "{\n  \"success\": false,\n  \"message\": \"Unauthenticated.\"\n}",
                            "name": "Missing or expired token"
                        },
                        {
                            "header": [],
                            "code": 403,
                            "body": "{\n  \"success\": false,\n  \"message\": \"This action requires an admin account.\"\n}",
                            "name": "Token belongs to a non-admin account"
                        },
                        {
                            "header": [],
                            "code": 422,
                            "body": "{\n  \"success\": false,\n  \"message\": \"The selected case id is invalid.\",\n  \"errors\": {\n    \"case_id\": [\"The selected case id is invalid.\"]\n  }\n}",
                            "name": "No case with that id"
                        },
                        {
                            "header": [],
                            "code": 422,
                            "body": "{\n  \"success\": false,\n  \"message\": \"The pinned field is required.\",\n  \"errors\": {\n    \"pinned\": [\"The pinned field is required.\"]\n  }\n}",
                            "name": "pinned left out"
                        },
                        {
                            "header": [],
                            "code": 422,
                            "body": "{\n  \"success\": false,\n  \"message\": \"The pinned field must be true or false.\",\n  \"errors\": {\n    \"pinned\": [\"The pinned field must be true or false.\"]\n  }\n}",
                            "name": "pinned is not a boolean"
                        }
                    ]
                }
            ]
        },
        {
            "name": "Admin - Questions",
            "description": "\nThe reusable Question Bank. Requires an admin bearer token.\n\nA question moves through `draft` -> `pending_review` -> `approved` or\n`rejected`, mirroring the case review workflow. Only `approved` questions\ncan be attached to a quiz (see Admin - Quizzes).",
            "item": [
                {
                    "name": "Question List",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/admin\/questions",
                            "query": [
                                {
                                    "key": "category",
                                    "value": "Dermoscopy",
                                    "description": "Exact match.",
                                    "disabled": false
                                },
                                {
                                    "key": "subcategory",
                                    "value": "Pigmented lesions",
                                    "description": "Exact match.",
                                    "disabled": false
                                },
                                {
                                    "key": "type",
                                    "value": "single_choice",
                                    "description": "`single_choice`, `multiple_choice`, `true_false`, `match_following`, or `poll`.",
                                    "disabled": false
                                },
                                {
                                    "key": "difficulty",
                                    "value": "2",
                                    "description": "1 to 3.",
                                    "disabled": false
                                },
                                {
                                    "key": "status",
                                    "value": "pending_review",
                                    "description": "`all`, `draft`, `pending_review`, `approved`, or `rejected`.",
                                    "disabled": false
                                },
                                {
                                    "key": "tag",
                                    "value": "dermoscopy",
                                    "description": "Matches a single tag.",
                                    "disabled": false
                                },
                                {
                                    "key": "search",
                                    "value": "seborrheic",
                                    "description": "Matches the question text.",
                                    "disabled": false
                                },
                                {
                                    "key": "page",
                                    "value": "1",
                                    "description": "Page number. Defaults to 1.",
                                    "disabled": false
                                },
                                {
                                    "key": "limit",
                                    "value": "20",
                                    "description": "Questions per page, capped at 100. Defaults to 20.",
                                    "disabled": false
                                }
                            ],
                            "raw": "{{baseUrl}}\/api\/v1\/admin\/questions?category=Dermoscopy&subcategory=Pigmented%20lesions&type=single_choice&difficulty=2&status=pending_review&tag=dermoscopy&search=seborrheic&page=1&limit=20"
                        },
                        "method": "GET",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": null,
                        "description": "Paginated, filterable list of bank questions."
                    },
                    "response": []
                },
                {
                    "name": "Create Question",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/admin\/questions",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/admin\/questions"
                        },
                        "method": "POST",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "multipart\/form-data"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": {
                            "mode": "formdata",
                            "formdata": [
                                {
                                    "key": "category",
                                    "value": "Dermoscopy",
                                    "type": "text",
                                    "description": "Organizes the question in the bank."
                                },
                                {
                                    "key": "subcategory",
                                    "value": "Pigmented lesions",
                                    "type": "text",
                                    "description": "Optional, narrower than category."
                                },
                                {
                                    "key": "question",
                                    "value": "Which dermoscopic feature is most suggestive of seborrheic keratosis?",
                                    "type": "text",
                                    "description": "The question text."
                                },
                                {
                                    "key": "type",
                                    "value": "single_choice",
                                    "type": "text",
                                    "description": "`single_choice`, `multiple_choice`, `true_false`, `match_following`, or `poll`."
                                },
                                {
                                    "key": "explanation",
                                    "value": "Milia-like cysts are the classic dermoscopic clue for seborrheic keratosis.",
                                    "type": "text",
                                    "description": "Shown to the student after they answer."
                                },
                                {
                                    "key": "reference",
                                    "value": "Braun RP, et al. Dermoscopy of pigmented skin lesions.",
                                    "type": "text",
                                    "description": "A citation or source, shown alongside the explanation."
                                },
                                {
                                    "key": "tags[0]",
                                    "value": "dermoscopy",
                                    "type": "text",
                                    "description": ""
                                },
                                {
                                    "key": "tags[1]",
                                    "value": "seborrheic-keratosis",
                                    "type": "text",
                                    "description": ""
                                },
                                {
                                    "key": "difficulty",
                                    "value": "2",
                                    "type": "text",
                                    "description": "1 (easy) to 3 (hard). Must be between 1 and 3."
                                },
                                {
                                    "key": "options[0]",
                                    "value": "Blue-white veil",
                                    "type": "text",
                                    "description": ""
                                },
                                {
                                    "key": "options[1]",
                                    "value": "Milia-like cysts",
                                    "type": "text",
                                    "description": ""
                                },
                                {
                                    "key": "options[2]",
                                    "value": "Irregular streaks",
                                    "type": "text",
                                    "description": ""
                                },
                                {
                                    "key": "options[3]",
                                    "value": "Regression",
                                    "type": "text",
                                    "description": ""
                                },
                                {
                                    "key": "correct_answer",
                                    "value": "Milia-like cysts",
                                    "type": "text",
                                    "description": "The option text (or id) that is correct. Ignored for poll."
                                },
                                {
                                    "key": "image",
                                    "src": [],
                                    "type": "file"
                                }
                            ]
                        },
                        "description": "Adds a question to the bank at `status = draft`. Submit it for review\nseparately once it is ready."
                    },
                    "response": []
                },
                {
                    "name": "Review Question",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/admin\/questions\/status",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/admin\/questions\/status"
                        },
                        "method": "POST",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": {
                            "mode": "raw",
                            "raw": "{\"question_id\":901,\"status\":\"approved\",\"reason\":\"Reference is missing.\"}"
                        },
                        "description": "Moves a question to `pending_review`, `approved`, `rejected`, or back\nto `draft`, recording who reviewed it and when. Approving clears any\nprevious rejection reason. Only `approved` questions can be attached\nto a quiz."
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"success\": true,\n  \"message\": \"Question approved successfully.\",\n  \"data\": { \"question_id\": 901, \"status\": \"approved\" }\n}",
                            "name": "Approved"
                        }
                    ]
                },
                {
                    "name": "Approve Selected Questions",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/admin\/questions\/approve-selected",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/admin\/questions\/approve-selected"
                        },
                        "method": "POST",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": {
                            "mode": "raw",
                            "raw": "{\"question_ids\":[901,902]}"
                        },
                        "description": "Bulk-approves every listed question in one call."
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"success\": true,\n  \"message\": \"2 questions approved.\",\n  \"data\": { \"approved\": 2 }\n}",
                            "name": "Success"
                        }
                    ]
                },
                {
                    "name": "Get Question",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/admin\/questions\/:id",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/admin\/questions\/:id",
                            "variable": [
                                {
                                    "id": "id",
                                    "key": "id",
                                    "value": 1,
                                    "description": "The ID of the question."
                                },
                                {
                                    "id": "question",
                                    "key": "question",
                                    "value": 901,
                                    "description": "The question ID."
                                }
                            ]
                        },
                        "method": "GET",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": null,
                        "description": "A single bank question, including its answer key."
                    },
                    "response": []
                },
                {
                    "name": "Update Question",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/admin\/questions\/:id",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/admin\/questions\/:id",
                            "variable": [
                                {
                                    "id": "id",
                                    "key": "id",
                                    "value": 1,
                                    "description": "The ID of the question."
                                },
                                {
                                    "id": "question",
                                    "key": "question",
                                    "value": 901,
                                    "description": "The question ID."
                                }
                            ]
                        },
                        "method": "POST",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "multipart\/form-data"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": {
                            "mode": "formdata",
                            "formdata": [
                                {
                                    "key": "category",
                                    "value": "Dermoscopy",
                                    "type": "text",
                                    "description": "Organizes the question in the bank."
                                },
                                {
                                    "key": "question",
                                    "value": "Which dermoscopic feature is most suggestive of seborrheic keratosis?",
                                    "type": "text",
                                    "description": "The question text."
                                },
                                {
                                    "key": "type",
                                    "value": "single_choice",
                                    "type": "text",
                                    "description": "`single_choice`, `multiple_choice`, `true_false`, `match_following`, or `poll`."
                                },
                                {
                                    "key": "difficulty",
                                    "value": "2",
                                    "type": "text",
                                    "description": "Must be between 1 and 3."
                                },
                                {
                                    "key": "image",
                                    "src": [],
                                    "type": "file"
                                },
                                {
                                    "key": "_method",
                                    "value": "POST",
                                    "type": "text"
                                }
                            ]
                        },
                        "description": "Partially updates a question's content. Does not change its review\nstatus - use the status endpoint for that."
                    },
                    "response": []
                },
                {
                    "name": "Delete Question",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/admin\/questions\/:id",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/admin\/questions\/:id",
                            "variable": [
                                {
                                    "id": "id",
                                    "key": "id",
                                    "value": 1,
                                    "description": "The ID of the question."
                                },
                                {
                                    "id": "question",
                                    "key": "question",
                                    "value": 901,
                                    "description": "The question ID."
                                }
                            ]
                        },
                        "method": "DELETE",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": null,
                        "description": "Permanently deletes a question and detaches it from every quiz it was\nattached to. Quizzes themselves are not deleted."
                    },
                    "response": []
                }
            ]
        },
        {
            "name": "Admin - Quizzes",
            "description": "\nAuthoring and monitoring of quizzes. Requires an admin bearer token.\n\nA quiz is composed of existing, approved Question Bank entries (see\nAdmin - Questions), attached via `question_ids`. `format` is `standard`\n(a plain graded quiz), `poll` (ungraded, aggregate results only), or\n`clinical_case` (a case stem shown before its questions, with diagnosis\nand management revealed to the student after submission).",
            "item": [
                {
                    "name": "Quiz List",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/admin\/quizzes",
                            "query": [
                                {
                                    "key": "status",
                                    "value": "published",
                                    "description": "`all`, `draft`, or `published`.",
                                    "disabled": false
                                },
                                {
                                    "key": "format",
                                    "value": "standard",
                                    "description": "`all`, `standard`, `poll`, or `clinical_case`.",
                                    "disabled": false
                                },
                                {
                                    "key": "search",
                                    "value": "knowledge",
                                    "description": "Matches the quiz title.",
                                    "disabled": false
                                },
                                {
                                    "key": "page",
                                    "value": "1",
                                    "description": "Page number. Defaults to 1.",
                                    "disabled": false
                                },
                                {
                                    "key": "limit",
                                    "value": "20",
                                    "description": "Quizzes per page, capped at 100. Defaults to 20.",
                                    "disabled": false
                                }
                            ],
                            "raw": "{{baseUrl}}\/api\/v1\/admin\/quizzes?status=published&format=standard&search=knowledge&page=1&limit=20"
                        },
                        "method": "GET",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": null,
                        "description": "Paginated list of quizzes with their question and submission counts."
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"success\": true,\n  \"message\": \"Quizzes retrieved successfully.\",\n  \"data\": [\n    {\n      \"id\": 301,\n      \"title\": \"Medical Knowledge Quiz\",\n      \"description\": \"Test your medical knowledge.\",\n      \"format\": \"standard\",\n      \"total_questions\": 20,\n      \"status\": \"published\",\n      \"total_submissions\": 145,\n      \"created_at\": \"2026-08-31T11:00:00.000000Z\"\n    }\n  ],\n  \"pagination\": { \"current_page\": 1, \"per_page\": 20, \"total\": 25, \"last_page\": 2 }\n}",
                            "name": "Success"
                        }
                    ]
                },
                {
                    "name": "Create Quiz",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/admin\/quizzes",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/admin\/quizzes"
                        },
                        "method": "POST",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "multipart\/form-data"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": {
                            "mode": "formdata",
                            "formdata": [
                                {
                                    "key": "title",
                                    "value": "Medical Knowledge Quiz",
                                    "type": "text",
                                    "description": "The quiz title."
                                },
                                {
                                    "key": "description",
                                    "value": "Test your medical knowledge.",
                                    "type": "text",
                                    "description": "Optional summary shown to students."
                                },
                                {
                                    "key": "format",
                                    "value": "standard",
                                    "type": "text",
                                    "description": "`standard`, `poll`, or `clinical_case`. Defaults to `standard`."
                                },
                                {
                                    "key": "status",
                                    "value": "published",
                                    "type": "text",
                                    "description": "`draft` or `published`. Defaults to `draft`."
                                },
                                {
                                    "key": "question_ids[0]",
                                    "value": "16",
                                    "type": "text",
                                    "description": ""
                                },
                                {
                                    "key": "history",
                                    "value": "52-year-old male, 6-month history of an enlarging pigmented lesion.",
                                    "type": "text",
                                    "description": "clinical_case only. Shown to the student before the questions."
                                },
                                {
                                    "key": "diagnosis",
                                    "value": "Superficial spreading melanoma.",
                                    "type": "text",
                                    "description": "clinical_case only. Revealed after submission."
                                },
                                {
                                    "key": "management",
                                    "value": "Urgent excision with 1cm margins and staging workup.",
                                    "type": "text",
                                    "description": "clinical_case only. Revealed after submission."
                                },
                                {
                                    "key": "clinical_image",
                                    "src": [],
                                    "type": "file"
                                },
                                {
                                    "key": "dermoscopic_image",
                                    "src": [],
                                    "type": "file"
                                }
                            ]
                        },
                        "description": "Creates a quiz and attaches the given, already-approved bank questions\nin order."
                    },
                    "response": []
                },
                {
                    "name": "Get Quiz",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/admin\/quizzes\/:id",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/admin\/quizzes\/:id",
                            "variable": [
                                {
                                    "id": "id",
                                    "key": "id",
                                    "value": 1,
                                    "description": "The ID of the quiz."
                                },
                                {
                                    "id": "quiz",
                                    "key": "quiz",
                                    "value": 301,
                                    "description": "The quiz ID."
                                }
                            ]
                        },
                        "method": "GET",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": null,
                        "description": "A single quiz with its attached questions, including the answer key.\nUse this to populate an edit form."
                    },
                    "response": []
                },
                {
                    "name": "Quiz Submissions",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/admin\/quizzes\/:quiz_id\/submissions",
                            "query": [
                                {
                                    "key": "page",
                                    "value": "1",
                                    "description": "Page number. Defaults to 1.",
                                    "disabled": false
                                },
                                {
                                    "key": "limit",
                                    "value": "20",
                                    "description": "Submissions per page, capped at 100. Defaults to 20.",
                                    "disabled": false
                                }
                            ],
                            "raw": "{{baseUrl}}\/api\/v1\/admin\/quizzes\/:quiz_id\/submissions?page=1&limit=20",
                            "variable": [
                                {
                                    "id": "quiz_id",
                                    "key": "quiz_id",
                                    "value": 1,
                                    "description": "The ID of the quiz."
                                },
                                {
                                    "id": "quiz",
                                    "key": "quiz",
                                    "value": 301,
                                    "description": "The quiz ID."
                                }
                            ]
                        },
                        "method": "GET",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": null,
                        "description": "Who has taken a quiz, and what they scored. `score` counts correct\nanswers out of `total_questions`. Not meaningful for a `poll` quiz."
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"success\": true,\n  \"message\": \"Quiz submissions retrieved successfully.\",\n  \"data\": {\n    \"quiz_id\": 301,\n    \"quiz_title\": \"Medical Knowledge Quiz\",\n    \"total_submissions\": 145,\n    \"submissions\": [\n      {\n        \"user_id\": 101,\n        \"user_name\": \"Dr. Ahmed Khan\",\n        \"submitted_at\": \"2026-08-31T12:00:00.000000Z\",\n        \"score\": 18,\n        \"total_questions\": 20\n      }\n    ]\n  },\n  \"pagination\": { \"current_page\": 1, \"per_page\": 20, \"total\": 145, \"last_page\": 8 }\n}",
                            "name": "Success"
                        }
                    ]
                },
                {
                    "name": "Update Quiz",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/admin\/quizzes\/:id",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/admin\/quizzes\/:id",
                            "variable": [
                                {
                                    "id": "id",
                                    "key": "id",
                                    "value": 1,
                                    "description": "The ID of the quiz."
                                },
                                {
                                    "id": "quiz",
                                    "key": "quiz",
                                    "value": 301,
                                    "description": "The quiz ID."
                                }
                            ]
                        },
                        "method": "POST",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "multipart\/form-data"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": {
                            "mode": "formdata",
                            "formdata": [
                                {
                                    "key": "title",
                                    "value": "Medical Knowledge Quiz",
                                    "type": "text",
                                    "description": "The quiz title."
                                },
                                {
                                    "key": "format",
                                    "value": "standard",
                                    "type": "text",
                                    "description": ""
                                },
                                {
                                    "key": "status",
                                    "value": "published",
                                    "type": "text",
                                    "description": "`draft` or `published`."
                                },
                                {
                                    "key": "question_ids[0]",
                                    "value": "16",
                                    "type": "text",
                                    "description": ""
                                },
                                {
                                    "key": "clinical_image",
                                    "src": [],
                                    "type": "file"
                                },
                                {
                                    "key": "dermoscopic_image",
                                    "src": [],
                                    "type": "file"
                                },
                                {
                                    "key": "_method",
                                    "value": "POST",
                                    "type": "text"
                                }
                            ]
                        },
                        "description": "Partially updates a quiz. Omit `question_ids` to change only the\ntitle, description, or status, which is how publishing and\nunpublishing works. Send `question_ids` to replace the attached set."
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"success\": true,\n  \"message\": \"Quiz updated successfully.\",\n  \"data\": { \"id\": 301, \"title\": \"Medical Knowledge Quiz\", \"status\": \"published\" }\n}",
                            "name": "Success"
                        }
                    ]
                },
                {
                    "name": "Delete Quiz",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/admin\/quizzes\/:id",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/admin\/quizzes\/:id",
                            "variable": [
                                {
                                    "id": "id",
                                    "key": "id",
                                    "value": 1,
                                    "description": "The ID of the quiz."
                                },
                                {
                                    "id": "quiz",
                                    "key": "quiz",
                                    "value": 301,
                                    "description": "The quiz ID."
                                }
                            ]
                        },
                        "method": "DELETE",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": null,
                        "description": "Permanently deletes a quiz and every submission recorded against it.\nAttached bank questions are only detached, never deleted. This cannot\nbe undone."
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"success\": true,\n  \"message\": \"Quiz deleted successfully.\"\n}",
                            "name": "Success"
                        }
                    ]
                }
            ]
        },
        {
            "name": "Admin - Learning",
            "description": "\nLearning articles the app shows to users, who can like them. Requires an\nadmin bearer token.\n\nCreate and update take `multipart\/form-data` because of the cover image.",
            "item": [
                {
                    "name": "Learning List",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/admin\/learning",
                            "query": [
                                {
                                    "key": "status",
                                    "value": "published",
                                    "description": "`all`, `draft`, or `published`.",
                                    "disabled": false
                                },
                                {
                                    "key": "search",
                                    "value": "diagnosis",
                                    "description": "Matches the title.",
                                    "disabled": false
                                },
                                {
                                    "key": "page",
                                    "value": "1",
                                    "description": "Page number. Defaults to 1.",
                                    "disabled": false
                                },
                                {
                                    "key": "limit",
                                    "value": "20",
                                    "description": "Items per page, capped at 100. Defaults to 20.",
                                    "disabled": false
                                }
                            ],
                            "raw": "{{baseUrl}}\/api\/v1\/admin\/learning?status=published&search=diagnosis&page=1&limit=20"
                        },
                        "method": "GET",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": null,
                        "description": "Paginated list of learning articles with their like counts."
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"success\": true,\n  \"message\": \"Learning content retrieved successfully.\",\n  \"data\": [\n    {\n      \"id\": 701,\n      \"image\": \"https:\/\/dots.mhn.services\/storage\/learning\/cover.jpg\",\n      \"title\": \"Introduction to Clinical Diagnosis\",\n      \"description\": \"Basic information about clinical diagnosis.\",\n      \"content\": \"Complete learning content here.\",\n      \"status\": \"published\",\n      \"total_likes\": 125,\n      \"created_at\": \"2026-08-31T12:30:00.000000Z\"\n    }\n  ],\n  \"pagination\": { \"current_page\": 1, \"per_page\": 20, \"total\": 40, \"last_page\": 2 }\n}",
                            "name": "Success"
                        }
                    ]
                },
                {
                    "name": "Create Learning Content",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/admin\/learning",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/admin\/learning"
                        },
                        "method": "POST",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "multipart\/form-data"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": {
                            "mode": "formdata",
                            "formdata": [
                                {
                                    "key": "title",
                                    "value": "Introduction to Clinical Diagnosis",
                                    "type": "text",
                                    "description": "The article title."
                                },
                                {
                                    "key": "description",
                                    "value": "Basic information about clinical diagnosis.",
                                    "type": "text",
                                    "description": "Short summary shown in the list."
                                },
                                {
                                    "key": "content",
                                    "value": "Complete learning content here.",
                                    "type": "text",
                                    "description": "The full article body."
                                },
                                {
                                    "key": "status",
                                    "value": "published",
                                    "type": "text",
                                    "description": "`draft` or `published`. Defaults to `draft`."
                                },
                                {
                                    "key": "image",
                                    "src": [],
                                    "type": "file"
                                }
                            ]
                        },
                        "description": "Send as `multipart\/form-data`; the cover image is required."
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 201,
                            "body": "{\n  \"success\": true,\n  \"message\": \"Learning content created successfully.\",\n  \"data\": {\n    \"id\": 701,\n    \"image\": \"https:\/\/dots.mhn.services\/storage\/learning\/cover.jpg\",\n    \"title\": \"Introduction to Clinical Diagnosis\",\n    \"description\": \"Basic information about clinical diagnosis.\",\n    \"content\": \"Complete learning content here.\",\n    \"status\": \"published\",\n    \"total_likes\": 0,\n    \"created_at\": \"2026-08-31T12:30:00.000000Z\"\n  }\n}",
                            "name": "Success"
                        }
                    ]
                },
                {
                    "name": "Get Learning Content",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/admin\/learning\/:id",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/admin\/learning\/:id",
                            "variable": [
                                {
                                    "id": "id",
                                    "key": "id",
                                    "value": 1,
                                    "description": "The ID of the learning."
                                },
                                {
                                    "id": "learning",
                                    "key": "learning",
                                    "value": 701,
                                    "description": "The learning content ID."
                                }
                            ]
                        },
                        "method": "GET",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": null,
                        "description": "A single article, for populating an edit form."
                    },
                    "response": []
                },
                {
                    "name": "Update Learning Content",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/admin\/learning\/:id",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/admin\/learning\/:id",
                            "variable": [
                                {
                                    "id": "id",
                                    "key": "id",
                                    "value": 1,
                                    "description": "The ID of the learning."
                                },
                                {
                                    "id": "learning",
                                    "key": "learning",
                                    "value": 701,
                                    "description": "The learning content ID."
                                }
                            ]
                        },
                        "method": "POST",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "multipart\/form-data"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": {
                            "mode": "formdata",
                            "formdata": [
                                {
                                    "key": "title",
                                    "value": "Updated Learning Title",
                                    "type": "text",
                                    "description": "The article title."
                                },
                                {
                                    "key": "description",
                                    "value": "Updated description",
                                    "type": "text",
                                    "description": "Short summary shown in the list."
                                },
                                {
                                    "key": "content",
                                    "value": "Updated learning content",
                                    "type": "text",
                                    "description": "The full article body."
                                },
                                {
                                    "key": "status",
                                    "value": "published",
                                    "type": "text",
                                    "description": "`draft` or `published`."
                                },
                                {
                                    "key": "image",
                                    "src": [],
                                    "type": "file"
                                },
                                {
                                    "key": "_method",
                                    "value": "POST",
                                    "type": "text"
                                }
                            ]
                        },
                        "description": "Partially updates an article. Send as `multipart\/form-data` when\nreplacing the image; the old file is deleted. Omit `image` to keep it."
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"success\": true,\n  \"message\": \"Learning content updated successfully.\",\n  \"data\": {\n    \"id\": 701,\n    \"title\": \"Updated Learning Title\",\n    \"description\": \"Updated description\",\n    \"status\": \"published\"\n  }\n}",
                            "name": "Success"
                        }
                    ]
                },
                {
                    "name": "Delete Learning Content",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/admin\/learning\/:id",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/admin\/learning\/:id",
                            "variable": [
                                {
                                    "id": "id",
                                    "key": "id",
                                    "value": 1,
                                    "description": "The ID of the learning."
                                },
                                {
                                    "id": "learning",
                                    "key": "learning",
                                    "value": 701,
                                    "description": "The learning content ID."
                                }
                            ]
                        },
                        "method": "DELETE",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": null,
                        "description": "Permanently deletes an article, its cover image file, and its likes."
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"success\": true,\n  \"message\": \"Learning content deleted successfully.\"\n}",
                            "name": "Success"
                        }
                    ]
                }
            ]
        },
        {
            "name": "Notifications",
            "description": "\nThe authenticated user's in-app notification feed (a comment, a like or\ndislike, a case being approved or declined, and admin broadcasts like a\npinned post, a new quiz, or a new article). Every notification also goes\nout as a push notification when the user has a saved device token; this\nfeed is the in-app record of the same events, and is what drives the\nnotifications KPI\/badge in the app.",
            "item": [
                {
                    "name": "List Notifications",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/notifications",
                            "query": [
                                {
                                    "key": "skip",
                                    "value": "0",
                                    "description": "Number of notifications to skip. Defaults to 0.",
                                    "disabled": true
                                },
                                {
                                    "key": "limit",
                                    "value": "15",
                                    "description": "Max notifications to return (capped at 100). Defaults to 15.",
                                    "disabled": false
                                }
                            ],
                            "raw": "{{baseUrl}}\/api\/v1\/notifications?skip=0&limit=15"
                        },
                        "method": "GET",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": null,
                        "description": "Paginated with `skip`\/`limit` query params - `skip` defaults to 0,\n`limit` defaults to 15 (max 100). Newest first.\n\n`counts` reports the totals for the user's whole feed (not just the\ncurrent page), so the app can render a KPI\/badge without a separate\nrequest."
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"data\": [\n    {\n      \"id\": \"9e1f7e2a-3b1a-4b3a-8f2a-2f6a1b1e9c31\",\n      \"type\": \"comment_added\",\n      \"title\": \"New Comment\",\n      \"body\": \"Dr John Roe commented on your post.\",\n      \"data\": { \"case_id\": 12, \"comment_id\": 5, \"commenter_id\": 4 },\n      \"read\": false,\n      \"read_at\": null,\n      \"created_at\": \"2026-09-17T06:00:00.000000Z\"\n    }\n  ],\n  \"meta\": { \"skip\": 0, \"limit\": 15, \"total\": 1 },\n  \"counts\": { \"total\": 1, \"unread\": 1, \"read\": 0 }\n}",
                            "name": "Success"
                        },
                        {
                            "header": [],
                            "code": 401,
                            "body": "{\n  \"message\": \"Unauthenticated.\"\n}",
                            "name": "Missing or expired token"
                        }
                    ]
                },
                {
                    "name": "Mark All Notifications as Read",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/notifications\/read-all",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/notifications\/read-all"
                        },
                        "method": "POST",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": null,
                        "description": ""
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"message\": \"All notifications marked as read.\"\n}",
                            "name": "Success"
                        },
                        {
                            "header": [],
                            "code": 401,
                            "body": "{\n  \"message\": \"Unauthenticated.\"\n}",
                            "name": "Missing or expired token"
                        }
                    ]
                },
                {
                    "name": "Mark Notification as Read",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/notifications\/:notification\/read",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/notifications\/:notification\/read",
                            "variable": [
                                {
                                    "id": "notification",
                                    "key": "notification",
                                    "value": "9e1f7e2a-3b1a-4b3a-8f2a-2f6a1b1e9c31",
                                    "description": "The notification ID."
                                }
                            ]
                        },
                        "method": "POST",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": null,
                        "description": ""
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"message\": \"Notification marked as read.\"\n}",
                            "name": "Success"
                        },
                        {
                            "header": [],
                            "code": 401,
                            "body": "{\n  \"message\": \"Unauthenticated.\"\n}",
                            "name": "Missing or expired token"
                        },
                        {
                            "header": [],
                            "code": 404,
                            "body": "{\n  \"message\": \"No query results for model [Illuminate\\\\Notifications\\\\DatabaseNotification].\"\n}",
                            "name": "No notification with that id for this user"
                        }
                    ]
                }
            ]
        },
        {
            "name": "Users",
            "description": "\nLooking up another user's profile. Requires `Authorization: Bearer {token}`.",
            "item": [
                {
                    "name": "View User Profile",
                    "request": {
                        "url": {
                            "host": "{{baseUrl}}",
                            "path": "api\/v1\/users\/:id",
                            "query": [],
                            "raw": "{{baseUrl}}\/api\/v1\/users\/:id",
                            "variable": [
                                {
                                    "id": "id",
                                    "key": "id",
                                    "value": 1,
                                    "description": "The ID of the user."
                                },
                                {
                                    "id": "user",
                                    "key": "user",
                                    "value": 3,
                                    "description": "The user ID."
                                }
                            ]
                        },
                        "method": "GET",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application\/json"
                            },
                            {
                                "key": "Accept",
                                "value": "application\/json"
                            }
                        ],
                        "body": null,
                        "description": "Public profile of any user, for when someone taps a name or avatar in the\ncase feed or a comment thread.\n\nThis is narrower than Get Profile: email, phone number, and the\nPMDC\/fellowship\/institutional numbers are private to the account owner\nand are never returned here. `cases_count` counts only the user's\napproved cases, matching what the feed shows."
                    },
                    "response": [
                        {
                            "header": [],
                            "code": 200,
                            "body": "{\n  \"data\": {\n    \"id\": 3,\n    \"full_name\": \"Dr Jane Doe\",\n    \"designation\": \"Consultant\",\n    \"bio\": \"Dermatologist with a special interest in dermoscopy.\",\n    \"province\": \"Punjab\",\n    \"city\": \"Lahore\",\n    \"role\": \"student\",\n    \"avatar_url\": null,\n    \"cases_count\": 4,\n    \"created_at\": \"2026-07-27T10:08:49.000000Z\"\n  }\n}",
                            "name": "Success"
                        },
                        {
                            "header": [],
                            "code": 404,
                            "body": "{\n  \"message\": \"Resource not found.\"\n}",
                            "name": "No such user"
                        }
                    ]
                }
            ]
        }
    ],
    "auth": {
        "type": "bearer",
        "bearer": [
            {
                "key": "Authorization",
                "type": "string"
            }
        ]
    }
}