/api/v1/*). It is not the MCP integration used for ChatGPT and Claude connectors.
What you get
| Piece | Value |
|---|---|
| API base | https://external.vin.gs/api |
| OpenAPI | https://external.vin.gs/api/openapi.json |
| OAuth discovery | GET /api/.well-known/oauth-protected-resource |
| Vings setup | Dashboard → Settings → Integrations → Personal dashboards |
Reference implementation
Use this public v0 example app as the canonical pattern for OAuth (PKCE), httpOnly cookies, and server-side API proxies: github.com/chriskrogh/v0-personal-finance-dashboard When building with v0 or Lovable, point your agent at that repository (and this guide) instead of inventing routes or field names from scratch.Setup steps
- Deploy or preview your app with a fixed HTTPS callback path (for example
https://your-app.vercel.app/auth/callback). Localhost callbacks are also allowed when registered. - Copy the callback URL from your deployment (scheme, host, path, and trailing slash must match exactly).
- In Vings, open Settings → Integrations → Personal dashboards, choose Add dashboard, paste the redirect URI, and copy the Client ID.
- Set
VINGS_OAUTH_CLIENT_IDin your project to that Client ID. - Discover OAuth endpoints — fetch
GET /api/.well-known/oauth-protected-resource, thenGET {authorization_servers[0]}/.well-known/openid-configuration. Useauthorization_endpointandtoken_endpointfrom that JSON (see OAuth flow). - PKCE sign-in — public client, S256
code_challenge, scopesopenid email profile; exchange the authorization code at the token endpoint withcode_verifier(no client secret). After login, approve access on/oauth/consent. - Proxy API calls server-side — browser apps cannot call
external.vin.gsdirectly (CORS). Add Next.js (or similar) routes that attach the bearer token, then call your proxy from the client. - Verify —
GET /v1/methrough your proxy. Use REST only — not MCP.
Give your builder the right instructions
On the Personal dashboards settings page, open the v0 or Lovable tab and copy the platform-specific agent prompt into your project instructions or project knowledge. It links back to this guide and the live OpenAPI document so the agent does not invent endpoints or use MCP by mistake.v0
- Paste the v0 agent prompt into project instructions before scaffolding OAuth.
- Add the reference implementation repo URL so the agent can mirror working routes.
- Add
VINGS_OAUTH_CLIENT_IDin the project environment. - If your preview or production URL changes, update the redirect URI in Vings and redeploy.
Lovable
- Paste the Lovable agent prompt into project knowledge so generated code uses REST, not MCP.
- Link the reference implementation in project knowledge.
- Set
VINGS_OAUTH_CLIENT_IDin env or secrets. - Re-check the redirect URI in Vings when Lovable changes your published domain.
OAuth flow
Common mistake: Personal dashboard OAuth uses the Vings authorize proxy atExpected URL shapes after discovery:/api/oauth/authorizeand Supabase/auth/v1/oauth/token. Do not use/auth/v1/authorizeor/auth/v1/token— those are for Google/Apple social sign-in and return errors likeUnsupported provider: Provider could not be found.
authorization_endpoint and token_endpoint values from OpenID discovery rather than guessing paths.
Discovery
Step 1 — protected resource metadata:authorization_servers[0] as the external host (for example https://external.vin.gs), not the Supabase /auth/v1 path.
Step 2 — OpenID configuration:
authorization_endpoint and token_endpoint fields from that JSON verbatim in your authorize and token requests. authorization_endpoint is the Vings authorize proxy (/api/oauth/authorize); token_endpoint remains on Supabase Auth.
Manual fallback (only if you cannot read discovery): use https://external.vin.gs/api/oauth/authorize for authorize and the Supabase …/auth/v1/oauth/token URL for token exchange. Do not append bare /authorize or /token to Supabase.
Public client + PKCE
Vings registers personal dashboards as public clients withtoken_endpoint_auth_method: none. Your app must:
- Generate a
code_verifier/code_challenge(S256) for each sign-in. - Send
client_id,redirect_uri,response_type=code,code_challenge, andcode_challenge_method=S256to the OAuth authorization endpoint. - Exchange the authorization code at the token endpoint with
code_verifier(no client secret).
openid, email, profile). Vings data scopes (for example portfolio:read, transactions:read) are attached to the OAuth client when you create it in settings and are shown on the Vings consent screen at /oauth/consent.
Consent
After the user signs in to Vings (if needed), they approve readonly access on the Vings consent page. If someone other than the client owner tries to authorize a personal dashboard client, consent is rejected.Cookie configuration
Store OAuthstate and code_verifier in httpOnly cookies before redirecting to the authorization endpoint. After token exchange, store the access token the same way (not localStorage in production).
httpOnly: true— tokens are not readable from client JavaScript.sameSite: "lax"(not"strict") — cookies are sent when the identity provider redirects back to your callback.secure: truein production (HTTPS).- Short
maxAgefor state and verifier (for example 10 minutes).
/auth/login, /auth/callback, and cookie names).
Server-side API proxy (required for browser apps)
The External API does not allow direct browser requests (CORS). Every dashboard UI should call your own server routes, which forward requests withAuthorization: Bearer <access_token>.
- Create one route file per upstream endpoint (for example
/api/vings/me,/api/vings/transactions). - Read the access token from your httpOnly cookie on the server, then
fetchhttps://external.vin.gs/api/.... - Do not use catch-all proxy routes like
app/api/vings/[...path]/route.ts— they can return 404 or behave inconsistently on some hosts. - From the browser, call only your
/api/vings/*routes — neverhttps://external.vin.gs/api/...directly.
Call the API (server or curl)
GET /v1/me, then add endpoints for the widgets you need. See Authentication for scopes and Introduction for the full REST list.
Example (after you have a token):
Common field names
The OpenAPI spec is authoritative. These names differ from what many agents guess:| You might expect | Actual name / notes |
|---|---|
amount | amount_cents (integer; divide by 100 for display) |
limit | pageSize (query param on GET /v1/transactions) |
merchant | merchant_name on transaction objects |
description | title on transaction objects |
is_income | type — "INCOME" or "EXPENSE" (also INTERNAL_TRANSFER) |
category | category — uppercase enum (for example GROCERIES, RENT) |
| Money totals | Object { amount_cents, currency } (not a bare number) |
| Next page | pagination.nextCursor and pagination.hasMore (not page numbers) |
| Min/max filters | minAmountCents, maxAmountCents (query params, in cents) |
| Merchant search | merchantQuery (query param, not merchant) |
Suggested dashboard endpoints
| Goal | Endpoint |
|---|---|
| Sanity check | GET /v1/me |
| Net worth / holdings | GET /v1/portfolio, GET /v1/portfolio/history |
| Spending overview | GET /v1/spending/summary, GET /v1/spending/categories |
| Recent activity | GET /v1/transactions |
| Budget progress | GET /v1/budgets/summary |
| Cashflow | GET /v1/cashflow/summary |
Prototype without OAuth (optional)
For local experiments only, you can use a personal access token (vng_pat_*) created under Settings → Integrations → API keys. PATs are for scripts and quick tests—not for shipping a multi-user hosted dashboard. Production personal apps should use OAuth.
Security practices
- Treat the access token like a password; never commit it or pass it in URLs.
- Responses are user-specific and use
Cache-Control: no-store. - The External API is read-only; no writes or bank linking through this API.
- Revoke the OAuth client in Vings settings to cut off access immediately.
- Do not use MCP (
/api/mcp) for v0/Lovable dashboard UIs unless you are explicitly building an MCP host.
Troubleshooting
| Symptom | What to check |
|---|---|
Unsupported provider: Provider could not be found | Wrong authorize URL — use OIDC authorization_endpoint (/auth/v1/oauth/authorize), not /auth/v1/authorize. |
redirect_uri mismatch | Redirect in the authorize request must exactly match a URI registered on the personal dashboard client. |
| Consent says the app is private | Personal dashboards only work for the Vings user who created the client. |
401 on API calls | Missing or expired bearer token; repeat sign-in. |
| Empty portfolio or spending | User may have no linked accounts yet; handle empty states in the UI. |
| CORS errors from the browser | Do not call external.vin.gs from the browser — add server-side proxy routes (see reference implementation). |
| Callback route returns 404 | Use explicit API route files per endpoint, not catch-all [...path] proxies (see example repo). |
| State mismatch on callback | OAuth cookies lost on redirect — use sameSite: "lax", httpOnly: true, and secure: true in production. |
amount is undefined in UI | Response field is amount_cents; divide by 100 for dollars. |
| No transactions returned | Query param is pageSize, not limit — e.g. GET /v1/transactions?pageSize=5. |