Skip to content

Alhuda ERP API Specification (REST)

Superseded — kept for history only

This page was written in January 2026 and is not a description of how the system works today. It is kept so the reasoning behind early decisions stays readable. Current behaviour is described in the API reference. Do not build from this page.

Base: /api

Auth responses use JWT/session cookies.

Common Errors

  • 400: Validation error
  • 401: Not authenticated
  • 403: Not authorized
  • 404: Not found
  • 409: Conflict (capacity reached, duplicate IDs)
  • 422: Business rule violation

Auth

POST /auth/login

Request:

{ "email": "user@alhuda.com", "password": "secret" }
Response:
{ "user": { "id": "uuid", "role": "OPS_MANAGER" } }

GET /auth/me

Response:

{ "user": { "id": "uuid", "email": "user@alhuda.com", "role": "SALES_EXEC" } }

Inventory

GET /airlines

Response:

[ { "id": "uuid", "name": "Airline", "code": "SV" } ]

POST /quota-blocks

Request:

{
  "airlineId": "uuid",
  "flightNo": "SV123",
  "departureDate": "2025-01-01",
  "origin": "DEL",
  "destination": "JED",
  "totalSeats": 100,
  "price": 1200,
  "currency": "SAR"
}
Response:
{ "id": "uuid" }

POST /slots/generate

Request:

{ "quotaBlockId": "uuid", "count": 100 }
Response:
{ "created": 100 }

Groups

POST /groups

Request:

{
  "groupCode": "GRP-2025-001",
  "name": "Umrah Jan Group",
  "departureDate": "2025-01-10",
  "returnDate": "2025-01-20",
  "capacity": 40
}
Response:
{ "id": "uuid" }

POST /groups/:id/assign-slots

Request:

{ "slotIds": ["uuid-1", "uuid-2"] }
Rules: - Total assigned slots <= group capacity.

POST /groups/:id/assign-customer

Request:

{ "bookingId": "uuid" }
Rules: - Group must have capacity.

Sales

POST /customers

Request:

{ "firstName": "Ali", "lastName": "Hassan", "passportNo": "P12345" }

POST /quotations

Request:

{
  "customerId": "uuid",
  "agentId": "uuid",
  "currency": "INR",
  "total": 100000,
  "discount": 5000,
  "items": [ { "description": "Hotel", "quantity": 1, "unitPrice": 60000 } ]
}

POST /bookings

Request:

{
  "quotationId": "uuid",
  "customerId": "uuid",
  "groupId": "uuid",
  "agentId": "uuid"
}

Operations

POST /bookings/:id/ops-approve

Request:

{ "notes": "Verified via call" }
Rule: finance policy must be met.

POST /bookings/:id/ops-reject

Request:

{ "reason": "Missing documents" }

Finance

POST /payments

Request:

{ "bookingId": "uuid", "amount": 25000, "method": "CASH", "reference": "REC-001" }

POST /bookings/:id/finance-approve

Request:

{ "policy": "partial", "status": "APPROVED" }

Visa

POST /visa

Request:

{ "bookingId": "uuid", "status": "APPLIED" }

PATCH /visa/:id/status

Request:

{ "status": "ISSUED" }

POST /visa/:id/upload

Request:

{ "fileType": "VISA_PDF", "storageKey": "s3://bucket/file.pdf" }

Tickets

PATCH /tickets/:id/name-update

Request:

{ "passengerName": "Ali Hassan" }

POST /tickets/:id/issue

Rule: finance clearance OR GM exception proof.

POST /tickets/:id/exception-approve

Request:

{ "gmExceptionProof": "s3://bucket/approval-email.pdf" }

Reports

GET /reports/group-manifest

Filters: groupId, departureDate

GET /reports/dues

Filters: agentId, groupId, agingBucket

GET /reports/visa-pipeline

Filters: status, departureRange