Skip to main content

Overview

This guide walks through transitioning your existing integration from per-vehicle user tokens to application-level access tokens using API Authentication. Important: This migration is optional. Your existing per-vehicle user tokens continue to work indefinitely. Migrate at your own pace.

Before You Begin

Ensure you have the following:
  • Active Smartcar developer account with dashboard access
  • Existing integration using per-vehicle user tokens and Smartcar Connect
  • Ability to update your backend database schema
  • Development and staging environments ready for testing
  • Team familiarity with your current token storage approach

Migration Overview

The transition follows four steps. You control the pace.
  1. Create API credentials — Generate your Client ID and Secret in the Dashboard
  2. Capture user_id from Connect — Store the user_id returned in the Connect redirect URL
  3. Backfill existing connections — Use the Connections endpoint to retrieve user IDs for existing connections
  4. Switch to application tokens — Replace per-vehicle user tokens with your application access token + sc-user-id header

Phase 1: Enable API Authentication (Parallel Operation)

1

Create API credentials in Dashboard

  1. Log in to your Smartcar Dashboard
  2. Navigate to API Credentials in your application settings
  3. Click Create Secret
  4. Copy the client_id and client_secret and store them securely
Store these credentials as environment variables. Never commit them to version control.
2

Continue using per-vehicle user tokens

Keep your existing token refresh and storage logic unchanged. You’ll phase this out in Phase 3.
3

Test API Authentication

Exchange your credentials for an access token:
curl -X POST https://iam.smartcar.com/oauth2/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET"
Access tokens are valid for 1 hour. Cache them and refresh when expired. Build a token refresh loop in Phase 2.
4

Run Phase 1 in staging

Deploy your API credentials to your staging environment. Make test API calls using the access token. Verify responses match your per-vehicle user token calls.

Phase 2: Update Integration

1

Capture userId from Connect redirect

When users complete Smartcar Connect, the redirect now includes a userId parameter:
https://example.com/callback?code=CODE&userId=a4c82e9f-...&state=STATE
Update your callback handler to extract and store this userId:
app.get('/callback', (req, res) => {
  const { code, userId, state } = req.query;

  // Verify state token (CSRF protection)
  if (state !== req.session.state) {
    return res.status(400).send('Invalid state');
  }

  // Store userId for later
  req.session.smartcarUserId = userId;

  // Exchange code for per-vehicle user token (Phase 1 & 2)
  const tokenData = await exchangeCodeForToken(code);

  // Store both for now (Phase 1 & 2)
  req.user.smartcarUserId = userId;
  req.user.smartcarToken = tokenData.access_token;
  await req.user.save();

  res.redirect('/dashboard');
});
2

Store userId ↔ user ID mappings

Create a new database column to store the Smartcar userId alongside your internal user ID. See the schema changes section below.
3

Query connections by userId

Start querying the connections endpoint with the userId filter:
curl -X GET "https://vehicle.api.smartcar.com/v3/connections?filter[user_id]=a4c82e9f-..." \
  -H "Authorization: Bearer {access_token}" \
  -H "sc-user-id: a4c82e9f-..."
The sc-user-id header tells Smartcar which user’s connections you’re accessing. Always include it.
4

Handle webhooks with user.id

Smartcar webhooks now include user.id in the payload. Use this to route events to the correct user:
{
  "eventId": "b9e4d2a6-5f18-43c7-a0b3-8d1f6e9c7a54",
  "eventType": "VEHICLE_STATE",
  "data": {
    "vehicle": {
      "id": "vehicle123",
      "make": "Tesla",
      "model": "Model S",
      "year": 2020,
      "vin": "5YJSA1E26FFP12345"
    },
    "signals": [],
    "user": {
      "id": "a4c82e9f-..."
    }
  },
  "triggers": [],
  "meta": {}
}
Update your webhook handler to extract and use the user ID:
app.post('/webhooks/smartcar', (req, res) => {
  const { data, eventType } = req.body;
  const userId = data.user.id;
  const vehicle = data.vehicle;

  // Route to correct user
  const userRecord = User.findBySmartcarId(userId);

  if (eventType === 'VEHICLE_STATE') {
    handleVehicleStateChange(userRecord, vehicle);
  }

  res.status(200).send('OK');
});

Phase 3: Full Migration

1

Switch to API Authentication

