API Reference パートナー予約連携API

API Guide (Partner Reservation Integration)

Authentication

The GOVIGO API uses API key authentication. Include the X-API-Key header in every request.

Request header example
GET /api/golf-clubs/plans.php?golf_club_id=1 HTTP/1.1
Host: govigolf.comX-API-Key: YOUR_API_KEY
API keys are issued from the admin panel. Each key has scopes (permissions); use a key granted the scopes required for your operations. Reservation partners are issued a partner-type key.

Available scopes:

ScopeDescription
golf_clubs:readRead golf club / plan information
tee_times:readRead availability / price calendar
reservations:readRead quote / reservation information
reservations:writeCreate / update / cancel reservations
integration:read / integration:writeWebhook management and golf-course-side integration (described later)

Common response format

All API responses are returned in the following common format.

{
  "success": true,
  "data": { ... },
  "message": "",
  "http_code": 200
}
FieldTypeDescription
successbooleantrue on success, false on failure
dataobject|nullResponse data (null on error)
messagestringMessage (error details on error)
http_codeintegerHTTP status code

Error codes

HTTP codeMeaningDescription
400Bad RequestInvalid request parameters
401UnauthorizedAPI key invalid or missing
403ForbiddenInsufficient scope
404Not FoundResource does not exist (or no permission)
405Method Not AllowedInvalid HTTP method
409ConflictResource conflict (duplicate booking, non-editable state, etc.)
429Too Many RequestsRate limit exceeded
500Internal Server ErrorInternal server error

Reservation lookup & authorization

A reservation can be referenced by two keys.

KeyDescription
reservation_id / idGOVIGO internal reservation ID (integer). Returned in responses.
external_refThe partner system's reservation reference ID (string). If passed at creation, you can reference / update / cancel using your own ID afterwards. Recommended.
Authorization (important): An owner scope applies to API keys. A partner key can reference / operate only on reservations it created. Specifying another party's reservation ID returns 404 Not Found. Keep external_ref unique within the same partner (a duplicate returns the existing reservation).

Connectivity check

GET /api/ping.php any

Checks the API key validity, owner, granted scopes and rate limit. Use it for initial connectivity testing (no scope required).

Response example
{
  "success": true,
  "data": {
    "authenticated": true,
    "owner": { "type": "partner", "id": 3, "name": "Partner A" },
    "key_prefix": "a1b2c3d4",
    "scopes": ["golf_clubs:read", "tee_times:read", "reservations:read", "reservations:write"],
    "rate_limit": 120,
    "expires_at": null,
    "server_time": "2026-06-01T10:00:00+07:00"
  },
  "message": "pong",
  "http_code": 200
}

Golf club & plan information

GET /api/golf-clubs/detail.php golf_clubs:read

Gets golf club details. Omit id to return a list.

ParameterTypeRequiredDescription
idintegerOptionalGolf club ID
countryintegerOptionalCountry code (0=Vietnam, 1=Thailand, 2=Japan)
Response example
{
  "success": true,
  "data": {
    "count": 1,
    "golf_clubs": [
      {
        "id": 1,
        "name": "Sample Golf Club",
        "area": "Hanoi",
        "country": 0,
        "address": "123 Golf Street",
        "google_map": "https://maps.google.com/...",
        "course_info": "18 holes, par 72",
        "cancel_policy": "Free up to 3 days before",
        "cancel_policy_govigo": "..."
      }
    ]
  },
  "message": "",
  "http_code": 200
}
GET /api/golf-clubs/plans.php golf_clubs:read

Gets the golf club plan list. The id obtained here is the golf_plan_id (required) used when booking.

ParameterTypeRequiredDescription
golf_club_idintegerRequiredGolf club ID
datestringOptionalOnly plans valid on the given date (YYYY-MM-DD)
Response example
{
  "success": true,
  "data": {
    "golf_club_id": 1,
    "count": 1,
    "plans": [
      {
        "id": 10,
        "source": "golf_plan",
        "name": "Weekday Plan",
        "day_type": "weekday",
        "price_local": 1500000,
        "price_inbound": 2000000,
        "partner_price_local": 1450000,
        "partner_price_inbound": 1850000,
        "apply_period": { "from": "2026-01-01", "to": "2026-12-31" },
        "time_range": "06:00-10:00",
        "minimum_players": 1,
        "summary": "Great value weekday-only plan",
        "rental_club_fee": 300000,
        "rental_shoes_fee": 100000
      }
    ]
  },
  "message": "",
  "http_code": 200
}
price_local is the local price, price_inbound is the inbound (visitor) price. Prices are applied per player-type counts (local / inbound), which can be mixed within a single reservation. Bookings / quotes are finalized at partner_price_local / partner_price_inbound (partner prices). Only plans with bookable: true (source: golf_plan) can be booked.
GET /api/golf-clubs/cancel-policy.php golf_clubs:read

