# Authentication



All requests to the **Qpien API** must be authenticated using an **access token**.  
This page explains how to generate and use tokens, as well as important notes about token lifecycle.

---

## How It Works

1. Each customer can generate one or more **API Keys** in the **Qpien App** dashboard.  
   - An API Key contains a **Client ID** and **Client Secret**.  
   - Each API Key also has a set of **scopes** (permissions) such as `read` and `write`.

2. To interact with the API, you must first call the GraphQL mutation **`generateOAuthToken`** with your Client ID and Client Secret.

3. You will receive an **access token** that is valid for **1 hour**.  
   - Include this token in the `Authorization` header of every request:  
     ```
     Authorization: <access_token>
     ```

4. If you update the **scopes** of your API key in the dashboard (e.g., enabling `write`), you must **generate a new access token**. 

    :::danger[]
       - Old tokens will not automatically gain new permissions.  
    ::: 
   
5. When your token got expired, simply call `generateOAuthToken` again with the same Client ID and Client Secret to get a fresh token.

---

## Get Access Token

### GraphQL Mutation

```graphql
mutation {
  generateOAuthToken(
    clientId: "your_client_id",
    clientSecret: "your_client_secret"
  ) {
    accessToken
    expiresIn
  }
}
```

### Example in Bash (cURL)

```bash
curl --location 'https://api.qpien.com/api/v1' \
--header 'Content-Type: application/json' \
--data '{
  "query": "mutation GenerateOAuthToken($input: GenerateOAuthTokenInput!) { generateOAuthToken(input: $input) { success data { accessToken expiresIn } code message } }",
  "variables": {
    "input": {
      "clientId": "YOUR_CLIENT_ID",
      "clientSecret": "YOUR_CLIENT_SECRET"
    }
  }
}'
```

```js
{
  "data": {
    "generateOAuthToken": {
      "accessToken": "eyJhbGciOiJIUzI1NiIs...",
      "expiresIn": 3600
    }
  }
}
```

### Example in Node.js


```js
async function getAccessToken() {
  const response = await fetch("https://api.qpien.com/api/v1", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      query: `
        mutation {
          generateOAuthToken(
            clientId: "your_client_id",
            clientSecret: "your_client_secret"
          ) {
            accessToken
            expiresIn
          }
        }
      `,
    }),
  });

  const { data } = await response.json();
  console.log("Access Token:", data.generateOAuthToken.accessToken);
  console.log("Expires In:", data.generateOAuthToken.expiresIn);
}

getAccessToken();

```

### Using the Token

After retrieving your token, include it in the **Authorization** header of every request:

```bash
curl -X POST https://api.qpien.com/api/v1 \
  -H "Authorization: eyJhbGciOi..." \
  -H "Content-Type: application/json" \
  -d '{
    "query": "{ externalGetContacts { id firstName lastName ... } }"
  }'
```

## Error Handling

If something goes wrong while authenticating, the API will return one of the following error codes:

| Error Code            | Description                                                                                     |
| --------------------- | ----------------------------------------------------------------------------------------------- |
| `UNAUTHORIZED`        | Missing or invalid token.                                                                       |
| `INVALID_CLIENT`      | The provided Client ID or Client Secret is incorrect.                                           |
| `TOKEN_EXPIRED`       | The token has expired (tokens last for 1 hour). Generate a new one.                             |
| `INSUFFICIENT_SCOPES` | Your token does not have permission for this operation. Update scopes and generate a new token. |

## Best Practices

📌 Always store your **Client Secret** securely. **Never** expose it in frontend code.

📌 Regenerate your token when it expires by calling **generateOAuthToken** again.

📌 Generate a new token after updating your **API key’s** scopes.

📌 For long-running jobs, implement retry logic to handle **TOKEN_EXPIRED** gracefully.



