Integration Documentation

Ferry Admin REST API v1

Complete reference for public agent-facing endpoints under /api/v1. This file is self-contained — open it in any browser or host it anywhere.

Base path: /api/v1 Auth: X-API-KEY Format: JSON

1. Overview

The Ferry Admin API allows authorized agents to search ferry routes, create bookings, complete payments, and retrieve user or station data needed for the booking flow.

Base URL

https://{your-domain}/api/v1

Replace {your-domain} with your production or staging host (e.g. localhost:8001 for local).

2. Authentication

Every request under /api/v1 must include an agent API key in the HTTP header. The key must belong to an active agent with API access enabled (is_use_api = Y, isactive = Y).

Header Required Description
X-API-KEY Yes Agent API key issued by Ferry Admin
Content-Type For POST application/json
Accept Recommended application/json

Example request headers

X-API-KEY: your-agent-api-key-here
Content-Type: application/json
Accept: application/json

401 Unauthorized

{
  "message": "API key is required"
}

403 Forbidden

{
  "message": "Invalid or inactive API key"
}

3. Conventions

Typical success envelope

{
  "success": true,
  "code": "XX-S-0001",
  "message": "…",
  "data": { }
}

Typical error envelope

{
  "success": false,
  "code": "XX-E-1001",
  "message": "…",
  "errors": { },
  "error": "…"
}

Booking status values

  • DR — Draft (created, awaiting payment; expires after ~15 minutes)
  • CO — Completed / paid (ticket issued)
  • VO — Void / canceled

Trip type values

  • O — One way
  • R — Round trip
  • M — Multi-leg / multi-route

Payment condition values (booking detail)

  • credit-card — Pay by card (default / no sales partner)
  • wallet — Deduct from sales-partner wallet (type agent)
  • credit — Charge against sales-partner credit limit (type broker)
  • credit-card-cash — Card or cash (type employee)
  • reject — Payment not allowed; see reject_reason

4. Recommended Booking Flow

  1. 1. Get departure stations → GET /station/departure
  2. 2. Get destination stations → GET /station/destination?depart_station={id}
  3. 3. Search available routes → GET /route?depart_station=…&dest_station=…&departdate=…
  4. 4. (Optional) Load user / sales partner → GET /user/{id}
  5. 5. Create booking → POST /booking/create (status DR)
  6. 6. Check payment condition → GET /booking/{invoiceno}
  7. 7. Complete payment → POST /booking/complete
  8. 8. Or cancel → POST /booking/cancel

6. User

GET /api/v1/user/{id}

Retrieve a user belonging to the authenticated agent, including nested agent, sales partner, agent account (wallet/credit), broker point, and station.

Path parameters
NameTypeDescription
idintegerUser ID
Example request
GET /api/v1/user/62
X-API-KEY: your-agent-api-key-here
Example response (200)
{
  "success": true,
  "code": "US-S-0001",
  "message": "User retrieved successfully",
  "data": {
    "id": 62,
    "name": "Somchai Broker",
    "email": "[email protected]",
    "role": "agent",
    "station_id": "uuid-station",
    "agent_id": "01990f86-997d-714c-a471-4448ac3b9162",
    "sales_partner_id": "uuid-sales-partner",
    "agent": {
      "id": "01990f86-997d-714c-a471-4448ac3b9162",
      "name": "Demo Agent Co., Ltd.",
      "code": "DEMO",
      "type": "agent",
      "site_url": "https://booking.example.com",
      "isactive": "Y"
    },
    "sales_partner": {
      "id": "uuid-sales-partner",
      "name": "Island Broker",
      "code": "BRK01",
      "type": "broker",
      "isactive": "Y",
      "agent_id": "01990f86-997d-714c-a471-4448ac3b9162",
      "agent_account": {
        "id": "uuid-account",
        "type": "broker",
        "wallet_balance": 0,
        "credit_balance": 15000,
        "credit_limit": 100000
      },
      "broker_point": {
        "id": "uuid-point",
        "balance": 120
      }
    },
    "station": {
      "id": "uuid-station",
      "name_en": "Phuket",
      "name_th": "ภูเก็ต",
      "nickname": "HKT"
    }
  }
}
Error (404)
{
  "success": false,
  "code": "US-E-1001",
  "message": "User not found"
}

