Skip to main content
RapidDev - Software Development Agency
bolt-ai-integrationsBolt Chat + API Route

How to Integrate Bolt.new with Withings

To integrate Withings with Bolt.new, register a partner app at account.withings.com, implement OAuth 2.0 through a Next.js API route (requires a deployed URL for the redirect), then fetch body measurements, heart rate, and sleep data via Withings Health API v2. All communication uses HTTPS — fully compatible with Bolt's WebContainer runtime. Deploy to Netlify or Bolt Cloud before testing the full OAuth flow.

What you'll learn

  • How to register a Withings partner app and configure the OAuth redirect URI for your deployed domain
  • How to implement the Withings OAuth 2.0 authorization code flow in Next.js API routes
  • How to fetch body measurements, heart rate, and sleep analysis data from the Withings Health API v2
  • How to build a health monitoring dashboard displaying data from Withings devices
  • Why OAuth redirect flows require deployment and cannot be tested in Bolt's WebContainer preview
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate18 min read30 minutesOtherApril 2026RapidDev Engineering Team
TL;DR

To integrate Withings with Bolt.new, register a partner app at account.withings.com, implement OAuth 2.0 through a Next.js API route (requires a deployed URL for the redirect), then fetch body measurements, heart rate, and sleep data via Withings Health API v2. All communication uses HTTPS — fully compatible with Bolt's WebContainer runtime. Deploy to Netlify or Bolt Cloud before testing the full OAuth flow.

Building a Connected Health Dashboard with Withings in Bolt.new

Withings makes some of the most medically credible consumer health devices on the market — Body+ smart scales that measure body composition, BPM Core blood pressure monitors with ECG, and Sleep Analyzer mats that track sleep stages, heart rate, and breathing disturbances. The Withings Health API v2 exposes all of this data through a REST interface over HTTPS, making it architecturally compatible with Bolt's WebContainer runtime. You can build a personal health dashboard, a wellness app, or a fitness tracking tool that pulls data from a user's connected Withings ecosystem.

The critical architectural decision is OAuth 2.0. Withings requires users to authorize your app through their account portal, which redirects back to your application via a callback URL. That redirect URL must be publicly accessible — a localhost address or the ephemeral WebContainer preview URL will not work because Withings won't accept dynamic URLs as registered redirect URIs. The standard workflow is to deploy your app to Netlify or Bolt Cloud first, register your deployed domain as the redirect URI in the Withings developer dashboard, and then test the full OAuth flow on your deployed site.

The Withings API rate limit is 120 requests per minute, which is generous enough for most health dashboards that fetch data on page load or on a scheduled basis. Data categories include body measurements (weight, body fat, muscle mass, bone mass, water percentage), cardiovascular metrics (heart rate, blood pressure, ECG readings), sleep analysis (duration, sleep stages, breathing disturbances, snoring index), and activity data (steps, calories, distance, elevation). Each category maps to a different API measure type code, which you specify in your API request parameters.

Integration method

Bolt Chat + API Route

Withings uses OAuth 2.0 for authorization, which requires a publicly accessible redirect URL — meaning the full flow cannot be tested inside Bolt's WebContainer preview. You build Next.js API routes that handle the OAuth exchange server-side, store tokens securely, and proxy all Withings Health API v2 requests. Once deployed to Netlify or Bolt Cloud, the OAuth redirect resolves correctly and your health dashboard receives real data from connected devices.

Prerequisites

  • A Withings developer account at account.withings.com/partner/dashboard — free to create
  • A deployed Bolt.new app URL (Netlify or Bolt Cloud) to use as the OAuth redirect URI — the preview URL will not work
  • Your Withings app Client ID and Consumer Secret from the developer dashboard
  • A Bolt.new project using Next.js (for server-side API routes to handle OAuth securely)
  • A Withings device paired to a test account for verifying real data during development

Step-by-step guide

1

Register a Withings Partner App and Get API Credentials

