# Overview



### This guide will help you understand how to authenticate, manage API keys, and make your first request.

**API Address**: https://api.qpien.com/api/v1

---

## The process looks like this:


<Steps>
  <Step title="Generate API Keys">
   - In your Qpien App, go to **Api Keys** and create a new API key.
   - Your **Client ID** and **Client Secret** will be generated.  
   - Configure API scopes and save changes.
   - **Not:** Client Secret only visible on creation.
  </Step>
  <Step title="Get an Access Token">
     - Use your `clientId` and `clientSecret` to request an access token. 
     - Tokens are valid for **1 hour**.  
     - You’ll need to refresh or request a new token after it expires.
  </Step>
  <Step title="Make Requests">
    - Send requests to our API at:  
     ```
     https://api.qpien.com/api/v1
     ```
     - Include your access token in the `Authorization` header:  
     ```
     Authorization: <access_token>
     ```
  </Step>
</Steps>

---

## Quick Example

### Step 1 — Get an Access Token

```bash
curl -X POST https://api.qpien.com/api/v1 \
  -H "Content-Type: application/json" \
  -d '{
    "query": "mutation { generateOAuthToken(clientId: \"your_client_id\", clientSecret: \"your_client_secret\") { accessToken expiresIn } }"
  }'
```


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

### Step 2 — Call the API (GraphQL Query)

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

```js
{
  "data": {
    "contacts": [
      { "id": "1", "firstName": "Alice", "lastName": "Doe" },
      { "id": "2", "name": "Bob", "lastName": "Odengard" }
    ]
  }
}
```

---


This page describes the general guidelines and conventions for working with the **Qpien API**. Developers should review this section before integrating with the modules.

---

## **Authentication**

- All requests to the API must be authenticated.
- Authentication is done using **Bearer Token** in the `Authorization` header:

```graphql
Authorization: <YOUR_ACCESS_TOKEN>
```

- Tokens are provided for each customer and should be stored securely.

---

## **Rate Limiting**

To ensure stability and fairness, all requests to the API are rate-limited.

- **Standard Limit**: 100 requests / minute

- **Burst Limit**: Short spikes may be tolerated, but exceeding the limit will result in a temporary block.

- **Headers for Tracking:** Each response contains rate-limit headers so you can monitor usage:

| Header                  | Description                                   |
| ----------------------- | --------------------------------------------- |
| `X-RateLimit-Limit`     | Maximum number of requests allowed per minute |
| `X-RateLimit-Remaining` | Remaining requests in the current window      |
| `X-RateLimit-Reset`     | Time (in seconds) until the limit resets      |

If you exceed the rate limit, the API will return an **error response** with a `429 Too Many Requests` code.

---

## **Status Codes**

Unlike REST APIs, GraphQL APIs usually return **`200 OK`** for all requests (since the transport succeeded), but include success/failure information inside the **response body**.



| Status Code                 | Description                                                                            |
| --------------------------- | ---------------------------------------------------------------------------------- |
| `200 OK`                    | Request processed successfully (may still contain errors in GraphQL response body) |
| `400 Bad Request`           | Invalid query, malformed input, or validation error                                |
| `401 Unauthorized`          | Missing or invalid authentication token                                            |
| `403 Forbidden`             | Insufficient permissions                                                           |
| `404 Not Found`             | The requested resource (entity) does not exist                                     |
| `429 Too Many Requests`     | Rate limit exceeded                                                                |
| `500 Internal Server Error` | Unexpected server error                                                            |
| `503 Service Unavailable`   | Temporary downtime or maintenance                                                  |

## <Icon icon="material-outline-assignment_returned"/> GraphQL Response Format

A GraphQL response always follows this structure:

```json
{
  "data": {
    "externalGetMessage": {
      "success": true,
      "data": { ... },
      "code": "200",
      "message": "Message retrieved successfully"
    }
  },
  "errors": [
    {
      "message": "Validation error: messageId is required",
      "path": ["externalGetMessage"],
      "extensions": {
        "code": "BAD_USER_INPUT"
      }
    }
  ]
}
```

- `data` → Contains the successful response (if any).

- `errors` → Contains validation or execution errors.

- `extensions.code` → Provides a machine-readable error type.

---

## Error Handling

We standardize errors across the API to make them predictable.

| Code             | Description             | Example                                 |
| ---------------- | ----------------------- | --------------------------------------- |
| `BAD_USER_INPUT` | Input arguments invalid | Missing `conversationId`                |
| `UNAUTHORIZED`   | Invalid/missing token   | Access denied                           |
| `FORBIDDEN`      | Not enough permissions  | User not allowed to delete conversation |
| `NOT_FOUND`      | Resource not found      | Conversation ID does not exist          |
| `RATE_LIMITED`   | Too many requests       | Exceeded 100 req/min                    |
| `INTERNAL_ERROR` | Server failure          | Unexpected issue                        |


--- 

## Pagination

For list queries (e.g. `externalGetConversationList`, `externalGetMessageList`):

- Pagination is cursor/offset-based depending on the query.

- Common fields in paginated responses:

| Field         | Description                                       |
| ------------- | ------------------------------------------------- |
| `docs`        | Array of returned entities                        |
| `hasNextPage` | Boolean flag indicating if there are more results |
| `totalDocs`   | Total number of documents matching query          |

    
## Example

```graphql
query {
  externalGetMessageList(input: { conversationId: "123", limit: 10,   offset: 0 }) {
    success
    data {
      docs {
        _id
        content
      }
      hasNextPage
      totalDocs
    }
  }
}
```


--- 

## Versioning

- The API currently runs on **v1**.

- Breaking changes will be introduced via a new version (e.g., `v2`) with backward compatibility guarantees.

- Non-breaking changes (adding fields, optional arguments) are rolled out continuously.

---

## Best Practices

- Always check for `errors` in GraphQL responses.

- Implement retry logic with exponential backoff for transient errors (`500`, `503`).

- Use pagination instead of requesting large datasets in a single call.

- Cache repeated queries where possible to reduce API usage.
