Developer Integration Guide
Connect Your App to
VectorGOLF.ai
Send scorecard and shot-level data from any golf app directly into a golfer's VectorGOLF.ai account. One endpoint, one token — unlock Strokes Gained, proximity analytics, and pro-grade insights.
Partner brief (Tangent & similar): golfer connection journey — when to open Vector sign-in, how import tokens work with in-app Post to Vector, and recommended URLs — is documented for your team at docs/integrations/tangent-vector-connect-golfer-journey.md (GitHub).
How It Works
Golfer Creates Token
On the VectorGOLF.ai dashboard, under Player Profile → API Access
Golfer Shares Token
Pastes the token into your app's "Connect to Vector" settings
Your App POSTs Data
After each round, POST the scorecard JSON to our API
Round in Vector
The round appears instantly in their Scorecard & Teebox Insights
No OAuth required. The import token system is designed for simplicity. A golfer generates a personal token, shares it with your app, and you use it as a Bearer token. That's it.
PRO at no charge for integration-sourced rounds. Authenticated POST of round JSON to the scorecards API with Authorization: Bearer ${VECTOR_IMPORT_TOKEN} (full import token from the dashboard; value always begins vg_imp_) qualifies the round; that golfer gets VECTOR GOLF ANALYTICS /// PRO at no subscription cost (Strokes Gained, teebox, proximity, AI plans). Partners get co-branding and enhanced AI analytics on JSON submissions. Offer runs 6–12 months while VectorGOLF.ai scales as a performance hub.
For Golfers
How to generate a token and connect your favorite app.
Sign in to your dashboard
Go to vectorgolf.ai/dashboard and sign in with your Google account.
Open Player Profile → API Access & Integrations
Scroll down past your profile info and billing. You'll see the API Access & Integrations section. Click to expand it.
Name your token and generate it
Type a label like "Tangent" or "TheGrint" so you know which app it's for. Click Generate Token.
Copy the token immediately
A yellow banner will show your full token starting with vg_imp_. Copy it now — it will not be shown again for security.
Paste into your partner app
Open your golf app (Tangent, TheGrint, Hole19, etc.) and find their "Connect to VectorGOLF" or "Import Token" setting. Paste the token there. After each round, the app will automatically send your scorecard to Vector.
Security note: Treat your import token like a password. Anyone who has it can add scorecards to your account. You can revoke a token at any time from the same API Access section — it's instant and permanent.
Token Management
5
Max active tokens
100/day
Requests per token
Instant
Revoke anytime
For Developers & Partners
Everything you need to build a "Send to Vector" feature in your app.
Endpoint
| Content-Type | application/json |
| Authorization | Bearer + one space + the golfer’s full import token (always starts with vg_imp_) |
| Success | 201 { "success": true, "round_id": 123 } |
| Errors | 401 invalid token | 400 bad JSON | 403 scope denied |
How to set Authorization. The golfer copies one long string from Dashboard → Player Profile → API Access & Integrations. That entire string is the token—send it verbatim after Bearer (include the vg_imp_ prefix). Omitting the prefix or pasting only part of the string returns 401. In the cURL example below, that full string is stored in VECTOR_IMPORT_TOKEN.
JSON Body
{
"course_name": "Wildhorse Golf Club",
"course_id": 45, // optional — Vector course ID
"tee_id": 112, // optional — Vector tee ID
"played_at": "2026-04-08",
"total_score": 82,
"total_par": 72,
"front_nine_score": 40,
"back_nine_score": 42,
"round_type": "course", // "course" or "simulator"
"holes": [
{
"hole_num": 1,
"par": 4,
"score": 5,
"putts": 2,
"gir": 0,
"fairway_hit": 1
},
// ... holes 2–18
],
"shots": [ // shot-level data — highly recommended
{
"hole_num": 1, "shot_index": 1,
"club": "Driver", "start_lie": "Tee",
"distance_to_target_y": 405, "proximity_y": 140
}
// ... all shots for holes 1–18
]
}
Round Fields
| Field | Type | Required | Notes |
|---|---|---|---|
course_name |
string | Yes | Full course name as displayed to the golfer |
course_id |
number | No | VectorGOLF course ID for precise tee matching |
tee_id |
number | No | VectorGOLF tee ID (slope/rating/yardage auto-fill) |
played_at |
string | Yes | ISO date YYYY-MM-DD |
total_score |
number | Yes | Gross strokes for the round |
total_par |
number | Yes | Course par (typically 72) |
front_nine_score |
number | No | Auto-computed from holes if omitted |
back_nine_score |
number | No | Auto-computed from holes if omitted |
round_type |
string | No | "course" (default) or "simulator" |
holes |
array | Yes | 1–18 hole objects (see below) |
Hole Fields
| Field | Type | Notes |
|---|---|---|
hole_num |
number | 1–18 |
par |
number | 3, 4, or 5 |
score |
number | null | Strokes on this hole |
putts |
number | null | Number of putts |
gir |
0 | 1 | null | Green in regulation (can be derived from score/putts/par) |
fairway_hit |
0 | 1 | null | Par 4/5 tee shots only |
fairway_miss |
"L" | "R" | null | Direction of miss (when fairway_hit = 0) |
Shot-Level Data Recommended
This is what makes the integration truly valuable.
If your app tracks GPS coordinates, club selection, or shot-by-shot data on the course, including shots[] in your payload unlocks the full power of VectorGOLF analytics:
Strokes Gained
vs Pro
Proximity
to Pin
Club Selection
Analytics
SG Breakdown
by Lie
Without shot data, the golfer still gets scorecard history and basic stats. With shot data, they get professional-grade analytics — Strokes Gained breakdowns, approach proximity trends, club distance tracking, and AI-powered improvement zones.
Each shot is an object in the shots[] array. The only required fields per shot are hole_num and shot_index — everything else can be null or omitted. Send whatever your app captures.
Example: shots with a few key fields
"shots": [ { "hole_num": 1, "shot_index": 1, "club": "Driver", "start_lie": "Tee", "end_lie": "Fairway", "distance_to_target_y": 405, "distance_traveled_y": 265, "proximity_y": 140, "strokes_gained_pro": 0.12 }, { "hole_num": 1, "shot_index": 2, "club": "8 Iron", "start_lie": "Fairway", "end_lie": "Green", "distance_to_target_y": 140, "proximity_y": 18, "strokes_gained_pro": 0.35 }, // ... all shots for holes 1–18 ]
Full Shot Field Reference
Send any combination of these fields per shot. The more you include, the richer the analytics.
| Field | Type | Notes |
|---|---|---|
hole_num |
number | Required — 1–18 |
shot_index |
number | Required — Shot order within the hole (1, 2, 3…) |
club |
string | null | Club used (e.g. "Driver", "7 Iron", "56° Wedge") |
club_type |
string | null | Category: "Driver", "Wood", "Iron", "Wedge", "Putter" |
club_brand |
string | null | Manufacturer (e.g. "Titleist", "Callaway") |
club_model |
string | null | Specific model (e.g. "TSR3", "Paradym") |
start_lie |
string | null | "Tee", "Fairway", "Rough", "Sand", "Green", etc. |
end_lie |
string | null | Lie after the shot |
distance_to_target_y |
number | null | Distance to target before the shot (yards) |
distance_traveled_y |
number | null | How far the ball traveled (yards) |
proximity_y |
number | null | Remaining distance to target after the shot (yards) |
strokes_gained_pro |
number | null | SG vs PGA Tour baseline for this shot |
quality |
string | null | Shot quality rating (app-defined) |
shot_quality |
string | null | Alternate quality field |
category |
string | null | Shot category (app-defined) |
shape |
string | null | Shot shape: "Draw", "Fade", "Straight", etc. |
trajectory |
string | null | Ball flight trajectory: "High", "Mid", "Low" |
degrees_offline |
number | null | Degrees offline from target line |
penalties |
number | null | Penalty strokes on this shot |
committed |
number | null | Whether golfer committed to the shot (app-defined) |
wind_bearing |
number | null | Wind direction in degrees |
wind_strength |
number | null | Wind speed |
elevation_ft |
number | null | Elevation change in feet |
hole_par |
number | null | Par for this hole (redundant with holes[] but accepted) |
hole_score |
number | null | Final score for this hole |
hole_length_y |
number | null | Hole length in yards (from tee played) |
hole_putts |
number | null | Putts on this hole |
hole_putts_ft |
number | null | Total putt distance in feet for this hole |
hole_handicap |
number | null | Handicap stroke index for this hole |
rating |
number | null | Course rating from tee played |
slope |
number | null | Slope rating from tee played |
tee_name |
string | null | Tee name (e.g. "Blue", "White") |
tee_color |
string | null | Tee color |
tee_number |
number | null | Tee ordinal (1 = longest, etc.) |
golfer |
string | null | Golfer name (from export) |
date_played |
string | null | Date (redundant with round played_at but accepted) |
course |
string | null | Course name per shot row |
round_type |
string | null | Round type per shot row |
round_par |
number | null | Course par per shot row |
round_score |
number | null | Total round score per shot row |
coach_tags |
string | null | Coach-assigned tags (comma-separated) |
High-value fields for most GPS apps: club, start_lie, end_lie, distance_to_target_y, distance_traveled_y, proximity_y, and strokes_gained_pro. If you can only send a few fields beyond hole_num / shot_index, prioritize these — they power the majority of Vector's advanced analytics.
Quick Test with cURL
Set an environment variable (example: VECTOR_IMPORT_TOKEN) to the full token (starts with vg_imp_), then run curl:
# Paste the complete token from the golfer's dashboard (must include vg_imp_) export VECTOR_IMPORT_TOKEN='vg_imp_your_complete_token_from_dashboard' curl -X POST https://api.vectorgolf.ai/scorecards \ -H "Authorization: Bearer ${VECTOR_IMPORT_TOKEN}" \ -H "Content-Type: application/json" \ -d '{ "course_name": "Test Golf Club", "played_at": "2026-04-08", "total_score": 82, "total_par": 72, "round_type": "course", "holes": [ {"hole_num":1,"par":4,"score":5,"putts":2,"gir":0,"fairway_hit":1}, {"hole_num":2,"par":3,"score":3,"putts":1,"gir":1}, {"hole_num":3,"par":5,"score":6,"putts":2,"gir":0,"fairway_hit":1}, {"hole_num":4,"par":4,"score":4,"putts":2,"gir":1,"fairway_hit":1}, {"hole_num":5,"par":4,"score":5,"putts":2,"gir":0,"fairway_hit":0}, {"hole_num":6,"par":3,"score":4,"putts":2,"gir":0}, {"hole_num":7,"par":4,"score":4,"putts":1,"gir":1,"fairway_hit":1}, {"hole_num":8,"par":5,"score":5,"putts":2,"gir":1,"fairway_hit":1}, {"hole_num":9,"par":4,"score":4,"putts":2,"gir":1,"fairway_hit":1}, {"hole_num":10,"par":4,"score":5,"putts":2,"gir":0,"fairway_hit":0}, {"hole_num":11,"par":3,"score":3,"putts":2,"gir":1}, {"hole_num":12,"par":5,"score":6,"putts":2,"gir":0,"fairway_hit":1}, {"hole_num":13,"par":4,"score":4,"putts":1,"gir":1,"fairway_hit":1}, {"hole_num":14,"par":4,"score":5,"putts":2,"gir":0,"fairway_hit":0}, {"hole_num":15,"par":3,"score":4,"putts":2,"gir":0}, {"hole_num":16,"par":4,"score":4,"putts":2,"gir":1,"fairway_hit":1}, {"hole_num":17,"par":5,"score":5,"putts":2,"gir":1,"fairway_hit":1}, {"hole_num":18,"par":4,"score":5,"putts":2,"gir":0,"fairway_hit":0} ] }' # Expected: 201 { "success": true, "round_id": ... }
Example: iOS (Swift)
Step-by-step for a Send to VectorGOLF flow in a native iOS app: Keychain storage for the import token, Codable models that match this guide’s JSON keys, URLSession POST, parsing round_id on 201, lie mapping, deduplication, and error UX (401 / 429 / 400).
- Store the golfer’s full import token (starts with
vg_imp_) in the Keychain, not UserDefaults. - Send
Authorization: Bearer <full token>— same value you would put inVECTOR_IMPORT_TOKENfor shell tests. - On
201, decoderound_idfor deep links and to avoid double-posting the same round. - Map your lie labels to the strings Vector expects (see shot field table above); include
shots[]when you have club and distance data.
Error Handling
| Status | Meaning | Action |
|---|---|---|
201 |
Round created | Show success. Use round_id to deep-link. |
400 |
Invalid JSON / missing required fields | Check your JSON payload structure. |
401 |
Invalid or revoked token | Ask user to check/regenerate their token on the VectorGOLF.ai dashboard. |
403 |
Scope denied (import tokens are POST-only) | Import tokens only allow creating scorecards, not reading. |
429 |
Rate limit exceeded | Max 100 requests per token per day. Resets at midnight UTC. Back off and retry tomorrow. |
500 |
Server error | Retry with backoff. Contact [email protected] if persistent. |
Best Practices
Avoid Duplicates
Each POST creates a new round. Deduplicate on your side (e.g. track sent round_ids per user). Vector does not reject duplicate rounds.
Course Matching
If you know the Vector course_id and tee_id, include them for precise slope/rating auto-fill. Otherwise, just send course_name and Vector will fuzzy-match.
Send All 18 Holes
Always send 18 hole objects. If the golfer didn't finish, set score: null for unplayed holes. Partial rounds (9 holes) are supported — send holes 1–9 or 10–18.
Token Storage
Store the user's import token encrypted or in your app's secure credential store. Never log it or expose it in client-side code.
What the Golfer Gets
Every round you POST automatically unlocks these features in their VectorGOLF.ai dashboard:
With scorecard data (holes[])
Scorecard Insights
Round history, trends, GIR/FIR, putts per round
Teebox Insights
Net scoring analysis per tee, tee-fit scale
Improvement Zones
Strokes lost analysis, AI-generated drills
Round Recap Email
Automated email with hole-by-hole breakdown
+ With shot data (shots[]) — the real value
Strokes Gained vs Pro
Full SG breakdown: Tee, Approach, Short Game, Putting
Proximity Analytics
Distance to pin by club, lie, and distance band
Club Analytics
Avg distance, dispersion, and selection patterns per club
Detailed Round Recap
Shot-by-shot narrative with lie transitions and decisions
Ready to Integrate?
Add a "Send to VectorGOLF" button to your app. Include shot-level data and your users get Strokes Gained, proximity analytics, club selection trends, and AI-powered improvement — analytics that keep them coming back to both your app and Vector.
Custom integrations. Beyond the standard JSON POST and import-token flow, we can work with your product and engineering teams on approaches tailored to your organization—batch or backfill pipelines, alternative auth boundaries, staging reviews, or other constraints. Email [email protected] with a short summary of your environment and timeline.