Go to account.withings.com/partner/dashboard and sign in with your Withings account. Click 'Create Application' and fill in the form: give your app a name, set the application type to 'Non-commercial personal use' for development or 'Application' for production. The most important field is the 'Callback URL' — this must be a publicly accessible URL where Withings will redirect users after authorization. For now, enter a placeholder like https://your-app.netlify.app/api/withings/callback and you will update it after deploying. Accept the terms and submit. After creation, Withings shows you your Client ID and Consumer Secret on the application detail page. Copy both values immediately — the Consumer Secret will not be shown again in full. Create a .env file in your Bolt project root and add them as WITHINGS_CLIENT_ID and WITHINGS_CLIENT_SECRET. Also add WITHINGS_REDIRECT_URI matching exactly what you registered. Withings differentiates between a 'development' and 'production' application. Development apps only allow accounts that you explicitly whitelist under 'Authorized Users' — ideal for testing with your own Withings devices before submitting for production review. If you only see your own data during testing, this is expected and correct behavior.

Bolt.new Prompt

Set up a Next.js project for a Withings health dashboard. Create a .env.local file with placeholder values for WITHINGS_CLIENT_ID, WITHINGS_CLIENT_SECRET, and WITHINGS_REDIRECT_URI. Install axios for HTTP requests. Create a basic layout with a 'Connect Withings' button that will later initiate OAuth, and a dashboard page that shows placeholder health metric cards for weight, blood pressure, and sleep score.

Paste this in Bolt.new chat

.env.local
1# .env.local
2WITHINGS_CLIENT_ID=your_client_id_here
3WITHINGS_CLIENT_SECRET=your_consumer_secret_here
4WITHINGS_REDIRECT_URI=https://your-app.netlify.app/api/withings/callback
5NEXT_PUBLIC_APP_URL=https://your-app.netlify.app

Pro tip: Withings development apps restrict access to whitelisted accounts only. Add your personal Withings account email under 'Authorized Users' in the partner dashboard so you can test with real device data.

Expected result: You have a Withings partner application with a Client ID and Consumer Secret. Your .env file has placeholders. Your Bolt app shows a 'Connect Withings' button on the home page.

2

Build the OAuth 2.0 Authorization Flow in API Routes

Withings OAuth 2.0 uses the standard authorization code flow. You redirect the user to Withings' authorization URL with your Client ID and a scope parameter, they approve access on the Withings site, and Withings redirects back to your callback URL with a temporary code. Your server exchanges that code for a long-lived access token and refresh token. Create two Next.js API routes: one at /api/withings/auth that builds the authorization URL and redirects the user, and one at /api/withings/callback that receives the code and exchanges it for tokens. The token exchange must happen server-side using your Consumer Secret — never expose it in client code. After obtaining tokens, store them in a database (or a secure HTTP-only cookie for single-user apps) and redirect the user to your dashboard. Withings tokens expire after 3 hours. You must implement token refresh using the refresh token before the access token expires. The Withings token endpoint at https://wbsapi.withings.net/v2/oauth2 handles both the initial exchange and refresh using the action=requesttoken and action=refreshtoken parameters respectively. Build your API client to automatically detect expired tokens and refresh them transparently. IMPORTANT: This OAuth flow cannot be tested inside Bolt's WebContainer preview. The preview URL is dynamic and cannot be registered as a valid Withings redirect URI. Deploy to Netlify or Bolt Cloud first, register your deployed URL, then test the full flow on the deployed site.

Bolt.new Prompt