7. Station

GET /api/v1/station

List all active stations.

Example response (200)
{
  "success": true,
  "code": "ST-S-0001",
  "message": "Get Station successfully",
  "data": [
    {
      "id": "uuid-station-1",
      "name": "Phuket",
      "name_th": "ภูเก็ต",
      "piername": "Rassada Pier",
      "piername_th": "ท่าเรือรัษฎา",
      "nickname": "HKT",
      "type": "pier"
    }
  ]
}
GET /api/v1/station/departure

Departure stations available for the authenticated agent’s routes.

Query parameters
NameTypeRequiredDescription
groupstringNoPass Y to group stations by tag/section
Example request
GET /api/v1/station/departure?group=Y
X-API-KEY: your-agent-api-key-here
Example response (grouped) (200)
{
  "success": true,
  "code": "ST-S-0001",
  "message": "Station list retrieved successfully",
  "data": {
    "Phuket Area": {
      "sections": {
        "name": "Phuket Area",
        "name_th": "โซนภูเก็ต",
        "icon": "phuket.png",
        "badge_text": "Popular"
      },
      "stations": [
        {
          "id": "uuid-station-1",
          "name": "Phuket",
          "name_th": "ภูเก็ต",
          "piername": "Rassada Pier",
          "piername_th": "ท่าเรือรัษฎา",
          "nickname": "HKT",
          "type": "pier",
          "st_sort": 1
        }
      ]
    }
  }
}
GET /api/v1/station/destination

Destination stations reachable from a selected departure station.

Query parameters
NameTypeRequiredDescription
depart_stationstring (UUID)YesDeparture station ID
groupstringNoPass Y to group by tag
Example request
GET /api/v1/station/destination?depart_station=uuid-station-1&group=Y
X-API-KEY: your-agent-api-key-here
Example response (200)
{
  "success": true,
  "code": "ST-S-0001",
  "msg": "Station list retrieved successfully",
  "depart_station": "uuid-station-1",
  "data": [
    {
      "id": "uuid-station-2",
      "name": "Koh Phi Phi",
      "name_th": "เกาะพีพี",
      "piername": "Tonsai Pier",
      "piername_th": "ท่าเรือต้นไทร",
      "nickname": "PP",
      "type": "island"
    }
  ]
}
GET /api/v1/station/{id}

Get a single active station by ID.

GET /api/v1/station/uuid-station-1
X-API-KEY: your-agent-api-key-here

8. Route

GET /api/v1/route

Search available ferry departures for the authenticated agent. Filters by seats, schedule availability, close time, and (for today) departures at least 30 minutes ahead.

Query parameters
NameTypeRequiredDescription
depart_stationUUIDNoDeparture station ID
dest_stationUUIDNoDestination station ID
departdatedate (Y-m-d)NoTravel date; defaults to today
Example request
GET /api/v1/route?depart_station=uuid-station-1&dest_station=uuid-station-2&departdate=2026-08-01
X-API-KEY: your-agent-api-key-here
Example response (200)
{
  "success": true,
  "code": "RT-S-0001",
  "message": "Route list retrieved successfully",
  "params": {
    "depart_station": "uuid-station-1",
    "dest_station": "uuid-station-2",
    "departdate": "2026-08-01"
  },
  "data": [
    {
      "id": "uuid-sub-route-1",
      "departure_time": "09:00",
      "arrival_time": "10:30",
      "departure_timezone": "Asia/Bangkok",
      "arrival_timezone": "Asia/Bangkok",
      "boat_type": "Speedboat",
      "seatamt": 28,
      "description": "Direct ferry",
      "description_2": null,
      "departure_station": {
        "id": "uuid-station-1",
        "name": "Phuket",
        "name_th": "ภูเก็ต",
        "piername": "Rassada Pier",
        "piername_th": "ท่าเรือรัษฎา",
        "nickname": "HKT",
        "type": "pier"
      },
      "destination_station": {
        "id": "uuid-station-2",
        "name": "Koh Phi Phi",
        "name_th": "เกาะพีพี",
        "piername": "Tonsai Pier",
        "piername_th": "ท่าเรือต้นไทร",
        "nickname": "PP",
        "type": "island"
      },
      "prices": {
        "regular_subtotal": 1200,
        "child_subtotal": 900,
        "infant_subtotal": 0,
        "regular_discount": 100,
        "child_discount": 50,
        "infant_discount": 0,
        "regular": 1100,
        "child": 850,
        "infant": 0
      },
      "icons": [
        "https://{domain}/images/icon-route/ico-wifi.png"
      ]
    }
  ]
}