Gets the golf club cancellation policy.

ParameterTypeRequiredDescription
golf_club_idintegerRequiredGolf club ID
Response example
{
  "success": true,
  "data": {
    "golf_club_id": 1,
    "cancel_policy": "Free cancellation up to 3 days before",
    "cancel_policy_govigo": "Reservations via GOVIGO...",
    "rules": [
      { "days_before": 3, "cancel_fee_rate": 0 },
      { "days_before": 1, "cancel_fee_rate": 50 },
      { "days_before": 0, "cancel_fee_rate": 100 }
    ]
  },
  "message": "",
  "http_code": 200
}

Availability

GET /api/tee-times/available.php tee_times:read

Gets available tee-time slots for the given date. To use a tee-time slot, pass tee_time_slot_id when booking (optional).

ParameterTypeRequiredDescription
golf_club_idintegerRequiredGolf club ID
datestringRequiredDate (YYYY-MM-DD)
Response example
{
  "success": true,
  "data": {
    "golf_club_id": 1,
    "date": "2026-03-15",
    "is_closed": false,
    "count": 1,
    "slots": [
      {
        "slot_id": 101,
        "tee_date": "2026-03-15",
        "tee_time": "07:00",
        "total_slots": 4,
        "available_slots": 2,
        "allow_join": true,
        "plan": { "id": 10, "name": "Weekday Plan", "price": 1500000 }
      }
    ]
  },
  "message": "",
  "http_code": 200
}
GET /api/tee-times/min-prices.php tee_times:read

Gets the daily minimum price for the given month (for calendar display).

ParameterTypeRequiredDescription
golf_club_idintegerRequiredGolf club ID
yearintegerOptionalYear (default: this year)
monthintegerOptionalMonth (default: this month)
Response example
{
  "success": true,
  "data": {
    "golf_club_id": 1,
    "year": 2026,
    "month": 3,
    "prices": {
      "2026-03-01": 1500000,
      "2026-03-02": 1800000
    }
  },
  "message": "",
  "http_code": 200
}

Price quote

GET /api/reservations/quote.php reservations:read

Gets the final amount before creating a reservation. The create API recalculates with the same logic, so it matches this quote. Amounts returned are partner prices (regular price minus the partner discount).

ParameterTypeRequiredDescription
golf_club_idintegerRequiredGolf club ID
golf_plan_idintegerRequiredPlan ID (id from plans)
local_playersintegerRequired*Number of players at local price
inbound_playersintegerRequired*Number of players at inbound price (* at least one of the two. Total 1-20. Can be mixed)
datestringOptionalPlay date (YYYY-MM-DD). If given, plan validity period / weekday are also validated
rental_clubintegerOptionalNumber of rental clubs
rental_shoesintegerOptionalNumber of rental shoes
Response example
{
  "success": true,
  "data": {
    "plan_id": 10,
    "plan_name": "Weekday Plan",
    "players": {
      "local":   { "count": 2, "regular_unit_price": 1500000, "discount_per_player": 50000, "unit_price": 1450000, "amount": 2900000 },
      "inbound": { "count": 2, "regular_unit_price": 2000000, "discount_per_player": 150000, "unit_price": 1850000, "amount": 3700000 }
    },
    "player_count": 4,
    "minimum_players": 1,
    "base_amount": 6600000,
    "rental": {
      "club_count": 2, "club_fee": 600000,
      "shoes_count": 0, "shoes_fee": 0,
      "subtotal": 600000
    },
    "currency": "VND",
    "total_amount": 7200000,
    "golf_club_id": 1
  },
  "message": "Quote calculated.",
  "http_code": 200
}

Reservations

POST /api/reservations/create.php reservations:write

Creates a new reservation. The request body is JSON. The amount (total_amount) is recalculated on the server from golf_plan_id, so it need not be specified in the request.

ParameterTypeRequiredDescription
golf_club_idintegerRequiredGolf club ID
golf_plan_idintegerRequiredPlan ID (id from plans)
tee_datestringRequiredPlay date(YYYY-MM-DD)
customer_namestringRequiredBooker name
customer_emailstringRequiredBooker email
customer_phonestringRequiredBooker phone
playersobjectRequiredPlayer counts by price type {local?, inbound?} (can be mixed; total 1-20)
tee_timestringOptionalDesired tee time (HH:MM). Normal booking only. Default 08:00
tee_time_slot_idintegerOptionalIf specified, instantly confirmed as a real-time slot and reflected on the golf course tee sheet (obtained via available). The slot's date must match tee_date. If omitted, a normal (request) booking
optionsobjectOptionalRentals etc. {rental_club, rental_shoes}
remarkstringOptionalRemark
attendeesarrayOptionalCompanion list ({name, email?, phone?})
external_refstringOptionalPartner reservation reference ID (recommended, unique)
Request example
POST /api/reservations/create.php HTTP/1.1
Host: govigolf.comX-API-Key: YOUR_API_KEY
Content-Type: application/json