Create two Next.js API routes for Withings OAuth. First, create /api/withings/auth/route.ts that builds the Withings authorization URL (https://account.withings.com/oauth2_user/authorize2) with client_id, response_type=code, scope=user.metrics,user.activity,user.sleepevents, and redirect_uri from environment variables, then redirects the user there. Second, create /api/withings/callback/route.ts that receives the code parameter, POSTs to https://wbsapi.withings.net/v2/oauth2 with action=requesttoken to exchange it for access_token and refresh_token, stores them in a cookie, and redirects to /dashboard. Use TypeScript throughout.

Paste this in Bolt.new chat

app/api/withings/auth/route.ts
1// app/api/withings/auth/route.ts
2import { NextResponse } from 'next/server';
3
4export async function GET() {
5 const params = new URLSearchParams({
6 response_type: 'code',
7 client_id: process.env.WITHINGS_CLIENT_ID!,
8 scope: 'user.metrics,user.activity,user.sleepevents',
9 redirect_uri: process.env.WITHINGS_REDIRECT_URI!,
10 state: crypto.randomUUID(),
11 });
12
13 const authUrl = `https://account.withings.com/oauth2_user/authorize2?${params}`;
14 return NextResponse.redirect(authUrl);
15}
16
17// app/api/withings/callback/route.ts
18import { NextRequest, NextResponse } from 'next/server';
19
20export async function GET(request: NextRequest) {
21 const code = request.nextUrl.searchParams.get('code');
22 if (!code) return NextResponse.redirect('/error?msg=no_code');
23
24 const tokenResponse = await fetch('https://wbsapi.withings.net/v2/oauth2', {
25 method: 'POST',
26 headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
27 body: new URLSearchParams({
28 action: 'requesttoken',
29 grant_type: 'authorization_code',
30 client_id: process.env.WITHINGS_CLIENT_ID!,
31 client_secret: process.env.WITHINGS_CLIENT_SECRET!,
32 code,
33 redirect_uri: process.env.WITHINGS_REDIRECT_URI!,
34 }),
35 });
36
37 const data = await tokenResponse.json();
38 if (data.status !== 0) {
39 return NextResponse.redirect('/error?msg=token_exchange_failed');
40 }
41
42 const { access_token, refresh_token, expires_in, userid } = data.body;
43 const response = NextResponse.redirect('/dashboard');
44
45 // Store tokens in HTTP-only cookies
46 response.cookies.set('withings_access_token', access_token, {
47 httpOnly: true,
48 secure: process.env.NODE_ENV === 'production',
49 maxAge: expires_in,
50 });
51 response.cookies.set('withings_refresh_token', refresh_token, {
52 httpOnly: true,
53 secure: process.env.NODE_ENV === 'production',
54 maxAge: 60 * 60 * 24 * 30, // 30 days
55 });
56 response.cookies.set('withings_user_id', userid.toString(), {
57 httpOnly: true,
58 secure: process.env.NODE_ENV === 'production',
59 maxAge: 60 * 60 * 24 * 30,
60 });
61
62 return response;
63}

Pro tip: Withings returns a numeric status code in every API response body. Status 0 means success. Any other status indicates an error — always check data.status before reading data.body.

Expected result: Your app has two API routes: /api/withings/auth redirects to Withings' authorization page, and /api/withings/callback exchanges the code for tokens and stores them in cookies. After deploying, clicking 'Connect Withings' starts the full OAuth flow.

3

Fetch Health Measurements from the Withings API

The Withings Health API v2 uses a single measurement endpoint at https://wbsapi.withings.net/measure with different 'meastype' codes to select which data category to fetch. Body measurements (weight, BMI, body fat, muscle mass, bone mass, water) use meastype codes 1, 4, 6, 8, 76, 77 respectively. Blood pressure uses meastype 9 (diastolic) and 10 (systolic). Heart rate uses meastype 11. All requests must include the Authorization header with the user's access token as a Bearer token. Build a server-side helper function that reads the access token from the HTTP-only cookie, makes the API request, and handles token refresh if the token has expired. Wrap all data fetching in Next.js API routes — never call the Withings API directly from client components, as that would expose the access token in browser network logs. The API returns measurements grouped by date and device. Each measurement group contains a date timestamp and an array of measure objects with type, value, and unit. Withings stores values as integers with a separate unit multiplier — for example, weight 70kg might be stored as value=7000 with unit=-2, meaning 7000 × 10^(-2) = 70.00 kg. You must apply this multiplier when displaying values. The Withings API enforces a rate limit of 120 requests per minute. For a health dashboard fetching 4-5 data types on load, you are well within this limit. However, avoid polling the API on a short interval — instead fetch data once on page load and cache it client-side for the session.

Bolt.new Prompt

Create a /api/withings/measurements/route.ts API route that reads the withings_access_token from cookies, then fetches the last 30 days of body measurements from https://wbsapi.withings.net/measure with meastype 1 (weight) and 6 (body fat). Parse the Withings response format — each measure has value and unit fields where actual_value = value * (10^unit). Return an array of cleaned measurement objects with date and formatted values. Also create a /api/withings/sleep/route.ts that fetches sleep summaries from https://wbsapi.withings.net/v2/sleep using the getsummary action for the last 7 days.

Paste this in Bolt.new chat

app/api/withings/measurements/route.ts
1// app/api/withings/measurements/route.ts
2import { NextRequest, NextResponse } from 'next/server';
3
4function applyUnit(value: number, unit: number): number {
5 return value * Math.pow(10, unit);
6}
7
8export async function GET(request: NextRequest) {
9 const accessToken = request.cookies.get('withings_access_token')?.value;
10 if (!accessToken) {
11 return NextResponse.json({ error: 'Not authenticated' }, { status: 401 });
12 }
13
14 const endDate = Math.floor(Date.now() / 1000);
15 const startDate = endDate - 30 * 24 * 60 * 60; // 30 days ago
16
17 const params = new URLSearchParams({
18 action: 'getmeas',
19 meastype: '1,6,8,76', // weight, body fat, fat mass, muscle mass
20 category: '1',
21 startdate: startDate.toString(),
22 enddate: endDate.toString(),
23 });
24
25 const response = await fetch(
26 `https://wbsapi.withings.net/measure?${params}`,
27 {
28 headers: { Authorization: `Bearer ${accessToken}` },
29 }
30 );
31
32 const data = await response.json();
33
34 if (data.status !== 0) {
35 return NextResponse.json(
36 { error: 'Withings API error', status: data.status },
37 { status: 400 }
38 );
39 }
40
41 // Transform Withings format into clean measurement objects
42 const measurements = data.body.measuregrps.map((group: any) => {
43 const date = new Date(group.date * 1000).toISOString().split('T')[0];
44 const measures: Record<string, number> = {};
45
46 group.measures.forEach((m: any) => {
47 const value = applyUnit(m.value, m.unit);
48 switch (m.type) {
49 case 1: measures.weight_kg = parseFloat(value.toFixed(2)); break;
50 case 6: measures.body_fat_pct = parseFloat(value.toFixed(1)); break;
51 case 8: measures.fat_mass_kg = parseFloat(value.toFixed(2)); break;
52 case 76: measures.muscle_mass_kg = parseFloat(value.toFixed(2)); break;
53 }
54 });
55
56 return { date, ...measures };
57 });
58
59 return NextResponse.json({ measurements });
60}

Pro tip: The Withings value+unit system is easy to get wrong. Always compute actual_value = measure.value * Math.pow(10, measure.unit) before displaying. A weight stored as {value: 7532, unit: -2} is 75.32 kg, not 7532 kg.

Expected result: Your /api/withings/measurements endpoint returns a clean array of daily measurement objects with weight, body fat percentage, and muscle mass in human-readable units. The /api/withings/sleep endpoint returns weekly sleep summaries.

4

Build the Health Monitoring Dashboard UI

With your API routes returning clean data, build the front-end dashboard that visualizes health trends. A health dashboard works best with a combination of current-value metric cards (today's weight, last blood pressure reading, last night's sleep score) and trend charts showing changes over time. Use a charting library like Recharts (which ships with shadcn/ui) to render line charts for weight trends and bar charts for sleep duration. The dashboard should handle three states: unauthenticated (show 'Connect Withings' button that links to /api/withings/auth), authenticated with data (show metric cards and charts), and authenticated but no device data yet (show a helpful message explaining that the user needs to sync their Withings device with the Withings app before data appears). For the connect button state, check for the presence of the withings_user_id cookie using a /api/withings/status endpoint that returns { connected: boolean }. This avoids exposing the actual token to the client. Fetch all dashboard data in parallel using Promise.all to minimize load time — even with the 120 req/min rate limit, loading 3-4 endpoints simultaneously is well within bounds. Consider adding a manual 'Sync' button that re-fetches all data from the API. Withings devices sync to the Withings servers when connected to Wi-Fi or Bluetooth, usually within minutes of a measurement. If a user just stepped on their scale, clicking Sync should pull the newest reading within a few seconds.

Bolt.new Prompt

Build a health dashboard page at /dashboard that first checks /api/withings/status to see if the user is connected. If not connected, show a centered card with a 'Connect Your Withings Devices' button linking to /api/withings/auth. If connected, fetch measurements from /api/withings/measurements and sleep data from /api/withings/sleep in parallel. Show four metric cards: latest weight (kg), latest body fat %, last night's sleep duration (hours), and sleep efficiency %. Below the cards, add a Recharts LineChart showing weight trend over the last 30 days. Make the page responsive and use a clean, clinical design with a white background and blue accent color. Add a Refresh button that re-fetches all data.

Paste this in Bolt.new chat

app/dashboard/page.tsx
1// app/dashboard/page.tsx
2'use client';
3import { useEffect, useState } from 'react';
4import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts';
5
6interface Measurement {
7 date: string;
8 weight_kg?: number;
9 body_fat_pct?: number;
10}
11
12interface SleepSummary {
13 date: string;
14 total_sleep_minutes: number;
15 sleep_efficiency: number;
16}
17
18export default function Dashboard() {
19 const [connected, setConnected] = useState<boolean | null>(null);
20 const [measurements, setMeasurements] = useState<Measurement[]>([]);
21 const [sleep, setSleep] = useState<SleepSummary[]>([]);
22 const [loading, setLoading] = useState(true);
23
24 const fetchData = async () => {
25 setLoading(true);
26 const [status, meaRes, sleepRes] = await Promise.all([
27 fetch('/api/withings/status').then(r => r.json()),
28 fetch('/api/withings/measurements').then(r => r.json()),
29 fetch('/api/withings/sleep').then(r => r.json()),
30 ]);
31 setConnected(status.connected);
32 if (meaRes.measurements) setMeasurements(meaRes.measurements);
33 if (sleepRes.summaries) setSleep(sleepRes.summaries);
34 setLoading(false);
35 };
36
37 useEffect(() => { fetchData(); }, []);
38
39 if (!connected) {
40 return (
41 <div className="flex items-center justify-center min-h-screen">
42 <div className="text-center p-8 border rounded-lg shadow-sm">
43 <h2 className="text-2xl font-semibold mb-4">Connect Your Withings Devices</h2>
44 <p className="text-gray-500 mb-6">Authorize access to view your health data</p>
45 <a href="/api/withings/auth" className="bg-blue-600 text-white px-6 py-3 rounded-lg hover:bg-blue-700">
46 Connect Withings
47 </a>
48 </div>
49 </div>
50 );
51 }
52
53 const latest = measurements[measurements.length - 1];
54 const lastSleep = sleep[sleep.length - 1];
55
56 return (
57 <div className="max-w-4xl mx-auto p-6">
58 <div className="flex justify-between items-center mb-6">
59 <h1 className="text-2xl font-bold">Health Dashboard</h1>
60 <button onClick={fetchData} className="text-sm border px-4 py-2 rounded hover:bg-gray-50">
61 Refresh
62 </button>
63 </div>
64 <div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-8">
65 <div className="p-4 border rounded-lg">
66 <p className="text-sm text-gray-500">Weight</p>
67 <p className="text-2xl font-bold">{latest?.weight_kg ?? '--'} kg</p>
68 </div>
69 <div className="p-4 border rounded-lg">
70 <p className="text-sm text-gray-500">Body Fat</p>
71 <p className="text-2xl font-bold">{latest?.body_fat_pct ?? '--'}%</p>
72 </div>
73 <div className="p-4 border rounded-lg">
74 <p className="text-sm text-gray-500">Sleep Duration</p>
75 <p className="text-2xl font-bold">
76 {lastSleep ? `${(lastSleep.total_sleep_minutes / 60).toFixed(1)}h` : '--'}
77 </p>
78 </div>
79 <div className="p-4 border rounded-lg">
80 <p className="text-sm text-gray-500">Sleep Efficiency</p>
81 <p className="text-2xl font-bold">{lastSleep?.sleep_efficiency ?? '--'}%</p>
82 </div>
83 </div>
84 <div className="border rounded-lg p-4">
85 <h3 className="font-semibold mb-4">Weight Trend (30 days)</h3>
86 <ResponsiveContainer width="100%" height={250}>
87 <LineChart data={measurements}>
88 <CartesianGrid strokeDasharray="3 3" />
89 <XAxis dataKey="date" tick={{ fontSize: 11 }} />
90 <YAxis domain={['auto', 'auto']} />
91 <Tooltip />
92 <Line type="monotone" dataKey="weight_kg" stroke="#2563eb" dot={false} strokeWidth={2} />
93 </LineChart>
94 </ResponsiveContainer>
95 </div>
96 </div>
97 );
98}

Pro tip: Withings devices only push data after the user opens the Withings Health Mate app or the device syncs automatically via Wi-Fi. If you see empty charts after connecting, ask the user to open Withings Health Mate on their phone to trigger a sync.

Expected result: The dashboard shows metric cards for weight, body fat, sleep duration, and sleep efficiency sourced from real Withings device data. A line chart visualizes 30-day weight trends. Unauthenticated users see a Connect button that starts the OAuth flow.

5

Deploy and Register Your OAuth Redirect URI

The OAuth flow cannot complete during Bolt development because Withings requires a stable, registered redirect URI. Deploy your app first, then update your Withings developer dashboard with the deployed URL. Deploy to Netlify by connecting your Bolt project to GitHub and importing the repository in Netlify, or use Bolt's built-in publish button for Bolt Cloud. Once deployed, copy your production URL (e.g., https://your-app.netlify.app). Go back to account.withings.com/partner/dashboard, open your application settings, and update the Callback URL to https://your-app.netlify.app/api/withings/callback. Add your environment variables in the Netlify dashboard under Site configuration → Environment variables: WITHINGS_CLIENT_ID, WITHINGS_CLIENT_SECRET, WITHINGS_REDIRECT_URI (matching exactly what you registered), and NEXT_PUBLIC_APP_URL. Trigger a new deployment after adding environment variables so they are picked up by the build. Test the full flow: visit your deployed app, click 'Connect Withings', complete the authorization on the Withings site, and verify that you land back on /dashboard with real data. If you see a redirect error from Withings, double-check that the WITHINGS_REDIRECT_URI environment variable matches the callback URL registered in the developer dashboard exactly — including trailing slashes and https vs http.

Bolt.new Prompt

Add a /api/withings/status/route.ts endpoint that checks if the withings_access_token cookie exists and returns { connected: true } or { connected: false }. Also add a /api/withings/disconnect/route.ts that clears all withings_* cookies and redirects to the home page. Add a 'Disconnect Withings' button to the dashboard that calls this endpoint.

Paste this in Bolt.new chat

app/api/withings/status/route.ts
1// app/api/withings/status/route.ts
2import { NextRequest, NextResponse } from 'next/server';
3
4export async function GET(request: NextRequest) {
5 const token = request.cookies.get('withings_access_token')?.value;
6 return NextResponse.json({ connected: !!token });
7}
8
9// app/api/withings/disconnect/route.ts
10import { NextResponse } from 'next/server';
11
12export async function GET() {
13 const response = NextResponse.redirect('/');
14 response.cookies.delete('withings_access_token');
15 response.cookies.delete('withings_refresh_token');
16 response.cookies.delete('withings_user_id');
17 return response;
18}

Pro tip: When updating the redirect URI in the Withings developer dashboard, the new URL takes effect immediately. You do not need to wait for propagation — but you do need to trigger a new Netlify deploy after updating environment variables.

Expected result: The complete OAuth flow works end-to-end on your deployed site. Users can connect their Withings account, view their health data, and disconnect. Your deployed URL is registered as the redirect URI in the Withings developer dashboard.

Common use cases

Personal Health Monitoring Dashboard

Build a dashboard that pulls weight trends, body composition, blood pressure readings, and sleep quality metrics from a user's Withings devices. Display data as charts showing changes over time — ideal for personal wellness tracking or health coaching apps.

Bolt.new Prompt

Create a health monitoring dashboard that connects to Withings via OAuth 2.0. After the user authorizes access, fetch their body measurements (weight, body fat percentage) from the last 30 days using the Withings Health API v2 getmeas endpoint. Display weight as a line chart and body fat as a gauge. Add a blood pressure section showing systolic/diastolic readings. Use Next.js API routes to handle the OAuth flow and all API calls. Store the access token in a secure HTTP-only cookie.

Copy this prompt to try it in Bolt.new

Sleep Quality Analyzer

Create a sleep tracking page that fetches Withings sleep analysis data including total sleep duration, sleep stages (light, deep, REM), heart rate during sleep, and breathing disturbances. Visualize weekly sleep patterns to help users understand their sleep quality.

Bolt.new Prompt

Build a sleep analysis page using Withings API. After OAuth authorization, call the Withings sleep summary endpoint to get last 7 days of sleep data including duration, sleep efficiency, deep sleep percentage, and breathing disturbance index. Create a weekly bar chart showing sleep duration with color-coded sleep quality scores. Show average heart rate during sleep as a metric card. Handle token refresh automatically when the access token expires.

Copy this prompt to try it in Bolt.new

Family Health Hub

Allow multiple family members to connect their individual Withings accounts and view consolidated health metrics in one app. Each family member's data is fetched using their own OAuth tokens stored against their user ID.

Bolt.new Prompt

Create a family health hub where multiple users can each connect their own Withings account. Build an onboarding flow that initiates Withings OAuth for each family member and stores their individual access tokens in the database linked to their user ID. Show a family overview page with each person's latest weight, most recent blood pressure reading, and last night's sleep score as summary cards. Include individual detail views. Use Next.js API routes to fetch data for the currently logged-in user.

Copy this prompt to try it in Bolt.new

Troubleshooting

Withings returns error code 401 (invalid_token) after the app has been running for a few hours

Cause: Withings access tokens expire after 3 hours. The stored token in the cookie is no longer valid, and your app is not automatically refreshing it before making API calls.

Solution: Implement a token refresh API route that POSTs to the Withings OAuth2 endpoint with action=requesttoken and grant_type=refresh_token. Call this route from a middleware that runs before any Withings API request, checking whether the token will expire within the next 10 minutes.

typescript
1// app/api/withings/refresh/route.ts
2import { NextRequest, NextResponse } from 'next/server';
3
4export async function POST(request: NextRequest) {
5 const refreshToken = request.cookies.get('withings_refresh_token')?.value;
6 if (!refreshToken) return NextResponse.json({ error: 'No refresh token' }, { status: 401 });
7
8 const response = await fetch('https://wbsapi.withings.net/v2/oauth2', {
9 method: 'POST',
10 headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
11 body: new URLSearchParams({
12 action: 'requesttoken',
13 grant_type: 'refresh_token',
14 client_id: process.env.WITHINGS_CLIENT_ID!,
15 client_secret: process.env.WITHINGS_CLIENT_SECRET!,
16 refresh_token: refreshToken,
17 }),
18 });
19
20 const data = await response.json();
21 if (data.status !== 0) return NextResponse.json({ error: 'Refresh failed' }, { status: 401 });
22
23 const res = NextResponse.json({ ok: true });
24 res.cookies.set('withings_access_token', data.body.access_token, { httpOnly: true, maxAge: data.body.expires_in });
25 return res;
26}

OAuth redirect fails with 'Invalid redirect URI' error on the Withings authorization page

Cause: The redirect URI passed in the authorization request does not exactly match the one registered in the Withings developer dashboard. Even a trailing slash difference or http vs https mismatch causes rejection.

Solution: Go to account.withings.com/partner/dashboard, open your application, and copy the exact Callback URL. Update your WITHINGS_REDIRECT_URI environment variable to match it precisely, including protocol and path. Redeploy your app after changing the variable.

Dashboard shows empty charts and '--' metric values even though the user authorized successfully

Cause: Withings device data is only available after the device syncs with Withings' servers. The device syncs when the user opens the Withings Health Mate app or the device connects to its configured Wi-Fi network. Newly authorized accounts with devices that haven't synced recently return empty measurement groups.

Solution: Add a helpful message on the dashboard when measurement arrays are empty, instructing the user to open the Withings Health Mate app on their phone to trigger a sync. Also verify in the Withings developer dashboard that your app has been granted access to the correct data scopes (user.metrics for measurements, user.sleepevents for sleep).

OAuth flow works locally in testing but not in the Bolt preview window

Cause: Bolt's WebContainer preview uses a dynamic URL that cannot be registered as a valid Withings redirect URI. Withings rejects redirect URIs that are not pre-registered in the developer dashboard.

Solution: This is expected behavior. Deploy your app to Netlify or Bolt Cloud, register your deployed URL as the redirect URI, and test the OAuth flow on the deployed site. Do not expect OAuth to work in the Bolt preview — it is architecturally incompatible with dynamic preview URLs.

Best practices

  • Always store Withings OAuth tokens in HTTP-only cookies or server-side sessions — never in localStorage or client-accessible cookies, as tokens grant full access to the user's medical-grade health data.
  • Implement automatic token refresh before every API call rather than waiting for a 401 error — check if the token expires within the next 10 minutes and refresh proactively.
  • Cache Withings API responses client-side for the duration of the user session to minimize API calls and stay well under the 120 requests/minute rate limit.
  • Always check data.status in every Withings API response before reading data.body — status 0 means success, any other value is an error code documented in the Withings developer reference.
  • Apply the value × 10^unit formula to all Withings measurements before displaying — never display the raw integer values which use a compact encoding with a separate unit multiplier.
  • Register your production domain as the OAuth redirect URI from day one — develop with awareness that the full OAuth flow requires deployment, and use Withings test credentials (mock data) for unit testing.
  • Request only the OAuth scopes your app actually needs (user.metrics, user.activity, user.sleepevents) rather than requesting all scopes — users are more likely to authorize apps that request minimal access to their health data.

Alternatives

Frequently asked questions

Can I use Withings API in Bolt.new during development without deploying first?

Outbound API calls to the Withings Health API v2 work fine during Bolt development — you can fetch measurements, sleep data, and heart rate using an existing access token. However, the OAuth authorization flow requires a publicly accessible redirect URI, which means you cannot complete the initial account connection from the Bolt preview. Deploy to Netlify or Bolt Cloud first, register your deployed URL as the redirect URI, and test OAuth on the deployed site.

Does the Withings API return real-time data from devices?

No — the Withings API returns data that has already been synced from the device to Withings' servers. Withings devices sync when the user opens the Health Mate app on their phone or when the device connects to its configured Wi-Fi network. For most use cases (health dashboards, weekly reports), this delay of a few minutes to hours is acceptable. Withings does offer webhook notifications when new measurements are available, but those require a deployed server with a public URL to receive.

How do I access Withings webhook notifications for real-time data updates?

Withings supports subscription webhooks that POST to your server when new measurements are available. Subscribe using the POST /notify endpoint with your deployed callback URL. Since webhooks require an incoming HTTP connection, they cannot be received by Bolt's WebContainer during development — you must deploy your app first, then register the webhook URL with Withings and test it on your deployed site.

Is Withings API free to use?

The Withings Health API is free for personal and non-commercial use. You register a developer account at account.withings.com/partner/dashboard at no cost. Commercial applications require submitting your app for review and acceptance into the Withings partner program, which may involve licensing terms depending on your use case.

Can I access data from any Withings device type with one API?

Yes — the Withings Health API v2 uses a unified measurement endpoint for all device types. You select which measurement types you want using meastype codes: code 1 for weight (from smart scales), codes 9/10 for blood pressure (from BPM monitors), code 11 for heart rate, and the sleep endpoint for Sleep Analyzer data. Different meastype codes are available depending on which devices the user owns and has synced.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.