Use data[].id (sub-route ID) and prices.regular / child / infant when creating a booking.

GET /api/v1/route/{id}

Get a single sub-route detail with agent-specific pricing. Path {id} is the sub-route UUID.

GET /api/v1/route/uuid-sub-route-1
X-API-KEY: your-agent-api-key-here
Example response (200)
{
  "success": true,
  "code": "RT-S-0001",
  "message": "Route list retrieved successfully",
  "data": {
    "id": "uuid-sub-route-1",
    "departure_time": "09:00",
    "arrival_time": "10:30",
    "departure_timezone": "Asia/Bangkok",
    "arrival_timezone": "Asia/Bangkok",
    "boat_type": "Speedboat",
    "seatamt": 40,
    "departure_station": { "id": "uuid-station-1", "name": "Phuket", "name_th": "ภูเก็ต", "piername": "Rassada Pier", "piername_th": "ท่าเรือรัษฎา", "nickname": "HKT", "type": "pier" },
    "destination_station": { "id": "uuid-station-2", "name": "Koh Phi Phi", "name_th": "เกาะพีพี", "piername": "Tonsai Pier", "piername_th": "ท่าเรือต้นไทร", "nickname": "PP", "type": "island" },
    "prices": {
      "regular_subtotal": 1200,
      "child_subtotal": 900,
      "infant_subtotal": 0,
      "regular_discount": 100,
      "child_discount": 50,
      "infant_discount": 0,
      "regular": 1100,
      "child": 850,
      "infant": 0
    },
    "description": "Direct ferry",
    "description_2": null
  }
}

9. Booking

GET /api/v1/booking

List bookings for the authenticated agent filtered by departure date range.

Query parameters
NameTypeRequiredDescription
start_datedateYesStart of departdate range
end_datedateYesEnd of departdate range
GET /api/v1/booking?start_date=2026-08-01&end_date=2026-08-31
X-API-KEY: your-agent-api-key-here
Example response (201)
{
  "success": true,
  "message": "",
  "data": [
    {
      "id": "uuid-booking",
      "departdate": "2026-08-01",
      "created_at": "2026-07-27T10:00:00.000000Z",
      "adult_passenger": 2,
      "child_passenger": 0,
      "infant_passenger": 0,
      "totalamt": 2200,
      "subtotal": 0,
      "discount": 100,
      "status": "CO",
      "invoiceno": "BK26080001",
      "ticketno": "TK26080001"
    }
  ]
}
POST /api/v1/booking/create

Create a draft booking (status = DR). Booking expires in approximately 15 minutes if not completed. If user_id is provided and that user has a sales partner, sales_partner_id is attached automatically.