{
  "golf_club_id": 1,
  "golf_plan_id": 10,
  "tee_date": "2026-03-15",
  "customer_name": "Taro Tanaka",
  "customer_email": "tanaka@example.com",
  "customer_phone": "090-1234-5678",
  "players": { "local": 2, "inbound": 2 },
  "options": { "rental_club": 2 },
  "external_ref": "EXT-2026-00500"
}
Response example (201 Created)
{
  "success": true,
  "data": {
    "reservation_id": 500,
    "status": 1,
    "golf_club_id": 1,
    "tee_date": "2026-03-15",
    "tee_time": "08:00",
    "player_count": 4,
    "players": { "local": 2, "inbound": 2 },
    "total_amount": 7200000,
    "source": "api_partner"
  },
  "message": "Reservation created successfully.",
  "http_code": 201
}
GET /api/reservations/list.php reservations:read

Gets the reservation list (only reservations you created). Supports pagination.

ParameterTypeRequiredDescription
golf_club_idintegerOptionalFilter by golf club ID
statusintegerOptionalStatus code (1=requested, 2=club confirming, 3=payment requested, 4=paid/confirmed, 5=rescheduling, 6=cancel requested, 7=cancelling(no refund), 8=cancelling(refund), 9=completed, 10=completed(cancelled))
date_fromstringOptionalPlay date start (YYYY-MM-DD)
date_tostringOptionalPlay date end (YYYY-MM-DD)
pageintegerOptionalPage number (default: 1)
per_pageintegerOptionalItems per page (default: 20, max: 100)
sortstringOptionalSort field (tee_date / created)
orderstringOptionalSort order (asc / desc)
Response example
{
  "success": true,
  "data": {
    "reservations": [
      {
        "id": 500,
        "golf_club_id": 1,
        "golf_club_name": "Sample Golf Club",
        "customer_name": "Taro Tanaka",
        "tee_date": "2026-03-15",
        "tee_time": "08:00",
        "player_count": 4,
        "players": { "local": 2, "inbound": 2 },
        "total_amount": 7200000,
        "status": "1.requested",
        "status_code": 1,
        "source": "api_partner",
        "external_ref": "EXT-2026-00500",
        "created_at": "2026-03-01 10:00:00"
      }
    ],
    "pagination": {
      "page": 1, "per_page": 20, "total_count": 1,
      "total_pages": 1, "has_next": false, "has_prev": false
    }
  },
  "message": "",
  "http_code": 200
}
GET /api/reservations/detail.php reservations:read

Gets reservation details. Specify either id or external_ref.

ParameterTypeRequiredDescription
idintegerone ofGOVIGO reservation ID
external_refstringone ofPartner reference ID
Response example
{
  "success": true,
  "data": {
    "reservation": {
      "id": 500,
      "golf_club_id": 1,
      "golf_club_name": "Sample Golf Club",
      "plan_id": 10,
      "plan_name": "Weekday Plan",
      "customer_name": "Taro Tanaka",
      "customer_email": "tanaka@example.com",
      "customer_phone": "090-1234-5678",
      "tee_date": "2026-03-15",
      "tee_time": "08:00",
      "player_count": 4,
      "players": { "local": 2, "inbound": 2 },
      "total_amount": 7200000,
      "remark": null,
      "options": { "rental_club": 2, "rental_shoes": 0 },
      "status": "1.requested",
      "status_code": 1,
      "source": "api_partner",
      "external_ref": "EXT-2026-00500",
      "created_at": "2026-03-01 10:00:00",
      "updated_at": "2026-03-01 10:00:00",
      "attendees": [
        { "name": "Hanako Sato", "email": null, "phone": null }
      ]
    }
  },
  "message": "",
  "http_code": 200
}
POST /api/reservations/update.php reservations:write

Updates the reservation. Only the specified fields are updated. If players / options change, total_amount is recalculated on the server. players follows the same interpretation as reservation creation: omitted types are treated as 0 players (always specify counts for all types). For real-time bookings, changing the total number of players also adjusts the golf course inventory. Cancelled (status=6-8) / completed (status=9,10) reservations cannot be updated.

ParameterTypeRequiredDescription
reservation_id / external_refinteger/stringRequired (one of)Reference key of the target reservation
playersobjectOptionalPlayer counts by price type {local?, inbound?} (recalculates total; omitted types are treated as 0)
optionsobjectOptionalRentals etc. (recalculates amount on change)
customer_name / customer_email / customer_phonestringOptionalBooker info
tee_datestringOptionalPlay date(YYYY-MM-DD). Real-time bookings (those with tee_time_slot_id) cannot be changed (cancel and re-create instead)
remarkstringOptionalRemark
attendeesarrayOptionalCompanions (full replacement if specified)
Request example
POST /api/reservations/update.php HTTP/1.1
Host: govigolf.comX-API-Key: YOUR_API_KEY
Content-Type: application/json

