API reference (Swagger)
Every endpoint with its request, responses and errors. Try requests right in the browser.
Postman collection
Download and import. Sign-in requests save the token, so the rest are authenticated.
OpenAPI 3 document
For code generators and API clients (Dart, Swift, Kotlin, TypeScript…).
Health
MySQL up 1.8 ms·Redis up 0.7 ms
Request logs
Recent requests with their body, response, SQL and timing. Look up a failing call by its X-Request-Id.
Background jobs
Queued and scheduled jobs (file cleanup now; expiry and notifications later), with failures and retries.
Base paths
| Path | Used by | Signs in with | Token |
|---|---|---|---|
/v1/customer | Customer app (customers and guests)coming soon | Mobile number + SMS code | mh_cus_… |
/v1/merchant | Merchant app (stores)coming soon | Username + password | mh_mer_… |
/v1/admin | Control panelcoming soon | Mobile number + password | mh_adm_… |
Send the token as Authorization: Bearer <token>. A token only works in its own app's path. Guests call public routes without one.
Uploading files
A form that takes files is sent as one multipart/form-data request: its fields as text and each file in a named part (for example images and video when creating an ad). Forms without files are JSON. The file type is read from its bytes; each kind has its own types and size limit (ad images 5 MB, ad videos 100 MB). Private files (commercial registration, work document) come back as signed links that expire. The first forms arrive with M1.
Conventions
Language
Send Accept-Language: ar or en (or ?lang=). Arabic is the default; a signed-in account's saved language is used when the request names none.
Errors
Always { code, message, details?, requestId }. Branch on code; show message. details lists field problems with their own message.
Request IDs
Every response has an X-Request-Id header. Include it when reporting a problem.
Mobile numbers
Saudi numbers as typed (05…, 5…, +9665…, Arabic digits too); responses use +9665….
Dates
Times are ISO 8601 in UTC, for example 2026-10-07T09:30:00.000Z.
Lists
App lists scroll with { items, nextCursor }; control panel tables page with { items, page, limit, total }.
Error codes
All 57 codes with their HTTP status and English message
| Code | Status | Message |
|---|---|---|
VALIDATION_FAILED | 400 | The request data is invalid |
INVALID_JSON | 400 | The request body could not be read |
UNAUTHORIZED | 401 | Please log in to complete this action. |
FORBIDDEN | 403 | You don't have permission to do this |
ACCOUNT_DISABLED | 403 | This user is disabled. Please contact the administration. |
NOT_FOUND | 404 | The requested item was not found |
CONFLICT | 409 | The request conflicts with existing data |
PAYLOAD_TOO_LARGE | 413 | The request is larger than allowed |
TOO_MANY_REQUESTS | 429 | Too many requests, please try again shortly |
MOBILE_NOT_REGISTERED | 404 | No account is registered with this number. Please make sure the mobile number is correct. |
MOBILE_ALREADY_REGISTERED | 409 | The mobile number is already registered, please check the number. |
MOBILE_UNCHANGED | 400 | The new number is the same as your current number. |
EMAIL_ALREADY_REGISTERED | 409 | The email address is already registered. |
USERNAME_TAKEN | 409 | This username is already in use. Please choose another one. |
CR_ALREADY_REGISTERED | 409 | The commercial registration number is already in use. |
INVALID_CREDENTIALS | 401 | The username or password is incorrect. |
ACCOUNT_UNDER_REVIEW | 403 | Your account is still under review. You can log in once it is approved. |
ACCOUNT_REJECTED | 403 | Your account was not approved. |
PASSWORD_INCORRECT | 400 | The current password is incorrect. |
PASSWORD_UNCHANGED | 400 | The new password must be different from the current one. |
RESET_TOKEN_INVALID | 400 | This password reset link is no longer valid. Please request a new code. |
CITY_NOT_AVAILABLE | 400 | This city is not available. Please choose another. |
CATEGORY_NOT_AVAILABLE | 400 | This category is not available. Please choose another category. |
CATEGORY_NAME_TAKEN | 409 | A category with this name already exists. Please enter a different name. |
CATEGORY_HAS_ADS | 409 | This category has ads, so it cannot be deleted. Deactivate it instead. |
OTP_INVALID | 400 | The verification code is incorrect. |
OTP_EXPIRED | 400 | The verification code is invalid or expired. Please request a new one. |
OTP_TOO_MANY_ATTEMPTS | 429 | Too many incorrect attempts. Please request a new code. |
OTP_RESEND_TOO_SOON | 429 | You can request a new code in {seconds} seconds. |
VERIFICATION_INVALID | 400 | This verification is no longer valid. Please request a new code. |
SMS_SEND_FAILED | 503 | The verification code could not be sent. Please try again. |
FILE_REQUIRED | 400 | No file was attached. |
FILE_TOO_LARGE | 413 | File size exceeds the allowed limit ({maxMb} MB). |
FILE_TYPE_NOT_ALLOWED | 415 | Unsupported file type. Allowed: {allowed} |
FILE_NOT_AVAILABLE | 400 | This file can't be used. Please upload it again. |
PACKAGE_NOT_AVAILABLE | 400 | This package is not available. Please choose another package. |
PACKAGE_NAME_TAKEN | 409 | An active package already has this name. Please enter a different name. |
PACKAGE_IN_USE | 409 | Merchants have subscribed to this package, so it cannot be deleted. Deactivate it instead. |
CONTACT_MESSAGE_REPLIED | 409 | This message has already been replied to. |
SUBSCRIPTION_ALREADY_ACTIVE | 409 | You already have an active subscription. You can subscribe to a new package when it ends. |
INSUFFICIENT_BALANCE | 402 | Your wallet balance is insufficient. Top up your wallet and try again. |
ROLE_NAME_TAKEN | 409 | A role with this name already exists. Please enter another name. |
ROLE_IN_USE | 409 | The role cannot be deleted because it is associated with a system administrator. |
SYSTEM_ROLE_LOCKED | 409 | The Super Admin role can’t be changed. |
SELF_CHANGE_NOT_ALLOWED | 403 | You can’t change your own role or account. |
ROLE_NOT_AVAILABLE | 400 | This role is not available. Please choose another role. |
SUPER_ADMIN_ROLE_RESERVED | 409 | The Super Admin role can’t be given to another administrator. |
SUPER_ADMIN_PROTECTED | 403 | Super Admin accounts can’t be changed by other administrators. |
AD_NOT_AVAILABLE | 404 | This ad is no longer available. |
MERCHANT_NOT_AVAILABLE | 404 | This store is no longer available. |
ALREADY_REPORTED | 409 | You have already reported this ad; we are reviewing it. |
AD_STATUS_INVALID | 409 | This can't be done to the ad in its current status. |
MERCHANT_REVIEW_INVALID | 409 | This request has already been reviewed. |
REPORT_ALREADY_REVIEWED | 409 | This report has already been reviewed. |
AD_FEE_REQUIRED | 402 | Publishing this ad costs {fee} SAR. Pay from your wallet or by card, or subscribe to a package. |
IDEMPOTENCY_KEY_REUSED | 409 | This Idempotency-Key was already used for a different request. |
INTERNAL_ERROR | 500 | Something went wrong, please try again |