Body fields
FieldTypeRequiredNotes
departdatedateYesMust be today or later
adult_passengerinteger 1–20YesAdult count
child_passengerinteger 0–20NoDefault 0
infant_passengerinteger 0–20NoDefault 0
user_idintegerNoLinks sales partner if present
trip_typeO|R|MNoDefault O
discountnumberNoOptional discount amount
notestringNoInternal note
book_channelstringNoDefault API
ispremiumflexY|NNoDefault N
referencenostringNoYour external reference
customersarrayYesAt least 1 passenger
customers[].fullnamestringYesPassenger full name
customers[].typestringNoe.g. ADULT, CHILD, INFANT
customers[].emailemailNoUsed for ticket email when completed
customers[].passportnostringNo
customers[].mobilestringNo
customers[].countrystringNo
routesarrayYesAt least 1 route leg
routes[].idUUIDYesSub-route ID from route search
routes[].traveldatedateYesTravel date for this leg
routes[].pricenumberYes*Adult total price for this leg
routes[].child_pricenumberNoChild total price
routes[].infant_pricenumberNoInfant total price
Example request body
POST /api/v1/booking/create
X-API-KEY: your-agent-api-key-here
Content-Type: application/json

{
  "departdate": "2026-08-01",
  "adult_passenger": 2,
  "child_passenger": 0,
  "infant_passenger": 0,
  "user_id": 62,
  "trip_type": "O",
  "book_channel": "API",
  "referenceno": "EXT-ORDER-1001",
  "note": "Window seats preferred",
  "customers": [
    {
      "fullname": "John Smith",
      "type": "ADULT",
      "email": "[email protected]",
      "mobile": "+66812345678",
      "passportno": "AB1234567",
      "country": "Thailand",
      "title": "Mr"
    },
    {
      "fullname": "Jane Smith",
      "type": "ADULT",
      "email": "[email protected]",
      "country": "Thailand"
    }
  ],
  "routes": [
    {
      "id": "uuid-sub-route-1",
      "traveldate": "2026-08-01",
      "price": 2200,
      "child_price": 0,
      "infant_price": 0
    }
  ]
}
Example response (201)
{
  "success": true,
  "code": "BK-S-0001",
  "message": "Booking created successfully, please request to complete payment.",
  "data": {
    "booking_id": "019abcde-1234-5678-9abc-def012345678",
    "invoiceno": "BK26080099",
    "status": "DR",
    "total_passengers": 2,
    "created_at": "2026-07-27T14:00:00+00:00",
    "expires_at": "2026-07-27T14:15:00+00:00",
    "qrcode_url": "https://booking.example.com/b/BK26080099"
  }
}
Validation error (400)
{
  "success": false,
  "code": "BK-E-1001",
  "message": "Invalid booking data",
  "errors": {
    "departdate": ["The departdate field is required."],
    "customers": ["The customers field is required."]
  }
}
GET /api/v1/booking/{invoiceno}

Get booking detail by invoice number (or booking UUID). Includes customers, routes, payments, and payment_condition / reject_reason based on linked sales partner.

GET /api/v1/booking/BK26080099
X-API-KEY: your-agent-api-key-here
Example response (200)
{
  "success": true,
  "code": "BK-S-0004",
  "message": "Booking detail retrieved successfully",
  "data": {
    "id": "019abcde-1234-5678-9abc-def012345678",
    "departdate": "2026-08-01",
    "adult_passenger": 2,
    "child_passenger": 0,
    "infant_passenger": 0,
    "totalamt": 2200,
    "subtotal": 0,
    "discount": 100,
    "ispayment": "N",
    "trip_type": "O",
    "note": "Window seats preferred",
    "status": "DR",
    "invoiceno": "BK26080099",
    "referenceno": "EXT-ORDER-1001",
    "complete_date": null,
    "cancel_date": null,
    "reason": null,
    "qrcode_url": "https://booking.example.com/b/BK26080099",
    "agent_id": "01990f86-997d-714c-a471-4448ac3b9162",
    "sales_partner_id": "uuid-sales-partner",
    "user_id": 62,
    "user_name": "Somchai Broker",
    "payment_condition": "credit",
    "reject_reason": null,
    "customers": [
      {
        "id": "uuid-customer",
        "fullname": "John Smith",
        "type": "ADULT",
        "passportno": "AB1234567",
        "email": "[email protected]",
        "mobile": "+66812345678",
        "mobile_th": null,
        "other_contact": null,
        "country": "Thailand"
      }
    ],
    "routes": [
      {
        "id": "uuid-sub-route-1",
        "departure_time": "09:00",
        "arrival_time": "10:30",
        "departure_timezone": "Asia/Bangkok",
        "arrival_timezone": "Asia/Bangkok",
        "boat_type": "Speedboat",
        "departure_station": { "id": "uuid-station-1", "name": "Phuket", "name_th": "ภูเก็ต", "piername": "Rassada Pier", "piername_th": "ท่าเรือรัษฎา", "nickname": "HKT", "type": "pier" },
        "destination_station": { "id": "uuid-station-2", "name": "Koh Phi Phi", "name_th": "เกาะพีพี", "piername": "Tonsai Pier", "piername_th": "ท่าเรือต้นไทร", "nickname": "PP", "type": "island" }
      }
    ],
    "payments": [
      {
        "id": "uuid-payment",
        "payment_method": "CASH",
        "totalamt": 2200
      }
    ]
  }
}
Reject example: when wallet/credit is insufficient, payment_condition becomes "reject" and reject_reason may be "Insufficient wallet balance for this booking", "Credit limit exceeded for this booking", or "Agent account not found".
POST /api/v1/booking/complete