{
  "external_ref": "EXT-2026-00500",
  "players": { "local": 2, "inbound": 1 },
  "remark": "1 person cancelled"
}
Response example
{
  "success": true,
  "data": {
    "reservation_id": 500,
    "golf_club_id": 1,
    "plan_id": 10,
    "player_count": 3,
    "players": { "local": 2, "inbound": 1 },
    "total_amount": 5350000,
    "status": "1.requested",
    "status_code": 1,
    "external_ref": "EXT-2026-00500",
    "updated_at": "2026-03-02 09:00:00"
  },
  "message": "Reservation updated.",
  "http_code": 200
}
POST /api/reservations/cancel.php reservations:write

Cancels the reservation. Specify reservation_id or external_ref. Like member bookings, it is accepted as a cancellation request (status=6) and finalized by staff. Real-time bookings automatically restore the golf course inventory. Reservations already cancelling (status=6-8) / completed (status=9,10) cannot be cancelled.

ParameterTypeRequiredDescription
reservation_id / external_refinteger/stringRequired (one of)Reference key of the target reservation
reasonstringOptionalCancellation reason
Request example
POST /api/reservations/cancel.php HTTP/1.1
Host: govigolf.comX-API-Key: YOUR_API_KEY
Content-Type: application/json

{
  "external_ref": "EXT-2026-00500",
  "reason": "Due to schedule change"
}
Response example
{
  "success": true,
  "data": {
    "reservation_id": 500,
    "status": "cancel_requested",
    "status_code": 6,
    "cancelled_at": "2026-03-02T10:00:00+07:00"
  },
  "message": "Reservation cancelled successfully.",
  "http_code": 200
}

Webhook

A mechanism to notify your system of reservation status changes. Notifications are sent via HTTPS POST when events occur.

Available events:
reservation.created / reservation.updated / reservation.cancelled / reservation.status_changed / *(all events)
POST /api/integration/webhooks/subscribe.php integration:write

Registers a Webhook receiving URL.

ParameterTypeRequiredDescription
golf_club_idintegerRequiredGolf club ID
urlstringRequiredWebhook receiving URL (HTTPS required)
eventsarrayRequiredEvent types to subscribe
Request example
POST /api/integration/webhooks/subscribe.php HTTP/1.1
Host: govigolf.comX-API-Key: YOUR_API_KEY
Content-Type: application/json

{
  "golf_club_id": 1,
  "url": "https://your-system.com/webhook/govigo",
  "events": ["reservation.created", "reservation.updated", "reservation.cancelled"]
}
Response example (201 Created)
{
  "success": true,
  "data": {
    "subscription_id": 10,
    "golf_club_id": 1,
    "url": "https://your-system.com/webhook/govigo",
    "events": ["reservation.created", "reservation.updated", "reservation.cancelled"],
    "secret": "a1b2c3d4e5f6..."
  },
  "message": "",
  "http_code": 201
}
secret is used for HMAC verification of the Webhook payload. Verify the signature on receipt to confirm authenticity.
GET /api/integration/webhooks/list.php integration:read

Gets the list of registered Webhooks.

ParameterTypeRequiredDescription
golf_club_idintegerRequiredGolf club ID
Response example
{
  "success": true,
  "data": {
    "golf_club_id": 1,
    "count": 1,
    "subscriptions": [
      {
        "id": 10,
        "golf_club_id": 1,
        "url": "https://your-system.com/webhook/govigo",
        "events": ["reservation.created", "reservation.cancelled"],
        "is_active": true,
        "created_at": "2026-03-01 10:00:00",
        "updated_at": "2026-03-01 10:00:00"
      }
    ]
  },
  "message": "",
  "http_code": 200
}
POST /api/integration/webhooks/unsubscribe.php integration:write

Unsubscribes a registered Webhook.

ParameterTypeRequiredDescription
subscription_idintegerRequiredSubscription ID to unsubscribe
Request example
POST /api/integration/webhooks/unsubscribe.php HTTP/1.1
Host: govigolf.comX-API-Key: YOUR_API_KEY
Content-Type: application/json

{ "subscription_id": 10 }
POST /api/integration/webhooks/test.php integration:write

Sends a test notification to a registered Webhook to verify URL connectivity and signature verification.

ParameterTypeRequiredDescription
subscription_idintegerRequiredSubscription ID to test
Request example
POST /api/integration/webhooks/test.php HTTP/1.1
Host: govigolf.comX-API-Key: YOUR_API_KEY
Content-Type: application/json

{ "subscription_id": 10 }