The GOVIGO API uses API key authentication. Include the X-API-Key header in every request.
GET /api/golf-clubs/plans.php?golf_club_id=1 HTTP/1.1 Host: govigolf.com X-API-Key: YOUR_API_KEY
partner-type key.
Available scopes:
| Scope | Description |
|---|---|
golf_clubs:read | Read golf club / plan information |
tee_times:read | Read availability / price calendar |
reservations:read | Read quote / reservation information |
reservations:write | Create / update / cancel reservations |
webhooks:read / webhooks:write | View/manage Webhooks (notifications for your own reservations) (described later) |
All API responses are returned in the following common format.
{
"success": true,
"data": { ... },
"message": "",
"http_code": 200
}
| Field | Type | Description |
|---|---|---|
success | boolean | true on success, false on failure |
data | object|null | Response data (null on error) |
message | string | Message (error details on error) |
http_code | integer | HTTP status code |
| HTTP code | Meaning | Description |
|---|---|---|
400 | Bad Request | Invalid request parameters |
401 | Unauthorized | API key invalid or missing |
403 | Forbidden | Insufficient scope |
404 | Not Found | Resource does not exist (or no permission) |
405 | Method Not Allowed | Invalid HTTP method |
409 | Conflict | Resource conflict (duplicate booking, non-editable state, etc.) |
429 | Too Many Requests | Rate limit exceeded |
500 | Internal Server Error | Internal server error |
A reservation can be referenced by two keys.
| Key | Description |
|---|---|
reservation_id / id | GOVIGO internal reservation ID (integer). Returned in responses. |
external_ref | The partner system's reservation reference ID (string). If passed at creation, you can reference / update / cancel using your own ID afterwards. Recommended. |
404 Not Found. Keep external_ref unique within the same partner (a duplicate returns the existing reservation).
Checks the API key validity, owner, granted scopes and rate limit. Use it for initial connectivity testing (no scope required).
{
"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
}
Retrieves golf club details. Omit id to get a list.
| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Optional | Golf club ID |
country | integer | Optional | Country code (0=Vietnam, 1=Thailand, 2=Japan). Currently only Vietnam (0) is provided (other countries return no results) |
lang | string | Optional | Response language: ja (default) / en / vi / kr / cn. Any other value returns 400 |
updated_since | string | Optional | ISO 8601 datetime (e.g. 2026-10-01T00:00:00Z). Returns only golf clubs whose updated_at is at or after this time (none = count: 0) |
lang=en){
"success": true,
"data": {
"lang": "en",
"count": 1,
"golf_clubs": [
{
"id": 1,
"name": "Sample Golf Club",
"area": "Central (Da Nang / Hue)",
"area_code": 3,
"country": 0,
"address": "Xa Loc Vinh, Phu Loc, Hue, Viet Nam",
"google_map": "https://maps.google.com/...",
"latitude": 16.3312358,
"longitude": 107.9558334,
"phone": "+842343695880",
"opening_hours": "05:30-18:00",
"description": "Located about an hour's drive from Da Nang ...",
"access": null,
"courses": [
{ "name": "Lagoon Course", "holes": 18, "par": 71, "length_yards": 7101, "designer": "Sir Nick Faldo" }
],
"course_info": "18 holes, Par 71 / 7,101 yards",
"remark": "Located about an hour's drive from Da Nang ...",
"opened_on": "2013-03-01",
"dress_code": "Collared shirts required ...",
"cancel_policy": "Free cancellation up to 9 days before tee time ...",
"main_image": {
"url": "https://govigolf.com/images/golf_club_top_picture/xxx.jpg",
"thumbnail_url": "https://govigolf.com/images/golf_club_picture/thumb/xxx.jpg"
},
"images": [
{ "url": "https://govigolf.com/images/golf_club_picture/yyy.jpg",
"thumbnail_url": "https://govigolf.com/api/golf-clubs/thumbnail.php?src=..." }
],
"facilities": {
"driving_range": { "available": true, "note": null },
"pro_shop": { "available": true, "note": null },
"restaurant": { "available": true, "note": null },
"lesson": { "available": false, "note": null },
"night_golf": { "available": false },
"note": null
},
"updated_at": "2026-08-18T09:30:40Z",
"rental": {
"club_available": true,
"club_fee": 300000,
"shoes_available": false,
"shoes_fee": null
},
"options": {
"rental_cart": { "available": true, "included": true, "fee": null },
"rental_umbrella": { "available": false, "included": false, "fee": null },
"caddy": { "available": true, "included": true, "fee": null },
"caddy_designate": { "available": true, "included": false, "fee": 200000 },
"caddybag_storage": { "available": false, "included": false, "fee": null },
"sent_caddybag": { "available": false, "included": false, "fee": null },
"accompany": { "available": false, "included": false, "fee": null },
"pickup": { "available": false, "description": null }
}
}
]
},
"message": "Golf club information retrieved.",
"http_code": 200
}
name / area / description / access / course_info / remark / dress_code / cancel_policy / facilities.note are returned in the requested lang. If a translation is not available, it falls back vi / kr / cn → en → ja, and en → ja (kr / cn are currently mostly English). address is in local notation only. facilities.*.note exists only in Japanese, so it is returned only for lang=ja.
description | Introduction / overview. |
access | Access information (only where registered, otherwise null). |
courses | Specifications per course (name, holes, par, total length in yards, designer). Multiple entries for clubs with several courses. [] if not registered; unregistered fields are null. |
course_info | Course overview (free text). Holes, par and length may be written here even when courses is empty. Both are returned when both exist. |
opening_hours | Opening hours (e.g. 05:30-18:00; null if not registered). |
cancel_policy | Cancellation policy for customers. |
latitude / longitude | Coordinates (null if not available). |
phone | Main phone number (null if none). |
opened_on | Opening date (YYYY-MM-DD, null if unknown). |
main_image / images | Main image and photos (up to 5). thumbnail_url is a resized version (480px on the long edge). Photo types (course, clubhouse, etc.) are not classified. |
facilities | Availability of driving range, pro shop, restaurant, lessons and night golf. |
updated_at | Latest update (UTC) of basic info, coordinates, introduction and course specifications. Prices and plans are not included (see /api/golf-clubs/plans.php). |
area / area_code | Area name and code (see table below). |
| area_code | ja | en | vi |
|---|---|---|---|
0 | 北部(ハノイ) | North (Hanoi) | Miền Bắc (Hà Nội) |
1 | 北部(ハイフォン) | North (Hai Phong) | Miền Bắc (Hải Phòng) |
2 | 北部(その他) | North (Other) | Miền Bắc (Khác) |
3 | 中部(ダナン・フエ) | Central (Da Nang / Hue) | Miền Trung (Đà Nẵng / Huế) |
4 | 中部(その他) | Central (Other) | Miền Trung (Khác) |
5 | 南部(ホーチミン) | South (Ho Chi Minh City) | Miền Nam (TP. Hồ Chí Minh) |
6 | 南部(その他) | South (Other) | Miền Nam (Khác) |
7 | 南部(ニャチャン) | South (Nha Trang) | Miền Nam (Nha Trang) |
8 | 南部(フーコック) | South (Phu Quoc) | Miền Nam (Phú Quốc) |
rental (clubs / shoes) are rentals that can be specified as booking parameters in this API (quote / create). options (cart, caddy, pickup, etc.) are reference information only (cannot be booked via API); availability (available), whether it is included in every plan (included) and fee (fee) are returned for display and control on the vendor side. included: true means it is part of the plan price at no extra charge (cart and caddy at most courses), so fee is null. available: true with fee: null and included: false means the fee is quoted separately. rental is available: true only when a fee is set.
Gets the golf club plan list. The id obtained here is the golf_plan_id (required) used when booking.
| Parameter | Type | Required | Description |
|---|---|---|---|
golf_club_id | integer | Required | Golf club ID |
date | string | Optional | Only plans valid on the given date (YYYY-MM-DD) |
lang | string | Optional | Response language: ja (default) / en / vi / kr / cn. Any other value returns 400 |
{
"success": true,
"data": {
"golf_club_id": 1,
"lang": "ja",
"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_ranges": [{ "from": "06:00", "to": "10:00" }],
"minimum_players": 1,
"summary": "Great value weekday-only plan",
"cancel_policy": "Free cancellation up to 3 days before",
"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. cancel_policy is the customer-facing cancellation policy: it returns the plan-specific policy if set, otherwise the golf club's cancellation policy. name / summary / cancel_policy are returned in the lang language. Missing translations fall back in the same order as /api/golf-clubs/detail.php (vi / kr / cn → en → ja, en → ja). For cancel_policy, each language checks the plan-specific policy first, then the golf club policy. time_ranges are the tee-time windows in which this plan can be booked (up to 3, both ends inclusive; an empty array means no time restriction). If the tee_time of a booking is outside all windows, the request returns 409.
Gets available tee-time slots for the given date. To use a tee-time slot, pass tee_time_slot_id when booking (optional).
| Parameter | Type | Required | Description |
|---|---|---|---|
golf_club_id | integer | Required | Golf club ID |
date | string | Required | Date (YYYY-MM-DD) |
lang | string | Optional | Response language: ja (default) / en / vi / kr / cn. Any other value returns 400 |
{
"success": true,
"data": {
"golf_club_id": 1,
"date": "2026-03-15",
"lang": "en",
"is_closed": false,
"count": 1,
"slots": [
{
"slot_id": 101,
"course_id": 3,
"course_name": "Lake Course",
"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
}
course_name and plan.name are returned in the lang language, using the names the golf club registered for each language. Missing languages fall back in this order: vi / kr / cn → en → the registered name, en → the registered name.Gets the daily minimum price for the given month (for calendar display).
| Parameter | Type | Required | Description |
|---|---|---|---|
golf_club_id | integer | Required | Golf club ID |
year | integer | Optional | Year (default: this year) |
month | integer | Optional | Month (default: this month) |
{
"success": true,
"data": {
"golf_club_id": 1,
"year": 2026,
"month": 3,
"prices": {
"2026-03-01": 1500000,
"2026-03-02": 1800000
}
},
"message": "",
"http_code": 200
}
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).
| Parameter | Type | Required | Description |
|---|---|---|---|
golf_club_id | integer | Required | Golf club ID |
golf_plan_id | integer | Required | Plan ID (id from plans) |
local_players | integer | Required* | Number of players at local price |
inbound_players | integer | Required* | Number of players at inbound price (* at least one of the two. Total 1-20. Can be mixed) |
date | string | Optional | Play date (YYYY-MM-DD). If given, plan validity period / weekday are also validated |
tee_time | string | Optional | Preferred tee time (HH:MM). If given, it is also checked against the plan time windows (time_ranges in plans); outside returns 409 |
rental_club | integer | Optional | Number of rental clubs (only at clubs that offer it) |
rental_shoes | integer | Optional | Number of rental shoes (only at clubs that offer it) |
/api/golf-clubs/detail.php (rental.club_available / shoes_available) or /api/golf-clubs/plans.php (rental_club_fee / rental_shoes_fee; null = not offered). Requesting a quantity at a club that does not offer it (fee not set) returns a 409 error (no zero-fee quote or booking is made).
{
"success": true,
"data": {
"plan_id": 10,
"plan_name": "Weekday Plan",
"time_ranges": [{ "from": "06:00", "to": "10:00" }],
"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
}
time_ranges in the response are the plan's bookable time windows (same as plans).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.
| Parameter | Type | Required | Description |
|---|---|---|---|
golf_club_id | integer | Required | Golf club ID |
golf_plan_id | integer | Required | Plan ID (id from plans) |
tee_date | string | Required | Play date(YYYY-MM-DD) |
customer_name | string | Required | Booker name |
customer_email | string | Required | Booker email |
customer_phone | string | Optional | Booker phone (if provided, saved as the booker's contact) |
players | object | Required | Player counts by price type {local?, inbound?} (can be mixed; total 1-20) |
tee_time | string | Required* | Preferred tee time (HH:MM). * Required for request bookings without tee_time_slot_id (omitting it returns 400). Must be within the plan time windows (time_ranges in plans); outside returns 409. Not used for real-time slot bookings |
tee_time_slot_id | integer | Optional | If 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 |
options | object | Optional | Rentals etc. {rental_club, rental_shoes} |
remark | string | Optional | Remark |
attendees | array | Optional | Companion list ({name, email?, phone?}) |
external_ref | string | Optional | Partner reservation reference ID (recommended, unique) |
POST /api/reservations/create.php HTTP/1.1
Host: govigolf.com
X-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"
}
{
"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
}
Gets the reservation list (only reservations you created). Supports pagination.
| Parameter | Type | Required | Description |
|---|---|---|---|
golf_club_id | integer | Optional | Filter by golf club ID |
status | integer | Optional | Status 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_from | string | Optional | Play date start (YYYY-MM-DD) |
date_to | string | Optional | Play date end (YYYY-MM-DD) |
page | integer | Optional | Page number (default: 1) |
per_page | integer | Optional | Items per page (default: 20, max: 100) |
sort | string | Optional | Sort field (tee_date / created) |
order | string | Optional | Sort order (asc / desc) |
{
"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
}
Gets reservation details. Specify either id or external_ref.
| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | one of | GOVIGO reservation ID |
external_ref | string | one of | Partner reference ID |
{
"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
}
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.
| Parameter | Type | Required | Description |
|---|---|---|---|
reservation_id / external_ref | integer/string | Required (one of) | Reference key of the target reservation |
players | object | Optional | Player counts by price type {local?, inbound?} (recalculates total; omitted types are treated as 0) |
options | object | Optional | Rentals etc. (recalculates amount on change) |
customer_name / customer_email / customer_phone | string | Optional | Booker info |
tee_date | string | Optional | Play date(YYYY-MM-DD). Real-time bookings (those with tee_time_slot_id) cannot be changed (cancel and re-create instead) |
remark | string | Optional | Remark |
attendees | array | Optional | Companions (full replacement if specified) |
POST /api/reservations/update.php HTTP/1.1
Host: govigolf.com
X-API-Key: YOUR_API_KEY
Content-Type: application/json
{
"external_ref": "EXT-2026-00500",
"players": { "local": 2, "inbound": 1 },
"remark": "1 person cancelled"
}
{
"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
}
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 slot inventory is restored when the cancellation is finalized (not at request time). Reservations already cancelling (status=6-8) / completed (status=9,10) cannot be cancelled.
| Parameter | Type | Required | Description |
|---|---|---|---|
reservation_id / external_ref | integer/string | Required (one of) | Reference key of the target reservation |
reason | string | Optional | Cancellation reason |
POST /api/reservations/cancel.php HTTP/1.1
Host: govigolf.com
X-API-Key: YOUR_API_KEY
Content-Type: application/json
{
"external_ref": "EXT-2026-00500",
"reason": "Due to schedule change"
}
{
"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
}
A mechanism to notify your system of status changes for reservations you (the partner) created. All reservations made with your API key are covered, regardless of golf club (no golf_club_id needed). Notifications are sent via HTTPS POST when events occur.
reservation.created (created) / reservation.updated (content change: players, options, etc.) / reservation.status_changed (status transition) / reservation.cancelled (cancellation) / *(all events)
reservation.cancelled and reservation.status_changed:reservation.status_changed fires on every status transition (payload includes old_status / new_status). reservation.cancelled is a convenience event additionally fired when the reservation reaches a confirmed cancellation (status 10 = confirmed cancellation) — it does not fire on a cancellation request (status 6), since a request can still be rejected. So a confirmed cancellation fires both. To track cancellations from the request stage, watch reservation.status_changed for new_status=6.
status / *_status_label)| Code | Label | Description |
|---|---|---|
1 | requested | Booking requested |
2 | confirming | Confirming with golf course |
3 | payment_waiting | Awaiting payment |
4 | paid | Payment confirmed. While schedule_confirmed is false the tee time is not yet secured (payment only); true once the schedule is fixed |
5 | reschedule_requested | Reservation being updated |
6 | cancel_requested | Cancellation requested |
7 | cancelling_no_refund | Cancelling (no refund) |
8 | cancelling_with_refund | Cancelling (with refund) |
9 | completed | Completed (played) |
10 | cancelled | Completed (cancelled) |
Registers a Webhook receiving URL. Delivery scope is automatically limited to reservations you created.
| Parameter | Type | Required | Description |
|---|---|---|---|
url | string | Required | Webhook receiving URL (HTTPS required) |
events | array | Required | Event types to subscribe |
POST /api/webhooks/subscribe.php HTTP/1.1
Host: govigolf.com
X-API-Key: YOUR_API_KEY
Content-Type: application/json
{
"url": "https://your-system.com/webhook/govigo",
"events": ["reservation.created", "reservation.status_changed", "reservation.cancelled"]
}
{
"success": true,
"data": {
"subscription_id": 10,
"url": "https://your-system.com/webhook/govigo",
"events": ["reservation.created", "reservation.status_changed", "reservation.cancelled"],
"secret": "a1b2c3d4e5f6..."
},
"message": "",
"http_code": 201
}
secret is used for HMAC verification of the Webhook payload. Verify X-Webhook-Signature: sha256=... on receipt to confirm authenticity. The reservation.status_changed payload includes old_status / new_status (numeric) and old_status_label / new_status_label.
Gets the list of registered Webhooks (no parameters).
{
"success": true,
"data": {
"count": 1,
"subscriptions": [
{
"id": 10,
"url": "https://your-system.com/webhook/govigo",
"events": ["reservation.created", "reservation.status_changed"],
"is_active": true,
"created_at": "2026-03-01 10:00:00",
"updated_at": "2026-03-01 10:00:00"
}
]
},
"message": "",
"http_code": 200
}
Unsubscribes a registered Webhook.
| Parameter | Type | Required | Description |
|---|---|---|---|
subscription_id | integer | Required | Subscription ID to unsubscribe |
POST /api/webhooks/unsubscribe.php HTTP/1.1
Host: govigolf.com
X-API-Key: YOUR_API_KEY
Content-Type: application/json
{ "subscription_id": 10 }
Sends a test notification to a registered Webhook to verify URL connectivity and signature verification.
| Parameter | Type | Required | Description |
|---|---|---|---|
subscription_id | integer | Required | Subscription ID to test |
POST /api/webhooks/test.php HTTP/1.1
Host: govigolf.com
X-API-Key: YOUR_API_KEY
Content-Type: application/json
{ "subscription_id": 10 }