Mark booking as paid/completed, update payment, and generate ticket number.

Body fields
FieldTypeRequiredNotes
invoicenostringYesBooking invoice number
payment_methodstringYese.g. CREDIT_CARD, WALLET, CREDIT, CASH
notestringNo
referencenostringNoPayment gateway / external ref
agent_idUUIDNoOverride agent context when needed
feeamtnumberNoPayment fee
discountnumberNoPayment discount
paidamtnumberNoAmount paid (stored as payment totalamt)
Example request
POST /api/v1/booking/complete
X-API-KEY: your-agent-api-key-here
Content-Type: application/json

{
  "invoiceno": "BK26080099",
  "payment_method": "CREDIT_CARD",
  "referenceno": "PAY-998877",
  "paidamt": 2200,
  "feeamt": 0,
  "discount": 0,
  "note": "Paid via card"
}
Example response (200)
{
  "success": true,
  "code": "PM-S-0001",
  "message": "Payment successful",
  "data": {
    "ticketno": "TK26080099",
    "status": "CO",
    "qrcode_url": "https://booking.example.com/b/BK26080099"
  }
}
POST /api/v1/booking/cancel

Cancel (void) a booking. Sets status to VO.

POST /api/v1/booking/cancel
X-API-KEY: your-agent-api-key-here
Content-Type: application/json

{
  "invoiceno": "BK26080099",
  "reason": "Customer requested cancellation"
}
Example response (200)
{
  "success": true,
  "code": "BK-S-0003",
  "message": "Booking canceled successfully"
}

10. Payment

GET /api/v1/payment/{invoiceno}

Retrieve the first payment record linked to a booking invoice number.

GET /api/v1/payment/BK26080099
X-API-KEY: your-agent-api-key-here
Example response (200)
{
  "success": true,
  "code": "P-S-0004",
  "message": "",
  "data": {
    "id": "uuid-payment",
    "payment_method": "CREDIT_CARD",
    "totalamt": 2200,
    "amount": 2200,
    "feeamt": 0,
    "status": "CO",
    "ispaid": "Y",
    "booking_id": "019abcde-1234-5678-9abc-def012345678",
    "paymentno": "PM26080001",
    "docdate": "2026-07-27",
    "payment_date": "2026-07-27T14:05:00.000000Z"
  }
}

15. Common Response Codes

Code HTTP Meaning
BK-S-0001201Booking created
BK-S-0003200Booking canceled
BK-S-0004200Booking detail retrieved
BK-E-1001400/500Invalid / failed booking create
BK-E-1003404Booking not found
PM-S-0001200Payment / complete successful
PM-E-1001500Complete booking failed
PM-E-1005400Invalid complete payload
PM-E-1006404Agent not found (complete override)
P-S-0004200Payment retrieved
RT-S-0001200Route list/detail OK
RT-E-1001404No routes found
ST-S-0001200Station list OK
ST-E-1001404Station not found
US-S-0001200User retrieved
US-E-1001404User not found / not owned by agent