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 5.7 ms·Redis up 5.6 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 (cleanups, notifications), with failures and retries.
- SMS is simulated: no message is sent and every verification code is 1234.
Base paths
| Path | Used by | Signs in with | Token |
|---|---|---|---|
/v1/customer | Customer app (buyers and guests) | Mobile number + SMS code | mq_cus_… |
/v1/advertiser | Advertiser app (individuals and establishments) | Mobile number + SMS code | mq_adv_… |
/v1/admin | Control panelcoming soon | Mobile number + password | mq_adm_… |
Send the token as Authorization: Bearer <token>. A token only works in its own app's path. Guests call public routes without one.
Sign in to the customer app
- New number? Send the registration form to get a code:
curl -X POST https://maqsed-api.dev-moltaqa.cloud/v1/customer/auth/register/otp \ -H 'Content-Type: application/json' \ -d '{"name":"Sara","mobile":"0551234567","acceptTerms":true}' - Send the same form with the code to create the account and get a token:
curl -X POST https://maqsed-api.dev-moltaqa.cloud/v1/customer/auth/register \ -H 'Content-Type: application/json' \ -d '{"name":"Sara","mobile":"0551234567","acceptTerms":true,"code":"1234"}' - Next time, log in with
login/otpthenlogin({"mobile","code"}). Call the API with the token:
Signed in,curl https://maqsed-api.dev-moltaqa.cloud/v1/customer/... \ -H 'Authorization: Bearer mq_cus_…'/accountchanges the language or the mobile (a code to the new number),DELETE /accountdeletes it, andPOST auth/logoutsigns out.
Advertisers
Same steps under /v1/advertiser/auth, with one form per account type: register/individual (name, mobile, email, cityId from GET /v1/advertiser/cities) or register/establishment (adds the CR number, tax number and the CR document, uploaded first to register/files). Each has an /otp step first. Login is login/otp then login. The response's nextStep says where the app goes. The control panel's sign-in is on the way.
Uploading files
Upload first with POST /v1/{app}/files?purpose=… (multipart, field file), then send the returned id to the endpoint that uses it. The file type is read from its bytes; each purpose has its own types and size limit (see the docs).
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 25 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 |
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} |
MOBILE_NOT_REGISTERED | 404 | No account is registered with this number. Please verify that the mobile number is correct. |
MOBILE_ALREADY_REGISTERED | 409 | The mobile number already exists. Please verify 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. |
CR_ALREADY_REGISTERED | 409 | The Commercial Registration number is already registered. |
CITY_NOT_AVAILABLE | 400 | This city is not available. Please choose another. |
FILE_NOT_AVAILABLE | 400 | This file can't be used. Please upload it again. |
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. |
SMS_SEND_FAILED | 503 | The verification code could not be sent. Please try again. |
INTERNAL_ERROR | 500 | Something went wrong, please try again |