# Fix HubSpot OAuth failing with a missing required scope

## TL;DR
The OAuth authorization URL did not include a scope the integration needs (like crm.objects.contacts.write), so the granted token cannot perform those calls. Add the missing scope to the app's required scopes and to the authorize URL, then have the user re-authorize: scopes bind at grant time, so existing tokens never gain new scopes. Re-auth is mandatory, not optional.

## The error
```text
GET /crm/v3/objects/contacts -> 403
{"status":"error","message":"This app hasn't been granted all required scopes","requiredScope":"crm.objects.contacts.write"}
```

## Use this skill
Agent? Search Vectle for this error:
```bash
curl -s "https://vectle.com/api/v1/search?q=hubspot%20oauth%20missing%20scope%20crm.objects.contacts.write%20error"
```

## Fix it

### Step 1: Confirm the scope is required by the app

```bash
In the HubSpot developer account, open the app / Auth tab and verify crm.objects.contacts.write is listed in required scopes.
```

Expected: the scope is listed. If it is not, add it here first; the authorize URL cannot grant what the app does not require.

### Step 2: Check the authorize URL actually requests it

```bash
Inspect the authorize URL the integration builds. The scope query parameter must include crm.objects.contacts.write.
```

Expected: the scope string contains the missing scope. A common bug is updating the app settings but not the URL builder.

### Step 3: Send the user through OAuth again

```bash
Direct the user to the corrected authorize URL to complete a fresh grant.
```

Expected: the consent screen shows the newly added scope. Only a new authorization code carries the new scopes.

### Step 4: Exchange the code and verify the call

```bash
Exchange the code for tokens, then call the contacts endpoint that previously 403d.
```

Expected: 200 or 201 instead of 403. The new token carries the granted scope.

### Step 5: Add a scope assertion at startup

```bash
After every token grant, call the token-info endpoint and assert all required scopes are present; fail fast with a clear message if not.
```

Expected: scope gaps surface at install time with a named missing scope, not weeks later as mysterious 403s.

## When this applies

- HubSpot API calls 403 with a scope error
- A scope was recently added to the app
- The integration worked before a scope change

## When it doesn't

- The error is 401 (token expired or revoked)
- The scope is granted but the call still 403s (check portal permissions)
- Using a private app token instead of OAuth (different auth model)

## Compatibility

HubSpot OAuth 2.0, CRM API v3 scopes. Notes apply per portal: each connected portal needs its own grant.

## Variant phrasings

### hubspot this app hasnt been granted all required scopes

Same error text. The fix is always re-authorize after fixing the scope list.

### hubspot oauth scope missing contacts write

Check both the app settings and the authorize URL; they drift apart silently.

## Why it happens

HubSpot binds scopes at grant time: the token carries exactly what the user consented to in that OAuth dance. Adding a scope to the app's required list changes future grants but does nothing to tokens already issued. Until the user completes a fresh authorization with the corrected URL, every call needing the new scope 403s.

## Edge cases

- Scope names change across API versions; verify against current HubSpot scope docs
- Private app tokens use a different permission model; do not mix the two in one integration
- Each portal grants separately; fixing one portal does not fix the others

## If it still fails

- Capture the exact timestamp, the username, and the full error from the system log before changing anything else.
- Reproduce with a single test user so you are not debugging a crowd.
- If it worked before, diff the config against the last known good, then open a vendor ticket with the timestamp and request id. Never send secrets or private keys.

## Prevention

- Track credential and certificate expiry with alerts, not memory.
- Run a synthetic check per app daily so breakage pages you, not a user.
- Document mappings, URLs, and runbook steps where the next admin will find them.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_DlbGH8gluqwwL2rjT4gQ1Q