Update all API calls to use the access token and sc-user-id header:
// Before (per-vehicle user token per vehicle)
async function getVehicleInfo(vehicleId, vehicleToken) {
  const response = await axios.get(
    `https://api.smartcar.com/v2.0/vehicles/${vehicleId}/info`,
    {
      headers: {
        'Authorization': `Bearer ${vehicleToken}`
      }
    }
  );
  return response.data;
}

// After (application access token + userId)
async function getVehicleInfo(vehicleId, appToken, userId) {
  const response = await axios.get(
    `https://vehicle.api.smartcar.com/v3/vehicles/${vehicleId}`,
    {
      headers: {
        'Authorization': `Bearer ${appToken}`,
        'sc-user-id': userId
      }
    }
  );
  return response.data;
}
2

Remove per-vehicle user token refresh logic

Delete the code that refreshes per-vehicle user tokens. Application access tokens are re-requested from the token endpoint when expired.
Ensure your access token cache and refresh loop are working before removing per-vehicle user token logic.
3

Decommission token storage

Once all API calls use API Authentication, remove the vehicle_access_token column from your database. You only need to store the smartcar_user_id per user.

Database Schema Changes

Before (Per-Vehicle User Tokens)

CREATE TABLE users (
  id INT PRIMARY KEY,
  email VARCHAR(255),
  name VARCHAR(255),
  created_at TIMESTAMP
);

CREATE TABLE vehicles (
  id INT PRIMARY KEY,
  user_id INT,
  vin VARCHAR(17),
  vehicle_access_token VARCHAR(255),
  token_created_at TIMESTAMP,
  FOREIGN KEY (user_id) REFERENCES users(id)
);

After (API Authentication with userId Mapping)

CREATE TABLE users (
  id INT PRIMARY KEY,
  email VARCHAR(255),
  name VARCHAR(255),
  smartcar_user_id VARCHAR(36),
  created_at TIMESTAMP
);

CREATE TABLE vehicles (
  id INT PRIMARY KEY,
  user_id INT,
  vin VARCHAR(17),
  smartcar_vehicle_id VARCHAR(36),
  FOREIGN KEY (user_id) REFERENCES users(id)
);
You can keep the smartcar_vehicle_id for reference, but you no longer need token storage.

Code Changes: Before & After

// Old approach: Store and refresh per-vehicle tokens
async function getVehicleData(user) {
  const vehicle = await Vehicle.findOne({ userId: user.id });

  // Check token expiration
  if (isTokenExpired(vehicle.tokenCreatedAt)) {
    const newToken = await refreshVehicleToken(vehicle.refreshToken);
    vehicle.vehicleAccessToken = newToken;
    await vehicle.save();
  }

  // Make API call with vehicle token
  const response = await axios.get(
    `https://vehicle.api.smartcar.com/v3/vehicles/${vehicle.id}/signals`,
    {
      headers: {
        'Authorization': `Bearer ${vehicle.vehicleAccessToken}`
      }
    }
  );

  return response.data;
}

Testing Your Migration

Use this checklist to validate each phase: Phase 1 Validation:
  • API credentials created and stored securely
  • Access token obtained successfully
  • Staging environment receives access token
  • Test API call with access token returns same data as per-vehicle user token call
  • Both opaque and access token calls work in parallel without conflicts
Phase 2 Validation:
  • userId captured from Connect redirect
  • smartcar_user_id stored in user database
  • Connections query with userId filter returns results
  • Webhook handler routes events correctly using user.id
  • Old per-vehicle user token calls still work for existing connections
Phase 3 Validation:
  • All API calls updated to use access token + sc-user-id header
  • Token refresh loop running and caching access tokens
  • Opaque token refresh code removed from codebase
  • Database migration completed (vehicle_access_token removed)
  • Production traffic flows through API Authentication
  • No errors in error logs for authentication failures

Rollback Plan

If you encounter issues, rolling back is simple:
  1. Phase 1 Rollback: Delete API credentials from the Dashboard. No code changes needed.
  2. Phase 2 Rollback: Stop consuming userId from Connect redirect. Revert webhook handler to previous version. Per-vehicle user tokens still work.
  3. Phase 3 Rollback: Restore per-vehicle user token refresh logic from version control. Redeploy previous code. Vehicles remain connected.
Because per-vehicle user tokens continue to work indefinitely, you can roll back at any phase without losing access to vehicles.

What’s Next