> ## Documentation Index
> Fetch the complete documentation index at: https://docs.useparagon.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Headless install stalls after OAuth with a pending credential

> A PENDING credential makes the next installFlow.start resume post-OAuth options instead of opening a new OAuth window.

Breadcrumb

If Connect looks successful once, then later clicks never open OAuth and the integration stays disconnected in your UI, the user likely still has a `PENDING` credential. That happens when OAuth finishes but required post-OAuth options are not set. The next `installFlow.start` resumes that incomplete install instead of starting a new one.

This is not a blocked popup. If `onError` reports `OAuthBlockedError`, use [OAuth popup is blocked when connecting an integration](https://docs.useparagon.com/knowledge-base/oauth-popup-blocked.md).

## Symptoms

* First Connect appears to complete OAuth, then the integration never shows as enabled.
* Dashboard or `getUser()` shows `credentialStatus: "PENDING"` (sometimes labeled pending configuration).
* Later Connect clicks do not open an OAuth window. The Headless flow goes straight to `postOptions`, or appears stuck with no UI.
* `onComplete` never fires. Your `onNext` handler may call `setPostOptions({})` and return.

## Cause

Headless install is a sequence of stages. OAuth is only one of them. After OAuth, some integrations emit `PostOptionsStage` (a site, workspace, or similar required field). [`setPostOptions`](https://docs.useparagon.com/apis/api-reference#installflowsetpostoptions) saves those values and completes the connection.

If you skip the stage or pass an empty object, Paragon can still create a credential, but it stays `PENDING` because required post-OAuth options were never provided. The next `installFlow.start` for that user and integration prefers completing the pending credential. You never get a new OAuth popup, so it looks like Connect is broken.

| `onNext` for `postOptions` | What Paragon stores | Next Connect |
| - | - | - |
| Collect the required fields and call `setPostOptions({ FIELD_ID: value })` | `credentialStatus` becomes usable (`VALID` when nothing else is wrong) | New installs work as usual |
| `setPostOptions({})` or no handler | Credential stays `PENDING` | Start resumes `postOptions`; no new OAuth window |
| User closes the tab mid-stage | Credential may stay `PENDING` | Same resume behavior until you cancel or uninstall |

[`installFlow.cancel`](https://docs.useparagon.com/apis/api-reference#installflowcancel) documents this: if OAuth completed but required post-OAuth options did not, cancel removes the `PENDING` credential. After a reload, call `uninstallIntegration` yourself.

## Fix

### 1. Confirm the credential is `PENDING`

```javascript theme={null}
const user = paragon.getUser();
const integration = user.integrations["<integration-type>"];

if (integration?.credentialStatus === "PENDING") {
  // This Connect attempt will resume post-OAuth options, not a new OAuth popup.
}
```

### 2. Complete post-OAuth options in `onNext`

Handle `postOptions` the same way you handle `preOptions`: render the fields from `next.options` and pass the selected values into `setPostOptions`. The [SDK example](https://docs.useparagon.com/apis/api-reference.md#installflowsetpostoptions) uses field ids from that stage (for example `JIRA_CLOUD_ID`).

### 3. Clear a stuck `PENDING` credential

1. If the install flow is still in memory, call `paragon.installFlow.cancel()`. That uninstalls a `PENDING` credential created in the current session.
2. If the page reloaded, call `paragon.uninstallIntegration("<integration-type>")` (and `selectedCredentialId` when using multiple accounts).
3. Start `installFlow.start` again, with a real `postOptions` handler.

## Prevention

* Implement `PostOptionsStage` for every integration you enable. A handler that only works for integrations without post-OAuth fields will fail the first time you add one that has them.
* After `onComplete`, check `getUser()` and refuse to treat `PENDING` as connected.
* If the user abandons Connect after OAuth, call `installFlow.cancel()` so they do not remain `PENDING`.

## Related articles

* [Headless Connect Portal](https://docs.useparagon.com/connect-portal/headless-connect-portal.md)
* [installFlow.setPostOptions](https://docs.useparagon.com/apis/api-reference.md#installflowsetpostoptions)
* [installFlow.cancel](https://docs.useparagon.com/apis/api-reference.md#installflowcancel)
* [OAuth popup is blocked when connecting an integration](https://docs.useparagon.com/knowledge-base/oauth-popup-blocked.md)
